Commit graph

419 commits

Author SHA1 Message Date
Patrick Mulligan
021aa6e168 fix(machine): wire up LNURL-withdraw completion callback
The onPaymentSuccess callback was passed to createATMServices but never
used, causing LNURL-withdraw claims to go undetected. Store the callback
so startLnurlCompletionPolling can trigger it when a withdrawal completes.

Refs: #24

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-14 15:39:46 -05:00
Patrick Mulligan
0c8a130413 feat(machine): add LNURL-withdraw as alternative payment method in cash-in
Add LNURL-withdraw (LUD-03) as an alternative to ndebit for the cash-in
flow. Users can now choose between:
- ndebit (CLINK) - for ShockWallet and compatible apps
- LNURL-withdraw - for any LNURL-compatible wallet (Zeus, Phoenix, etc.)

Changes:
- CashInView.vue: Add tab UI to switch between ndebit and LNURL QR codes
- atm.ts: Add generateLnurlWithdraw store action
- lightning.ts: Implement LNURL-withdraw service calling extension API
- Add @scure/base dependency for LNURL encoding

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-14 15:28:46 -05:00
Patrick Mulligan
940afe3a65 fix: correct bill denomination unit conversion and exchange rate
- Fix state machine to convert bill denominations from dollars to cents
  (e.g., $20 bill → 2000 cents in fiatAmount)
- Use fixed 1,000 sats/$ rate for development (~$100k BTC)
- Update test to expect correct cents value

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 09:24:30 -05:00
Patrick Mulligan
5ef43fb036 chore: remove Lamassu branding from ATM UI
Replace brand-specific text with generic "Bitcoin ATM" heading
and simplified thank you messages.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 09:10:43 -05:00
Patrick Mulligan
595ac3b863 feat: implement ndebit debit approval with single-use protection
Implement the complete ATM-side ndebit (cash-in) flow with single-use
protection to prevent double-spending from the same QR code.

Key features:
- Debit approval service subscribes to GetLiveDebitRequests from Lightning.Pub
- Session-based validation: each ndebit QR is valid for one use only
- Amount decoded from BOLT11 invoice when not provided in message
- Atomic session marking prevents race conditions
- Triple protection: event ID tracking, invoice tracking, session status

Changes:
- apps/machine/src/services/lightning.ts: Add debit approval service with
  session management, BOLT11 amount decoding, and RespondToDebit approval
- packages/state-machine: Add cashInSessionId to context and generation
- docs/ndebit-cash-in-flow.md: Comprehensive documentation of the flow,
  architecture diagrams, and troubleshooting guide

Infrastructure improvements:
- Auto-miner service for regtest (keeps LND synced during testing)
- Electron .env file loading for runtime configuration
- Preload script compilation for Electron IPC

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-31 08:12:34 -05:00
Patrick Mulligan
e185947259 fix(clink): align CLINK implementation with protocol spec
Reviewed the CLINK spec from the shocknet/clink repository and fixed
several compliance issues:

## noffer TLV encoding (NIP-19 style)
- Fixed TLV numbers: pubkey=0, relay=1, offerId=2, priceType=3, amount=4, currency=5
- Added offerId (TLV 2) and currency (TLV 5) fields to noffer encoding
- Updated CLINKOffer interface to use offerId instead of description

## Error codes
- Split error codes into OfferErrorCode (Kind 21001) and GFYCode (Kind 21002/21003)
- Added all error codes per CLINK spec with proper numeric values
- Deprecated CLINKErrorCode alias for backward compatibility

## CLINK events
- Added mandatory clink_version tag ["clink_version", "1"] to all CLINK events
- Validate clink_version tag when receiving events
- Switched to NIP-44 v2 encryption for all CLINK events (21001-21003)

## Debit requests (Kind 21002)
- Fixed DebitRequest to include bolt11 field for payment requests
- Split into DebitPaymentRequest (with bolt11) and DebitBudgetRequest
- Added isDebitPaymentRequest type guard

## Client API changes
- Renamed requestDebit to requestDebitPayment (requires bolt11 invoice)
- Added requestDebitBudget for recurring budget authorization
- Updated createOffer signature to use offerId instead of description

## apps/machine updates
- Updated lightning.ts to use OfferErrorCode enum values
- Updated atm.ts to generate invoice before debit request

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 13:31:42 -05:00
Patrick Mulligan
e816f50363 docs: add CLINK protocol documentation with examples
Comprehensive documentation for the CLINK protocol including:
- Kind 21001 (Offer), 21002 (Debit), 21003 (Manage) event types
- noffer and ndebit bech32 encoding formats with TLV fields
- Flow diagrams for ATM cash-out and cash-in scenarios
- JSON examples for all event types including error responses
- ATM-specific code examples using @lamassu/clink library
- Security considerations and related NIPs

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 11:22:31 -05:00
Patrick Mulligan
56b1d594d7 feat: add runtime config via Electron IPC
- Add .env.example with all configuration options documented
- Add get-config IPC handler in Electron main process
- Expose getConfig() in preload for renderer access
- Update lightning.ts to load config at runtime in Electron
- Fall back to Vite build-time env vars for browser dev mode
- Add TypeScript declarations for electronAPI

This allows the same build to be deployed to different machines
with different configurations (relay URL, Lightning.Pub pubkey, etc.)
set via environment variables at runtime.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 11:00:12 -05:00
Patrick Mulligan
c604aa501c feat: externalize Lightning.Pub configuration to environment variables
- Replace hardcoded DEV_CONFIG with environment variable loading
- Add VITE_RELAY_URL for Nostr relay WebSocket URL
- Add VITE_LIGHTNING_PUB_PUBKEY for Lightning.Pub's Nostr pubkey (required)
- Add VITE_LIGHTNING_PUB_API_URL for HTTP API
- Add VITE_ATM_PRIVATE_KEY for persistent ATM identity
- Generate ephemeral identity if no private key configured
- Update machine installation docs with correct env var names

Environment variables:
- VITE_RELAY_URL (default: ws://localhost:7777)
- VITE_LIGHTNING_PUB_PUBKEY (required)
- VITE_LIGHTNING_PUB_API_URL (default: http://localhost:1776)
- VITE_ATM_PRIVATE_KEY (optional, generates ephemeral if not set)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 10:45:49 -05:00
Patrick Mulligan
6371244b46 docs: add machine installation guide
Comprehensive deployment documentation for Sintra including:
- Build instructions
- AppImage and unpacked deployment options
- Environment variable configuration
- Systemd service setup for auto-start
- Hardware verification steps
- Troubleshooting guide

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 10:36:41 -05:00
Patrick Mulligan
55d963904c chore: fix electron-builder and add package metadata
- Add Python with setuptools to devenv.nix for node-gyp compatibility
- Remove Tauri dependencies from devenv (replaced by Electron)
- Add author, homepage, license to package.json for electron-builder
- Simplify Linux target to AppImage only

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 10:31:47 -05:00
Patrick Mulligan
a8cfaaf66f feat: add device configuration for HAL drivers (#20)
- Create src/config/device.ts with machine presets (Sintra, Gaia)
- Support environment variable overrides (VITE_LAMASSU_*)
- Add initializeForProduction() convenience method to ATM store
- Document configuration in docs/device-configuration.md

Default Sintra configuration:
- Validator: /dev/ttyJ5 (ID003 protocol)
- Dispenser: /dev/ttyJ7 (F56)
- Cassettes: [{ denomination: 20, count: 50 }]

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 21:25:27 -05:00
Patrick Mulligan
c26b6f6169 feat: convert apps/machine from Tauri to Electron (#18)
- Create electron/main.ts with BrowserWindow (1080x1920, kiosk mode)
- Create electron/preload.ts with contextBridge API
- Add electron/tsconfig.json for main process compilation
- Update package.json: remove Tauri deps, add Electron + electron-builder
- Update vite.config.ts: remove Tauri-specific build config
- Update .gitignore: remove Tauri entries, add Electron build outputs
- Delete src-tauri/ directory and root Cargo.toml
- Update CLAUDE.md to reflect Electron architecture

The Vue frontend is unchanged - works identically in Electron renderer.
HAL drivers can now run directly in Electron main process (Node.js).

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 21:14:07 -05:00
Patrick Mulligan
f7307c9159 feat: wire HAL drivers to state machine via service wrapper (#19)
- Create apps/machine/src/services/hal.ts bridging @lamassu/hal
  EventEmitter-based drivers to ATMServices interface
- Add initializeWithHal() to ATM store for hardware + Lightning init
- Merge HAL services (dispenseCash, getInventory) with Lightning services
- Wire validator events to state machine (BILL_INSERTED, BILL_REJECTED)
- Exclude @lamassu/hal from Vite browser bundle (Node.js only)
- Add @lamassu/hal as dependency to apps/machine

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-27 13:46:25 -05:00
Patrick Mulligan
9bfa0d74a2 docs: add ADR-001 for HAL architecture decisions
Documents the decision to:
- Use TypeScript HAL drivers extracted from lamassu-machine
- Use Electron instead of Tauri for the kiosk app
- Avoid Rust rewrite (adds complexity without benefit)

Related issues: #18, #19, #20, #21

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 23:20:34 -05:00
Patrick Mulligan
8f24e10299 feat: add @lamassu/hal package with id003 and f56 drivers
Extract bill validator and dispenser drivers from lamassu-machine:
- ID003 driver for JCM iVIZION bill validators (Sintra)
- F56 driver for Fujitsu F53/F56 bill dispensers (Sintra)

Converted to TypeScript with full type definitions. Uses existing
battle-tested protocol implementations from lamassu-machine.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 23:08:47 -05:00
Patrick Mulligan
16f5fb3cbc docs: add architecture comparison with traditional lamassu-server
Comprehensive document comparing Nostr-native approach with traditional
lamassu-server/machine for operators evaluating the transition. Covers
infrastructure, security, payments, compliance, and migration path.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 15:58:01 -05:00
Patrick Mulligan
113eee6ecb docs: fix SDK references and update implementation status
- Fix ndebit-cash-in-flow.md: replace @shocknet/clink-sdk with @lamassu/clink
- Fix function names: decodeBech32 → decodeNdebit, ndebitEncode → encodeNdebit
- Update decoded.data.* references to decoded.* (direct DebitPointer return)
- Clarify SendNdebitRequest is wallet-side code, not in @lamassu/clink
- Update CLAUDE.md state-machine test count: 9 → 14
- Update apps/machine status: (planned) → (in progress)
- Update TROUBLESHOOTING.md issue count: 6 → 9

Closes #12, Closes #16

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 12:40:17 -05:00
Patrick Mulligan
7defc10444 fix: use GetLiveUserOperations for incoming invoice payment detection
Lightning.Pub's GetPaymentState RPC is for checking OUTGOING payments
(invoices you've paid), not incoming payments (invoices you've created).

This caused invoice payment detection to fail with "invoice not found"
even when payments succeeded.

Solution: Subscribe to GetLiveUserOperations events which Lightning.Pub
sends in real-time when invoices are paid. These events have:
- requestId: "GetLiveUserOperations"
- operation.type: "INCOMING_INVOICE"
- operation.identifier: the full invoice string

Key changes:
- packages/lightning/src/client.ts: Rewrote watchInvoice() to use
  subscription-based detection instead of GetPaymentState polling
- apps/machine/src/services/lightning.ts: Updated to use full invoice
  string instead of payment hash
- packages/lightning/TROUBLESHOOTING.md: Added Issue #9 documenting
  this gotcha (GetPaymentState vs GetLiveUserOperations)

Also includes ATM-driven cash-out flow implementation with:
- Invoice generation via NewInvoice RPC
- Real-time payment detection
- State machine updates for cash-out states

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 10:39:55 -05:00
Patrick Mulligan
0f50401c8b chore: improve dev tooling and relay config for ndebit testing
- strfry.conf: Add ephemeral event config for CLINK (21000-21003)
  Events persist 5 minutes to allow request/response flows
- mock-machine.mjs: Enhanced mock ATM with state management,
  cash insertion flow, auto-reset, and better UX
- test-ndebit.mjs: Add debugging script for ndebit flow

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-26 09:10:22 -05:00
Patrick Mulligan
a28c113087 fix: use correct Lightning.Pub pubkey and pointer in ndebit generation
The ndebit must encode Lightning.Pub's pubkey (not the ATM's pubkey)
so that Kind 21002 debit requests are received by Lightning.Pub.

Also added the 'atm' pointer to identify which Lightning.Pub user
account should pay the invoice.

Fixes:
- Changed pubkey from ATM identity to Lightning.Pub pubkey
- Added pointer: 'atm' to ndebit encoding
- Updated hardcoded pubkey to match current Lightning.Pub instance

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 17:44:45 -05:00
Patrick Mulligan
cf053730c6 feat: implement ndebit QR code display for cash-in flow
Replace LNURL-withdraw with CLINK ndebit for cash-in transactions:
- Add ndebit.ts with TLV-based bech32 encoding/decoding
- Generate clink:ndebit1...?amount=<sats> URIs
- Display QR code with copyable URI in CashInView
- Update state machine flow: generatingNdebit → displayingQR
- Fix shadcn-vue Input binding with :model-value prop

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 17:24:14 -05:00
Patrick Mulligan
66a3c054e7 feat: update CashOutView UI for noffer-based flow
- Redesign cash-out view for noffer (CLINK Offers) workflow
- Add states: generatingNoffer, displayingNoffer, validatingRequest,
  sendingInvoice, awaitingPayment
- Remove old selectingAmount state (user specifies amount in wallet)
- Add debug controls to simulate offer requests for testing
- Wire up offer request callback in ATM store to state machine

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 16:54:48 -05:00
Patrick Mulligan
94785f8d9d feat: implement noffer-based cash-out flow
Replace direct invoice generation with CLINK Offers (Kind 21001) for
cash-out. This provides a wallet-native flow where:

1. ATM displays noffer QR code
2. User's wallet sends Kind 21001 request with amount
3. ATM validates amount and responds with invoice
4. User pays invoice
5. ATM dispenses cash

Changes:
- packages/state-machine: Update cash-out flow with new states
  - displayingNoffer: Show noffer QR, subscribe to requests
  - validatingRequest: Check amount is dispensable
  - sendingInvoice: Send Kind 21001 response
  - awaitingPayment: Wait for payment confirmation
- packages/state-machine/types: Add OfferRequestEvent, new services
- apps/machine: Add service implementations and mocks

Benefits:
- Variable amount support (user enters in wallet)
- Standard CLINK protocol compliance
- Better UX (wallet-native flow)
- Static QR codes for multiple transactions

Closes #9

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 16:43:13 -05:00
Patrick Mulligan
a7a7b16dc1 feat: implement ndebit cash-in flow with clink: URI scheme
Adds mock ATM machine and infrastructure for testing the ndebit
cash-in flow where users scan a QR code to withdraw sats.

Key changes:
- Add mock-machine.mjs: displays ndebit QR with clink: scheme
- Add ndebit-cash-in-flow.md: comprehensive implementation guide
- Update docker-compose: configure relay for browser access
- Use clink: URI scheme (protocol-agnostic, supports future Cashu/Fedimint)

Flow: ATM displays clink:ndebit1...?amount=X → User scans with wallet
→ Wallet creates invoice → Sends Kind 21002 debit request → ATM pays

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-25 11:40:36 -05:00
Patrick Mulligan
e7fac251b7 Add status message when mining sync block 2026-01-24 18:51:39 -05:00
Patrick Mulligan
690b1548ca Fix test-setup LND sync check on regtest
Mine a block before running checks to wake up LND sync status.
On regtest, LND reports "synced_to_chain: false" when no blocks
have been mined recently, even though it's actually synced.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 18:49:51 -05:00
Patrick Mulligan
e7f32b92f9 Add README with setup instructions
Complete getting started guide for new developers:
- Prerequisites (Nix, devenv, Docker)
- Quick start from scratch (5 commands to working env)
- All available development commands
- Architecture overview
- Infrastructure services reference
- E2E testing instructions
- Troubleshooting section

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 18:40:33 -05:00
Patrick Mulligan
4bf78e25dc Update documentation and migrate future features to issues
CLAUDE.md updates:
- Add new testing commands (test-setup, fund-atm, test-payment, etc.)
- Add missing Bitcoin/Lightning commands (lncli-alice, alice-pay, setup-channel)
- Fix Related Documentation section (removed non-existent file references)
- Add External Resources section with relevant links

Migrated docs/future-features.md to Forgejo issues:
- #1: Public Cash Availability Display (P1, Low complexity)
- #2: Remote Initiation, Local Redemption (P2, Medium complexity)
- #3: Hold Invoices for Safe Dispensing (P2, High complexity)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 18:29:05 -05:00
Patrick Mulligan
6d0485bfb5 Add test infrastructure helpers for e2e testing
New devenv.nix scripts:
- test-setup: Validates entire test environment (services, connectivity,
  blockchain state, channels, and runs a payment test)
- fund-atm: Funds ATM Lightning.Pub account via Alice
- test-payment: Quick e2e payment test (ATM → customer)
- alice-invoice: Create invoice on Alice's node
- node-info: Display node pubkeys and channel information

Script improvements:
- fund-dev.mjs: Use FUND_AMOUNT env var instead of hardcoded value
- test-pay.mjs: Use NOSTR_RELAY_URL env var with localhost fallback
- Both scripts now use localhost:7777 by default instead of hardcoded IP

Updated shell hook to document new testing commands.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 18:23:54 -05:00
Patrick Mulligan
5b0e0e0d91 Fix Lightning.Pub RPC integration for e2e cash-in flow
Major fixes:
- Implement NIP-44 v1 encryption (Lightning.Pub requirement)
- Fix RPC request format (rpcName, params, query, body, authIdentifier, requestId)
- Fix PayInvoice amount field (always required, use 0 for invoices with amounts)
- Fix NostrClient subscriptions to use direct Relay instead of SimplePool
- Fix race condition by subscribing before publishing requests
- Remove #p/#e tag filters and match manually (relay compatibility)

New features:
- Add CLINK Debit support to cash-in flow
- Add invoice paste input for manual payments
- Add Input UI component

Documentation:
- Add TROUBLESHOOTING.md with 6 non-obvious integration gotchas
- Update CLAUDE.md with reference to troubleshooting doc

Test scripts:
- fund-dev.mjs - Create invoices for testing
- test-pay.mjs - Test PayInvoice RPC directly

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 18:05:00 -05:00
Patrick Mulligan
5d56ebb642 Implement LNURL-withdraw for cash-in flow
Replace CLINK offer with LNURL-withdraw for the "buy bitcoin" flow.
LNURL-withdraw is the correct protocol for customers to receive sats:
- ATM displays LNURL-withdraw QR code
- Customer scans with their Lightning wallet
- Wallet automatically creates invoice and sends it to ATM
- ATM pays the invoice, sending sats to customer

This provides a much better UX than manual invoice entry, especially
for a kiosk where users can't paste text.

Changes:
- Add LnurlWithdrawServer class that implements LUD-03 protocol
- Add LNURL HTTP server (port 3333) for wallet callbacks
- Update state machine: generatingOffer → generatingLnurlWithdraw
- Add lnurlWithdraw context field and generateLnurlWithdraw service
- Update CashInView to display LNURL-withdraw QR
- Add @scure/base dependency for bech32 encoding

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 14:04:25 -05:00
Patrick Mulligan
2356befbcf Use semantic theme variables for QR code colors
- Add --qr and --qr-foreground CSS variables to theme
- Light mode: standard gruvbox bg/fg for high contrast
- Dark mode: warm cream background, easier on eyes
- QRCode component now uses bg-qr and text-qr-foreground classes
- Reads CSS variables for qrcode.vue library props

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 13:21:59 -05:00
Patrick Mulligan
d5842bfd15 Use warm colors for QR code display in dark mode
- Warm cream background (#f5f0e6) instead of harsh white
- Gruvbox dark brown foreground (#3c3836) for good contrast
- Maintains ~10:1 contrast ratio for reliable phone scanning
- Easier on eyes in dark ATM environments

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 13:19:22 -05:00
Patrick Mulligan
36e326a27e Add QR code display and CLINK offer request handler
- Add qrcode.vue dependency for QR code rendering
- Create QRCode.vue component for displaying payment codes
- Update CashInView and CashOutView to use real QR codes
- Add CLINK offer request handler to respond when wallets scan noffers
- Wire payment received callback to state machine
- Auto-complete transaction when payment is detected

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 13:18:11 -05:00
Patrick Mulligan
687717bfce Wire Lightning.Pub services to ATM state machine
- Add lightning.ts services module with real LightningPubClient and CLINKClient
- Connect to local relay (ws://localhost:7777) and Lightning.Pub on init
- Generate real CLINK noffers for cash-in flow
- Generate real Lightning invoices for cash-out flow
- Add connection status indicator (Live/Connecting/Offline badge)
- Add toggle between live and mock services in debug panel
- Fall back to mock services on connection failure

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 13:12:15 -05:00
Patrick Mulligan
f068cdf01d Add Tauri 2.x ATM kiosk application
- Vue 3 + TypeScript + Vite frontend
- Tauri 2.x Rust backend for kiosk shell
- shadcn-vue components (Button, Card, Badge, Alert, Skeleton)
- Gruvbox color theme with semantic ATM colors
- XState state machine integration via Pinia store
- Three views: Idle, CashIn (buy), CashOut (sell)
- Dev mode with state machine debug overlay
- 1080x1920 portrait kiosk configuration
- Cargo workspace for rustfmt integration

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 12:18:20 -05:00
Patrick Mulligan
df27e59dcb Add future features roadmap and CLINK test script
- docs/future-features.md: Document planned features
  - Public cash availability display via kind 30078 beacons
  - Remote initiation with local redemption using signed claims
  - Hold invoices for safe dispensing (Lightning.Pub contribution)

- scripts/test-clink.ts: Test script for CLINK protocol verification

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 11:14:51 -05:00
Patrick Mulligan
a58dd7388e Update documentation with testing infrastructure details
- Document Alice LND node for payment testing
- Add testing payment workflow examples
- Update service ports table (Lightning.Pub: 1776)
- Add gitignore for vite temp files

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 11:05:49 -05:00
Patrick Mulligan
8cc462305f Add development helper scripts for Lightning testing
- Add lncli-alice for Alice's LND node
- Add setup-channel script to create channel between Alice and LND
- Add alice-pay script for paying invoices from Alice
- Update infra-up to wait for Alice's LND
- Update LIGHTNING_PUB_URL to correct port 1776
- Add mine-blocks, relay-test and other utility scripts

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 11:01:43 -05:00
Patrick Mulligan
bd08d0fdf9 Add Alice LND node and fix Lightning.Pub configuration
- Add second LND node (Alice) for payment testing
- Fix Lightning.Pub port mapping (1776:1776)
- Add ADMIN_TOKEN for HTTP API access
- Configure NOSTR_RELAYS with host.docker.internal for proper nprofile generation
- Fix strfry nofiles limit (set to 0 to skip limit configuration)
- Add extra_hosts for Linux host.docker.internal support

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-24 11:01:30 -05:00
Patrick Mulligan
b504aff037 Update CLAUDE.md with current implementation status
- Add Implementation Status section with completed/placeholder/planned breakdown
- Mark architecture tree with completion status (✅, planned, placeholder)
- Add Kind 21000 (Lightning.Pub RPC) to Nostr Event Kinds table
- Fix Kind 30078 description: "Service Beacon" not "Machine Status"
- Update Key Technologies table with current state
- Remove NIP-17 (not currently used)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 06:29:10 -05:00
Patrick Mulligan
92b0182523 Fix tests and add placeholder tests for all packages
- Fix noffer.ts encodeUint64BE to use division instead of bit shifts
  (JS bitwise ops only work on 32-bit integers)
- Update noffer.test.ts to use amountSats instead of amountMsat
- Update client.test.ts to remove serviceUrl (no longer in config)
- Add decodeInvoice tests for different BOLT11 amount formats
- Add placeholder tests for @lamassu/cashu package
- Add placeholder tests for @lamassu/ui-shared package

All 43 tests now pass across 6 packages.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 06:22:18 -05:00
Patrick Mulligan
d5a77c4311 Align CLINK and Lightning.Pub packages with actual protocol specs
CLINK Protocol (@lamassu/clink):
- Add CLINKErrorCode enum (codes 1-6) matching Lightning.Pub
- Update OfferRequest to use { offer, amount_sats, payer_data, ... }
- Update OfferResponse to { bolt11 } or { code, error, range }
- Update DebitRequest to { amount_sats, memo, rules }
- Update DebitResponse to { res: 'ok'|'GFY', preimage?, error?, code? }
- Add ManagementRequest/Response with proper action types
- Add ServiceBeacon type for Kind 30078 discovery
- Add DebitRules for recurring payment authorization
- Change all amounts from millisatoshis to satoshis
- Add helper functions: createOfferSuccess, createOfferError, etc.
- Add type guards: isOfferError, isDebitFailure, isManagementFailure

Lightning.Pub RPC (@lamassu/lightning):
- Change from NIP-47 kinds (23194/23195) to Kind 21000 RPC
- Add proper RPC request format: { rpcName, body, authIdentifier, requestId, appId }
- Add RPC response handling: { status: 'OK'|'ERROR', reason?, ...result }
- Add LightningPubEventKind enum
- Add isRPCError type guard
- Remove unused types (ChannelInfo, NodeInfo)
- Update all amounts from millisatoshis to satoshis

This aligns our implementation with the actual Lightning.Pub source code
at /home/padreug/Work/tries/2026-01-22-lamassu-refactor-packages/Lightning.Pub

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 06:18:04 -05:00
Patrick Mulligan
84eb605741 Fix nostr-tools v2.10 TypeScript compatibility issues
- nostr-client: Fix Relay.auth() callback to use verifyEvent and VerifiedEvent type
- nostr-client: Add @ts-expect-error for subscribeMany/querySync Filter[] types
- clink: Fix finalizeEvent to not pass pubkey (derived from secret key)
- clink: Fix bech32.decode to cast input as template literal type
- lightning: Fix finalizeEvent to not pass pubkey (derived from secret key)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 06:06:26 -05:00
Patrick Mulligan
1b0c1c8c2b Fix nostr-tools API compatibility in nostr-client
- Remove useWebSocketImplementation (not needed in newer versions)
- Fix Relay.auth() callback signature to match nostr-tools API
- Fix filter type casting for subscribeMany and querySync
- Remove ws dependency (not needed for browser/node compatible builds)
- Mark connections as authenticated when no auth required

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 05:59:46 -05:00
Patrick Mulligan
2fab50bdfc Add placeholder scaffolding for ui-shared and cashu packages
- ui-shared: Add Vite library config and minimal entry point
- cashu: Add TypeScript config and minimal entry point

These packages are placeholders that will be implemented as needed.
The scaffolding allows the monorepo build to complete.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 05:58:07 -05:00
Patrick Mulligan
8fc22e5291 Implement @lamassu/lightning package
Lightning.Pub client for Nostr-native Lightning operations:

Features:
- Account information and balance queries
- Invoice generation (BOLT11)
- Invoice payment with preimage
- Invoice lookup and status checking
- Payment watching with callbacks
- Exchange rate fetching (mock for now)
- Basic invoice decoding

Communication:
- NIP-47 compatible request/response events
- NIP-44 encrypted content
- Timeout handling

Types:
- AccountInfo, Invoice, PaymentResult
- ExchangeRate, ChannelInfo, NodeInfo

Includes basic unit tests for client initialization.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 05:54:58 -05:00
Patrick Mulligan
f35d0330b9 Implement @lamassu/state-machine package
XState v5 state machine for ATM transaction flows:

States:
- idle: Waiting for user selection
- cashIn: Buy bitcoin (insert bills -> display QR -> payment received)
- cashOut: Sell bitcoin (select amount -> display invoice -> dispense cash)

Features:
- Full cash-in flow with bill accumulation
- Full cash-out flow with cash dispensing
- Exchange rate fetching
- Sats calculation with fee deduction
- Optional Nostr receipt sending
- Error handling with retry logic
- Transaction ID generation
- Timeout handling

Service injection for testability:
- generateClinkOffer
- generateInvoice
- sendNostrReceipt
- dispenseCash
- getExchangeRate

Includes comprehensive tests for state transitions.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 05:51:42 -05:00
Patrick Mulligan
6ddb7c7a06 Implement @lamassu/clink package
CLINK protocol implementation for Nostr-native Lightning payments:

- noffer encoding/decoding (bech32 TLV format)
- CLINKClient for protocol communication
- Event kinds: 21001 (Offer), 21002 (Debit), 21003 (Manage)

Features:
- Create static payment codes (noffers) for ATM
- Handle incoming offer requests and generate invoices
- Send/receive debit requests
- Process management commands from operator
- Encrypted communication with NIP-44

Includes tests for noffer encoding/decoding round-trip.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 05:48:29 -05:00