> For clean Markdown of any page, append .md to the page URL.
> For a complete documentation index, see https://docs.nvidia.com/openshell/llms.txt.
> For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://docs.nvidia.com/openshell/_mcp/server.

# Policy Schema Reference

> Complete field reference for the sandbox policy YAML including static and dynamic sections.

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:

```yaml showLineNumbers={false}
version: 1
filesystem_policy: { ... }
landlock: { ... }
process: { ... }
network_policies: { ... }
network_middlewares: { ... }
```

| Field                 | Type    | Required | Category | Description                                                                                                                          |
| --------------------- | ------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `version`             | integer | Yes      | --       | Policy schema version. Must be `1`.                                                                                                  |
| `filesystem_policy`   | object  | No       | Static   | Controls which directories the agent can read and write.                                                                             |
| `landlock`            | object  | No       | Static   | Configures Landlock LSM enforcement behavior.                                                                                        |
| `process`             | object  | No       | Static   | Sets the user and group the agent process runs as.                                                                                   |
| `network_policies`    | map     | No       | Dynamic  | Declares which binaries can reach which network endpoints.                                                                           |
| `network_middlewares` | map     | No       | Dynamic  | Attaches ordered middleware by destination host; each implementation's manifest selects its supported HTTP and WebSocket operations. |

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:

| Field     | Type    | Required | Description                                   |
| --------- | ------- | -------- | --------------------------------------------- |
| `version` | integer | Yes      | Schema 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.

| Field             | Type            | Required | Description                                                                                                                     |
| ----------------- | --------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
| `include_workdir` | bool            | No       | When `true`, automatically adds the agent's working directory to `read_write`.                                                  |
| `read_only`       | list of strings | No       | Paths the agent can read but not modify. Typically system directories like `/usr`, `/lib`, `/etc`.                              |
| `read_write`      | list of strings | No       | Paths the agent can read and write. Typically `/tmp`; set `include_workdir: true` to add the driver-resolved working directory. |

**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:

```yaml showLineNumbers={false}
filesystem_policy:
  include_workdir: true
  read_only:
    - /usr
    - /lib
    - /proc
    - /dev/urandom
    - /etc
  read_write:
    - /tmp
    - /dev/null
```

## Landlock

**Category:** Static

Configures [Landlock LSM](https://docs.kernel.org/security/landlock.html) enforcement at the kernel level. Landlock provides mandatory filesystem access control below what UNIX permissions allow.

| Field           | Type   | Required | Values                            | Description                                                                 |
| --------------- | ------ | -------- | --------------------------------- | --------------------------------------------------------------------------- |
| `compatibility` | string | No       | `best_effort`, `hard_requirement` | How OpenShell handles Landlock failures. Refer to the behavior table below. |

**Compatibility modes:**

| Value              | No paths configured       | Kernel ABI unavailable                | Individual path inaccessible                                                                                     | All paths inaccessible                                                    |
| ------------------ | ------------------------- | ------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------- |
| `best_effort`      | Landlock skipped (no-op). | Warns and continues without Landlock. | Skips the path, applies remaining rules.                                                                         | Warns and continues without Landlock (refuses to apply an empty ruleset). |
| `hard_requirement` | Aborts sandbox startup.   | Aborts sandbox startup.               | Aborts sandbox startup, except in Kubernetes sidecar (current-user) mode where the inaccessible path is skipped. | 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. Configuring `hard_requirement` with no filesystem paths is also a startup error.

In Kubernetes sidecar (current-user) mode the sandbox cannot distinguish an intentionally denied path from a misconfigured one, so an individual inaccessible path is skipped and the remaining rules are applied instead of aborting. The other `hard_requirement` failures (kernel ABI unavailable, no paths configured, all paths inaccessible) still abort startup.

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:

```yaml showLineNumbers={false}
landlock:
  compatibility: best_effort
```

## Process

**Category:** Static

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

| Field          | Type   | Required | Description                                                                                                            |
| -------------- | ------ | -------- | ---------------------------------------------------------------------------------------------------------------------- |
| `run_as_user`  | string | No       | Overrides the user name or UID selected by the compute driver. Docker and Podman fall back to the image's OCI `USER`.  |
| `run_as_group` | string | No       | Overrides the group name or GID selected by the compute driver. Docker and Podman fall back to the image's OCI `USER`. |

**Validation constraint:** An explicit policy value must be `sandbox` or a
numeric UID/GID from `1` through `4294967294`. OpenShell rejects `0` as root
and `4294967295` as the invalid identity sentinel. Docker and Podman may select
other named identities through OCI `USER` fallback.

Omission is preserved independently for each field. For example, setting only
`run_as_user` keeps that explicit user while allowing the active driver to
select the group.

Example:

```yaml showLineNumbers={false}
process:
  run_as_user: "1500"
  run_as_group: "1500"
```

## 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:

| Field       | Type                     | Required | Description                                                                     |
| ----------- | ------------------------ | -------- | ------------------------------------------------------------------------------- |
| `name`      | string                   | No       | Display name for the policy entry. Used in log output. Defaults to the map key. |
| `endpoints` | list of endpoint objects | Yes      | Hosts and ports this entry permits.                                             |
| `binaries`  | list of binary objects   | Yes      | Executables allowed to connect to these endpoints.                              |

### Endpoint Object

Each endpoint defines a reachable destination and optional inspection rules.

| Field                             | Type                       | Required                      | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| --------------------------------- | -------------------------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `host`                            | string                     | Conditional                   | Hostname or IP address. Required for `protocol: tcp`; transparent TCP requires a valid DNS hostname and rejects literal IPs. A non-TCP proxy endpoint may omit `host` only when `allowed_ips` supplies the destination constraint. 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. Prefer exact hosts for `protocol: tcp`: a wildcard authorizes DNS queries for all matching names and can provide a DNS-label exfiltration channel.                                                                                                                                                                                                                                                                                                                                                                                    |
| `port`                            | integer                    | Yes                           | TCP port number.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `path`                            | string                     | No                            | Optional 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`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `protocol`                        | string                     | No                            | Set to `tcp` with a valid DNS hostname to allow native TCP clients through policy DNS and transparent capture without payload inspection. Omit the field for L4 passthrough through an explicit proxy, including legacy hostless `allowed_ips` endpoints. Set 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. Provider-credentialed endpoints require an inspected protocol unless `allow_uninspected_credentials` is explicitly set.                                                                                                                                                                                                                                                                              |
| `tls`                             | string                     | No                            | TLS 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. Provider-credentialed endpoints reject `tls: skip` unless `allow_uninspected_credentials` is explicitly set. The values `terminate` and `passthrough` are deprecated and log a warning; they are still accepted for backward compatibility but have no effect on behavior.                                                                                                                                                                                                                                                                                                                                                                                                                                                                        |
| `enforcement`                     | string                     | No                            | `enforce` actively blocks disallowed requests. `audit` logs violations but allows traffic through.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |
| `access`                          | string                     | No                            | Access 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.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |
| `rules`                           | list of allow rule objects | No                            | Fine-grained protocol-specific allow rules. Mutually exclusive with `access`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                           |
| `deny_rules`                      | list of deny rule objects  | No                            | L7 deny rules that block specific requests even when allowed by `access` or `rules`. Deny rules take precedence over allow rules.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `allowed_ips`                     | list of string             | No                            | CIDR 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. A hostless allowlist is valid only for the legacy proxy path and cannot be combined with `protocol: tcp`. 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_slash`             | bool                       | No                            | When `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_rewrite`    | bool                       | No                            | When `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. On provider-credentialed endpoints without `allow_uninspected_credentials`, OpenShell uses the parsed relay and rejects binary frames; text frames containing placeholders fail closed when rewrite is disabled. Defaults to `false`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
| `request_body_credential_rewrite` | bool                       | No                            | When `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. For chunked requests, the limit counts framing, extensions, and trailers. When rewrite is disabled and the sandbox has provider credentials, bodies continue to stream. Authoritatively unknown placeholder keys and valid issued credentials pass unchanged, including credentials bound to the destination. Invalid or unavailable credentials, and unavailable classification metadata fail closed with `credential_placeholder_in_request_body` (HTTP 403). Candidates are limited to 4096 wire bytes. No secret is substituted. Defaults to `false`. Mutually exclusive with `credential_signing`.                                                                                                                                                |
| `allow_uninspected_credentials`   | bool                       | No                            | Explicit security-sensitive opt-in that permits a provider-credentialed endpoint to use traffic paths OpenShell cannot inspect or rewrite, including L4-only and `tls: skip` tunnels. Defaults to `false`. Policy proposals that set it require explicit security-flagged approval.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |
| `credential_signing`              | string                     | No                            | Proxy-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](/providers/aws-sigv4).                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `signing_service`                 | string                     | No                            | AWS service name for SigV4 signing (e.g. `bedrock`, `s3`, `sts`). Required when `credential_signing` is set.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            |
| `signing_region`                  | string                     | No                            | AWS 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.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                |
| `credential_binding`              | object                     | No                            | Binds static credentials from an attached provider to this endpoint when that provider's profile defines no endpoints. This field is valid only in a sandbox-scoped policy.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |
| `credential_binding.provider`     | string                     | Yes with `credential_binding` | Exact name of the provider instance attached to the sandbox. The referenced provider must have a profile, and that profile must define no endpoints.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |
| `persisted_queries`               | string                     | No                            | GraphQL 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_queries`       | map                        | No                            | Trusted 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_bytes`          | integer                    | No                            | Maximum GraphQL-over-HTTP request body bytes buffered for inspection. Defaults to `65536`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                              |
| `mcp`                             | object                     | No                            | MCP endpoint options for `protocol: mcp`. Omit this key to use all MCP endpoint defaults, including the exact `2025-11-25` revision; `mcp: null` is invalid. The object is rejected on other protocols. Every MCP endpoint must still set a concrete `host` and `port` or `ports`; an entry containing only `protocol: mcp` is invalid and is not treated as a wildcard endpoint.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                       |
| `mcp.versions`                    | list of string             | No                            | Nonempty allowlist of exact supported MCP core revisions: `2025-03-26`, `2025-06-18`, and `2025-11-25`. Omission resolves to the exact allowlist `["2025-11-25"]`; it never means latest or all known revisions. The key must be absent to use this default; `versions: null` and `versions: []` are invalid. Values must be unique, contain no extra whitespace, and name a revision in the closed supported set. For every request except a valid standalone `initialize`, OpenShell selects the revision from one `MCP-Protocol-Version` header or, when the header is absent, the MCP specification's `2025-03-26` compatibility fallback. The selected revision must appear in this allowlist. Duplicate, empty, or unsupported header values receive `400 Bad Request`; a supported revision outside the allowlist receives `403 Forbidden`. The sessionless `2026-07-28` revision is not yet supported. For an unsupported revision, omit `protocol` and `mcp` only when deliberate uninspected L4 passthrough is an acceptable weaker boundary. |
| `mcp.max_body_bytes`              | integer                    | No                            | Maximum MCP JSON-RPC-over-HTTP request body bytes buffered for inspection. Defaults to `65536`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         |
| `mcp.strict_tool_names`           | bool                       | No                            | Defaults 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_methods` | bool                       | No                            | Defaults 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_rpc`                        | object                     | No                            | JSON-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`.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                     |

**Validation constraints:**

* `access` and `rules` are mutually exclusive; setting both is rejected.
* `protocol: tcp` requires a valid DNS hostname. Hostless `allowed_ips`, IP-literal hosts, trailing-dot names, and malformed DNS selectors are rejected with a policy-validation error.
* `protocol: tcp` requires at least one port and rejects L7-only fields, including `path`, `enforcement`, `access`, `rules`, `deny_rules`, request rewriting and credential signing fields, and GraphQL, JSON-RPC, or MCP options.
* A `protocol: tcp` hostname constrains connection routing, not application authority. OpenShell does not inspect TLS SNI, HTTP `Host`, or another protocol-level destination in the stream. Compatible shared infrastructure can therefore expose other tenants, virtual hosts, or services behind an allowed hostname.
* A sandbox runtime must support policy DNS and transparent TCP capture before it can activate a policy containing `protocol: tcp`. Docker and Podman provide this runtime support.
* Adding the first `protocol: tcp` endpoint to a running sandbox that started without one is rejected atomically because its DNS and capture substrate is startup infrastructure. Recreate the sandbox with a TCP endpoint. A sandbox that started with the substrate can remove and re-add TCP endpoints dynamically.
* Policy-advisor agent proposals cannot request `protocol: tcp` or `tls: skip`. Add native TCP or raw TLS access through an administrator-authored policy. Agent proposals may omit `protocol` to use the explicit proxy with its default TLS termination and HTTP authority checks.
* When `protocol` is set, at least one of `access` or `rules` is required for `rest`, `websocket`, `graphql`, and `sql`.
* `mcp` and `json-rpc` reject `access` presets; use explicit `rules`.
* `json-rpc` requires explicit `rules` with `allow.method`.
* `mcp` requires `rules` unless `mcp.allow_all_known_mcp_methods: true`.
* YAML defaulting requires the `mcp` or `mcp.versions` key to be absent. Explicit `mcp: null`, `versions: null`, and `versions: []` values are rejected. At protobuf ingress, an empty repeated `versions` field means omission and resolves to `["2025-11-25"]` because protobuf repeated fields do not preserve field presence.
* `deny_rules` require `protocol`. For non-MCP protocols, `deny_rules` also require `rules` or `access` to define the base allow set. MCP `deny_rules` may omit both when `mcp.allow_all_known_mcp_methods: true` supplies the base allow set.
* `rules: []` (empty list) is rejected; use `access: full` or remove `rules`.
* Non-empty `rules` must contain at least one effective allow clause; rules where every entry lacks an allow are rejected as deny-all.
* `deny_rules: []` (empty list) is rejected; remove it if no denials are needed.
* `credential_signing` requires a resolvable AWS credential source before a sandbox policy can activate. Use an attached endpoint-bearing profile that declares `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` and covers the signed endpoint, or bind an attached endpointless profile that declares those keys with `credential_binding.provider`.

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.

Static provider placeholders also require the request host, port, and path to
match their credential binding. Profile endpoints supply this boundary by
default. An endpointless profile can instead use a sandbox policy endpoint with
`credential_binding.provider` set to the exact attached provider name. OpenShell
rejects unattached providers, profileless providers, endpointful profiles, and
global policies that use this field. Network policy admission does not expand
the credential boundary unless the endpoint explicitly supplies this binding.
OpenShell rejects a request mismatch with HTTP 403 and
`credential_endpoint_mismatch`. Refer to [Static Credential Endpoint Binding](/providers/profiles#understand-static-credential-endpoint-binding).

This example allows the sandbox to reach Google Cloud Storage and binds the
static credentials from the attached `work-gcp` provider to that endpoint:

```yaml showLineNumbers={false}
network_policies:
  gcp_storage:
    endpoints:
      - host: storage.googleapis.com
        port: 443
        protocol: rest
        access: full
        credential_binding:
          provider: work-gcp
```

#### 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.

| Value        | REST expansion                                    | WebSocket expansion                                            | GraphQL expansion                  | MCP / JSON-RPC expansion |
| ------------ | ------------------------------------------------- | -------------------------------------------------------------- | ---------------------------------- | ------------------------ |
| `full`       | All methods and paths.                            | WebSocket upgrade and all inspected client text-message paths. | All operation types.               | Rejected.                |
| `read-only`  | `GET`, `HEAD`, `OPTIONS`.                         | WebSocket upgrade handshake only.                              | `query` operations.                | Rejected.                |
| `read-write` | `GET`, `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.

| Field    | Type   | Required | Description                                                                                                                                                             |
| -------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `method` | string | Yes      | HTTP method to allow (for example, `GET`, `POST`). `*` matches any method.                                                                                              |
| `path`   | string | Yes      | URL path glob. `*` and `**` match zero or more characters and may cross `/`; `?` matches one character; bracket classes such as `[0-9]` and `[!0]` are supported.       |
| `query`  | map    | No       | Query 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:

```yaml showLineNumbers={false}
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.

| Field    | Type   | Required | Description                                                                                                                             |
| -------- | ------ | -------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| `method` | string | Yes      | `GET` allows the upgrade handshake, `WEBSOCKET_TEXT` allows client text messages after upgrade, and `*` matches both inspected actions. |
| `path`   | string | Yes      | URL path pattern from the original upgrade request. Supports `*` and `**` glob syntax.                                                  |
| `query`  | map    | No       | Query parameter matchers from the original upgrade request. Matcher syntax is the same as REST allow rules.                             |

Example WebSocket allow rules:

```yaml showLineNumbers={false}
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.

| Field            | Type           | Required | Description                                                                                                        |
| ---------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------ |
| `operation_type` | string         | Yes      | GraphQL operation type: `query`, `mutation`, `subscription`, or `*`.                                               |
| `operation_name` | string         | No       | GraphQL operation-name glob. Omit to match any operation name.                                                     |
| `fields`         | list of string | No       | GraphQL root-field globs. Every selected root field must match one configured glob. Omit to match all root fields. |

Example GraphQL allow rules:

```yaml showLineNumbers={false}
rules:
  - allow:
      operation_type: query
      fields: [viewer, repository]
  - allow:
      operation_type: mutation
      operation_name: Issue*
      fields: [createIssue]
```

Example GraphQL-over-WebSocket allow rules:

```yaml showLineNumbers={false}
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. An endpoint that omits `mcp.versions`, including one that omits the entire `mcp` object, immediately resolves to the exact `2025-11-25` allowlist. Canonical serialization and stored policy data contain that explicit materialized list, so adding another supported revision cannot widen the normalized policy. For every request except a valid standalone `initialize`, OpenShell checks exactly one `MCP-Protocol-Version` value against the allowlist before policy evaluation and repeats the check after middleware changes the request. An absent header selects the `2025-03-26` compatibility fallback; it does not select the policy default. The check stores no connection or session state. The current parser remains version-independent and does not yet apply the batch rules recorded in each revision's wire profile. `mcp.allow_all_known_mcp_methods` defaults to `false`, so endpoints require explicit MCP method rules. Set it to `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.

| Field    | Type              | Required | Description                                                                                                                                                                                                                                                                                                                                                                                                             |
| -------- | ----------------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `method` | string            | No       | MCP 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. |
| `tool`   | string or matcher | No       | Convenience 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.                                                                                                                                                                                 |
| `params` | map               | No       | MCP 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.

```yaml showLineNumbers={false}
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
```

This example omits `mcp.versions`, so OpenShell materializes `["2025-11-25"]`. To authorize an intentional compatibility range for an older server, set an explicit nonempty allowlist:

```yaml showLineNumbers={false}
mcp:
  versions: ["2025-03-26", "2025-11-25"]
```

OpenShell canonicalizes an explicit list in semantic order. Later runtime negotiation selects one allowed revision and applies only that profile; it does not combine the profiles.

##### 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.

| Field    | Type   | Required | Description                                                                                                                                                                               |
| -------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `method` | string | Yes      | Exact 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:

```yaml showLineNumbers={false}
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.

| Field    | Type   | Required | Description                                                                    |
| -------- | ------ | -------- | ------------------------------------------------------------------------------ |
| `method` | string | Yes      | HTTP method to deny (for example, `POST`, `DELETE`). `*` matches any method.   |
| `path`   | string | Yes      | URL path pattern. Same glob syntax as allow rules. Use `**` to match any path. |
| `query`  | map    | No       | Query parameter matchers. Same syntax as allow rule `query`.                   |

Example REST deny rules:

```yaml showLineNumbers={false}
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.

| Field    | Type   | Required | Description                                                                                                                                            |
| -------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `method` | string | Yes      | `GET` denies matching upgrade handshakes, `WEBSOCKET_TEXT` denies matching client text messages after upgrade, and `*` matches both inspected actions. |
| `path`   | string | Yes      | URL path pattern from the original upgrade request. Same glob syntax as allow rules.                                                                   |
| `query`  | map    | No       | Query parameter matchers from the original upgrade request. Same syntax as allow rule `query`.                                                         |

Example WebSocket deny rules:

```yaml showLineNumbers={false}
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.

| Field            | Type           | Required | Description                                                                                                                                            |
| ---------------- | -------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `operation_type` | string         | Yes      | GraphQL operation type to deny: `query`, `mutation`, `subscription`, or `*`.                                                                           |
| `operation_name` | string         | No       | GraphQL operation-name glob.                                                                                                                           |
| `fields`         | list of string | No       | GraphQL 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:

```yaml showLineNumbers={false}
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.

| Field    | Type   | Required | Description                                                                                                                                           |
| -------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
| `method` | string | Yes      | Exact 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:

```yaml showLineNumbers={false}
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.

| Field  | Type   | Required | Description                                                                                                                                                              |
| ------ | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `path` | string | Yes      | Filesystem path to the executable. Supports glob patterns with `*` and `**`. For example, `/sandbox/.vscode-server/**` matches any executable under that directory tree. |

## Network Middleware

**Category:** Dynamic

A map of up to 10 middleware configs selected after network and L7 policy admit an HTTP request or WebSocket upgrade. Each map key is the stable policy-local config identity. Middleware selection is independent of the network policy entry that admitted the traffic. Every matching config runs once by ascending `order` before provider credential injection. WebSocket-capable bindings continue on client text messages after the upgrade. Order values must be unique across the policy, and runtime selection also enforces the 10-stage maximum.

```yaml showLineNumbers={false}
network_middlewares:
  regex-redactor:
    name: Redact API tokens
    middleware: openshell/regex
    order: 10
    config:
      mode: redact
    on_error: fail_closed
    endpoints:
      include: ["*.example.com"]
      exclude: ["trusted.example.com"]
```

| Field        | Type    | Required | Description                                                                                                                                                                                                                                                                                 |
| ------------ | ------- | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `name`       | string  | No       | Human-readable name for the middleware config. Defaults to the map key, which remains its stable identity.                                                                                                                                                                                  |
| `middleware` | string  | Yes      | Built-in middleware name or operator-owned registration name. `openshell/` is reserved for built-ins.                                                                                                                                                                                       |
| `order`      | integer | No       | Execution priority. Lower values run first, and values must be unique across the policy. Defaults to `0`; therefore, policies with multiple configs normally specify it explicitly.                                                                                                         |
| `config`     | object  | No       | Implementation-owned configuration validated by the selected middleware.                                                                                                                                                                                                                    |
| `on_error`   | string  | No       | Applies only after an advertised operation binding is selected. `fail_closed` denies the HTTP request or closes the WebSocket when that stage fails; `fail_open` skips a failed HTTP stage or disables a broken WebSocket stage for the rest of that connection. Defaults to `fail_closed`. |
| `endpoints`  | object  | Yes      | Host selector with required non-empty `include` and optional `exclude` lists, limited to 32 combined patterns. Exclusions take precedence.                                                                                                                                                  |

Host selectors use the same case-insensitive exact and DNS glob semantics as network endpoints: `*` matches exactly one DNS label and `**` matches one or more labels, so `**.example.com` covers subdomains but not `example.com` itself. Brace alternates are rejected at validation. A matching attachment joins only the operation chains its implementation advertises. An HTTP-only attachment may inspect a WebSocket upgrade GET without joining the post-upgrade chain; OpenShell permits the messages and records `binding_not_selected` coverage regardless of `on_error`. WebSocket bindings inspect complete client text messages. Binary messages pass with `unsupported_message_type` coverage for active stages. A fail-closed selector that can cover a `tls: skip` endpoint is rejected because OpenShell cannot inspect that traffic through any operation. An all-`fail_open` match may cover the endpoint; the supervisor bypasses the middleware and emits a detection finding.

See [Supervisor Middleware](/extensibility/supervisor-middleware) for registration, failure behavior, body limits, and operational guidance.

## Full Example

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

```yaml showLineNumbers={false}
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
```