Policy Schema Reference

View as Markdown

Complete field reference for the sandbox policy YAML. Each field is documented with its type, whether it is required, and whether it is static (locked at sandbox creation) or dynamic (hot-reloadable on a running sandbox).

Top-Level Structure

A policy YAML file contains the following top-level fields:

version: 1
filesystem_policy: { ... }
landlock: { ... }
process: { ... }
network_policies: { ... }
FieldTypeRequiredCategoryDescription
versionintegerYesPolicy schema version. Must be 1.
filesystem_policyobjectNoStaticControls which directories the agent can read and write.
landlockobjectNoStaticConfigures Landlock LSM enforcement behavior.
processobjectNoStaticSets the user and group the agent process runs as.
network_policiesmapNoDynamicDeclares which binaries can reach which network endpoints.

Static fields are set at sandbox creation time. Changing them requires destroying and recreating the sandbox. Dynamic fields 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.

Version

The version field identifies which schema the policy uses:

FieldTypeRequiredDescription
versionintegerYesSchema version number. Currently must be 1.

Filesystem Policy

Category: Static

Controls filesystem access inside the sandbox. Paths not listed in either read_only or read_write are inaccessible.

FieldTypeRequiredDescription
include_workdirboolNoWhen true, automatically adds the agent’s working directory to read_write.
read_onlylist of stringsNoPaths the agent can read but not modify. Typically system directories like /usr, /lib, /etc.
read_writelist of stringsNoPaths the agent can read and write. Typically /sandbox (working directory) and /tmp.

Validation constraints:

  • Every path must be absolute (start with /).
  • Paths must not contain .. traversal components. The server normalizes paths before storage, but rejects policies where traversal would escape the intended scope.
  • Read-write paths must not be overly broad (for example, / alone is rejected).
  • Each individual path must not exceed 4096 characters.
  • The combined total of read_only and read_write paths must not exceed 256.

Policies that violate these constraints are rejected with INVALID_ARGUMENT at creation or update time. Disk-loaded YAML policies that fail validation fall back to a restrictive default.

Example:

filesystem_policy:
include_workdir: true
read_only:
- /usr
- /lib
- /proc
- /dev/urandom
- /etc
read_write:
- /sandbox
- /tmp
- /dev/null

Landlock

Category: Static

Configures Landlock LSM enforcement at the kernel level. Landlock provides mandatory filesystem access control below what UNIX permissions allow.

FieldTypeRequiredValuesDescription
compatibilitystringNobest_effort, hard_requirementHow OpenShell handles Landlock failures. Refer to the behavior table below.

Compatibility modes:

ValueKernel ABI unavailableIndividual path inaccessibleAll paths inaccessible
best_effortWarns and continues without Landlock.Skips the path, applies remaining rules.Warns and continues without Landlock (refuses to apply an empty ruleset).
hard_requirementAborts sandbox startup.Aborts sandbox startup.Aborts sandbox startup.

best_effort (the default) is appropriate for most deployments. It handles missing paths gracefully. For example, /app might not exist in every container image but is included in the baseline path set for containers that do have it. Individual missing paths are skipped while the remaining filesystem rules are still enforced.

hard_requirement is for environments where any gap in filesystem isolation is unacceptable. If a listed path cannot be opened for any reason (missing, permission denied, symlink loop), sandbox startup fails immediately rather than running with reduced protection.

When a path is skipped under best_effort, the sandbox logs a warning that includes the path, the specific error, and a human-readable reason (for example, “path does not exist” or “permission denied”).

Example:

landlock:
compatibility: best_effort

Process

Category: Static

Sets the OS-level identity for the agent process inside the sandbox.

FieldTypeRequiredDescription
run_as_userstringNoThe user name or UID the agent process runs as. Default: sandbox.
run_as_groupstringNoThe group name or GID the agent process runs as. Default: sandbox.

Validation constraint: Neither run_as_user nor run_as_group can be set to root or 0. Policies that request root process identity are rejected at creation or update time.

Example:

process:
run_as_user: sandbox
run_as_group: sandbox

Network Policies

Category: Dynamic

A map of named network policy entries. Each entry declares a set of endpoints and a set of binaries. Only the listed binaries are permitted to connect to the listed endpoints. The map key is a logical identifier. The name field inside the entry is the display name used in logs.

Network Policy Entry

Each entry in the network_policies map has the following fields:

FieldTypeRequiredDescription
namestringNoDisplay name for the policy entry. Used in log output. Defaults to the map key.
endpointslist of endpoint objectsYesHosts and ports this entry permits.
binarieslist of binary objectsYesExecutables allowed to connect to these endpoints.

Endpoint Object

Each endpoint defines a reachable destination and optional inspection rules.

FieldTypeRequiredDescription
hoststringYesHostname or IP address. Supports a * wildcard inside the first DNS label only: *.example.com, **.example.com, and intra-label patterns like *-aiplatform.googleapis.com are accepted; bare */**, TLD wildcards (*.com), and wildcards outside the first label are rejected at load time.
portintegerYesTCP port number.
pathstringNoOptional HTTP path glob used to select between L7 endpoints that share the same host and port. Empty means all paths. Use this when REST and GraphQL live under the same host, such as /repos/** and /graphql.
protocolstringNoSet to rest for HTTP method/path inspection, websocket for RFC 6455 upgrade and client text-message inspection, graphql for GraphQL-over-HTTP operation inspection, mcp for MCP Streamable HTTP request inspection, or json-rpc for generic JSON-RPC-over-HTTP method inspection. WebSocket endpoints can also use GraphQL operation rules for GraphQL-over-WebSocket traffic. Omit for TCP passthrough.
tlsstringNoTLS handling mode. The proxy auto-detects TLS by peeking the first bytes of each connection and terminates it for inspected HTTPS traffic, so this field is optional in most cases. Set to skip to disable auto-detection for edge cases such as client-certificate mTLS or non-standard protocols. The values terminate and passthrough are deprecated and log a warning; they are still accepted for backward compatibility but have no effect on behavior.
enforcementstringNoenforce actively blocks disallowed requests. audit logs violations but allows traffic through.
accessstringNoAccess preset. One of read-only, read-write, or full. Mutually exclusive with rules. Not valid on protocol: mcp or protocol: json-rpc; MCP uses explicit rules unless mcp.allow_all_known_mcp_methods: true enables the endpoint method profile, and JSON-RPC always uses explicit rules.
ruleslist of allow rule objectsNoFine-grained protocol-specific allow rules. Mutually exclusive with access.
deny_ruleslist of deny rule objectsNoL7 deny rules that block specific requests even when allowed by access or rules. Deny rules take precedence over allow rules.
allowed_ipslist of stringNoCIDR or IP allowlist for SSRF override. Exact user-declared hostname endpoints may resolve to RFC 1918 private addresses without this field, but wildcard, hostless, and policy-advisor-proposed endpoints still require allowed_ips for private resolved IPs. Entries overlapping loopback (127.0.0.0/8), link-local (169.254.0.0/16), or unspecified (0.0.0.0) are rejected at load time.
allow_encoded_slashboolNoWhen true, L7 request parsing preserves %2F inside path segments instead of rejecting it. Use this for registries and APIs such as npm scoped packages (/@scope%2Fname). Defaults to false.
websocket_credential_rewriteboolNoWhen true on a protocol: rest or protocol: websocket endpoint, OpenShell rewrites credential placeholders in client-to-server WebSocket text messages after an allowed HTTP 101 upgrade. Binary frames are relayed but not rewritten. Defaults to false.
request_body_credential_rewriteboolNoWhen true on a protocol: rest endpoint, OpenShell rewrites credential placeholders in UTF-8 application/json, application/x-www-form-urlencoded, and text/* request bodies before forwarding upstream. The proxy buffers at most 256 KiB and updates Content-Length after rewriting. Defaults to false. Mutually exclusive with credential_signing.
credential_signingstringNoProxy-side credential signing mode. When set, the proxy strips the sandbox client’s Authorization header and re-signs with real provider credentials. Values: sigv4 (auto-detect payload mode from client headers), sigv4:body (buffer and hash body, max 10 MiB), sigv4:no_body (unsigned payload, stream body). Mutually exclusive with request_body_credential_rewrite. See AWS SigV4.
signing_servicestringNoAWS service name for SigV4 signing (e.g. bedrock, s3, sts). Required when credential_signing is set.
signing_regionstringNoAWS region override for SigV4 signing (e.g. us-east-1). When omitted, the region is extracted from the endpoint hostname. Required for non-standard AWS endpoints where the region cannot be inferred.
persisted_queriesstringNoGraphQL hash-only behavior for protocol: graphql and GraphQL-over-WebSocket operation policy. Default is deny; use allow_registered only with graphql_persisted_queries.
graphql_persisted_queriesmapNoTrusted GraphQL persisted-query registry keyed by hash or saved-query ID. Values contain operation_type, optional operation_name, and optional root fields.
graphql_max_body_bytesintegerNoMaximum GraphQL-over-HTTP request body bytes buffered for inspection. Defaults to 65536.
mcpobjectNoMCP endpoint options for protocol: mcp. MCP endpoints must set a concrete host and port or ports; protocol: mcp alone is invalid and is not treated as a wildcard endpoint.
mcp.max_body_bytesintegerNoMaximum MCP JSON-RPC-over-HTTP request body bytes buffered for inspection. Defaults to 65536.
mcp.strict_tool_namesboolNoDefaults to true. Requires tools/call params.name values to match ^[A-Za-z0-9_.-]{1,128}$ before policy evaluation. Set to false only for compatibility with MCP servers that intentionally use non-recommended tool names. Wildcard tool matchers require this to remain enabled.
mcp.allow_all_known_mcp_methodsboolNoDefaults to false. When true, enables the endpoint MCP method profile: omitted rules allow all MCP-family methods and all tools before deny_rules, and omitted rule method uses that profile. When unset or false, explicit MCP method rules are required; rules with tool or params.name must set method: tools/call.
json_rpcobjectNoJSON-RPC endpoint options. For protocol: json-rpc, json_rpc.max_body_bytes sets the maximum JSON-RPC-over-HTTP request body bytes buffered for inspection. Defaults to 65536.

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.

Access Levels

The access field accepts one of the following values on REST, WebSocket, and GraphQL endpoints. MCP and JSON-RPC endpoints reject access because HTTP method/path presets cannot authorize JSON-RPC safely. Use explicit MCP rules, set mcp.allow_all_known_mcp_methods: true for the MCP method profile, or use explicit JSON-RPC rules.

ValueREST expansionWebSocket expansionGraphQL expansionMCP / JSON-RPC expansion
fullAll methods and paths.WebSocket upgrade and all inspected client text-message paths.All operation types.Rejected.
read-onlyGET, HEAD, OPTIONS.WebSocket upgrade handshake only.query operations.Rejected.
read-writeGET, HEAD, OPTIONS, POST, PUT, PATCH.WebSocket upgrade handshake and client text messages.query and mutation operations.Rejected.

For MCP endpoints, configure explicit rules with method, optional tool, and supported params. For generic JSON-RPC endpoints, configure explicit rules with method; JSON-RPC policy params matchers are not presently supported.

Allow Rule Objects

Used when access is not set. Each entry in rules contains an allow object. The tables below list the fields inside that allow object.

REST Allow Rule (protocol: rest)

REST allow rules match HTTP requests by method, path, and optional query parameters.

FieldTypeRequiredDescription
methodstringYesHTTP method to allow (for example, GET, POST). * matches any method.
pathstringYesURL path pattern. Supports * and ** glob syntax.
querymapNoQuery parameter matchers keyed by decoded param name. Matcher value can be a glob string (tag: "foo-*") or an object with any (tag: { any: ["foo-*", "bar-*"] }).

Example REST allow rules:

rules:
- allow:
method: GET
path: /**/info/refs*
query:
service: "git-*"
- allow:
method: POST
path: /**/git-upload-pack
query:
tag:
any: ["v1.*", "v2.*"]
WebSocket Allow Rule (protocol: websocket)

WebSocket allow rules match the RFC 6455 HTTP upgrade by path and match client-to-server text messages on the same upgraded connection with the synthetic WEBSOCKET_TEXT method. Binary frames are relayed but are not rewritten.

FieldTypeRequiredDescription
methodstringYesGET allows the upgrade handshake, WEBSOCKET_TEXT allows client text messages after upgrade, and * matches both inspected actions.
pathstringYesURL path pattern from the original upgrade request. Supports * and ** glob syntax.
querymapNoQuery parameter matchers from the original upgrade request. Matcher syntax is the same as REST allow rules.

Example WebSocket allow rules:

rules:
- allow:
method: GET
path: /v1/realtime/**
- allow:
method: WEBSOCKET_TEXT
path: /v1/realtime/**
GraphQL Allow Rule (protocol: graphql or GraphQL-over-WebSocket)

GraphQL allow rules match parsed GraphQL operations by operation type, optional operation name, and optional root fields. On protocol: graphql, they apply to GraphQL-over-HTTP GET and POST requests. On protocol: websocket, include a separate GET allow rule for the RFC 6455 upgrade, then use GraphQL allow rules for client operation messages using the graphql-transport-ws subscribe message type or the legacy graphql-ws start message type.

FieldTypeRequiredDescription
operation_typestringYesGraphQL operation type: query, mutation, subscription, or *.
operation_namestringNoGraphQL operation-name glob. Omit to match any operation name.
fieldslist of stringNoGraphQL root-field globs. Every selected root field must match one configured glob. Omit to match all root fields.

Example GraphQL allow rules:

rules:
- allow:
operation_type: query
fields: [viewer, repository]
- allow:
operation_type: mutation
operation_name: Issue*
fields: [createIssue]

Example GraphQL-over-WebSocket allow rules:

rules:
- allow:
method: GET
path: /graphql
- allow:
operation_type: subscription
fields: [messageAdded]
- allow:
operation_type: query
fields: [viewer]

Do not combine method, path, or query with operation_type, operation_name, or fields inside the same WebSocket rule. When a WebSocket endpoint has GraphQL operation policy, use GraphQL rules for client messages instead of a raw WEBSOCKET_TEXT allow rule.

MCP Allow And Deny Rules (protocol: mcp)

MCP rules match sandbox-to-server MCP Streamable HTTP request bodies by MCP method and optional tool selectors. OpenShell parses the underlying JSON-RPC 2.0 envelope, validates known MCP request and notification params, and preserves unknown extension methods as policy-addressable literal method strings. Until OpenShell exposes explicit MCP version profiles, mcp.allow_all_known_mcp_methods defaults to false, so endpoints require explicit MCP method rules. Set mcp.allow_all_known_mcp_methods: true to enable the endpoint method profile; in that mode, rules can omit method, and tool selectors are normalized to tools/call internally. By default, tools/call params.name must match the MCP-recommended tool-name pattern ^[A-Za-z0-9_.-]{1,128}$; configure mcp.strict_tool_names: false on the endpoint only to allow a server that intentionally uses names outside that pattern. Wildcard tool matchers require mcp.strict_tool_names to remain enabled. JSON-RPC responses and server-to-client MCP messages on response bodies or SSE streams are relayed but are not currently parsed for policy enforcement.

Use rules for MCP allow rules and deny_rules for MCP deny rules. Deny rules take precedence over allow rules. If an MCP endpoint sets mcp.allow_all_known_mcp_methods: true and omits rules, OpenShell allows all MCP-family methods and all tools, then applies any deny_rules. Otherwise, the endpoint must define explicit rules. A broad allow or deny rule whose method matcher includes tools/call cannot be combined with tool-specific allow rules because it would bypass or erase the tool filter; add tool or params.name to scope tools/call, or remove the tool-specific rules. In a batch request, one denied call denies the full batch.

FieldTypeRequiredDescription
methodstringNoMCP method name, such as initialize, tools/list, tools/call, or an unknown extension method. Globs are accepted only for the tools/ method family, such as tools/*. Required unless mcp.allow_all_known_mcp_methods is true; when that option is true, omitted method uses the endpoint method profile. Do not use method: "*" for MCP; omit method only when using the allow-all MCP method profile.
toolstring or matcherNoConvenience matcher for tools/call params.name. Supports a glob string or { any: [...] }. Requires method: tools/call unless mcp.allow_all_known_mcp_methods is true; validation fails otherwise. Omit to match every tool.
paramsmapNoMCP currently accepts only params.name as a lower-level tool-name matcher. Requires method: tools/call unless mcp.allow_all_known_mcp_methods is true; validation fails otherwise. Tool argument matching is not supported yet; allowed tools accept all argument payloads by default.

An MCP client first sends initialize. After the server returns a successful response, the client sends notifications/initialized. After initialization completes and the server advertises the tools capability, the client can call an advertised tool. The response does not need an allow rule because these rules inspect messages sent from the client to the server. This example adds both client initialization messages to the existing tool rules. It omits tools/list because it assumes the client already knows the tool names; add that method when the client performs discovery.

endpoints:
- host: mcp.example.com
port: 443
path: /mcp
protocol: mcp
enforcement: enforce
mcp:
max_body_bytes: 131072
rules:
- allow:
method: initialize
- allow:
method: notifications/initialized
- allow:
method: tools/call
tool: search_web
- allow:
method: tools/call
tool:
any: [create_issue, list_issues]
deny_rules:
- method: tools/call
tool: send_email
- method: tools/call
tool: execute_code
JSON-RPC Allow Rule (protocol: json-rpc)

JSON-RPC allow rules match sandbox-to-server JSON-RPC-over-HTTP request objects by RPC method. They apply to single JSON-RPC requests and batch requests. For a batch, OpenShell evaluates each call independently. Client-to-server JSON-RPC response frames in POST bodies are denied. Server-to-client messages on HTTP response bodies or MCP SSE streams are relayed but are not currently parsed for policy enforcement.

FieldTypeRequiredDescription
methodstringYesExact JSON-RPC method name such as initialize or reports.search. Use * only as the allow-all sentinel. Other wildcard or glob patterns are rejected for generic JSON-RPC endpoints.

Generic JSON-RPC policy params matchers are not supported. Allow rules match only the JSON-RPC method.

Example JSON-RPC allow rules:

endpoints:
- host: jsonrpc.example.com
port: 443
path: /rpc
protocol: json-rpc
enforcement: enforce
json_rpc:
max_body_bytes: 131072
rules:
- allow:
method: initialize
- allow:
method: reports.list
- allow:
method: reports.search

Deny Rule Objects

Blocks specific operations on endpoints that otherwise have broad access. Deny rules are evaluated after allow rules and take precedence: if a request matches any deny rule, it is blocked regardless of what the allow rules or access preset permit.

REST Deny Rule (protocol: rest)

REST deny rules use the same field names as REST allow rules, but they appear directly under each deny_rules entry instead of under an allow wrapper.

FieldTypeRequiredDescription
methodstringYesHTTP method to deny (for example, POST, DELETE). * matches any method.
pathstringYesURL path pattern. Same glob syntax as allow rules. Use ** to match any path.
querymapNoQuery parameter matchers. Same syntax as allow rule query.

Example REST deny rules:

endpoints:
- host: api.github.com
port: 443
protocol: rest
enforcement: enforce
access: read-write
deny_rules:
- method: POST
path: "/repos/*/pulls/*/reviews"
- method: PUT
path: "/repos/*/branches/*/protection"
- method: "*"
path: "/repos/*/rulesets"
WebSocket Deny Rule (protocol: websocket)

WebSocket deny rules use the same field names as WebSocket allow rules, but they appear directly under each deny_rules entry instead of under an allow wrapper.

FieldTypeRequiredDescription
methodstringYesGET denies matching upgrade handshakes, WEBSOCKET_TEXT denies matching client text messages after upgrade, and * matches both inspected actions.
pathstringYesURL path pattern from the original upgrade request. Same glob syntax as allow rules.
querymapNoQuery parameter matchers from the original upgrade request. Same syntax as allow rule query.

Example WebSocket deny rules:

endpoints:
- host: realtime.example.com
port: 443
protocol: websocket
enforcement: enforce
access: read-write
deny_rules:
- method: WEBSOCKET_TEXT
path: "/v1/admin/**"
GraphQL Deny Rule (protocol: graphql or GraphQL-over-WebSocket)

GraphQL deny rules use the same field names as GraphQL allow rules, but they appear directly under each deny_rules entry instead of under an allow wrapper. On WebSocket GraphQL endpoints, they apply only to classified GraphQL operation messages; protocol lifecycle messages such as connection_init, ping, pong, and complete are allowed as WebSocket control-plane messages and are not payload-logged.

FieldTypeRequiredDescription
operation_typestringYesGraphQL operation type to deny: query, mutation, subscription, or *.
operation_namestringNoGraphQL operation-name glob.
fieldslist of stringNoGraphQL root-field globs. Any matching root field blocks the request. Omit to deny every operation that matches operation_type and operation_name.

Example GraphQL deny rules:

endpoints:
- host: api.github.com
port: 443
protocol: graphql
enforcement: enforce
access: read-write
deny_rules:
- operation_type: mutation
fields: [deleteRepository]
- operation_type: mutation
operation_name: Admin*
JSON-RPC Deny Rule (protocol: json-rpc)

JSON-RPC deny rules use the same field names as JSON-RPC allow rules, but they appear directly under each deny_rules entry instead of under an allow wrapper. Deny rules take precedence over allow rules. In a batch request, one denied call denies the full batch.

FieldTypeRequiredDescription
methodstringYesExact JSON-RPC method name to deny, or * to deny all JSON-RPC methods. Other wildcard or glob patterns are rejected for generic JSON-RPC endpoints.

JSON-RPC deny rules do not support policy params matchers yet. Use MCP tool rules for MCP tool calls, or deny generic JSON-RPC methods by method.

Example JSON-RPC deny rules:

endpoints:
- host: jsonrpc.example.com
port: 443
path: /rpc
protocol: json-rpc
enforcement: enforce
rules:
- allow:
method: "*"
deny_rules:
- method: reports.delete

Binary Object

Identifies an executable that is permitted to use the associated endpoints.

FieldTypeRequiredDescription
pathstringYesFilesystem path to the executable. Supports glob patterns with * and **. For example, /sandbox/.vscode-server/** matches any executable under that directory tree.

Full Example

The following policy grants read-only GitHub API access and npm registry access:

network_policies:
github_rest_api:
name: github-rest-api
endpoints:
- host: api.github.com
port: 443
protocol: rest
enforcement: enforce
access: read-only
binaries:
- path: /usr/local/bin/claude
- path: /usr/bin/node
- path: /usr/bin/gh
npm_registry:
name: npm-registry
endpoints:
- host: registry.npmjs.org
port: 443
protocol: rest
access: read-only
allow_encoded_slash: true
binaries:
- path: /usr/bin/npm
- path: /usr/bin/node