Files
ssmithx 63874fa3ba Initial commit: Checkstr, a Nostr-backed hotel check-in/check-out app
Static three-file app (index.html/ui.js/app.js) -- no backend, no build
step. Room occupancy is a NIP-33 addressable event (kind 33001); check-in/
check-out actions also log a tagged kind-1 note for an activity feed.
Event construction, signing, addressable replace-on-publish, and
filter-based subscribe were round-trip tested against a real relay.
2026-11-11 21:42:38 +00:00

4.0 KiB

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 = "<propertyId>:<roomId>". 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-<propertyId>, 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:

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.