Skip to content

feat(platform): the lab runs the 4.x line as an installation does — Substrate and the platform Postgres from the chart, JWT Strict on the controller route, a measured preflight budget, every image side-loaded - #156

Merged
teemow merged 6 commits into
mainfrom
feat/4-0-topology-from-the-chart
Sep 11, 2026

Conversation

@teemow

@teemow teemow commented Sep 11, 2026

Copy link
Copy Markdown
Member

The lab runs the meta chart's 4.x line the way an installation does. Fixes #138.

What changes

Agent Substrate comes from the chart — the lab installs none. The substrate and substrate-crds components follow components.kagent inside the 4.x meta chart, the connectivity release's hook Job mints the CA/JWT pools, the kagent chart creates the WorkerPool, the connectivity chart renders the platform Harness. The lab's own install (substrate.go's install/uninstall, substratepools.go — the Go port of kubectl-ate admin, the substrate-values.yaml.tmpl, the platform.substrate knob and configure --substrate) retires with the last line that needed it (the POC dev channel); platform-down leaves Substrate to the chart's ordered teardown. What stays is the one check the chart cannot make early: agentlab up/platform refuse a cluster that does not serve certificates.k8s.io/v1beta1 (a kind cluster created before the gates) before the install — gated on the chart's rendered roster carrying the substrate release (platformRoster.shipsSubstrate), never on a version string. A lab that ran the POC channel upgrades in place: the chart's substrate component adopts the lab's substrate/substrate-crds releases in ate-system by name. HACKS U22 is FIXED upstream.

The lab template renders the 4.x shape natively (agent-platform-values.yaml.tmpl; no overlay): kagent.controller.auth.mode: trusted-proxy (the fleet's; unsecure is gone), kagent.controllerRoute.jwtAuthentication Strict against the lab Dex's JWKS (dex.dex.svc.cluster.local:5556/dex/keys, TLS), gateway.jwksEgress for the dex namespace always, kagent.harness.snapshotLocation: s3://ate-snapshots/kagent on the chart's bundled RustFS, kagent.serviceMonitor.enabled: false always (the line serves no /metrics), no lab workerImage (the chart pins it). CNPG in the lab, the fleet's shape: components.cloudnative-pg + postgres.enabled — one Cluster kagent-pg (one instance, 2Gi on local-path, the platform's postgresql-cnpg:18.3 operand, pgvector as a CNPG ImageVolume extension) with kagent's kagent_v2 and Substrate's databases as connectivity Database CRs, the derived-Secret hook live, the controller mounting kagent-pg-kagent-v2-app's uri as database.postgres.urlFile, both bundled Postgres instances off.

The identity proof (platform-test, with agents on): a call to the kagent controller through the edge without a token is refused by the JWT Strict policy (HTTP 401); a call with a valid token and a forged x-user-id is attributed to the token's subject by the controller (SystemService/GetCurrentUser claims), by muster/mcp-kubernetes (the viewer's kube-system Secrets stay Forbidden under User "oidc:viewer@lab.local" with the same forged header) and by agent-manager (a viewer's create_agent with x-user-id forged to the admin is the apiserver's Forbidden for the viewer; no AgentTemplate exists).

The preflight budget is measured on the 4.x topology (resources.go): every group carries what it requests and what it was measured to use (2026-09-11, agent-platform 4.7.11, kagent 0.11.0-gs.3, Substrate 0.0.27-gs.5); the Substrate group is the WorkerPool's four workers' requests (1000m / 2Gi; the control plane declares nothing and idles at ~480Mi), the platform Postgres group the operator + one instance (declare nothing), kagent's bundled Postgres only on a chart without CNPG; the run-time headroom follows the topology — one Go ADK turn inside a pre-provisioned worker (300m) with Substrate, six agent pods without — and the memory floor is the measured use plus a quarter, never below the requests. Groups gate on the rendered roster (platformTopology), so a 3.x lab is held to its own floor. The Docker resources table is the human copy.

The preload side-loads every image the 4.0 topology pulls and names them in the boot log: the scraper reads the run-time image keys (workerImage of the WorkerPool, imageName and the ImageVolume reference of the CNPG Cluster) next to the pod images (controller, UI, the Go ADK Harness by digest, Substrate's control plane, the CNPG operator, the hook Jobs' kubectl/openssl); the side-load lists what landed.

DefaultChartVersion4.7.11. Docs: platform.md (the dev channel without the Substrate coupling; "Agent Substrate and the platform Postgres — from the chart"; the deviations table), agents.md, getting-started.md (the measured table), cli.md, HACKS.md (U22), CLAUDE.md, README.md.

Lab verification (agentlab-dev2, agent-platform 4.7.11)

Window 1 — the released agentlab v0.33.0 moves the shared lab from the POC channel onto the released 4.7.11 (16:34Z–16:58Z; agentlab.yaml: chartVersion: 4.7.11, chartBranch removed, platform.substrate.enabled: false, a valuesFiles overlay with what v0.33.0 does not render — kagent.harness.snapshotLocation, controller.auth.mode: trusted-proxy, controllerRoute.jwtAuthentication Strict against the lab Dex, kagent.serviceMonitor.enabled: false, substrate.rustfs.enabled: true): agentlab platform exit 0 in 513 s, 14/14 HelmReleases Ready — kagent/kagent-crds 0.11.0-gs.3, substrate/substrate-crds 0.0.27-gs.5 adopted from the lab's own releases (helm -n ate-system history substrate: rev 41 0.0.27-dev…h213d76b → 42 0.0.27-gs.5, one history, the pools untouched), connectivity 4.7.11, agent-manager 1.1.1, backstage 2.1.2, model-manager 0.20.0; the POC Harness claude, RemoteMCPServer muster and the REST HTTPRoute pruned by the connectivity upgrade; Harness kagent (selector agent-platform.giantswarm.io/harness: kagent), WorkerPool kagent-default 4/4 on ateom-gvisor:0.0.27-gs.5, GRPCRoute kagent-controller + AgentgatewayPolicy kagent-controller-jwt (Strict). Proofs (v0.33.0): platform-test PASS 6/6, test PASS 10/10, skills-test PASS (Ready in 20 s, the turn answered from the skill); backstage-test, agents-test, toolsets-test and models-test's agent turn red on the POC shape they assert (the kagent.dev/harness label, created.agentTemplate) — agentlab#140/#142.

Window 2 — this branch's binary renders the 4.x shape itself (17:14Z–18:13Z; agentlab.yaml back to chartVersion: 4.7.11 and nothing else — no overlay, no substrate knob): agentlab platform exit 0 in 217 s, 15/15 HelmReleases Ready (cloudnative-pg 0.29.0 joined the roster); the boot log names what it side-loaded (ghcr.io/cloudnative-pg/cloudnative-pg:1.30.0, ghcr.io/giantswarm/kagent/golang-adk@sha256:a2d23f5e…, gsoci.azurecr.io/giantswarm/postgresql-cnpg:18.3, gsoci.azurecr.io/giantswarm/pgvector:0.8.2-18-bookworm, …; 34 refs derived, 9 loaded, 25 already on the node) and reports Agent Substrate 0.0.27-gs.5 (the actors' runtime, from the chart) runs in ate-system. CNPG live: Cluster kagent-pg healthy (1 instance), Database kagent-pg-kagent-v2 (kagent_v2, applied) and kagent-pg-substrate (applied), the derived Secrets kagent/kagent-pg-kagent-v2-app and ate-system/kagent-pg-substrate-app written by the connectivity hook; the kagent controller mounts kagent-pg-kagent-v2-app at /etc/cnpg with POSTGRES_DATABASE_URL_FILE=/etc/cnpg/uri and AUTH_MODE=trusted-proxy; ate-api-server reads ATE_API_POSTGRES_CONNECTION_STRING from kagent-pg-substrate-app; kagent-postgresql and ate-system's postgres-0 are gone. The node's Allocated resources read exactly the model's requests: 3340m / 4388Mi. Proofs: platform-test PASS 8/8 — including the new identity legs: a call to the controller through the edge without a token → HTTP 401 from the JWT Strict policy; GetCurrentUser with the admin token and x-user-id: attacker@lab.localemail: admin@lab.local; the viewer's kube-system Secrets with the forged header → Forbidden for User "oidc:viewer@lab.local"; the viewer's create_agent with x-user-id forged to the admin → the apiserver's Forbidden for the viewer, no AgentTemplate; test PASS 10/10; skills-test PASS on the CNPG shape (Ready in 20 s, the turn answered, 70 s) after one finding: the database switch under the running WorkerPool left the workers pinned to actors the new database never knew (AssignWorker: ResourceExhausted: no free workers available; the first skills-test red after its 10-min wait) — recreating the pool's pods freed them in 15 s, documented in platform.md's gotchas. backstage-test, agents-test, toolsets-test, models-test's turn: the same POC-shape assertions as in window 1 (agentlab#140/#142). Teardown/reinstall on the chart-owned Substrate + CNPG: platform-down exit 0 in 45 s (the chart's ordered teardown; left behind as the chart's contract: ate-system with the pools, podcertificate-controller-system with its pools, the podcert ClusterTrustBundles, the kept CNPG Cluster kagent-pg and its PVC, 19 kept CRDs; no Helm release in ate-system). platform (the reinstall) brought Substrate back on the kept trust chain (agent-platform#384's fix in gs.5: the pools untouched since 2026-09-10, the bootstrap hook present), the CNPG operator re-adopted the kept Cluster and the hook found the derived Secrets present — and exposed a pre-existing preload defect fixed in this PR: the side-load carried digest-pinned references (RustFS, the bucket-init CLI, the Harness digest) through docker save + ctr images import, which records a digest-only reference as an unnamed import-<date>@sha256:… image, and the new RustFS pod failed with failed to check if this is a checkpoint image … import-2026-09-11@sha256:… not found, so the substrate release never became Ready and the reinstall's wait timed out (exit 1 after 950 s). Healed on the node (the stale CRI records removed, containerd restarted; rustfs Running, substrate Ready two minutes later, 15/15 Ready); since 80c0a5b the side-load leaves digest-pinned references to the kubelet and says so in the boot log. On the recovered reinstall: skills-test PASS (Ready in 2m35s — the actor image re-pulled after the cache purge — and the turn answered), test PASS; then the idempotent platform re-run exit 0 in 65 s (nothing to do beyond re-reading every component; 15/15 Ready) and the final platform-test/test/skills-test PASS/PASS/PASS (skills-test Ready in 20 s, the turn answered, 76 s; the sampled turn: the worker hosting the actor peaked at 64 MiB with 2.0 CPU-seconds over boot and turn, idle workers 12 MiB — the numbers in resources.go). The node's Allocated resources after the reinstall: 3340m / 4388Mi, the model's requests..

Not verified here: the 3.x line's own floor on a live 3.x lab (unit-tested: the topology gate holds the 3.x line to the old requests + six-pod headroom); a fresh agentlab up from nothing on the 4.x line (the shared lab is long-lived; the preflight's roster render and the preload's new lanes ran on platform); a Podman host.

Alignment with open PRs

agentlab#153 (marians, fix(models): hostmodels.go/backends.go/models*.go/discover.go/modelmanager.go) — no shared file; #52 (paurosello, apsPath/llmRouting: config.go, platform.go, render.go, the values template) — this PR touches those files too; #52 is stale (2026-09-03) and rebases onto this; #43 (pipo02mix, pinned-version warning: config.go, up.go) — config.go's DefaultChartVersion moves to 4.7.11 here, the warning's comparison is unaffected.

…ubstrate and the platform Postgres from the chart, JWT Strict on the controller route, a measured preflight budget

WIP: the lab's own Substrate install retires (the chart ships it), the values
template renders the 4.x shape (trusted-proxy + JWT Strict against the lab
Dex, the snapshot store, CNPG with kagent's and Substrate's databases), the
preflight budgets what the rendered roster ships, the preload scrapes the
run-time image keys and names what it side-loaded, DefaultChartVersion 4.7.11.
…t at the edge, the token's subject at the controller, muster and agent-manager); the measured 4.x resource model; docs and HACKS U22
…new — free the workers after a database switch
…chive of a digest-only reference imports unnamed and the CRI cannot start a pod from it
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant