Skip to content

Make sandbox work stick to one container - #833

Merged
ghostwriternr merged 29 commits into
naresh/stabilize-next-releasefrom
naresh/runtime-incarnation-admission
Jul 22, 2026
Merged

Make sandbox work stick to one container#833
ghostwriternr merged 29 commits into
naresh/stabilize-next-releasefrom
naresh/runtime-incarnation-admission

Conversation

@ghostwriternr

@ghostwriternr ghostwriternr commented Jul 21, 2026

Copy link
Copy Markdown
Member

Make sandbox work stick to one container at a time

A Sandbox Durable Object can outlive the container it is talking to. After sleep, eviction, or a crash, Cloudflare may start a replacement container under the same Durable Object.

Without this change, an old process handle, stream, WebSocket, backup, mount, tunnel, or extension sidecar could keep talking to the new container as if nothing happened. That can drop output, attach to the wrong process, or run the same side effect twice.

This PR ties each piece of work to the exact container that started it.

What callers should expect

  • Starting work may wake a stopped container when that work needs one.
  • Looking up or cleaning up processes and terminals does not wake a stopped container.
  • If the container is replaced while work is in flight, that work fails with a clear interruption error.
  • The SDK does not automatically retry that work. Retry only operations that are safe to run again.
  • Old process and terminal handles do not transfer to the replacement container. Create or look them up again after the new container is running.

Also included

  • Sandbox environment variables and the /workspace default working directory are applied on process launch again.
  • Warm-pool discard waits for full container teardown before freeing capacity.
  • @cloudflare/containers is upgraded so stop and replacement startup no longer race each other.
  • Extension authors should do runtime work inside withRuntime() and withSidecar() instead of keeping long-lived runtime clients.

This PR is stacked on #827. Retarget it to next after #827 merges.

Out of scope

Typed error details for some Worker RPC process-stream failures are deferred to a follow-up. The no-retry and container-binding behavior already land here.

Bind SDK operations to activated runtime incarnations so starts, streams,
handles, forwarding, and teardown cannot cross runtime replacement.
Preview and tunnel workflows need one exact runtime authority across
admission, side effects, transport lifetime, and durable publication.
Keep discovery and stale-owner cleanup non-waking while preventing old
sessions and interrupted callbacks from publishing runtime-owned state.
Keep archive transfer and cleanup under the same admitted runtime so a
replacement cannot replay side effects or switch controls mid-attempt.
Preserve independent sandbox-lifetime interruptions and durable restore
ownership for explicit caller reconciliation.
Keep mount setup and long-lived local sync pinned to one runtime while
making unmount and teardown cleanup non-waking. Exact incarnation and
sandbox-lifetime fences prevent stale registry publication.
Keep extension control, sidecar connection, and callback streaming inside
one admitted runtime so stale remotes cannot reconnect after replacement.
Delete transitional identity and transport retry paths so runtime work is
admitted once, scoped to an exact incarnation, and never replayed.
Exercise deterministic real-container replacement and non-waking lookup
paths while ensuring failed WebSocket closure cannot retain runtime
authority.
Exercise real control-process replacement without assuming the
container entrypoint is namespace PID 1.
@changeset-bot

changeset-bot Bot commented Jul 21, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 3e1fbef

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@cloudflare/sandbox Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jul 21, 2026

Copy link
Copy Markdown

Open in StackBlitz

npm i https://pkg.pr.new/@cloudflare/sandbox@833

commit: 7c28964

@github-actions

github-actions Bot commented Jul 21, 2026

Copy link
Copy Markdown
Contributor

🐳 Docker Images Published

Variant Image
Default cloudflare/sandbox:0.0.0-pr-833-3e1fbef9
Python cloudflare/sandbox:0.0.0-pr-833-3e1fbef9-python
OpenCode cloudflare/sandbox:0.0.0-pr-833-3e1fbef9-opencode
Musl cloudflare/sandbox:0.0.0-pr-833-3e1fbef9-musl

Usage:

FROM cloudflare/sandbox:0.0.0-pr-833-3e1fbef9

Version: 0.0.0-pr-833-3e1fbef9


📦 Standalone Binary

For arbitrary Dockerfiles:

COPY --from=cloudflare/sandbox:0.0.0-pr-833-3e1fbef9 /container-server/sandbox /sandbox
ENTRYPOINT ["/sandbox"]

Download via GitHub CLI:

gh run download 29941971984 -n sandbox-binary

Extract from Docker:

docker run --rm cloudflare/sandbox:0.0.0-pr-833-3e1fbef9 cat /container-server/sandbox > sandbox && chmod +x sandbox

@ghostwriternr
ghostwriternr force-pushed the naresh/stabilize-next-release branch from 4ea6fba to 6da925d Compare July 21, 2026 15:08
@ghostwriternr
ghostwriternr force-pushed the naresh/runtime-incarnation-admission branch from f567cce to aca7f4c Compare July 21, 2026 15:08
@ghostwriternr
ghostwriternr force-pushed the naresh/stabilize-next-release branch from 6da925d to 047dc46 Compare July 21, 2026 15:14
@ghostwriternr
ghostwriternr force-pushed the naresh/runtime-incarnation-admission branch from aca7f4c to 36c95b2 Compare July 21, 2026 15:14
Restore sandbox environment and workspace defaults that were masked by
cold-start admission failures. Observe completed stop boundaries in
incarnation tests and fully destroy discarded warm containers before
releasing capacity.
Use the upstream fix for stop events racing replacement startup instead
of maintaining SDK-specific lifecycle fencing. Preserve JSON null
environment values as explicit unsets.
Preserve watches, process logs, and terminal output across Workers RPC
boundaries while retaining the inherited Container request APIs. Keep
runtime replacement and control-plane routing explicit so callers do not
attach resources to stale container incarnations.
Explicit restores must reapply committed backups when their destination
has changed. Inactivity expiry must also complete without re-entering
the sandbox stop gate, while E2E workflows synchronize on observable
process and terminal behavior.
Characterize Durable Object memory loss separately from persisted Sandbox
configuration across eviction and local Worker reloads. Align the local
Cloudflare test toolchain so the real container-backed harness remains
reproducible.
Allow the first waking establishment after a physical stop to clean up the
previous runtime without treating its delayed onStop bookkeeping as
invalidation of the sequential replacement.
Route container start and stop hooks through replacement-start marking and
observed-stop reconciliation while keeping explicit stop and destroy as hard
invalidations.
Control-server exit can surface stopped_with_code when the
monitor reports a numeric exit code. Match the wait helper.
@ghostwriternr ghostwriternr changed the title Establish runtime incarnation admission Make sandbox work stick to one container Jul 22, 2026
@ghostwriternr
ghostwriternr marked this pull request as ready for review July 22, 2026 17:23
@ghostwriternr
ghostwriternr merged commit e5c529b into naresh/stabilize-next-release Jul 22, 2026
14 of 15 checks passed
@ghostwriternr
ghostwriternr deleted the naresh/runtime-incarnation-admission branch July 22, 2026 17:23
ghostwriternr added a commit that referenced this pull request Jul 22, 2026
* Serialize branch release workflows

Prevent an older build from replacing a newer Worker and container
release when several commits reach a release branch close together.

* Make terminal output assertion deterministic

Shell prompts can arrive before command output, so collect terminal data
until the expected command result is observed.

* Declare sandbox execution workspace version

Preview publishing resolves workspace dependency versions and requires
every internal package to declare one.

* Clean up failed E2E sandboxes

Destroy partially initialized sandboxes so failed environment setup does
not leak resources into disposable pull request deployments.

* Pull process events across RPC

Worker RPC stream capabilities can close without delivering retained
process events. Pull each event explicitly and keep runtime control alive
until the local subscription settles.

* Make sandbox work stick to one container (#833)

* Establish runtime operation admission

Bind SDK operations to activated runtime incarnations so starts, streams,
handles, forwarding, and teardown cannot cross runtime replacement.

* Fence preview and tunnel operations

Preview and tunnel workflows need one exact runtime authority across
admission, side effects, transport lifetime, and durable publication.
Keep discovery and stale-owner cleanup non-waking while preventing old
sessions and interrupted callbacks from publishing runtime-owned state.

* Fence backup operations to one runtime

Keep archive transfer and cleanup under the same admitted runtime so a
replacement cannot replay side effects or switch controls mid-attempt.
Preserve independent sandbox-lifetime interruptions and durable restore
ownership for explicit caller reconciliation.

* Thread runtime leases through mounts

Keep mount setup and long-lived local sync pinned to one runtime while
making unmount and teardown cleanup non-waking. Exact incarnation and
sandbox-lifetime fences prevent stale registry publication.

* Scope extension workflows to runtimes

Keep extension control, sidecar connection, and callback streaming inside
one admitted runtime so stale remotes cannot reconnect after replacement.

* Remove implicit runtime ownership

Delete transitional identity and transport retry paths so runtime work is
admitted once, scoped to an exact incarnation, and never replayed.

* Add runtime lifecycle regressions

Exercise deterministic real-container replacement and non-waking lookup
paths while ensuring failed WebSocket closure cannot retain runtime
authority.

* Test control server exit recovery

Exercise real control-process replacement without assuming the
container entrypoint is namespace PID 1.

* Fix runtime execution regressions

Restore sandbox environment and workspace defaults that were masked by
cold-start admission failures. Observe completed stop boundaries in
incarnation tests and fully destroy discarded warm containers before
releasing capacity.

* Upgrade container lifecycle handling

Use the upstream fix for stop events racing replacement startup instead
of maintaining SDK-specific lifecycle fencing. Preserve JSON null
environment values as explicit unsets.

* Stabilize sandbox proxy transports

Preserve watches, process logs, and terminal output across Workers RPC
boundaries while retaining the inherited Container request APIs. Keep
runtime replacement and control-plane routing explicit so callers do not
attach resources to stale container incarnations.

* Stabilize lifecycle regression workflows

Explicit restores must reapply committed backups when their destination
has changed. Inactivity expiry must also complete without re-entering
the sandbox stop gate, while E2E workflows synchronize on observable
process and terminal behavior.

* Add Sandbox reconstruction harness

Characterize Durable Object memory loss separately from persisted Sandbox
configuration across eviction and local Worker reloads. Align the local
Cloudflare test toolchain so the real container-backed harness remains
reproducible.

* Reconcile delayed runtime stops

Allow the first waking establishment after a physical stop to clean up the
previous runtime without treating its delayed onStop bookkeeping as
invalidation of the sequential replacement.

* Wire pending stop reconciliation

Route container start and stop hooks through replacement-start marking and
observed-stop reconciliation while keeping explicit stop and destroy as hard
invalidations.

* Accept both terminal container stop statuses

Control-server exit can surface stopped_with_code when the
monitor reports a numeric exit code. Match the wait helper.

* Bump E2E deploy hash for CI rerun

* Bump E2E deploy hash again

* Add isolated browser test containers

* Parallelize isolated browser tests

* Integrate browser container readiness

* Fix browser test import order

* Bound browser test concurrency

* Bootstrap Wrangler in rollout checks

* Reduce browser cold-start pressure

* Limit browser cold starts to one

* Avoid orphaned browser configuration

* Restore parallel browser coverage

* Clarify release notes for callers

Rewrite the PR #833 changesets in plain language so release notes
describe user-visible behavior instead of internal runtime jargon.

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 1 additional finding.

Open in Devin Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant