feat(access): verified Bolt Card session at entry + hidden-by-default balance #93

Merged
padreug merged 3 commits from feat/boltcard-session-balance into dev 2026-09-20 15:16:42 +00:00
Owner

Tap-to-enter now opens a verified session on the card server instead of soft entry, and shows the holder their card wallet balance.

Session. The tap's single-use SUN is spent once on the boltcards fork's new /session endpoint, which proves a genuine, non-replayed card and returns the wallet balance plus the hit-keyed LUD-03 withdraw and LUD-06 pay second steps. Complete still needs no second tap; the ATM holds no p/c for the visit. The session stays single-shot (dropped after the first Complete attempt). A withheld withdraw step (daily limit spent) declines with the server's reason. Wire contract: docs/boltcard-session.md.

Balance, hidden by default. CardChip shows the card label with the balance masked and an eye toggle. Revealed, it mirrors the LNbits wallet page: sats, then the fiat equivalent (Intl currency formatting) in the wallet's own currency, else the instance default, else the ATM's fiat at its display rate. On the idle menu and both cash screens; resets on re-lock.

Trust boundary, unchanged. The session URL comes from the card's own lnurlw host, so with open enrollment a forged tag naming a cooperative server still unlocks the gate. Money is unaffected. #91 stays open; pinning accepted card-server hosts is the fix.

Depends on aiolabs/boltcards /session (feat/card-session-endpoint) being deployed on the card server before this merges.

Typecheck clean; machine 109 tests; full Electron build green.

Tap-to-enter now opens a verified session on the card server instead of soft entry, and shows the holder their card wallet balance. **Session.** The tap's single-use SUN is spent once on the boltcards fork's new /session endpoint, which proves a genuine, non-replayed card and returns the wallet balance plus the hit-keyed LUD-03 withdraw and LUD-06 pay second steps. Complete still needs no second tap; the ATM holds no p/c for the visit. The session stays single-shot (dropped after the first Complete attempt). A withheld withdraw step (daily limit spent) declines with the server's reason. Wire contract: docs/boltcard-session.md. **Balance, hidden by default.** CardChip shows the card label with the balance masked and an eye toggle. Revealed, it mirrors the LNbits wallet page: sats, then the fiat equivalent (Intl currency formatting) in the wallet's own currency, else the instance default, else the ATM's fiat at its display rate. On the idle menu and both cash screens; resets on re-lock. **Trust boundary, unchanged.** The session URL comes from the card's own lnurlw host, so with open enrollment a forged tag naming a cooperative server still unlocks the gate. Money is unaffected. #91 stays open; pinning accepted card-server hosts is the fix. Depends on aiolabs/boltcards `/session` (feat/card-session-endpoint) being deployed on the card server before this merges. Typecheck clean; machine 109 tests; full Electron build green.
Entry now spends the tap's single-use SUN once, on the card server's new
/session endpoint (aiolabs/boltcards feat/card-session-endpoint), instead
of parsing the lnurlw locally and deferring every check to Complete. The
server proves a genuine, non-replayed card and returns the wallet balance
plus the hit-keyed LUD-03 withdraw and LUD-06 pay second steps — the same
single-use bearer /scan and /pay hand out — so Complete still needs no
second tap and the ATM holds no p/c for the visit.

- electron/boltcard-session.ts: /scan → /session URL derivation, response
  parsing, 404 → 'card server does not support sessions'.
- lnurl-withdraw / lnurl-pay: the second steps are now callable on their
  own (executeWithdrawCallback, resolveInvoiceFromPayStep); the tap paths
  are unchanged and reuse them.
- IPC: lnurl:open-card-session, lnurl:withdraw-session, lnurl:pay-session.
- store: handleBoltCardEntry opens the session then authorizes the
  server-returned external_id; the payment handlers take a source (raw
  tap or session); a withheld withdraw step declines with the server's
  reason. The boltcard AccessScan no longer carries the lnurlw.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A tap-to-enter session is effectively the holder logging into their card
wallet, so show its balance — but a kiosk in a public place must not
display a stranger's balance unasked. CardChip renders the card label
with the balance masked (••••••) and an eye toggle; revealed, it mirrors
the LNbits wallet page: sats, then the fiat equivalent formatted with
Intl currency style. Fiat comes from the card server (the wallet's own
currency, else the instance default, at its rate) and falls back to the
ATM's fiat at its display rate when the server priced nothing. Shown on
the idle menu and both cash screens; reveal state resets on re-lock.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
docs/boltcard-session.md is the /session wire contract (sibling of
boltcard-receive-resolver.md), including the trust boundary: the session
URL is derived from the card's own host, so open enrollment is still not
a security boundary (#91). ADR-003's amendment now records verified entry
via /session and the hidden-by-default balance display.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
padreug deleted branch feat/boltcard-session-balance 2026-09-20 15:16:42 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiolabs/bitspire!93
No description provided.