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

2.9 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 is that a generated artifact knows what it needs and had no way to say so. A shunt built from a client's spreadsheets can hold its rows in memory, 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.

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

Turning 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. python build.py --cfg <room> merges each cabinet's service.yml into the room's docker-compose.yml and appends its settings to .env.example:

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

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

On a cluster

cabinet.json names a rig_addon. Where the 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

The two paths are deliberately separate — compose for a laptop, helm for a cluster — and rig_addon is the thread between them, so a room declares the dependency once either way.

Writing one

cabinet.json:

Key Purpose
name must match the directory
title, description shown on the station index
image for the record; service.yml is what runs
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
ports host ports it wants, for the collision note in the docs

service.yml is a plain compose fragment — one top-level key, the service name:

postgres:
  image: postgres:16-alpine
  environment:
    POSTGRES_DB: ${POSTGRES_DB:-soleprint}
  ...

Kept as YAML rather than generated from JSON so it reads like the compose 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.