Skip to main content

OpenKruise Agents v0.6.0: Making Agent Sandboxes Safer, Easier to Operate, and More Resource-Efficient

· 22 min read

Keywords: L4/L7 Traffic Control · Credential Injection · CheckpointRestore · PoolAutoscaler · Auto Pause/Resume

The v0.3.0 release answered one question: once a Sandbox is handed to an Agent, how do you upgrade it, resize it, and account for it. Its roadmap closed with two promises — TrafficPolicy / SecurityProfile for network governance, and a security identity integration that keeps platform credentials out of Agent code.

OpenKruise Agents v0.6.0 delivers both, and adds the operational machinery around them: 283 merged pull requests since v0.3.0. The four capabilities this post covers in depth are:

  • Layer 4 and Layer 7 network access control plus token management — TrafficPolicy for destination reachability, SecurityProfile for per-request inspection, MCP tool-level ACLs, credential injection from Kubernetes Secrets;
  • SandboxUpdateOps with CheckpointRestore and paused-instance upgrades — upgrade a hibernated fleet without waking it by hand, and upgrade without losing the root filesystem;
  • PoolAutoscaler — declarative warm pool capacity, with watermarks and cron schedules;
  • Automatic pause and resume — probe-driven idle detection, scheduled wake, and wake-on-traffic, so an idle Sandbox stops consuming CPU without any manual intervention.

Read together, these are three kinds of customer value: security hardening (an Agent that runs untrusted code can no longer reach whatever it wants, and never holds your API keys), operational capability (batch upgrades, autoscaling, and lifecycle automation become declarative instead of scripted), and cost optimization (idle Sandboxes hibernate, warm pools shrink at night, and paused fleets still receive upgrades).

This post is organized in three parts: the architecture changes that made all of this possible, a deep dive into the four features, and the value and roadmap summary.


Part One: What Changed in the Architecture​

If v0.3.0 was a workload layer, v0.6.0 is a workload layer with its own data plane. The full component model is documented in Architecture; the short version is that traffic inspection, policy distribution, and identity all became first-class components rather than external addons.

alt

1.1 From Four Components to Six​

ComponentRole in v0.6.0
sandbox-managerStateless backend that serves the E2B-compatible northbound API and owns sandbox lifecycle, quota, and route & traffic-token orchestration
sandbox-controllerReconciles Sandbox, SandboxSet, SandboxClaim and warm pools, and hosts the admission webhooks. In v0.6.0 it also drives the automatic pause/resume controller
sandbox-gatewayThe data plane, built as Envoy Go filters. The ingress gateway proxies user traffic to sandboxes with host/path routing, access tokens, and wake-on-traffic. The egress gateway proxies outbound sandbox traffic with policy-enforced L7 egress
traffic-control-planeNew. istiod distributes xDS configuration to the gateways and the in-pod proxies; the Egress Policy Enforcer (EPE) applies L7 policy, MCP ACL, and token injection on egress traffic
agent-runtimeSidecar providing E2B envd-compatible command, file, and port (49983) operations
traffic-proxyNew. A sidecar that acts as the in-pod L4 enforcement point for egress traffic policies

Two consequences are worth internalizing. First, egress is no longer the Pod's own business — it is captured in-pod, routed through an egress gateway, and decided by a policy engine that sees the request, not just the socket. Second, the ingress gateway can wake a sandbox, which is what makes wake-on-traffic possible without an external poller.

1.2 The Enforcement Path​

Every gateway-routed outbound request from a Sandbox running the traffic-proxy runtime walks the same path:

Sandbox application container
-> TrafficProxy in-pod capture
-> egress gateway chosen by egress routing
-> Envoy external processing
-> Egress Policy Enforcer decision
-> external service, or a blocked response

The split of responsibility is deliberate: EPE compiles TrafficPolicy / GlobalTrafficPolicy and pushes them to the data plane, so an L4 rejection is dropped before the gateway. EPE only inspects L7 for gateway-routed HTTP requests — direct passthrough traffic never reaches it, and L7 cannot override an L4 rejection.

1.4 The CRD Surface Doubled​

v0.3.0 shipped six CRDs. v0.6.0 ships twelve:

CRDScopeRole
SandboxNamespacedA single sandbox instance with full lifecycle operations
SandboxSetNamespacedManages a batch of homogeneous Sandboxes; maintains the warm pool
SandboxTemplateNamespacedImmutable template version snapshot for version tracing
SandboxClaimNamespacedClaims an idle Sandbox and performs post-processing (image swap, mounts, in-place resize)
CheckpointNamespacedRuntime snapshot (memory and/or rootfs) used to fork new instances
CommitNamespacedAlpha, behind the Commit feature gate. Saves the writable layer as a new image and pushes it to a registry
SandboxUpdateOpsNamespacedOne-shot batch upgrade of claimed Sandboxes
PoolAutoscalerNamespacedNew. Declarative warm pool capacity and cron scaling
TrafficPolicyNamespacedNew. L3/L4 egress and ingress destination control
GlobalTrafficPolicyClusterNew. Cluster-scoped L4 baseline
SecurityProfileNamespacedNew. L7 request inspection, MCP ACL, credential injection, audit
GlobalSecurityProfileClusterNew. Cluster-scoped L7 baseline

1.5 E2B Compatibility Moved Forward, With Documented Edges​

The northbound API stays E2B-compatible, and the compatibility matrix is now explicit about where it is partial:

  • pause is fully compatible and synchronous — the request blocks until the Sandbox reaches Paused;
  • create is partially compatible — network access control is supported, MCP integration on the create path is still pending;
  • network is partially compatible — allowOut, denyOut, and rules are supported; egressProxy and maskRequestHost are rejected outright rather than silently ignored;
  • Not yet supported: pre-signed upload/download URLs, logs, metrics, lifecycle events, and Volumes.

Part Two: The Four Features in Depth​

2.1 Network Access Control and Token Management​

This is the largest single area of the release, and it answers three distinct questions: what may a Sandbox reach, who is allowed to reach the Sandbox, and — the one most platforms get wrong — who holds the upstream API key.

Layer 4: Is the Destination Reachable at All​

TrafficPolicy selects calling Pods and holds an ordered egress rule list. Every rule must declare to, and may declare action (allow or reject) and a ports list. A peer accepts fqdn, cidr, service, or workload, and multiple peers are ORed.

apiVersion: agents.kruise.io/v1alpha1
kind: TrafficPolicy
metadata:
name: sandbox-egress
namespace: default
spec:
selector:
matchLabels:
app: traffic-sample
egress:
rules:
- action: allow
to:
- service:
name: kube-dns
namespace: kube-system
- action: allow
to:
- fqdn: api.example.com
- action: reject
ports:
- protocol: TCP
port: 443
to:
- cidr: 10.0.0.0/8

Three semantics matter more than the syntax:

  • Once a policy selects a Pod, outbound traffic matching no allow rule is rejected. The policy is an allowlist the moment it attaches;
  • A rule missing its peer field is skipped by the compiler — it does not establish a default deny. A typo in to weakens your policy silently;
  • Always allow the cluster DNS Service, or hostname resolution fails and every FQDN rule looks broken.

GlobalTrafficPolicy is the cluster-scoped equivalent, for baselines your platform team owns centrally.

Layer 7: What May This Specific Request Do​

SecurityProfile matches HTTP requests and applies actions. Each rule has a unique name, at least one match clause, and actions. Clauses in match are ORed; every populated field inside one clause is ANDed; domains is required.

The action set is small and its terminal behavior is the part to memorize:

ActionTerminalBehavior
blockYesReturns statusCode (default 403) with an optional body, never forwards upstream, discards pending mutations
bypassYesForwards the request and skips every remaining action and rule, preserving mutations already applied
mcpToolPolicyNoAllows or denies MCP JSON-RPC tools/call requests by tool name
headerManipulationNoSets or removes plaintext headers. Values are stored verbatim — never use it for credentials
tokenTransformationNoRewrites a request credential from a Secret or credential provider. Governed by failStrategy, default Block
auditNoSends an asynchronous webhook event; never changes the decision

Evaluation order is a merge, not a namespace-local list: lower spec.priority first (default 1000), then earlier creation time, name, and namespace. Global and namespaced profiles share this order, and an exact tie places the global profile first. Matching is not first-rule-wins — a later rule can still match after a broad earlier rule; only a terminal action stops the chain. Within one rule, actions run in the fixed order bypass, block, mcpToolPolicy, headerManipulation, tokenTransformation, regardless of YAML key order.

A representative baseline that blocks management paths anywhere:

apiVersion: agents.kruise.io/v1alpha1
kind: SecurityProfile
metadata:
name: deny-management-paths
namespace: default
spec:
selector:
matchLabels:
app: traffic-sample
rules:
- name: deny-management-paths
match:
- domains:
- "*"
paths:
- type: Prefix
value: /admin
- type: Prefix
value: /console
- type: Prefix
value: /dashboard
actions:
block:
statusCode: 403
body: '{"error":"management paths are blocked"}'

The highest-value profile for Agent workloads, though, is the SSRF guard. Prompt-injected code reliably tries to reach internal services and cloud metadata endpoints:

apiVersion: agents.kruise.io/v1alpha1
kind: GlobalSecurityProfile
metadata:
name: cluster-baseline
spec:
selector:
matchLabels:
app: traffic-sample
priority: 50
rules:
- name: deny-metadata-endpoint
match:
- domains:
- metadata.internal
- "*.internal.company.com"
- "*.corp.net"
actions:
block:
statusCode: 403
body: '{"error":"access to internal services is forbidden"}'

Note priority: 50 — a cluster baseline should sort ahead of tenant profiles on purpose, and a GlobalSecurityProfile must not set metadata.namespace.

MCP Tool-Level ACL​

When an Agent talks to an MCP server, domain-level control is too coarse: the dangerous operation is a specific tool. mcpToolPolicy governs only JSON-RPC tools/call and matches the exact params.name; tools/list and initialize pass through.

apiVersion: agents.kruise.io/v1alpha1
kind: SecurityProfile
metadata:
name: mcp-tool-whitelist
namespace: default
spec:
selector:
matchLabels:
app: traffic-sample
rules:
- name: protect-mcp-tools
match:
- domains:
- mcp.example.com
schemes:
- https
paths:
- type: Prefix
value: /mcp
actions:
mcpToolPolicy:
defaultAction: deny
unsupportedVersionAction: deny
denyResponse:
statusCode: 403
body: MCP tool is not permitted
rules:
- method: tools/call
toolNames:
- read_file
- search_docs
action: allow

Rules are evaluated in document order and the first match wins; an empty toolNames list matches every tool for that method. Use defaultAction: deny for a whitelist — with defaultAction: allow, an unreadable body or an unnameable tool call is allowed by design. The enforcer buffers the full body, so payloads over 8 MiB, JSON-RPC batches, and unsupported encodings fall back to defaultAction. Supported MCP protocol versions are 2025-06-18, 2025-11-25, and 2026-07-28.

Credential Injection: Your API Key Never Enters the Sandbox​

This is the piece that changes the security conversation with your compliance team. The credential lives in a Kubernetes Secret; the egress proxy resolves it per request and rewrites the outbound request just before it leaves the cluster. The workload and the Agent code only ever see their own placeholder header.

apiVersion: v1
kind: Secret
metadata:
name: example-api-credentials
namespace: agent-demo
type: Opaque
stringData:
apiKey: REPLACE_WITH_YOUR_API_KEY
apiVersion: agents.kruise.io/v1alpha1
kind: SecurityProfile
metadata:
name: example-api-credentials
namespace: agent-demo
spec:
selector:
matchLabels:
app: agent
rules:
- name: inject-example-api-key
match:
- domains:
- api.example.com
schemes:
- https
paths:
- type: Prefix
value: /v1/
actions:
tokenTransformation:
type: ApiKey
failStrategy: Block
credentialRef:
secret:
name: example-api-credentials
apiKey:
targetHeaders:
names:
- authorization
value:
template: "Bearer {{ .Token }}"

Because resolution happens at the proxy, the credential is not in the Pod spec, the environment, or Sandbox metadata — and rotating the Secret takes effect without restarting the Sandbox. credentialRef.secret.namespace is optional: a SecurityProfile falls back to its own namespace, a GlobalSecurityProfile to each selected Pod's namespace. Exactly one of secret or credentialProvider may be set, targetHeaders.cel supports dynamic header names, and failStrategy defaults to Block so a missing credential fails closed rather than leaking an unauthenticated call.

E2B-Native Network Controls​

Teams that do not want to write CRDs can express network intent through the E2B SDK, provided the Sandbox runs traffic-proxy:

from e2b_code_interpreter import Sandbox

sandbox = Sandbox.create(
template="code-interpreter",
network={
"allow_out": ["api.example.com", "10.0.0.0/8"],
"deny_out": ["0.0.0.0/0"],
},
)

The asymmetry to remember: allowOut accepts IPs, CIDRs, and concrete FQDNs (wildcards rejected), while denyOut accepts IPs and CIDRs only. Hostname filtering is therefore expressed as an allowlist plus a 0.0.0.0/0 deny, and allow beats deny. rules carries per-domain header transforms and does not grant reachability. Updates go through PUT /sandboxes/{sandboxID}/network or sandbox.update_network(...): an omitted rules key keeps the existing chain, an empty object clears it, a non-empty one replaces it.

These controls compose into a hierarchy: cluster-scoped GlobalTrafficPolicy and GlobalSecurityProfile set the global baseline, namespace-scoped TrafficPolicy and SecurityProfile carry per-tenant rules, and per-Sandbox inline network controls apply to a single Sandbox — multi-layered, fine-grained network access control from the global dimension down to the tenant and the individual Sandbox.

Guardrails You Should Not Skip​

The four manuals behind this section are Traffic Access Control, E2B Network Controls, Credential Injection, and Traffic Access Token Rotation. They are explicit about the limits, and so is this post:

  • Enforcement covers gateway-routed traffic only. HTTPS inspection additionally requires TLS termination, a signing CA, and workload trust;
  • A container running as root or with NET_ADMIN / SYS_ADMIN can bypass the in-pod ACLs. Pair these policies with Kubernetes NetworkPolicies, a host firewall, or cloud security groups — this is defense in depth, not a single wall;
  • TrafficProxy reserves ports 15001, 15006, 15012, 15020, 15021, 15090 and runs as UID/GID 1337;
  • A forged Host header can evade L4 matching, which is exactly why L7 profiles exist;
  • Per-Sandbox inline rules (agents.kruise.io/security-rules) are evaluated after selector-matched profiles; a compile failure keeps the previous rules or enforces none, surfaced by the epe_profile_stale and epe_profile_unenforced metrics with scope="pod";
  • EPE is a network hop on every gateway-routed request. Size its Deployment independently of the gateways.

2.2 SandboxUpdateOps: CheckpointRestore and Upgrading Paused Sandboxes​

v0.3.0 introduced SandboxUpdateOps with a single mode: Recreate, meaning the Pod is destroyed and rebuilt, so memory, IP, and the rootfs writable layer are all lost, and preUpgrade / postUpgrade hooks are your only way to carry state across. v0.6.0 adds two things users asked for immediately: a mode that preserves the rootfs, and the ability to include paused Sandboxes in a batch upgrade. Both require v0.6.0; the base flow works from v0.3.0. Details are in Sandbox Update.

alt

Two Upgrade Modes​

ModeRootfsMemoryIPBusiness image change
Recreate (default)LostLostChangedSupported
CheckpointRestorePreserved — checkpointed before, restored afterLostChangedNot supported

CheckpointRestore trades image flexibility for data continuity. The constraint is absolute: you cannot change the business container image under CheckpointRestore — doing so discards the restored rootfs, which is the entire point of the mode. What you can do is roll sidecar versions and any non-image field:

apiVersion: agents.kruise.io/v1alpha1
kind: SandboxUpdateOps
metadata:
name: upgrade-sidecar
namespace: default
spec:
updateStrategy:
type: CheckpointRestore
patch:
metadata:
annotations:
agents.kruise.io/upgrade-sidecar: "20260714"

That is the pattern for a fleet-wide agent-runtime or traffic-proxy bump: no user data touched, no backup scripts to write, no restore window to schedule. For an image change with state, Recreate plus hooks remains the answer — back up to OSS or NAS in preUpgrade, restore in postUpgrade.

Upgrading Hibernated Sandboxes​

Before v0.6.0, SandboxUpdateOps only touched Sandboxes in Running or Upgrading phase. A paused fleet was invisible to it, which forced operators into an awkward manual cycle of wake, upgrade, re-pause — and made a hibernated pool a standing source of version drift.

stateFilter fixes this:

apiVersion: agents.kruise.io/v1alpha1
kind: SandboxUpdateOps
metadata:
name: upgrade-with-paused
namespace: default
spec:
stateFilter:
states:
- Running
- Paused
selector:
matchLabels:
agents.kruise.io/sandbox-template: my-sandbox-pool
updateStrategy:
maxUnavailable: 10%
patch:
spec:
containers:
- name: sandbox
image: my-registry/sandbox-image:v2

The controller wakes each paused Sandbox, upgrades it, then re-pauses it if spec.paused is still true. The end state of the fleet is the same shape as the start state — paused Sandboxes stay paused — so an upgrade does not silently convert a hibernated pool into a billing event.

2.3 PoolAutoscaler: Warm Pool Capacity as a Policy​

Warm pools are the reason OpenKruise Agents delivers sub-second claims, and they are also the reason a platform team's bill grows on Saturday night when nobody is using it. PoolAutoscaler turns pool sizing into a declarative policy instead of a cron job that runs kubectl scale.

Be precise about what it does: PoolAutoscaler rewrites SandboxSet.spec.replicas. It does not collect business metrics, predict traffic, or create and delete Pods itself — the SandboxSet controller does that. What it gives you is watermark-driven feedback on available capacity, plus time-driven overrides.

Capacity Policy​

apiVersion: agents.kruise.io/v1alpha1
kind: PoolAutoscaler
metadata:
name: sandbox-pool-scaler
namespace: default
spec:
scaleTargetRef:
kind: SandboxSet
name: sandbox-pool
minReplicas: 2
maxReplicas: 50
capacityPolicy:
targetAvailable: "50%"
tolerance: "10%"
scaleUp:
stabilizationWindowSeconds: 60
scaleDown:
stabilizationWindowSeconds: 300

targetAvailable: "50%" means the scaler keeps available Sandboxes at half of total replicas — a buffer that absorbs a claim burst while the pool refills. Watermarks are computed as ceil(avgReplicas x (target +/- tolerance)%), with tolerance defaulting to 10%. Asymmetric stabilization windows (fast up, slow down) are the right default for a warm pool: users feel a shortage immediately and never notice a gradual shrink.

Cron Policies​

For known traffic shapes, cron wins — it triggers ahead of the burst rather than reacting to it:

apiVersion: agents.kruise.io/v1alpha1
kind: PoolAutoscaler
metadata:
name: sandbox-pool-scaler
namespace: default
spec:
scaleTargetRef:
kind: SandboxSet
name: sandbox-pool
minReplicas: 2
maxReplicas: 50
cronPolicies:
- name: weekday-peak
timezone: Asia/Shanghai
schedule: "30 8 * * 1-5"
targetReplicas: 30
- name: weekday-offpeak
timezone: Asia/Shanghai
schedule: "0 20 * * 1-5"
targetReplicas: 5

Cron takes priority when triggered and is not subject to the capacity stabilization windows, but it is still clamped by minReplicas / maxReplicas. Set timezone explicitly — the schedule is five-field cron, and validation allows up to 20 entries with unique names.

Tuning Traps​

  • Do not set targetAvailable: "100%" — the pool then only ever grows;
  • Scale-down reachability has hard arithmetic. For absolute values, target + downTolerance must not exceed minReplicas; for percentages, p + q must not exceed 100 x minReplicas / (minReplicas + 1). Otherwise the policy can never trigger a shrink and validation is your only warning;
  • tolerance must be strictly less than targetAvailable; a percentage target requires minReplicas of at least 1; stabilization windows are 60 to 3600 seconds;
  • Validation failures return HTTP 422, including a scaleTargetRef.kind that is not SandboxSet, maxReplicas at or below 0, or minReplicas above maxReplicas;
  • spec.suspend: true pauses the scaler without deleting it — the right move during an incident;
  • Deleting a PoolAutoscaler leaves spec.replicas at its last value. The pool does not shrink back on its own.

2.4 Automatic Pause and Resume​

A claimed Sandbox with an active user session and a claimed Sandbox where nobody has typed in four hours look identical to Kubernetes. Automatic pause and resume makes the difference observable, and acts on it.

The feature is configured on SandboxSet through two top-level spec fields — probes and autoPausePolicy — and probe-driven pause plus scheduled wake require the AutoPauseController feature gate on sandbox-controller, which is enabled by default. Probes on real nodes additionally require OpenKruise installed (for kruise-daemon) and the KruiseIntegration feature gate — otherwise real-node probe conditions stay Unknown and automatic pause never triggers; probes on virtual-kubelet nodes are unaffected. Inbound-traffic wake applies only to gateway-routed requests to claimed Sandboxes, and needs the gateway flags enable-wake-on-traffic and wake-timeout-seconds (default 60, after which the caller gets 503 sandbox wake failed). Both are gateway-level.

Probes: Exec Only, Message-Driven​

apiVersion: agents.kruise.io/v1alpha1
kind: SandboxSet
metadata:
name: openclaw-sbs
namespace: default
spec:
probes:
- name: Active
periodSeconds: 30
timeoutSeconds: 5
exec:
command:
- sh
- -c
- |
if openclaw sessions list --active 900 | grep -q .; then
printf 'active'
else
printf 'inactive'
fi

Each probe needs a unique name (it becomes condition type agents.kruise.io/<name>), an optional containerName (defaults to the first container), and only exec is supported — no HTTP, no TCP. The command is not shell-wrapped, so write sh -c yourself. Exit code 0 produces status: "True" with stdout in message; a failed or timed-out probe never triggers a pause or a wake, which is the safe direction.

Idle-Driven Pause​

  autoPausePolicy:
probe: Active
pause:
whenProbedIdleState:
probe: Active
messageRegex: "^inactive$"
thresholdDuration: 10m

thresholdDuration is required and non-negative; 0 means pause on the first idle report. The window arithmetic is worth spelling out for capacity planning: with a 30-second period, a 15-minute inactivity window inside the probe command, and a 10-minute threshold, the Sandbox pauses roughly 25 minutes after the last real activity. The pending pause is visible in status.schedules as reason: probedIdle with a nextPauseTime, and a later active reading clears it.

Warm unclaimed Sandboxes run the probes but do not participate in pause decisions — the pool is managed by PoolAutoscaler, not by auto-pause.

Scheduled Wake​

Some workloads are idle on a schedule: a notebook that only matters at 9am, an RL rollout that runs nightly. A second probe can print the next activation time, and the controller wakes the Sandbox ahead of it:

  autoPausePolicy:
resume:
whenProbedScheduleTime:
probe: Cron
timeFormat: unix
leadTime: 5m

timeFormat is unix (default, positive integer seconds) or datetime (RFC 3339 with timezone). leadTime defaults to 5m, may be 0, and may not be negative; wake happens at task time minus lead time, surfaced as reason: probedSchedule with nextResumeTime. Two classic bugs: printing milliseconds from the probe (divide by 1000), and converting local business time to a timestamp inside the probe instead of emitting a timezone-aware value.

Wake on Inbound Traffic​

This is the path that makes pausing safe for user-facing Sandboxes — the first request after a pause wakes the Sandbox instead of failing:

from e2b_code_interpreter import Sandbox

sandbox = Sandbox.create(
template="code-interpreter",
timeout=600,
lifecycle={
"on_timeout": "pause",
"auto_resume": True,
},
)
host = sandbox.get_host(3000)

After a wake, the timeout countdown restarts from the wake-up moment using the creation timeout, and positive values below 5 minutes are treated as 5 minutes. sandbox.commands.run(...) and sandbox.files.write(...) also trigger a wake. In CRD form the same re-arm is autoPausePolicy.resume.onIngressTraffic.pauseTimeout: 5m, which applies only to Sandboxes that have spec.pauseTime set — if it is unset or not greater than 0, the countdown is not reset. On its own onIngressTraffic pauses nothing; it only governs the resume behavior.

Three behaviors are universal and will otherwise cost you an afternoon:

  • The request must be authorized. An unauthorized request gets 401 and the Sandbox stays paused — wake-on-traffic is not an unauthenticated doorbell;
  • A successful wake can accompany a failed request. The triggering call may return 503 upstream connect error, or 503 sandbox wake failed if it arrives mid-transition to paused. Check Sandbox.status.phase and retry;
  • A warm Sandbox is still owned by its SandboxSet, so a request to one returns 502 healthy sandbox not found.

Part Three: Customer Value, Roadmap, and Community​

3.1 What This Buys You​

Before v0.6.0With v0.6.0
Security hardeningEgress was the Pod's own business; a prompt-injected Sandbox could reach internal services, cloud metadata, and any MCP tool the server exposed. Upstream API keys had to be handed to the Agent runtime. Long-lived opaque tokens bounded replay exposure to "forever"L3/L4 allowlists with TrafficPolicy, L7 request inspection with SecurityProfile, MCP tool whitelists, a cluster-wide Global* baseline layer, credentials resolved at the egress proxy from a Secret and rotatable without restart, and short-lived rotating JWT traffic access tokens
Operations capabilityPaused Sandboxes were invisible to batch upgrades, so hibernated fleets drifted. Pool sizing was a cron job calling kubectl scale. Idle detection was somebody's shell scriptSandboxUpdateOps covers Running and Paused via stateFilter and preserves the rootfs via CheckpointRestore; PoolAutoscaler makes capacity declarative with watermarks and cron; auto pause/resume turns idle detection into a probe
Cost optimizationA claimed Sandbox burned CPU whether or not anyone was using it, and the warm pool was sized for peak around the clockIdle Sandboxes hibernate on probe evidence and wake on schedule or on first request; pools shrink at 20:00 and grow at 08:30; CheckpointRestore upgrades and paused-fleet upgrades avoid both data loss and unnecessary wake cycles

3.2 What Is Next: Community Capability Building​

The roadmap ahead emphasizes community capability building — building with other open-source communities instead of growing a proprietary stack:

  • Open-source checkpoint with the Kata Containers community — implement an open-source checkpoint capability on top of the PodCheckpoint API introduced in Kubernetes 1.37, in collaboration with the Kata community, moving beyond today's platform-specific snapshot backend;
  • On-demand mounting for open-source storage — build on the existing on-demand volume mounting framework so open-source storage solutions such as MinIO can be mounted into a Sandbox on demand;
  • Integration with open-source identity systems — such as Keycloak, so platform authentication can be unified with your existing identity provider.

3.3 Community​

OpenKruise Agents is built in the open, and this release had 27 new contributors. If you are running Agent sandboxes on Kubernetes, the fastest way to shape the roadmap is to file an issue or join the community call.

OpenKruise Agents v0.6.0 is now available on GitHub Releases and major container registries. We look forward to your feedback and Pull Requests.