Fixed
- (deps) Update module github.com/giantswarm/mcp-oauth to v1.3.23 in #1219 by @renovate[bot]
Full Changelog: https://github.com/giantswarm/muster/compare/v5.19.5...v5.19.6
Full Changelog: https://github.com/giantswarm/muster/compare/v5.19.5...v5.19.6
{prefix}cap:{sessionID} held, per connected server, the server’s full tool/resource/prompt list with every input schema — 9–180 KB a server, 0.9 MB a session on one installation — for 30 days, and every forwarded bearer (a Dev Portal, kagent or Backstage ID token) is a session of its own, so identical documents were written again and again: 448 session hashes, 462 MB, and muster-valkey was OOM-killed 19 times in 90 minutes with every token refresh blocked meanwhile. The hash field is now a 71-byte reference sha256:<hex>; the document lives once under {prefix}capblob:<hex> with the store TTL, refreshed by every Set that references it. Reads resolve the references (one pipelined GET per distinct document); a reference whose document has expired is a cache miss, which the aggregator already answers by listing the server again on the next connect. Fields written by an earlier muster still read, and on start the aggregator rewrites them to references in the background (MigrateInlineEntries: one SCAN, idempotent, logged with the session, field, document and byte counts) — an installation shrinks on its first start with this release instead of after 30 days. The store is proportional to the number of distinct documents now (that installation: 84 documents ≈ 1.3 MB, the references ≈ 3.6 MB). Unit tests run the store against an in-process miniredis.muster-valkey gets the memory a grown token store needs (valkey.valkey.resources: requests 256Mi, limits 1Gi, from 64Mi / 256Mi). Valkey loads the whole RDB into memory on every start; gazelle’s store held 247 MB after three weeks and every restart under the 256Mi limit was OOM-killed — when 4.12.0 rolled the pod once for the new annotation, muster’s token endpoint stayed down for five minutes until the limit was raised by hand. UPGRADE.md carries the recognition and the recovery.Multi-Attach). #431’s guards address voluntary disruption only; a spot reclaim terminates the instance whatever the budget says. New meta-chart block scheduling.singletons (nodeSelector, tolerations; empty by default, so no installation changes behaviour until it is set): merged into, and appended to, the scheduling knobs of the four single-replica stateful pods before their releases render — muster (muster.nodeSelector), muster-valkey (valkey.valkey.nodeSelector), the kagent controller (kagent.controller.nodeSelector), klaus-gateway (klausGateway.nodeSelector) — with a component’s own keys winning (agent-platform.scheduling.apply); held back from the connectivity release (components.agent-platform-connectivity.omitKeys), so a live connectivity chart that predates the block is never failed by it. The intended value on a Karpenter spot installation is karpenter.sh/capacity-type: on-demand (one small on-demand node for the four; the fleet’s NodePools admit it untainted) — UPGRADE.md carries the cost note, the roll and the “never on a cluster without Karpenter” warning. klaus-gateway takes the keys from chart 1.3.3 on (giantswarm/klaus-gateway#253: the earlier 1.x schemas refused every key under nodeSelector — found in the lab when the release failed validation). muster-valkey now carries karpenter.sh/do-not-disrupt: "true" (the valkey subchart’s valkey.valkey.podAnnotations) and a PodDisruptionBudget muster-valkey (valkey.podDisruptionBudget, minAvailable: 1, AlwaysAllow) rendered by the connectivity chart — neither the wrapper nor the upstream subchart has a budget knob — selecting the pod as the subchart labels it (name valkey, instance valkey), named after valkey.valkey.fullnameOverride; the key never reaches the valkey release (components.valkey.omitKeys). Every installation rolls the muster-valkey pod once. make verify-disruption extended: the merge and its precedence, the empty default (byte-identical component values), the hold-back, the valkey budget on/off/inert and its guards, a stray key under the block refused by the schema.Full Changelog: https://github.com/giantswarm/agent-platform/compare/v4.10.6...v4.11.0
substrate-s3proxy container mounts two emptyDirs (/tmp for the JVM, the image’s /data) and carried no ephemeral-storage requests or limits, so Kyverno’s require-emptydir-requests-and-limits (Audit on the fleet) reported Containers mounting emptyDir volumes must specify requests and limits for ephemeral-storage on every rollout — the one Kyverno finding the platform left on a fresh 4.x installation, and a refusal on a cluster that enforces the policy. kagent.harness.snapshotStore.s3proxy.resources now defaults requests.ephemeral-storage: 256Mi and limits.ephemeral-storage: 1Gi in both charts (the meta chart forwards the block; the façade streams, nothing of a snapshot lands on its disk), both emptyDirs take the limit as their sizeLimit, and the render refuses a resources block without either field, naming the key. make verify-substrate-store asserts the fields and the bound on the capz and the façade-alone shapes, the knob, the forwarded default and the guard; tests/verify-components.py holds the two charts’ resources defaults together. An installation with the façade rolls its two substrate-s3proxy pods once (a pod template change; the rolling update surges the new pod first) — UPGRADE.md. Lab proof (agentlab, the façade alone against Azurite, hack/agentlab/): a lab on connectivity 4.10.4 upgraded to this branch’s dev build rolled the façade in 6 s (new ReplicaSet, 2/2 Ready, old at 0) with ephemeral-storage 256Mi/1Gi and both emptyDirs sizeLimit: 1Gi on the live pods; an AgentTemplate created right after went Ready on the platform Harness in 5 s with its golden snapshot (5 objects, 1.3 MiB) written through the rolled façade into Azurite; agentlab platform-test all PASS before and after. The Kyverno half is fleet-only (agentlab runs no Kyverno): the acceptance is no require-emptydir-requests-and-limits event on glean once the release rolls.Full Changelog: https://github.com/giantswarm/muster/compare/v5.19.3...v5.19.4
Full Changelog: https://github.com/giantswarm/klaus-operator/compare/v0.0.159...v0.0.160
streamable-http, sse) settles in Disconnected, never in Stopped, but the reconciler’s suspend path only recognised Stopped and Stopping as done – so a server with spec.suspended: true was stopped again every 30 s for as long as it stayed suspended: Suspending MCPServer service <name> (spec.suspended=true) and Stopped service: <name> in the log, an MCPServerStopped Kubernetes event and a second reconcile from the state change on each tick, 13 days of it for one server on a management cluster. reconcileSuspend now returns early for Disconnected as well; Failed, Error and Unreachable are still stopped, so suspending a server whose endpoint is down still ends its reconnect schedule. Service.Stop has the same guard: called on a remote server that is already Disconnected it returns without a state write and without an event, as it always did for a local server that is Stopped. Resume (spec.suspended: false) is unchanged. Scenario: mcpserver-suspend-remote-stops-once; muster test runs instances with MUSTER_RECONCILER_RESYNC_INTERVAL=2s (a new Go-duration override of the reconciler’s 30 s resync) and instance_logs gained occurrences, an exact line count per substring.jwtAuthentication.jwks.host in a namespace, or on a port, that gateway.jwksEgress does not name now fails the render, under networkPolicy.enabled. That key renders one rule, for one namespace on one port, so a host elsewhere reached nothing while the render stayed green and every request read 401 token uses the unknown key. The message names the value to set. Both guards stand down for a host reached by address: gateway.jwksEgress.external.cidrs non-empty on an external.port equal to the route’s jwks.port, which is how a second in-cluster issuer is carried — the one rule covers one namespace on one port, so two routes on in-cluster hosts in different namespaces cannot both be satisfied. gateway.jwksEgress.podSelector is read by no guard, because a hostname carries no pod labels. An installation whose issuer is dex.giantswarm.svc.cluster.local on 5556, the default, is unaffected.