Skip to content

All posts

KubernetesAI

Check a subagent's OpenShell policy against a boundary

Ritesh Patel

Kubernetes: Check a subagent's OpenShell policy against a boundary

OpenShell’s docs do not use the word “ceiling.” They call the equivalent a boundary: a policy that lists the most access allowed in an environment. The Policy Prover’s boundary check reports whether a candidate policy, such as a subagent’s, allows anything beyond it. The check runs offline and does not change what a running sandbox enforces.

Version note: this post is checked against the OpenShell docs on the main branch, read on 2026-11-11. The prover is documented there, and the commands were checked against those pages only. They have not been run on a live gateway or against a tagged release.

What OpenShell calls a ceiling

Search the policy docs for “ceiling,” “delegation” or “least privilege,” and you find none of those terms. The docs use “boundary” for the maximum-access policy, and they describe one delegation case: a parent agent that checks a subagent’s policy against its own maximum. Nothing in the docs says a child sandbox inherits its parent’s policy or gets clamped to it at runtime.

The table below separates the mechanisms that limit access, because they answer different questions and run at different times.

Mechanism OpenShell term Where it runs When it applies
Maximum access for an environment Boundary policy openshell-prover CLI, on files you pass it Before you apply or hand off a policy
Policy under test Candidate policy Same CLI Same
Admin-wide policy Global policy Gateway, replaces each sandbox’s policy While active
Agent asks for network access Policy advisor proposal Gateway, after the proposal risk check When the agent submits a proposal
Filesystem, Landlock and process limits Sandbox policy sections Sandbox runtime and the container runtime At sandbox startup

Write a boundary and a candidate

  1. Write the boundary as the most access the environment allows. The check covers filesystem, process identity, Landlock, network connections and REST requests.
  2. Write the candidate, which is the policy you want to check.
  3. Run the check. Only the within_boundary result passes.

The docs use this pair as the example. The boundary allows reading /usr and /etc, and the first candidate allows reading only /usr:

version: 1
filesystem_policy:
  read_only:
    - /usr
    - /etc
version: 1
filesystem_policy:
  read_only:
    - /usr
openshell-prover check candidate.yaml --boundary boundary.yaml

The candidate passes, and the output includes a coverage line that names the parts the prover checked. If you then add read_write: [/tmp] to the candidate, the check fails with exceeds_boundary and prints counterexample: filesystem write /tmp. The boundary never granted writes, so any write exceeds it.

Read the result

The prover returns one of five results. Exit codes let scripts stop a pipeline on anything but a pass.

Result Exit code Meaning
within_boundary 0 The candidate allows nothing beyond the boundary in the checked parts
exceeds_boundary 1 The candidate allows access the boundary does not. The counterexample shows one case
error 2 The check did not run, for example a missing file or an invalid policy
unsupported 3 The policy uses something the prover cannot check. The reason says what
inconclusive 3 The prover did not finish, for example after the time limit

The docs say to treat every result except within_boundary as a failure. For agents and scripts, add --output json and read the result and reason_code fields. The default time limit is 10 seconds, which --timeout changes.

Check a subagent before it runs

A parent agent can use the same check to keep a subagent’s access inside its own. The steps below apply the docs’ example to that case. The docs do not describe a delegation feature, so the workflow is yours.

  1. Save the parent’s maximum allowed policy as boundary.yaml.
  2. Write the subagent’s policy as candidate.yaml.
  3. Run openshell-prover check candidate.yaml --boundary boundary.yaml --output json.
  4. On exceeds_boundary, read the counterexample, narrow the candidate, and run the check again.
  5. Confirm coverage.domains lists every part of the policy you rely on, and that the result is within_boundary.

To check a live sandbox, save its effective policy and use it as the candidate. The effective policy includes rules that attached providers add, and the prover does not add provider rules itself:

openshell sandbox get my-sandbox --policy-only > candidate.yaml
openshell-prover check candidate.yaml --boundary boundary.yaml

The check sees only the files you pass it. If a sandbox gets its policy another way, check the effective policy you actually apply.

Where the check goes quiet

The prover returns unsupported or inconclusive for several common shapes. Treat each one as a gap in your evidence, not as a pass.

  • Audit-mode REST endpoints.: An endpoint in audit mode returns unsupported, because it does not block requests. Set enforcement: enforce on endpoints you want checked.
  • Other protocols.: WebSocket, GraphQL, MCP and JSON-RPC rules return unsupported, and so do REST rules that match query parameters.
  • Path comparison.: Filesystem rules compare path by path. A boundary entry for / covers every path. A candidate for /tmp/cache under a boundary for /tmp returns unsupported, because a symlink in the image could point elsewhere.
  • Binary globs.: An exact binary path that the boundary covers only with a glob returns unsupported.
  • Hostnames.: The prover does not resolve hostnames, so overlapping exact and wildcard hosts on one port can return unsupported.
  • Size.: More than 1,024 network rules or 4,096 endpoints across both files returns inconclusive with resource_limit.

Let agents propose, and people approve

The policy advisor is the other half of least privilege. An agent that hits a denied request can propose a network rule. It cannot remove a rule or change filesystem, Landlock or process settings, and a proposal never changes the policy on its own. The default approval mode is manual, so every proposal waits for a person.

The proposal risk check is a different test from the boundary check. It does not compare the policy with a boundary you wrote. It compares what the sandbox can reach with and without the proposed rule, and it flags four kinds of new access:

  1. Link-local addresses or cloud metadata hostnames.
  2. Traffic OpenShell cannot inspect, such as git, ssh or nc, sent to a host where a provider credential is available.
  3. Use of a provider credential at a host and port it could not reach before.
  4. A new HTTP method at a host and port where a provider credential is already in use.

In auto mode, a proposal is approved only when no finding is raised and the destination is not flagged. A gateway-wide value overrides the sandbox value, so an administrator can require manual review everywhere by setting manual on the gateway.

A global policy replaces, it does not limit

A global policy is an administrator’s override, and it is easy to mistake for a ceiling. The docs say it replaces each sandbox’s policy, so a team’s policy does not have to fit under it. While it is active, sandbox policy changes and proposal approvals are blocked, and provider-contributed network rules are suppressed. Only a platform administrator can set or delete one. Running sandboxes load a change within about 10 seconds, and --wait is not available for global policies.

openshell policy set --global --policy ./global-policy.yaml
openshell policy delete --global

Deleting a global policy fails if a sandbox’s own policy and provider rules would be invalid once restored. Test the change with traffic from a sandbox, because the CLI does not confirm it for you.

A least-privilege checklist

  1. Write the boundary before the subagent’s policy, and keep it under version control with the parent’s own policy.
  2. Use exact binary paths in both files, so the prover can compare them.
  3. Set enforce on every endpoint that the boundary should constrain. Audit-mode endpoints fail the check.
  4. Run the check with --output json in the same pipeline that applies the policy, and stop on any exit code other than 0.
  5. Save each effective policy with --policy-only and check it again after a provider is attached.
  6. Keep proposal approval on manual for sandboxes that reach public hosts, unless you accept what automatic approval lets through.

What the check does not cover

A passing check does not prove the sandbox is safe for a task, and it does not prove the sandbox runs the policy you checked. It also says nothing about the Kubernetes layer around the sandbox, such as the Pod’s image, privileges and credentials. The OpenShell on Kubernetes guide covers that setup. For what a sandbox policy does and does not control, read how to write OpenShell policies, and for how credentials reach a request, read OpenShell credentials and inference routing explained. The wider pattern is in how to sandbox AI agents on Kubernetes, and the AI agent governance hub lists the other controls.

Sources

Frequently asked questions

Does OpenShell have a setting called a policy ceiling?

No. The OpenShell docs do not use the word ceiling. They describe a boundary policy, which defines the most access allowed in an environment, and the Policy Prover's boundary check, which tests a candidate policy against it. Nothing in the docs makes a running sandbox clamp itself to a boundary. The check is a standalone command that reads policy files and does not apply them.

Does a passing boundary check mean the sandbox is enforcing the policy?

No. A within_boundary result means the candidate allows nothing beyond the boundary in the parts of the policy the prover checks. The prover docs say the result does not mean the policy is as narrow as it could be, that it suits a particular task, or that a running sandbox enforces it. Apply the policy, then test it with requests that should be allowed and blocked.

Why does the boundary check return unsupported for my policy?

The prover returns unsupported instead of guessing when a comparison needs information it lacks, such as the files in the sandbox image. Common causes are an endpoint in audit mode, a REST rule with query parameters, a WebSocket, GraphQL, MCP or JSON-RPC rule, or an exact binary path checked against a glob. Read the reason line, change the policy so the prover can model it, and run the check again.

Can a sandbox agent remove a rule or widen its filesystem access through the policy advisor?

No. The policy advisor lets an agent propose a network rule, and it cannot remove rules or change filesystem, Landlock or process settings. By default every proposal waits for a person to review it. Automatic approval applies only when the proposal risk check finds nothing and the destination is not flagged. A gateway-wide approval mode overrides the setting on each sandbox.

Does a global policy limit what a sandbox can do?

It replaces the sandbox's own policy rather than limiting it. While a global policy is active, OpenShell blocks sandbox policy changes and proposal approvals, and it suppresses provider-contributed network rules. Only a platform administrator can set or delete one. Treat it as an administrator's override, not as a ceiling a team's policy must fit under.

Ritesh Patel, Founder & VP Product, Nirmata

Ritesh Patel is a founder of Nirmata and leads its products. He writes about governing AI coding agents, MCP security and Kubernetes policy as code.

LinkedIn

Govern AI agents with AIControlsControl what AI agents do, and prove it.
ShareXLinkedIn

Stay up-to-date with the latest in AI, governance, and infrastructure

Subscribe to our free monthly newsletter to get industry news, DevOps tips, and articles right to your inbox on Kyverno, containerization, Kubernetes deployment & management and more.