Fixed
- (deps) Update module github.com/giantswarm/mcp-oauth to v1.4.3 in #492 by @renovate[bot]
Full Changelog: https://github.com/giantswarm/klaus/compare/v0.1.22...v0.1.23
Full Changelog: https://github.com/giantswarm/klaus/compare/v0.1.22...v0.1.23
InferenceService serving path — llm-d is the one serving path. Breaking (giantswarm/agent-platform#574; the platform’s decision that no model is served on the classic vLLM path any more, no fallback, no deprecation window). The connectivity chart rendered two paths for a served model: the classic InferenceService on the chart’s kserve-vllm ClusterServingRuntime (modelServing.runtime, modelServing.additionalRuntimes, the classic predictor pod shape in the Kyverno policies, the network policies and the PolicyException, the serving.kserve.io/v1beta1 check behind kserve.requireApi, the runtime / runtimes keys of the discovery ConfigMap) and the llm-d path (LLMInferenceService on the well-known LLMInferenceServiceConfigs, the models Gateway), while every shipped preset was composed onto the llm-d template by model-manager. Now the chart renders no serving.kserve.io object at all: model-manager composes a published preset into an LLMInferenceService, and the serving namespace’s policies, network policies and exception select that workload pod alone (kserve.io/component=workload, app.kubernetes.io/part-of=llminferenceservice, container main, port 8000). Removed, and refused by the render naming the key: templates/model-serving/clusterservingruntime.yaml; the values modelServing.runtime, modelServing.additionalRuntimes (4.36.0, #550), modelServing.networkPolicy.predictor (its additionalIngressNamespaces is modelServing.networkPolicy.additionalIngressNamespaces), modelServing.serving.deploymentStrategyType and .timeoutSeconds; the preset fields spec.runtime and spec.predictor (the shipped qwen3-8-27b, qwen3-8-27b-l40s and qwen3-8-flash-next-nvfp4 lose their predictor.timeout, the Flash-Next preset its runtime: kserve-vllm-b12x — its B12X serving image is the well-known config’s to name); the discovery ConfigMap’s spec.runtime and spec.runtimes. modelServing.kserve.requireApi and modelManager.kserve.requireApi check serving.kserve.io/v1alpha2 (LLMInferenceService) and are satisfied by the kserve-llmisvc-crd + kserve-llmisvc-resources components. The meta chart drops the classic controller’s components kserve-crd and kserve-resources and their values blocks (refused too): kserve-llmisvc-crd ships every CRD the llm-d path needs (llminferenceservices, llminferenceserviceconfigs, clusterstoragecontainers), and the kserve-llmisvc-resources release renders the control plane’s shared objects itself (kserve.createSharedResources: true, the chart’s default; a false is refused) with controller.deploymentMode: Standard and gateway.disableIngressCreation: true, receives the derived models-Gateway reference (controller.gateway.ingressGateway.kserveGateway) and depends on kserve-llmisvc-crd alone; the connectivity release depends on no kserve component (it renders no serving.kserve.io CR), model-manager depends on kserve-llmisvc-resources and its range is >=0.23.0 <2.0.0 (model-manager 1.0.0 composes LLMInferenceServices only; nothing the chart forwards names the values it drops). examples/serving-slice.yaml, ci/test-slice-serving-values.yaml, the CI fixtures and the BOM switch the llm-d components on alone; tests/verify-runtimes.py, its fixture and the classic predictor pod fixture are gone, and verify-wiring asserts that the classic keys and components fail the render naming them and that the serving render carries no serving.kserve.io object. UPGRADE.md, “the classic KServe InferenceService path is removed”. Closes giantswarm/agent-platform#561 (removed with the feature).modelServing.imageVerification is on by default: a model pod’s images are verified at admission against the Giant Swarm signing identity (giantswarm/agent-platform#575; epic giantswarm/giantswarm#37853). 4.41.0 gave the block its defaults — images: [gsoci.azurecr.io/giantswarm/*], one keyless attestor for the identity the architect orb’s cosign keyless signing gives every image a Giant Swarm CircleCI project builds or re-signs (issuer https://oidc.circleci.com, subject the pipeline definition that ran), type: SigstoreBundle — and left the switch off until every image a model pod runs was signed. Every one is now, verified with cosign verify against that identity: the curated model image the shipped oci:// preset names, gsoci.azurecr.io/giantswarm/models/qwen3-8-flash-next-nvfp4:7c4f1bc1a2d6, which giantswarm/models builds, signs and publishes (giantswarm/agent-platform#554); the llm-d runtime and sidecars of the well-known LLMInferenceServiceConfigs at the llm-d-fast/ prefix (llm-d-cuda:v0.8.0, llm-d-router-disagg-sidecar:v0.9.0, the endpoint picker, tokenizer and latency-predictor images; giantswarm/llm-d signs every mirror and repack); the KServe storage-initializer:v0.20.0 and agent:v0.20.0 retagger copies (giantswarm/retagger#1230); the pre-pull DaemonSet’s pause:3.10.1; and the vllm-b12x:20260913 mirror an installation serves the flash-next preset with. Both charts’ defaults carry enabled: true, so a serving shape under Kyverno renders <release>-model-serving-image-verification without being asked: one verifyImages rule per pod shape over the model pods of the serving namespace at CREATE and UPDATE, every verified image pinned to its digest, a signature required, Enforce. A container image matching no pattern is left alone, so an installation serving from a registry of its own is untouched until it names its pattern and its signer; an installation whose model pods run an image under the platform’s namespace that none of these signed adds its signer or turns the switch off before it upgrades (UPGRADE.md). make verify-model-serving-policies asserts the default render carries the policy with its defaults and that the switch off or no Kyverno renders none; make verify-target holds the switch on both sides of the golden comparison until origin/main carries it. Proof: kyverno apply of the rendered default policy over a pod of the llm-d workload shape naming every image above admits it with every image rewritten to its digest, and denies a pod naming an unsigned image under the pattern with sigstore bundle verification failed; the same two cases on a lab’s Kyverno.gateway.metricLabels’ agent and agent_namespace were the source workload resolved by IP (source.unverifiedWorkload.serviceAccount / .namespace), the shape of kagent 0.10 — one Deployment and one ServiceAccount per agent. Under kagent API v2 an agent is an AgentTemplate executed as a Substrate actor in the shared WorkerPool, and every actor connection leaves the worker as a mTLS CONNECT through the Substrate egress (atenet-egress), which authenticates the actor at CONNECT time and tunnels the bytes opaquely: by source IP every agent’s model call was the egress pod, every agent’s spend one row named after a proxy, and user unknown (actor traffic carries no bearer). The egress cannot add a header. The kagent runtime sends the identity on every model call as request headers — x-kagent-agent and x-kagent-agent-namespace (the template’s name and namespace) and x-kagent-user (the person of the turn) — and the three default expressions read them when, and only when, the source workload is the egress (agent-platform.substrate.egressCall: the namespace and ServiceAccount from the chart’s Substrate names, agent-platform.substrate.egressServiceAccount); every other caller keeps its own ServiceAccount, so no pod relabels itself by sending the header, and a call from the egress without the header reads unknown — the egress ServiceAccount itself appears in no new sample. The identity stays an accounting identity, never an authorization input. Mirrored in the meta chart. make verify-metric-labels asserts the three expressions with the predicate and the header names (parsed, since toYaml folds a long expression across lines), the person label’s hold and claim knob behind the predicate, an installation’s own expression replacing the default, and the meta chart forwarding the expressions unrendered; verify-target holds the old expressions equal on both sides of its golden comparison until origin/main carries this. README “Metric labels”. Installations act: none — the -metrics policy is re-rendered on the upgrade; the runtime half is the kagent line’s carried patch, and until an installation runs it the egress row reads unknown instead of the egress ServiceAccount.gsoci.azurecr.io under the upstream path — giantswarm/kagent/{controller,ui,golang-adk,claude-harness}, giantswarm/substrate/{ateapi,atecontroller,atelet,atenet,podcertcontroller,ateom-gvisor}, giantswarm/fluxcd/{source-controller,helm-controller} — with the index digests preserved, so one registry value moves per line and everything the charts stamp stands: kagent.registry is gsoci.azurecr.io (the controller and UI at the chart’s tag, the Go ADK and Claude harness images at the digests the chart carries, the kagent-images ConfigMap Substrate’s image cache reads), substrate.image.registry is gsoci.azurecr.io/giantswarm/substrate (the control plane at the chart’s tag; the worker image the meta chart derives from the Substrate floor follows it, as does the helper’s default) and flux-engine.instance.distribution.registry is gsoci.azurecr.io/giantswarm/fluxcd (the operator composes <registry>/<controller>:<tag>; Flux 2.9.5’s source-controller:v1.9.5 and helm-controller:v1.6.4 under the pinned operator). The two lines’ chart sources stay on ghcr.io (components.kagent*.repository, components.substrate*.repository): retagger copies images, not charts, and the lines publish their release charts to gsoci through their own tag workflows once that lands — the three chart references (and the upstream CloudNativePG chart, behind a Giant Swarm wrapper release on the same operator line) are what make verify-images still tolerates by name. A dev build’s images are not copied: the dev channel sets kagent.registry: ghcr.io and substrate.image.registry: ghcr.io/giantswarm/substrate next to its filter (README “Dev channel”). verify-components, verify-worker-image, verify-kyverno and verify-engine assert the new defaults; verify-target holds them on both sides of the golden comparison until origin/main carries them. UPGRADE.md.Full Changelog: https://github.com/giantswarm/agent-platform/compare/v4.41.0...v4.41.1
gsoci.azurecr.io reference, and modelServing.imageVerification defaults to the Giant Swarm signing identity (giantswarm/agent-platform#575; epic giantswarm/giantswarm#37853). The meta chart’s kubectl hook images (gitops.hooks.image, gitops.hooks.helmImage), the bundled Flux engine’s operator image (flux-engine.operator.image) and the connectivity chart’s hook images (hooks.kubectlImage, hooks.opensslImage) move to their gsoci copies — giantswarm/kubectl, giantswarm/alpine-k8s, giantswarm/alpine-openssl (mirrored by giantswarm/retagger#1228, the same digests as upstream) and giantswarm/flux-operator (the Renovate manager that moves the subchart’s appVersion with the operator tag names the gsoci image). modelServing.imageVerification gains defaults: images: [gsoci.azurecr.io/giantswarm/*] and one keyless attestor — issuer https://oidc.circleci.com, subject ^https://circleci\.com/api/v2/projects/[a-f0-9-]+/pipeline-definitions/[a-f0-9-]+$ — the identity the architect orb’s cosign keyless signing gives every image a Giant Swarm CircleCI project builds or re-signs (the certificate’s subject is the pipeline definition that ran; the orb verifies with the same pair), and a new type (SigstoreBundle, the default | Cosign): cosign 3 attaches the signature to the image as a Sigstore bundle in an OCI referrer and Kyverno reads one format per rule, so an orb-signed image verified with Kyverno’s default Cosign type fails with no signatures found (kyverno apply against a signed gsoci image: Cosign fails, SigstoreBundle passes with the attestor). The switch stays off in the defaults until the curated model images are published and signed (giantswarm/agent-platform#554); the runtime and the llm-d sidecars (the llm-d line signs every mirror and repack since v0.5.0) and every image retagger copies (giantswarm/retagger#1230) already carry the identity. New make verify-images (in CI through verify-all): every container image of every pod template, every OCIRepository url and every registry and reference the meta chart forwards in the rendered defaults of both charts — the engine and every component on — is on gsoci; the references whose copies are still being published are tolerated by name with the issue that lands each — the kagent and Substrate lines’ charts and images, the CloudNativePG chart and the Flux controllers (giantswarm/agent-platform#580, giantswarm/retagger#1229), the classic runtime’s vLLM image (giantswarm/agent-platform#574) — and a stale entry fails the check. The five KServe components (kserve-crd, kserve-resources, kserve-llmisvc-crd, kserve-llmisvc-resources, kserve-runtime-configs) move to 0.4.x, the giantswarm/kserve line whose every image default is a gsoci reference (giantswarm/kserve#81: the controllers, agent, router, storage-initializer, the local-model images, kube-rbac-proxy; the classic runtimes ship no image any more, which this chart’s servingruntime.enabled: false never rendered); 0.4.1 is the version the storage-initializer’s resources follow kserve.storage.resources from (giantswarm/kserve#86) — the BOM example pins it. The connectivity CI fixtures name the gsoci pgvector mirror. README: “Verifying the model images”. Lab proof (a fresh agentlab on the branch’s dev build, Kyverno 1.17.2): platform-test all PASS; every hook Job ran from gsoci.azurecr.io/giantswarm/alpine-k8s / alpine-openssl and the Flux operator from gsoci, the only images off gsoci the pending set; the rendered verifyImages policy with the defaults admitted the orb-signed kserve-controller:0.2.4 pinned to its digest (kyverno.io/verify-images … pass) and the retagger-signed kubectl:v1.37.0 likewise, left a docker.io image alone (no pattern matches), denied the unsigned alpine-k8s:1.37.0 and — with the subject pattern swapped for a foreign identity — denied the signed image (sigstore bundle verification failed: no matching signatures found). Installations act: none for the defaults — a hook Job is created per event, nothing rolls; an installation with modelServing.imageVerification.enabled: true and a signer of its own that writes cosign 2’s .sig tags sets type: Cosign (UPGRADE.md).Full Changelog: https://github.com/giantswarm/klausctl/compare/v0.4.8...v0.4.9
api.UpdateMCPServerState, which queued a reconcile unconditionally, so every new session’s SSO fan-out cost one pass per server it reached, several times per ten minutes on a busy installation, while nothing changed. The handler now only hands the state to the service. A state that actually changes (Auth Required to Connected on the first session, back on the last session’s loss) reaches the CRD status through the service’s state-change event and the StateChangeBridge, as every other state change does; an unchanged state is logged at debug and costs nothing. TriggerReconcile is gone from the reconcile manager’s API; it had no other caller. (#1290)oauth-auth-config-change-resets-live-sessions (the pin of an authorization server lost to a status write of the connect it followed, so the session stayed Connected and the reset never came) and oauth-pinned-authorization-server-change-takes-effect (the same loss, surfacing as the sign-in rate limit). Status now lives under status/ at the definition’s relative path (status/mcpservers/<name>.yaml), the definition files are only ever written by core_mcpserver_create/core_mcpserver_update and their workflow counterparts, a status: block left in a definition file by an earlier muster is ignored, and a status write no longer fires the change detector that watches the definition directories. (#1288)