get_machine_config kind-21000 RPC — deliver operator pubkey + fee config over the transport (bitspire#70 P1) #41
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?
Summary
Add a
get_machine_configRPC on the kind-21000 nostr transport so a paired ATM can pull its operator pubkey + fee config (+ fiat, cassettes) over the already-authenticated channel, instead of the operator pubkey being provisioned into the machine's.envand the fee config being learned only from an operator-signed kind-30078 broadcast.This is the server half of bitspire#70 P1. The client half is aiolabs/bitspire (see that repo's companion issue). It's what lets a seed-only machine (blank
.env, scannedspire-seed) leave "awaiting configuration" with zero provisioning.Why (design rationale — so this isn't re-litigated)
A paired ATM was getting stuck on "awaiting configuration": the ATM only trusts fee config (kind-30078) signed by an operator pubkey it holds in
VITE_OPERATOR_PUBKEYS, but the pairing seed doesn't carry that pubkey, so a seed-only machine has an empty operator allowlist → the Fees service disables itself. The obvious fix is "addoperator_npubto the seed" — but that would be the 3rd/4th npub stuffed into the pairing blob, and a review of comparable systems says that's the wrong direction:{wallet-service pubkey, relay, secret}. Capabilities are discovered after connect via the kind-13194infoevent — nothing else goes in the URI.bunker://is only{signer pubkey, relays, one-shot secret}; the client learns the actual identity viaget_public_keypost-connect.nprofile(server pubkey + relays) + a one-shot token; thenGetServiceStatestreams config down includingadmin_npub(the operator identity). It delivers the operator key over the wire, not in the pairing blob.The seed's irreducible floor is two channel anchors —
lnbits_npub+relays(to reach/auth LNbits) andspire_npub+bunker_secret(to redeem the NIP-46 bunker; can't leave, you can't connect to a bunker without its pubkey). The operator pubkey is not a channel anchor — it's config/policy — so it belongs on the LNbits channel once open, exactly like LP'sadmin_npub.Trust: delivering the operator pubkey over the transport means LNbits introduces the operator identity. On this stack that doesn't widen the trust base — spirekeeper/satmachineadmin is an LNbits extension in the same trust domain as the transport server, and the ATM already trusts LNbits with the wallet and server-side fee computation (
create_withdraw). So the operator-signature-on-kind-30078 check is largely redundant here; config-over-transport aligns config trust with the trust already extended for money.Why it's clean server-side (the enabling invariant)
The transport is already per-machine authenticated. An inbound kind-21000 event is signature-verified, and
sender_pubkeyis the ATM's bunker-minted spire key — which isdca_machines.machine_npub. So at dispatch time the server resolves the exact machine → wallet → operator purely from the verified sender, with no client-supplied trust. Aget_machine_configreply can therefore be scoped to that machine with zero new auth primitive.Evidence:
machine_npub = spire_pubkey_hex, granted kind-21000 signing at pair:pairing.py(pair_spire, policy grant),views_api.py(api_pair_machine,machine_npub = result.spire_pubkey_hex)nostr_transport_roster.py(resolve()→get_machine_by_atm_pubkey_hex),crud.py(get_machine_by_atm_pubkey_hex)nostr_transport/dispatcher.py,auth.py,roster.pyProposed implementation
Register a new RPC from spirekeeper's extension init (next to
register_with_lnbits/register_roster_resolverinnostr_transport_roster.py), via lnbits'register_rpc(name, handler, auth_level):Notes:
build_fee_payload) and the same operator-pubkey resolution as the publish path — no new signer path.created_atwatermark: include a monotonic value (e.g. the super-config / machineupdated_at) so the bitspire client can keep itsapplyFeeConfigreplay-watermark guards unchanged.Migration / compatibility
mainand the current bitspire consumer still expect it. The RPC is the new pull path; the push remains for live mid-run fee updates.Process
Money-adjacent extension → feature-branch + PR (hand off to merge via the Forgejo UI). Companion client issue: aiolabs/bitspire (P1 consumer). Related: bitspire#70, this repo's #37/#38.
Companion client issue: aiolabs/bitspire#71.