# Checkstr Hotel-style check-in / check-out, with Nostr as the data layer instead of a database. No backend at all — it's a static page that talks straight to Nostr relays. ## How it works - **Identity**: sign in with a NIP-07 browser extension (Alby, nos2x, ...), or generate a throwaway local key ("demo key") for trying it out. The demo key lives only in `localStorage` — don't use it for real guest data. - **Room status**: each room's current state (vacant/occupied, guest name, check-in time) is a NIP-33 **addressable event**, kind `33001`, with `d = ":"`. Publishing a new one for the same room *replaces* the old one on any relay that implements addressable events correctly — there's no "update a row," you just publish the new state. - **Activity log**: every check-in/check-out also publishes a plain kind-1 note tagged `#t checkstr` and `#t checkstr-`, so there's a human-readable history (and it's incidentally visible to any regular Nostr client browsing the `#checkstr` hashtag). - **Rooms list**: which room IDs exist for a property is local config (`localStorage`), not a Nostr event — Nostr tracks occupancy *state*, not the hotel's floor plan. `33001` is not a registered NIP kind, just picked clear of the well-known reserved ones (long-form 30023, live event 30311, classified listing 30402/3, calendar 31922/3, app handler 31989/90, git repo 30617/8). Addressable events are scoped per `(pubkey, kind, d-tag)`, so this only matters if another app renders unrelated JSON under the same kind — worth registering properly if this grows past a demo. ## Running it It's three static files — no build step: ```bash cd checkstr python3 -m http.server 8787 # open http://localhost:8787 ``` Or host it anywhere static (GitHub Pages, nginx, etc.). First run: pick relay(s), a property ID (anything unique to your hotel — it's just a string, no registration), and a comma-separated room list, then connect. Everything after that persists in relay storage + your browser's localStorage. ## What's actually been verified - Event construction (kind 33001 + kind 1), signing, and publish were tested against a real relay — explicit `OK` responses confirmed for both. - Addressable replace-on-publish (NIP-33) and filter-based subscribe (`kinds` + `authors` + `#t`) were confirmed round-trip correct via a raw WebSocket test against a real relay, bypassing any client library. - **Not tested**: the actual browser UI end-to-end (this environment has no browser with a NIP-07 extension installed). The "demo key" path should let you self-test without one — worth doing before relying on this for anything real. - One thing worth knowing if you hit it: `nostr-tools`' `SimplePool` choked (in a Node test harness, not necessarily in a browser) talking to an older `nostr-rs-relay` (0.8.9) — a raw hand-written REQ to the same relay worked fine. If a specific relay seems to silently never respond, it's worth suspecting the relay/library combo before suspecting this app's event shapes. - Public relays vary a lot in how they treat a brand-new pubkey's first write — some (nos.lol, relay.primal.net) let the publish time out silently rather than reject it outright; a NIP-07 identity with some existing history on a relay will likely fare better than a fresh demo key. ## Known limits (MVP, not production) - No encryption — room/guest data is public on whatever relays you publish to. Don't put anything sensitive in the guest name field as-is; a real deployment would want this behind NIP-44 encryption (e.g. gift-wrapped to staff pubkeys only) rather than plaintext content. - No access control — anyone with the property's relay+propertyId and a Nostr key can publish room-status events claiming to be "the property." The app only *reads* events authored by the logged-in pubkey, so a single property today is really "whoever is logged in," not a real multi-staff permission model. - No history view beyond the last 50 log notes kept in memory per session.