refactor(config): rename VITE_LAMASSU_* machine-config env vars to VITE_BITSPIRE_*

MACHINE_MODEL, FIAT_CODE, VALIDATOR_DEVICE, DISPENSER_DEVICE and CASSETTES
carried the old brand in their names. Renamed everywhere they are read
(device.ts, electron/main.ts), written (flake.nix, mkAtmApp.nix, live.nix,
provision-atm.sh, factory-reset-atm.sh) and documented (.env.example,
docs/device-configuration.md). No compatibility fallback in code: the
machine reads VITE_BITSPIRE_* and nothing else.

The deployed .env files are the one place the old names persist — sintra's
/var/lib/bitspire/.env holds all three keys today — and the machine reads
MACHINE_MODEL / FIAT_CODE / CASSETTES from that file on every boot. Renaming
the keys in code alone would boot a live machine on preset defaults (wrong
bays, wrong fiat) at the next nightly pull. So configuration.nix gains an
activation script, beside the existing lamassu→bitspire user migration,
that rewrites VITE_LAMASSU_* → VITE_BITSPIRE_* in that file. Idempotent;
runs before bitspire.service starts.
This commit is contained in:
Padreug 2026-10-09 21:57:38 +02:00
commit bf2b9427aa
10 changed files with 68 additions and 52 deletions

View file

@ -6,17 +6,17 @@
# =============================================================================
# Machine model preset (sintra, gaia, or custom)
VITE_LAMASSU_MACHINE_MODEL=sintra
VITE_BITSPIRE_MACHINE_MODEL=sintra
# Fiat currency code (ISO 4217)
VITE_LAMASSU_FIAT_CODE=USD
VITE_BITSPIRE_FIAT_CODE=USD
# Custom device paths (optional - uses preset defaults if not set)
# VITE_LAMASSU_VALIDATOR_DEVICE=/dev/ttyJ5
# VITE_LAMASSU_DISPENSER_DEVICE=/dev/ttyJ7
# VITE_BITSPIRE_VALIDATOR_DEVICE=/dev/ttyJ5
# VITE_BITSPIRE_DISPENSER_DEVICE=/dev/ttyJ7
# Cassette configuration (optional - JSON array)
# VITE_LAMASSU_CASSETTES='[{"denomination":20,"count":100}]'
# VITE_BITSPIRE_CASSETTES='[{"denomination":20,"count":100}]'
# =============================================================================
# LNbits Connection (dev override — normally seed-provided) — nostr-native-transport

View file

@ -355,11 +355,11 @@ ipcMain.handle('get-config', () => {
appId: process.env.VITE_APP_ID || '',
// Hardware configuration
machineModel: process.env.VITE_LAMASSU_MACHINE_MODEL || 'sintra',
fiatCode: process.env.VITE_LAMASSU_FIAT_CODE || 'USD',
validatorDevice: process.env.VITE_LAMASSU_VALIDATOR_DEVICE,
dispenserDevice: process.env.VITE_LAMASSU_DISPENSER_DEVICE,
cassettes: process.env.VITE_LAMASSU_CASSETTES,
machineModel: process.env.VITE_BITSPIRE_MACHINE_MODEL || 'sintra',
fiatCode: process.env.VITE_BITSPIRE_FIAT_CODE || 'USD',
validatorDevice: process.env.VITE_BITSPIRE_VALIDATOR_DEVICE,
dispenserDevice: process.env.VITE_BITSPIRE_DISPENSER_DEVICE,
cassettes: process.env.VITE_BITSPIRE_CASSETTES,
// SECURITY: In production (packaged app), mock fallback is always disabled.
// Only allow it in development mode, and only when explicitly opted in via env.
allowMockFallback: isDev && process.env.VITE_ALLOW_MOCK_FALLBACK === 'true',
@ -836,7 +836,7 @@ function startCommandPoller(): void {
const result = await halInstance.dispenseCash(parsed.bills)
const txid = `manual-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`
const totalFiatCents = parsed.bills.reduce((s, b) => s + b.denomination * b.count * 100, 0)
const fiatCode = process.env.VITE_LAMASSU_FIAT_CODE || 'USD'
const fiatCode = process.env.VITE_BITSPIRE_FIAT_CODE || 'USD'
recordTransaction({
txid,
@ -908,19 +908,19 @@ app.whenReady().then(() => {
if (existing.length === 0) {
let seedCassettes: { denomination: number; count: number }[] = []
// Priority 1: explicit VITE_LAMASSU_CASSETTES env var
const cassettesJson = process.env.VITE_LAMASSU_CASSETTES
// Priority 1: explicit VITE_BITSPIRE_CASSETTES env var
const cassettesJson = process.env.VITE_BITSPIRE_CASSETTES
if (cassettesJson) {
try {
seedCassettes = JSON.parse(cassettesJson)
} catch (e) {
console.warn('[Electron] Failed to parse VITE_LAMASSU_CASSETTES:', e)
console.warn('[Electron] Failed to parse VITE_BITSPIRE_CASSETTES:', e)
}
}
// Priority 2: default presets per model
if (seedCassettes.length === 0) {
const model = process.env.VITE_LAMASSU_MACHINE_MODEL || 'sintra'
const model = process.env.VITE_BITSPIRE_MACHINE_MODEL || 'sintra'
const presets: Record<string, { denomination: number; count: number }[]> = {
douro: [
{ denomination: 100, count: 50 },

View file

@ -6,7 +6,7 @@
*
* Configuration priority (highest to lowest):
* 1. Runtime overrides (passed directly to functions)
* 2. Environment variables (LAMASSU_*)
* 2. Environment variables (BITSPIRE_*)
* 3. Machine preset defaults
*/
@ -143,7 +143,7 @@ export const MACHINE_PRESETS: Record<MachineModel, Omit<DeviceConfig, 'fiatCode'
// stable udev symlink /dev/ttyMEI (batm3.nix, serial A9YW78OC). The old
// /dev/ttyACM0 default assumed a CDC-ACM BNR; this hardware enumerates as
// ttyUSB* instead, so ACM0 never existed and cash-in was silently
// disabled ("[HAL] No validator"). Per-box override: VITE_LAMASSU_VALIDATOR_DEVICE.
// disabled ("[HAL] No validator"). Per-box override: VITE_BITSPIRE_VALIDATOR_DEVICE.
device: '/dev/ttyMEI',
},
dispenser: {
@ -152,7 +152,7 @@ export const MACHINE_PRESETS: Record<MachineModel, Omit<DeviceConfig, 'fiatCode'
// /dev/ttyF56 (batm3.nix, serial DDDLb103Y23). Avoids the raw
// /dev/ttyUSB0, which is enumeration-order dependent and could point at
// the wrong adapter after a re-plug/reboot. Per-box override:
// VITE_LAMASSU_DISPENSER_DEVICE.
// VITE_BITSPIRE_DISPENSER_DEVICE.
device: '/dev/ttyF56',
cassettes: [
{ denomination: 20, count: 400 },
@ -214,31 +214,31 @@ export function getDeviceConfig(
* Load device configuration from environment variables
*
* Supported variables:
* - LAMASSU_MACHINE_MODEL: Machine model preset (sintra, gaia, custom)
* - LAMASSU_FIAT_CODE: Fiat currency code (USD, EUR, etc.)
* - LAMASSU_VALIDATOR_DEVICE: Bill validator serial device path
* - LAMASSU_DISPENSER_DEVICE: Bill dispenser serial device path
* - LAMASSU_CASSETTES: JSON array of cassette configs
* - BITSPIRE_MACHINE_MODEL: Machine model preset (sintra, gaia, custom)
* - BITSPIRE_FIAT_CODE: Fiat currency code (USD, EUR, etc.)
* - BITSPIRE_VALIDATOR_DEVICE: Bill validator serial device path
* - BITSPIRE_DISPENSER_DEVICE: Bill dispenser serial device path
* - BITSPIRE_CASSETTES: JSON array of cassette configs
*
* @example
* LAMASSU_MACHINE_MODEL=sintra
* LAMASSU_FIAT_CODE=USD
* LAMASSU_CASSETTES='[{"denomination":20,"count":100},{"denomination":50,"count":50}]'
* BITSPIRE_MACHINE_MODEL=sintra
* BITSPIRE_FIAT_CODE=USD
* BITSPIRE_CASSETTES='[{"denomination":20,"count":100},{"denomination":50,"count":50}]'
*/
export function loadDeviceConfigFromEnv(): DeviceConfig {
const model = (import.meta.env.VITE_LAMASSU_MACHINE_MODEL as MachineModel) || 'sintra'
const fiatCode = import.meta.env.VITE_LAMASSU_FIAT_CODE || 'USD'
const model = (import.meta.env.VITE_BITSPIRE_MACHINE_MODEL as MachineModel) || 'sintra'
const fiatCode = import.meta.env.VITE_BITSPIRE_FIAT_CODE || 'USD'
const overrides: Partial<DeviceConfig> = {}
// Validator device override (preserve validator type from preset)
const validatorDevice = import.meta.env.VITE_LAMASSU_VALIDATOR_DEVICE
const validatorDevice = import.meta.env.VITE_BITSPIRE_VALIDATOR_DEVICE
if (validatorDevice) {
overrides.validator = { type: MACHINE_PRESETS[model].validator.type, device: validatorDevice }
}
// Dispenser device override
const dispenserDevice = import.meta.env.VITE_LAMASSU_DISPENSER_DEVICE
const dispenserDevice = import.meta.env.VITE_BITSPIRE_DISPENSER_DEVICE
if (dispenserDevice) {
overrides.dispenser = {
type: MACHINE_PRESETS[model].dispenser.type,
@ -248,7 +248,7 @@ export function loadDeviceConfigFromEnv(): DeviceConfig {
}
// Cassettes override (JSON)
const cassettesJson = import.meta.env.VITE_LAMASSU_CASSETTES
const cassettesJson = import.meta.env.VITE_BITSPIRE_CASSETTES
if (cassettesJson) {
try {
const cassettes = JSON.parse(cassettesJson) as CassetteConfig[]
@ -262,7 +262,7 @@ export function loadDeviceConfigFromEnv(): DeviceConfig {
}
}
} catch (e) {
console.error('[Config] Failed to parse LAMASSU_CASSETTES:', e)
console.error('[Config] Failed to parse BITSPIRE_CASSETTES:', e)
}
}