docs: refresh README + CLAUDE.md for LNbits-backend bitSpire on dev
Both top-level docs were stale after the lamassu-next → bitSpire +
Lightning.Pub → LNbits migration shipped on this branch. They still
listed @lamassu/* package scopes, treated Lightning.Pub as the backend,
described cash-in as ndebit/CLINK, and pointed at removed packages.
README.md: rewritten end-to-end. Calls out the branch model (main vs
dev) so contributors don't accidentally affect production ATMs.
Documents the actual cash-out (BOLT11 + subscribe_payments by hash)
and cash-in (LNURL-withdraw + subscribe_payments by tag/link_id) wire
flows. Quick-start uses the dev compose's bundled LNbits with
nostr-transport + the LNbits-internal nostrrelay extension (the relay
endpoint is ws://<host>:5001/nostrrelay/test — no separate strfry
container, this was a pitfall during Sintra provisioning). Disk-image
deployment summary points at deploy/nixos/README for full details.
CLAUDE.md: rewritten to describe dev-branch reality. Lists actual
package names (@bitSpire/*), notes packages/lightning was deleted in
3d, calls out the LightningBackend adapter pattern in services/
lightning.ts, and gives an env-var reference table + a wire-envelope
crib for kind-21000. Sintra-specific hardware notes (UART layout,
initrd modules for the ACPI eMMC controller) bake in everything we
hard-learned during the first flash.
Both docs now include an Acknowledgements / Provenance section that:
- credits Lamassu Industries AG's open-source lamassu-machine /
lamassu-server (the v8.1.5 release line) as the prior art the
HAL drivers and state machine derive from — bitSpire wouldn't
exist without that foundation
- explicitly states Lamassu transitioned to a proprietary,
source-available "Appendix A SLA" on 2024-01-26 with v8.1.6+
gated behind a paid OSA subscription, and that bitSpire
incorporates no code from v8.1.6 or later
- declares bitSpire independent of Lamassu Industries AG
- in CLAUDE.md specifically: a hard rule that future contributors
(or future Claude runs) must not pull / port / copy code from
lamassu-machine at v8.1.6+; only the 8.1.5 tree is in-scope
License clarification: AGPL-3.0 (matches LNbits, which we link
against) — dropped the earlier "matches LNbits + lamassu-machine
pedigree" phrasing since lamassu-machine is no longer under a free
license.
References:
https://blog.lamassu.is/updates-to-our-lamassu-software-license/
https://github.com/lamassu/lamassu-machine
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2551c6fcf8
commit
bac130dbf1
2 changed files with 236 additions and 437 deletions
216
README.md
216
README.md
|
|
@ -1,75 +1,59 @@
|
|||
# bitSpire
|
||||
|
||||
A Nostr-native Lightning ATM system. KYC-free, open source, auditable.
|
||||
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.
|
||||
|
||||
> **Considering adopting this approach?** See [Architecture Comparison](docs/architecture-comparison.md) for a detailed comparison with traditional lamassu-server/machine.
|
||||
> 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](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](https://docs.docker.com/get-docker/) and Docker Compose
|
||||
- [Lightning.Pub](https://github.com/shocknet/Lightning.Pub) cloned to `~/dev/shocknet/Lightning.Pub`
|
||||
- Regtest environment at `~/dev/local/docker/regtest` (provides bitcoind + LND nodes)
|
||||
- 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 the repository
|
||||
# 1. Clone
|
||||
git clone ssh://forgejo@git.atitlan.io/aiolabs/lamassu-next.git
|
||||
cd bitSpire
|
||||
cd lamassu-next # repo name kept for now — rename to bitSpire is a follow-up
|
||||
git checkout dev
|
||||
|
||||
# 2. Enter the development environment
|
||||
# 2. Enter the dev environment
|
||||
devenv shell
|
||||
|
||||
# 3. Install dependencies
|
||||
# 3. Install JS deps
|
||||
pnpm install
|
||||
|
||||
# 4. Start regtest infrastructure with auto-funding
|
||||
./docker/dev.sh up --fund
|
||||
# 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. Check status
|
||||
./docker/dev.sh status
|
||||
# 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_LNBITS_HTTP_URL=http://localhost:5001
|
||||
VITE_ATM_PRIVATE_KEY=$(openssl rand -hex 32)
|
||||
EOF
|
||||
|
||||
# 6. Run the kiosk in browser dev mode
|
||||
cd apps/machine && pnpm dev
|
||||
```
|
||||
|
||||
If status shows all services running and ATM funded, you're ready to develop!
|
||||
|
||||
## Development Commands
|
||||
|
||||
### dev.sh (Regtest Environment)
|
||||
|
||||
```bash
|
||||
./docker/dev.sh <command> [options]
|
||||
```
|
||||
|
||||
| Command | Description |
|
||||
| ---------------- | -------------------------------------- |
|
||||
| `up` | Start regtest + Lightning.Pub + relay |
|
||||
| `up --fund` | Start and auto-fund ATM with 100k sats |
|
||||
| `up --machine` | Start and launch ATM app |
|
||||
| `down` | Stop all services |
|
||||
| `status` | Show service status and ATM balance |
|
||||
| `atm` | Launch ATM application (Electron) |
|
||||
| `zeus` | Show Zeus wallet connection QR code |
|
||||
| `fund [sats]` | Fund ATM app owner (default: 100000) |
|
||||
| `mine [blocks]` | Mine regtest blocks (default: 1) |
|
||||
| `logs [service]` | Follow service logs |
|
||||
| `reset` | Stop services and clear all state |
|
||||
|
||||
### pnpm Scripts
|
||||
|
||||
| Command | Description |
|
||||
| ------------ | ----------------------------------- |
|
||||
| `pnpm dev` | Start machine app (Vite + Electron) |
|
||||
| `pnpm build` | Build all packages |
|
||||
| `pnpm test` | Run unit tests |
|
||||
|
||||
### Machine App
|
||||
|
||||
```bash
|
||||
cd apps/machine
|
||||
pnpm dev # Start Electron app with hot reload
|
||||
```
|
||||
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
|
||||
|
||||
|
|
@ -78,110 +62,64 @@ bitSpire/
|
|||
├── apps/
|
||||
│ └── machine/ # Electron + Vue 3 ATM kiosk
|
||||
├── packages/
|
||||
│ ├── nostr-client/ # Nostr client (NIP-01, NIP-42, NIP-44)
|
||||
│ ├── clink/ # CLINK protocol (ndebit, noffer)
|
||||
│ ├── lightning/ # Lightning.Pub RPC client
|
||||
│ ├── hal/ # Hardware Abstraction Layer (bill validators, dispensers)
|
||||
│ ├── state-machine/ # XState v5 ATM state machine
|
||||
│ ├── 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)
|
||||
└── docker/ # Development infrastructure (dev.sh, regtest)
|
||||
└── deploy/nixos/ # NixOS module + provisioning script for Sintra/tejo/douro/batm3
|
||||
```
|
||||
|
||||
## Infrastructure Services
|
||||
`packages/lightning/` (the Lightning.Pub RPC client) was removed on `dev` — see `git log packages/lightning` on `main` for the historical sources.
|
||||
|
||||
The development environment uses a shared regtest stack from `~/dev/local/docker/regtest/`:
|
||||
## Deploying to real hardware
|
||||
|
||||
| Service | Port | Description |
|
||||
| ------------------ | ----- | --------------------------------- |
|
||||
| strfry | 7777 | Nostr relay (ws://localhost:7777) |
|
||||
| bitcoind | 18443 | Bitcoin regtest node (shared) |
|
||||
| lnd-1 | 10001 | Hub node (funds lnd-3 and lnd-4) |
|
||||
| lnd-3 | 10003 | Payment source for ATM funding |
|
||||
| lnd-4 | 10004 | Lightning.Pub's backend node |
|
||||
| Lightning.Pub | 1776 | Nostr-native accounts API |
|
||||
| Withdraw Extension | 1777 | LNURL-withdraw for cash-in |
|
||||
| PostgreSQL | 5432 | Lightning.Pub database |
|
||||
|
||||
**Automatic channel setup**: When `dev.sh up` runs, it automatically:
|
||||
|
||||
1. Ensures lnd-1 has funds (mines blocks if needed)
|
||||
2. Opens a channel from lnd-1 → lnd-4 (so Lightning.Pub can receive payments)
|
||||
3. Opens a channel from lnd-1 → lnd-3 (so lnd-3 can pay invoices for funding)
|
||||
4. Mines blocks for channel confirmation and graph propagation
|
||||
|
||||
This makes the environment work from a clean Docker slate without manual intervention.
|
||||
|
||||
## Testing Cash-In Flow
|
||||
|
||||
After starting the environment with `./docker/dev.sh up --fund`:
|
||||
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
|
||||
# Start the ATM app
|
||||
cd apps/machine
|
||||
pnpm dev
|
||||
# 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>
|
||||
```
|
||||
|
||||
1. Click "Cash In" on the ATM idle screen
|
||||
2. Insert simulated bills (dev mode)
|
||||
3. Click "Done Inserting"
|
||||
4. Choose payment method:
|
||||
- **CLINK** - Scan with Shock Wallet (ndebit protocol)
|
||||
- **LNURL** - Scan with any Lightning wallet (Phoenix, Zeus, etc.)
|
||||
5. Claim the withdrawal in your wallet
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Issues
|
||||
|
||||
**Services won't start:**
|
||||
|
||||
```bash
|
||||
./docker/dev.sh reset
|
||||
./docker/dev.sh up --fund
|
||||
```
|
||||
|
||||
**ATM not funded / "not enough balance":**
|
||||
|
||||
```bash
|
||||
./docker/dev.sh fund 100000
|
||||
```
|
||||
|
||||
**Port already in use:**
|
||||
|
||||
```bash
|
||||
./docker/dev.sh down
|
||||
# Kill any orphan processes on ports 1776, 1777, 7777
|
||||
./docker/dev.sh up
|
||||
```
|
||||
|
||||
**LNURL-withdraw fails:**
|
||||
|
||||
- Ensure withdraw extension is running (`./docker/dev.sh status`)
|
||||
- Check ATM app has valid `VITE_APP_ID` in `apps/machine/.env`
|
||||
- Verify app owner is funded (not just app_user balance)
|
||||
|
||||
## Key Concepts
|
||||
|
||||
- **CLINK**: Protocol for Lightning payments over Nostr (ndebit, noffer)
|
||||
- **ndebit**: Customer-initiated debit authorization (cash-in primary method)
|
||||
- **LNURL-withdraw**: Standard Lightning withdrawal link (cash-in alternative)
|
||||
- **Lightning.Pub**: Nostr-native account system wrapping LND
|
||||
- **NIP-44**: Encryption standard for private Nostr messages
|
||||
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 |
|
||||
| ---------------------------------------------------------- | ----------------------------------------------- |
|
||||
| [Architecture Comparison](docs/architecture-comparison.md) | Nostr-native vs traditional lamassu-server |
|
||||
| [ndebit Cash-In Flow](docs/ndebit-cash-in-flow.md) | Technical walkthrough of cash-in implementation |
|
||||
| [Troubleshooting](packages/lightning/TROUBLESHOOTING.md) | Lightning.Pub integration issues and solutions |
|
||||
| [CLAUDE.md](CLAUDE.md) | Development guidelines and code style |
|
||||
| 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 and code style.
|
||||
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
|
||||
|
||||
MIT
|
||||
AGPL-3.0 (matches LNbits, which we link against).
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue