Tap-to-receive for the buy flow, the receive counterpart to #83's
cash-out tap-to-pay. A Bolt Card only emits an lnurlw withdraw voucher
(wrong direction to deposit into it), so the tap is used as an
authenticated identity (external_id + SUN p/c) to resolve the card
wallet's lnurlp/Lightning Address; the ATM then pays an invoice for the
payout over its existing nostr transport.
- electron/lnurl-pay.ts: resolveCardInvoice() — resolve card -> pay
target -> LUD-16/LUD-06 -> BOLT11. scanUrlToResolver() is the single
HTTPS-today / nostr-tomorrow transport seam. 13 tests.
- IPC lnurl:pay-card (main-process HTTPS to dodge renderer CORS) +
preload/electron.d.ts surface.
- stores/atm.ts: handleBoltCardReceive() settles via the existing
payInvoice -> PAYMENT_RECEIVED path; the one NFC listener now routes
the same tap by flow (cash-out pulls, cash-in receives).
- CashInView.vue: NFC status + dev tap input.
- docs/boltcard-receive-resolver.md: spec for the custom LNbits
/boltcards/api/v1/pay/<id> resolver endpoint (omni-private side).
Card issuance is unchanged — same NDEF/keys/external_id; receive is a
server-side reading of the same tap.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
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>
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.
Closes gap 2 from coord log 2026-06-01T18:30Z. The LNbits withdraw
extension's nostr-transport RPC now populates `link.lnurl` from
`settings.lnbits_baseurl` (aiolabs/withdraw#1 / commit e9d911e), so the
ATM no longer needs a separate HTTP URL on the wire to compose the
LNURL-withdraw callback itself.
What goes:
- `VITE_LNBITS_HTTP_URL` env var (renderer + Electron main)
- `lnbitsHttpUrl` field on `LightningConfig`, `RuntimeConfig`, and the
Window mirror in `src/types/electron.d.ts`
- The manual `${lnbitsHttpUrl}/withdraw/api/v1/lnurl/${unique_hash}`
composition in `generateLnurlWithdraw`
- The `encodeLnurl` bech32 helper in `lightning.ts` (LNbits returns
bech32-encoded; we just `.toUpperCase()` to match BOLT/LNURL convention)
- `@scure/base` dep from `apps/machine/package.json` (only used by the
removed helper; clink still uses it directly)
- The `lnbitsHttpUrl` option + `LNBITS_HTTP_URL=…` env var + boot echo
in `deploy/nixos/bitspire-atm.nix`
- Doc references in CLAUDE.md, README.md, deploy/nixos/README.md,
docs/architecture-comparison.md, and the lightning-check skill
What stays:
- `link.lnurl` consumption, with an explicit error if LNbits returns
null (which signals `LNBITS_BASEURL` is unset on the server side —
better to fail clearly than silently)
- The receiver-side bech32 uppercasing (LNbits returns lowercase per
the standard library)
Why this is a net win:
- Removes a config-drift surface — if LNbits's external URL moved
(DNS, port, reverse-proxy rewrite), every ATM in the field would
stop issuing redeemable LNURL-withdraw QRs until reconfigured.
Now LNbits derives its own URL from `settings.lnbits_baseurl`,
one source of truth.
- Removes an extra provisioning step. No more `LNBITS_HTTP_URL=…`
before running `provision-atm.sh`; the relay + server pubkey suffice.
- Removes the misleading boot echo that triggered the §`18:30Z`
smoke triage confusion ("LNbits HTTP: <url>" read like ATM-→-LNbits
connectivity, when it was only ever a URL embedded in customer QRs).
Also adds a `# pragma: allowlist secret` marker above the
`VITE_ATM_PRIVATE_KEY` doc block in `.env.example` so the global
secret scanner stops false-positiving on the documentation prose.
Workspace typecheck + 24/24 apps/machine tests still green.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Three breakages handled:
- hardware.opengl → hardware.graphics (renamed in 24.11). Touches
upboard.nix, douro.nix, batm3.nix, live.nix.
- vaapiIntel dropped — legacy pre-Broadwell driver, removed in
nixpkgs. UP Board (Cherry Trail) and OptiPlex 9030 (Haswell) both
use intel-media-driver, which stays.
- vaapiVdpau renamed → libva-vdpau-driver.
system.stateVersion stays 24.05 — convention is to never bump after
install. Existing Sintra and fresh flashes keep the 24.05 state
semantics; that's correct.
All 8 nixosConfigurations (4 models × {live,installed}) evaluate
clean with zero deprecation warnings on 24.11.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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>
deploy/nixos/README.md was the most-stale doc in the tree: it still
talked about a `lamassu-atm` systemd unit, `/opt/lamassu-atm` paths,
nixos-install with a non-existent `lamassu-atm` flake output, and an
scp-the-built-electron-bundle workflow that hasn't been the deploy
path for many months. Replaced with a rewrite that documents the
actual current pipeline:
- File layout: bitspire-atm.nix (not lamassu-atm.nix), live.nix,
hardware/{douro,batm3,upboard}.nix, and the udev / helper scripts
- Build pipeline: `nix build .#disk-image-<model>` and the four
flake-output flavours per model (live config, installed config,
iso, disk-image)
- Full Sintra walkthrough end-to-end: prep a flashing USB on the
dev box, boot Alpine live on the Sintra, identify the eMMC,
dd with count= to skip the trailing USB padding, repair the
GPT secondary header + grow root with parted, poweroff, boot,
provision via provision-atm.sh. Every quirk we hit during the
first real flash is now baked in (mdev for /dev nodes, parted
Fix prompt, lbu-style notes).
- Runtime layout cheat-sheet: /var/lib/bitspire/.env (0600
lamassu:lamassu), state.db, /etc/bitspire/config.env, etc.
- Common-operations playbook: re-provision, nixos-rebuild switch
over SSH with --use-remote-sudo (much faster than reflashing),
journalctl filtering, hardware-side health checks.
- NixOS module reference for services.bitspire, including the
LNbits-flavoured options (relayUrl, lnbitsServerPubkey,
lnbitsHttpUrl) instead of the retired lightningPubUrl.
- Sintra-specific gotchas section: eMMC-via-sdhci-acpi, the
ttyS4 dispenser placement, the ttyS1..3 phantom-node issue.
- Security-notes section updated to reflect passwordless sudo
enabled for nixos-rebuild deploys, and the implications.
Auto-upgrade behaviour explained explicitly (the ?ref=dev pin) so
contributors understand why production ATMs on main don't pick up
dev branch changes.
docs/ndebit-cash-in-flow.md: added a header banner flagging the
document as historical — cash-in on dev is LNURL-withdraw +
subscribe_payments push, not ndebit. Original content kept as a
reference for any future revival of nostr-native cash-in.
docs/clink-protocol.md: same treatment — flagged as dormant on dev,
explaining which pieces still apply (kind-21003 management) and
which are unused (kinds 21001/21002). Protocol reference content
left intact since the wire format is unchanged upstream.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Final rename commit covering user-facing copy and the docs that
describe current state. The mechanics of the rename are done after
this; the LNbits backend swap (phase 3) is the next concern.
Code branding strings (Lightning invoice descriptions):
apps/machine/src/services/lightning.ts
apps/machine/src/stores/atm.ts
docs/clink-protocol.md (example code blocks)
"Lamassu ATM Payment" → "bitSpire Payment"
"Lamassu ATM - Cash Out" → "bitSpire - Cash Out"
`Lamassu ATM - Buy ${n} sats`→ `bitSpire - Buy ${n} sats`
Top-level docs:
README.md, CLAUDE.md — title + intro + dir-tree references.
deploy/nixos/README.md — title + worktree-path commands.
docs/machine-installation.md — opening line carries the historical
note ("Lamassu Next" → "bitSpire"). The body still uses
`/opt/lamassu/` paths and the `lamassu-kiosk` systemd unit
because the dev branch is moving to NixOS disk-image flash
(phase 4) — this AppImage-sideload doc represents the legacy
deploy path. Leaving the LP/lamassu refs in there as part of
its historical context; a separate doc will describe the
NixOS path.
.claude/skills/nostr-check.md — header only.
DELIBERATELY left as "Lamassu Next" (pedagogical / historical):
- docs/adr/001-hal-architecture.md — frozen ADR; renaming
distorts the historical decision context.
- docs/architecture-comparison.md — deliberately contrasts
"lamassu-server" (prior) with "lamassu-next" (us at the time
of writing).
NOT done in this commit (deferred to LNbits/clean-up phase):
- docker/docker-compose.dev.yml container names
(lamassu-relay, lamassu-bitcoind, etc.) — these belong to the
LP-bearing dev stack that 3c/3d will significantly reshape.
Verified: pnpm typecheck clean (12/12 cached).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Use h-dvh instead of h-screen for mobile browser viewport
- Add ScrollArea component (shadcn-vue) to replace native scrollbars
- Move Hide Debug into debug bar row, show Show Debug only when off
- Responsive cash-out denomination grid (single col on mobile)
- Add safe-area padding for mobile gesture bar
- Add business model documentation
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- 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>
- Catalog all existing lamassu-machine drivers (7 validators, 5 dispensers,
3 printers, 4 scanners)
- Add "Port, don't rewrite" principle - leverage battle-tested existing code
- Update HAL structure to include all drivers to port
- Replace NV200 example with ID003 (default validator in lamassu-machine)
- Add Printer trait definition
- Update napi-rs bindings with ValidatorDriver and DispenserDriver enums
- Add configuration mapping for migrating from legacy lamassu-machine config
- Update milestone checklist with prioritized driver porting tasks
- Add driver complexity ranking to guide porting order
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Market context added:
- 95%+ activity is cash-out (selling Bitcoin for cash)
- Typical for remittance corridors, Bitcoin-paid workers
- Bill dispenser is REQUIRED for testing, not optional
Updated development priorities:
- Cash-out first, cash-in second
- Dispenser in minimum dev kit (~$655-855)
- Bill validator becomes secondary priority
Updated milestones:
- Priority 1: Cash-out with real dispenser
- Priority 2: Cash-in (mock validator OK initially)
- Priority 3: Advanced features (Cashu, etc.)
Phase 4 reorganized to implement cash-out before cash-in.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Hardware section now includes:
- One-way dev kit (cash-in only): ~$535-735
- Two-way dev kit (cash-in + cash-out): ~$955-1,555
- Bill dispenser options: Puloon LCDM-1000/2000, Fujitsu F53
- Sourcing tips for used equipment
HAL section now includes:
- BillDispenser trait definition
- Puloon LCDM RS-232 implementation
- napi-rs bindings for dispenser
- TypeScript usage examples for both validator and dispenser
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comprehensive redesign using Nostr for all ATM communication:
Lightning.Pub as Core Server:
- Replaces lamassu-server entirely
- Nostr-native account system wrapping LND
- Zero server config (no DNS/SSL/ports)
- CLINK native, one-line deployment
- Built-in liquidity management
Private Nostr Relay:
- NIP-42 authenticated relay (strfry/rnostr)
- Whitelist: ATM npubs + Operator npubs only
- Encrypted command/control channel
- Negentropy sync for offline reconciliation
Machine Identity:
- Each ATM has Nostr keypair (nsec/npub)
- Replaces client certificates
- Cryptographic authentication
- No PKI/CA required
Replacing SMS (KYC vector elimination):
- NIP-17 encrypted DMs for receipts
- No phone numbers collected
- User provides npub voluntarily
- Operator alerts via Nostr events
Event Schema:
- Kind 21001-21003: CLINK protocol
- Kind 30078: Machine status (replaceable)
- Kind 30079: Transaction records
- Kind 14: Encrypted receipt DMs
This is the logical extension of CLINK - if we're using
Nostr for payments, why not use it for everything?
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
BOLT12 problems (per @justin_shocknet):
- Onion messages add latency and failure probability
- Every hop is a point of failure (Tor-like)
- Normalizes mobile nodes (bad for Lightning)
- Redundant (LND keysend already exists)
- Astroturfed by NGOs with questionable motives
CLINK Offers advantages:
- Uses Nostr relays (commodity, trustless via NIP-44)
- No HTTP callbacks, WebSockets, or onion messages
- Keys decoupled from Lightning node identity
- Already working: ShockWallet, Lightning.Pub, Stacker News
- More traction in weeks than BOLT12 in years
Updated:
- Protocol stack recommendation
- New components (clink-handler)
- Implementation roadmap
- References section
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comprehensive rethinking of the project vision:
Core Principles:
- KYC-free: No identity collection
- Lightning-native: Security through protocol
- Self-custodial: Operator and user control keys
- Privacy by default: Minimize data retention
- Autonomous machines: Reduce server dependency
Key Technical Decisions:
- LNbits as primary Lightning backend
- Cashu ecash for offline capability and privacy
- LNURL-withdraw/pay for user-friendly flows
- BOLT12 offers for static payment codes
- NFC BOLT cards for tap-to-withdraw
- Fedimint option for community custody
Architecture Options:
- A: Lean Server (recommended starting point)
- B: Serverless Machine (maximum autonomy)
- C: Fedimint Community Model
Assessment: 60%+ of existing lamassu-server is compliance code.
Recommendation: Rebuild core, reuse hardware drivers.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Documents e-ink options for testing:
- Modos Paper (75Hz, open-source FPGA) - ships Jan 2026
- DASUNG Paperlike 253 (33Hz, frontlight)
- Waveshare development boards
- Industrial/outdoor options (SEEKINK, Geniatech)
Includes honest assessment of trade-offs and recommended
use cases (outdoor, solar-powered, idle signage).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Comprehensive document covering:
- Compute: Raspberry Pi CM5 (recommended), Pine64 RISC-V (future)
- Bill validators: ITL NV200 with eSSP protocol, MEI Cashflow
- Bill dispensers: Puloon LCDM series, Fujitsu F53/F56
- Displays: Elo I-Series, Waveshare, Faytech open-frame
- Printers: ESC/POS compatible thermal printers
- NFC: ACR122U with libnfc, PN532 modules
- Protocol support matrix and driver migration strategy
- Bill of materials for MVP and full-featured ATMs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Benefits of shadcn-vue:
- Copy components to repo (you own the code)
- Tailwind-native (no style conflicts)
- Radix Vue primitives (a11y-first)
- Much smaller bundle (~15kb vs ~80kb tree-shaken)
- Full control over styling and behavior
Updated:
- Admin UI: Complete shadcn-vue examples with TanStack Table
- Machine UI: Note about shared shadcn-vue components
- Modernization plan: Updated architecture diagram and bundle sizes
- Bundle target: ~60kb total (down from ~120kb with PrimeVue)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update architecture diagram to show Vue 3 for both admin and machine
- Replace React decision with Vue 3 unification strategy
- Update machine UI section to include Vue 3 + Tauri
- Add Vue 3 to trade-offs table
- Link to new detailed migration docs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Migrate from React 18 + MUI to Vue 3 for consistency with machine UI:
- PrimeVue for components (tree-shakeable vs MUI's 300kb)
- tRPC replacing Apollo GraphQL (~5kb vs ~50kb)
- Pinia for state (same as machine UI)
- Shared ui-shared package for common components
Benefits:
- Single framework across all UIs
- ~70% bundle size reduction (400kb → 120kb)
- End-to-end type safety with tRPC
- Shared composables between admin and machine
Includes:
- Complete project structure
- PrimeVue integration with Tailwind
- tRPC client setup
- Passkey authentication flow
- 4-phase migration strategy
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Replaces legacy vanilla JS + jQuery with modern stack:
- Vue 3 with Composition API
- TypeScript throughout
- Pinia for state management
- Vite for building
- Vue I18n for localization
Includes:
- Complete project structure
- Core component examples (App.vue, stores, composables)
- WebSocket integration pattern
- i18n strategy preserving existing translations
- 4-phase migration plan
- Testing patterns with Vitest
- Performance targets (<120kb total bundle)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add lamassu-server, lamassu-machine, lamassu-install as submodules
- Add CLAUDE.md with development guidance
- Add docs/modernization-plan.md with 2026 refactoring roadmap
- Add .gitignore for Node.js/Nix development
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>