What's New
⚠️ Breaking Changes
-
Credential Vault requires
dns+nftenforcement — DNS-only egress can no longer carry credential-bound traffic. Migrate to[egress] mode = "dns+nft";defaultAction: allowstill works but is deprecated and warns. #1136 -
X-Forwarded-Protono longer trusted by default — the server proxy strips client-supplied forwarded headers and rebuilds them from the connection. Deployments that terminate TLS in front of the egress sidecar must list their proxy addresses in the newOPENSANDBOX_EGRESS_CREDENTIAL_VAULT_TRUSTED_PROXY_CIDRS. #1138 -
OPENSANDBOX_EGRESS_MITMPROXY_SCRIPTremoved from the env allowlist — the variable let a sandbox point the egress sidecar'smitmdump -sat any file under the shared/opt/opensandboxemptyDir, enabling code execution in the sidecar and Credential Vault plaintext access. Requests carrying it are now rejected. Ship custom addons in a purpose-built egress image viaENV. #1194
🔒 Security
-
Secure-access token now enforced on the server proxy — the lifecycle proxy at
/sandboxes/{id}/proxy/{port}/...previously skipped API-key checks and never validated theOpenSandbox-Secure-Accessheader (CVSSv3.1 ~7.5). Both HTTP and WebSocket paths now require a matching header (constant-time compare) and strip the token before forwarding. #1191 -
Sandbox pods no longer mount a service-account token — the
kubernetes.service_accountconfig is removed andautomountServiceAccountToken: falseis set on both providers. Nothing in the sandbox consumed the token. Operators depending on the SA'simagePullSecretsshould moveserviceAccountNameinto their agent-sandbox template. #1227 -
21 CVE-remediating dependency bumps —
pydantic-settings,python-multipart,starlette,cryptography,pyjwt,undici,js-yaml. Direct deps pinned inpyproject.toml, transitives via[tool.uv] constraint-dependenciesandpnpm.overrides. #1145
✨ Features
-
Multi-tenancy provider with file and HTTP backends — sandboxes and snapshots can be isolated per tenant via K8s namespaces. Ships a file provider (TOML with mtime hot-reload) and an HTTP provider (per-key TTL cache + singleflight). Tenant context is propagated per request through auth middleware; unavailable providers return 503. Closes #972 #497. #1184
-
sandbox.createlatency reported via metrics events — newPOST /v1/metrics/eventsaccepts fire-and-forgetMetricsEventpayloads and optionally records them as an OTEL histogram (opensandbox.sandbox.create.duration). Python/Go/JS SDKs already hook this on every create. Configure via[otel]. #1307 -
Sandbox extensions returned in lifecycle responses —
CreateSandboxResponse,getSandbox, andlistSandboxesnow surfaceopensandbox.extensions.*back to callers, matching the values set at create time. K8s reads from pod annotations; Docker persists them as container labels. Closes #1060. #1112 #1167 -
Snapshot list supports exact-name filtering —
GET /snapshotsaccepts an optionalnamequery parameter, honored through the API, service, and SQLite repository while preserving pagination. Exposed in the Python, JavaScript, Go, C#, and Kotlin SDKs. Closes #1205. #1301 -
Configurable Docker port range for bridge-mode sandboxes — new
[docker] port_range_min/port_range_maxfields (default40000-60000) narrow host-port allocation so multiple bridge-mode sandboxes can expose distinct execd ports (host mode always binds 44772). #1182 -
OPENSANDBOX_EGRESS_POLICY_FILEandOPENSANDBOX_EGRESS_SANDBOX_IDnow allowlisted — sandbox creates can opt into egress policy-file persistence and per-sandbox OTLP attribution (OSEP-0010) via the standardenvfield. #1190 #1335 -
JDK trust store hardened for MITM CA —
bootstrap.shnow discovers every JDK on the system and imports the egress MITM CA into eachcacertsviakeytool. Fixes PKIX failures on tarball / Alpine / distroless JDKs when using the credential proxy. Idempotent and best-effort. Closes #1201. #1229
🐛 Bug Fixes
-
Docker
listSandboxesno longer flakes on concurrent deletion — docker-py's high-levelcontainers.list()does a follow-up inspect per match, so a sibling cleanup mid-listing failed the whole call with 500. The service now uses the low-leveldocker_client.api.containers(...)summary endpoint and mapsNotFoundfrom single-container lookups to 404. List view dropsentrypoint, exit code, andFinishedAtfidelity (callers needing those must usegetSandbox). #1342 -
K8s informer honors
informer_resync_seconds— periodic full resync was silently disabled while watch mode was active. Watch streams are now capped by the remaining resync interval. Fixes #1322. #1323 -
networkPolicyrejected in pool mode — combiningextensions.poolRefwith anetworkPolicyis unsupported; requests are now rejected at schema validation with a defense-in-depth guard inBatchSandboxProvider. Closes #1315. #1319 -
Diagnostics scoped to the tenant namespace — K8s pod/log/event lookups use the request-scoped tenant namespace instead of the server default. Non-tenant requests unchanged. Closes #1304. #1305
-
use_server_proxypreserves the API mount prefix — the API is mounted both bare and under/v1, but proxied endpoints were derived fromrequest.base_urland dropped the prefix, breaking ingress deployments that path-split/v1/*. The rewritten endpoint now keeps the prefix;server.eipis still respected verbatim. #1297 -
Renewed Docker sandbox expirations survive restart — timers were rebuilt from immutable container labels, so a renewed sandbox reverted to its original TTL. Renewals now write an override into the file-backed metadata store; old callbacks that observe a renewal rearm cleanly. Closes #1200. #1267
-
PVC
readOnlypropagated to the volume source — the flag was only applied to the mount, not topersistentVolumeClaim.readOnlyon the pod spec. Both are now aligned. Closes #545. #1246 -
Fail fast for missing
poolRefpools —extensions.poolRefis validated against the Pool CRD before creating aBatchSandbox, returningKUBERNETES::POOL_NOT_FOUNDinstead of failing downstream. Fixes #1175. #1176 -
Docker egress no longer crashes on hosts without IPv6 procfs entries —
egress.disable_ipv6=trueunconditionally injectednet.ipv6.conf.*.disable_ipv6sysctls. The injection is now skipped when the required procfs knobs are missing. Fixes #1168. #1172 -
Event loop no longer blocked during Docker image pull — provisioning runs on a daemon thread with an
asyncio.Futureso health checks stay responsive. Newtimeout_graceful_shutdownconfig (default 5s) makes SIGINT actually exit. #1171 -
K8s diagnostics logs endpoint works on multi-container pods —
read_namespaced_pod_logwas called without acontainerargument, so every standard sandbox (withegresssidecar +execd-installerinit) returned 500. The endpoint now defaults to thesandboxcontainer, accepts?container=for sidecar/init logs, and maps K8s errors to structured 400/403/404/502. Fixes #1150. #1151
📦 Misc
-
renew_intentmarked stable — OSEP-0009 is out of experimental; drops[EXPERIMENTAL]/ 🧪 markers from config docstrings, example TOML, startup logs, andconfiguration.md. No behavior change. #1350 -
Removed unused
PendingSandboxmachinery and fixed the spec — Docker and K8s both provision synchronously; the async worker, related helpers, and their tests were dead code (−357 lines). ThePOST /sandboxes202 response description inspecs/sandbox-lifecycle.ymlalso described aPending → Runningpolling flow that was never implemented — now corrected. #1178 -
GHCR image publishing — component images (server, k8s, execd, egress, ingress) are now published to GitHub Container Registry alongside Docker Hub and Aliyun. Refs #1160. #1161
-
Package metadata and docs point at
opensandbox-group/OpenSandbox— user-facing links across README, docs, Helm chart, examples, andserver/clipyproject.tomlupdated. No package rename. #1197 #1140 -
Auto-bumps —
execdv1.0.20 → v1.0.21 (#1144 #1279);egressv1.1.3 → v1.1.4 (#1142 #1278).
👥 Contributors
Thanks to these contributors ❤️
- @1363653611
- @bcho
- @FAUST-BENCHOU
- @ferponse
- @GreenShadeZhang
- @Gujiassh
- @hellomypastor
- @jianpingpei
- @jwx0925
- @Pangjiping
- @ruirui6946
- @shenshouer
- @tea-artist
- PyPI: opensandbox-server==0.2.2
- Docker Hub: opensandbox/server:v0.2.2
- Aliyun Registry: sandbox-registry.cn-zhangjiakou.cr.aliyuncs.com/opensandbox/server:v0.2.2
- GHCR: ghcr.io/opensandbox-group/server:v0.2.2