Enrich kind 30078 beacon for public ATM status pages #43
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
The kind 30078 availability beacon currently publishes a minimal payload:
This is enough for an operator to see "is the machine alive", but not enough for a public-facing ATM status page that shows customers useful information — like what the screenshot below shows:
A public status page just subscribes to kind 30078 events from known machine npubs (or from the operator's NIP-51 fleet roster) and renders a card per machine. The beacon needs to carry enough data to make that useful without any backend.
Current state
Two related but disconnected data models exist:
useAvailabilityBroadcast.ts(what the machine actually publishes) — no fees, no name, no location, no denominations.ServiceBeaconinterface inpackages/clink/src/types.ts— hasname,avatarUrl,fees, but the machine doesn't use this type at all.These should converge into a single schema that serves both fleet management and customer-facing status.
Proposed beacon content
Field breakdown
namelocationgeolat,lonfor map display (optional)fiatmodelcash_incash_outcash_level"none" | "low" | "good" | "full"denominations.acceptdenominations.dispensefees.cash_in_pctfees.cash_out_pctfees.cash_in_flatfees.cash_out_flatlimits.cash_in_minlimits.cash_in_maxlimits.cash_out_minlimits.cash_out_maxversionAll fields are intentionally public. The beacon is a replaceable event (kind 30078,
dtagatm-availability) so only the latest state is visible — no history of stock levels or fee changes.What stays out
cash_levelis deliberately coarse. Exact counts are fleet-only telemetry (kind 30079, see #42).Where the data comes from
name,location,geo,fiat,model.envor config file, seeded at provisioning)cash_in,cash_out,cash_level,denominations.dispenseuseAvailabilityBroadcast)denominations.acceptfees.*,limits.*versionpackage.jsonversionChanges needed
ServiceBeacontype — updatepackages/clink/src/types.tsServiceBeaconinterface to match the proposed schema. The machine and the CLINK package should share one type.useAvailabilityBroadcast.ts— extendUseAvailabilityBroadcastOptionsto accept the new config fields (name, location, fees, limits, denominations). Updatepublish()to include them.hasChanged()— fees and limits are config-driven and won't change often, but if the operator pushes a fee change via fleet control (#42), the beacon should republish immediately.name,location,geo,fees,limitsto the machine's config schema (wherever that lives — currently.env).apps/dashboardor be its own thing) that subscribes to kind 30078 and renders the fleet status cards. This is the public-facing consumer of the beacon.Relation to #42
The fleet management issue (#42) introduces kind 30079 for operator-only telemetry (exact bill counts, LP balance, error logs). This issue is about making the existing kind 30078 beacon useful for public-facing status. The two events serve different audiences:
References
apps/machine/src/composables/useAvailabilityBroadcast.ts— current implementationpackages/clink/src/types.ts:302-313— existingServiceBeacontype (unused by machine)2026-05-26 cross-codebase review — two privacy / resilience nits worth folding in:
1.
cash_leveltiming leak. The issue already states the 4-level coarseness is deliberate ("not precise counts"). Good. But the transitions still leak:"none"→"full"(or any upward step) is a strong signal the operator just loaded cash.Mitigation options (cheap, complementary to this issue's enrichment work):
"full"vs"good"for the top bucket (e.g. 50/50). Same idea on the bottom —"none"vs"low".Recommend baking one of these into the enrichment work in this issue — once the beacon is the public-facing source of truth, the timing privacy story matters.
2. Boot-race resilience.
packages/nostr-client/src/client.ts:240-250(publish()) throws immediately if no writable relays — the beacon will silently drop on startup if the relay hasn't connected yet. With this issue's richer payload, the cost of "no beacon for 5min while relay reconnects" becomes "no beacon for 5min on a customer-facing status page." Worth adding a small retry queue (or a one-shot retry-on-relay:connectedevent) when publish fails due to no writable relays. ~30-min fix.Both points identified during the 2026-05-26 cross-codebase review pass. Not blocking this issue's main thrust — flagging for the implementation work.