ssmithx f8b9bbfbe2 fix: Check In/Out button hanging forever with no feedback
Root cause, reproduced live against checkstr.atobitcoin.io with a real
browser: the default relays were bad. wss://relay.damus.io was returning
503 at the WS handshake, and wss://nos.lol accepted the connection but
silently never sent an OK for a brand-new pubkey's first event -- no
error, just permanent silence. Promise.any over both left the check-in
dialog frozen indefinitely with zero feedback.

- Swap default relays to three that were actually verified (via a raw
  WebSocket test, bypassing any client library) to send an explicit OK
  within a couple seconds for a fresh key: relay.primal.net, offchain.pub,
  relay.nostr.wirednet.jp.
- Add a hard client-side timeout (10s) around every publish, normalizing
  both failure paths (AggregateError from all-relays-rejected, or nothing
  responding at all) into one clear message instead of an indefinite hang.
- Disable the Check In/Check Out buttons and show a pending label while a
  publish is in flight, instead of giving no feedback at all.
2026-11-12 04:07:32 +00:00

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.
S
Description
Hotel check-in/check-out, backed by Nostr.
Readme
40 KiB
Languages
JavaScript 74%
CSS 14.4%
HTML 11.6%