Skip to content

fix(gateway): allow adding network_policies for sandbox created without one #491

@pimlock

Description

@pimlock

Agent Diagnostic

  • Investigated the live policy update path and the draft chunk approval path.
  • Confirmed that openshell policy set fails with:
    cannot add network policies to a sandbox created without them (Block -> Proxy mode change requires restart)
  • Traced that error to validate_network_mode_unchanged(...) in crates/openshell-server/src/grpc.rs:2614, called from UpdateSandboxPolicy in crates/openshell-server/src/grpc.rs:1016.
  • Confirmed that the draft approval path does not use this validator. approve_draft_chunk merges rules directly into the current active policy through merge_chunk_into_policy(...) in crates/openshell-server/src/grpc.rs:1593 and crates/openshell-server/src/grpc.rs:2073.
  • Confirmed that the sandbox runtime already operates in proxy mode for proto policies in cluster mode, even when network_policies is empty, in crates/openshell-sandbox/src/policy.rs:101.
  • Confirmed that the restrictive default policy used for sandboxes created without an explicit policy has network_policies: {} in crates/openshell-policy/src/lib.rs:377.
  • Found an existing unit test that currently expects the full-policy path to reject this transition: crates/openshell-server/src/grpc.rs:4156.
  • Did not find regression coverage asserting consistency between full policy replacement and draft chunk approval.
  • User repro: openshell policy set --policy empty-policy.yaml on sandbox humane-halibut fails with the invalid argument error above, but the chunk approval workflow succeeds after generating a denied request and approving the resulting draft.

Description

A running sandbox created without network policies cannot accept its first network rule through openshell policy set, but it can accept the same rule through the draft approval flow.

This creates inconsistent server behavior for the same effective policy change:

  • full policy replace is rejected
  • chunk-based incremental merge is allowed

That suggests the server-side validation in UpdateSandboxPolicy is stale or too strict relative to the current runtime and draft merge behavior.

Reproduction Steps

  1. Create a sandbox without network policies:
    openshell sandbox create --name demo --keep --no-auto-providers
  2. Try to apply a policy file that preserves the existing filesystem / landlock / process settings and adds the first network rule:
    openshell policy set demo --policy github-readonly.yaml --wait
  3. Observe failure:
    cannot add network policies to a sandbox created without them (Block -> Proxy mode change requires restart)
  4. In the same sandbox, trigger a denied request such as curl https://google.com from inside the sandbox.
  5. Approve the resulting draft chunk.
  6. Observe that the rule is merged successfully and starts working.

Environment

  • Host OS: reproduced locally
  • OpenShell repo: current main checkout on investigation date
  • Sandbox: humane-halibut
  • Exact CLI / Docker versions: not collected in this repo-only investigation

Logs

openshell policy set --policy empty-policy.yaml
-> Using sandbox 'humane-halibut' (last used)
Error:   x status: InvalidArgument, message: "cannot add network policies to a sandbox created without them (Block -> Proxy mode change requires restart)", details:
  [], metadata: MetadataMap { headers: {"content-type": "application/grpc", "date": "Fri, 20 Mar 2026 00:44:04 GMT"} }

Regression Test Coverage Needed

  • Server test: prove UpdateSandboxPolicy allows empty -> non-empty network_policies when the runtime model already supports live proxy-mode reloads.
  • Server test: prove draft chunk approval and full policy replacement are consistent for the first network rule.
  • E2E test: create sandbox with no policy, apply first network rule via openshell policy set, wait for load, then verify outbound access succeeds.
  • Optional E2E parity test: compare direct policy set vs draft approval on the same sandbox.

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:policyPolicy engine and policy lifecycle workarea:sandboxSandbox runtime and isolation worktest:e2eRequires end-to-end coverage

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions