Commit graph

10 commits

Author SHA1 Message Date
65f5f982ec docs(adr): record that the operations wire shipped
Decisions 1 through 4 are built on both sides, so the status section says
what landed rather than what is planned, and decision 4a is marked
superseded.

4a is kept rather than deleted. It is the calibration for what a warning
dialog is worth: it depended on an operator reading it at the end of a
refill round, and it could not help at all when the stale value was
already in the form. The dialog and the endpoint behind it are both gone
now, which is a stronger guarantee than any wording could be.

Also records the one gap left open on purpose. An operation recorded
while the relay is unreachable waits for the operator's next action,
because only an operator action triggers a publish.
2026-09-23 12:56:19 +02:00
7e3112987d docs(adr): record the publish warning, and the live verification
Two things learned after the ADR was first written.

The dashboard's publish dialog already warns that the publish overwrites
the ATM's tracked counts and that decrements since the last baseline will
be lost, and says v2 reconciliation will replace it. That changes how the
gap should be read: a known risk with a human-factors mitigation, not an
oversight, and the product had already reached the same conclusion these
decisions formalise. Worth stating that a warning is the weakest control
available — it depends on an operator reading a dialog, and cannot help
when the stale value is the one already in the form.

Also records that decisions 5 to 8 were verified live on sintra rather
than only by unit test, and which of the listed failures those decisions
do not close.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-23 00:06:29 +02:00
e4742963a3 docs(adr): record the cassette-state synchronization model
This protocol spans two repos and decides how much cash a machine will
pay out, and its only specification was a closed issue and a chat log.
That is how four separate divergence bugs went unnoticed.

Records what the transport actually permits — an addressable event is an
unconditional overwrite ordered by a second-granularity clock, and a
relay acknowledges an event it then discards, so a losing writer is
never told — and why that rules out compare-and-swap and leads to the
ATM owning the count while the operator publishes operations.

Decisions 5 through 8 shipped in #104 and spirekeeper#44; 1 through 4
are the v2 operations wire and are not yet built.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-22 23:01:25 +02:00
ec2b15c08f docs: Bolt Card session contract, ADR-003 amendment for verified entry
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>
2026-09-20 17:07:17 +02:00
f11aced450 chore(access): prune unwired readers, amend ADR-003 for what shipped
ADR-003, .env.example, the access module's headers and the provisioning
schema still described the planned npub-QR → UID → serial-reader path.
What shipped (#86) is Bolt Card tap-to-enter over the main-process
pcscd reader with external_id as the identity, soft entry and
verify-at-payment. Nothing ever called availableAccessReaders(): the
camera npub-QR reader, the mock reader and the AccessReader seam were
dead, so they go; services/access now holds authorize, the card parser
and the credential types. The unused 'uid' scan variant goes with them;
'npub' (+PIN) and the 'challenge' seam stay.

The ADR gets an amendment section recording the differences, including
that open enrollment is not a security boundary and that the audit is
still a stub (both tracked as issues).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-20 15:16:59 +02:00
Patrick Mulligan
a7b409b109 feat(access): access-control gate — npub-QR badge + PIN + dev bypass (ADR-003)
Squashed skeleton (was 11 commits on feat/access-control-skeleton) for a
clean rebase onto dev. Adds a `locked` gate the terminal boots into until a
credential is presented; opt-in and non-breaking (defaults off → boots
straight to idle as before).

- state-machine: `locked` state + ACCESS_GRANTED/ACCESS_DENIED/DEV_UNLOCK
  events + accessBypass/devUnlockAllowed guards (packages/state-machine).
- services/access: reader abstraction, npub+PIN authorize() (nostr-tools
  nip19; accepts nostr:/nprofile), camera npub-QR reader, mock reader.
- LockedView.vue + ColorModeToggle: branded viewfinder, PIN pad, denied
  reason, dev-unlock; camera off-by-default + idle return.
- store/main/electron.d.ts: seed gate config, grant/deny/devUnlock wiring,
  access.json provisioning (no rebuild), get-config surface.
- deploy: access.example.json + provision-access.sh; ADR-003.

Credential union is npub today; UID (NFC tap) is the next step.
2026-09-19 10:34:45 +02:00
Patrick Mulligan
44f5c0dbcf docs(adr): amend ADR-002 — app recovery is first-line, SSH/NetBird last-resort
The access/recovery plane (SSH/NetBird) stands and may carry recovery
procedures, but it is explicitly NOT the only or first-line recovery. Add
a layered, cheapest-first recovery model: (1) app auto-recovery of its own
relay/Lightning connectivity, (2) an on-screen Retry for an operator at the
kiosk, (3) SSH/NetBird as the last-resort remote plane for genuine app/OS
failure. A public kiosk must not need remote shell access to recover from a
transient/boot-before-network outage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-08-04 18:11:40 +02:00
627d5e63e5 docs(adr): ADR-002 remote access & fleet management — three planes, NetBird
Separate payment (Nostr↔LNbits, SaaS-operator-owned), fleet control
(Nostr #42, machine-operator-owned), and access/recovery (SSH) planes
by trust owner. Recovery access is provisioned at install and
app-independent. Adopt NetBird for the access plane (scale + fully FOSS
self-hostable control plane; rejects Tailscale's closed control plane).

Reject a dashboard 'revoke SaaS-operator access' toggle as a false
promise — the SaaS operator controls LNbits and the default control
plane, so exclusion is by ownership (operator self-hosts), not by
toggle.
2026-06-14 11:17:02 +02:00
53b0d382e8 docs: finish the LNbits-era doc sweep across docs/ + .claude/skills/
Second + final batch of the doc refresh. README + CLAUDE went out in
8c9ae29; deploy/nixos/README + obsolete-flow flags in 924844f. This
commit covers everything left.

docs/machine-installation.md
  Was describing a manual AppImage scp deploy + a `lamassu-kiosk`
  systemd unit that hasn't been the deployment path for months.
  Replaced with a high-level "what the pipeline does and why"
  overview that points at deploy/nixos/README.md for the full
  command-by-command walkthrough. Includes the BATM3 chassis-mod
  note (custom Dell OptiPlex retrofit, not a stock Dell).

docs/architecture-comparison.md
  Rewrote the comparison to be lamassu-server (≤ v8.1.5) vs bitSpire
  (LNbits-backed) instead of the original lamassu-server vs LP-backed
  lamassu-next framing. Updated the cash-out + cash-in flow diagrams
  to show the actual nostr-transport path (LNbits-bundled nostrrelay
  extension at ws://<host>:5001/nostrrelay/test, no separate strfry
  container). Replaced the migration-path section with a softer
  "when to choose what" framing that includes Lamassu's current
  commercial offering as a legitimate third option. Added a header
  pointer to the Acknowledgements section.

docs/business-model.md
  Light touch-ups: Lightning.Pub → LNbits where it appeared, swapped
  the [[ndebit-cash-in-flow]] link for [[architecture-comparison]],
  noted the kind-30078 service beacon for availability broadcasts.

docs/device-configuration.md
  Dropped "Lamassu" branding from the machine-model headings
  (Sintra / tejo / douro / batm3 are referenced by hardware identity
  here, not by Lamassu's product line). Added the Sintra-specific
  ttyS4-vs-placeholder-ttyS1..3 gotcha we hard-learned during the
  first flash. Corrected the BATM3 entry: stock GeneralBytes chassis
  with a Dell OptiPlex 9030 AIO motherboard physically grafted in,
  NOT a Dell out of the box. Updated the example /dev/ttyJ* symlink
  output to match what a healthy Sintra actually shows.

docs/adr/001-hal-architecture.md
  ADRs are historical artifacts — kept the original decision text
  intact. Added a postscript noting:
    - The package rename @lamassu/hal → @bitSpire/hal
    - The v8.1.5 boundary on any lamassu-machine source-tree
      references (Lamassu's 2024-01-26 license transition)
    - That the "Remaining Work" list is complete and the first
      successful Sintra hardware integration ran on 2026-05-13

.claude/skills/lightning-check.md
  Rewrote end-to-end. Was Lightning.Pub-flavoured with CLINK kinds
  21001/21002 as the primary flows; now validates the LNbits nostr-
  transport surface (kind-21000 envelope, NIP-44 v2 encryption,
  subscribe_payments filter discipline, lnurlw link composition).
  Preserved a --clink mode for the still-live kind-21003 operator-
  management surface. Includes a "what to check" rubric for cash-out
  vs cash-in flows that mirrors the actual code in
  apps/machine/src/services/lightning.ts.

.claude/skills/hal-check.md
  Two pivots: (1) acknowledge ADR-001's TypeScript-not-Rust choice
  and reframe all the safety checklists in TS-flavour (type safety,
  discriminated unions, single-writer serial, bounded emitters)
  instead of Rust-flavour (unsafe, borrow checker). (2) Add explicit
  v8.1.5 provenance boundary plus a "forbidden operations" section
  that prohibits diffing or porting from v8.1.6+ lamassu-machine
  source. Updated the port-validation source-reference table to
  list TS file paths under packages/hal/ instead of Rust paths.

.claude/skills/docs.md
  @lamassu/* → @bitSpire/*. Replaced the Lightning.Pub mermaid
  diagram with a current cash-out flow showing the nostr-transport
  RPC + subscribe_payments push path. Left the createOffer noffer
  example in the API-docs template section since it's illustrative
  ("here's what a good TSDoc block looks like") rather than current
  reference documentation.

.claude/skills/test.md
  One-line: @lamassu/nostr-client → @bitSpire/nostr-client in the
  pnpm-filter example.

deploy/nixos/README.md
  Single touch-up: clarified the douro/batm3 hardware-module comments
  to reflect that BATM3 is a custom-installed Dell board in a
  GeneralBytes BATM3 chassis (not a Dell OEM).

Files NOT touched in this sweep (intentionally):
  - packages/hal/src/**/*.ts attribution comments — those reference
    "lamassu-machine" in their port-source headers. Those are
    factually accurate (the drivers ARE ported from there, up to
    v8.1.5) and constitute necessary license/attribution metadata.
    Editing them would erase the provenance trail.
  - .claude/skills/{nostr-check,security}.md — already protocol-
    neutral, no LP/lamassu references to clean up.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 19:08:03 +02:00
Patrick Mulligan
c98f126ba7 feat(docker): add dev.sh with auto-funding and ATM app setup
- Add dev.sh script for managing regtest development environment
- Implement cmd_fund to fund ATM app owner via Lightning.Pub API
- Add --fund flag to cmd_up for automatic funding on startup
- Update setup_atm_app to write VITE_APP_ID to machine .env
- Fix Electron IPC to pass appId and extensionApiUrl to renderer
- Restructure repo from nested lamassu-next/ to root

The dev.sh script now supports:
- ./dev.sh up --fund  # Start regtest and auto-fund ATM
- ./dev.sh fund       # Fund existing ATM app
- ./dev.sh status     # Show environment status
- ./dev.sh reset      # Clean restart

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-15 14:19:16 -05:00