# `ctrl/k8s` — cluster shape, and what runs on it Same layout as every other project here: a kind config, a kustomize `base/`, and an `overlays/dev/` that patches it. ``` kind-config*.yaml.tpl the cluster itself — nodes, ports, audit base/ the components, as plain manifests overlays/dev/ how this rig differs from the base audit-policy.yaml mounted into the apiserver by the audit shapes ``` ## Why the cluster config is a template Every other project checks in a literal `kind-config.yaml`, because there is exactly one `unt` and one `nvi`. A rig is copied and renamed to make a second environment, and both the cluster name and the host port block follow the directory name — so a literal would make every copy collide on both. `ctrl/cluster.sh` renders it with `sed`, substituting `${CLUSTER}`, `${NODE_IMAGE}`, `${HTTP_PORT}` and `${HOST_WORKDIR}`. Not `envsubst`: that is `gettext-base`, which a minimal Debian does not have, and Docker being the only prerequisite is the one promise rig makes. **The chosen file is the source of truth for node count and audit.** `lib/config.sh` reads both back out of it, so a profile names a shape and does not restate what the YAML already says. | file | nodes | audit | profiles | | --- | --- | --- | --- | | `kind-config.yaml.tpl` | 1 | off | the default; the `data` example | | `kind-config.audit.yaml.tpl` | 1 | on | the `offline` example | | `kind-config.client.yaml.tpl` | 3 | on | the `client` example | With no profile the default shape is used; a profile picks another with `KIND_CONFIG`. Adding a shape is adding a file — there is no dispatcher to edit. Audit is an apiserver flag and therefore fixed at creation: changing it is `make cluster reset`, not a re-apply. ## `base/` — replace these **The two components in `base/` are examples, not the system.** They exist so the real manifests have a shape to be written against. The real ones are expected to be versioned **separately from the installer** — they change on a different cadence, by different people, under different review. Point `MANIFESTS_DIR` in `ctrl/.env` at their overlay and rig stops owning them: ``` MANIFESTS_DIR=../platform-manifests/overlays/dev ``` Until then it defaults to `ctrl/k8s/overlays/dev`. ### The three states a component can be in Switching between them should be a one-line change, never a rewrite. The DNS name stays the same in every case, so callers never know the difference: | state | what exists | when | | --- | --- | --- | | **real** | an image built from source, hot-reloaded | the one thing you are working on | | **mock** | a stub returning canned responses (`example-mock.yaml`) | everything else — most of the estate | | **remote** | no pod at all, just a Service (`example-remote.yaml`) | when the real system is reachable and you want it | Most components should be **mock**. What has to be faithful is the topology — names, ports, dependency order, who can reach whom, how it fails. The workloads are noise, and mocking them is what makes several copies of a large estate fit on one laptop.