Policy Schema Reference
This reference defines the sandbox policy schema. A policy is a YAML file of at most 4 MiB. OpenShell rejects a policy that contains unknown fields or duplicate keys.
Top-Level Fields
Startup fields take effect when a sandbox starts. Live fields can change while it runs. Refer to How Changes Take Effect.
Filesystem Policy
When filesystem_policy is omitted, include_workdir is true. When
filesystem_policy is present, include_workdir defaults to false. Paths
that are not listed are inaccessible. When the effective policy has at least one
network rule, OpenShell also adds the baseline paths described in Default
Policy.
Each path must be absolute, must not contain .., and must not exceed 4096
bytes. read_write cannot contain /. A policy can list at most 256 paths.
Landlock
Both values skip individual paths that are missing or that the workload cannot open. They differ when none of the listed paths can be applied, or when Landlock fails to enforce the policy’s rules:
In either mode, a sandbox requires a kernel with Landlock ABI v3 or later. The
sandbox log’s Landlock ruleset built event reports how many paths were applied
and skipped.
Process
A numeric ID must be from 1 through 4294967294, so OpenShell rejects root.
Each field is independent, so you can set one and let the compute driver choose
the other. Only Docker and Podman apply these fields, and only from the policy
that you pass when you create the sandbox. Without them, Docker and Podman use
the image’s USER. Kubernetes and VM sandboxes run as the identity configured
for their driver.
Network Policies
A map of named rules. The key is the rule’s name. Each rule allows every listed binary to reach every listed endpoint. For how OpenShell evaluates rules, and for examples, refer to Network Rules.
Rule keys cannot start with _provider_, which OpenShell reserves for rules
that providers contribute.
Endpoint Object
Endpoint fields fall into four groups.
Destination Fields
A wildcard host must have at least three DNS labels. * can appear within the
first label, such as *-api.example.com, or as a whole later label, such as
api.*.example.com. ** is allowed only as the whole first label.
An endpoint can omit host only when it sets allowed_ips. Exact hostnames can
reach the private addresses they resolve to, unless the endpoint comes from an
approved policy advisor proposal. Wildcard and hostless endpoints can reach
private addresses only through allowed_ips.
Loopback, link-local, and unspecified addresses, including the cloud metadata
address 169.254.169.254, are always blocked. openshell policy update rejects
an allowed_ips entry that overlaps them, and in a complete policy such an
entry blocks every connection to the endpoint. OpenShell also blocks the
Kubernetes and etcd control-plane ports 2379, 2380, 6443, 10250, and 10255 on
endpoints that use an exact hostname, an IP address, or allowed_ips. A rule
for host.openshell.internal can still reach services on the gateway host.
Inspection Fields
Credential Fields
Credential placeholders use the form openshell:resolve:env:KEY. Body rewriting
applies to UTF-8 JSON, form, and text bodies of up to 256 KiB, and cannot be
combined with credential_signing. Signing requires an attached provider with
AWS credentials. Refer to AWS SigV4 and Static
Credential Endpoint
Binding.
Protocol Options
Endpoint Constraints
OpenShell rejects an endpoint that breaks these rules:
accessandrulescannot be combined.rest,websocket, andgraphqlendpoints need one of them.mcpandjson-rpcendpoints needrules, unless an MCP endpoint setsmcp.allow_all_known_mcp_methods: true.deny_rulesrequireprotocol, and requirerulesoraccesson endpoints other than MCP.protocol: tcprequires a hostname and a port. It accepts no request fields, such aspath,enforcement,access,rules, credential rewriting or signing, or protocol options.- Endpoints whose hosts can match the same name on the same port, including
through wildcards, must use the same
tlsandallowed_ipsvalues. Inspected endpoints that can match the same request must also agree onprotocol,enforcement, and credential settings.
Without protocol, access and rules have no effect.
Access Presets
REST, WebSocket, and GraphQL endpoints accept these presets. MCP and JSON-RPC endpoints do not.
Allow and Deny Rules
Each entry in rules wraps its matcher fields in allow. Each entry in
deny_rules lists the matcher fields directly. With enforcement: enforce, a
request that matches any deny rule is blocked, regardless of the allow rules or
access preset.
The matcher fields depend on the endpoint’s protocol.
REST Rules
In an allow rule, every value of a repeated query parameter must match. In a deny rule, every configured parameter must be present, and one matching value per parameter is enough.
WebSocket Rules
OpenShell does not inspect binary frames or messages from the server.
GraphQL Rules
In an allow rule, every top-level field must match. In a deny rule, one matching
field is enough, and omitting fields denies every matching operation. One
denied operation denies an entire batched request.
For GraphQL over WebSocket, use protocol: websocket with a GET rule for the
upgrade and GraphQL rules for operations. Do not mix WebSocket and GraphQL
matcher fields in one rule.
MCP Rules
- Rules with
toolorparams.namemust setmethod: tools/call, unlessmcp.allow_all_known_mcp_methodsistrue. - A rule that matches all of
tools/callcannot be combined with tool-specific allow rules. - Wildcard
toolmatchers requiremcp.strict_tool_names: true. - Tool arguments are not matched, so an allowed tool accepts any arguments.
- One denied call denies an entire batched request.
- Server responses and server-to-client messages are not inspected.
A client sends initialize and notifications/initialized before calling
tools, so allow both:
MCP Version Selection
mcp.versions lists the MCP revisions an endpoint accepts: 2025-03-26,
2025-06-18, or 2025-11-25. When omitted, only 2025-11-25 is allowed.
OpenShell does not check the revision of a single initialize request, because
the client negotiates the revision in that request. For other requests,
OpenShell reads the revision from the MCP-Protocol-Version header, or uses
2025-03-26 when the header is absent. A duplicate, empty, or
unsupported header value returns 400, and a supported revision that the
endpoint does not allow returns 403. For a client that requires an unsupported
revision, omit protocol and mcp to allow its traffic without MCP inspection.
JSON-RPC Rules
Parameters are not matched. One denied call denies an entire batched request.
Binary Object
A binary matches the executable that opens the connection or any of its parent
processes. Scripts run as their interpreter, so list the interpreter, such as
/usr/bin/python3.12, for a Python script. List the executable’s real path, not
a symlink to it. OpenShell records a hash of each executable the first time it
takes part in a connection, and denies later connections if the file changes.
Refer to Binary
Matching.
Network Middleware
A map of up to 10 middleware configurations. Middleware runs on traffic that
network rules allow, in ascending order.
Host patterns match the same way as endpoint hosts, up to 32 patterns per
configuration. A fail_closed configuration cannot apply to an endpoint with
tls: skip. Refer to Supervisor
Middleware.
Matcher Semantics
Glob patterns follow one set of rules. Each matcher splits values at a separator:
*matches any characters except the separator.**matches across separators only when it is a whole segment, as in/repos/**,**.example.com, orgithub.**. Next to other characters, as in**secret**, it behaves like*. A whole-segment**needs at least one segment, so/repos/**does not match/repos.?matches one character except the separator, and bracket classes such as[0-9]match one character from a set. Endpoint hosts accept only*and**, as described in Destination Fields.
Because query values and MCP tool names use . as the separator, * does not
match a value that contains a dot. For example, 1.* matches 1.2 but not
1.2.3, and github.* matches github.search but not github.search.code.
Use ** to match any value.
The endpoint path field, which selects among endpoints on the same host and
port, uses different rules. An empty path, **, or /** matches every path,
/v1/** matches /v1 and every path under it, and in other patterns * also
matches /.