feat: expose Chatelet over the LNbits nostr transport (#1)

Register kind-21000 RPC handlers on the core nostr_transport dispatcher so
the booking flow runs over relays with no HTTP, mirroring lnurlp:

- operator (AUTH_WALLET): room create/update/publish, block create — all
  ownership-checked; room_list_mine (AUTH_ACCOUNT).
- public (AUTH_NONE): room_list/get (wallet id stripped), availability,
  booking_request, booking_get. Guest identity is the signed sender_pubkey,
  so no guest_pubkey is trusted from the body.
- register_link_owner_resolver(tag=chatelet, key=booking_id) lets the
  operator stream settlements via subscribe_payments.

Handlers delegate to services.py — no logic duplicated. Graceful no-op if
the core transport module isn't in this LNbits build (pre-#4). Guests can't
subscribe to the operator wallet, so they poll booking_get to confirm.

Note documented in event-flow.md: with availability now an RPC, the custom
kind:22000/22001 pair is redundant for RPC clients (revisit in #2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019VUQCfdqiLSsFS2jcGnaFD
This commit is contained in:
Padreug 2026-07-19 01:30:13 +02:00
commit 5b176e5186
3 changed files with 291 additions and 9 deletions

View file

@ -35,12 +35,57 @@ def chatelet_start():
scheduled_tasks.append(
create_permanent_unique_task("chatelet_hold_expiry", expire_holds_loop)
)
# TODO(relay): once relay plumbing lands, also start the inbound Nostr
# subscription so guests can query availability + book over Nostr:
# from .nostr.service import subscribe_inbound
# scheduled_tasks.append(
# create_permanent_unique_task("chatelet_nostr_in", subscribe_inbound)
# )
# Expose the booking flow over the core LNbits nostr transport (kind-21000
# RPC) so an HTTP-allergic client can drive Chatelet over relays. Also wire
# the booking-owner resolver so the operator can stream booking
# settlements via subscribe_payments({tag:"chatelet", link_id:<booking_id>}).
# No-op if the core transport module isn't present in this LNbits build.
try:
from lnbits.core.services.nostr_transport.dispatcher import (
AUTH_ACCOUNT,
AUTH_NONE,
AUTH_WALLET,
register_rpc,
)
from lnbits.core.services.nostr_transport.subscriptions import (
register_link_owner_resolver,
)
except ImportError:
return
from .transport_rpcs import (
handle_availability,
handle_block_create,
handle_booking_get,
handle_booking_request,
handle_room_create,
handle_room_get,
handle_room_list,
handle_room_list_mine,
handle_room_publish,
handle_room_update,
resolve_chatelet_owner,
)
# operator (wallet-scoped)
register_rpc("chatelet_room_create", handle_room_create, AUTH_WALLET)
register_rpc("chatelet_room_update", handle_room_update, AUTH_WALLET)
register_rpc("chatelet_room_publish", handle_room_publish, AUTH_WALLET)
register_rpc("chatelet_block_create", handle_block_create, AUTH_WALLET)
register_rpc("chatelet_room_list_mine", handle_room_list_mine, AUTH_ACCOUNT)
# public (discovery + guest booking)
register_rpc("chatelet_room_list", handle_room_list, AUTH_NONE)
register_rpc("chatelet_room_get", handle_room_get, AUTH_NONE)
register_rpc("chatelet_availability", handle_availability, AUTH_NONE)
register_rpc("chatelet_booking_request", handle_booking_request, AUTH_NONE)
register_rpc("chatelet_booking_get", handle_booking_get, AUTH_NONE)
# tasks.py stamps extra["booking_id"] on settlement (see on_invoice_paid),
# so override the default link_extra_key ("link") to match.
register_link_owner_resolver(
"chatelet", resolve_chatelet_owner, link_extra_key="booking_id"
)
__all__ = [