Skip to content

feat(cli): add sandbox prune command to delete ERROR-phase sandboxes #2594

Description

@engelmi

Problem Statement

When sandboxes fail during provisioning or runtime, they remain in the ERROR phase indefinitely. Users currently have two options:

  • delete them individually by name with sandbox delete <name>, or
  • wipe everything with sandbox delete --all

There is no way to clean up only the broken sandboxes while leaving healthy ones running.

This is especially painful in environments where multiple sandboxes are running concurrently - a single failed experiment shouldn't require manually identifying and deleting each errored sandbox, and --all is too destructive.

Proposed Design

Add openshell sandbox prune - a CLI command backed by a new PruneSandboxes gRPC RPC that performs server-side filtering and deletion. The semantics are similar to podman container prune and docker container prune.

Proto:

  • New PruneSandboxes RPC on the OpenShell service with sandbox:write scope and user workspace role (matching DeleteSandbox).
  • PruneSandboxesRequest takes workspace and all_workspaces (admin-only, mirrors ListSandboxes).
  • PruneSandboxesResponse returns repeated string pruned_names and repeated string failed_names for best-effort semantics - one sandbox failing to delete doesn't abort the rest.

Alternatives Considered

  1. Client-side filtering - CLI lists all sandboxes, filters for ERROR, then issues individual DeleteSandbox RPCs. Simpler (no proto changes), but introduces a TOCTOU race between list and delete, produces N round trips, and pushes logic into every client (CLI, SDK, Python SDK).
  2. Add a phase filter to ListSandboxes. More general, but still requires client-side delete loop. The prune operation is common enough to warrant a dedicated RPC that does both steps atomically.
  3. Extend DeleteSandbox with a phase filter. Overloads the existing RPC's semantics. A separate RPC is cleaner and doesn't risk breaking existing callers.

Agent Investigation

  • Explored the full DeleteSandbox call chain: CLI (run::sandbox_delete) → gRPC
    → gateway handler (handle_delete_sandbox) → ComputeRuntime::delete_sandbox()
    → compute driver. The handler is a thin authz wrapper; all complexity lives in
    the compute layer which is safe to reuse.
  • Confirmed SandboxPhase enum has ERROR = 3 and the established pattern for
    checking phase is SandboxPhase::try_from(sandbox.phase()).ok() ==
    Some(SandboxPhase::Error).
  • Confirmed the gateway's existing reconciliation loop
    (reconcile_store_with_backend) already prunes orphan sandboxes on a timer, but
    does not prune by phase — this is a separate concern.
  • Confirmed ListSandboxesRequest has no phase filter field, so client-side
    filtering would require fetching all sandboxes.

Checklist

  • I've reviewed existing issues and the architecture docs
  • This is a design proposal, not a "please build this" request

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    state:triage-neededOpened without agent diagnostics and needs triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions