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:
commit
9adee6e37f
7 changed files with 670 additions and 0 deletions
493
docs/modernization-plan.md
Normal file
493
docs/modernization-plan.md
Normal 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
|
||||
Loading…
Add table
Add a link
Reference in a new issue