- @lamassu/clink import examples → @bitSpire/clink, the package's real name. - machine-installation.md: the service user is `bitspire`, not `lamassu` (renamed in configuration.nix long ago; the doc never followed). - README: clone aiolabs/bitspire, not lamassu-next; the fleet sentence claiming batm3/douro run `main` against Lightning.Pub was stale. - nostr-check skill: table headers say bitSpire. - hal-check skill: the boundary is c0b69d1, not v8.1.5 (CLAUDE.md corrected this 2026-07-04; the skill kept asserting the wrong tag), and the "forbidden operations" now reflect the recorded permission — reference over port, name the source commit — plus a rule born of the GTQ window: no value table without a test over it. Deliberately kept: every `aiolabs/lamassu-next#NN` issue citation, the provenance sections, "Ported from lamassu-machine" driver headers, and the hardware names "Lamassu Sintra/Tejo/Douro" — those are the machines.
124 lines
7 KiB
Markdown
124 lines
7 KiB
Markdown
# 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). Every live machine now runs `dev` against LNbits; `main` is the Lightning.Pub-era history (see CLAUDE.md → Branch model).
|
|
|
|
## 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](https://git.atitlan.io/aiolabs/lnbits/issues/9)).
|
|
|
|
## Prerequisites
|
|
|
|
- [Nix](https://nixos.org/download.html) with flakes enabled
|
|
- [devenv](https://devenv.sh/getting-started/)
|
|
- 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
|
|
|
|
```bash
|
|
# 1. Clone
|
|
git clone ssh://forgejo@git.atitlan.io/aiolabs/bitspire.git
|
|
cd bitspire
|
|
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:
|
|
|
|
```bash
|
|
# 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](docs/machine-installation.md) | Step-by-step Sintra install: build, flash, provision |
|
|
| [deploy/nixos/README.md](deploy/nixos/README.md) | NixOS module options, runtime config layout, hardware variants |
|
|
| [docs/architecture-comparison.md](docs/architecture-comparison.md) | Nostr-native ATM vs traditional lamassu-server |
|
|
| [docs/device-configuration.md](docs/device-configuration.md) | Validator/dispenser hardware configuration |
|
|
| [docs/business-model.md](docs/business-model.md) | Deployment economics |
|
|
| [docs/adr/001-hal-architecture.md](docs/adr/001-hal-architecture.md) | HAL design decision record |
|
|
| [docs/clink-protocol.md](docs/clink-protocol.md) | CLINK protocol reference — historical, no longer wired on dev |
|
|
| [docs/ndebit-cash-in-flow.md](docs/ndebit-cash-in-flow.md) | Pre-LNbits cash-in flow — historical, replaced by LNURL-withdraw + subscribe_payments push |
|
|
| [CLAUDE.md](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`](https://github.com/lamassu/lamassu-machine) and [`lamassu-server`](https://github.com/lamassu/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](https://blog.lamassu.is/updates-to-our-lamassu-software-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).
|