github opensandbox-group/OpenSandbox release-1.2.1-rc.1
OpenSandbox 1.2.1-rc.1

pre-release2 hours ago

First release candidate on the 1.2.1 patch line, cut from release-1.2.0 (2026-10-27). Headline: OSEP-0025 Credential Vault Exchange — an exchange credential source with per-request header resolution through a pluggable resolver, wired end to end across egress, server, all five SDKs, fast-sandbox e2e, and the Credential Vault guide. Also notable: execd atomic uploads, digest validation for rootfs snapshots, Kubernetes create-timeout startup diagnostics, and informer-cache scoping for large clusters. Baseline for every section is release-1.2.0. rc releases publish images but hold packages — SDK/CLI artifacts ship at the 1.2.1 stable bump.

Highlights

  • Credential Vault Exchange (OSEP-0025): per-request dynamic header resolution, fail-closed and never persisted, across server and all five SDKs (#2457 #2535 #2534 #2560)
  • Data-safety: execd uploads replace atomically so a failed write keeps the old file (#2433); the controller refuses rootfs snapshots whose commit result lacks digests (#2482)
  • Operability: create timeouts report the pod startup failure reason (#2434); controller and fsb controller gain --namespaces cache scoping (#2571 #2581)

Server

  • Sandbox list pushes metadata equality filters into the Kubernetes label selector, so large namespaces no longer materialize every workload before filtering (#2559)
  • Template OCI hardening: publish repository syntax is validated at create, and sandbox creation requires a digest-pinned publish reference — a succeeded template can no longer resolve to a mutable or unrelated manifest (#2491)
  • Kubernetes create timeouts append the pod startup failure reason (unschedulable, ImagePullBackOff, warning events) before rollback — image pull and scheduling failures are diagnosable through the API again (#2434)
  • Docker: a concurrent container removal no longer aborts the startup expiration restore, and remaining Docker errors fail startup instead of silently dropping timers (#2533); duplicate, root, and server-mount-clashing destinations are rejected with 400 before provisioning (#2421 #2505)
  • An explicit POSIX root in storage.allowed_host_paths now covers its descendants; the descendant check produced // and never matched (#2474)
  • Proxy: a GET that declares a body is forwarded with it, so Elasticsearch-style requests stop failing with 500 (#2518); pool exhaustion returns 503 PROXY_POOL_EXHAUSTED instead of a 180-second wait ending in 500 (#2519)
  • Smaller fixes: metadata=env= filters on the empty value instead of matching everything (#2461); gateway, bucket-name, and env-name validators reject trailing newlines (#2499)

SDKs

Exchange credential vault payloads (exchange source + typeless binding auth) are accepted across all five SDKs, aligned with the server and egress wire contract (#2534 #2560); see the Kotlin/C# ABI note in Upgrade & Compatibility. Detailed download methods expose response metadata (status, Content-Range, isPartial) in all five languages; existing signatures unchanged, custom filesystem implementations must add them (#1597)

Python

  • SSE command streams get a read timeout: commands.run() raises SandboxTimeoutException after max(request_timeout, 30s) of silence instead of blocking forever on a stalled execd (#2527)
  • Async pool graceful shutdown drains pending kills instead of cancelling them and leaving untracked sandboxes until TTL (#2507)
  • Download streams release the HTTP response when dropped, closed early, or abandoned mid-iteration — no more connection-pool exhaustion (#2487 #2531)
  • Transport errors during stream requests and body reads raise SDK exceptions instead of leaking raw httpx errors (#2515 #2525)
  • Smaller fixes: endpoint cache invalidation scoped to the inflight key (#2409); NetworkPolicyAdapter propagates redirect options and event hooks (#2553); the MCP server reports SDK errors instead of a generic message (#2524)

JavaScript

  • Isolated-session requests throw SandboxApiException with status/request id/error code instead of a flat Error (#2466)
  • An infinite or negative redirect deadline is treated as none — requestTimeoutSeconds: Infinity no longer fails every authenticated request (#2488)
  • Closing a download stream inside its own loop ends iteration cleanly instead of throwing a TypeError (#2514)

Kotlin

  • Sub-millisecond command timeouts round up to 1 ms; 500 µs previously serialized as 0, which execd treats as unbounded (#2537)

C#

Covered by the cross-SDK exchange models (#2560).

Go

  • Retry/timeout hardening: Retry-After saturation and the 60 s cap now apply on the generic retry path (#2452 #2508); backoff saturates instead of wrapping negative (#2509); ReadyTimeout is enforced under longer caller deadlines (#2528); interpreter readiness probes are deadline-bounded and require a confirmed zero exit (#2504 #2480); telemetry POSTs get their own five-second bound (#2540)
  • Endpoint cache: in-flight fetches fenced across invalidation (#2503); lifecycle mutations invalidate cached endpoints (#2564); the last waiter leaving cancels the shared fetch (#2579); config/endpoint headers merge case-insensitively (#2529)
  • Pool: idle FIFO survives remove/reinsert (#2453); shutdown waiters honor caller cancellation (#2542); Destroy bounds state-store draining (#2580)
  • Execution.Text() concatenates code fragments without separators, so "Loading" + "...done\n" no longer gains a newline; command formatting unchanged (#2447)
  • Smaller fixes: SSE parser accepts CR-delimited events (#2439); multipart uploads keep their generated boundary (#2468); a flaky diagnostic assertion checks net.Error.Timeout() (#2479)

Controller

  • Rootfs snapshot commits whose result lacks digests now fail (CommitResultUnavailable/CommitResultIncomplete) and report PauseFailed with pods kept — previously pause reported success, deleted the pods, and every later resume failed permanently (#2482)
  • --namespaces scopes the informer cache, fixing startup OOM on large shared clusters; empty default keeps full-cluster semantics; chart value controller.namespaces (#2571)

Execd

  • Uploads are atomic: staged temp file + rename, preserving mode/owner and following symlinks — a failed upload (ENOSPC, I/O error, kill) no longer destroys the replaced file (#2433)
  • The Jupyter token is discovered from the code-interpreter image's jpserver-*.json when JUPYTER_TOKEN is unset; explicit config wins (#2448)
  • The Jupyter startup wait is restored to ~5 minutes — a backoff Cap interaction had aborted after ~11 s against cold images (#2486)
  • EXECD_SSE_LEGACY_COMPAT (default off) omits exit_code from SSE events for pre-1.0.18 SDKs whose kotlinx runtime crashes on the unknown key (#2472)
  • filename* is encoded with RFC 8187 attr-chars, so names with =, :, ' parse in strict clients (#2516)

Networking

egress

  • Credential Vault Exchange (OSEP-0025): exchange source (UDS/HTTPS resolver endpoint), typeless binding auth, marker-only snapshot bindings, a POST /credential-vault/_resolve endpoint, and addon integration injecting resolved headers with fail-closed 407/stream-kill — resolved material is never persisted, cached, or read back; inline-only vaults unchanged (#2457 #2495 #2501 #2500 #2535 #2538); sidecar profile integrated end to end with fast-sandbox e2e (#2549 #2547 #2561); guide chapter added (#2563)
  • Resolver caller auth is deferred (D5): https:// resolver endpoints must be egress-only reachable (UDS permissions or network-scoped nft rules) as the current trust boundary (#2549)
  • OSEP-0023 groundwork continues [EXPERIMENTAL]: Vault v2 safe reader (#2441), strict legacy management with owner fencing (#2460), strict-mode Vault v2 writer with server caller (#2493) — defaults remain v1; strict mode requires the paired fast-sandbox producer
  • A replayed data-plane-ready hook is idempotent (200) instead of a spurious 409 (#2510)

ingress

  • Startup validation: --wake-park-budget must be below --wake-max-flight-lifetime; the misconfiguration previously answered parked requests with 400 or empty 200 instead of 503 + Retry-After (#2511)
  • A queued wake request is bounded by one park budget instead of nearly two (#2513)
  • Smaller fixes: the final status is recorded after a 1xx response, so 103 Early Hints no longer masquerades as the request status (#2484); legacy host-route ports are validated (sb-70000 no longer 502s, sb-08080 no longer reaches port 8080) (#2578); a test-only data race fixed (#2512)

Fast Sandbox

  • Checkpoint chains stay bounded: pause/restore checkpoint layer chains are squashed through overlaybd-merge during commit, so repeated cycles no longer grow the restore chain without limit (#2546 fast-sandbox#158)
  • Node-local VM discovery: the firecracker runtime writes a write-once fast-sandbox.json record per VM — full sandbox UID, namespace, name, placement — next to the process log, so node-local agents discover local VMs by directory scan instead of watching Sandbox CRs cluster-wide; publication never blocks provisioning and teardown removes the record (#2546 fast-sandbox#154)
  • Memory: opt-in guest memory reclamation with pressure-based admission, and free-page reporting enabled by default in built templates (#2546 fast-sandbox#114)
  • Fastlet hardening: the strict managed Actions v2 transport (producer half of this release's egress strict-management receiver), owner-retirement race fixes, terminal-fence preservation across absent inventory, and parallelized admission probes (#2546 fast-sandbox#152)
  • fsb controller: --namespaces scopes the informer cache (refuses to start when the set misses the runtime-environment namespace) and --kube-client-qps/--kube-client-burst tune the shared API-server client rate limiter — opt-in chart values controller.namespaces/controller.kubeClientQPS/controller.kubeClientBurst, defaults render no new flags (#2581 fast-sandbox#161)
  • Init topology: runtime.execd_run_as_init now bakes EXECD_INIT=1 into fsb golden-image template envs — previously ignored on fsb, so execd never took the OSEP-0018 init role; the injected value wins over a client-provided key (#2494); the integration environment pins the full in-guest topology and the whole e2e surface defaults it on (#2497)
  • Integration environment: overlaybd template builds probe the ublk add/del path up front and self-heal wedged kernel devices instead of failing ~16 minutes into a doomed build, and teardown releases orphan devices (#2454); smoke assertions surface real nft errors instead of reading them as missing rules (#2543); a full-chain exchange-resolver verify stage (server relay → egress _resolve → injected headers observed by the guest) rides verify-native/verify-overlaybd (#2561)

Misc

  • CLI: text is escaped instead of parsed as Rich markup — paths like /tmp/notes[draft].txt print correctly and no longer fail successful commands (#2520); broken config files report cleanly instead of crashing every command (#2521); config set stores string-key values verbatim (007123 stays 007123) (#2522); the PTY session is deleted when the WebSocket never opens (#2523)
  • node-agent: a log stream whose directory never appeared is forgotten after the retention window instead of persisting forever (#2471)
  • Docs: image-signing claims aligned with actual workflow coverage (#2467); AAIF charter and governance consolidation — code contributions now require DCO sign-offs (#2490); S3 template examples set format: native (#2506); OSEP-0025 status and contract pins (#2539)
  • Housekeeping: dead code removed across modules (#2496); execd CI mounts tracefs for the eBPF smoke (#2473)

Upgrade & Compatibility

  • Kotlin/C# SDKs are source-compatible but not ABI-compatible with 1.2.0 — recompile; no wire changes (#2560)
  • Python: silent SSE streams now raise SandboxTimeoutException after max(request_timeout, 30s) (#2527); download-body errors raise SDK exceptions instead of raw httpx errors (#2525)
  • Go: Execution.Text() no longer inserts separators between code fragments (#2447); nonempty positional Execution{...} literals no longer compile — use keyed literals
  • Execd atomic uploads: writable file in non-writable directory is rejected, device/FIFO destinations refused, xattrs/ACLs/setgid bits not carried, bind-mounted destinations fail (EBUSY), a SIGKILL mid-upload can leave a .osb-upload-* staging file, peak disk ~3× file size (#2433)
  • Server startup: a Docker error during expiration restore (other than concurrent removal) now prevents startup — run under a restarting supervisor (#2533)
  • Ingress: --wake-park-budget below --wake-max-flight-lifetime is now enforced at startup; defaults unaffected (#2511)
  • Template list: metadata=env= now filters on the empty value (#2461)
  • OSEP-0025: exchange is additive and opt-in, but resolver caller auth is deferred — https:// resolver endpoints must be egress-only reachable (#2549 #2563)
  • Fast Sandbox: free-page reporting on by default; per-VM fast-sandbox.json record; controller.namespaces must cover pools/sandboxes and the runtime-environment namespace or the controller refuses to start (#2546 #2581)
  • Images: opensandbox/<component>:release-1.2.1-rc.1 (all three registries; fsb images for fast-sandbox)
  • Packages: unchanged — SDK/CLI artifacts publish at the 1.2.1 stable release
  • Charts: render from this tag (helm template ./manifests/charts/opensandbox)
  • Kubernetes: supported range v1.21 – v1.34
  • Skew: server ↔ CLI/SDK same line supported; ±1 minor warns

👥 Contributors

Thanks to these contributors ❤️

Artifacts

  • Images: opensandbox/<component>:release-1.2.1-rc.1 (Docker Hub / GHCR / ACR)

Installation

# Platform (Kubernetes) — render the chart at this tag and apply
git clone https://github.com/opensandbox-group/OpenSandbox
cd OpenSandbox
git checkout release-1.2.1-rc.1
helm dependency build manifests/charts/opensandbox  # package file:// sub-charts (not committed)
helm template ./manifests/charts/opensandbox | kubectl apply -f -
# or point your GitOps platform (Argo / Flux) at the repo path + tag

Verify what you installed against the BOM: see Release Verification.

Don't miss a new OpenSandbox release

NewReleases is sending notifications on new releases.