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
Conversation
…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
…tstrap (operator 63Mi, instance 114Mi)
…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
…2 CPU seconds over a boot and a turn
teemow
marked this pull request as ready for review
September 11, 2026 18:15
This was referenced Sep 11, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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
substrateandsubstrate-crdscomponents followcomponents.kagentinside the 4.x meta chart, the connectivity release's hook Job mints the CA/JWT pools, the kagent chart creates theWorkerPool, the connectivity chart renders the platformHarness. The lab's own install (substrate.go's install/uninstall,substratepools.go— the Go port ofkubectl-ate admin, thesubstrate-values.yaml.tmpl, theplatform.substrateknob andconfigure --substrate) retires with the last line that needed it (the POC dev channel);platform-downleaves Substrate to the chart's ordered teardown. What stays is the one check the chart cannot make early:agentlab up/platformrefuse a cluster that does not servecertificates.k8s.io/v1beta1(a kind cluster created before the gates) before the install — gated on the chart's rendered roster carrying thesubstraterelease (platformRoster.shipsSubstrate), never on a version string. A lab that ran the POC channel upgrades in place: the chart'ssubstratecomponent adopts the lab'ssubstrate/substrate-crdsreleases inate-systemby 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;unsecureis gone),kagent.controllerRoute.jwtAuthenticationStrictagainst the lab Dex's JWKS (dex.dex.svc.cluster.local:5556/dex/keys, TLS),gateway.jwksEgressfor the dex namespace always,kagent.harness.snapshotLocation: s3://ate-snapshots/kagenton the chart's bundled RustFS,kagent.serviceMonitor.enabled: falsealways (the line serves no /metrics), no labworkerImage(the chart pins it). CNPG in the lab, the fleet's shape:components.cloudnative-pg+postgres.enabled— oneCluster kagent-pg(one instance, 2Gi on local-path, the platform'spostgresql-cnpg:18.3operand, pgvector as a CNPG ImageVolume extension) with kagent'skagent_v2and Substrate's databases as connectivityDatabaseCRs, the derived-Secret hook live, the controller mountingkagent-pg-kagent-v2-app'suriasdatabase.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 JWTStrictpolicy (HTTP 401); a call with a valid token and a forgedx-user-idis attributed to the token's subject by the controller (SystemService/GetCurrentUserclaims), by muster/mcp-kubernetes (the viewer's kube-system Secrets stay Forbidden underUser "oidc:viewer@lab.local"with the same forged header) and by agent-manager (a viewer'screate_agentwithx-user-idforged 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 (
workerImageof the WorkerPool,imageNameand the ImageVolumereferenceof 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.DefaultChartVersion→4.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,chartBranchremoved,platform.substrate.enabled: false, avaluesFilesoverlay with what v0.33.0 does not render —kagent.harness.snapshotLocation,controller.auth.mode: trusted-proxy,controllerRoute.jwtAuthenticationStrict against the lab Dex,kagent.serviceMonitor.enabled: false,substrate.rustfs.enabled: true):agentlab platformexit 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 410.0.27-dev…h213d76b→ 420.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 POCHarness claude,RemoteMCPServer musterand the RESTHTTPRoutepruned by the connectivity upgrade;Harness kagent(selectoragent-platform.giantswarm.io/harness: kagent),WorkerPool kagent-default4/4 onateom-gvisor:0.0.27-gs.5,GRPCRoute kagent-controller+AgentgatewayPolicy kagent-controller-jwt(Strict). Proofs (v0.33.0):platform-testPASS 6/6,testPASS 10/10,skills-testPASS (Ready in 20 s, the turn answered from the skill);backstage-test,agents-test,toolsets-testandmodels-test's agent turn red on the POC shape they assert (thekagent.dev/harnesslabel,created.agentTemplate) — agentlab#140/#142.Window 2 — this branch's binary renders the 4.x shape itself (17:14Z–18:13Z;
agentlab.yamlback tochartVersion: 4.7.11and nothing else — no overlay, nosubstrateknob):agentlab platformexit 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 reportsAgent Substrate 0.0.27-gs.5 (the actors' runtime, from the chart) runs in ate-system. CNPG live:Cluster kagent-pghealthy (1 instance),Database kagent-pg-kagent-v2(kagent_v2, applied) andkagent-pg-substrate(applied), the derived Secretskagent/kagent-pg-kagent-v2-appandate-system/kagent-pg-substrate-appwritten by the connectivity hook; the kagent controller mountskagent-pg-kagent-v2-appat/etc/cnpgwithPOSTGRES_DATABASE_URL_FILE=/etc/cnpg/uriandAUTH_MODE=trusted-proxy; ate-api-server readsATE_API_POSTGRES_CONNECTION_STRINGfromkagent-pg-substrate-app;kagent-postgresqland ate-system'spostgres-0are gone. The node's Allocated resources read exactly the model's requests: 3340m / 4388Mi. Proofs:platform-testPASS 8/8 — including the new identity legs: a call to the controller through the edge without a token → HTTP 401 from the JWTStrictpolicy;GetCurrentUserwith the admin token andx-user-id: attacker@lab.local→email: admin@lab.local; the viewer's kube-system Secrets with the forged header →ForbiddenforUser "oidc:viewer@lab.local"; the viewer'screate_agentwithx-user-idforged to the admin → the apiserver's Forbidden for the viewer, no AgentTemplate;testPASS 10/10;skills-testPASS 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 inplatform.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-downexit 0 in 45 s (the chart's ordered teardown; left behind as the chart's contract:ate-systemwith the pools,podcertificate-controller-systemwith its pools, the podcert ClusterTrustBundles, the kept CNPGCluster kagent-pgand its PVC, 19 kept CRDs; no Helm release inate-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 hookpresent), the CNPG operator re-adopted the kept Cluster and the hook found the derived Secretspresent— 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) throughdocker save+ctr images import, which records a digest-only reference as an unnamedimport-<date>@sha256:…image, and the new RustFS pod failed withfailed 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-testPASS (Ready in 2m35s — the actor image re-pulled after the cache purge — and the turn answered),testPASS; then the idempotentplatformre-run exit 0 in 65 s (nothing to do beyond re-reading every component; 15/15 Ready) and the finalplatform-test/test/skills-testPASS/PASS/PASS (skills-testReady 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 inresources.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 upfrom nothing on the 4.x line (the shared lab is long-lived; the preflight's roster render and the preload's new lanes ran onplatform); 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'sDefaultChartVersionmoves to 4.7.11 here, the warning's comparison is unaffected.