bitspire/apps/machine/electron
Padreug c889f7f0df feat(cassettes): consume operator operations instead of counts
The machine now owns its bay counts outright. The operator publishes what
it did — a refill in notes added, an empty, a recount, a denomination
change — and this process applies it to the total it already holds.

Both sides used to write the same value over a transport that never tells
a writer it lost. Addressable events order by created_at at second
granularity with ties broken on event id, and a relay returns OK for an
event it then discards, so a dashboard form loaded before a dispense
silently discarded that dispense and neither side could detect it. A
value with one writer cannot be clobbered.

Schema v13 adds cassette_ops, the dedup ledger. A delta applied twice is
wrong and addressable events are re-delivered on every reconnect, so the
operator mints an id per operation and this table records the ones
applied. That also retires the created_at watermark on this path: it was
the only replay defence under absolute counts, but it drops an
out-of-order event whole, operations included, where per-op ids let the
unseen ones through and no-op the rest.

A window is applied oldest-first by `at`, ties broken by id, in one
transaction with the count mutation. A recount then a refill is not the
same as the reverse, and a crash mid-apply must roll back to a coherent
count rather than a partial one.

A malformed op or one naming a bay this machine does not have is neither
applied nor recorded, so it stays pending on the operator's dashboard.
That is the honest outcome. Recording it as applied would stop the noise
by telling the operator their refill landed.

The state document gains applied_ops, seq and schema_version. applied_ops
is the acknowledgement leg — echoing the ids back is the only way the
operator can tell an operation that landed from one merely sent. seq is
bumped on every local count change from any cause, so a reader can reject
a regression without trusting either clock.
2026-09-23 12:55:52 +02:00
..
__tests__ feat(cassettes): consume operator operations instead of counts 2026-09-23 12:55:52 +02:00
boltcard-session.test.ts feat(machine): open a verified Bolt Card session at tap-to-enter 2026-09-20 17:07:16 +02:00
boltcard-session.ts feat(machine): open a verified Bolt Card session at tap-to-enter 2026-09-20 17:07:16 +02:00
fund-atm.ts chore(machine): fund-atm resumes from binding; VITE_SPIRE_SEED docs/env 2026-06-19 00:15:59 +02:00
hal-service.ts fix(hal): EBDS escrow stack/return latch + return-on-disable 2026-07-30 00:40:06 +02:00
lnurl-pay.test.ts feat(machine): open a verified Bolt Card session at tap-to-enter 2026-09-20 17:07:16 +02:00
lnurl-pay.ts feat(machine): open a verified Bolt Card session at tap-to-enter 2026-09-20 17:07:16 +02:00
lnurl-withdraw.test.ts feat(machine): open a verified Bolt Card session at tap-to-enter 2026-09-20 17:07:16 +02:00
lnurl-withdraw.ts feat(machine): open a verified Bolt Card session at tap-to-enter 2026-09-20 17:07:16 +02:00
main.ts feat(cassettes): consume operator operations instead of counts 2026-09-23 12:55:52 +02:00
nfc-service.test.ts fix(machine): read NTAG424 NDEF via Capability Container (Bolt Card FileID) 2026-08-05 20:02:26 +02:00
nfc-service.ts fix(nfc): auto-recover a wedged CCID reader via USB power-cycle 2026-08-06 19:51:39 +02:00
preload.ts feat(cassettes): consume operator operations instead of counts 2026-09-23 12:55:52 +02:00
state-store.ts feat(cassettes): consume operator operations instead of counts 2026-09-23 12:55:52 +02:00
tsconfig.json feat(machine): add HAL IPC bridge for real hardware in Electron 2026-02-25 17:02:13 -05:00
tsconfig.preload.json feat(docker): add dev.sh with auto-funding and ATM app setup 2026-02-15 14:19:16 -05:00