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.
These defaults and the filesystem path restrictions below also apply when the supervisor or standalone proxy loads local policy files directly. Local loading enforces the same non-root process identities as typed policy. It rejects invalid filesystem, Landlock, and process field types, unknown fields in those sections, and explicit null JSON-RPC or MCP options. Network settings must also use the declared types: for example, allow_encoded_slash: "true" and an array-valued deny-rule method are rejected. A rejected reload keeps the active OPA policy and its generation unchanged.
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. Docker and Podman use these fields to select the workload identity from the policy passed at creation; omitted fields use the image’s USER. Kubernetes uses the identity configured for its driver.
MicroVM preserves the driver’s resolved overlay owner. An explicit numeric selector must match that owner; sandbox names the guest account reconciled to that owner. A conflicting selector fails before the workload starts, and omitted fields accept the driver default. openshell sandbox get <name> reports the resolved UID:GID in its WorkloadIdentity condition. This condition reports identity selection, not workload readiness. Sandbox policy updates cannot change process selectors; global policy updates must still match the resolved identity. Ordinary exec uses the same identity as the canonical process.
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.
Network policy never authorizes an outbound endpoint whose destination is
loopback, link-local, or unspecified, including the cloud metadata address
169.254.169.254. This does not prevent a workload from connecting to a
sandbox-local service listening on loopback. openshell policy update rejects
an allowed_ips entry that overlaps the blocked ranges, 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.
- Only an allow rule that names an extension method exactly can allow it.
Method globs and
mcp.allow_all_known_mcp_methodsdo not. An extension method is one that no supported MCP revision defines.
Under the 2025 revisions, a client sends initialize and notifications/initialized before calling tools. When mcp.allow_all_known_mcp_methods is omitted or false, allow both explicitly:
OpenShell uses the tower-mcp-types library to inspect each request under its
MCP revision. It checks the JSON-RPC structure, whether a client can send the
method in that revision, and the parameter types of known methods. It rejects a
body that repeats a key within any JSON object, and a method that MCP defines
but the request’s revision does not, such as server/discover under
2025-11-25. A request that fails these checks returns 400 with the error
invalid_mcp_request, even on an endpoint that uses enforcement: audit. These
type checks are not complete JSON Schema validation.
Allow Core MCP Methods
Set mcp.allow_all_known_mcp_methods: true to allow the core methods available in each request’s selected MCP revision. A policy that accepts several revisions still checks each request against its own revision. The option does not combine their method sets.
Tool-specific allow rules continue to restrict tools/call to matching names, and deny rules take precedence over every allow. Without tool-specific allow rules, this option allows calls to every tool name, subject to deny rules and tool-name validation. Extension methods require an allow rule that names the method exactly. An explicit allow rule cannot enable a core method that is unavailable in the selected revision.
For example, this endpoint allows core methods for 2025-11-25, while limiting tool calls to search_web and read_document:
Here, tools/call is the JSON-RPC method, and search_web is the tool name in params.name. Allowing a method does not establish that the server supports it or that a tool is read-only.
Core MCP Methods
The following list covers client-to-server requests and notifications that OpenShell inspects over HTTP. Server-to-client methods, such as sampling/createMessage, roots/list, and elicitation/create, are outside this inspection. Each row lists the revisions that accept those methods over HTTP in the client-to-server direction.
For 2026-07-28 HTTP requests, cancel a request by closing its response stream. OpenShell rejects notifications/cancelled over HTTP in that revision.
MCP introduced tasks as an experimental feature in 2025-11-25, so the March and June 2025 revisions do not include the task methods or notifications/tasks/status. The default policy revision includes them, but using tasks also requires the peer’s task capabilities and, for task-augmented tool calls, the tool’s task-support declaration. The policy option does not negotiate these capabilities.
MCP Version Selection
mcp.versions lists the MCP revisions an endpoint accepts: 2025-03-26,
2025-06-18, 2025-11-25, or 2026-07-28. When omitted, the list is exactly
["2025-11-25"], so the endpoint does not accept 2026-07-28 until you list
it.
OpenShell selects each request’s revision from its MCP-Protocol-Version
header. When the header is absent, OpenShell selects 2025-03-26, so an
endpoint whose clients omit the header must allow 2025-03-26. A duplicate,
empty, or unsupported header value returns 400, and a supported revision that
the endpoint does not allow returns 403.
OpenShell inspects each request under its selected revision only. For example,
only 2025-03-26 accepts JSON-RPC batches, with at most 64 messages each, and
later revisions reject a request body that is a JSON array.
Under the 2025 revisions, a client starts with a single initialize request
that proposes a revision in its body, so OpenShell does not check that request’s
revision against mcp.versions. An endpoint that allows only 2026-07-28
rejects initialize.
For a client that requires an unsupported revision, omit protocol and mcp to
allow its traffic without MCP inspection.
Understand MCP Rejections
Use the response’s error code and detail to distinguish a protocol problem from a policy denial:
unsupported_mcp_protocol_version(400) means this OpenShell build does not support the header’s revision. Configure the client to use a revision that OpenShell and the server support and thatmcp.versionspermits.mcp_protocol_version_not_allowed(403) means the selected revision is supported but excluded by the endpoint policy. Use a compatible revision the policy permits, or ask the policy owner to reviewmcp.versions.- A detail that names the missing-header fallback means OpenShell selected
2025-03-26, even if the policy defaults to2025-11-25. Send the client/server revision explicitly inMCP-Protocol-Version; the policy must permit it. invalid_mcp_request(400) can mean the method is unavailable in the selected revision. For example,tasks/getis unavailable in2025-06-18. Use a method available in that revision, or configure the client and server to use a compatible revision that includes it and is permitted by policy. Adding an allow rule or enablingallow_all_known_mcp_methodscannot override this protocol check.- A rule denial (
403underenforcement: enforce) requires a policy review. An extension method needs an exact method allow rule; a denied tool call may need a matching tool selector. An explicit deny rule takes precedence, so adding an allow rule cannot override it. Ask the policy owner to review the relevant allow or deny rule.
Sessionless MCP Requests
The 2026-07-28 revision has no initialize handshake or session. Each request
carries the client’s protocol version and capabilities. A client can call
server/discover to learn what the server supports, and can call
subscriptions/listen to receive notifications on that request’s response
stream. Each HTTP POST carries one JSON-RPC request or extension notification.
OpenShell checks each 2026-07-28 request for the following:
params._metacontainsio.modelcontextprotocol/protocolVersionset to2026-07-28, andio.modelcontextprotocol/clientCapabilities. Extension requests need this metadata too.- The
MCP-Protocol-Versionheader is2026-07-28. Without it, OpenShell selects2025-03-26and rejects the request. - The
Mcp-Methodheader equals the JSON-RPCmethod. - For
tools/callandprompts/get, theMcp-Nameheader equalsparams.name. Forresources/read, it equalsparams.uri. OpenShell decodes a=?base64?...?=value before comparing it.
A request with missing or invalid metadata, or with a missing, duplicate, or
mismatched Mcp-Method or Mcp-Name header, returns 400. OpenShell also
rejects initialize, notifications/initialized, notifications/cancelled,
JSON-RPC responses from the client, and batches. A request that uses an HTTP
method other than POST returns 405.
OpenShell repeats these checks after middleware changes a request. A middleware
that replaces the body of a 2026-07-28 request must also update Mcp-Method
and Mcp-Name to match the new body.
An extension notification needs MCP-Protocol-Version: 2026-07-28 and an allow
rule that names its method exactly, such as method: vendor/notice. It does
not need params._meta, Mcp-Method, or Mcp-Name.
OpenShell forwards Mcp-Param-* headers without comparing them with tool
arguments.
JSON-RPC Rules
Parameters are not matched. One denied call denies an entire batched request. OpenShell rejects a body that repeats a key within any JSON object.
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 /.