# 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 ```bash 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 ```bash 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](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. | ```bash 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`](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//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/.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: ```bash kubectl -n data get secret postgres -o jsonpath='{.data.POSTGRES_PASSWORD}' | base64 -d kubectl -n data port-forward svc/airflow 8080:8080 ```