bitspire/docs/adr/001-hal-architecture.md
Padreug 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

6 KiB

ADR-001: Hardware Abstraction Layer Architecture

Status: Accepted Date: 2026-01-26 Context: Lamassu Next ATM proof of concept

Decision

We will use TypeScript HAL drivers extracted from lamassu-machine rather than rewriting in Rust, and use Electron instead of Tauri for the kiosk application.

Context

The Lamassu Next project needs to control ATM hardware (bill validators, dispensers) on Lamassu Sintra machines. Initial plans considered:

  1. Rust HAL with Tauri backend
  2. TypeScript HAL with Tauri (IPC bridge)
  3. TypeScript HAL with Electron (direct integration)

Hardware Target: Lamassu Sintra

  • Platform: Aaeon UP Board (Intel Atom x5-Z8350, x86 Linux)
  • Bill Validator: JCM iVIZION using ID003 protocol
  • Bill Dispenser: Fujitsu F53/F56
  • Serial: /dev/ttyJ5 (validator), /dev/ttyJ7 (dispenser)
  • Protocol: RS-232, 9600 baud, 8 data bits, even parity, 1 stop bit

Options Considered

Option 1: Rust HAL + Tauri

Pros:

  • Native Tauri integration
  • Memory safety guarantees
  • Single backend language

Cons:

  • Requires full protocol reimplementation
  • Two debugging environments (Rust + TypeScript)
  • Serial at 9600 baud doesn't benefit from Rust performance
  • Significant time investment with uncertain payoff

Option 2: TypeScript HAL + Tauri

Pros:

  • Reuse battle-tested lamassu-machine drivers
  • TypeScript throughout application code

Cons:

  • IPC boundary between Tauri (Rust) and HAL (Node.js)
  • Serialization overhead and complexity
  • Two processes to manage and debug

Option 3: TypeScript HAL + Electron (Selected)

Pros:

  • Direct serialport npm access in main process
  • Single debugging environment (Chrome DevTools)
  • All packages run natively (state-machine, HAL, lightning, clink)
  • Mature ecosystem, well-documented edge cases
  • Fastest path to working proof of concept

Cons:

  • Larger binary size (~150MB vs ~10MB)
  • Higher memory usage than Tauri
  • Bundles Chromium

Decision Rationale

Why not Rust?

The JavaScript drivers in lamassu-machine have run in production for years. The complexity is in the protocol state machines (ID003 FSM, F56 DLE/STX framing), not raw performance. Serial communication at 9600 baud is trivial - Rust's performance benefits are irrelevant here.

Adding Rust creates a language boundary that requires:

  • IPC serialization/deserialization
  • Two build systems
  • Two debugging environments
  • Potential for bugs at the boundary

For a proof of concept, minimizing unknowns is critical.

Why Electron over Tauri?

Tauri's benefits (small binary, low RAM) optimize for end-user desktop apps where download size matters. An ATM is a single-purpose kiosk:

  • Binary size irrelevant - installed once on dedicated hardware
  • RAM sufficient - Sintra has 2-4GB, not competing with other apps
  • Development speed critical - proof of concept needs fast iteration

Electron allows the Vue UI, state machine, HAL, and Lightning packages to all run in the same Node.js environment with unified debugging.

Implementation Status

Completed

  • @lamassu/hal package created with:
    • ID003 bill validator driver (JCM iVIZION)
    • F56 bill dispenser driver (Fujitsu F53/F56)
    • TypeScript types for all interfaces
    • Extracted from lamassu-machine, fully typed

Remaining Work

  1. Convert apps/machine from Tauri to Electron
  2. Wire HAL to state machine services:
    • dispenseCash → F56Dispenser.dispense()
    • Bill events → state machine events
  3. Add device configuration (serial port paths)
  4. Test on physical Sintra hardware

Consequences

Positive

  • Faster proof of concept development
  • Easier debugging during hardware integration
  • Reuse of proven driver code
  • Single language throughout

Negative

  • Larger deployment size (acceptable for kiosk)
  • Cannot leverage Rust safety guarantees (mitigated by mature JS drivers)

Future Considerations

If a compelling reason for Rust emerges (it likely won't), the HAL interface is abstracted - drivers could be reimplemented without changing the state machine integration. The Vue UI works with either Electron or Tauri.

References

  • packages/hal/ — TypeScript HAL implementation
  • packages/state-machine/src/types.ts — ATMServices interface
  • deploy/nixos/hardware/upboard.nix — Sintra device paths and udev rules
  • lamassu-machine lib/id003/, lib/f56/ — Original JS drivers (v8.1.5 release line and earlier; see postscript below)

Postscript (2026-05-13)

This ADR is still in effect — TypeScript HAL in Electron remains the right call for the same reasons documented above. A few clarifications for readers in 2026 and beyond:

  1. Package naming. The HAL package was originally @lamassu/hal (per the line above in "Completed"). It was renamed to @bitSpire/hal during the project rename from lamassu-next to bitSpire (commit 924844f in the 2a sweep). All references to the package scope should now read @bitSpire/hal.

  2. Source-tree provenance, post-Lamassu license change. The "Original JS drivers" reference above refers to lamassu-machine's lib/id003/ and lib/f56/ directories in the v8.1.5 release line and earlier, which were published under a fully-open license. Lamassu Industries AG transitioned to a proprietary source-available license on 2024-01-26 (v8.1.6+). bitSpire incorporates no code from v8.1.6 or later; the protocol-level reimplementations we have are derived from v8.1.5 + protocol specs published by JCM, Fujitsu, etc. See CLAUDE.md → Provenance + legal status for the operating rules going forward.

  3. Implementation status. The "Remaining Work" list above is complete:

    • apps/machine runs on Electron (the original Tauri scaffolding was removed early on)
    • HAL is wired to the state machine via the ATMServices interface
    • Device configuration ships via deploy/nixos/hardware/upboard.nix + the services.bitspire NixOS module
    • First successful Sintra hardware integration test ran on 2026-05-13 (cash-out flow verified end-to-end against a regtest LNbits instance)