🚀 Announcing Agent Sandbox v1.0.10!
We're excited to announce the release of Agent Sandbox v1.0.10! Highlights: gRPC-Web transcoding in sandbox-router for browser clients, sandboxd unary execution limits and expanded file operations (mkdir, recursive list, create-only writes, move, copy, and tar extraction), registry.k8s.io image promotion for sandboxd, ConnectivityRouterPortForward for Kata and gVisor in the Go SDK, controller fixes for SandboxClaim mirroring and SandboxWarmPool replica accounting, and new examples for Hyperlight micro-VMs, GKE L4 GPU snapshots, OpenCode, and multi-tenant RL.
⚠️ Action Required
- Router
NetworkPolicychanges (#1550, #2095, #2144):sandbox-router/deploy/networkpolicy.yaml(applied bykubectl apply -k sandbox-router/deploy/) now restricts sandbox egress to Pods carryingagents.x-k8s.io/sandbox-name-hashand allows all TCP ports to those Pods instead of only8888and9090(#1550). The router Helm chart (sandbox-router/helm/, version0.1.2) already required that label and now restricts that rule to TCP (#2095).- Both
sandbox-router/deploy/networkpolicy.yamlandsandbox-router/helm/now allow egress on TCP443and6443(API server) and UDP/TCP53(DNS) to any destination by default (#1550, #2144), removing thedefaultandkube-systemnamespace selectors that broke Pod-cache sync andTokenReviewwhen the API server runs on host-network nodes (for example under Cilium). With the default--authz-mode=allow-all, callers can also target ports443and6443viaX-Sandbox-Pod-IP. In the Helm chart, settingnetworkPolicy.apiServerCIDRsnow replaces the port-only rule with only those CIDRs instead of adding them alongside thedefaultnamespace. - Action: If you route through
sandbox-routerto Pods withoutagents.x-k8s.io/sandbox-name-hash, deploy an additionalNetworkPolicypermitting those targets. To restrict API server egress from0.0.0.0/0on443and6443, setnetworkPolicy.apiServerCIDRsin Helm or add anipBlocktargeting your API server endpoint addresses (and ClusterIP on CNIs that evaluate policy before DNAT).
- OLM bundle includes the router
NetworkPolicy(#1550, #2141): The OLM bundle (olm/bundle/, updated to operator version1.0.9in #2141) now packages thesandbox-routerNetworkPolicy. Upstream OLM added bundle support forNetworkPolicyin v0.32.0; earlier upstream OLM versions reject theInstallPlan(OpenShift OLM 4.12+ supports it).- Action: Upgrade upstream OLM to v0.32.0 or later before installing or upgrading from an OLM bundle at or after
1.0.9. Because OLM owns bundled manifests, deploy a secondNetworkPolicyrather than editing the bundled one if you need to allow extra ingress or egress peers.
- Action: Upgrade upstream OLM to v0.32.0 or later before installing or upgrading from an OLM bundle at or after
sandbox-router/server.Options.ProbeAddrremoved (#2076):Options.ProbeAddrwas removed fromsigs.k8s.io/agent-sandbox/sandbox-router/serverwhen probe serving moved toserver.StartProbes.- Action: Go code that imports
sigs.k8s.io/agent-sandbox/sandbox-router/servermust callserver.StartProbesinstead of settingOptions.ProbeAddr.
- Action: Go code that imports
Highlights
Core & Extension Controllers
- Bound
SandboxClaims keep mirroring when their template is unavailable (#2168): Deleting or losing access to a referencedSandboxWarmPoolorSandboxTemplatepreviously caused bound claims withadditionalPodMetadatato clear their mirroredFinishedcondition (preventingttlSecondsAfterFinishedexpiry) and stop updatingpodIPsandserviceFQDN. When the boundSandboxalready carries the claim'sadditionalPodMetadata, reconciliation now succeeds; when unapplied metadata or a template key conflict remains,ReadyreportsFalsewhileFinished,Name,podIPs, andserviceFQDNcontinue to mirror theSandbox. Routine reconcile logs inSandboxClaimReconcileralso moved fromV(1)toV(4). - Warm-pool replica accounting on failed updates, deletes, and adoptions (#2158):
SandboxWarmPoolpreviously dropped sandboxes from capacity accounting when alaunch-typelabel update, stale-member deletion, or orphan adoption failed (and ignored terminating orphans), causingreconcilePoolto create extra sandboxes pastspec.replicas. Pool-owned sandboxes with failed metadata updates or failed stale deletes now stay in active accounting, and failed adoptions and terminating or failed-delete orphans count toward inactive capacity so no replacements are created until they are gone. PodTerminatingreason onPodScheduledfor terminating unscheduled Pods (#1761): When an unscheduled backing Pod (such asUnschedulableorSchedulingGated) hasdeletionTimestampset,Sandboxnow setsPodScheduled=Falsewith reasonPodTerminatinginstead of copying the scheduler's last reason.SandboxWarmPoolexemptsUnschedulableandSchedulingGatedmembers from stuck-sandbox cleanup, so a terminating Pod wedged on a finalizer no longer holds a pool slot indefinitely past the readiness grace period.
Sandbox Router
- gRPC-Web transcoding for browser clients (#2143): The Go
sandbox-routernow transcodes binaryapplication/grpc-webandapplication/grpc-web+protoPOSTrequests (HTTP/1.1 or HTTP/2) to native gRPC (h2c) upstream for unary and server-streaming RPCs, includingsandboxd'sProcessServiceandFileWatcherService. It's always on:- Text-mode
application/grpc-web-textand non-proto codecs are rejected with gRPCInvalidArgument. - Set
--grpc-web-allowed-originsto a comma-separated list of explicitscheme://host[:port]origins (*is rejected; default allows same-origin only) to enable CORS headers andOPTIONSpreflight handling. --path-routing-prefixdoesn't apply to gRPC-Web; callers must sendX-Sandbox-IDand an explicitX-Sandbox-Portheader.--grpc-proxy-timeoutandGrpc-Timeoutapply.- As with native gRPC, the router strips
Authorization, so a token-protectedsandboxd(--auth-token-*) can't be reached through it.
- Text-mode
/healthzserved before initial Pod cache sync (#2076, #2144):sandbox-routernow starts its probe listener (:8081) before OTel initialization and the initial Pod cache sync (whose timeout increased from60sto90s)./healthzreturns200for up to120sduring startup (then503if still not ready), while/readyzstays503until the proxy listeners are bound. This prevents kubelet liveness restarts on large clusters, and #2144 removes the redundantstartupProbefromsandbox-router/helm/(chart0.1.2).- Optional controller
NetworkPolicy(#1550): Newk8s/hardening/networkpolicy.yamlrestrictsagent-sandbox-controlleringress to metrics (8080) and health probes (8081), and egress to DNS (53) and the API server (443,6443). It isn't included insandbox.yamlorsandbox-with-extensions.yaml.
sandboxd
- Unary execution limits and bounded output (#2117): New gRPC RPC
process.v1.ProcessService/ExecuteWithLimitsruns a unary command with an optionaltime_limit(independent of the RPC deadline, killing the process tree on expiry and reportingEXECUTE_TERMINATION_REASON_TIME_LIMIT_EXCEEDEDandelapsed_duration) and independentstdout_limit_bytesandstderr_limit_bytesprefix caps (stdout_truncated,stderr_truncated).- Six daemon flags (
--execute-default-time-limit,--execute-max-time-limit,--execute-default-stdout-limit-bytes,--execute-max-stdout-limit-bytes,--execute-default-stderr-limit-bytes,--execute-max-stderr-limit-bytes; all0/ unlimited by default) set deployment defaults and maximums. - When those daemon flags are set, existing
ProcessService/Executealso enforces them and returnsRESOURCE_EXHAUSTEDif the time limit is exceeded or output is truncated. - Older
sandboxdversions returnUNIMPLEMENTEDforExecuteWithLimits. The Go, Python, and TypeScript SDKs don't callExecuteWithLimitsyet.
- Six daemon flags (
- Filesystem REST operations (#2097, #2101, #2112, #2118, #2130):
- Directory creation (#2097):
PUT /v1/files/{path}?type=directorycreates a directory (defaultmode=0755, idempotent204if it already exists,409if a file exists). Missing parent directories return404unlessparents=trueis set.PUT ?type=fileon an existing directory also now returns409. - Recursive directory listing (#2101):
GET /v1/files/{path}?recursive=truewith optionalmax_depth=N(>= 1). Entry names are/-separated paths relative to{path}. Symlinked directories are listed without descending into them, and multi-level listings cap at10,000entries (400). - Create-only file writes (#2112):
PUT /v1/files/{path}withIf-None-Match: *atomically creates the file vialink(2)and returns412(FAILED_PRECONDITION) if{path}already exists. Requires hard-link support on the backing filesystem (not supported on GCS FUSE). - Move and copy (#2118):
POST /v1/files/{path}:move?destination={dest}and:copy?destination={dest}move or copy files, symlinks, and directory trees, creating destination parent directories automatically and staging cross-filesystem moves. Existing destinations return409unlessoverwrite=truereplaces a non-directory with a non-directory. - Tar extraction on upload (#2130):
PUT /v1/files/{path}?extract=tarunpacks a plain.taror gzip-compressed.tar.gzstream into{path}(created if missing), confining entry paths to{path}(403on escape) and capping archives at100,000entries or1 GiBof file data (413). Extraction isn't atomic across the whole archive. - Version skew:
sandboxdv1.0.9and earlier ignore unknown query parameters andIf-None-MatchonPUTandGET: against<= v1.0.9,?type=directoryand?extract=tarwrite a regular file with204,If-None-Match: *overwrites an existing file, and?recursive=truereturns a flat listing (POST :moveand:copyreturn405). Upgradesandboxdtov1.0.10before calling these endpoints.
- Directory creation (#2097):
sandboxdimage promoted toregistry.k8s.io(#2139): Releases now promoteregistry.k8s.io/agent-sandbox/sandboxd:v1.0.10(linux/amd64andlinux/arm64).
SDKs
- Go SDK:
- New
ConnectivityRouterPortForward("router-port-forward") andOptions.RouterNamespacereachRuntimeSandboxdsandboxes through a port-forward tosandbox-router-svc, supporting both REST and gRPC (Run,Commands().Start,WatchDir) (#2003, #2145). Use this from a laptop or CI runner for Kata and gVisor sandboxes, where Pod port-forward can't reach the guest network stack. gRPC calls requiresandbox-routerv1.0.9or later, andsandboxdbearer-token auth (--auth-token-*) isn't supported because the router stripsAuthorization. - New
Mkdir(ctx, path, parents, opts...),Move(ctx, src, dst, overwrite, opts...), andCopy(ctx, src, dst, overwrite, opts...)on*Sandboxand*Files(#2156).MoveandCopydefault to 1 attempt (WithMaxAttemptsopts into retries). Requiressandboxdv1.0.10+.
- New
- TypeScript SDK:
- New
sandbox.files.mkdir(path, { parents, mode }),move(src, dst, { overwrite }), andcopy(src, dst, { overwrite })(#2140). - New
appendandcreateOnlyoptions onsandbox.files.write()andsandbox.files.writeStream()(#2151);createOnlythrowsSandboxdApiError(status: 412,code: "FAILED_PRECONDITION") if the target exists. - New
recursiveandmaxDepthoptions onsandbox.files.list(path, { recursive: true, maxDepth })(#2167). - Requires
sandboxdv1.0.10+(appendrequiresv1.0.9+).
- New
- Python SDK:
- Broke the
SandboxandAsyncSandboxhandle-to-connector reference cycle onclose_connection(), freeing closed handles without waiting for cyclic GC (#2086). - Stopped
atexitcleanup registration from retaining closedSandboxClientandAsyncSandboxClientinstances until interpreter exit (#2087). AsyncSandboxConnectornow initializeshttpx.AsyncClientlazily on first HTTP use, andAsyncSandboxClientshares one cached defaultssl.SSLContextacross handles instead of reloading the CA bundle per handle (#2132).- Fixed sync
kubectl port-forwardtunnels (LocalTunnelConnectionStrategyandSandboxdPodTunnelStrategy) stalling after ~250 reset connections filledkubectl's unreadstderrpipe (#2106). - Widened
kubernetes_asynciosupport from<34.0.0to<37.0.0(#2071), and raised thetracingextra's OpenTelemetry upper bound to<1.46sopip install "k8s-agent-sandbox[tracing,grpc]"resolves cleanly withprotobuf>=7(#2099).
- Broke the
Reinforcement Learning & Examples
- Multi-tenant RL over shared node image caches (#2165): New
examples/agent-sandbox-rl/examples/multi_tenant/adds per-userrl-<user>namespace RBAC,ResourceQuota,LimitRange, andNetworkPolicyprovisioning, shared node-cache pre-pull (admin/prepull_shared.py), quota-fitting runner CLIs (run_experiment.py,run_many.py), and isolation verification (verify.sh). Also updatedexamples/agent-sandbox-rl/CHANGELOG.md(#2038, #2114). - Hyperlight micro-VM sandboxes (#2119): New
examples/hyperlight-sandboxruns a stdlib Python runtime speaking thepython-runtime-sandboxHTTP API inside a Hyperlight micro-VM (hyperlight-unikraftwith a Unikraft unikernel guest) in an unprivilegedFROM scratchcontainer via thehyperlight.dev/hypervisorCDI device plugin, supporting both initrd cold boot and sub-200ms VM snapshot restore (Containerfile.snapshot). - L4 GPU Pod Snapshots on GKE (#2147): New
examples/gpu-l4-snapshot-sandboxcheckpoints and restores an NVIDIA L4 GPU gVisorSandboxon GKE using GKE Pod Snapshots (podsnapshot.gke.io, which usescuda-checkpointunder the hood), releasing the GPU whileSuspendedand restoring host and GPU memory on resume. - OpenCode sandbox (#2100): New
examples/opencode-sandboxruns the OpenCode coding agent inside a coreSandbox(viakubectl exec) or aSandboxWarmPooldriven by the Python SDK. - Terminal-Bench 2.0 with Harbor (#2126): New
examples/harbor-terminal-benchadds Harbor configs, RBAC, image mirroring, and result reporting for running Terminal-Bench 2.0 onSandboxClaims (requires theclients/integrations/harborenvironment adapter in #2125). - Example updates: Made the
jupyter-hf-tokenSecret optional and init-container-only inexamples/jupyterlabso public models need no Secret and notebook kernels don't inheritHF_TOKEN(#2160); added Cloud Hypervisor (kata-clh) setup instructions toexamples/kata-gke-sandbox(#2134); updatedexamples/recall-memorytorecall0.24.0andv1.0.9(#2148).
Documentation, CI & Tooling
- Kubernetes version requirements (#1892): Documented minimum (1.25+, with
CustomResourceValidationExpressionsenabled on 1.25–1.28), recommended (1.29+, where CRD CEL validation is GA), and Gateway API v1.5.0 bundle (1.31+) Kubernetes versions. sandboxdrouting docs (#2113): Updatedpackages/sandboxd/USER_GUIDE.mdand SDK comments to reflect native gRPC proxying throughsandbox-router.- CI and release tooling: Fixed a duplicate Dockerfile directory name in
push-imagesand added a presubmit uniqueness test (#2161), addedgo installretries in kOps CI lanes (#2121), and excluded bot accounts from generated contributor lists (#2115).
Installation
Standard Install (Core + Extensions)
Recommended for most users and GitOps engines (Argo CD, Config Sync, kustomize):
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/v1.0.10/sandbox-with-extensions.yamlSelective Install
Install components separately:
# Core only:
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/v1.0.10/sandbox.yaml
# Extensions (opt-in):
kubectl apply -f https://github.com/kubernetes-sigs/agent-sandbox/releases/download/v1.0.10/extensions.yamlPython SDK
pip install k8s-agent-sandbox==1.0.10Contributors
We extend our sincere thanks to all contributors to this release:
@D-Joey-G, @HasonoCell, @LucasGois1, @RyoKusnadi, @Sean-790761, @abhayjoshi201, @aditya-shantanu, @alanhuangch, @anuptalwalkar, @briankhoi, @cloudonly, @danbugs, @dongjiang1989, @ericcurtin, @esposem, @fponsse, @janetkuo, @lilong555, @lunarwhite, @nsknojj, @pbxqdown, @rkb32, @tomergee
New Contributors
- @rkb32 made their first contribution in #2076
- @nsknojj made their first contribution in #2099
- @danbugs made their first contribution in #2119
- @D-Joey-G made their first contribution in #2132
- @cloudonly made their first contribution in #1892
- @RyoKusnadi made their first contribution in #2160
Full Changelog: v1.0.9...v1.0.10