Per-Agent sandbox network policies
Each Agent owns one Namespace-scoped SandboxPolicy. It stores desired network
additions separately from the Installation's Sandbox Driver defaults. OpenShell
supports this contract with Kubernetes Compute and dedicated Agents. Other
Drivers opt in explicitly; the networking facet alone does not imply support.
Policy operations require an explicit facet in the HTTP path and controller
argument. The current implementation supports networking; filesystem and
process requests fail explicitly without reading or changing network additions.
The console discovers the owner’s contextual action URLs; the CLI exposes
--facet networking.
Discovery and preview
The selected Sandbox implementation contributes the shared definition for
(agent, sandbox-network-policy), whose value is { additions }. Agent discovery
returns its schema and contextual read, validate, save, and apply URLs. Policy GET
includes the same configuration descriptor when supported. Discovery requires
exact-Agent read and filters out contributions whose owner read is denied.
Unavailable actions remain hints; every invocation rechecks current authority.
POST /namespaces/{namespaceId}/agents/{agentId}/sandbox-policy/{facet}/validate
accepts { expectedVersion, additions }. It uses the same manage_policy and
policy update checks, editing flag, version, pending-work checks, schema,
semantics, ceiling, and provider admission as save, without policy or job writes.
Invalid additions return HTTP 200 with data.success: false and error.details;
a malformed envelope returns HTTP 400. Authorization and concurrency refusals
keep their resource errors. Each detail has a JSON Pointer, code, and safe message.
Preview success neither saves nor grants authority to a later save or apply.
Ceiling refusals identify the Installation delegation limit and retain
OUTSIDE_CEILING details at the rejected addition. The CLI prints these field
details with the error; it does not reduce them to a generic Driver refusal.
Authority and admission
User editing is disabled by default. Enabling or disabling
Agent.sandboxPolicyEditing requires both Installation and exact-Agent
administer. Changing additions or requesting live application requires exact-Agent
manage_policy and exact-SandboxPolicy update. Live application also requires
SandboxPolicy read: it admits and returns the saved additions. When saved
additions exist, Agent deploy and an Agent update that leaves repository bindings
also require SandboxPolicy read before any refusal that names them. Reads require
read on both resources. Ordinary Agent update does not authorize these operations.
The editing switch is temporary until the platform Guardrails primitive owns
these restrictions. Its controller and HTTP operation are named
TEMPORARY_API_WILL_BREAK_IN_FUTURE_setAgentSandboxPolicyEditing; callers must
expect this API to change when Guardrails replaces it.
Disabling editing preserves approved additions and previously admitted work.
Revocation requires an explicit policy change or Agent shutdown.
Without per-Agent network policy support, enabling editing returns HTTP 409;
disabling it stays allowed. When the Installation selects no Sandbox Driver,
validate, save and apply return the same 409 right after authorization. Policy GET
then returns capability: null and the cause in unavailableReason.
The trusted startup field sandboxNetworkPolicyCeiling bounds additions in both
the API and worker. Each entry has id, an exact absolute binary path, a DNS
host, and ports containing inclusive {from,to} ranges. Defaults grant
access; the ceiling grants no access. An absent or empty ceiling delegates no
additions. A changed ceiling governs future admission and pending application;
it does not revoke already running access automatically.
sandboxNetworkPolicyCeiling: - id: approved-service binary: /usr/bin/python3.11 host: service.example.com ports: - from: 443 to: 443The complete requested scope must fit the ceiling. Executable paths must match
exactly; a symlink or alternate interpreter path is a different executable.
*.example.com covers one DNS label, not the apex or deeper subdomains. A
wildcard addition requires that exact wildcard in the ceiling. Port coverage may
combine adjacent ceiling ranges; gaps remain unauthorized. Drivers reject
unsupported constraints. Policy documents allow at most 32 grants and 16 ranges
per grant. OpenShell additionally limits each grant to 1024 distinct ports.
Default rules, generated workspace access, model credential delivery, and Compute
isolation remain controlled by their existing owners. Users cannot submit
arbitrary provider policy YAML, alter defaults, share policies, or change their
Agent/Namespace association. Policy identities use sbp_ and the owner's UUID.
Policies are created and removed with the Agent; there is no standalone
create/delete or policy-reference API. Exact policy AccessBindings and
Restrictions are removed with their policy.
Desired and applied versions
Saving additions requires expectedVersion and advances the desired policy
version. It performs no provider mutation. Deploy explicitly to freeze selected
Driver defaults and approved additions into a new AgentRevision;
deployment retains its usual authorization and interruption behavior.
Save and apply to current revision sends the reviewed additions, expected
policy version and running revision in one request to the existing apply URL.
OCC saves the new version and queues its application in one transaction. An
empty additions list requests removal of the Agent-specific grants. Omitting
the additions field applies the previously saved version. If the response is
lost, refresh the policy and its application status before deciding what to do.
Live application additionally requires a supporting Driver, enabled editing, the
expected desired version, and the exact running revision. OCC records a durable
application with its requester, operation ID, snapshot, and expected provider
resource version. The worker rechecks authority, ownership, lifecycle, and the
current ceiling before effects. It preserves defaults from the admitted revision.
A concurrent provider update causes a conflict instead of an overwrite.
OCC rejects a save that removes a pending application. After the worker records
applied or failed, a save that advances the policy to its next version can
clear the application record.
pending means enforcement is unconfirmed. applied requires the provider's
complete desired policy to match the admitted candidate and its active, loaded
version to match. failed includes an unconfirmed outcome: the provider may have
accepted a request before a connection or worker was lost. Exhausted stale work,
its failed application, and audit evidence commit in one transaction; interruption
rolls back all three so a later worker can retry recovery. Refresh, inspect, and
retry explicitly. Failure never triggers automatic redeployment.
A failed application carries a reason code. The console shows its meaning
and next step. The worker repeats its checks on every pass, including passes
after it sent the policy. A failure after a send can leave the Sandbox running
that policy, and the worker does not send it again:
reason |
Cause and next step |
|---|---|
SANDBOX_POLICY_REVISION_CHANGED |
The current version changed or the Agent stopped. Apply again. |
AUTHORIZATION_DENIED |
The requester lost Agent manage_policy or SandboxPolicy update. Ask an Installation administrator. |
SANDBOX_POLICY_CEILING_CHANGED |
The delegation ceiling no longer covers every rule. Remove those rules and save. |
SANDBOX_POLICY_UNSUPPORTED |
The version's Sandbox Driver cannot apply or refused the rules. Deploy instead. |
SANDBOX_POLICY_PROTECTED_EXECUTION |
The version runs under protected execution. Remove the saved rules. |
SANDBOX_POLICY_VALIDATION_FAILED |
The Driver settings refused the rules. Review, correct and save them. |
SANDBOX_POLICY_PLACEMENT_UNAVAILABLE |
The Compute Driver changed since the version deployed. Deploy a new version. |
SANDBOX_POLICY_PROVIDER_FAILED |
The provider reported failure. Refresh and apply again. |
SANDBOX_POLICY_UNCONFIRMED |
No confirmation in time; the provider may have applied part. Refresh and retry. |
The last
confirmed live version remains recorded separately when desired additions change.
Policy GET returns the running revision as defaultsRevisionId and the saved
policy version it was deployed with as deployedPolicyVersion. When that version
is the saved version and no application targets that revision, the Sandbox
enforces the saved rules without live application. The console shows Deployed.
Revision snapshots and attributable audit evidence preserve the original admission.
The console shows read-only defaults from the running revision, or the selected
Driver before deployment, separately from the delegation ceiling and additions.
OpenShell changes network rules through UpdateConfig, using a fixed nonzero CAS
version and request ID. It retains static filesystem/process policy and generated
transport access. Its create-time Sandbox spec remains unchanged; status reads
confirm the later policy version independently. Other Sandbox Drivers may support
redeployment without live application.
The OCC gateway identity needs OpenShell sandbox:read and Workspace role user
for GetSandbox and GetSandboxPolicyStatus, and config:write and Workspace
role admin for UpdateConfig. Recommendations through GetDraftPolicy need
config:read and Workspace role user. OpenShell scope enforcement must be configured
on its OIDC gateway; these permissions are separate from OCE resource authority.
Denials and recommendations
The console's Policy denials tab uses the existing bounded, exact-revision
Sandbox log path and its read_logs/administration rules. Typed metadata is
optional. Recognized OpenShell records are parsed by the Driver; OCC validates,
redacts, and applies the normal output and retention limits. Unknown fields stay
unknown. The denial count describes retained records, not unique requests or
lifetime denials. The console retains up to 1,000 log records and initially shows
five denials per page, newest first. Search filters the loaded window. Follow
collects new records without moving inspected rows; Show newest displays them. Buffer gaps, withheld records, permission denial, and missing
metadata remain visible. An empty successful read says No denials in this window.
The Network policy card precedes the denial log and distinguishes saved policy from confirmed live application. Allow this prefills one exact rule; Allow selected stages eligible denials selected across pages. Select page selects only the visible page. Repeated executable/host/port requests and requests covered by existing rules do not create additional rules. The complete proposed policy preserves existing rules and passes the normal preview, save, and apply admission. Selecting denials grants no access until the user reviews and saves. Manage rules searches and pages explicit rules, five at a time, with separate controls to edit or remove a rule. Installation defaults stay read-only. The Policy editing switch sits beside Manage rules. With editing off, View rules, suggestions, and View network limits remain available for inspection; mutation controls are hidden. The ceiling has a separate read-only dialog. Policy details on the page shows versions and Installation defaults. Suggestions initially show three entries, with an explicit action to show the rest. Their executable basenames have expandable full paths. Bulk review shows selected record and new-rule counts, repeated requests, and already-covered requests. The rule dialog footer offers review for unsaved changes, application for saved changes, and Done when no action remains. Save feedback distinguishes saved, pending, confirmed, and unconfirmed outcomes. Refresh status only reads; Retry application requires an explicit refresh of the failed application and rechecks application authority. Neither action starts a deployment.
Provider suggestions use a separate authorized and audited read. OpenShell's
GetDraftPolicy returns aggregated chunks, including its lifecycle and observation
count. Chunks can be observation-generated or agent-authored; origin is not
inferred. Only representable executable/host/port suggestions are shown. A
suggestion is an untrusted candidate, and its observation count is not a denial
count. Reviewing it prefills the ordinary policy editor. OCE validates and applies
its canonical policy; it does not approve native drafts directly.
Recommendation reads return at most 32 suggestions, with a 4 MiB provider message limit. They share the replica-local read limits of two requests per second per principal/Agent, burst ten and sixteen concurrent reads. Follow polls logs every three seconds and suggestions every five seconds. Draft versions are not used as complete change signals. Editing works without logs or suggestions.
See the operator workflow, Driver contract, API, and source flow. See runtime coverage and the implementation plan for verification and qualification limits.
