Policy Schema Reference

View as Markdown

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

version: 1
filesystem_policy: { ... }
landlock: { ... }
process: { ... }
network_policies: { ... }
network_middlewares: { ... }
FieldTypeRequiredTakes effectDescription
versionintegerYes—Schema version. Must be 1.
filesystem_policyobjectNoStartupPaths the workload can read and write.
landlockobjectNoStartupLandlock enforcement behavior.
processobjectNoStartupUser and group that the workload runs as.
network_policiesmapNoLiveWhich binaries can reach which network endpoints.
network_middlewaresmapNoLiveMiddleware applied to allowed traffic.

Startup fields take effect when a sandbox starts. Live fields can change while it runs. Refer to How Changes Take Effect.

Filesystem Policy

FieldTypeDefaultDescription
include_workdirboolSee belowAdds the sandbox’s working directory to read_write.
read_onlylist of strings[]Paths the workload can read.
read_writelist of strings[]Paths the workload can read and write.

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.

filesystem_policy:
include_workdir: true
read_only: [/usr, /lib, /etc]
read_write: [/tmp]

Landlock

FieldTypeDefaultValues
compatibilitystringbest_effortbest_effort or hard_requirement

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:

ValueBehavior
best_effortThe sandbox runs without the filesystem rules and logs a high-severity finding.
hard_requirementThe sandbox fails to start.

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

FieldTypeDefaultDescription
run_as_userstringDriver defaultsandbox or a numeric UID for the workload.
run_as_groupstringDriver defaultsandbox or a numeric GID for the workload.

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.

process:
run_as_user: "1500"
run_as_group: "1500"

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.

FieldTypeRequiredDescription
namestringNoDisplay name in logs. Defaults to the key.
endpointslist of endpoint objectsNoDestinations the rule allows.
binarieslist of binary objectsNoExecutables the rule applies to. An empty list matches no binary.

Rule keys cannot start with _provider_, which OpenShell reserves for rules that providers contribute.

Endpoint Object

Endpoint fields fall into four groups.

Destination Fields

FieldTypeRequiredDescription
hoststringConditionalHostname, IP address, or wildcard pattern, such as *.example.com.
portintegerConditionalTCP port. Set port or ports.
portslist of integersConditionalTCP ports. Use instead of port, not with it.
pathstringNoPath glob that selects among inspected endpoints on the same host and port. The most specific match wins.
allowed_ipslist of stringsNoIP addresses or CIDR ranges that resolved addresses must fall within.

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

FieldTypeDefaultDescription
protocolstringNonerest, websocket, graphql, mcp, or json-rpc for request inspection, or tcp for a native TCP connection. Refer to Connection and Request Checks.
tlsstringAutomaticskip relays traffic without terminating TLS, so OpenShell cannot inspect it. Do not use it with a request protocol.
enforcementstringauditenforce blocks requests that break the endpoint’s rules. audit logs them and allows the request.
accessstringNoneAccess preset: read-only, read-write, or full. Refer to Access Presets.
ruleslistNoneAllow rules.
deny_ruleslistNoneDeny rules, which take precedence over allow rules.
allow_encoded_slashboolfalseAccepts %2F in request paths, as used by npm scoped packages.

Credential Fields

FieldTypeDefaultDescription
credential_binding.providerstringNoneBinds the static credentials of an attached provider whose profile defines no endpoints. Valid only in a sandbox policy.
request_body_credential_rewriteboolfalseReplaces credential placeholders in REST request bodies.
websocket_credential_rewriteboolfalseReplaces credential placeholders in client WebSocket text messages.
allow_uninspected_credentialsboolfalseAllows a provider-credentialed endpoint to omit request inspection or use tls: skip.
credential_signingstringNoneAWS request signing: sigv4, sigv4:body, or sigv4:no_body.
signing_servicestringNoneAWS service name, such as bedrock or s3. Required with credential_signing.
signing_regionstringFrom hostnameAWS region override, such as us-east-1.

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.

network_policies:
gcp_storage:
endpoints:
- host: storage.googleapis.com
port: 443
protocol: rest
access: full
credential_binding:
provider: work-gcp
binaries:
- path: /usr/bin/curl

Protocol Options

FieldTypeDefaultDescription
persisted_queriesstringdenyGraphQL hash-only queries: deny or allow_registered.
graphql_persisted_queriesmapNoneTrusted persisted-query registry, keyed by hash or saved-query ID. With allow_registered, OpenShell denies hash-only queries that are not in the registry.
graphql_max_body_bytesinteger65536Maximum GraphQL request body size for inspection.
mcp.versionslist of strings["2025-11-25"]Allowed MCP revisions. Refer to MCP Version Selection.
mcp.max_body_bytesinteger65536Maximum MCP request body size for inspection.
mcp.strict_tool_namesbooltrueRequires tool names to match ^[A-Za-z0-9_.-]{1,128}$.
mcp.allow_all_known_mcp_methodsboolfalseWhen true, the endpoint allows every MCP method except those that deny rules match. If rules name specific tools, tools/call is limited to those tools. Rules can omit method. Refer to MCP Rules.
json_rpc.max_body_bytesinteger65536Maximum JSON-RPC request body size for inspection.

Endpoint Constraints

OpenShell rejects an endpoint that breaks these rules:

  • access and rules cannot be combined. rest, websocket, and graphql endpoints need one of them. mcp and json-rpc endpoints need rules, unless an MCP endpoint sets mcp.allow_all_known_mcp_methods: true.
  • deny_rules require protocol, and require rules or access on endpoints other than MCP.
  • protocol: tcp requires a hostname and a port. It accepts no request fields, such as path, 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 tls and allowed_ips values. Inspected endpoints that can match the same request must also agree on protocol, 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.

ValueRESTWebSocketGraphQL
fullAll methods and paths.Upgrade and all client text messages.All operations.
read-onlyGET, HEAD, OPTIONS.Upgrade only.query operations.
read-writeGET, HEAD, OPTIONS, POST, PUT, PATCH.Upgrade and client text messages.query and mutation operations.

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.

rules:
- allow:
method: GET
path: /repos/**
deny_rules:
- method: GET
path: /repos/private/**

The matcher fields depend on the endpoint’s protocol.

REST Rules

FieldTypeRequiredDescription
methodstringYesHTTP method, or * for any method.
pathstringYesPath glob. Refer to Matcher Semantics.
querymapNoQuery parameter matchers, keyed by parameter name. Each value is a glob or { any: [globs] }.

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.

rules:
- allow:
method: GET
path: /api/v1/download
query:
platform:
any: ["linux-*", "darwin-*"]
deny_rules:
- method: "*"
path: "/repos/*/*/rulesets"

WebSocket Rules

FieldTypeRequiredDescription
methodstringYesGET for the upgrade request, WEBSOCKET_TEXT for client text messages, or * for both.
pathstringYesPath glob of the upgrade request, not message content.
querymapNoQuery parameter matchers of the upgrade request.

OpenShell does not inspect binary frames or messages from the server.

rules:
- allow:
method: GET
path: /v1/realtime
- allow:
method: WEBSOCKET_TEXT
path: /v1/realtime
deny_rules:
- method: WEBSOCKET_TEXT
path: /v1/admin/**

GraphQL Rules

FieldTypeRequiredDescription
operation_typestringYesquery, mutation, or subscription. To allow every operation type, use access: full.
operation_namestringNoOperation name glob.
fieldslist of stringsNoTop-level field globs.

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.

rules:
- allow:
operation_type: query
- allow:
operation_type: mutation
fields: [createIssue]
deny_rules:
- operation_type: mutation
fields: [deleteRepository]

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

FieldTypeRequiredDescription
methodstringConditionalMCP method, such as initialize or tools/call. Globs are allowed only in the tools/ family, and * is not allowed. Required unless mcp.allow_all_known_mcp_methods is true.
toolstring or { any: [globs] }NoTool name matcher for tools/call.
params.namestring or { any: [globs] }NoLower-level equivalent of tool.
  • Rules with tool or params.name must set method: tools/call, unless mcp.allow_all_known_mcp_methods is true.
  • A rule that matches all of tools/call cannot be combined with tool-specific allow rules.
  • Wildcard tool matchers require mcp.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:

rules:
- allow:
method: initialize
- allow:
method: notifications/initialized
- allow:
method: tools/call
tool:
any: [search_web, list_issues]
deny_rules:
- method: tools/call
tool: send_email

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.

mcp:
versions: ["2025-03-26", "2025-11-25"]

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

FieldTypeRequiredDescription
methodstringYesExact method name, or * for all methods. Other globs are rejected.

Parameters are not matched. One denied call denies an entire batched request.

rules:
- allow:
method: reports.search
deny_rules:
- method: reports.delete

Binary Object

FieldTypeRequiredDescription
pathstringYesExecutable path or glob, such as /usr/bin/curl or /usr/lib/jvm/*/bin/java.

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.

FieldTypeRequiredDescription
middlewarestringYesBuilt-in middleware, such as openshell/regex, or the name of a service registered with the gateway.
endpoints.includelist of stringsYesHost patterns the middleware applies to.
endpoints.excludelist of stringsNoHost patterns to skip. Takes precedence over include.
orderintegerNoRun order. Lower values run first. Values must be unique. Defaults to 0.
configobjectNoConfiguration for the middleware.
on_errorstringNofail_closed blocks traffic when the middleware fails. fail_open skips it. Defaults to fail_closed.
namestringNoDisplay name. Defaults to the key.

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.

network_middlewares:
regex-redactor:
middleware: openshell/regex
order: 10
config:
mode: redact
on_error: fail_closed
endpoints:
include: ["*.example.com"]
exclude: ["trusted.example.com"]

Matcher Semantics

Glob patterns follow one set of rules. Each matcher splits values at a separator:

MatcherSeparatorCase-sensitive
Endpoint host and middleware hosts.No
Binary path/Yes
REST and WebSocket rule path/Yes
Query values and MCP tool names.Yes
  • * matches any characters except the separator.
  • ** matches across separators only when it is a whole segment, as in /repos/**, **.example.com, or github.**. 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 /.

Full Example

version: 1
filesystem_policy:
include_workdir: true
read_only: [/usr, /lib, /etc]
read_write: [/tmp]
network_policies:
github_rest_api:
endpoints:
- host: api.github.com
port: 443
protocol: rest
enforcement: enforce
access: read-only
binaries:
- path: /usr/bin/gh
npm_registry:
endpoints:
- host: registry.npmjs.org
port: 443
protocol: rest
enforcement: enforce
access: read-only
allow_encoded_slash: true
binaries:
- path: /usr/bin/node