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
62 lines
2.8 KiB
Markdown
62 lines
2.8 KiB
Markdown
# 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`](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`](docs/data-model.md) — tables, entities, lifecycle
|
|
- [`docs/event-flow.md`](docs/event-flow.md) — who publishes what, when (with sequence diagram)
|
|
- [`docs/adr-0001-nostr-event-model.md`](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
|
|
```
|