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
- Write the boundary as the most access the environment allows. The check covers filesystem, process identity, Landlock, network connections and REST requests.
- Write the candidate, which is the policy you want to check.
- Run the check. Only the
within_boundaryresult 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.
- Save the parent’s maximum allowed policy as
boundary.yaml. - Write the subagent’s policy as
candidate.yaml. - Run
openshell-prover check candidate.yaml --boundary boundary.yaml --output json. - On
exceeds_boundary, read the counterexample, narrow the candidate, and run the check again. - Confirm
coverage.domainslists every part of the policy you rely on, and that the result iswithin_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. Setenforcement: enforceon 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/cacheunder a boundary for/tmpreturnsunsupported, 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
inconclusivewithresource_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:
- Link-local addresses or cloud metadata hostnames.
- Traffic OpenShell cannot inspect, such as
git,sshornc, sent to a host where a provider credential is available. - Use of a provider credential at a host and port it could not reach before.
- 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
- Write the boundary before the subagent’s policy, and keep it under version control with the parent’s own policy.
- Use exact binary paths in both files, so the prover can compare them.
- Set
enforceon every endpoint that the boundary should constrain. Audit-mode endpoints fail the check. - Run the check with
--output jsonin the same pipeline that applies the policy, and stop on any exit code other than 0. - Save each effective policy with
--policy-onlyand check it again after a provider is attached. - Keep proposal approval on
manualfor 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
- OpenShell policy prover: boundary and candidate, the subagent check, results, coverage limits.
- OpenShell policy overview: the boundary definition and the policy sections.
- OpenShell policy advisor: proposal limits, approval modes, the proposal risk check.
- OpenShell manage sandbox policies: global policy behavior and commands.
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.