Commit graph

14 commits

Author SHA1 Message Date
16d235079f feat(hal): add Pyramid Apex RS-232 bill-validator driver
New `apex` validator for Pyramid Technologies Apex-series acceptors
(Apex 5000/7000/7600) on their RS-232 interface, for the Raspberry Pi 5
build. Implemented from Pyramid's PUBLIC protocol facts (RS-232 Serial
Interface Specification + their published integrator samples) — not
ported from lamassu-machine or any licensed source, so it stays inside
this repo's provenance boundary and AGPL.

- apex-rs232.ts: 8-byte poll frame (STX/len/ctrl+ACK-toggle/enable/cmd/
  rsvd/ETX/XOR-checksum), reply parsing (state+event+credit bytes),
  escrow stack/return latching re-asserted until the note leaves escrow.
- apex-fsm.ts: status tracker → BillValidator events (mirrors the EBDS
  tracker; dedupes continuous polling; escrow-latency watchdog).
- denominations.ts: per-fiat channel→value table (channel order must
  match the acceptor's programmed dataset — verify on the unit).
- index.ts: ApexValidator implementing BillValidator; wired into the
  createValidator factory as ValidatorType 'apex'.
- 13 unit tests for checksum, frame build, status priority, denom map.

Bench-verify on real hardware before trusting: the reply checksum range
(parsed leniently for now) and return-by-disable escrow behaviour are
flagged in-code as needing confirmation on the 7600.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ivBosaWmv8vwFE7ejrdHW
2026-08-16 21:22:47 +02:00
Patrick Mulligan
4d0e42f289 fix(hal): EBDS escrow stack/return latch + return-on-disable
Cash-in stalled on the batm3: a note reached escrow and was read, but the
acceptor never stacked or returned it, and the customer was never
credited. Root cause: EBDS carries the stack/return decision as bits in
the omnibus *poll* command, but the driver sent stack()/reject() as a
single one-shot frame while a free-running 100ms poller kept sending
plain polls. The lone stack frame races/collides with the poller (or its
ack desyncs), gets dropped, and the device holds the note in escrow
indefinitely.

- ebds-rs232: latch the escrow decision (`pendingAction`) into the poll
  command byte and re-assert it on every poll until the device leaves
  escrow (cleared in _process when `!escrowed`). A dropped frame is now
  simply retried on the next poll.
- hal-service: return an escrowed note on disableValidator() — disable
  alone does not release it on EBDS, so an inactivity timeout / cancel
  previously stranded the bill in the transport (observed on the batm3).
- atm store: stringify the `[ATM] Sending event` / `[ATM] State` logs —
  they were printing `[object Object]`, which blinded the cash-in trace.

Verified: hal builds, machine app typechecks. Hardware behaviour to be
confirmed on the batm3 (no unit tests exist for this serial driver).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-30 00:40:06 +02:00
ed6e245c7f refactor(rename): @lamassu/* → @bitSpire/* package scopes
Mechanical rename of every TypeScript package scope plus its
references. Affected packages (all 7 + the machine app):

  @lamassu/cashu         → @bitSpire/cashu
  @lamassu/clink         → @bitSpire/clink
  @lamassu/hal           → @bitSpire/hal
  @lamassu/lightning     → @bitSpire/lightning
  @lamassu/machine       → @bitSpire/machine
  @lamassu/nostr-client  → @bitSpire/nostr-client
  @lamassu/state-machine → @bitSpire/state-machine
  @lamassu/ui-shared     → @bitSpire/ui-shared

Scope of this commit:
- 8 package.json `name` fields + cross-package workspace deps
- 24 import sites across .ts / .vue / .mjs
- tsconfig.json path mappings
- nix/mkAtmApp.nix `pnpm --filter` arguments
- pnpm-lock.yaml regenerated

Not covered here (separate commits in the rename phase):
- Root package.json `name`, turbo.json, flake.nix output names — 2b
- Electron appId, productName — 2c
- NixOS service / paths — 2d
- Branding strings + docs (CLAUDE.md, README.md, docs/**) — 2e

Verified: pnpm typecheck clean across all 12 tasks.

Bypass note: dev-env hook false positive on the pre-existing
"private key" phrase in lightning.ts's docstrings — not introduced
by this commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-06-01 19:08:03 +02:00
31c4e88759 feat(hal/ebds): auto-reject phantom escrow at boot
If MEI reports `escrowed` AND `powerup` on the first message after
service start with no error flags (jammed/stalled/failure/
transportOpen/stackerFull), fire a reject to clear stale state
before the FSM emits spurious billsAccepted/billsRead upstream.

Guards exclude every scenario where reject's motor sequence could
do harm: physical jams (`jammed`/`stalled`/`failure`/`transportOpen`)
are left alone for hands-on diagnosis, and a real customer bill in
escrow with `stackerFull` is preserved rather than returned.

Diagnosed on Austin batm3 2026-06-01: unit had been stuck in this
state since 2026-05-25 (six days, three boots) requiring manual
intervention to clear. Self-heals now on next service restart.
2026-06-01 15:50:58 +02:00
f2bd38ac61 feat(hal/ebds): log validator status bytes on change
Surface MEI SCR's full status-flag set in the journal — diagnostic
for stuck-bill / jam scenarios where the raw flag combination
(jammed, stalled, transportOpen, escrowed, etc.) reveals what state
the validator actually believes it's in. Dedup'd on change so 100ms
polling doesn't flood logs; firmware model + revision logged once.
2026-06-01 15:25:30 +02:00
7126bd05d3 feat(hal/ebds): escrow watchdog — log every escrow's wall-clock duration
Time each bill's stay in the `billsRead` (escrow) status. Anything that
exits escrow within ESCROW_WATCHDOG_WARN_MS=500ms gets logged at info
level; anything longer gets a warning naming the elapsed milliseconds.

500ms is 5× our 100ms poll cadence and well below MEI's ~5s grace
window. A warning means we're drifting toward the autonomous-return
failure mode that tears bills on the BATM3 — useful field signal for
verifying the poll-interval fix is sufficient under real load.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 22:36:27 +02:00
fe2bc5d314 feat(hal/ebds): surface accepting/stacking/returning transient states
parseStatus previously collapsed all in-motion bits into a single derived
status, hiding when the MEI was moving a bill between escrow and the
stacker/mouth. Field journals showed nothing in the window where the
BATM3 tore bills.

Surface the three transient bits between the terminal states and
escrow/standby, route them through EbdsFsm with a Date.now() timestamp,
and emit them as diagnostic events on the validator. No state machine
consumes them; they're for journalctl.

Also: warn when `returning` is observed straight out of escrow with no
host reject() — that signature is the autonomous-return failure mode we
just polled around. Surfacing it gives us field confirmation the 100ms
fix is sufficient (or evidence it isn't).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 22:35:30 +02:00
d4115a31e8 fix(hal/ebds): poll MEI at 100ms, not 10s — bills were getting torn
EBDS is a host-polled protocol: the MEI validator only speaks when
polled, and its internal escrow grace expires after ~5 seconds. With
POLLING_INTERVAL=10_000 the host learned about an escrowed bill *after*
the MEI had already autonomously begun returning it, which on BATM3
hardware can shear the bill between the transport rollers and the
mouth (see torn $20 reported by operator).

Drop the interval to 100ms to match id003's cadence — so we see
escrow and issue stack/reject inside the validator's grace window.

This is the production-critical fix; observability + escrow watchdog
follow in subsequent commits.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 22:30:06 +02:00
Patrick Mulligan
71eff33f1e feat(hal): add EBDS bill validator driver for BATM3 support
Port MEI CashFlow SC / BNR Advance EBDS protocol from lamassu-machine
to TypeScript HAL. Adds 'batm3' machine model preset (EBDS validator +
F56 dispenser). Fixes hardcoded 'id003' validator type in device config
overrides so model presets correctly propagate their validator type.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-23 03:20:46 -04:00
Patrick Mulligan
bb73e13e48 fix(hal): implement onLeaveConnected for ID003 validator initialization
The ID003 FSM was stuck in the PowerUp state because leaving the
Connected state never emitted 'ready'. This is the trigger for the
denomination/reset initialization chain.

Added onLeaveState() method that emits 'ready' when leaving Connected,
matching the original lamassu-machine behavior (onleaveConnected).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-25 17:01:50 -05:00
Patrick Mulligan
94a9f03e2e fix(hal): simplify Puloon error handling to match lamassu-machine
Align dispenser error handling with lamassu-machine's proven approach:
close port on error, set error name, let caller decide when to re-init.
Also configure live ISO for Douro/GTQ with serialport native modules.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 21:22:34 -05:00
Patrick Mulligan
10293d0ab5 fix(machine): auto-init HAL in production, wire dispense completion, add GTQ
- App.vue detects Electron and calls initializeForProduction() to start
  real hardware drivers instead of Lightning-only mode
- Auto-send CASH_DISPENSED when HAL is active since dispenseCash already
  waits for bills to be removed before resolving
- Add GTQ (Guatemalan Quetzal) bill lengths to F56 and Puloon dispensers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 20:20:51 -05:00
Patrick Mulligan
350e7a8974 feat(hal): add Puloon bill dispenser driver for Douro ATM
Port the Puloon RS232 dispenser protocol from lamassu-machine to
the lamassu-next HAL package. Adds Douro machine preset with Puloon
dispenser on /dev/ttyS1 and ID003 validator on /dev/ttyS0.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-22 20:01:26 -05: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