feat(deploy): add NixOS live USB ISO for ATM hardware testing
Add NixOS configuration to build a bootable live USB ISO that runs the ATM Electron app in kiosk mode on physical hardware (UpBoard). The ISO boots from squashfs, auto-starts X11/openbox, and launches Electron in production mode. Key changes: - deploy/nixos/live.nix: Live USB module (squashfs+tmpfs, no disk install) - deploy/nixos/flake.nix: Nix flake with ISO build output - deploy/nixos/provision-atm.sh: Auto-provision LP credentials via API - deploy/nixos/build-iso.sh: End-to-end build workflow script - apps/machine: Fix Electron production mode (ELECTRON_FORCE_PROD), Vue Router hash mode for file:// protocol, relative asset paths Build: cd deploy/nixos && bash build-iso.sh Test: qemu-system-x86_64 -enable-kvm -m 2G -cdrom result/iso/*.iso Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c8e5bf9ee1
commit
19d43c2939
13 changed files with 1203 additions and 3 deletions
|
|
@ -40,7 +40,9 @@ function loadEnvFile() {
|
||||||
loadEnvFile()
|
loadEnvFile()
|
||||||
|
|
||||||
// Determine if we're in development
|
// Determine if we're in development
|
||||||
const isDev = process.env.NODE_ENV === 'development' || !app.isPackaged
|
const isDev =
|
||||||
|
process.env.ELECTRON_FORCE_PROD !== '1' &&
|
||||||
|
(process.env.NODE_ENV === 'development' || !app.isPackaged)
|
||||||
|
|
||||||
// Window dimensions (vertical ATM display)
|
// Window dimensions (vertical ATM display)
|
||||||
const WINDOW_WIDTH = 1080
|
const WINDOW_WIDTH = 1080
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import { createApp } from 'vue'
|
import { createApp } from 'vue'
|
||||||
import { createPinia } from 'pinia'
|
import { createPinia } from 'pinia'
|
||||||
import { createRouter, createWebHistory } from 'vue-router'
|
import { createRouter, createWebHashHistory } from 'vue-router'
|
||||||
import App from './App.vue'
|
import App from './App.vue'
|
||||||
import './style.css'
|
import './style.css'
|
||||||
|
|
||||||
|
|
@ -11,7 +11,7 @@ import CashInView from './views/CashInView.vue'
|
||||||
|
|
||||||
// Create router
|
// Create router
|
||||||
const router = createRouter({
|
const router = createRouter({
|
||||||
history: createWebHistory(),
|
history: createWebHashHistory(),
|
||||||
routes: [
|
routes: [
|
||||||
{ path: '/', name: 'idle', component: IdleView },
|
{ path: '/', name: 'idle', component: IdleView },
|
||||||
{ path: '/cash-out', name: 'cash-out', component: CashOutView },
|
{ path: '/cash-out', name: 'cash-out', component: CashOutView },
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,8 @@ import tailwindcss from '@tailwindcss/vite'
|
||||||
|
|
||||||
// https://vitejs.dev/config/
|
// https://vitejs.dev/config/
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
|
// Use relative paths so assets load correctly with file:// protocol (Electron production)
|
||||||
|
base: './',
|
||||||
plugins: [vue(), tailwindcss()],
|
plugins: [vue(), tailwindcss()],
|
||||||
resolve: {
|
resolve: {
|
||||||
alias: {
|
alias: {
|
||||||
|
|
|
||||||
192
deploy/nixos/README.md
Normal file
192
deploy/nixos/README.md
Normal file
|
|
@ -0,0 +1,192 @@
|
||||||
|
# Lamassu ATM NixOS Deployment
|
||||||
|
|
||||||
|
NixOS configuration for deploying Lamassu Next ATM software on UP Board hardware.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### 1. Build Installation ISO
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd deploy/nixos
|
||||||
|
nix build .#iso
|
||||||
|
```
|
||||||
|
|
||||||
|
The ISO will be in `result/iso/`.
|
||||||
|
|
||||||
|
### 2. Install on UP Board
|
||||||
|
|
||||||
|
1. Write ISO to USB drive:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo dd if=result/iso/*.iso of=/dev/sdX bs=4M status=progress
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Boot UP Board from USB
|
||||||
|
|
||||||
|
3. Run the installer:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo nixos-install --flake .#lamassu-atm
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Reboot and remove USB
|
||||||
|
|
||||||
|
### 3. Post-Install Configuration
|
||||||
|
|
||||||
|
SSH into the machine and configure:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Set up the ATM application
|
||||||
|
sudo mkdir -p /opt/lamassu-atm
|
||||||
|
sudo chown lamassu:lamassu /opt/lamassu-atm
|
||||||
|
|
||||||
|
# Copy the built Electron app
|
||||||
|
scp -r apps/machine/dist/* lamassu@<atm-ip>:/opt/lamassu-atm/
|
||||||
|
|
||||||
|
# Configure the ATM
|
||||||
|
sudo nano /etc/lamassu-atm/config.env
|
||||||
|
```
|
||||||
|
|
||||||
|
## Configuration Options
|
||||||
|
|
||||||
|
Edit `/etc/nixos/configuration.nix` to customize:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
services.lamassu-atm = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
# Nostr relay for ATM communication
|
||||||
|
relayUrl = "wss://relay.lamassu.is";
|
||||||
|
|
||||||
|
# Lightning.Pub instance
|
||||||
|
lightningPubUrl = "https://lp.lamassu.is";
|
||||||
|
|
||||||
|
# Hardware configuration
|
||||||
|
billValidator = {
|
||||||
|
enable = true;
|
||||||
|
device = "/dev/ttyUSB0";
|
||||||
|
type = "id003"; # or "mei", "ccnet"
|
||||||
|
};
|
||||||
|
|
||||||
|
billDispenser = {
|
||||||
|
enable = false; # Enable for two-way machines
|
||||||
|
device = "/dev/ttyUSB1";
|
||||||
|
type = "puloon";
|
||||||
|
};
|
||||||
|
|
||||||
|
camera = {
|
||||||
|
enable = true;
|
||||||
|
device = "/dev/video0";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Hardware Support
|
||||||
|
|
||||||
|
### Bill Validators
|
||||||
|
|
||||||
|
- **ID-003** (JCM) - Most common in Lamassu machines
|
||||||
|
- **MEI** (Mars Electronics)
|
||||||
|
- **CCNET** (CashCode)
|
||||||
|
|
||||||
|
### Bill Dispensers
|
||||||
|
|
||||||
|
- **Puloon** - LCDM series
|
||||||
|
- **Genmega**
|
||||||
|
|
||||||
|
### Cameras
|
||||||
|
|
||||||
|
- Any V4L2-compatible USB camera
|
||||||
|
|
||||||
|
## File Structure
|
||||||
|
|
||||||
|
```
|
||||||
|
deploy/nixos/
|
||||||
|
├── flake.nix # Nix flake entry point
|
||||||
|
├── configuration.nix # Base system configuration
|
||||||
|
├── lamassu-atm.nix # ATM service module
|
||||||
|
├── hardware/
|
||||||
|
│ └── upboard.nix # UP Board hardware config
|
||||||
|
├── udev/
|
||||||
|
│ └── 99-lamassu-hardware.rules # Hardware device rules
|
||||||
|
└── README.md # This file
|
||||||
|
```
|
||||||
|
|
||||||
|
## Troubleshooting
|
||||||
|
|
||||||
|
### Check ATM service status
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl status lamassu-atm
|
||||||
|
sudo journalctl -u lamassu-atm -f
|
||||||
|
```
|
||||||
|
|
||||||
|
### Check hardware detection
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# List serial devices
|
||||||
|
ls -la /dev/ttyUSB* /dev/ttyACM*
|
||||||
|
|
||||||
|
# Check for bill validator symlink
|
||||||
|
ls -la /dev/bill-validator
|
||||||
|
|
||||||
|
# Test camera
|
||||||
|
v4l2-ctl --list-devices
|
||||||
|
```
|
||||||
|
|
||||||
|
### Manual service control
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo systemctl restart lamassu-atm
|
||||||
|
sudo systemctl stop lamassu-atm
|
||||||
|
```
|
||||||
|
|
||||||
|
### Debug mode
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Run manually with verbose output
|
||||||
|
sudo -u lamassu DISPLAY=:0 LOG_LEVEL=debug electron /opt/lamassu-atm
|
||||||
|
```
|
||||||
|
|
||||||
|
## Updating
|
||||||
|
|
||||||
|
### Update system
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo nixos-rebuild switch --flake /etc/nixos#lamassu-atm
|
||||||
|
```
|
||||||
|
|
||||||
|
### Update ATM application
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Build new version
|
||||||
|
cd lamassu-next/apps/machine
|
||||||
|
pnpm run build
|
||||||
|
|
||||||
|
# Copy to ATM
|
||||||
|
scp -r dist/* lamassu@<atm-ip>:/opt/lamassu-atm/
|
||||||
|
|
||||||
|
# Restart service
|
||||||
|
ssh lamassu@<atm-ip> "sudo systemctl restart lamassu-atm"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Development vs Production
|
||||||
|
|
||||||
|
For development/testing, you can use the regtest docker environment:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd lamassu-next
|
||||||
|
./docker/dev.sh up --fund
|
||||||
|
./docker/dev.sh atm
|
||||||
|
```
|
||||||
|
|
||||||
|
For production, deploy this NixOS configuration and point to your production Nostr relay and Lightning.Pub instance.
|
||||||
|
|
||||||
|
## Security Notes
|
||||||
|
|
||||||
|
- The default `lamassu` user has `wheel` access for initial setup
|
||||||
|
- Remove wheel access after configuration: `sudo gpasswd -d lamassu wheel`
|
||||||
|
- SSH is enabled by default - configure key-based auth and disable password auth
|
||||||
|
- Firewall blocks all incoming connections by default
|
||||||
64
deploy/nixos/build-iso.sh
Executable file
64
deploy/nixos/build-iso.sh
Executable file
|
|
@ -0,0 +1,64 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Build a bootable NixOS Live USB ISO for testing the ATM Electron app
|
||||||
|
# on physical hardware (UpBoard).
|
||||||
|
#
|
||||||
|
# Usage: bash build-iso.sh
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
REPO_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||||
|
MACHINE_DIR="$REPO_ROOT/apps/machine"
|
||||||
|
|
||||||
|
echo "=== Building Lamassu ATM Live USB ISO ==="
|
||||||
|
|
||||||
|
# Step 1: Build the Electron app
|
||||||
|
echo ""
|
||||||
|
echo "--- Step 1: Building Electron app ---"
|
||||||
|
cd "$REPO_ROOT"
|
||||||
|
pnpm run build --filter=@lamassu/machine
|
||||||
|
|
||||||
|
# Step 2: Verify build outputs exist
|
||||||
|
echo ""
|
||||||
|
echo "--- Step 2: Verifying build outputs ---"
|
||||||
|
if [ ! -d "$MACHINE_DIR/dist" ]; then
|
||||||
|
echo "ERROR: $MACHINE_DIR/dist not found. Build failed?"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
if [ ! -d "$MACHINE_DIR/dist-electron" ]; then
|
||||||
|
echo "ERROR: $MACHINE_DIR/dist-electron not found. Build failed?"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "OK: dist/ and dist-electron/ present"
|
||||||
|
|
||||||
|
# Step 3: Build the NixOS ISO
|
||||||
|
echo ""
|
||||||
|
echo "--- Step 3: Building NixOS ISO (this takes a while) ---"
|
||||||
|
cd "$SCRIPT_DIR"
|
||||||
|
export MACHINE_DIR="$MACHINE_DIR"
|
||||||
|
nix build .#iso --impure --show-trace
|
||||||
|
|
||||||
|
# Step 4: Print results
|
||||||
|
ISO_PATH=$(ls result/iso/*.iso 2>/dev/null | head -1)
|
||||||
|
if [ -z "$ISO_PATH" ]; then
|
||||||
|
echo "ERROR: ISO not found in result/iso/"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
ISO_SIZE=$(du -h "$ISO_PATH" | cut -f1)
|
||||||
|
echo ""
|
||||||
|
echo "=== ISO built successfully ==="
|
||||||
|
echo "File: $ISO_PATH"
|
||||||
|
echo "Size: $ISO_SIZE"
|
||||||
|
echo ""
|
||||||
|
echo "--- Test in QEMU ---"
|
||||||
|
echo "qemu-system-x86_64 -enable-kvm -m 2G \\"
|
||||||
|
echo " -bios /usr/share/edk2-ovmf/OVMF_CODE.fd \\"
|
||||||
|
echo " -cdrom $ISO_PATH -display gtk"
|
||||||
|
echo ""
|
||||||
|
echo "--- Write to USB flash drive ---"
|
||||||
|
echo "sudo dd if=$ISO_PATH of=/dev/sdX bs=4M status=progress oflag=sync"
|
||||||
|
echo ""
|
||||||
|
echo "--- After booting, SSH in and configure ---"
|
||||||
|
echo "ssh lamassu@<atm-ip>"
|
||||||
|
echo "sudo nano /var/lib/lamassu-atm/.env"
|
||||||
|
echo "sudo systemctl restart lamassu-atm"
|
||||||
150
deploy/nixos/configuration.nix
Normal file
150
deploy/nixos/configuration.nix
Normal file
|
|
@ -0,0 +1,150 @@
|
||||||
|
# Lamassu ATM NixOS Configuration
|
||||||
|
# Base system configuration for ATM kiosk
|
||||||
|
|
||||||
|
{ config, lib, pkgs, pkgs-unstable, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
# System basics
|
||||||
|
system.stateVersion = "24.05";
|
||||||
|
|
||||||
|
# Networking
|
||||||
|
networking = {
|
||||||
|
hostName = "lamassu-atm";
|
||||||
|
|
||||||
|
# Use NetworkManager for easy WiFi configuration
|
||||||
|
networkmanager.enable = true;
|
||||||
|
|
||||||
|
# Firewall - minimal exposure
|
||||||
|
firewall = {
|
||||||
|
enable = true;
|
||||||
|
allowedTCPPorts = [ ]; # ATM initiates all connections
|
||||||
|
allowedUDPPorts = [ ];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Timezone - set to your location
|
||||||
|
time.timeZone = "UTC";
|
||||||
|
|
||||||
|
# Locale
|
||||||
|
i18n.defaultLocale = "en_US.UTF-8";
|
||||||
|
|
||||||
|
# Users
|
||||||
|
users.groups.lamassu = { };
|
||||||
|
users.users.lamassu = {
|
||||||
|
isNormalUser = true;
|
||||||
|
group = "lamassu";
|
||||||
|
description = "Lamassu ATM";
|
||||||
|
extraGroups = [
|
||||||
|
"wheel" # For admin access
|
||||||
|
"video" # GPU access
|
||||||
|
"audio" # Sound
|
||||||
|
"dialout" # Serial ports
|
||||||
|
"plugdev" # USB devices
|
||||||
|
"networkmanager" # Network config
|
||||||
|
];
|
||||||
|
# No password - kiosk mode
|
||||||
|
initialPassword = "lamassu";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Kiosk display configuration
|
||||||
|
services.xserver = {
|
||||||
|
enable = true;
|
||||||
|
|
||||||
|
# Display manager - auto-login
|
||||||
|
displayManager = {
|
||||||
|
autoLogin = {
|
||||||
|
enable = true;
|
||||||
|
user = "lamassu";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# No desktop environment - just the ATM app
|
||||||
|
desktopManager.xterm.enable = false;
|
||||||
|
|
||||||
|
# Basic window manager for Electron
|
||||||
|
windowManager.openbox.enable = true;
|
||||||
|
|
||||||
|
# Disable screen blanking
|
||||||
|
serverFlagsSection = ''
|
||||||
|
Option "BlankTime" "0"
|
||||||
|
Option "StandbyTime" "0"
|
||||||
|
Option "SuspendTime" "0"
|
||||||
|
Option "OffTime" "0"
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Intel driver
|
||||||
|
videoDrivers = [ "modesetting" ];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Audio (for transaction sounds)
|
||||||
|
security.rtkit.enable = true;
|
||||||
|
services.pipewire = {
|
||||||
|
enable = true;
|
||||||
|
alsa.enable = true;
|
||||||
|
pulse.enable = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# System packages
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
# System utilities
|
||||||
|
htop
|
||||||
|
vim
|
||||||
|
git
|
||||||
|
curl
|
||||||
|
wget
|
||||||
|
|
||||||
|
# Hardware debugging
|
||||||
|
usbutils
|
||||||
|
pciutils
|
||||||
|
lsof
|
||||||
|
|
||||||
|
# Serial port tools
|
||||||
|
minicom
|
||||||
|
screen
|
||||||
|
|
||||||
|
# For the Electron app
|
||||||
|
pkgs-unstable.electron
|
||||||
|
|
||||||
|
# Node.js for the application
|
||||||
|
pkgs-unstable.nodejs_22
|
||||||
|
|
||||||
|
# Camera support
|
||||||
|
v4l-utils
|
||||||
|
fswebcam
|
||||||
|
];
|
||||||
|
|
||||||
|
# Enable SSH for remote administration
|
||||||
|
services.openssh = {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
PasswordAuthentication = false;
|
||||||
|
PermitRootLogin = "no";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Auto-updates (optional - disabled by default for stability)
|
||||||
|
# system.autoUpgrade.enable = false;
|
||||||
|
|
||||||
|
# Journal configuration
|
||||||
|
services.journald = {
|
||||||
|
extraConfig = ''
|
||||||
|
SystemMaxUse=100M
|
||||||
|
MaxRetentionSec=1week
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# Nix settings
|
||||||
|
nix = {
|
||||||
|
settings = {
|
||||||
|
experimental-features = [ "nix-command" "flakes" ];
|
||||||
|
auto-optimise-store = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Garbage collection
|
||||||
|
gc = {
|
||||||
|
automatic = true;
|
||||||
|
dates = "weekly";
|
||||||
|
options = "--delete-older-than 7d";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
44
deploy/nixos/flake.lock
generated
Normal file
44
deploy/nixos/flake.lock
generated
Normal file
|
|
@ -0,0 +1,44 @@
|
||||||
|
{
|
||||||
|
"nodes": {
|
||||||
|
"nixpkgs": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1735563628,
|
||||||
|
"narHash": "sha256-OnSAY7XDSx7CtDoqNh8jwVwh4xNL/2HaJxGjryLWzX8=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "b134951a4c9f3c995fd7be05f3243f8ecd65d798",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixos-24.05",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nixpkgs-unstable": {
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1771177547,
|
||||||
|
"narHash": "sha256-trTtk3WTOHz7hSw89xIIvahkgoFJYQ0G43IlqprFoMA=",
|
||||||
|
"owner": "NixOS",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"rev": "ac055f38c798b0d87695240c7b761b82fc7e5bc2",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NixOS",
|
||||||
|
"ref": "nixpkgs-unstable",
|
||||||
|
"repo": "nixpkgs",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": "nixpkgs",
|
||||||
|
"nixpkgs-unstable": "nixpkgs-unstable"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"root": "root",
|
||||||
|
"version": 7
|
||||||
|
}
|
||||||
62
deploy/nixos/flake.nix
Normal file
62
deploy/nixos/flake.nix
Normal file
|
|
@ -0,0 +1,62 @@
|
||||||
|
{
|
||||||
|
description = "Lamassu Next ATM - NixOS Deployment";
|
||||||
|
|
||||||
|
inputs = {
|
||||||
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
|
||||||
|
|
||||||
|
# For Electron/Node.js packaging
|
||||||
|
nixpkgs-unstable.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||||
|
};
|
||||||
|
|
||||||
|
outputs = { self, nixpkgs, nixpkgs-unstable }:
|
||||||
|
let
|
||||||
|
system = "x86_64-linux";
|
||||||
|
|
||||||
|
pkgs = import nixpkgs {
|
||||||
|
inherit system;
|
||||||
|
config.allowUnfree = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
pkgs-unstable = import nixpkgs-unstable {
|
||||||
|
inherit system;
|
||||||
|
config.allowUnfree = true;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
{
|
||||||
|
# NixOS configuration for UP Board ATM (installed to disk)
|
||||||
|
nixosConfigurations.lamassu-atm = nixpkgs.lib.nixosSystem {
|
||||||
|
inherit system;
|
||||||
|
|
||||||
|
specialArgs = { inherit pkgs-unstable; };
|
||||||
|
|
||||||
|
modules = [
|
||||||
|
./hardware/upboard.nix
|
||||||
|
./configuration.nix
|
||||||
|
./lamassu-atm.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Live USB configuration (boots from USB, no disk install)
|
||||||
|
nixosConfigurations.lamassu-live = nixpkgs.lib.nixosSystem {
|
||||||
|
inherit system;
|
||||||
|
|
||||||
|
specialArgs = { inherit pkgs-unstable nixpkgs; };
|
||||||
|
|
||||||
|
modules = [
|
||||||
|
./live.nix
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Standalone module for importing into existing NixOS configs
|
||||||
|
nixosModules.default = import ./lamassu-atm.nix;
|
||||||
|
nixosModules.lamassu-atm = import ./lamassu-atm.nix;
|
||||||
|
|
||||||
|
packages.${system} = {
|
||||||
|
# ISO image for live USB testing
|
||||||
|
iso = self.nixosConfigurations.lamassu-live.config.system.build.isoImage;
|
||||||
|
|
||||||
|
# SD card image (if needed)
|
||||||
|
sdcard = self.nixosConfigurations.lamassu-atm.config.system.build.sdImage;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
107
deploy/nixos/hardware/upboard.nix
Normal file
107
deploy/nixos/hardware/upboard.nix
Normal file
|
|
@ -0,0 +1,107 @@
|
||||||
|
# UP Board Hardware Configuration
|
||||||
|
# Supports UP Board, UP Squared, and similar Intel Atom/Celeron boards
|
||||||
|
# commonly used in Lamassu ATM machines
|
||||||
|
|
||||||
|
{ config, lib, pkgs, ... }:
|
||||||
|
|
||||||
|
{
|
||||||
|
# Boot configuration for UP Board
|
||||||
|
# UP Board uses 64-bit CPU but some models have 32-bit UEFI
|
||||||
|
boot = {
|
||||||
|
loader = {
|
||||||
|
# Use systemd-boot for UEFI systems
|
||||||
|
systemd-boot.enable = true;
|
||||||
|
efi.canTouchEfiVariables = true;
|
||||||
|
|
||||||
|
# Timeout for boot menu (useful for remote debugging)
|
||||||
|
timeout = 3;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Kernel modules needed for UP Board hardware
|
||||||
|
initrd.availableKernelModules = [
|
||||||
|
"xhci_pci" # USB 3.0
|
||||||
|
"ahci" # SATA
|
||||||
|
"usb_storage" # USB mass storage
|
||||||
|
"sd_mod" # SCSI disk
|
||||||
|
"sdhci_pci" # SD card (eMMC)
|
||||||
|
"i915" # Intel graphics
|
||||||
|
];
|
||||||
|
|
||||||
|
kernelModules = [
|
||||||
|
"kvm-intel" # Virtualization (if needed)
|
||||||
|
"i2c-dev" # I2C for hardware control
|
||||||
|
"spi-dev" # SPI for hardware control
|
||||||
|
];
|
||||||
|
|
||||||
|
# UP Board specific kernel parameters
|
||||||
|
kernelParams = [
|
||||||
|
# Intel graphics
|
||||||
|
"i915.enable_psr=0"
|
||||||
|
|
||||||
|
# Serial console for debugging (UP Board has debug UART)
|
||||||
|
"console=ttyS4,115200n8"
|
||||||
|
"console=tty0"
|
||||||
|
|
||||||
|
# Quiet boot for kiosk mode
|
||||||
|
"quiet"
|
||||||
|
"splash"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Filesystem configuration
|
||||||
|
# Adjust these to match your actual disk layout
|
||||||
|
fileSystems."/" = {
|
||||||
|
device = "/dev/disk/by-label/nixos";
|
||||||
|
fsType = "ext4";
|
||||||
|
};
|
||||||
|
|
||||||
|
fileSystems."/boot" = {
|
||||||
|
device = "/dev/disk/by-label/boot";
|
||||||
|
fsType = "vfat";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Hardware-specific packages
|
||||||
|
hardware = {
|
||||||
|
# Intel GPU support
|
||||||
|
graphics = {
|
||||||
|
enable = true;
|
||||||
|
extraPackages = with pkgs; [
|
||||||
|
intel-media-driver # VAAPI driver for newer Intel
|
||||||
|
vaapiIntel # VAAPI driver (legacy)
|
||||||
|
vaapiVdpau
|
||||||
|
libvdpau-va-gl
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Enable firmware for Intel hardware
|
||||||
|
enableRedistributableFirmware = true;
|
||||||
|
|
||||||
|
# CPU microcode updates
|
||||||
|
cpu.intel.updateMicrocode = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Power management
|
||||||
|
powerManagement = {
|
||||||
|
enable = true;
|
||||||
|
cpuFreqGovernor = "performance"; # ATM should be responsive
|
||||||
|
};
|
||||||
|
|
||||||
|
# Disable suspend/hibernate for kiosk
|
||||||
|
systemd.targets = {
|
||||||
|
sleep.enable = false;
|
||||||
|
suspend.enable = false;
|
||||||
|
hibernate.enable = false;
|
||||||
|
hybrid-sleep.enable = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Serial port access for bill validator/dispenser
|
||||||
|
# UP Board GPIO/UART pins
|
||||||
|
services.udev.extraRules = ''
|
||||||
|
# UP Board serial ports
|
||||||
|
KERNEL=="ttyS[0-9]*", MODE="0666"
|
||||||
|
|
||||||
|
# USB serial adapters (common for bill validators)
|
||||||
|
KERNEL=="ttyUSB[0-9]*", MODE="0666"
|
||||||
|
KERNEL=="ttyACM[0-9]*", MODE="0666"
|
||||||
|
'';
|
||||||
|
}
|
||||||
240
deploy/nixos/lamassu-atm.nix
Normal file
240
deploy/nixos/lamassu-atm.nix
Normal file
|
|
@ -0,0 +1,240 @@
|
||||||
|
# Lamassu ATM Service Module
|
||||||
|
# Manages the ATM Electron application and related services
|
||||||
|
|
||||||
|
{ config, lib, pkgs, pkgs-unstable, ... }:
|
||||||
|
|
||||||
|
with lib;
|
||||||
|
|
||||||
|
let
|
||||||
|
cfg = config.services.lamassu-atm;
|
||||||
|
in
|
||||||
|
{
|
||||||
|
options.services.lamassu-atm = {
|
||||||
|
enable = mkEnableOption "Lamassu ATM service";
|
||||||
|
|
||||||
|
relayUrl = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "wss://relay.lamassu.is";
|
||||||
|
description = "Nostr relay URL for ATM communication";
|
||||||
|
};
|
||||||
|
|
||||||
|
lightningPubUrl = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "https://lp.lamassu.is";
|
||||||
|
description = "Lightning.Pub instance URL";
|
||||||
|
};
|
||||||
|
|
||||||
|
appDir = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "/opt/lamassu-atm";
|
||||||
|
description = "Directory containing the ATM application";
|
||||||
|
};
|
||||||
|
|
||||||
|
dataDir = mkOption {
|
||||||
|
type = types.path;
|
||||||
|
default = "/var/lib/lamassu-atm";
|
||||||
|
description = "Directory for ATM data and configuration";
|
||||||
|
};
|
||||||
|
|
||||||
|
logLevel = mkOption {
|
||||||
|
type = types.enum [ "error" "warn" "info" "debug" ];
|
||||||
|
default = "info";
|
||||||
|
description = "Logging level for the ATM application";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Hardware configuration
|
||||||
|
billValidator = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Enable bill validator support";
|
||||||
|
};
|
||||||
|
|
||||||
|
device = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "/dev/ttyUSB0";
|
||||||
|
description = "Serial device for bill validator";
|
||||||
|
};
|
||||||
|
|
||||||
|
type = mkOption {
|
||||||
|
type = types.enum [ "id003" "mei" "ccnet" ];
|
||||||
|
default = "id003";
|
||||||
|
description = "Bill validator protocol type";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
billDispenser = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = "Enable bill dispenser support (two-way machines)";
|
||||||
|
};
|
||||||
|
|
||||||
|
device = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "/dev/ttyUSB1";
|
||||||
|
description = "Serial device for bill dispenser";
|
||||||
|
};
|
||||||
|
|
||||||
|
type = mkOption {
|
||||||
|
type = types.enum [ "puloon" "genmega" ];
|
||||||
|
default = "puloon";
|
||||||
|
description = "Bill dispenser type";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
camera = {
|
||||||
|
enable = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Enable camera for QR code scanning";
|
||||||
|
};
|
||||||
|
|
||||||
|
device = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "/dev/video0";
|
||||||
|
description = "Camera device";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
# Create data directory
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d ${cfg.dataDir} 0750 lamassu lamassu -"
|
||||||
|
"d ${cfg.dataDir}/logs 0750 lamassu lamassu -"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Environment file for ATM configuration
|
||||||
|
environment.etc."lamassu-atm/config.env".text = ''
|
||||||
|
# Lamassu ATM Configuration
|
||||||
|
RELAY_URL=${cfg.relayUrl}
|
||||||
|
LIGHTNING_PUB_URL=${cfg.lightningPubUrl}
|
||||||
|
LOG_LEVEL=${cfg.logLevel}
|
||||||
|
DATA_DIR=${cfg.dataDir}
|
||||||
|
|
||||||
|
# Hardware
|
||||||
|
BILL_VALIDATOR_ENABLED=${boolToString cfg.billValidator.enable}
|
||||||
|
BILL_VALIDATOR_DEVICE=${cfg.billValidator.device}
|
||||||
|
BILL_VALIDATOR_TYPE=${cfg.billValidator.type}
|
||||||
|
|
||||||
|
BILL_DISPENSER_ENABLED=${boolToString cfg.billDispenser.enable}
|
||||||
|
BILL_DISPENSER_DEVICE=${cfg.billDispenser.device}
|
||||||
|
BILL_DISPENSER_TYPE=${cfg.billDispenser.type}
|
||||||
|
|
||||||
|
CAMERA_ENABLED=${boolToString cfg.camera.enable}
|
||||||
|
CAMERA_DEVICE=${cfg.camera.device}
|
||||||
|
|
||||||
|
# Display
|
||||||
|
DISPLAY=:0
|
||||||
|
ELECTRON_DISABLE_GPU=false
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Main ATM service
|
||||||
|
systemd.services.lamassu-atm = {
|
||||||
|
description = "Lamassu ATM Application";
|
||||||
|
wantedBy = [ "graphical.target" ];
|
||||||
|
after = [ "graphical.target" "network-online.target" ];
|
||||||
|
wants = [ "network-online.target" ];
|
||||||
|
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "simple";
|
||||||
|
User = "lamassu";
|
||||||
|
Group = "lamassu";
|
||||||
|
WorkingDirectory = cfg.appDir;
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
EnvironmentFile = "/etc/lamassu-atm/config.env";
|
||||||
|
|
||||||
|
# Start the Electron app
|
||||||
|
ExecStart = "${pkgs-unstable.electron}/bin/electron ${cfg.appDir}";
|
||||||
|
|
||||||
|
# Restart policy
|
||||||
|
Restart = "always";
|
||||||
|
RestartSec = 5;
|
||||||
|
|
||||||
|
# Resource limits
|
||||||
|
MemoryMax = "1G";
|
||||||
|
CPUQuota = "80%";
|
||||||
|
|
||||||
|
# Security hardening
|
||||||
|
NoNewPrivileges = true;
|
||||||
|
ProtectSystem = "strict";
|
||||||
|
ProtectHome = true;
|
||||||
|
ReadWritePaths = [ cfg.dataDir "/tmp" ];
|
||||||
|
PrivateTmp = true;
|
||||||
|
|
||||||
|
# Allow device access for hardware
|
||||||
|
DeviceAllow = [
|
||||||
|
"/dev/ttyUSB* rw"
|
||||||
|
"/dev/ttyACM* rw"
|
||||||
|
"/dev/ttyS* rw"
|
||||||
|
"/dev/video* rw"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Pre-start script to verify hardware
|
||||||
|
preStart = ''
|
||||||
|
echo "Lamassu ATM starting..."
|
||||||
|
echo "Relay: ${cfg.relayUrl}"
|
||||||
|
echo "Lightning.Pub: ${cfg.lightningPubUrl}"
|
||||||
|
|
||||||
|
# Check bill validator if enabled
|
||||||
|
if [ "${boolToString cfg.billValidator.enable}" = "true" ]; then
|
||||||
|
if [ ! -c "${cfg.billValidator.device}" ]; then
|
||||||
|
echo "Warning: Bill validator device ${cfg.billValidator.device} not found"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Check camera if enabled
|
||||||
|
if [ "${boolToString cfg.camera.enable}" = "true" ]; then
|
||||||
|
if [ ! -c "${cfg.camera.device}" ]; then
|
||||||
|
echo "Warning: Camera device ${cfg.camera.device} not found"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# Openbox autostart for kiosk mode
|
||||||
|
environment.etc."xdg/openbox/autostart".text = ''
|
||||||
|
# Disable screen saver and power management
|
||||||
|
xset s off
|
||||||
|
xset -dpms
|
||||||
|
xset s noblank
|
||||||
|
|
||||||
|
# Hide cursor after inactivity
|
||||||
|
unclutter -idle 3 &
|
||||||
|
|
||||||
|
# Start ATM (handled by systemd, but ensure display is ready)
|
||||||
|
sleep 2
|
||||||
|
'';
|
||||||
|
|
||||||
|
# udev rules for ATM hardware
|
||||||
|
services.udev.extraRules = ''
|
||||||
|
# ID-003 Bill Validator (JCM)
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="0451", ATTRS{idProduct}=="3410", MODE="0666", SYMLINK+="bill-validator"
|
||||||
|
|
||||||
|
# MEI Bill Validator
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="0b00", MODE="0666", SYMLINK+="bill-validator"
|
||||||
|
|
||||||
|
# CCNET Bill Validator (CashCode)
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="0x1b5a", MODE="0666", SYMLINK+="bill-validator"
|
||||||
|
|
||||||
|
# Puloon Bill Dispenser
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="0666", SYMLINK+="bill-dispenser"
|
||||||
|
|
||||||
|
# Generic USB-Serial adapters
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="067b", MODE="0666"
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", MODE="0666"
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", MODE="0666"
|
||||||
|
|
||||||
|
# Camera access
|
||||||
|
SUBSYSTEM=="video4linux", MODE="0666"
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Additional packages for hardware support
|
||||||
|
environment.systemPackages = with pkgs; [
|
||||||
|
unclutter # Hide cursor
|
||||||
|
];
|
||||||
|
};
|
||||||
|
}
|
||||||
181
deploy/nixos/live.nix
Normal file
181
deploy/nixos/live.nix
Normal file
|
|
@ -0,0 +1,181 @@
|
||||||
|
# Lamassu ATM Live USB Configuration
|
||||||
|
# Bootable ISO for testing on physical hardware (UpBoard) without installing to disk.
|
||||||
|
# Builds with: nix build .#iso
|
||||||
|
#
|
||||||
|
# Does NOT import hardware/upboard.nix (its fileSystems conflict with live boot).
|
||||||
|
# Instead, duplicates only the hardware-relevant kernel modules and GPU config.
|
||||||
|
|
||||||
|
{ config, lib, pkgs, pkgs-unstable, nixpkgs, ... }:
|
||||||
|
|
||||||
|
let
|
||||||
|
# Pre-built Electron app copied into the Nix store.
|
||||||
|
# Build first: pnpm run build --filter=@lamassu/machine
|
||||||
|
# Requires --impure: reads MACHINE_DIR env var to find build artifacts (dist/ is gitignored)
|
||||||
|
machineDir = builtins.getEnv "MACHINE_DIR";
|
||||||
|
atm-app = assert machineDir != ""
|
||||||
|
|| throw "MACHINE_DIR env var must be set (use build-iso.sh or: export MACHINE_DIR=/path/to/apps/machine)";
|
||||||
|
pkgs.runCommand "lamassu-atm-app" { } ''
|
||||||
|
mkdir -p $out
|
||||||
|
cp -r ${builtins.path { path = machineDir + "/dist"; name = "dist"; }} $out/dist
|
||||||
|
cp -r ${builtins.path { path = machineDir + "/dist-electron"; name = "dist-electron"; }} $out/dist-electron
|
||||||
|
cp ${builtins.path { path = machineDir + "/package.json"; name = "package.json"; }} $out/package.json
|
||||||
|
'';
|
||||||
|
|
||||||
|
# .env template with regtest defaults pointing to the dev machine
|
||||||
|
envTemplate = pkgs.writeText "lamassu-atm-env" ''
|
||||||
|
# Lamassu ATM Live USB Configuration
|
||||||
|
# Edit this file and restart: sudo systemctl restart lamassu-atm
|
||||||
|
#
|
||||||
|
# Dev machine LAN address (adjust to your network)
|
||||||
|
VITE_RELAY_URL=ws://192.168.1.190:7777
|
||||||
|
VITE_LIGHTNING_PUB_API_URL=http://192.168.1.190:1776
|
||||||
|
VITE_EXTENSION_API_URL=http://192.168.1.190:1777
|
||||||
|
|
||||||
|
# Lightning.Pub credentials (fill in after boot)
|
||||||
|
VITE_LIGHTNING_PUB_PUBKEY=
|
||||||
|
VITE_ATM_PRIVATE_KEY=
|
||||||
|
VITE_ADMIN_TOKEN=
|
||||||
|
VITE_APP_ID=
|
||||||
|
VITE_APP_TOKEN=
|
||||||
|
VITE_LINKING_TOKEN=
|
||||||
|
|
||||||
|
# Machine configuration
|
||||||
|
VITE_LAMASSU_MACHINE_MODEL=sintra
|
||||||
|
VITE_LAMASSU_FIAT_CODE=USD
|
||||||
|
|
||||||
|
# Hardware (uncomment and set for real hardware)
|
||||||
|
# VITE_LAMASSU_VALIDATOR_DEVICE=/dev/ttyUSB0
|
||||||
|
# VITE_LAMASSU_DISPENSER_DEVICE=/dev/ttyUSB1
|
||||||
|
# VITE_LAMASSU_CASSETTES=
|
||||||
|
|
||||||
|
# Force production mode when running via `electron /path`
|
||||||
|
ELECTRON_FORCE_PROD=1
|
||||||
|
|
||||||
|
# Display
|
||||||
|
DISPLAY=:0
|
||||||
|
'';
|
||||||
|
in
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
# NixOS ISO image builder (nixpkgs path passed via specialArgs from flake.nix)
|
||||||
|
"${nixpkgs}/nixos/modules/installer/cd-dvd/iso-image.nix"
|
||||||
|
"${nixpkgs}/nixos/modules/profiles/all-hardware.nix"
|
||||||
|
|
||||||
|
# Reuse kiosk config (X11, openbox, users, networking)
|
||||||
|
./configuration.nix
|
||||||
|
|
||||||
|
# Reuse ATM systemd service module
|
||||||
|
./lamassu-atm.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
# ISO image settings
|
||||||
|
isoImage = {
|
||||||
|
isoName = "lamassu-atm-live.iso";
|
||||||
|
makeEfiBootable = true;
|
||||||
|
makeBiosBootable = true;
|
||||||
|
squashfsCompression = "zstd -Xcompression-level 6";
|
||||||
|
};
|
||||||
|
|
||||||
|
# No fileSystems override needed — iso-image.nix handles squashfs + tmpfs root.
|
||||||
|
# We don't import hardware/upboard.nix, so there are no conflicting disk mounts.
|
||||||
|
|
||||||
|
# Boot: UpBoard-relevant kernel modules (from hardware/upboard.nix) without disk mounts
|
||||||
|
boot = {
|
||||||
|
initrd.availableKernelModules = [
|
||||||
|
"xhci_pci"
|
||||||
|
"ahci"
|
||||||
|
"usb_storage"
|
||||||
|
"sd_mod"
|
||||||
|
"sdhci_pci"
|
||||||
|
"i915"
|
||||||
|
"squashfs"
|
||||||
|
"iso9660"
|
||||||
|
"loop"
|
||||||
|
];
|
||||||
|
|
||||||
|
kernelModules = [
|
||||||
|
"kvm-intel"
|
||||||
|
"i2c-dev"
|
||||||
|
"spi-dev"
|
||||||
|
];
|
||||||
|
|
||||||
|
kernelParams = [
|
||||||
|
"i915.enable_psr=0"
|
||||||
|
"quiet"
|
||||||
|
"splash"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
# Intel GPU support (from hardware/upboard.nix)
|
||||||
|
# NB: nixos-24.05 uses hardware.opengl, not hardware.graphics
|
||||||
|
hardware = {
|
||||||
|
opengl = {
|
||||||
|
enable = true;
|
||||||
|
extraPackages = with pkgs; [
|
||||||
|
intel-media-driver
|
||||||
|
vaapiIntel
|
||||||
|
vaapiVdpau
|
||||||
|
libvdpau-va-gl
|
||||||
|
];
|
||||||
|
};
|
||||||
|
enableRedistributableFirmware = true;
|
||||||
|
cpu.intel.updateMicrocode = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Performance governor for responsive kiosk
|
||||||
|
powerManagement = {
|
||||||
|
enable = true;
|
||||||
|
cpuFreqGovernor = "performance";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Disable suspend/hibernate
|
||||||
|
systemd.targets = {
|
||||||
|
sleep.enable = false;
|
||||||
|
suspend.enable = false;
|
||||||
|
hibernate.enable = false;
|
||||||
|
hybrid-sleep.enable = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Enable the ATM service with live USB paths
|
||||||
|
services.lamassu-atm = {
|
||||||
|
enable = true;
|
||||||
|
appDir = "${atm-app}";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Allow unprivileged user namespaces (Electron sandbox needs this)
|
||||||
|
boot.kernel.sysctl."kernel.unprivileged_userns_clone" = 1;
|
||||||
|
|
||||||
|
# Override the systemd service for live USB environment
|
||||||
|
systemd.services.lamassu-atm = {
|
||||||
|
serviceConfig = {
|
||||||
|
EnvironmentFile = lib.mkForce "/var/lib/lamassu-atm/.env";
|
||||||
|
# Electron needs --no-sandbox in the live/testing environment
|
||||||
|
ExecStart = lib.mkForce "${pkgs-unstable.electron}/bin/electron --no-sandbox --disable-gpu-sandbox ${atm-app}";
|
||||||
|
# Disable all security hardening that conflicts with Electron
|
||||||
|
NoNewPrivileges = lib.mkForce false;
|
||||||
|
ProtectSystem = lib.mkForce false;
|
||||||
|
ProtectHome = lib.mkForce false;
|
||||||
|
PrivateTmp = lib.mkForce false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Install the .env template on first boot
|
||||||
|
system.activationScripts.lamassu-env = ''
|
||||||
|
mkdir -p /var/lib/lamassu-atm
|
||||||
|
if [ ! -f /var/lib/lamassu-atm/.env ]; then
|
||||||
|
cp ${envTemplate} /var/lib/lamassu-atm/.env
|
||||||
|
chmod 600 /var/lib/lamassu-atm/.env
|
||||||
|
chown lamassu:lamassu /var/lib/lamassu-atm/.env
|
||||||
|
fi
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Allow SSH with password for initial setup on the live system
|
||||||
|
services.openssh.settings.PasswordAuthentication = lib.mkForce true;
|
||||||
|
|
||||||
|
# Serial port udev rules (from hardware/upboard.nix)
|
||||||
|
services.udev.extraRules = lib.mkAfter ''
|
||||||
|
KERNEL=="ttyS[0-9]*", MODE="0666"
|
||||||
|
KERNEL=="ttyUSB[0-9]*", MODE="0666"
|
||||||
|
KERNEL=="ttyACM[0-9]*", MODE="0666"
|
||||||
|
'';
|
||||||
|
}
|
||||||
99
deploy/nixos/provision-atm.sh
Executable file
99
deploy/nixos/provision-atm.sh
Executable file
|
|
@ -0,0 +1,99 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# Provision a running ATM (live USB or QEMU VM) with Lightning.Pub credentials.
|
||||||
|
# Extracts credentials from the dev docker stack and writes them to the ATM's .env via SSH.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# bash provision-atm.sh # defaults: SSH to localhost:2222 (QEMU)
|
||||||
|
# bash provision-atm.sh 192.168.1.50 # SSH to a real ATM on the LAN
|
||||||
|
# bash provision-atm.sh 192.168.1.50 22 # custom SSH port
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
ATM_HOST="${1:-localhost}"
|
||||||
|
ATM_SSH_PORT="${2:-2222}"
|
||||||
|
ATM_USER="lamassu"
|
||||||
|
LP_API="http://localhost:1776"
|
||||||
|
ADMIN_TOKEN="lamassu-dev-admin-token"
|
||||||
|
ATM_PRIVATE_KEY="f391a2c3fc734f443b0f685688a0441b5fb9805853c0023f570c5a3c6412b136"
|
||||||
|
|
||||||
|
echo "=== Provisioning ATM at $ATM_HOST:$ATM_SSH_PORT ==="
|
||||||
|
|
||||||
|
# Step 1: Extract Lightning.Pub pubkey from docker logs
|
||||||
|
echo ""
|
||||||
|
echo "--- Step 1: Getting Lightning.Pub pubkey ---"
|
||||||
|
PUBKEY=$(docker logs lamassu-lightning-pub 2>&1 | grep -oP 'pubkey:\s*\K[a-f0-9]+' | tail -1)
|
||||||
|
if [ -z "$PUBKEY" ]; then
|
||||||
|
echo "ERROR: Could not extract pubkey from lamassu-lightning-pub logs."
|
||||||
|
echo "Is the docker stack running? Try: docker ps | grep lightning-pub"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Pubkey: ${PUBKEY:0:16}..."
|
||||||
|
|
||||||
|
# Step 2: Create ATM app via admin API
|
||||||
|
echo ""
|
||||||
|
echo "--- Step 2: Creating ATM app ---"
|
||||||
|
RESPONSE=$(curl -s -X POST "$LP_API/api/admin/app/add" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-H "Authorization: Bearer $ADMIN_TOKEN" \
|
||||||
|
-d '{"name":"lamassu-atm-live","allow_user_creation":true}' 2>/dev/null)
|
||||||
|
|
||||||
|
if echo "$RESPONSE" | grep -q '"status":"OK"'; then
|
||||||
|
APP_ID=$(echo "$RESPONSE" | grep -oP '"id":"\K[^"]+')
|
||||||
|
APP_TOKEN=$(echo "$RESPONSE" | grep -oP '"auth_token":"\K[^"]+')
|
||||||
|
echo "Created app: ${APP_ID:0:16}..."
|
||||||
|
else
|
||||||
|
echo "WARN: Could not create app (may already exist). Response:"
|
||||||
|
echo "$RESPONSE"
|
||||||
|
echo ""
|
||||||
|
echo "If the app already exists, check docker/dev-state/ for cached credentials."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 3: Determine the host IP as seen from the ATM
|
||||||
|
# For QEMU user-mode networking, the host is at 10.0.2.2
|
||||||
|
# For real hardware on LAN, use the dev machine's LAN IP
|
||||||
|
if [ "$ATM_HOST" = "localhost" ]; then
|
||||||
|
HOST_IP="10.0.2.2"
|
||||||
|
echo ""
|
||||||
|
echo "--- QEMU detected: using $HOST_IP as host gateway ---"
|
||||||
|
else
|
||||||
|
HOST_IP=$(hostname -I | awk '{print $1}')
|
||||||
|
echo ""
|
||||||
|
echo "--- LAN ATM: using $HOST_IP as dev machine address ---"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Step 4: Write .env to the ATM via SSH
|
||||||
|
echo ""
|
||||||
|
echo "--- Step 3: Writing .env to ATM ---"
|
||||||
|
ENV_CONTENT="# Lamassu ATM Configuration
|
||||||
|
# Auto-generated by provision-atm.sh on $(date -Iseconds)
|
||||||
|
|
||||||
|
# Lightning.Pub connection
|
||||||
|
VITE_RELAY_URL=ws://$HOST_IP:7777
|
||||||
|
VITE_LIGHTNING_PUB_PUBKEY=$PUBKEY
|
||||||
|
VITE_LIGHTNING_PUB_API_URL=http://$HOST_IP:1776
|
||||||
|
VITE_EXTENSION_API_URL=http://$HOST_IP:1777
|
||||||
|
|
||||||
|
# Credentials
|
||||||
|
VITE_ADMIN_TOKEN=$ADMIN_TOKEN
|
||||||
|
VITE_ATM_PRIVATE_KEY=$ATM_PRIVATE_KEY
|
||||||
|
VITE_APP_ID=$APP_ID
|
||||||
|
VITE_APP_TOKEN=$APP_TOKEN
|
||||||
|
|
||||||
|
# Machine configuration
|
||||||
|
VITE_LAMASSU_MACHINE_MODEL=sintra
|
||||||
|
VITE_LAMASSU_FIAT_CODE=USD
|
||||||
|
|
||||||
|
# Force production mode
|
||||||
|
ELECTRON_FORCE_PROD=1
|
||||||
|
DISPLAY=:0"
|
||||||
|
|
||||||
|
ssh -o StrictHostKeyChecking=no -p "$ATM_SSH_PORT" "$ATM_USER@$ATM_HOST" \
|
||||||
|
"echo '$ENV_CONTENT' | sudo tee /var/lib/lamassu-atm/.env > /dev/null && sudo systemctl restart lamassu-atm"
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "=== ATM provisioned successfully ==="
|
||||||
|
echo ""
|
||||||
|
echo "Credentials written to /var/lib/lamassu-atm/.env"
|
||||||
|
echo "ATM service restarted. It should connect to Lightning.Pub at $HOST_IP."
|
||||||
|
echo ""
|
||||||
|
echo "To check status: ssh -p $ATM_SSH_PORT $ATM_USER@$ATM_HOST 'sudo journalctl -u lamassu-atm -f'"
|
||||||
57
deploy/nixos/udev/99-lamassu-hardware.rules
Normal file
57
deploy/nixos/udev/99-lamassu-hardware.rules
Normal file
|
|
@ -0,0 +1,57 @@
|
||||||
|
# Lamassu ATM Hardware udev Rules
|
||||||
|
# Place in /etc/udev/rules.d/ or use services.udev.extraRules in NixOS
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Bill Validators
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# ID-003 Bill Validator (JCM/Japan Cash Machine)
|
||||||
|
# Uses TI USB-Serial chip
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="0451", ATTRS{idProduct}=="3410", MODE="0666", GROUP="dialout", SYMLINK+="bill-validator"
|
||||||
|
|
||||||
|
# MEI Bill Validator (Mars Electronics)
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="0b00", MODE="0666", GROUP="dialout", SYMLINK+="bill-validator"
|
||||||
|
|
||||||
|
# CCNET Bill Validator (CashCode)
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="1b5a", MODE="0666", GROUP="dialout", SYMLINK+="bill-validator"
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Bill Dispensers
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# Puloon Bill Dispenser (uses FTDI chip)
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6001", MODE="0666", GROUP="dialout", SYMLINK+="bill-dispenser"
|
||||||
|
|
||||||
|
# Genmega Bill Dispenser
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", ATTRS{idProduct}=="6015", MODE="0666", GROUP="dialout", SYMLINK+="bill-dispenser"
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Generic USB-Serial Adapters
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# Prolific PL2303
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="067b", MODE="0666", GROUP="dialout"
|
||||||
|
|
||||||
|
# FTDI
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="0403", MODE="0666", GROUP="dialout"
|
||||||
|
|
||||||
|
# Silicon Labs CP210x
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="10c4", MODE="0666", GROUP="dialout"
|
||||||
|
|
||||||
|
# CH340/CH341
|
||||||
|
SUBSYSTEM=="tty", ATTRS{idVendor}=="1a86", MODE="0666", GROUP="dialout"
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Camera
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# Allow access to all video4linux devices (cameras)
|
||||||
|
SUBSYSTEM=="video4linux", MODE="0666", GROUP="video"
|
||||||
|
|
||||||
|
# ============================================
|
||||||
|
# Printers
|
||||||
|
# ============================================
|
||||||
|
|
||||||
|
# Common thermal receipt printers
|
||||||
|
SUBSYSTEM=="usb", ATTRS{idVendor}=="04b8", MODE="0666", GROUP="lp" # Epson
|
||||||
|
SUBSYSTEM=="usb", ATTRS{idVendor}=="0519", MODE="0666", GROUP="lp" # Star Micronics
|
||||||
Loading…
Add table
Add a link
Reference in a new issue