Repository navigation
Releases: opensandbox-group/OpenSandbox
Release list
OpenSandbox 1.2.0-rc.2
Second release candidate on the 1.2.0 line, carrying everything merged since release-1.2.0-rc.1 — the headline is the Credential Vault (OSEP-0022) landing end to end for fast sandboxes: an egress-held vault with durable per-subject persistence and pause/resume transfer, the user-reachable server-routed vault API, and a pool-pinned MITM CA. Network policy follows the same egress-held pattern, and NetworkRule gains port-scoped rules in the classic profile — fast sandboxes reject them fail-closed until the fastlet nft path enforces them. Images publish now; SDK/CLI packages ship at the 1.2.0 stable bump. Please test and report issues.
Highlights
- Credential Vault (OSEP-0022) lands end to end for fast sandboxes: the egress sidecar holds the vault with durable per-subject files, sealed snapshot transfer for pause/resume, and stale-subject takeover on slot reuse (#2249 #2252 #2255 #2250); the server exposes the user-reachable
POST|GET|PATCH|DELETE /v1/sandboxes/{id}/credential-vaultAPI over the FastPath handler relay with credential values never appearing on any public read (#2263), and the MITM CA is pinned per pool so an egress restart no longer rotates the trust anchor under live sandboxes (#2251) - Network policy mirrors the vault: the recorded fast-sandbox policy becomes egress-held state with relay CRUD, durable persistence, and pause/resume snapshot transfer (#2309), and
NetworkRulegains port-scoped rules end to end in the classic profile — spec, sidecar nftables, server validation, and all five SDKs, while fast sandboxes rejectportsfail-closed for now — plus an operator-side TCP port floor that no user policy can open (#2212) - Lifecycle and diagnostics polish:
lifecycle.renewalPolicyreplaces the opaque renew extensions key (OSEP-0009) (#2296); the diagnostics context API reaches server, all five SDKs, and the CLI (#2292); execd fixes make start failures distinguishable, preserve session output byte-for-byte, and stop silent disk growth (#2303 #2318 #2295)
Server
- Implement the server side of the Credential Vault (OSEP-0022, fast-sandbox profile): the user-reachable
POST|GET|PATCH|DELETE /v1/sandboxes/{id}/credential-vaultAPI pushes complete vault revisions over the FastPathInvokeHandlerActionhandler relay and treats the relay response as the synchronous egress-validated ack — the CRD never carries vault material, and every public read is sanitized server-side; PATCH composes in memory (delete → replace → add, each name resolving at most once) withexpectedRevisionas the cross-replica backstop, non-fsb backends proxy the sidecar's/credential-vaultsurface, and the fsbcredentialProxycreate-time rejection is lifted (#2263) - Bake the pinned MITM CA into server-created SandboxTemplates (OSEP-0022): new
[kubernetes] template_mitm_ca_secret/template_mitm_ca_secret_keyconfig (default off) emitsspec.egressMitmCertificate— the certificate entry only, never the private key — on every server-created template; the Secret must exist in each template namespace (ops-runbook distribution), and a missed copy fails the build observably (#2288) - Add
lifecycle.renewalPolicy(OSEP-0009): the renew-on-access opt-in moves from the opaqueextensions["access.renew.extend.seconds"]create key to a structuredrenewalPolicy.extendSeconds(300–86400), mirroring theidlePolicypattern; the legacy key is deprecated (supported throughout 1.x, removed in 2.0), setting both is accepted only when the parsed values match, the value normalizes into the existing Docker label / Kubernetes annotation so pre-upgrade sandboxes keep renewing, and fast sandbox rejects it with 400 rather than silently accepting a no-op (#2296) - Implement the diagnostics context endpoint (OSEP contract from rc.1):
GET /v1/sandboxes/{sandboxId}/diagnostics/contextreturns the sandbox ID, lifecycle state, backend type, and backend-defined identifiers for Docker, Kubernetes, and Fast Sandbox without connecting to execd — Kubernetes context stays available before a Pod is allocated, and extension backends that don't implement it return 501 (#2292) - Forward the still-encoded path through the sandbox proxy: Starlette bound the
{full_path:path}route from the percent-decoded path and the proxy pasted that value back into the upstream URL, so a decoded?started a query,#a fragment,%2Fbecame a separator,%25and non-UTF-8 escapes were rewritten, and%2E%2Esegments were resolved — the proxy now takes the client's still-encoded tail from the raw path (validating it decodes to the routed path, with a re-encode fallback) and reads the query from the raw query string; backends receive request targets byte-for-byte, matching the ingress gateway, and the%2E%2Eprefix-escape on Docker host-mapped endpoints is closed (#2228) - Report BatchSandbox expiry while the pod is still Running: a past
spec.expireTimewas still reported asRunning(Ready pod with IP), so wall-clock billing kept growing after the configured timeout; expiry now checks before the ready states and returnsTerminated/SANDBOX_EXPIREDwithlast_transition_atset to theexpireTimeitself (#2238) - Reject gVisor snapshots that would silently drop writes:
docker commitunder gVisor's default--overlay2=root:selfcan reportREADYwhile restoring none of the sandbox's writes (only the opaque/.gvisor.filestore.*entry is visible); snapshot preflight now resolves the container's runtime and refuses gVisor unless the effective args include--overlay2=none, and create fails closed when the filesystem diff is filestore-only (#2241) - Apply
[docker]security profiles andsecure_runtimeto the execd cache container: it ignoredapparmor_profile/seccomp_profile/docker_runtime, so on a host whose default seccomp profile blocks the execd image, everyPOST /sandboxesfailed 503 with no configuration escape (#2254) - Make concurrent Docker metadata patches safe: the store's read-modify-write was unlocked and every write shared one temporary name, so two concurrent
PATCH /metadatarequests for a sandbox raced into a 500 (FileNotFoundErroron rename) or silently dropped the other's key; a store-wide lock plus per-write temp names serialize patches (last write wins per key), a failed write no longer leaves its temp file behind, and the endpoint contract is now documented per runtime — Kubernetes patches write back the whole label set it read, fsb patches send only their own keys fenced by UID and generation (#2267 #2281) - Resolve proxy endpoints off the event loop: HTTP/WebSocket proxy handlers called the synchronous
get_endpointdirectly — a Docker socket call per proxied request on Docker, an apiserver read on Kubernetes cache misses — blocking the loop for every other request and stream; a slow Docker daemon (e.g. during an image pull) was enough to freeze the server; the lookup now runs viaasyncio.to_threadwith tenant context preserved (#2268) - Apply the renew cooldown when the Redis consumer is enabled: proxy renews skipped
min_interval_secondsentirely (every proxied request ran aget_sandbox+ expiration renew) and queue intents were only throttled per ingress replica, so N replicas renewed up to N times per interval; the cooldown now applies to every renew source and starts only after a successful renew (#2276 #2279)
SDKs
Code changes below ride the 1.2.0 stable release — rc ships no SDK packages.
- Add the Credential Vault client contract to all five SDKs (OSEP-0022): template-backed sandboxes route vault calls through the server on the same template-origin signal as network policy (
OPEN-SANDBOX-ORIGIN: templateor the localcreateFromTemplateflag), while sidecar-direct adapters stay untouched; Python'sSandbox.credential_vaultnow returns the vault service for both profiles, and Go gainsSandbox-level vault methods branching on the same signal (#2263) - Add
lifecycle.renewalPolicyto all five SDKs (OSEP-0009): a typed create field with client-side fail-fast validation (extendSeconds300–86400) wired through the create converters, replacing the opaque extensions key (#2296) - Implement diagnostics across the SDK surface (with [#2292](https://github.com/op...
OpenSandbox 1.2.0-rc.1
First release candidate on the 1.2.0 line, carrying everything merged since release-1.1.1 — the headline is OSEP-0024: auto-pause and wake-on-access for fast sandboxes, landing end to end. Images publish now; SDK/CLI packages ship at the 1.2.0 stable bump. Please test and report issues.
Highlights
- Auto-pause and wake-on-access for fast sandboxes (OSEP-0024) lands end to end: ingress activity tracking and the wake path, the server-side idle sweeper, the opt-in
lifecycle.idlePolicycreate field, and the wake-retry contract in all five SDKs — off by default, additive only (#2156 #2180 #2192 #2198 #2218) - Egress sidecar (experimental, opt-in): live credential-bound Vault admission, HTTP/2 support with bounded drain, atomic policy-file persistence, and runtime fault containment (#2176 #2201 #2182 #2162 #2172)
- SDK reliability: file uploads and downloads are no longer aborted at the 30-second request timeout in any of the five SDKs; execd now reports sandbox-scoped CPU/memory and OOM kills (#2165 #2186 #2196 #2206 #2191 #2200)
Server
- Implement the server side of OSEP-0024 auto-pause for fast sandboxes: the lifecycle create field
lifecycle.idlePolicy(idleTimeoutSeconds30–86400,onIdle: pause,wakeOnAccess, optionalactiveProbe) validates with pydantic bounds and persists as the reservedsandbox.opensandbox.io/idle-policySandbox CR annotation (annotation-write failure stays fail-safe — the sandbox is created unpolicied); an idle sweeper enumerates opted-in fsb sandboxes from the informer cache, checks activity in Redis (monotonic-max writes, missing-key tombstones can only delay a pause), takes a per-sandbox pause lock with re-verify under lock, and issues an idempotent FastPathPauseSandbox; a declaredactiveProbeexecutes through the ingress gateway and a busy answer records ordinary activity; anOpenSandbox-Skip-Activity: skipsentinel suppresses the observation for one request and is stripped on both gateway and server-proxy hops (#2192 #2181) - Configure idle-pause under the new lifecycle-automation umbrella:
[idle_pause]becomes[automation.idle_pause]and the redundantredis.enabledgate is removed —redis.dsnis required (validated at load) whenenabled = true, failing loudly instead of silently not starting the sweeper;configuration.mdis rewritten for conciseness and now documentsingress.secure_accessand the missing env-var overrides (#2215) - Default BatchSandbox pods to
restartPolicy: Never: a kubelet in-place container restart recreated the main container from its image — wiping the writable layer and in-memory state — while the sandbox kept reportingSucceed; restarting is never a meaningful recovery for a sandbox, so the honest default is no restart, which also makes the controller's terminal-failure path reachable. An explicitrestartPolicyin the template still wins, the Windows profile keeps forcingAlways(multi-phase installation relies on it), and pool mode is unchanged (#2128) - Treat
pasta/slirp4netns/noneas container-runtime network modes and pass them straight through, making podman's kernel-level networking selectable — on Windows 11 + WSL2 where netavark breaks bridge mode,pastawas the only working network and previously failed create with a 400 asking for a user-defined Docker network;networkPolicyis now rejected for these modes since there is no bridge for the egress sidecar to attach to (previously silently accepted fornone) (#2203) - Parse Docker timestamps whose fractional second is not 3 or 6 digits: Docker writes RFC3339Nano (trailing zeros dropped), Python 3.10's
fromisoformatrejects 5-digit fractions, and the helper fell back todatetime.now()— socreatedAtandstatus.lastTransitionAtsilently reported the current time; the fraction is now zero-padded to 6 digits (#2220)
SDKs
Code changes below ride the 1.2.0 stable release — rc ships no SDK packages.
- Add the OSEP-0024 SDK contract to all five languages: a typed
lifecycle.idlePolicycreate field (IdlePolicy/ActivityProbemodels with client-side fail-fast validation mirroring the server: timeout 30–86400 s,onIdle: "pause"pairing, probe path/timeout bounds; wired through both standard and template create) and the wake-retry contract — on a503carrying the gateway-ownedOpenSandbox-Wake: expiredmarker (its presence proves the request was parked and never forwarded, making any-method retry safe), the SDK resends afterRetry-Afterunder a bounded budget (default 5 attempts / 60 s, per-client knobs), exhaustion raises a typed wake-timeout exception carrying attempts and waited time, the server-side resume is never canceled, bare503handling is unchanged, and SSE/streaming transports bypass wake retry (#2198 #2218) - Stop capping file uploads and downloads at the request timeout (30 s default) in all four remaining SDKs — the whole-call timeout (
http.Client.Timeout, OkHttpcallTimeout,AbortController,HttpClient.Timeout) covered sending the request body and reading the response, so any large or slow transfer failed mid-flight even while healthy; transfers now run on a dedicated client with no whole-call deadline that still bounds the wait for response headers, so a stalled transfer still fails instead of hanging (#2165 #2186 #2196 #2206) - Suppress
sandbox.createclient metrics for client-pool warmup creates in Go, Python, and JavaScript, aligning with Kotlin: warmup traffic (hardcodedskipHealthCheck, hundreds of ms) previously mixed into user-facing create-latency metrics and dragged p50/p99 down; direct-create from pool acquire and user-facing creates keep reporting (#2199)
Python
- Clean up cancelled async pool startup: cancelling
SandboxPoolAsync.start()mid-await left the instance stuck inSTARTING, where a subsequentstart()silently returned and startup-owned resources skipped cleanup; cancellation now reuses the reconciliation cleanup path, moves toSTOPPED, and re-raises so the caller can retry (#2209) - Stop reporting failed file replacements as successful: the ordinary filesystem adapters caught and discarded the generated client's
JSONDecodeError, so an empty or non-JSON HTTP 400/500 fromreplace_contents()returned success and an agent could keep building on an unapplied edit; the catch is removed and the documentedSandboxExceptionfailure contract is restored (isolated-session adapters keep their empty-body compatibility catch) (#2222)
Go
- Strip all
OPEN-SANDBOX-*/OPENSANDBOX-*headers and every configured authentication header (includingX-EXECD-ACCESS-TOKEN,X-API-Key, and customWithAuthHeadernames) on cross-origin redirects, comparing each destination against the initial request's scheme, host, and effective port so credentials cannot reappear on later foreign-origin hops; applied across lifecycle, execd, egress, file transfers, SSE, and creation metrics while preserving customCheckRedirectpolicies (#2178)
Controller
- Retain a BatchSandbox until cleanup completes: a deleted sandbox could disappear from the lifecycle API (GET 404) while its owned pod still ran, letting a replacement sandbox write the same persistent directory before the old writer exited; deletion now uses foreground cascading deletion alongside the existing task-cleanup and pool-release finalizers, and TTL reconciliation stops once deletion is accepted —
DELETEstill returns 204 when accepted, queries reportStoppingduring managed cleanup, and 404 arrives only after the runtime is gone; all five SDKs'killdocs and logs describe the accepted-deletion semantics (#2154)
Execd
- Report OOM kills via cgroupfs polling: kernel-side evidence was out of reach from inside a sandbox (no
CAP_SYSLOG, no/dev/kmsg), so OOM events were unobservable; execd now polls the sandbox cgroup every 5 s and diffs the kernel OO...
OpenSandbox 1.1.1
Stable cut of the 1.1.1 patch line: fixes the broken 1.1.0 server wheel — hatchling's VCS-exclude filtering silently dropped the committed FastPath gRPC stubs from the PyPI package, so every 1.1.0 install of opensandbox-server failed at import time; PyPI is immutable, so the fix ships on this line (#1959). SDK/CLI/server packages publish at 1.1.1 (1.1.1-rc.1 shipped images only). Everyone on 1.1.0 should upgrade. Baseline for every section below: release-1.1.0.
Highlights
- Fixes the
1.1.0server wheel: FastPath stubs now ship in the package and installs import cleanly; SDK/CLI/server packages publish at1.1.1(#1959) - Identity-bound filesystem operations: execd executes file APIs as an explicit Linux UID/GID, with
FilesWithIdentity/files_with_identityin all five SDKs; isolated sessions gain multiple independent overlay mounts (#2070 #2093 #2094 #2095 #2096 #2097 #2032) - Server hardening: snapshot creation submits the runtime object before persisting so early reads can't map to
Failed, proxied backend failures map to 502/504 instead of a generic 500, and diagnostics work for pooled sandboxes (#2098 #2077 #2084 #2104)
Server
- Implement pause/resume for the agent-sandbox provider by bridging to
spec.operatingMode(Running/Suspended) onagents.x-k8s.io/v1beta1, with state derivation (Paused/Pausing/Resuming/Terminated) and 409 on invalid transitions; both endpoints previously returned "not supported" (#1975) - Isolate tenants that share one Kubernetes namespace: creates stamp
opensandbox.io/tenant, and list/get/delete/pause/resume/proxy hide other tenants' labeled sandboxes as 404 while unlabeled legacy sandboxes stay visible until expiry; also addskubernetes.insecure_skip_tls_verifyfor ServiceAccount CA mismatches (#1973) - Heal the silent renew-intent drop after a server restart in multi-tenant mode: the resolver now falls back to a cluster-wide label lookup with a 403 circuit breaker and structured warnings, trusts an ingress-observed namespace from the intent payload when verifiable, and single-tenant deployments never resolve beyond the configured namespace (#1819)
- Add
[docker] publish_hostto publish bridge-mode sandbox ports on one host address (default0.0.0.0unchanged) — with127.0.0.1or the Docker bridge gateway, the tokenless execd port no longer lands on public interfaces (#1954) - Add a typed, admin-only
[egress.upstream_proxy]config section (URL + optional authorization, validated at load) that injects the sidecar's chained upstream CONNECT proxy on both Docker and Kubernetes, with fail-closed 400s when the requestednetworkPolicyis incompatible; secret values are no longer echoed in config-validation errors (#1999), plusca_cert_path(Docker) andca_secret_name(Kubernetes) sohttps://proxies signed by a private CA are trusted without replacing the system trust store — the PEM augments/etc/ssl/certsin the sidecar (#2030) - Make snapshot creation race-free for early readers on k8s/fsb:
POST /sandboxes/{id}/snapshotsnow creates the runtime object (SandboxSnapshot CR / fsb submit) before persisting theCreatingrow and returns without queueing a worker, so read-time sync can no longer map a not-yet-created object toFailed— including on other HA replicas; recovery resubmits stuck rows inline (#2098), fixing the window where a client polling right after create flipped the row to a permanentFailed(#2077) - Map proxied backend failures to meaningful statuses: read/write timeouts return 504
BACKEND_TIMEOUT, mid-response resets and protocol errors return 502BACKEND_RESPONSE_FAILED, keeping 502BACKEND_CONNECTION_FAILEDfor connect failures — a sandbox that dies mid-request no longer looks like a server bug (#2084) - Find pool-allocated pods for k8s diagnostics: pooled BatchSandboxes don't carry the
opensandbox.io/idpod label, so every logs/events/inspect call returned 404; the lookup now falls back to the pods listed in the workload'salloc-statusannotation (#2104) - Remove auto-created Docker volumes when create fails before the provisioning block runs (image pull, credential/upstream-proxy checks, Windows prerequisites, OSSFS prep) — a mistyped image name no longer leaks the PVC it triggered (#2103)
- Report deleting pooled/task-mode BatchSandboxes as
Stopping/DELETING:metadata.deletionTimestampnow wins over runtime status publication, so a sandbox whose pods go not-ready during finalization no longer flips back toPending/CREATING(#2122) - Stop Docker metadata PATCHes from resurrecting deleted keys:
patch()now starts from the override file (asget()does) instead of re-merging container labels, so{"env": null}stays deleted and an emptied override file doesn't restore the original labels (#2065) - Accept
envon template create and bake it into the fast-sandbox golden image (/etc/sandbox-init.env, inheriting the source image's OCIConfig.Env), closing the gap whereSandboxTemplate.spec.envsexisted but the lifecycle API rejected it; supported across all SDKs (#1984) - Wake Kubernetes creation waits on informer watch events instead of fixed 1-second polling, cutting prewarmed-pool create latency from ~1 s to tens of milliseconds while keeping the polling fallback (#1996)
- Ship the committed FastPath gRPC stubs in the server wheel by force-including them past hatchling's VCS excludes, and smoke-import the built wheel in the release workflow before publishing — this is the
1.1.0install-time breakage fix (#1959) - Fix the Fast Sandbox snapshot CR plural (
sandboxsnapshots) in Helm RBAC and the fsb snapshot runtime, wheresandboxesnapshotscould fail list/watch with missing RBAC (#1972) - Propagate the underlying FastPath error message through the 429 capacity mapping (keeping status and
Retry-After, adding acausefield) so admission/runtime failures like a broken KVM device are no longer masked as pool exhaustion, and warn-log the raw error before mapping (#2001) - Clarify the renewal contract:
renew-expirationmay shorten, preserve, or extend expiration (any future target is valid) andmax_sandbox_timeout_secondscaps only creation, not renewal or total lifetime — documentation and SDK references aligned, with regression coverage across Docker, Kubernetes, and FastSandbox (#2087) - Fix Docker edges: reject a repeated pause of an already-paused sandbox with the documented 409 instead of surfacing Docker's "already paused" as 500 (#2023), and clear Docker masked/read-only system paths only for sandboxes that opt into the isolation extension so nested Bubblewrap can mount a private procfs (#1982)
SDKs
- Add identity-bound filesystem operations end to end: execd gains additive
/v1/filesystem/{uid}/{gid}routes backed by a per-request worker process running with explicit credentials and account-derived supplementary groups (fail-closed, existing routes unchanged), and every sandbox SDK exposes it — Pythonfiles_with_identity, JavaScriptfilesWithIdentity, KotlinfilesWithIdentity, C#FilesWithIdentity, GoFilesWithIdentity— so Linux checks the selected user's permissions during actual access; identity selection belongs in the trusted backend, this enforces filesystem permissions and is not a tenant boundary (#2070 #2093 #2094 #2095 #2096 #2097) - Expose multiple independent overlay mounts per isolated session across the API and all five SDKs: `CreateIsolatedSessionRequest...
OpenSandbox 1.1.1-rc.1
First release candidate on the 1.1.1 patch line, cut to fix the broken 1.1.0 server wheel: hatchling's VCS-exclude filtering silently dropped the committed FastPath gRPC stubs from the PyPI package, so every 1.1.0 install of opensandbox-server failed at import time — PyPI is immutable, so the patched line is 1.1.1 (#1959). Beyond that fix, this rc carries everything merged since release-1.1.0: agent-sandbox pause/resume, shared-namespace tenant isolation, watch-driven create latency, commands.setEnv and a systematic audit cleanup across all five SDKs, upstream-proxy chaining for egress, and two fast-sandbox pin bumps. Baseline for every section is release-1.1.0. rc releases publish images but hold packages — SDK/CLI artifacts ship at the 1.1.1 stable bump. Please test and report issues.
Highlights
- Fixes the
1.1.0server wheel: FastPath stubs now ship in the package and installs import cleanly (#1959) - Server: pause/resume on the agent-sandbox provider, tenant isolation for sandboxes sharing a Kubernetes namespace, and pool creates woken by watch events instead of 1-second polling (#1975 #1973 #1996)
- SDKs:
commands.setEnvruntime env injection in all five languages, plus a systematic cross-language audit cleanup fixing dozens of correctness bugs (#2013 #2020)
Server
- Implement pause/resume for the agent-sandbox provider by bridging to
spec.operatingMode(Running/Suspended) onagents.x-k8s.io/v1beta1, with state derivation (Paused/Pausing/Resuming/Terminated), resourceVersion preconditions, and 409 on invalid transitions; both endpoints previously returned "not supported" (#1975) - Isolate tenants that share one Kubernetes namespace: creates stamp
opensandbox.io/tenant, and list/get/delete/pause/resume/proxy hide other tenants' labeled sandboxes as 404 while unlabeled legacy sandboxes stay visible until expiry; also addskubernetes.insecure_skip_tls_verifyfor ServiceAccount CA mismatches (#1973) - Heal the silent renew-intent drop after a server restart in multi-tenant mode: the resolver now falls back to a cluster-wide label lookup with a 403 circuit breaker and structured warnings, trusts an ingress-observed namespace from the intent payload when verifiable, and single-tenant deployments never resolve beyond the configured namespace (#1819)
- Add
[docker] publish_hostto publish bridge-mode sandbox ports on one host address (default0.0.0.0unchanged) — with127.0.0.1or the Docker bridge gateway, the tokenless execd port no longer lands on public interfaces (#1954) - Add a typed, admin-only
[egress.upstream_proxy]config section (URL + optional authorization, validated at load) that injects the sidecar's chained upstream CONNECT proxy on both Docker and Kubernetes, with fail-closed 400s when the requestednetworkPolicyis incompatible; secret values are no longer echoed in config-validation errors (#1999) - Accept
envon template create and bake it into the fast-sandbox golden image (/etc/sandbox-init.env, inheriting the source image's OCIConfig.Env), closing the gap whereSandboxTemplate.spec.envsexisted but the lifecycle API rejected it; supported across all SDKs (#1984) - Wake Kubernetes creation waits on informer watch events instead of fixed 1-second polling, cutting prewarmed-pool create latency from ~1 s to tens of milliseconds while keeping the polling fallback (#1996)
- Ship the committed FastPath gRPC stubs in the server wheel by force-including them past hatchling's VCS excludes, and smoke-import the built wheel in the release workflow before publishing — this is the
1.1.0install-time breakage fix (#1959) - Fix the Fast Sandbox snapshot CR plural (
sandboxsnapshots) in Helm RBAC and the fsb snapshot runtime, wheresandboxesnapshotscould fail list/watch with missing RBAC (#1972) - Propagate the underlying FastPath error message through the 429 capacity mapping (keeping status and
Retry-After, adding acausefield) so admission/runtime failures like a broken KVM device are no longer masked as pool exhaustion, and warn-log the raw error before mapping (#2001) - Fix Docker edges: reject a repeated pause of an already-paused sandbox with the documented 409 instead of surfacing Docker's "already paused" as 500 (#2023), and clear Docker masked/read-only system paths only for sandboxes that opt into the isolation extension so nested Bubblewrap can mount a private procfs (#1982)
SDKs
Code changes below ride the 1.1.1 stable release — rc ships no SDK packages.
- Add
commands.setEnvfor persistent runtime env injection in all five sandbox SDKs (Python async/sync, JavaScript, Kotlin/Java, Go, C#): appendsKEY=VALUEto the sandbox env file with runtime-exact serialization, local key/NUL validation, and a loud failure whenEXECD_ENVSis unset (#2013) - Systematic cross-language audit cleanup across sandbox, code-interpreter, and MCP SDKs: Go retry backoff collapsing to zero delay and dropped session
cwd, Python isolated-session ignoringencodingand readiness budgets reporting stale errors, JS resume sharing and killing the source sandbox's transport, Kotlin codes runs dropping every SSE-framed event and never settingexitCode, C# create failing on fresh-sandbox 404 and NREs on empty bodies, and MCP per-call closes tearing down the server-wide shared transport — each fixed with regression tests (#2020) - Support
envon template create across all five SDKs, matching the server-side golden-image baking (#1984) - Align background command stream completion across languages: with execd's fixed completion delay removed, background result collection in Go, Kotlin, and C# now returns on startup acknowledgement like Python and JavaScript instead of waiting for EOF and inferring exit code 0 — query command status for the eventual outcome; foreground calls still read to EOF for older servers (#1825)
Controller
- Fix an image-committer deadlock where pausing the QEMU container via cgroup v2 freeze never completes (KVM worker kernel threads never reach
frozen), by skipping the redundant freeze for the QEMU container already paused over QMP and committing with--pause=false(#1986)
Execd
- Remove the fixed one-second delay after command completion, cutting median terminal-event-to-return latency from ~1000 ms to sub-millisecond while retaining output flushing and heartbeat cleanup (#1825)
- Support multiple independent overlay mounts per session in the bwrap isolation layer (separate uppers for workspace vs system paths, nested shadowing, ephemeral tmpfs uppers); internal foundation with no public API change — the request-level
overlaysfield follows (#2021) - Stop logging every stdout/stderr chunk at info level, which accounted for 99.8% of execd log volume on output-heavy workloads (#1906)
Networking
egress
- Add an opt-in chained upstream CONNECT proxy to the transparent mitmproxy path (
OPENSANDBOX_EGRESS_UPSTREAM_PROXY/_AUTH), routing sandbox egress through corporate forward proxies with SNI/Host-consistent CONNECT authority, fail-closed startup validation, and no behavior change when unset (#1816) - Continue the credential-bound TLS interception foundation (OSEP-0023) [EXPERIMENTAL]: bootstrap authenticated revision sessions on sidecar launch and restart behind an internal gate (#1948), stage unpublished Credential Vault mutation candidates with acknowledge-before-publish commit semantics (#1980), serialize
/policyand Vault writes behind a shared mutation barrier (#1991), stage always-rule reloads before publishing (#1997), retain exact attempt identity across indeterminate coordinator outcomes (#2007), and add post-bootstrap revision updates with exact-attempt reconciliation (#2017) — groundwork only; public Vault writes still 503 under the gate and selective ...
OpenSandbox 1.1.0
First stable release of the unified umbrella release line: one platform version — 1.1.0 — covers server, component images, charts, CLI, and every published SDK, cut from one commit and pinned in a signed BOM (versioning starts at 1.1.0; 1.0.0 was already consumed by legacy releases, so the line skips it by design per the umbrella governance OSEP). Everyone on a legacy per-component version should upgrade; legacy tag namespaces are frozen. Because this is the first umbrella cut, sections carry different baselines — the controller jumps from its May v0.2.0 release, ingress from July, the JavaScript/C#/Go SDKs from their July releases — so those sections list several months of changes at once.
Highlights
- First unified umbrella release: every artifact ships at
1.1.0from tagrelease-1.1.0, with a Sigstore-signed BOM as the digest authority - First release of the Fast Sandbox integration: a Firecracker microVM runtime behind the FastPath fleets backend, with pause/resume, ingress FastPath routing, and fsb template management in all five SDKs
- Snapshot persistence gains an opt-in PostgreSQL store with multi-process HA and a SQLite migration command, and sandboxes gain preStart/periodic lifecycle hooks
Server
- Add the FastPath fleets lifecycle backend: create, renew, delete, and status mapping served through FastPath v2, plus end-to-end fsb pause/resume with async 202 semantics and UID fencing (#1703 #1844)
- Converge snapshot status via a per-namespace watch instead of blocking polls, and add fast-sandbox snapshot support (#1846)
- Add an opt-in PostgreSQL snapshot store with pooled connections and CAS semantics, a one-shot
migrate-snapshotscommand for moving SQLite records, and multi-process HA coordination for snapshot create/recovery/delete on Kubernetes (#1653 #1670 #1724) - Transport sandbox lifecycle hooks (OSEP-0020): create requests accept
preStartandperiodichooks, validated and serialized into a reserved environment variable for Kubernetes workloads; the Docker provider rejects lifecycle requests for now (#1589 #1542) - Implement a stable, scoped diagnostics API over the existing log/event collectors with
logs:container|allandevents:runtime|lifecycle|allscopes, explicit warnings on partial scopes,400 DIAGNOSTICS_SCOPE_UNSUPPORTEDfor unknown scopes, and backward-compatible plain-text responses (#1553) - Export generic HTTP request metrics (request rate, error rate, latency) from the server over OTLP, complementing the SDK-reported create-duration histogram (#1561)
- Reject unsupported gVisor (
runsc) snapshots before persistence with409 SNAPSHOT::UNSUPPORTED_RUNTIME(#1720) - Support independent resource requests/limits for the Kubernetes egress sidecar via
[egress.resources](#1620), add[egress] otlp_endpointso the sidecar exports metrics to an OpenTelemetry collector on both runtimes (#1753), pause and resume the sidecar together with the sandbox (#1441), make the sidecar readiness wait configurable via[egress] timeout_seconds(#1522), and injectOPENSANDBOX_EGRESS_SANDBOX_IDserver-side so sidecar telemetry can no longer be spoofed by request env (#1381) - Honor
image.authin create requests for the agent-sandbox provider with per-sandbox pull secrets that are garbage-collected with the Sandbox CR (#1789) - Source OSEP-0011 secure-access signing keys from the
OPENSANDBOX_SECURE_ACCESS_KEYSenvironment variables or a Kubernetes Secret instead of plaintext chart values (#1445) - Make the reverse proxy's internal resolution configurable with
[proxy] resolve_internalfor deployments that cannot route to Docker bridge IPs (#1631) - Raise the
preStart.timeoutSecondslimit from 300 to 10800 seconds across the server API, execd, and SDKs (#1637) - Add Docker-runtime-wide
sandbox_envandsandbox_bindsconfiguration, giving Docker parity with the Kubernetes pod template for fleet-wide CA bundles, proxies, and mounts (#1303) - Breaking: migrate the agent-sandbox provider and ingress informer to
agents.x-k8s.io/v1beta1required by agent-sandbox v1.0.0, creating workloads withoperatingMode: Runningandservice: true; see Upgrade & Compatibility (#1763) - Support pool-only sandbox creation (
poolRefwithout entrypoint) instead of rejecting it with HTTP 400, scheduling the execd-start task per allocation (#1729), fail fast on exhausted namespace ResourceQuotas by surfacing the Sandbox CR's admission rejection (#1752), and surface pool back-pressure as HTTP 429KUBERNETES::POOL_CAPACITY_EXHAUSTEDwithRetry-Afterinstead of a generic readiness timeout (#1581) - Propagate template container
securityContextinto generated pods (#1564), honor the PVCreadOnlyflag on the volume source and not just the mount (#1246), allow mixed read-onlysubPathmounts from a shared PVC (#1440), rejectnetworkPolicycombined withextensions.poolRef(#1319), and patch PVC owner references through the supported strategic-merge path (#1538) - Make the informer-backed cache the only Kubernetes read path by removing the
informer_enabledflag, and expire stale informer entries when a watch stops delivering (#1926 #1534) - Reserve Docker host ports in-process between probing and container start, and retry container creation when the probed port conflicts at Docker bind time (#1666 #1715)
- Map completed agent-sandbox pods to terminal states (#1643), report
Failedwhen a pooled sandbox task fails (#1663), keep direct responses out of the informer cache (#1674), resolve agent-sandbox server-proxy endpoints fromstatus.podIPswith IPv6 bracketing (#1424), and use the public state in invalid-resume errors (#1586) - Handle websocket close semantics: map abnormal upstream closures and relay client closes with a legal status code (#1642 #1731)
- Fix HTTP proxy fidelity: preserve repeated response headers without duplicating Uvicorn's own (#1858 #1471), forward the query string unchanged (#1902), rewrite root-relative redirects to stay on the proxy path (#1516), release backend responses when clients abort mid-stream (#1488), stop leaking the ingress routing header on server-proxied endpoints (#1503), and generate unique OpenAPI operation IDs (#1634)
- Improve proxy diagnostics: identify backend connection failures (#1675) and describe transparent proxy responses in the spec (#1701)
- Preserve the egress auth token on internal proxy requests to port 18080 across Docker and Kubernetes (#1880)
- Route Compose proxying through host port mappings (#1698)
- Fix Docker runtime edges: skip a missing IPv6 sysctl in Kubernetes init (#1721), use the supported
no-new-privilegesseparator ([#1383](https://...
python/sandbox/v0.1.17.dev0
Merge pull request #1707 from jianpingpei/chore/python-sdk-0.1.17-dev…
components/execd 1.1.0
What's New
✨ Features
-
execd as sandbox init (OSEP-0018). execd can now run as PID 1: single reaper with a
managedProcessabstraction on every launch path, signal forwarding (TERM/HUP/USR1/USR2/WINCH), entrypoint-owned exit-code propagation, and a subreaper fallback for the Pool path. Opt-in viaEXECD_INIT=1(injected by the server'sruntime.execd_run_as_initfor Docker/K8s Batch/Agent/Pool); classic topology unchanged and default off. (#1474) -
Pre-exec hardening floor. The new
opensandbox-launchernative helper applies env strip → KEEPCAPS → bounding-set trim →no_new_privs→ identity drop → seccomp → execve;[landlock]adds a filesystem allowlist (fail-open below ABI/kernel 5.13, reported asunsupported).GET /v1/isolated/capabilitiesgains ahardeningobject with per-layer state; every layer degrades fail-open. (#1474) -
eBPF observation variant.
execd-ebpf(CGO + cilium/ebpf) hooks exec/connect/privilege with CO-RE, scoped to the sandbox cgroup, emitting a rotating JSONL audit; needs kernel ≥5.10 with BTF. The default image is unchanged. (#1474 #1562) -
preStartandperiodiclifecycle hooks (OSEP-0020).CreateSandboxRequest.lifecycleis transported to the sandbox via the internalOPEN_SANDBOX_LIFECYCLEenv (Kubernetes; the Docker provider rejects lifecycle requests). execd starts HTTP beforepreStart, runs non-overlappingperiodichooks through the reaper-aware managed-process path, and only releases the user process afterpreStartsucceeds. Config persists at$HOME/.execd/lifecycle.toml, withEXECD_LIFECYCLE_CONFIGas exact override; bootstrap sync and preStart shutdown are fail-closed with TERM/KILL watchdogs, and internal transport vars are stripped from user code. API models aligned across Go/Python/JS/Kotlin/C# SDKs. (#1587 #1588 #1589)
🐛 Bug Fixes
-
In-namespace signals no longer leak into
/commandin init mode.kill -HUP 1previously killed whatever/commandwas running (signal: hangup); signals are now owned by the init forwarder only. (#1546) -
PTY creation failure emitted a second success body.
POST /ptynow stops immediately after the error response instead of appending a success body and a nonexistent session id. (#1603) -
cwdvalidation now honors request-scoped envs. Foreground/background/commandrequests can use acwdvariable from their ownenvsmap instead of being rejected with 400. (#1611) -
Non-root workloads could not start:
/bootstrap.shnormalized to0755. A deployed image carriedbootstrap.shas0751 root:root, sending images with a non-root default user (e.g.admin) into CrashLoopBackOff before hooks ran. The image build now forces exec bits onexecd/execd-ebpf/opensandbox-supervisor/bootstrap.shregardless of checkout permissions. (#1619 #1606) -
preStarttimeout raised from 300s to 10800s (3h).periodic.timeoutSecondsstays at 300s; product policy bounds are now enforced Server-side only, with execd keeping just a technical overflow guard. (#1637)
📦 Misc
-
OSEP-0018 coverage closed out: server-path and Kubernetes hardening e2e (ConfigMap-carried isolation TOML, no server logic change), PTY-under-hardening, bwrap + init-reaper integration, custom
[seccomp]/keep_capabilitiespolicy,EXECD_INIT↔TOML drift pin, K8s Restart recycle against init-mode pods, runtime-initiated stop, and anexecd-ebpfbare-container smoke (doubles as kernel-5.10 validation). Test redundancy and dead code trimmed (-359 lines). (#1554 #1555 #1562 #1574) -
OTLP endpoint auto-allow shipped in the shared components (egress honors
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT/OTEL_EXPORTER_OTLP_ENDPOINT; PTY viewer read loop refactored along the way). (#1504) -
Lifecycle hook guide with timing diagram and per-SDK creation examples; SDK client-side timeout validation aligned with the Server. (#1605)
👥 Contributors
Thanks to these contributors ❤️
- Docker Hub: opensandbox/execd:v1.1.0
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/execd:v1.1.0
- GHCR: ghcr.io/opensandbox-group/opensandbox/execd:v1.1.0
server 0.2.3
What's New
⚠️ Breaking Changes
OPENSANDBOX_EGRESS_SANDBOX_IDis now server-injected and rejected in request env — the server unconditionally sets the egress sidecar's sandbox-id attribution variable in both Docker and Kubernetes runtimes, and removed it from the env allowlist. Requests that pass it inenvnow get a 400 instead of a user-spoofable value. #1381
✨ Features
-
Sandbox lifecycle hooks (
preStart/periodic), OSEP-0020 —CreateSandboxRequest.lifecycleis accepted, validated (reserved transports, duplicate periodic names, pool-allocation combinations), and transported to Kubernetes workloads via the reservedOPEN_SANDBOX_LIFECYCLEenv for execd to execute. Docker explicitly rejects lifecycle requests; PATCH and other hook types are out of scope. Ships with aligned Java/Kotlin, Python, and TypeScript SDK models. #1588 #1589 -
preStart.timeoutSecondslimit raised from 300s to 3 hours — server and OpenAPI limits now allow up to 10800s for pre-start hooks; the periodic limit stays at 300s. The execd-side product policy bound is removed, keeping policy changes server-only. #1637 -
QEMU VMState pause/resume for runc-based sandboxes — opt-in capture of guest memory/vCPU/device state via QMP migration; rootfs and compressed VMState are stored as separate OCI images keyed by immutable SHA-256 digests. Supports pooled BatchSandbox pause, detachment, and standalone restoration while preserving guest memory, writable disk, boot ID, and counter continuity. Rootfs-only snapshots remain backward compatible. #1584
-
Pool capacity exhaustion is now machine-identifiable — the controller publishes a
PoolAllocationPending/PoolCapacityExhaustedcondition; the server waits a bounded acquisition window (newkubernetes.pool_acquisition_timeout_seconds, default 30s) and then returns HTTP 429 withKUBERNETES::POOL_CAPACITY_EXHAUSTEDandRetry-After: 5instead of the generic 504 readiness timeout. Older controllers safely fall back to the existing behavior. #1581 -
Pool allocation summary in lifecycle responses —
getSandboxnow surfaces an optionalSandbox.allocationblock (mode: pool,poolRef,state: allocated) for confirmed Kubernetes pool allocations, emitted only for concrete non-wildcard pool references with matching allocation evidence. No pod/namespace/IP data is exposed. #1481 -
Generic HTTP request metrics via OTLP — a thin ASGI middleware exports
server.http.request.duration(ms) withhttp_method, matchedhttp_route(orunknownfor unrouted/auth-failed requests), andhttp_status_codeattributes, complementing the existing create-duration histogram. Raw paths, IDs, keys, and bodies are never attributes; no-op when[otel]is disabled. #1561 -
Stable diagnostics API with scopes — diagnostics now honor the public inline descriptor contract:
logs: container|allandevents: runtime|lifecycle|all, with 400DIAGNOSTICS_SCOPE_UNSUPPORTEDfor unknown scopes and explicit warnings for incomplete best-effort scopes. Deprecated no-scopeplain-text responses still work with aDeprecationheader. #1553 -
fast-sandbox fleets backend groundwork (OSEP-0007) — a FastPath v2 gRPC client wraps all FastPathService RPCs with typed error mapping (
FastPathNotFound→ 404, etc.), and a create/status mapping layer converts the public contract to fast-sandbox semantics, rejecting unsupported fields (volumes,platform,resourceRequests,credentialProxy,snapshotId, etc.) with clear errors. Not yet wired as an active runtime. #1558 #1559 -
Secure-access signing keys can come from a Kubernetes Secret — the server accepts
OPENSANDBOX_SECURE_ACCESS_KEYS/OPENSANDBOX_SECURE_ACCESS_ACTIVE_KEYenv overrides (mirroring the API-key override, gateway mode required), and the Helm chart addsserver.gateway.secureAccess.existingSecret(mutually exclusive with plaintextkeys, enforced at render time) so GitOps flows never commit key material. #1445 -
Docker runtime
sandbox_env/sandbox_binds— new[docker]fields apply fleet-wide environment variables and host bind mounts to every sandbox container (requestenvwins oversandbox_env; config binds precede request-derived binds), giving the Docker runtime the operator-side hook the K8s pod template has. #1303 -
Egress sidecar readiness timeout configurable — new
[egress] timeout_seconds(default 30s, non-positive values rejected) lets Docker deployments tune how long the server waits for the sidecar health endpoint. #1522 -
Tenant namespaces validated at startup (OSEP-0014) — the server fail-fast verifies every tenant namespace exists and is accessible before serving traffic, aggregating all missing/inaccessible namespaces into a single fatal error instead of failing only at request time. OSEP-0004/0005/0014 marked implemented. #1389
🔒 Security
- CVE-remediating dependency bumps —
google.golang.org/grpc→ v1.82.1 across kubernetes/ingress/execd/egress/internal (xDS RBAC and HTTP/2 fixes),postcss→ 8.5.11 (arbitrary file read via sourceMappingURL). #1385
🐛 Bug Fixes
-
Docker endpoint lookups ~108x faster — container lookup now goes through the deterministic
sandbox-<id>name withopensandbox.io/idlabel validation, falling back to the label scan only when needed. Local benchmark: 6.07s → 56ms for execd+egress endpoint resolution. #1400 -
Informer cache expires when the watch stalls —
has_syncednow means "cache is currently live", not "an initial list finished once"; a stalled or half-open watch (e.g. after a control-plane upgrade) degrades to direct API reads instead of serving frozen status forever, which previously caused every create to 504 withPOD_READY_TIMEOUT. Bound derived from existing knobs (resync_period + watch_timeout); no new config. #1534 -
Proxy connection leak on aborted responses — when a downstream client disconnected before response-body streaming started, the backend httpx response was never released, gradually exhausting the shared pool until
httpcore.PoolTimeout. The proxy now owns the backend response for the full ASGI lifecycle, shieldsaclose()from cancellation, and releases on setup failure. Stress test: 1000/1000 disconnects cleaned up. #1488 -
Unique, stable proxy operation IDs — the 20 proxied HTTP operations previously emitted 4 process-dependent duplicate operation IDs (16 OpenAPI warnings); each method now gets its own deterministic ID with identical runtime dispatch, auth, and 405 behavior. Generated clients should be regenerated. #1634
-
PVC auto-create no longer crashes with a malformed 500 — the PVC owner-reference patch went through an unsupported
_content_typepath that raisedApiTypeError(leaving PVCs without owner refs) andSandboxErrorCodes.INTERNAL_ERRORwas undefined, masking the real error. Both fixed: patch uses the supported strategic-mergecall_apipath; error code defined. #1538 #1447 -
Template container
securityContextnow reaches pods — the deep-merge previously replaced the template's containers wholesale, silently dropping container-level securityContext. Template keys now supplement (never override) runtime values, matching the existing volumeMounts philosophy. #1564 -
Root-relative backend redirects stay under the proxy route —
Location: /loginfrom a sandbox backend is rebased to/v1/sandboxes/{id}/proxy/{port}/login; absolute, network-path, and query-only references are left untouched. #1516 -
Server-proxy endpoints drop the ingress routing header —
OpenSandbox-Ingress-To(case-insensitive) is removed fromuse_server_proxy=trueresponses while secure-access and other endpoint headers are preserved. #1503 -
No duplicate
Date/Serverheaders on proxied responses — the proxy preserves an originDatewhile suppressing Uvicorn's default (and the backendServerheader), emitting exactly oneDate. #1471 -
Invalid-resume error no longer leaks controller internals — the 409 message for a Running (internally Succeed) sandbox now says "state Running" instead of "phase Succeed"; HTTP status and error code unchanged. #1586
-
Non-finite CPU limits rejected —
nan, infinite, and overflowed values are rejected before Docker NanoCpus conversion instead of being silently accepted. #1568 -
Mixed read-only PVC subPath mounts allowed — the PVC source is set read-only only when every mount of the claim is read-only; the service-level rejection that blocked legitimate mixed-policy PVC creation is removed. #1440
-
Agent-sandbox server-proxy endpoints resolve — internal endpoint resolution now delegates to the workload provider and reads agent-sandbox pod addresses from
status.podIPsinstead of the BatchSandbox-only annotation, and brackets IPv6 literals before appending the port. #1424 -
Version reporting fixed everywhere —
/openapi.json, installed package metadata, and the chartappVersionall reported stale0.1.0; the server now reads its version from package metadata at runtime, builds injectSETUPTOOLS_SCM_PRETEND_VERSION, and the release script bumpsChart.yamlappVersion. #1439
📦 Misc
-
execd v1.0.22 ships OSEP-0018 init-mode hardening — execd as sandbox init (PID 1) with pre-exec hardening floor (env strip, bounding set, no_new_privs, identity drop, ambient caps, seccomp) and Kubernetes hardening e2e + PTY/bwrap coverage. #1474 #1555
-
Isolated-session fail-closed workload lifecycle gate — the server distributes a native Bubblewrap lifecycle gate to
/opt/opensandboxfor both runtimes; workloads stay blocked until execd marks them ready. First phase only — capability admission and session networking remain disabled. #1388 -
Egress transparent MITM can intercept extra TCP ports — experimental
OPENSANDBOX_EGRESS_MITMPROXY_EXTRA_PORTSappends ports to the always-on 80/...
Python Sandbox SDK v0.1.16
What's New
✨ Features
- Resilient HTTP transport — Async and sync clients now share a configurable retry transport with backoff, jitter,
Retry-Afterhandling, per-attempt timeouts, an overall deadline, and retry callbacks. Retries are enabled by default for idempotent requests on HTTP 429, 502, and 503; callers that require the previous fast-fail behavior can useRetryPolicy.disabled(). User-supplied transports remain untouched, and streaming SSE requests bypass replay. #1372 - Sandbox lifecycle hooks —
Sandbox.createandSandboxSync.createnow accept optionalpreStartand periodic lifecycle hooks, with stable Python models and server-aligned validation. Hook timeout ranges remain server-authoritative. #1588 #1605 - Background runs in isolated sessions — Isolated sessions can start detached work and poll its status and incremental combined output. Active background runs suspend idle collection; read-only workspaces reject background execution because they cannot persist run logs. #1456
- Runtime allocation and hardening visibility — Sandbox responses can expose confirmed Pool allocation summaries, while isolated capabilities include execd init mode and hardening-layer state. These fields are additive and remain absent when the server cannot confirm the corresponding state. #1481 #1474
- Pool capacity back-pressure — Compatible lifecycle servers can now surface exhausted Kubernetes Pool capacity as HTTP 429 with
KUBERNETES::POOL_CAPACITY_EXHAUSTEDandRetry-After, instead of eventually reporting a generic readiness timeout. Sandbox creation remains non-idempotent and is not automatically replayed on this status. #1581
⚡ Performance
- Faster sandbox startup — Async sandbox creation, connection, and resume resolve execd and egress endpoints concurrently, reducing the two management API lookups to one parallel wait. Failure propagation and create cleanup semantics remain unchanged. #1531
- Bounded parallel Pool cleanup — Idle sandbox release uses bounded concurrency while preserving best-effort cleanup and legacy public behavior. The measured 100-sandbox cleanup in the PR dropped from about 8.3 seconds to 0.8 seconds. #1475
🐛 Bug Fixes
- Standards-compliant SSE parsing — Command and isolated execution streams now use SSE-specific framing, preserving Unicode separators inside JSON strings while remaining compatible with legacy bare-JSON frames. #1444
- Reliable background command completion — Background command streams stop after
execution_complete, preventing falseRemoteProtocolErrorfailures when the peer closes before the final HTTP terminator. Foreground streaming behavior is unchanged. #1532 - Endpoint cache race fix — Completing an older endpoint fetch no longer removes a newer replacement inflight fetch in async or sync clients. #1567
- More useful API errors — Unstructured 4xx and 5xx response bodies are included in exception messages and remain available as raw
response_bodybytes. HTTP 429 responses useSandboxRateLimitExceptionand expose parsedretry_afterwhen present. #1496 #1372 - Cleaner readiness diagnostics — Readiness timeout messages retain timing, connection context, and the last health-check error without suggesting deployment-specific network fixes that may not apply. #1493
📦 Misc
- Regenerated lifecycle models, bumped the package fallback version to
0.1.16, and aligned async and sync default User-Agent strings with the release. #1622 #1623
👥 Contributors
Java/Kotlin SDKs v1.0.19
What's New
⚠️ Breaking Changes
- Client-pool reconcile interval is no longer configurable —
SandboxPool.Builder.reconcileInterval(...)and the correspondingPoolConfigsetting have been removed. Pool reconciliation now runs on a fixed one-second cadence, while new sandbox creation is bounded bywarmupCreateQps; callers should delete uses ofreconcileIntervalwith no replacement. #1575
✨ Features
- Scalable asynchronous client-pool warmup — Pool refill now separates create, readiness, prepare, post-prepare health checking, renewal, and commit into a staged asynchronous pipeline. Create admission is rate-limited per pool, delayed retries no longer occupy worker threads, and staged warmups use a single create transport attempt while direct create and other non-pool paths retain their existing retry behavior. #1575
- Pool warmup tracing and operational summaries — Applications can opt in with
ConnectionConfig.enableTracing(true)to emit OpenTelemetry traces covering queue wait and every warmup phase, using the application's global SDK, exporter, sampler, and propagators. Warmups now also expose bounded terminal outcome attributes, rate-limited failure logs, and active-pool summaries without allowing telemetry failures to affect pool behavior. #1524 #1604 - Lifecycle hook models — Sandbox creation now accepts additive
preStartandperiodiclifecycle hook models. Hook timeout validation remains authoritative on the server rather than being duplicated in stable Kotlin models; Docker support, PATCH operations, and additional hook types remain outside this release's scope. #1588 #1605 - Detached runs in isolated sessions — Kotlin consumers can start background work inside a persistent isolated session, poll its status, and read logs incrementally. Background runs are bounded by the session lifetime, do not support per-run timeouts, and are rejected for read-only workspaces because they require a host-visible writable log location. #1456
- Richer runtime metadata — The SDK now models execd hardening capability results and exposes an optional
Sandbox.allocationsummary for confirmed Kubernetes Pool allocations. Allocation metadata is omitted when evidence is incomplete or inconsistent and does not expose Pod, node, namespace, or internal controller details. #1474 #1481 - Shared HTTP connections for client pools — When callers do not provide an OkHttp connection pool,
SandboxPoolnow creates and owns one sized fromwarmupConcurrency, reuses it across warmup, acquire, and direct-create paths, and evicts it on shutdown. User-supplied connection pools retain their existing ownership semantics. #1517
🐛 Bug Fixes
- Stable warmup pacing under failures and throttling — Fast create failures can no longer drive a self-sustaining reconcile loop, and pool warmups now honor
Retry-Afterwithout counting rate limits as degraded failures. Stale-sandbox cleanup triggers prompt replenishment, while concurrent warmup commits no longer serialize behind a single JVM mutex or starve health-check work. #1494 #1512 #1519 #1621 - More actionable client errors — Kotlin
ClientExceptionmessages now retain response bodies for caller-fixable 4xx responses across generated and handwritten paths. Readiness timeout messages no longer suggest Docker bridge, remote-network, or server-proxy settings that may not apply, while preserving timeout and last-error context. #1496 #1493 - Faster bounded idle cleanup —
releaseAllIdle()now deletes idle sandboxes with bounded concurrency instead of serially, preserving best-effort cleanup semantics while substantially reducing drain time for large pools. #1475
📦 Misc
- Published Kotlin/JVM sandbox, code-interpreter, Redis pool, and BOM artifacts at version
1.0.19under the existingcom.alibaba.opensandboxcoordinates.