Initial commit: Lamassu modernization project

- Add lamassu-server, lamassu-machine, lamassu-install as submodules
- Add CLAUDE.md with development guidance
- Add docs/modernization-plan.md with 2026 refactoring roadmap
- Add .gitignore for Node.js/Nix development

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Patrick Mulligan 2026-01-22 14:19:00 -05:00
commit 9adee6e37f
7 changed files with 670 additions and 0 deletions

44
.gitignore vendored Normal file
View file

@ -0,0 +1,44 @@
# Dependencies
node_modules/
.pnpm-store/
# Build outputs
dist/
build/
*.tsbuildinfo
# Environment files
.env
.env.*
!.env.example
# IDE
.idea/
.vscode/
*.swp
*.swo
*~
# OS
.DS_Store
Thumbs.db
# Nix
result
result-*
.direnv/
.devenv/
.devenv.flake.nix
# Logs
*.log
logs/
# Test coverage
coverage/
# Lamassu specific
.lamassu/
*.pem
*.crt
*.key

9
.gitmodules vendored Normal file
View file

@ -0,0 +1,9 @@
[submodule "lamassu-server"]
path = lamassu-server
url = https://github.com/lamassu/lamassu-server.git
[submodule "lamassu-machine"]
path = lamassu-machine
url = https://github.com/lamassu/lamassu-machine.git
[submodule "lamassu-install"]
path = lamassu-install
url = https://github.com/lamassu/lamassu-install.git

121
CLAUDE.md Normal file
View file

@ -0,0 +1,121 @@
# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Repository Overview
This is the Lamassu Bitcoin ATM system, consisting of three components:
- **lamassu-server/** - Backend services and admin dashboard (pnpm monorepo with Turbo)
- **lamassu-machine/** - ATM kiosk software that runs on the physical machines
- **lamassu-install/** - Production installation and upgrade scripts
## Commands
### lamassu-server (monorepo)
```bash
cd lamassu-server
# Install dependencies
pnpm install
# Run all packages in development mode (server + admin-ui)
pnpm run dev
# Build all packages
pnpm run build
# Run tests across all packages
pnpm run test
# Run a single test file
cd packages/admin-ui && pnpm vitest run path/to/file.test.js
# Database migrations
node packages/server/bin/lamassu-migrate
# Generate SSL certificates (first-time setup)
bash packages/server/tools/cert-gen.sh
# Create admin user
node packages/server/bin/lamassu-register admin@example.com superuser
# Regenerate database types (requires running postgres)
cd packages/typesafe-db && pnpm run generate-types
```
### lamassu-machine
```bash
cd lamassu-machine
# Install and build
npm install
bash ./setup.sh
npm run build
# Run tests
npm test
# Development with mock hardware
node bin/fake-bills.js # In one terminal
node bin/lamassu-machine --mockBillValidator --mockBillDispenser --mockCam --mockPair '<totem>'
```
## Architecture
### lamassu-server Monorepo Structure
```
packages/
├── server/ # Express + Apollo GraphQL backend (CommonJS)
├── admin-ui/ # React 18 + Vite + MUI admin dashboard (ESM)
├── coins/ # @lamassu/coins - cryptocurrency constants (TypeScript)
└── typesafe-db/ # @lamassu/typesafe-db - Kysely database layer (TypeScript)
```
**Dependency flow**: `server` and `admin-ui` depend on `coins` and `typesafe-db`
**Key server entry points**:
- `bin/lamassu-server` - Main HTTPS server (port 3000, client cert auth)
- `bin/lamassu-admin-server` - Admin API server
- 20+ CLI utilities in `bin/` for operations tasks
**GraphQL**: Two implementations exist:
- `lib/graphql/` - Machine-facing API
- `lib/new-admin/graphql/` - Admin dashboard API
### lamassu-machine
The ATM kiosk uses a state machine architecture (`machina.js`) in `lib/brain.js` (134KB). The UI is vanilla JavaScript with Babel transpilation.
**Hardware drivers** in `lib/`: id003, mei, puloon, ccnet (bill validators), printer, leds, camera
## Code Style
**Formatting** (enforced by husky pre-commit):
- 2-space indent, no semicolons, single quotes, trailing commas
- Prettier + ESLint with auto-fix on commit
**TypeScript**: Only in `packages/coins/` and `packages/typesafe-db/`. Use `@typescript-eslint/consistent-type-imports` for imports.
**Server code**: CommonJS (`require`/`module.exports`)
**Admin UI**: ESM (`import`/`export`)
## Database
PostgreSQL with Kysely ORM. Types are auto-generated from the schema.
**Environment**: Configure postgres connection in `packages/server/.env`
## Requirements
- Node.js 22+
- pnpm 10+
- PostgreSQL
- Python 3 (for native dependency builds)
## Documentation
- `docs/modernization-plan.md` - 2026 modernization roadmap (Obsidian-compatible)

493
docs/modernization-plan.md Normal file
View file

@ -0,0 +1,493 @@
---
title: Lamassu Modernization Plan
created: 2026-01-22
updated: 2026-01-22
tags:
- architecture
- refactoring
- roadmap
- nix
status: draft
---
# Lamassu Modernization Plan
> [!abstract] Summary
> Aggressive refactoring plan to bring Lamassu Bitcoin ATM software to 2026 standards, focusing on **reproducibility**, **security**, and **open-source maintainability** with NixOS deployment.
## Quick Links
- [[#Architecture Overview]]
- [[#Technology Decisions]]
- [[#Migration Phases]]
- [[#Trade-offs]]
---
## Architecture Overview
```mermaid
graph TB
subgraph "NixOS Production Server"
deploy[deploy-rs / Colmena]
sops[sops-nix secrets]
subgraph "lamassu-server"
fastify[Fastify + tRPC + GraphQL]
otel[OpenTelemetry]
end
pg[(PostgreSQL + Drizzle)]
jaeger[Jaeger / Grafana]
end
subgraph "Admin UI"
react[React 19 + TanStack]
shadcn[Shadcn/ui + Tailwind]
end
subgraph "lamassu-machine"
tauri[Tauri 2.x Rust Core]
xstate[XState v5]
hal[Rust HAL napi-rs]
hw[Hardware Drivers]
end
react -->|tRPC| fastify
tauri -->|GraphQL| fastify
fastify --> pg
fastify --> otel
otel --> jaeger
hal --> hw
```
---
## Technology Decisions
### Runtime & Language
> [!decision] Full TypeScript + Node.js 22 LTS
> While Bun offers 3-4x performance, Node.js remains the backbone of enterprise applications. For mission-critical financial software, **reliability > raw performance**.
| Aspect | Current | Target |
|--------|---------|--------|
| Runtime | Node.js 22 | Node.js 22 LTS |
| Language | JS + partial TS | Full TypeScript (strict) |
| Modules | CommonJS + ESM mix | ESM only |
**Action Items:**
- [ ] Migrate all JavaScript to TypeScript
- [ ] Enable `strictNullChecks` and `noUncheckedIndexedAccess`
- [ ] Eliminate all CommonJS requires
#typescript #nodejs
---
### Backend Framework
> [!decision] Express → Fastify
> Fastify provides 70-80k req/s vs Express's 20-30k, with first-class TypeScript support and JSON Schema validation.
| Framework | Performance | TypeScript | Ecosystem |
|-----------|-------------|------------|-----------|
| Express | 20-30k req/s | Partial | Mature |
| **Fastify** | 70-80k req/s | First-class | Growing |
| Hono | Ultra-light | Good | Edge-focused |
**Why Fastify:**
- JSON Schema validation built-in
- HTTP/2 support
- Plugin architecture
- Better for long-running server processes
#backend #fastify
---
### API Layer
> [!decision] Hybrid: tRPC + GraphQL
> tRPC for Admin UI (type-safe monorepo), GraphQL for machine communication (stable contract).
```mermaid
graph LR
AdminUI -->|tRPC| Server
Machine -->|GraphQL| Server
```
**tRPC Benefits:**
- End-to-end type safety without codegen
- Faster iteration
- Smaller bundle
**Keep GraphQL for:**
- Machine API (stable contract)
- Potential non-TypeScript clients
- Consider Yoga over Apollo (lighter)
#api #trpc #graphql
---
### Database & ORM
> [!decision] Kysely → Drizzle ORM
> SQL-first, schema-as-code, zero binary dependencies (critical for NixOS reproducibility).
| ORM | Bundle | Cold Start | Migrations |
|-----|--------|------------|------------|
| Prisma | Heavy | Slow | Excellent |
| Kysely | Light | Fast | Basic |
| **Drizzle** | ~7kb | Fastest | Good |
**Why Drizzle:**
- SQL-like syntax (readable)
- Zero binary dependencies
- 90% reduction in cold starts
- TypeScript schema definitions
#database #drizzle #postgresql
---
### Frontend (Admin UI)
> [!decision] Keep React, Modernize Patterns
> React's ecosystem dominance (190M weekly downloads) makes migration unnecessary for admin dashboards.
**Modernization:**
- [ ] Upgrade to React 19 (Server Components)
- [ ] Replace MUI with Shadcn/ui + Tailwind
- [ ] TanStack Query for data fetching
- [ ] TanStack Router for type-safe routing
#frontend #react #tailwind
---
### Machine State Management
> [!decision] Machina.js → XState v5
> Actor-based state management with visual editor and TypeScript inference.
**Current:** `lib/brain.js` (134KB monolith)
**XState Benefits:**
- Visual state machine editor (Stately.ai)
- TypeScript 5.0+ with excellent inference
- Actor model for complex flows
- Production-tested at scale
> [!warning] Learning Curve
> XState has a steep learning curve. The mental model differs significantly from Redux/Context.
#statemachine #xstate
---
### Machine UI Framework
> [!decision] Vanilla JS → Tauri 2.x
> Security-first design critical for financial kiosk applications.
| Aspect | Electron | Tauri |
|--------|----------|-------|
| Bundle Size | ~100MB | **~2.5MB** |
| RAM Usage | 150-300MB | **30-50MB** |
| Startup | 1-2s | **<0.5s** |
| Security | Full Node access | **Explicit allowlist** |
**Why Tauri for ATMs:**
- Security by default (restrictive API model)
- Lower resource usage on embedded hardware
- Rust backend integrates with hardware drivers
- 35% adoption increase in 2024
#tauri #kiosk #security
---
### Hardware Abstraction
> [!decision] JavaScript → Rust + napi-rs
> Memory safety at compile time for hardware drivers.
```
TypeScript Application
↓
napi-rs bindings
↓
Rust HAL Layer
↓
Hardware (bill validators, printers, etc.)
```
**Benefits:**
- No null pointer dereferences
- No buffer overflows
- Pre-built binaries for platforms
- Integrates with Tauri (both Rust)
#rust #napi #hardware
---
### Schema Validation
> [!decision] Yup → Zod
> Better TypeScript integration, larger ecosystem, tRPC compatibility.
| Library | Bundle | Ecosystem | tRPC |
|---------|--------|-----------|------|
| Yup | ~15kb | Mature | Manual |
| **Zod** | ~17kb | Large | Native |
| Valibot | ~1.4kb | Growing | Adapter |
**Use Valibot** for machine-side code where bundle size matters.
#validation #zod
---
### Observability
> [!decision] OpenTelemetry
> Vendor-neutral, unified traces/metrics/logs.
```typescript
import { NodeSDK } from '@opentelemetry/sdk-node'
import { getNodeAutoInstrumentations } from '@opentelemetry/auto-instrumentations-node'
```
**Self-hosted stack:**
- **Jaeger** → Distributed tracing
- **Prometheus + Grafana** → Metrics
- All deployable via NixOS modules
#observability #opentelemetry #monitoring
---
### Authentication
> [!decision] Passkey-First Authentication
> Resistant to phishing and credential theft.
**Current:** Client certs + Argon2 + SimpleWebAuthn
**Target:**
- Passkeys as primary auth (WebAuthn)
- Keep client certs for machine-to-server
- Upgrade to SimpleWebAuthn v10+
> [!warning] 2025 Context
> 4B credentials leaked in January 2025. Password-based auth is a liability.
#security #passkeys #webauthn
---
## NixOS Infrastructure
### Development Environment
> [!decision] devenv
> 100% reproducible development environments.
```nix
# devenv.nix
{ pkgs, ... }: {
languages.javascript = {
enable = true;
package = pkgs.nodejs_22;
pnpm.enable = true;
};
languages.typescript.enable = true;
languages.rust.enable = true;
services.postgres = {
enable = true;
initialDatabases = [{ name = "lamassu"; }];
};
pre-commit.hooks = {
prettier.enable = true;
eslint.enable = true;
};
}
```
**Benefits:**
- Single `devenv.nix` replaces Docker, brew, apt
- DevContainer generation for VS Code
- Built-in PostgreSQL service
- Pre-commit hooks integration
#nix #devenv #reproducibility
---
### Secrets Management
> [!decision] sops-nix
> Atomic, declarative secret provisioning.
**Features:**
- Supports age, GPG, AWS KMS, HashiCorp Vault
- Works with existing SSH keys
- Version-control friendly (encrypted in git)
- Compatible with all NixOS deployment tools
```nix
sops.secrets.database_password = {
sopsFile = ./secrets/db.yaml;
owner = "lamassu";
};
```
#secrets #sops #security
---
### Production Deployment
> [!decision] deploy-rs
> Automatic rollback on failure - critical for ATM servers.
| Tool | Rollback | Secrets | Parallel |
|------|----------|---------|----------|
| **deploy-rs** | Automatic | External | Yes |
| Colmena | Manual | Built-in | Yes |
**Why deploy-rs:**
- Connects after activation to confirm availability
- Auto-rollback if machine becomes unreachable
- Critical for network config changes on remote ATMs
#deployment #deploy-rs #nixos
---
### Node.js Packaging
> [!tip] dream2nix
> Auto-generates Nix derivations from `package-lock.json`.
```nix
{
inputs.dream2nix.url = "github:nix-community/dream2nix";
outputs = { dream2nix, ... }:
dream2nix.lib.makeFlakeOutputs {
source = ./.;
};
}
```
#nix #packaging
---
## Testing Strategy
### Unit/Integration
> [!check] Keep Vitest
> Already in use, works well with TypeScript.
### E2E Testing
> [!decision] Add Playwright
> TypeScript-first, auto-wait eliminates flaky tests.
**Best Practices:**
- Use accessible locators (`getByRole`, `getByLabel`)
- Test isolation (each test independent)
- Run `tsc --noEmit` in CI
```typescript
test('user can complete transaction', async ({ page }) => {
await page.getByRole('button', { name: 'Start' }).click()
await expect(page.getByText('Insert bill')).toBeVisible()
})
```
#testing #playwright #vitest
---
## Migration Phases
### Phase 1: Foundation
> [!todo] High Impact, Lower Risk
- [ ] Full TypeScript migration
- [ ] devenv for development environment
- [ ] Drizzle ORM migration
- [ ] OpenTelemetry instrumentation
- [ ] Nix flake for builds
### Phase 2: Backend Modernization
- [ ] Express → Fastify migration
- [ ] Add tRPC for admin API
- [ ] Zod schema validation
- [ ] Playwright E2E tests
### Phase 3: Machine Modernization
> [!warning] Higher Risk - Requires extensive testing
- [ ] XState v5 for state machine
- [ ] Tauri migration
- [ ] Rust HAL for hardware drivers
### Phase 4: Deployment
- [ ] NixOS module for lamassu-server
- [ ] sops-nix secrets management
- [ ] deploy-rs for production deployments
---
## Trade-offs
| Decision | We Get | We Lose |
|----------|--------|---------|
| Node.js over Bun | Stability, ecosystem | Raw performance |
| Fastify over Hono | Mature plugins | Minimal bundle |
| Drizzle over Prisma | Bundle size, speed | DX features |
| Tauri over Electron | Security, efficiency | Ecosystem maturity |
| XState over simple FSM | Visualization, debugging | Simplicity |
---
## References
### Backend
- [Fastify vs Express 2025](https://medium.com/codetodeploy/express-or-fastify-in-2025-whats-the-right-node-js-framework-for-you-6ea247141a86)
- [tRPC vs GraphQL](https://betterstack.com/community/guides/scaling-nodejs/trpc-vs-graphql/)
- [Drizzle vs Prisma vs Kysely](https://levelup.gitconnected.com/the-2025-typescript-orm-battle-prisma-vs-drizzle-vs-kysely-007ffdfded67)
### Machine
- [Tauri vs Electron 2025](https://www.dolthub.com/blog/2025-11-13-electron-vs-tauri/)
- [XState v5](https://stately.ai/blog/2023-12-01-xstate-v5)
- [napi-rs Guide](https://blog.logrocket.com/building-nodejs-modules-rust-napi-rs/)
### NixOS
- [devenv](https://devenv.sh/)
- [sops-nix](https://github.com/Mic92/sops-nix)
- [deploy-rs](https://github.com/serokell/deploy-rs)
### Security
- [Passkeys Guide](https://www.passkeys.com/guide)
- [OpenTelemetry Node.js](https://opentelemetry.io/docs/languages/js/)
---
## Related Notes
- [[CLAUDE]] - Claude Code guidance
- [[Architecture Decision Records]] - ADRs for each decision
- [[NixOS Configuration]] - Production NixOS setup

1
lamassu-install Submodule

@ -0,0 +1 @@
Subproject commit c15dbbcae8902d99e056b9397bd46cbb640edce1

1
lamassu-machine Submodule

@ -0,0 +1 @@
Subproject commit 8c83ad8bdbd49621a20c01d725b1ce0c8eee813e

1
lamassu-server Submodule

@ -0,0 +1 @@
Subproject commit 5909e609576e078e4c2189fc6cce464b8dca8a71