Files
soleprint/docs/data/en/station-cabinets.md
2026-09-17 01:12:15 -03:00

3.8 KiB

Cabinets

A cabinet is a dependency container a room can switch on: postgres, redis, airflow. The vocabulary already had the word — execution.container in every room's config.json is "Cabinet — tool container" — and until now nothing stood behind it.

The problem it solves: a generated artifact knows what it needs and had no way to say so. A shunt built from a client's spreadsheets holds its rows in memory happily, but the moment you want them to survive a restart you need postgres, and wiring postgres in meant hand-editing a room's docker-compose.yml and then hand-editing the cluster too. A cabinet is that declaration, made once and read by both paths.

Switching one on

Add cfg/<room>/data/cabinets.json, the same shape as its sibling data/*.json files:

[
  { "name": "postgres" },
  { "name": "redis" },
  { "name": "airflow", "env": { "AIRFLOW_ADMIN_PASSWORD": "change-me" } }
]

Then build. The compose merge is a step in Export / Compile:

python build.py --cfg sample
cd gen/sample && docker compose up -d

build.py merges each cabinet's compose fragment into the room's docker-compose.yml, declares its named volumes, and appends its settings to .env.example — never to .env.

A service the room already declares wins. cfg/amar/docker-compose.yml ships its own db; switching the postgres cabinet on will not replace it. The build says so when it skips one:

Composing cabinets...
  cabinets: redis, airflow
  cabinets already declared by the room, left alone: postgres

Dependencies come along automatically. Airflow without a metadata database is a container that exits on boot, so asking for airflow brings postgres and redis with it, ordered so compose reads them before the thing that needs them.

On a cluster

Every cabinet names a rig_addon. Where a room runs on kind rather than compose, the same dependency installs as a rig addon of that name:

cd rig
PROFILE=data make cluster up      # installs the addons too

kubectl -n data port-forward svc/postgres 5432:5432
kubectl -n data port-forward svc/airflow  8080:8080

The two paths are deliberately separate — compose for a laptop, manifests for a cluster — and rig_addon is the thread between them, so the room declares the dependency once either way. The addons generate their own passwords on first install and keep them across re-runs, so re-running never rotates a credential out from under something already connected.

What ships

Cabinet Image Notes
postgres postgres:16-alpine healthcheck wired, so depends_on: service_healthy works
redis redis:7-alpine cache, and the broker for anything queue-shaped
airflow apache/airflow:2.10.4 one container on standalone; needs postgres and redis

Writing one

soleprint/station/cabinets/<name>/
  cabinet.json    what it is, what it needs, what it exports
  service.yml     the compose service, verbatim

cabinet.json:

Key Purpose
name must match the directory
title, description shown on the station index
service the key to merge under in services: (defaults to name)
env settings and defaults, written to .env.example
volumes named volumes to declare at the top level
depends_on other cabinets that must come with it
rig_addon the matching rig/ctrl/addons/<name>.sh, if there is one
notes lines written into .env.example as comments

service.yml is a plain compose fragment with one top-level key — the service name. It stays YAML rather than being generated from JSON so it reads like the file it becomes, and so anything compose supports is available without this tool learning about it first.

Adding a cabinet is adding a directory. Nothing dispatches on the name.