feat(docker): add dev.sh with auto-funding and ATM app setup

- 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>
This commit is contained in:
Patrick Mulligan 2026-02-15 14:19:16 -05:00
commit c98f126ba7
180 changed files with 2695 additions and 9587 deletions

View file

@ -0,0 +1,197 @@
# Device Configuration
This document describes how to configure the ATM hardware for different machine models.
## Overview
The ATM application supports multiple Lamassu machine models out of the box. Configuration is handled through:
1. **Machine presets** - Built-in defaults for known hardware (Sintra, Gaia)
2. **Environment variables** - Override any setting at runtime
3. **Runtime overrides** - Programmatic configuration
## Supported Machine Models
### Sintra (Default)
The Lamassu Sintra (Gen 2) uses:
| Device | Protocol | Path |
| -------------- | -------- | ------------ |
| Bill Validator | ID003 | `/dev/ttyJ5` |
| Bill Dispenser | F56 | `/dev/ttyJ7` |
| Printer | Nippon | `/dev/ttyJ4` |
**Hardware:**
- Platform: Aaeon UP Board (Intel Atom x5-Z8350)
- Validator: JCM iVIZION
- Dispenser: Fujitsu F53/F56
### Gaia
The Lamassu Gaia uses similar hardware with different device paths. Verify paths on your specific unit.
## Environment Variables
All configuration can be overridden via environment variables. For Vite/Electron, prefix with `VITE_`:
| Variable | Description | Default |
| ------------------------------- | ---------------------------- | ------------- |
| `VITE_LAMASSU_MACHINE_MODEL` | Machine preset | `sintra` |
| `VITE_LAMASSU_FIAT_CODE` | Fiat currency (ISO 4217) | `USD` |
| `VITE_LAMASSU_VALIDATOR_DEVICE` | Validator serial device path | (from preset) |
| `VITE_LAMASSU_DISPENSER_DEVICE` | Dispenser serial device path | (from preset) |
| `VITE_LAMASSU_CASSETTES` | JSON array of cassettes | (from preset) |
### Example: Custom Cassette Configuration
```bash
# Two cassettes: $20 bills (100 count) and $50 bills (50 count)
export VITE_LAMASSU_CASSETTES='[{"denomination":20,"count":100},{"denomination":50,"count":50}]'
```
### Example: Custom Device Paths
```bash
export VITE_LAMASSU_VALIDATOR_DEVICE="/dev/ttyUSB0"
export VITE_LAMASSU_DISPENSER_DEVICE="/dev/ttyUSB1"
```
## Cassette Configuration
Each cassette is defined with:
```typescript
interface CassetteConfig {
denomination: number // Bill denomination (e.g., 20 for $20)
count?: number // Number of bills loaded (optional, for inventory tracking)
}
```
### Default Sintra Configuration
```json
[{ "denomination": 20, "count": 50 }]
```
This configures a single cassette with $20 bills, 50 bill capacity.
### Multi-Cassette Example
```json
[
{ "denomination": 20, "count": 100 },
{ "denomination": 50, "count": 50 },
{ "denomination": 100, "count": 25 }
]
```
## Programmatic Configuration
You can also configure devices programmatically:
```typescript
import { getDeviceConfig, toHalConfig } from '@/config'
// Use preset with custom fiat code
const config = getDeviceConfig('sintra', 'EUR')
// Or with overrides
const config = getDeviceConfig('sintra', 'USD', {
dispenser: {
type: 'f56',
device: '/dev/ttyUSB1',
cassettes: [
{ denomination: 20, count: 100 },
{ denomination: 50, count: 50 },
],
},
})
// Convert to HAL config format
const halConfig = toHalConfig(config)
```
## Initialization
### For Production (Recommended)
The simplest way to initialize with hardware:
```typescript
import { useAtmStore } from '@/stores/atm'
const atm = useAtmStore()
// Uses environment variables with Sintra defaults
await atm.initializeForProduction()
```
### With Custom Config
```typescript
import { useAtmStore } from '@/stores/atm'
import { getDeviceConfig, toHalConfig } from '@/config'
const atm = useAtmStore()
const config = getDeviceConfig('sintra', 'USD', {
dispenser: {
type: 'f56',
device: '/dev/ttyJ7',
cassettes: [{ denomination: 20, count: 100 }],
},
})
await atm.initializeWithHal(toHalConfig(config))
```
## Verifying Device Paths
On a Sintra running Linux, verify the serial devices exist:
```bash
ls -la /dev/ttyJ*
```
Expected output:
```
lrwxrwxrwx 1 root root 10 Jan 29 12:00 /dev/ttyJ4 -> ttyS4
lrwxrwxrwx 1 root root 10 Jan 29 12:00 /dev/ttyJ5 -> ttyS5
lrwxrwxrwx 1 root root 10 Jan 29 12:00 /dev/ttyJ7 -> ttyS7
```
If the symlinks don't exist, check the udev rules or use the underlying `/dev/ttyS*` devices directly.
## Troubleshooting
### Permission Denied on Serial Port
Add your user to the `dialout` group:
```bash
sudo usermod -a -G dialout $USER
# Log out and back in for changes to take effect
```
### Device Not Found
1. Check the device exists: `ls -la /dev/ttyJ*` or `ls -la /dev/ttyS*`
2. Check dmesg for hardware detection: `dmesg | grep tty`
3. Verify udev rules are loaded: `udevadm info /dev/ttyS5`
### Validator Not Responding
1. Verify baud rate: ID003 uses 9600 baud, 8 data bits, even parity, 1 stop bit
2. Check cable connections
3. Power cycle the validator
4. Check validator is in "online" mode (not standalone)
### Dispenser Not Dispensing
1. Verify cassette is properly seated
2. Check for bill jams
3. Verify dispenser firmware is compatible with F56 protocol
4. Check the dispenser is powered and initialized