feat(meta): Agent Substrate ships inside the chart — the Substrate line's substrate and substrate-crds components on one pin, the bootstrap hook, Substrate's database on the platform CNPG Cluster, the snapshot location, atelet scheduling, PolicyExceptions and network policies for the worker pods and Substrate's hops - #359
Merged
Merged
Conversation
teemow
force-pushed
the
substrate-in-chart
branch
from
September 11, 2026 00:51
de0674a to
89c5aaf
Compare
teemow
changed the base branch from
kagent-line-4-0
to
v1alpha3-templates-shared-muster-retired
September 11, 2026 00:51
Published Helm chart
|
| Chart | agent-platform |
| Version | 4.2.1-dev.substrate-in-chart.2026-09-11.03-54-03.ha38b66c |
| OCI reference | oci://gsoci.azurecr.io/charts/giantswarm/agent-platform:4.2.1-dev.substrate-in-chart.2026-09-11.03-54-03.ha38b66c |
| Digest | sha256:307cad0cffa4fdd203a4c9efdbdcbabe2575c57c256d95358b04f128dac68218 |
| Registry | public — gsoci.azurecr.io |
| Git catalog | giantswarm-test-catalog (index) |
Pull this chart
helm pull oci://gsoci.azurecr.io/charts/giantswarm/agent-platform --version 4.2.1-dev.substrate-in-chart.2026-09-11.03-54-03.ha38b66cPosted by architect-orb · build 5445 · commit a38b66c · updated in place on every push
teemow
force-pushed
the
substrate-in-chart
branch
from
September 11, 2026 00:57
89c5aaf to
ea71814
Compare
9 tasks
Published Helm chart
|
| Chart | agent-platform-connectivity |
| Version | 4.2.1-dev.substrate-in-chart.2026-09-11.03-54-03.ha38b66c |
| OCI reference | oci://gsoci.azurecr.io/charts/giantswarm/agent-platform-connectivity:4.2.1-dev.substrate-in-chart.2026-09-11.03-54-03.ha38b66c |
| Digest | sha256:79fdcad2f559a83d9f7a903f5b926cf063130b04837650829b01c27aec95385e |
| Registry | public — gsoci.azurecr.io |
| Git catalog | giantswarm-test-catalog (index) |
Pull this chart
helm pull oci://gsoci.azurecr.io/charts/giantswarm/agent-platform-connectivity --version 4.2.1-dev.substrate-in-chart.2026-09-11.03-54-03.ha38b66cPosted by architect-orb · build 5444 · commit a38b66c · updated in place on every push
teemow
changed the base branch from
v1alpha3-templates-shared-muster-retired
to
main
September 11, 2026 02:35
teemow
force-pushed
the
substrate-in-chart
branch
2 times, most recently
from
September 11, 2026 02:58
9fbaaf6 to
968194b
Compare
…rate + substrate-crds from the Giant Swarm Substrate line on one pin, the connectivity release's bootstrap hook (CA/JWT pools, trust anchor, ate-api authentication), Substrate's database on the platform CNPG Cluster (postgres.databases), kagent.harness.snapshotLocation, atelet scheduling and the WorkerPool architecture pin, PolicyExceptions per workload, network policies of Substrate's hops in both flavours, the 1.35 + feature-gates guard
…s.2 <0.0.28-0) components.substrate and components.substrate-crds move from the exact dev build the kagent line was proven with to the Giant Swarm Substrate line's release range, the kagent entry's shape; the range's floor 0.0.27-gs.2 is the customer BOM's pin and the tag of kagent.substrateWorkerPool.workerImage. v0.0.27-gs.2 is upstream's anticipated 0.0.27 with the carried patches the chart forwards values for: the egress gateway authorizes a resuming actor at CONNECT time as a frontend policy of its agentgateway dataplane (v1.5.1-gs.2), ate-api-server reads its PostgreSQL connection string from a Secret (postgres.connectionStringSecretRef — the CNPG mode's derived Secret) and the atelet DaemonSet takes nodeSelector, tolerations and affinity. tests: verify-components holds the floor to the BOM pin and the worker image; verify-components-charts drops the Substrate UNRELEASED entries and the worker-image fallback (the release resolves); verify-kyverno renders the release. Docs and the helm-docs README follow.
… history there components.<name>.targetNamespace now also sets the HelmRelease's storageNamespace, so the Helm release history lives where the release installs — where `helm -n <namespace>` stores it. Without it helm-controller kept the history in the platform namespace, found no release there and installed a second `substrate` release beside one installed by hand in ate-system (the lab's), instead of adopting it by name. Components without their own targetNamespace are unchanged (the fleet's flux-giantswarm history stays where it is). verify-components asserts it for substrate and substrate-crds.
teemow
force-pushed
the
substrate-in-chart
branch
from
September 11, 2026 03:56
0603594 to
a38b66c
Compare
6 tasks
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.
Problem
kagent API v2 has no runtime without Agent Substrate: a
Harnessrequiresspec.substrate.workerPoolRefandsnapshotPolicy.location, and every agent runs as a Substrate actor in a gVisor worker pod. The plan (bumblebee-plans#51 D3) decided that Substrate ships inside meta chart 4.0 and goes to every installation — installed, bootstrapped and upgraded from the same chart and GitOps chain as kagent, with no imperative step per installation. Until now Substrate was cluster infrastructure installed outside the chart (agentlab installs the charts and runs a Go port ofkubectl-ate adminduringup;kagent.controller.substrate.*points atate-system).Solution
Substrate is a component of the 4.0 chart. Base
main(4.0 is onmainsince #341; #299 and #345 landed before this — the branch is rebased on them). Three commits: Substrate in the chart, the Substrate pin as the line's release range, the release history of a component with its owntargetNamespacestored there. Merge order on the connectivity 4.0.x train: this → #361 (#344) → #354 (#346).Delivery route — roster components, not the kagent charts'
substratesubchart switches.components.substrate-crds(the threeate.devCRDs as templates) andcomponents.substrate(the control plane inate-system, the podcertificate-controller in its namespace) from the Giant Swarm Substrate line (oci://ghcr.io/giantswarm/substrate/helm). Both followcomponents.kagentwhen unset (the render refuses kagent on with either off —agent-platform.validateSubstrate), both land inate-systemthrough the new roster keycomponents.<name>.targetNamespace, both carry one version pin — the same range in bothversionRanges, its floor the tag ofkagent.substrateWorkerPool.workerImage, held to one bymake verify-components. Why: each release has its own Flux status on an installation, the order is explicit, the repository is a value (a mirror for a proxied installation, withsubstrate.image.registry), Substrate's namespace is not kagent's, and no fork stamping ofSUBSTRATE_REPOinto the kagent charts is needed. The pin is the line's release range>=0.0.27-gs.2 <0.0.28-0(the kagent entry's shape), its floor0.0.27-gs.2the BOM pin and the worker image's tag.v0.0.27-gs.2is upstream's anticipated 0.0.27 with the carried patches this chart forwards values for: the egress gateway authorizes a resuming actor at CONNECT time as a frontend policy of its agentgatewayv1.5.1-gs.2dataplane (giantswarm/substrate#7, #9), ate-api-server reads its connection string from a Secret and the atelet DaemonSet takes scheduling knobs (#8).Install order (implements the program's decision on the cut-over sequencing):
substrate-crds,kagent-crds→agent-platform-connectivity→substrate→kagent→ the managers. The connectivity releasedependsOnthe CRD components and never kagent or substrate; those twodependsOnconnectivity, whose hooks mint what their pods start against — so nothing waits on a pod that waits on a later release, andcomponents.kagent.installDisableWaitgoes.Bootstrap — the connectivity release's
pre-install,pre-upgradehook Job<release>-substrate-bootstrap(templates/substrate/bootstrap.yaml; theagent-platform.hooks.jobinclude intemplates/_hooks.tpl, identity<release>-hooks): mints the CA poolsservice-dns-ca-pool,pod-identity-ca-pool, the JWT authority poolactor-id-jwt-pool, the CA poolactor-id-ca-pool, the trust anchoractor-id-ca-certsand the ConfigMapate-api-authenticationwith the apiserver's own issuer (from its OpenID discovery document — the FQDN on kubeadm, the IRSA issuer on CAPA) withopensslin an init container andkubectl, in the pools' wire format; creates the two namespaces bare when missing (the substrate release adopts them); never touches a pool that exists — every upgrade logspresent. Neither line publishes akubectl-ateimage, andkubectl-ate admin make-*-poolis a plainCreate(not idempotent), so the Job is a shell port of the same key material rather than a wrapper around it.Database on CNPG —
postgres.databases(a map: Helm merges maps, so the chart ships defaults and an installation adds entries; 1b's #346 addskagent-v2to the same map): one CNPGDatabase<clusterName>-<key>per entry plus a derived connection Secret<clusterName>-<key>-appwritten by thepost-install,post-upgradehook<release>-postgres-databasesintopostgres.namespaceand everysecretNamespacesentry (CNPG mints<clusterName>-appfor the initdb database only). The shippedsubstrateentry is ate-api-server's database;substrate.postgres.enabled: autoresolves in the meta chart's shape pass (the Cluster's Database withpostgres.enabledand a derivedsubstrate.postgres.connectionStringSecretRef; the bundled StatefulSet without the Cluster — a lab). The substrate chart'spostgres.connectionStringSecretRefis the carried patch giantswarm/substrate#8 (cherry-pick of the open upstream kagent-dev/substrate#32).Snapshot store —
kagent.harness.snapshotLocation(the contract name; #344 renders it into the Harness) is required whenever kagent is on; S3 via IRSA on CAPA (bucket provisioning documented, not rendered), an S3-compatible store throughsubstrate.atelet.extraEnv, a lab's in-cluster store.Scheduling —
substrate.atelet.{nodeSelector,tolerations,affinity}(carried patch, giantswarm/substrate#8, inv0.0.27-gs.2; default: every node the DaemonSet controller admits — the worker nodes on a cluster with tainted control planes); the WorkerPool pinned toamd64(kagent.substrateWorkerPool.template.nodeSelector).Kyverno — one
PolicyExceptionper workload (substrate-atelet,substrate-workersonate.dev/worker-pool,substrate-control-plane,substrate-podcertificate-controller), each naming exactly the rules its pod spec violates with theirautogen-copies;kyvernoPolicies.rules(rule → ClusterPolicy) replaces the fourseccomp*/volumeTypes*keys; thekagent-declarative-seccompexception (app: kagent) is gone. Newmake verify-kyvernocomputes every workload's violations from the rendered pod specs (and the worker pod from ate-controller's template) and holds the exceptions to them.Network policies (both flavours,
templates/substrate/netpol.yaml) — Substrate's hops and the actors' destinations on the egress gatewayatenet-egress, where an actor's outbound connections leave (muster, the kagent controller, the LLM listener/provider, DNS); the worker pods reach only the egress gateway, the dns and the cluster DNS; theapp: kagentagent-pod rules go; the kagent controller opens ate-api and the router and admits the egress gateway.Prerequisites — README "Agent Substrate", UPGRADE.md: Kubernetes 1.35, the three gates on the three components, giantswarm/cluster#1005 and the cluster chart's bridging knob, the gVisor asset for proxied installations, one architecture per pool. A live render refuses a cluster without
certificates.k8s.io/v1beta1/PodCertificateRequest, naming the gates (kubelet's undetectable — the message says so);helm template/CI never refused. Security write-up:docs/substrate-security.md.Verification
make verify-*targets green, plus the newverify-kyverno;verify-kagent-netpol,verify-postgres,verify-components,verify-components-charts,verify-auto,verify-modes(golden diff filtered byGOLDEN_EXCLUDEfor the two intentionally changed objects) extended;helm lintboth charts;pre-commit run --all-files(schemas, helm-docs).make verify-components-chartsandmake verify-kyvernorender the substrate chart at the pin with the forwarded values — red while the pin was the dev build that predates feat(helm): the Postgres connection string from a Secret (kagent-dev/substrate#32) and atelet scheduling knobs substrate#8'spostgres.connectionStringSecretRef(the checks doing their job), green since the pin isv0.0.27-gs.2(the pin commit: substrate and substrate-crds render the forwarded values at the range and at the BOM pin; every Substrate workload's violations match its exception).<cluster>-second-appinto the Cluster namespace and asecretNamespacesentry withdbname/uri/jdbc-uri/pgpassrewritten, a re-run logspresentand changes nothing (resourceVersion unchanged); the bootstrap hook (its two namespaces retargeted to scratch names) created the three CA pools, the JWT pool, the trust anchor and the authentication ConfigMap with the apiserver's FQDN issuer, a re-run logspresent×8. The minted pools were validated in Go against Substrate's wire format (x509.ParsePKCS8PrivateKey,ParseCertificate, key ↔ cert, self-signature,IsCA+keyCertSign, the trust anchor equals the active root, ES256 = ECDSA P-256) — which caught the JWT key coming out as SEC1 fromopenssl pkey -outform DERin alpine/openssl 3.5; the hook writes PKCS#8 withopenssl pkcs8 -topk8 -outform DERnow.platform.chartPathon this branch,platform.substrate.enabled: false, the overlaylab-overlay-342.yaml: connectivity → this branch's dev build, siblings on the lab's pins,kagent.harness.snapshotLocation: s3://ate-snapshots/kagent, the bundled Postgres and RustFS, agent-manager at the released 1.x,kagent.serviceMonitor.enabled: false— Meta chart 4.0:components.kagentmoves to the Bumblebee-owned kagent line (kagent+kagent-crdsfromgiantswarm/kagent-upstream, one pinned build, the Go ADK image by digest), the ten bundled example agents go, the controller ServiceMonitor is gated off, the dev channel'ssemverFilter/range defaults return to release ranges (kagent API v2, bumblebee-plans#51 D2/D11/D12) #341's platform-test bridge). helm-controller adopted agentlab'ssubstrate/substrate-crdsreleases inate-systemby name ("release not managed by controller: found existing release in storage" →upgrade: revision 33 → 34, chart0.0.27-gs.2, one history, no second one in the platform namespace — thestorageNamespacecommit; the first window showed the default storage namespace produces a second release beside the hand-installed one instead). HelmReleasessubstrate-crds,substrate,kagent-crds,kagent,agent-platform-connectivityReady with thedependsOngraph as designed; everyate-systempod Running on gs.2 (ate-api-server ×2, ate-controller, atelet on every node, atenet-egress 2/2 on agentgatewayv1.5.1-gs.2, atenet-router 2/2, dns; the bundled postgres and rustfs kept); WorkerPoolkagent-defaultonateom-gvisor:0.0.27-gs.2ready 4/4; the bootstrap hook Job completed and a secondagentlab platformleft every pool at the same resourceVersion (idempotent; the CA/JWT pools' uid/resourceVersion equal the BEFORE snapshot throughout);agentlab platform-testPASS; anAgentTemplate(kagent.dev/v1alpha3, labelkagent.dev/harness: kagent) reached Ready on the platform Harness within the wait — an actor booted on the chart-installed WorkerPool through the chart-installed ate-api / atelet / atenet. Hand-back proven three times (suspend + finalizer drop + delete of the two HelmRelease CRs → nohelm uninstall; agentlab's own upgrade continues the same history), the lab restored to its baseline after each window (snapshot diff: only revision numbers and pod names).agents-test/toolsets-testreturn agentlab's POC-shape expectation against agent-manager 1.x's composed release (Agent proofs assert the Generic chart 1.x contract: HelmRelease → AgentTemplate + per-agent RemoteMCPServer, commit-pinned skills, 1.x range; one agent helper; the v1alpha2 proofs, dispatch and heals retire with 4.0 agentlab#140; the same in the sibling windows) — recorded, not gating.mainafter feat(connectivity): the kagent catalog renders at kagent.dev/v1alpha3, the shared muster RemoteMCPServer is retired (the agent chart 1.x renders one per agent), the muster MCP URL reaches the composers from one helper, the Kyverno Agent mutations go #360 (Connectivity 4.0:ModelConfigandRemoteMCPServertemplates atkagent.dev/v1alpha3; the shared cross-namespace musterRemoteMCPServeris retired — the Generic chart renders one per agent — and the KyvernoAgent/srt-settings mutations go (kagent API v2, bumblebee-plans#51 D7) #299: the v1alpha3 catalog, the branch publish job) and feat(connectivity): the kagent controller route is a GRPCRoute with the JWT policy on by default, the identity header set from the verified email claim, the UI path stripping it and the controller admitting agentgateway and the UI only #353 (Connectivity 4.0: the kagent controller route — JWT policy on by default inStrictmode with a request transformation that sets the identity header from the verifiedemailclaim and drops any inbound identity header, the UI path strips the identity header, a gRPC-capable route on the controller path for Swarmgeist and the Dev Portal (bumblebee-plans#51 D4, D13, D16) #345: the controller GRPCRoute — its rewrite of the controller-ingress policies and this PR's Substrate policies met intemplates/kagent/netpol.yaml,_helpers.tpland the CI verify list; the union is on this branch).Not lab-verified, and why
Kyverno admission (the lab has no Kyverno), Cilium enforcement of the network policies (no CNI policy enforcement in kind), the CNPG database and derived Secret for Substrate (the lab runs kagent's bundled Postgres, no CNPG operator — render-proven by
verify-postgres; the derived-Secret hook was proven against a CNPG-shaped Secret by #354's lab run), IRSA against S3 (the lab's store is the bundled in-cluster one). The runtime proof of the policies is the first Giant Swarm installation of the rollout. Two lab-tooling gaps met on the way, worked around in the window's overlay and left to giantswarm/agentlab#138 (the lab consuming Substrate from the chart as an installation will):agentlab platformhard-fails its post-install wait on the agent-manager MCPServer whencomponents.agent-manageris legitimately off, and its chartPath-shaped values setkagent.serviceMonitor.enabled: truefor a kagent line that serves no/metrics.agents-test/toolsets-teston agent-manager 1.x's composed release are giantswarm/agentlab#140.Teammate alignment
<release>-kagent-agent-muster-egress, which this PR removes (the v1alpha2 agent pods are gone); the same rules belong onsubstrate-atenet-egress(the actors' egress gateway). Rebase-after: whoever merges second moves the block; his PR is untouched.Strictmode with a request transformation that sets the identity header from the verifiedemailclaim and drops any inbound identity header, the UI path strips the identity header, a gRPC-capable route on the controller path for Swarmgeist and the Dev Portal (bumblebee-plans#51 D4, D13, D16) #345 owns the controller-ingress block oftemplates/kagent/netpol.yaml; this PR removes theapp: kagentpeer there (no such pod on API v2) and adds the egress gateway as a separate policy (substrate-actors-to-kagent-controller) — the later merger resolves.ModelConfigandRemoteMCPServertemplates atkagent.dev/v1alpha3; the shared cross-namespace musterRemoteMCPServeris retired — the Generic chart renders one per agent — and the KyvernoAgent/srt-settings mutations go (kagent API v2, bumblebee-plans#51 D7) #299 deletesdeclarative-agent-{pod-security,srt-settings}.yaml;verify-kagent-netpolallowsapp: kagentin exactly those two files until then.migrateonce per installation askagent-flux— rewrite portal-created Generic-chart releases to 1.x, emit the GitOps-owned diffs, then delete the v1alpha2 objects and CRDs (bumblebee-plans#51 D9, D11) #346 (1b) adoptspostgres.databasesas a map and addskagent-v2; Connectivity 4.0: one platformHarnessper managed namespace —kagenttype on the line's Go ADK digest,KAGENT_PROPAGATE_TOKENin its environment, the meta chart's default WorkerPool, the snapshot location from values, admission by the labelagent-platform.giantswarm.io/harness: kagent; WorkerPool replicas and pod template as values with lab-measured defaults (bumblebee-plans#51 D5) #344 renderskagent.harness.snapshotLocationand adds capacity knobs tokagent.substrateWorkerPool.template.Fixes #342