- Python 87.2%
- HTML 7.3%
- JavaScript 5.3%
- Makefile 0.2%
Chatelet is multi-tenant: any LNbits user can host rooms. What an operator decides for all their rooms now lives in chatelet.operator_settings, keyed by user id and created lazily (m003, which also indexes bookings by guest): check-in/out times, cancellation policy, and accept_fiat. Guests see it: the public room view (both doors) gains house_rules and payment_methods, and the kind:30402 listing carries payment_methods, checkin_time and checkout_time tags so a generic Nostr client can render the right pay buttons and rules without our RPC. The check-in DM reads the room owner's rules instead of the instance row. Card is offered only when the operator opted in, the room is fiat-priced, and LNbits core has a fiat provider for that user — resolved through settings.get_fiat_providers_for_user(owner), the one seam lnbits#67's per-user Stripe credentials will plug into; chatelet never sees creds. Operator endpoints: GET/PUT /api/v1/operator (admin key → wallet user) and RPC twins chatelet_operator_get/update (AUTH_WALLET); saving re-publishes the owner's active listings. Admin UI moves the house-rule inputs into a per-operator card with the card toggle and a provider hint. The old house-rule columns on settings stay for old rows but are no longer read. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> |
||
|---|---|---|
| docs | ||
| nostr | ||
| static | ||
| templates/chatelet | ||
| tests | ||
| .gitignore | ||
| __init__.py | ||
| config.json | ||
| crud.py | ||
| LICENSE | ||
| Makefile | ||
| migrations.py | ||
| models.py | ||
| pyproject.toml | ||
| README.md | ||
| services.py | ||
| tasks.py | ||
| transport_rpcs.py | ||
| views.py | ||
| views_api.py | ||
Chatelet
Nostr-native room rentals for LNbits — an "Airbnb for the castle". List rooms, take booking requests, arbitrate availability, and settle stays over Lightning, with Nostr as the interop layer.
Status: functional prototype. Data model, migrations, availability arbiter (atomic hold), FX + invoice + settlement, the REST + kind-21000 RPC doors, and the nostrclient relay layer (publish + availability queries) are in place and tested. The guest UI and encrypted-event delivery pre-bunker are the remaining gaps.
Soft dependency: publishing/subscribing to relays uses the nostrclient extension (imported in-process). Install + activate it to reach relays; if absent, the booking flow still works over HTTP/RPC and relay publish is skipped with a logged warning. Encrypted events (reservation
30078, check-in DMs) additionally need a bunker/server-signing signer (lnbits #18); on a LocalSigner they soft-fail until then.
Why not just reuse an existing NIP?
No single NIP models a rental-booking flow. Chatelet composes existing NIPs and only invents an event kind where nothing fits:
| Concern | Mechanism |
|---|---|
| Room listing | NIP-99 classified listing (kind:30402) |
| Guest's reservation record | NIP-78 app data (kind:30078), NIP-44 encrypted |
| Public availability calendar | NIP-52 (kind:31923/31924), no guest PII |
| Private request → quote → confirm | NIP-17/59 giftwrapped DMs |
| Live "is it free?" query | aiolabs band kind:22000/22001 (ephemeral) |
| Payment / deposit | LNbits Lightning invoice |
Full rationale in docs/adr-0001-nostr-event-model.md.
The one invariant that matters
LNbits is the booking authority; Nostr events are requests, not locks.
Availability is derived from the DB (bookings + blocks), never stored
as a positive fact, and a date range is only truly locked when a held
booking row is written. Payment — not any Nostr event — promotes a booking
to confirmed.
Docs
docs/data-model.md— tables, entities, lifecycledocs/event-flow.md— who publishes what, when (with sequence diagram)docs/adr-0001-nostr-event-model.md— why these kinds
Layout
config.json LNbits extension manifest
models.py data model (pydantic)
migrations.py schema (ext_chatelet DB)
crud.py persistence + the availability arbiter
views_api.py REST surface (shares the booking flow with Nostr)
views.py operator admin page route
tasks.py invoice listener (settle) + hold-expiry sweep
nostr/ event kinds, builders, sign/publish/subscribe service
docs/ design docs