feat: nostr event layer (builders + sign/publish service)

Implements ADR-0001. kinds.py pins the allocations; events.py has pure
builders (30402 listing, 30078 encrypted reservation, 31923 blocked-range
calendar, 22001 availability response); service.py signs/encrypts via
resolve_signer (spirekeeper hybrid pattern — no nsec at rest) and sketches
publish + inbound subscription. Relay plumbing marked TODO(relay).

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 00:16:39 +02:00
commit 900b682986
4 changed files with 264 additions and 0 deletions

27
nostr/kinds.py Normal file
View file

@ -0,0 +1,27 @@
"""Nostr event-kind allocations used by Chatelet.
Rationale for each choice lives in docs/adr-0001-nostr-event-model.md.
Short version: reuse standard NIPs wherever one fits the semantics, and
only reach into the aiolabs custom band for the live availability RPC that
no standard kind models well.
Aiolabs custom band is 22000-22099 (see workspace CLAUDE.md). The CLINK
band 21001-21099 is OFF-LIMITS. 22000/22001 are hereby allocated to
Chatelet register them in workspace CLAUDE.md before this ships so a
future extension doesn't collide.
"""
# --- standard NIPs (preferred) ---
KIND_LISTING = 30402 # NIP-99 classified listing (the room). Addressable.
KIND_RESERVATION = 30078 # NIP-78 app-specific data: the guest's durable
# reservation object, NIP-44 encrypted. Addressable
# so status updates (confirmed->checked_in->...) replace
# in place under the same d-tag (booking id).
KIND_CALENDAR = 31924 # NIP-52 calendar aggregating a room's blocked ranges.
KIND_CALENDAR_EVENT = 31923 # NIP-52 time-based event: one blocked range (no PII).
KIND_GIFTWRAP = 1059 # NIP-59 giftwrap carrying the private booking DMs
# (request -> quote -> confirm -> check-in details).
# --- aiolabs custom band (only where no standard kind fits) ---
KIND_AVAILABILITY_QUERY = 22000 # ephemeral: "is room X free for [in,out)?"
KIND_AVAILABILITY_RESPONSE = 22001 # ephemeral: open/closed + non-binding quote