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

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