Skip to content

fix(server): consistent sandbox access after the main process exits - #4950

Open
pimlock wants to merge 9 commits into
mainfrom
4949-sandbox-access-after-exit/pimlock
Open

pimlock wants to merge 9 commits into
mainfrom
4949-sandbox-access-after-exit/pimlock

Conversation

@pimlock

@pimlock pimlock commented Oct 27, 2026 •

Copy link
Copy Markdown
Collaborator

Summary

When a retained sandbox's main process exits, the supervisor stays connected so you can still get into the sandbox. SSH, file transfer and port forwarding already worked in that state. Exec and exposed services did not, so openshell sandbox exec -- cat result.txt failed on a sandbox you could download the same file from. On a gateway with several replicas, the post-exit check also only looked at the replica that received the request, so access worked or failed depending on the load balancer.

This PR applies one rule to every way into a sandbox. A Ready sandbox is reachable. A Completed or Error sandbox is reachable while its supervisor is connected to any replica. Every other phase is refused. #4949 has the full set of rules and a lifecycle diagram.

The rule lives in a new gateway module, sandbox_access. Exec, interactive exec, SSH sessions, port forwarding and exposed services all call sandbox_access::acquire and open their relays through the handle it returns. New ways into a sandbox should do the same, and anything that has to happen before a sandbox can be used (for example, resuming a suspended sandbox) belongs there.

Only the gateway decides. The CLI no longer checks the phase before exec; when the gateway refuses, the CLI error still names the phase it saw. A service forward stops only when its sandbox is gone or being deleted, and the gateway checks each connection.

Related Issue

Closes #4949

Changes

  • New sandbox_access module in the gateway holds the access rule and opens relays. supervisor_session keeps only the replica-aware supervisor_connected check.
  • Exec, interactive exec, SSH sessions, port forwarding and exposed services go through it. Exposed services check access on every request, including ones served from a pooled upstream connection.
  • The CLI leaves the access decision to the gateway for exec and service forwards.
  • The docs and CLI skill now say what you can still do after a retained sandbox's main process exits, and that --no-keep sandboxes are deleted.
  • A new sandbox-retention conformance scenario checks both cases, for exit codes 0 and 3. A --no-keep sandbox is deleted. A retained sandbox records the exit code and still accepts exec.

Testing

  • mise run pre-commit passes
  • Unit tests added/updated
  • E2E tests added/updated (if applicable)

The conformance scenarios ran against a local Docker gateway built from this branch, before the sandbox_access refactor. The refactor is covered by unit tests and the CLI exec integration tests. I didn't test Kubernetes, Podman or a real multi-replica gateway live.

Still broken and tracked in #4949: after a Docker gateway restart, Completed and Error sandboxes lose their supervisor and can't be reached.

Checklist

  • Follows Conventional Commits
  • Commits are signed off (DCO)

After the main process exits, the supervisor keeps its access plane, and
SSH and port forwarding already reached Completed and Error sandboxes
while it stayed connected. Exec still required Ready in both the CLI
and the gateway, so it was refused on the same sandboxes.

Exec, SSH sessions, and TCP forwarding now share one gate: a sandbox is
reachable when it is Ready, or Completed/Error with a supervisor session
on this replica or a fresh owner record naming a peer replica. The
previous check only looked at the local session map, so on a
multi-replica gateway a request landing on a non-owner replica was
refused even though relay opening routes to the owner.

The CLI exec pre-check and the service forward recheck accept Ready,
Completed, and Error and leave the supervisor decision to the gateway.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
…upervisor

Exposed-service HTTP routing still required Ready while port forwarding
to the same sandbox kept working after the main process exited. Service
routing now uses the shared relay reachability gate, so a retained
Completed or Error sandbox serves its exposed services while its
supervisor stays connected.

Document what stays reachable after a retained sandbox's main process
exits, and that --no-keep sandboxes are deleted once it does.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
Add a sandbox-retention scenario that runs for exit codes 0 and 3:
an ephemeral (--no-keep) sandbox returns the exit code and is deleted,
and a retained sandbox records Completed or Error with the exit code
and still accepts exec.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
@github-actions

Copy link
Copy Markdown

Add a phase diagram to the Sandbox Lifecycle section and describe what
happens after the main process exits. A --no-keep sandbox is deleted. A
retained sandbox keeps its compute and still accepts exec, file
transfer, port forwarding and exposed services while its supervisor is
connected. sandbox connect fails because it attaches to the exited main
process, so the docs point to sandbox exec --tty for a shell.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
…gate

Exec, interactive exec, SSH sessions, port forwarding, and exposed services
now call sandbox_access::acquire and open their relays through the returned
SandboxAccess handle. The phase rules live in one module, so anything that
must happen before a sandbox can be used applies to every entry point.
supervisor_session keeps only the replica-aware supervisor_connected check.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
Exec no longer checks the sandbox phase before calling the gateway; when
the gateway refuses, the CLI names the phase it saw. Service forwarding
stops only when the sandbox is gone or being deleted, and leaves each
connection to the gateway's access check.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
@pimlock
pimlock requested a review from drew October 31, 2026 01:07
@pimlock pimlock added area:supervisor Proxy and routing-path work test:e2e Requires end-to-end coverage labels Oct 31, 2026
@github-actions

This comment was marked as outdated.

@pimlock
pimlock requested a review from TaylorMutch October 31, 2026 01:08
…fter-exit/pimlock

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>

# Conflicts:
#	crates/openshell-server/src/service_routing.rs
Exposed services do not authenticate callers, so they now reach an Error
sandbox only when its main process failed. Exec, SSH, and forwarding keep
the debugging path into an infrastructure Error.

The CLI service forward stops again once its sandbox is stopping, stopped,
or being deleted, and the TypeScript SDK forward applies the same check
instead of requiring Ready. Exec refusals name what makes the sandbox
usable again.

supervisor_connected skips the owner index on a single replica, the
unused relay error variant is gone, and relay opening outside
sandbox_access is crate-private.

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>

This branch has not been deployed

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

Labels

area:supervisor Proxy and routing-path work test:e2e Requires end-to-end coverage

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat: consistent sandbox access after the main process exits

1 participant