Use this page to apply and iterate policy changes on running sandboxes. For a full field-by-field YAML definition, use the Policy Schema Reference.
A policy has static sections filesystem_policy, landlock, and process that are locked at sandbox creation, and a dynamic section network_policies that is hot-reloadable on a running sandbox.
Static sections are locked at sandbox creation. Changing them requires destroying and recreating the sandbox.
Dynamic sections can be updated on a running sandbox with openshell policy update for incremental merges or openshell policy set for full replacement, and take effect without restarting.
When a hot reload changes rules on an active HTTP L7 endpoint, existing keep-alive tunnels are closed before forwarding another parsed request. Credential-injection-only HTTP passthrough tunnels use the same reload boundary. Most HTTP clients reconnect automatically, and the next request is evaluated against the current policy.
Raw streams are connection-scoped and outside L7 live-reload guarantees. This includes tls: skip, non-HTTP TCP payloads, HTTP upgrades such as WebSocket, and long-lived response streams such as SSE. A reload applies to the next connection or next parsed HTTP request; it does not interrupt an already-forwarded raw stream. Use protocol: websocket when policy should stay attached to the RFC 6455 upgrade and client text messages after the allowed upgrade. Add websocket_credential_rewrite: true only when the relay should rewrite credential placeholders in client-to-server WebSocket text messages. Add request_body_credential_rewrite: true only on inspected REST endpoints that need OpenShell to rewrite placeholders in supported text request bodies.
When a sandbox runs in proxy mode (the default), OpenShell automatically adds baseline filesystem paths required for the sandbox child process to function: /usr, /lib, /etc, /var/log (read-only) and /sandbox, /tmp (read-write). Paths like /app are included in the baseline set but are only added if they exist in the container image.
This filtering prevents a missing baseline path from degrading Landlock enforcement. Without it, a single missing path could cause the entire Landlock ruleset to fail, leaving the sandbox with no filesystem restrictions at all.
User-specified paths in your policy YAML are not pre-filtered. If you list a path that does not exist:
best_effort mode, the path is skipped with a warning and remaining rules are still applied.hard_requirement mode, sandbox startup fails immediately.This distinction means baseline system paths degrade gracefully while user-specified paths surface configuration errors.
Pass a policy YAML file when creating the sandbox:
openshell sandbox create keeps the sandbox running after the initial command exits, which is useful when you plan to iterate on the policy. Add --no-keep if you want the sandbox deleted automatically instead.
To avoid passing --policy every time, set a default policy with an environment variable:
The CLI uses the policy from OPENSHELL_SANDBOX_POLICY whenever --policy is not explicitly provided.
To change what the sandbox can access, pull the current policy, edit the YAML, and push the update. The workflow is iterative: create the sandbox, monitor logs for denied actions, pull the policy, modify it, push, and verify.
The following steps outline the hot-reload policy update workflow.
Create the sandbox with your initial policy by following Apply a Custom Policy above (or set OPENSHELL_SANDBOX_POLICY).
Monitor denials. Each log entry shows host, port, binary, and reason. Alternatively, use openshell term for a live dashboard.
For additive network changes, use openshell policy update. This is the fastest path for adding endpoints, binaries, or REST and WebSocket allow/deny rules without replacing the full policy. The full option and format reference is in Incremental Policy Updates.
--add-allow and --add-deny target existing protocol: rest or protocol: websocket endpoints. If you pass multiple update flags in one command, OpenShell applies them as one atomic merge batch and persists at most one new revision.
For larger edits, pull the current policy and edit the YAML directly. Strip the metadata header (Version, Hash, Status) before reusing the file.
Edit the YAML: add or adjust network_policies entries, binaries, access, or rules.
Push the updated policy when you need a full replacement. Exit codes: 0 = loaded, 1 = validation failed, 124 = timeout.
Verify the new revision. If status is loaded, repeat from step 2 as needed; if failed, fix the policy and repeat from step 4.
Use openshell policy update when you want to merge network policy changes into the current live policy instead of replacing the whole YAML document. This command only updates the dynamic network_policies section.
openshell policy update is useful when you want to:
--dry-run before you send it to the gateway.Use openshell policy set instead when you want to replace the full policy, update static sections, or make broader edits that are easier to express in YAML.
The incremental update surface is split into endpoint-level operations and method/path rule-level operations for REST and WebSocket endpoints.
--wait and --dry-run cannot be used together.
--add-endpoint works at the endpoint and rule level. It creates a new network_policies entry when needed, or merges into an existing rule that already covers the same host and port. Use it when you define where traffic can go and which binaries can send it.
--add-allow and --add-deny work at the method/path rule level. They do not create binaries, and they do not create a new endpoint. They modify an existing endpoint that already has protocol: rest or protocol: websocket.
This is the practical difference:
--add-endpoint to say “allow this binary to reach api.github.com:443.”--add-allow to say “for that existing REST endpoint, also allow POST /repos/*/issues.”--add-deny to say “for that existing REST endpoint, explicitly deny POST /admin/**.”--add-allow to say “for that existing WebSocket endpoint, also allow client text messages on /v1/realtime/**.”Current constraints:
--add-allow and --add-deny work on protocol: rest and protocol: websocket endpoints.--add-deny requires the endpoint to already have an allow base, either an access preset or explicit allow rules.protocol: sql is not a practical incremental workflow today. OpenShell does not do full SQL parsing, and SQL enforcement is not meaningfully supported yet.--add-endpoint uses this format:
Each segment has a fixed meaning:
Examples:
If you set protocol: rest or protocol: websocket, you also need an allow shape. With incremental updates, that means you should provide an access preset on --add-endpoint, then use --add-allow or --add-deny to refine method/path rules later.
Use the websocket-credential-rewrite endpoint option with protocol: websocket when the sandbox should send credential placeholders in client text frames and have OpenShell resolve them after the allowed upgrade. The option can also be used with protocol: rest compatibility endpoints that perform a WebSocket upgrade. It is rejected for plain L4 or protocol: sql endpoints.
Use the request-body-credential-rewrite endpoint option with protocol: rest when an API expects OpenShell-managed credentials in UTF-8 JSON, form, or text request bodies. OpenShell buffers up to 256 KiB, rewrites recognized credential placeholders, updates Content-Length, and rejects unresolved placeholders instead of forwarding them. The option is rejected for WebSocket, GraphQL, SQL, and plain L4 endpoints.
Credential rewrite recognizes the canonical openshell:resolve:env:KEY placeholder form and whole-token provider-shaped aliases such as provider-OPENSHELL-RESOLVE-ENV-API_TOKEN when the referenced environment key exists in the configured provider credentials.
For example:
api.github.com:443:read-only:rest is valid.realtime.example.com:443:read-write:websocket is valid.api.github.com:443::rest is invalid. It does not mean “allow all traffic.” An L7 endpoint with protocol but no access or rules is rejected when the policy loads.Endpoint options belong to the individual --add-endpoint spec. When you pass multiple --add-endpoint flags in one command, every --binary value applies to every added endpoint in that command. If different endpoints need different binaries, use separate policy update commands.
If you do not pass --rule-name, OpenShell generates one from the host and port, such as allow_api_github_com_443.
--add-allow and --add-deny use this format:
This string identifies an existing REST or WebSocket endpoint and the request pattern you want to add.
In shell commands, quote the full SPEC when it contains * or ** so your shell passes it literally instead of expanding it as a local file glob.
This example:
means:
api.github.com:443.POST./repos/acme/issues./repos/acme/project/issues/123 because * matches one path segment.Path globs follow the same semantics as YAML allow and deny rules:
* matches one path segment.** matches any number of segments./repos/*/issues matches one repository owner or name segment in the middle./repos/** matches everything under /repos/.The rule-level commands only modify method and path constraints. They do not change binaries, hostnames, ports, protocol settings, or WebSocket message payload matching.
Use these patterns as starting points when you decide whether to update an endpoint or append REST/WebSocket rules.
Use --add-endpoint when you need a new host and port and do not need REST inspection.
This creates or merges endpoint entries and binds them to the listed binaries. It does not create inspected method/path rules.
Use --add-endpoint first when the endpoint does not exist yet.
This creates a REST endpoint and sets its base allow behavior through the read-only access preset.
Use --add-allow after the REST endpoint already exists.
This keeps the existing endpoint definition and appends one new allow rule. It does not add binaries or change the endpoint host and port.
Use --add-deny when you want to carve out a blocked subtree under an existing REST endpoint.
This adds a deny rule to the existing REST endpoint. The endpoint must already have an allow base.
Use --add-endpoint with protocol: websocket when the destination is an RFC 6455 WebSocket API.
This creates a WebSocket endpoint and sets its base allow behavior through the read-write access preset. For WebSocket endpoints, read-write expands to the upgrade GET and client WEBSOCKET_TEXT messages on the upgraded request path. The rewrite option lets the sandbox send openshell:resolve:env:* placeholders in client text frames; OpenShell resolves them before forwarding to the upstream service.
Use WEBSOCKET_TEXT when you want to refine client-to-server text-frame policy without matching message payload content.
This adds a deny rule to the existing WebSocket endpoint. The path glob matches the WebSocket upgrade path.
Use --remove-endpoint to remove one host and port pair, or --remove-rule to delete the whole named rule.
If the target endpoint is part of a multi-port endpoint, --remove-endpoint removes only the specified port and keeps the rest.
OpenShell applies all update flags from one openshell policy update command as one merge batch. The gateway validates the full merged result and persists at most one new policy revision.
This means:
When two updates race, the gateway uses optimistic retry. It fetches the latest revision, reapplies the full batch, validates the result again, and retries the write. This preserves the intent of each individual command while still allowing concurrent sandbox policy updates.
Use --dry-run when you want to inspect the merged YAML before you send it to the gateway.
The CLI validates the argument shapes before it sends the request. The gateway then validates the merged policy against the current live policy and returns clear errors when:
1 through 65535.--add-allow or --add-deny points at an endpoint that does not exist.--add-allow or --add-deny targets an endpoint that is neither REST nor WebSocket.--add-deny targets an endpoint that has no base allow set.Use a global policy when you want one policy payload to apply to every sandbox.
When a global policy is configured:
To restore sandbox-level policy control, delete the global policy setting:
You can inspect a sandbox’s effective settings and policy source with:
Check openshell logs <name> --tail --source sandbox for the denied host, path, and binary.
For agent-authored draft updates on running sandboxes, enable Policy Advisor. Policy advisor lets the sandboxed agent submit a narrow proposal through policy.local while a developer still approves or rejects the structured rule from outside the sandbox.
When triaging denied requests, check:
binaries entry needs to be added or adjusted.GET / WEBSOCKET_TEXT and the upgraded request path for WebSocket endpoints, to confirm which rules entry needs to be added or adjusted.Then push the updated policy as described above.
For small changes, prefer openshell policy update over rewriting the full YAML:
Add these blocks to the network_policies section of your sandbox policy. Apply simple endpoints and REST/WebSocket rule additions with openshell policy update, or apply any complete YAML block with openshell policy set <name> --policy <file> --wait.
Use Simple endpoint for host-level allowlists and Granular rules for method/path control.
Allow pip install and uv pip install to reach PyPI:
Endpoints without protocol use TCP passthrough, where the proxy allows the stream without inspecting payloads. If the stream is HTTP and TLS is auto-terminated, the proxy can still rewrite configured credential placeholders and closes keep-alive passthrough tunnels on policy reload before forwarding another request. WebSocket text-frame policy requires an explicit protocol: websocket endpoint. WebSocket payload credential rewrite can also be enabled on a protocol: rest compatibility endpoint with websocket_credential_rewrite: true. REST request body credential rewrite requires an inspected protocol: rest endpoint with request_body_credential_rewrite: true.
REST rules can also constrain query parameter values:
query matchers are case-sensitive and run on decoded values. If a request has duplicate keys (for example, tag=a&tag=b), every value for that key must match the configured glob(s).
GraphQL endpoints use protocol: graphql. The proxy parses GraphQL-over-HTTP GET and POST requests, classifies each operation, and evaluates rules against the operation type, optional operation name, and selected root fields.
GraphQL endpoint policies currently require full policy YAML applied with openshell policy set; the incremental openshell policy update --add-endpoint parser does not accept graphql as a protocol.
For allow rules, every selected root field in an operation must match one of the configured fields globs. For deny rules, one matching root field blocks the request. Batched GraphQL requests are fail-closed: if any operation is malformed, denied, or unregistered, the whole HTTP request is denied.
Hash-only persisted queries cannot be classified from the request alone. OpenShell denies them unless the endpoint uses persisted_queries: allow_registered and provides a trusted graphql_persisted_queries entry keyed by hash or saved-query ID.
Some APIs carry GraphQL operations over RFC 6455 WebSockets, commonly for subscriptions and realtime updates. Configure these as protocol: websocket, allow the upgrade with a normal GET rule, then add GraphQL operation rules for client operation messages. OpenShell recognizes modern graphql-transport-ws subscribe messages and legacy graphql-ws start messages.
When a WebSocket endpoint has GraphQL operation policy, client operation messages are fail-closed on malformed JSON, unsupported message types, parse errors, unregistered hash-only persisted queries, or unallowed operations. Use GraphQL operation rules for client messages rather than a raw WEBSOCKET_TEXT allow rule. Protocol lifecycle messages such as connection_init, ping, pong, and complete are allowed without payload logging; if websocket_credential_rewrite: true is set, placeholders inside those text messages are resolved before forwarding.
GraphQL field names are application-specific, so treat these as starting shapes to review against the actual app schema:
Explore related topics: