No description
  • TypeScript 56.4%
  • Vue 11.2%
  • JavaScript 9.4%
  • Shell 9%
  • Nix 8.1%
  • Other 5.9%
Find a file
Padreug 082f738ffa fix(deploy): console=tty0 was being dropped on the Pi 5
raspberry-pi-5.nix set `boot.kernelParams = lib.mkDefault [ "console=tty0" ]`
to keep the serial console off the GPIO UART so a validator can own it. It
never took effect: kernelParams is list-merged, and only definitions at the
highest priority survive — nixpkgs defines loglevel/lsm at normal priority,
so the mkDefault list was discarded wholesale. Effective params on
rpi5-installed were `[ "loglevel=4" "lsm=landlock,yama,bpf" ]`, no console=
at all, which makes the kernel fall back to the device tree's stdout-path:
that same UART.

Drop the mkDefault so the entry merges. Verified by evaluating
config.boot.kernelParams before/after.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_013A6683cCHnQxFUosx1krY4
2026-09-24 21:43:53 +02:00
.claude/skills refactor(machine): drop VITE_LNBITS_HTTP_URL — lnurl now arrives populated from LNbits (#57 gap 2) 2026-06-01 20:33:28 +02:00
apps/machine fix(nfc): auto-recover a wedged CCID reader via USB power-cycle 2026-08-06 19:51:39 +02:00
deploy fix(deploy): console=tty0 was being dropped on the Pi 5 2026-09-24 21:43:53 +02:00
docker feat(docker): streamline regtest dev environment 2026-03-10 01:31:51 -04:00
docs feat(machine): Bolt Card (NFC) tap-to-receive on cash-in 2026-08-06 00:30:13 +02:00
nix feat(deploy): build nfc-pcsc's native pcsclite addon for Electron (mkAtmApp) 2026-08-05 04:55:30 +02:00
packages feat(hal): add Pyramid Apex RS-232 bill-validator driver 2026-08-16 21:22:47 +02:00
scripts test: add LNURL-withdraw Nostr RPC test scripts 2026-03-07 13:02:48 -05:00
.devenv.flake.nix feat(docker): add dev.sh with auto-funding and ATM app setup 2026-02-15 14:19:16 -05:00
.gitignore chore: gitignore nix results, sqlite DBs, compiled JS 2026-03-02 13:49:50 -05:00
.prettierrc feat(docker): add dev.sh with auto-funding and ATM app setup 2026-02-15 14:19:16 -05:00
CLAUDE.md docs: correct the lamassu-machine licensing boundary to commit c0b69d1 2026-07-04 01:00:12 +02:00
devenv.lock feat(docker): add dev.sh with auto-funding and ATM app setup 2026-02-15 14:19:16 -05:00
devenv.nix feat(docker): display Zeus lndconnect as QR code 2026-02-15 14:38:40 -05:00
devenv.yaml feat(docker): add dev.sh with auto-funding and ATM app setup 2026-02-15 14:19:16 -05:00
flake.lock feat(deploy): add aarch64 Raspberry Pi 5 target (sd-image) 2026-08-16 21:30:53 +02:00
flake.nix feat(deploy): split rpi5 target into installed + image variants 2026-08-17 08:41:52 +02:00
package.json refactor(rename): root + flake output names → bitSpire/bitspire 2026-06-01 19:08:03 +02:00
pnpm-lock.yaml feat(machine): NFC Bolt Card reader driver + IPC (main process) 2026-08-05 04:42:07 +02:00
pnpm-workspace.yaml feat(docker): add dev.sh with auto-funding and ATM app setup 2026-02-15 14:19:16 -05:00
README.md refactor(machine): drop VITE_LNBITS_HTTP_URL — lnurl now arrives populated from LNbits (#57 gap 2) 2026-06-01 20:33:28 +02:00
tsconfig.json refactor: drop Lightning.Pub backend; LNbits-only path (3d) 2026-06-01 19:08:03 +02:00
turbo.json feat(docker): add dev.sh with auto-funding and ATM app setup 2026-02-15 14:19:16 -05:00

bitSpire

A Nostr-native Lightning ATM. KYC-free, open source, auditable. Talks to its Lightning backend over the nostr-native-transport (kind-21000 NIP-44 v2) on a relay — never HTTP — so the kiosk has no admin tokens to leak and no API surface to attack.

Originally lamassu-next. Renamed during the LNbits-backend transition on the dev branch (commits leading up to 2026-05-13). Production ATMs (batm3, douro) still run from main against Lightning.Pub until cutover; this README describes the dev branch state.

What the ATM actually does

Flow Customer side ATM side
Cash-out (customer pays ATM, gets cash) scans BOLT11 invoice, pays from any LN wallet lnbits.createInvoice() over nostr → subscribe_payments({payment_hash}) push fires on settlement → dispense
Cash-in (customer hands ATM cash, gets sats) scans LNURL-withdraw QR, redeems with any LN wallet that supports LNURL-w lnbits.createWithdrawLink({uses:1}) over nostr → subscribe_payments({tag:"withdraw", link_id}) push fires when LNbits settles → mark complete

No HTTP to the Lightning backend. No admin tokens on the kiosk. The ATM's nostr private key is its credential — LNbits auto-creates the wallet on first contact via the signature (see aiolabs/lnbits#9).

Prerequisites

  • Nix with flakes enabled
  • devenv
  • Docker + Docker Compose
  • A running LNbits instance with the nostr-native-transport branch built in. The local dev compose lives at ~/dev/local/docker/regtest — that ships an LNbits with the transport pre-enabled and the relevant extensions (withdraw, lnurlp, nostrrelay) installed.

The nostrrelay extension inside LNbits is what the ATM connects to — there is no separate strfry/khatru container in the dev compose. The relay URL is ws://<host>:5001/nostrrelay/test.

Quick Start

# 1. Clone
git clone ssh://forgejo@git.atitlan.io/aiolabs/lamassu-next.git
cd lamassu-next        # repo name kept for now — rename to bitSpire is a follow-up
git checkout dev

# 2. Enter the dev environment
devenv shell

# 3. Install JS deps
pnpm install

# 4. Start the regtest stack (bitcoind, LNDs, LNbits with nostr-transport, relay)
cd ~/dev/local/docker/regtest && ./start-regtest
docker logs regtest-lnbits-1 | grep 'Public key (share this)'
# → copy that pubkey, you'll need it as VITE_LNBITS_SERVER_PUBKEY

# 5. Configure the machine app for the dev LNbits
cat > apps/machine/.env <<EOF
VITE_RELAY_URL=ws://localhost:5001/nostrrelay/test
VITE_LNBITS_SERVER_PUBKEY=<paste pubkey from step 4>
VITE_ATM_PRIVATE_KEY=$(openssl rand -hex 32)
EOF

# 6. Run the kiosk in browser dev mode
cd apps/machine && pnpm dev

The kiosk should come up at http://localhost:5173, log [Lightning] LNbits client initialized, and report a wallet id once LNbits auto-creates one for the ATM's pubkey.

Architecture

bitSpire/
├── apps/
│   └── machine/          # Electron + Vue 3 ATM kiosk
├── packages/
│   ├── nostr-client/     # NIP-01 relay client, NIP-44 v2 encryption
│   ├── lnbits/           # LnbitsClient — talks to LNbits over kind-21000 transport
│   ├── clink/            # CLINK protocol (kind-21001/2/3) — still in tree as a
│   │                     #   reference; unused on dev since the LNbits backend
│   │                     #   does cash-in via LNURL-withdraw and cash-out via
│   │                     #   BOLT11, both of which subsume CLINK's role
│   ├── hal/              # Hardware abstraction (JCM iVIZION validator, F56 dispenser)
│   ├── state-machine/    # XState v5 state machine driving cash-out + cash-in
│   ├── cashu/            # Cashu ecash (placeholder)
│   └── ui-shared/        # Shared Vue components (placeholder)
└── deploy/nixos/         # NixOS module + provisioning script for Sintra/tejo/douro/batm3

packages/lightning/ (the Lightning.Pub RPC client) was removed on dev — see git log packages/lightning on main for the historical sources.

Deploying to real hardware

The Sintra/tejo/douro/batm3 disk-image pipeline lives in flake.nix + deploy/nixos/. See deploy/nixos/README.md for the full flow; the abbreviated path:

# Build the disk image for a Sintra
nix build .#disk-image-sintra
# → result/nixos.img

# Flash to a USB stick
sudo dd if=result/nixos.img of=/dev/sdX bs=4M status=progress conv=fsync && sync

# Boot Sintra from the USB, dd onto the eMMC from inside Alpine live (see
# deploy/nixos/README.md), then provision the .env from the dev box:
bash deploy/nixos/provision-atm.sh <sintra-lan-ip>

The auto-upgrade timer (flake.nix:152-160) pulls dev daily at 04:00, so the Sintra stays in sync with whatever's on the dev branch. Production ATMs run from main and are unaffected.

Documentation

Document Description
docs/machine-installation.md Step-by-step Sintra install: build, flash, provision
deploy/nixos/README.md NixOS module options, runtime config layout, hardware variants
docs/architecture-comparison.md Nostr-native ATM vs traditional lamassu-server
docs/device-configuration.md Validator/dispenser hardware configuration
docs/business-model.md Deployment economics
docs/adr/001-hal-architecture.md HAL design decision record
docs/clink-protocol.md CLINK protocol reference — historical, no longer wired on dev
docs/ndebit-cash-in-flow.md Pre-LNbits cash-in flow — historical, replaced by LNURL-withdraw + subscribe_payments push
CLAUDE.md Development guidelines and code style (read this if using Claude Code)

Contributing

See CLAUDE.md for development guidelines, package layout conventions, and code style.

Acknowledgements

bitSpire's hardware drivers (JCM iVIZION / ID003, MEI EBDS, Fujitsu F56, etc.) and the cash-flow state machine derive from prior art first published as open source by Lamassu Industries AG in the lamassu-machine and lamassu-server repositories, up to and including the v8.1.5 release line — the last published under a fully-open license. bitSpire wouldn't exist without that foundation, and we're grateful for the years of operational hardening that went into it.

Lamassu Industries AG transitioned to a proprietary, source-available license on 2024-01-26, with v8.1.6 and subsequent releases gated behind a paid Operator Support Agreement. bitSpire incorporates no code from v8.1.6 or later, is an independent project, and is not affiliated with or endorsed by Lamassu Industries AG.

License

AGPL-3.0 (matches LNbits, which we link against).