Files
2026-08-20 11:24:42 -03:00
..
2026-08-20 11:24:42 -03:00
2026-08-20 11:24:42 -03:00
2026-08-20 11:24:42 -03:00
2026-08-20 11:24:42 -03:00
2026-08-20 11:24:42 -03:00
2026-08-20 11:24:42 -03:00
2026-08-20 11:24:42 -03:00
2026-08-20 11:24:42 -03:00

rig

A runnable local model of a large, regulated estate — legacy and new side by side. Its job is onboarding and exploration, not a production replica: most services are deliberately mocked, because what has to be faithful is the topology, not the workloads.

Prerequisite

Docker. Nothing else — no curl, no jq, no python, no apt repositories.

Read the docs first

make docs      # serves on localhost, prints the URL

They run before anything is installed, which matters because they are the instructions for everything else. No cluster and no toolchain required.

Then

make station        # report host and config problems; changes nothing
make deps          # install the toolchain (add `core` on a managed machine)
make cluster up    # build the cluster for the active profile

make help lists every target.

On a machine where Docker really is the only thing installed, make deps has nothing to download with — see BOOTSTRAP.md, which runs the toolchain through the wizard container and carries on to scaffolding and running a new project.

One directory is one environment

Copy this directory, rename it, run it. Cluster name, kubectl context, image tags and the host port block all derive from the directory name, so copies never collide and neither one's teardown can touch the other.

rig lives inside soleprint, at spr/rig — it is soleprint's cluster half, and a copy is a sibling: spr/acme-rig. That is why the ignore rules for client rigs sit in spr/.gitignore rather than here; a rule in this directory cannot see a directory beside it.

Profiles

A profile is the shape of the cluster: how many nodes, which addons, whether the apiserver audits. They live in ctrl/env.d/, and the active one is PROFILE.

Profile For
minimal the default. One node, no addons, boots fast.
client the regulated-estate shape — multi-node, audit on, registry mirror.
offline air-gapped: everything from a preloaded local registry.
data the dependency containers a soleprint room asks for.
PROFILE=data make cluster up
PROFILE=data make addons install
make addons                       # what the active profile wants, and what exists

A profile names a cluster shape — a file in ctrl/k8s/ — rather than restating node count and audit as variables:

shape nodes audit used by
kind-config.yaml.tpl 1 off minimal, data
kind-config.audit.yaml.tpl 1 on offline
kind-config.client.yaml.tpl 3 on client

Both numbers are read back out of the chosen file, so the YAML is the only place that decides and there is nothing to drift. The layout under ctrl/k8s/ is the same as every other project here — a kind config, a kustomize base/, an overlays/dev/ — see ctrl/k8s/README.md.

Addons

Each addon is its own idempotent script in ctrl/addons/, and a profile names the ones it wants in ADDONS. Adding one is adding a file — there is no dispatcher to edit.

There is no ingress controller, deliberately. They pin a narrow window of Kubernetes versions, so depending on one would constrain which k8s a rig can be built with — and running a trailing-edge control plane to model a legacy estate is the whole point. Services are reached through MetalLB and type: LoadBalancer, which carries no such constraint and is also what a real cluster does.

Addon Does
metallb gives type: LoadBalancer an address it can actually reach
cert-manager a local CA, so TLS works offline
metrics-server makes kubectl top work on kind
postgres database, in the data namespace
redis cache and broker
airflow scheduled pipelines; needs postgres and redis

The last three are the cluster half of soleprint's cabinets. A room declares what it needs once, in cfg/<room>/data/cabinets.json; soleprint's build.py composes those services into docker-compose.yml for a laptop, and these install the same ones here. The names match on purpose — each cabinet carries a rig_addon field pointing at ctrl/addons/<name>.sh.

Plain manifests rather than helm charts, like every other addon: a chart repo is a network dependency, and the offline profile exists precisely so there is a path with none. Images are pinned in ctrl/versions.env and can be preloaded.

Passwords are generated on first install and kept across re-runs, so re-running an addon never rotates a credential out from under something already connected:

kubectl -n data get secret postgres -o jsonpath='{.data.POSTGRES_PASSWORD}' | base64 -d
kubectl -n data port-forward svc/airflow 8080:8080