Govern a Coding Agent
Coding agents need source control and a model to be useful, and those are the paths where code and credentials leak. This tutorial builds a bundle that gives a coding agent exactly two destinations: its inference endpoint, and the GitHub API in read-only mode. Every request to GitHub passes APF’s content gate, and the only MCP tool the bundle grants is a read tool.
After completing it, you understand:
- How the sandbox perimeter, provider profiles, and capability grants work together.
- Which layer enforces which restriction.
- How to confirm the restrictions from the audit trail.
Prerequisites
- APF running and registered with OpenShell, as described in Add APF to OpenShell.
- A working copy of the demo bundle, a signing key, and the policy service HTTP surface, as described in Your First Signed Policy. This tutorial uses the same
$WORKdirectory and works on a copy,$WORK/coder, so$WORK/bundlestays as the other tutorials expect it. - A workload image with
curlat/usr/bin/curland asandboxuser and group. - Terminals set up as in Set Up Each Terminal, and
jq.
Narrow the Perimeter
Open $WORK/coder/sandbox/default-policy.yaml. Keep only the anthropic and github entries under network_policies, and delete nvidia-inference, openai, httpbin, and egress-gateway:
The read-only preset lets only read methods reach GitHub. The binaries list lets only gh and curl open those connections through this entry.
Route GitHub Through APF
In the same file, change the middleware entry so APF evaluates every request to GitHub:
Requests to integrate.api.nvidia.com are not routed to APF in this bundle. If you add the inference host, APF’s content gate also inspects prompts, and the demo’s controls would rewrite or deny prompts. redact-wholesale replaces any prompt that contains a credential pattern, which the model API will then reject, and a prompt that contains personal data is denied, because model request bodies have no top-level text field for redact-pii-body to replace.
Keep Only the Providers You Need
Remove the provider profiles the agent does not use, and keep claude-code.yaml and github.yaml. In $WORK/coder/demo-bundle.template.yaml, delete the manifest entries for provider-profiles/slack.yaml, provider-profiles/nvidia-inference.yaml, and provider-profiles/anthropic.yaml.
With APF governing the OpenShell gateway, providers of other types cannot be created on it.
Keep Only the GitHub Tool Grant
The demo bundle also grants a Slack tool. Remove the grant this agent does not need, so a call to that tool is denied rather than granted. In the template, delete the manifest entry for rules/slack-grants.yaml.
rules/github-grants.yaml permits one MCP tool, github_repo_read. Any other MCP tool call, such as github_repo_write, now gets no grant and is denied. With the read-only preset above, OpenShell already blocks the POST requests that carry MCP calls to api.github.com, so the grant is a second layer that still holds if you later open a write method or add an MCP endpoint.
Sign and Load the Bundle
Set policy_revision in $WORK/coder/demo-bundle.template.yaml to one more than the active revision, then sign, verify, and load the copy:
The refresh returns the new version. New sandboxes are admitted with the narrowed perimeter.
Create a Sandbox
To run a real coding agent instead of a shell, attach the claude-code and github providers. Attaching a provider also adds the provider profile’s endpoints and binaries to the sandbox’s effective policy, so with the demo github profile, git can reach api.github.com too. Edit provider-profiles/github.yaml if that is not what you want. Then start the agent as described in OpenShell’s agent guide. The checks below use curl so the results are predictable.
Check Each Restriction
In the sandbox, read from GitHub:
The request succeeds. OpenShell allowed it at Layer 1, and APF allowed it at Layer 2. Its authorization record has "verb":"http.get", resource api.github.com, and "decision":"allow".
Try to write to GitHub:
OpenShell rejects it at Layer 1 with policy_denied, because the read-only preset does not include POST. APF is not called.
Try to reach any other host:
OpenShell rejects the connection because no network policy allows the host.
What You Built
Next Steps
- To see the content gate in action, continue with Protect Sensitive Data.
- To change this policy later, refer to Update a Signed Policy.