S3 — NIP-57-style signed settlement receipts (preimage attestation) #17
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?
Part of #13. Closes gaps G2 (Payment.extra is unauthenticated) and G7 (no signed attestation).
Problem
Today
dca_settlementsis the source of truth. If the LNbits DB is ever tampered with — by a super skim, an insider, or a hostile host — there's no out-of-band record. Payment.extra is mutable JSON; auditors have no cryptographic anchor.Fix
After every settlement's three leg groups complete, LNbits publishes a signed event (NIP-57-style zap-receipt shape, possibly a custom kind in the 21001–21002 range) carrying:
The preimage is the unforgeable anchor: it hashes to
payment_hashon everydca_settlementsrow. Mismatch ⇒ forged settlement.Changes
LNbits side (
aiolabs/lnbits)kind:13194).lnbits.core.services.nostr_attestation.publish_settlement_receipt(payment, sender_pubkey, operator_pubkey).This repo (
aiolabs/satmachineadmin)tasks.py: subscribes to kind:9735 with#P=<atm_npub>filter for every active machine.dca_settlements.receipt_event_idandreceipt_relays(JSON).dca_settlementsrow, verifysha256(preimage_from_receipt) == payment_hash. Mismatch ⇒ alert.Open question
Use
kind:9735(NIP-57) verbatim or a new kind? NIP-57's receipt shape fits, but the semantics are "this was a zap of a Nostr event." We're attesting a payment, not a zap. Suggest: new kind21001("bitSpire settlement attestation") with the same tag shape. Decision in design review before implementation.Acceptance
dca_settlements.payment_hash.UPDATE dca_settlements SET gross_sats=…), reconciliation flags the row as drifted from the receipt.Reference
NIP-57 spec:
~/dev/nostr-protocol/nips/57.md.Design doc:
docs/security-pathway-v1.md§5.1 (end-state diagram), §6.S3.2026-05-26 — ATM-side prerequisite is now ready
The ATM-side receipt-subscription path planned in the Changes section of this issue will use
LnbitsClient.subscribePayments. Per the bitspire session, that path's hash-based dedup primitive just shipped ataiolabs/lamassu-next#50(commit8c4be01):seenEventIds(FIFO 1000) inLnbitsClient.handleReply— catches exact-replay.ActiveSubscriptionseenPaymentHashes(FIFO 500) — catches logical duplicates from server fan-out.When S3 work begins, the ATM's
subscribePayments({kinds: [9735], '#P': [atm_npub]})callback will get duplicate-safe processing for free. The bitspire session confirmed this dependency was the blocker; it's no longer one.Other state:
aiolabs/lamassu-nextrepo — the bitspire session offered to wire it once the kind/event-shape is locked (per their 2026-05-26 review note on #50).This issue is not yet unblocked overall — still needs the LNbits-server-side publisher work (the actual settlement-receipt emission). But the ATM-side hot path is ready.
2026-05-26 — producer-side issue filed at
aiolabs/lnbits#22The S3 work has two halves; the LNbits-server-side publisher half is now a separate tracking issue:
aiolabs/lnbits#22— "nostr-transport: publish signed settlement-receipt events (NIP-57-style preimage attestation)".This consumer-side issue (
#17) waits until that producer issue ships. There's nothing to subscribe to without receipts on the wire.When
lnbits#22lands the events, the consumer side here is:tasks.py—subscribePayments-style filter{"kinds": [9735 or 21001], "#p": ["<operator_pubkey>"]}per active machine.dca_settlements.receipt_event_id+receipt_relays(JSON).sha256(preimage_from_receipt) == payment_hashon every row.The hash-dedup primitive that this'll piggyback on already shipped in
aiolabs/lamassu-next#50(commit8c4be01).Closing as paused-on-upstream; will reopen / re-engage when
aiolabs/lnbits#22ships.➡️ Migrated to aiolabs/spirekeeper#11 (aiolabs/spirekeeper#11).
The v2-bitspire line of this extension now lives in its own repo,
aiolabs/spirekeeper. Tracking for this issue continues there; closing here. (Issue numbers were reassigned in the new repo.)