Document the in-process nostrclient publish/subscribe path, the public-vs- encrypted split (encrypted events await bunker/server-signing), and the soft runtime dependency on the nostrclient extension. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019VUQCfdqiLSsFS2jcGnaFD
2.8 KiB
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