See how OpenShell's network policy system works in under five minutes. You'll create a sandbox, watch a request get blocked by the default-deny policy, apply a fine-grained L7 rule, and verify that reads are allowed while writes are blocked — all without restarting anything.
- A running OpenShell gateway (
openshell gateway start) - Docker daemon running
| File | Description |
|---|---|
policy.yaml |
L7 read-only policy for the GitHub REST API, scoped to curl |
demo.sh |
Automated script that runs the full walkthrough non-interactively |
openshell sandbox create --name demo --keep --no-auto-providers--keep keeps the sandbox running after you exit so you can reconnect
later. --no-auto-providers skips the provider setup prompt since this
demo doesn't use an AI agent.
You'll land in an interactive shell inside the sandbox:
sandbox@demo:~$
curl -s https://api.github.com/zenThe request fails. By default, all outbound network traffic is denied.
The sandbox proxy intercepted the HTTPS CONNECT request to
api.github.com:443 and rejected it because no network policy authorizes
curl to reach that host.
curl: (56) Received HTTP code 403 from proxy after CONNECT
Exit the sandbox (the sandbox stays alive thanks to --keep):
exitopenshell logs demo --since 5mYou'll see a line like:
action=deny dst_host=api.github.com dst_port=443 binary=/usr/bin/curl deny_reason="no matching network policy"
Every denied connection is logged with the destination, the binary that attempted it, and the reason. Nothing gets out silently.
Review the policy:
cat examples/sandbox-policy-quickstart/policy.yamlversion: 1
# Default sandbox filesystem and process settings.
# These static fields are required when using `openshell policy set`
# because it replaces the entire policy.
filesystem_policy:
include_workdir: true
read_only: [/usr, /lib, /proc, /dev/urandom, /app, /etc, /var/log]
read_write: [/sandbox, /tmp, /dev/null]
landlock:
compatibility: best_effort
process:
run_as_user: sandbox
run_as_group: sandbox
network_policies:
github_api:
name: github-api-readonly
endpoints:
- host: api.github.com
port: 443
protocol: rest
tls: terminate
enforcement: enforce
access: read-only
binaries:
- { path: /usr/bin/curl }The top section preserves the default sandbox filesystem and process
settings (required because policy set replaces the entire policy).
The network_policies section is the interesting part: curl may make
GET, HEAD, and OPTIONS requests to api.github.com over HTTPS.
Everything else is denied. The proxy terminates TLS (tls: terminate)
to inspect each HTTP request and enforce the read-only access preset
at the method level.
Apply it:
openshell policy set demo \
--policy examples/sandbox-policy-quickstart/policy.yaml \
--wait--wait blocks until the sandbox confirms the new policy is loaded.
No restart required — policies are hot-reloaded.
openshell sandbox connect democurl -s https://api.github.com/zenAnything added dilutes everything else.
It works. Try a more visual endpoint:
curl -s https://api.github.com/octocat MMM. .MMM
MMMMMMMMMMMMMMMMMMM
MMMMMMMMMMMMMMMMMMM ____________________________
MMMMMMMMMMMMMMMMMMMMM | |
MMMMMMMMMMMMMMMMMMMMMMM | Speak like a human. |
MMMMMMMMMMMMMMMMMMMMMMMM |_ ________________________|
MMMM::- -:::::::- -::MMMM |/
MM~:~ 00~:::::~ 00~:~MM
.. MMMMM::.00:::+:::.00teleMMM ..
.MM::::: ._. :::::MM.
MMMM;:::::;MMMM
-MM MMMMMMM
^ M+ MMMMMMMMM
MMMMMMM MM MM MM
MM MM MM MM
MM MM MM MM
.~~MM~MM~MM~MM~~.
~~~~MM:~MM~~~MM~:MM~~~~
~~~~~~==googler======~~~~~~
~~~~~~==googler======
:MMMMMMMMMMM:
'=googler=='
curl -s -X POST https://api.github.com/repos/octocat/hello-world/issues \
-H "Content-Type: application/json" \
-d '{"title":"oops"}'{"error":"policy_denied","policy":"github-api-readonly","detail":"POST /repos/octocat/hello-world/issues not permitted by policy"}The CONNECT request succeeded (api.github.com is allowed), but the L7
proxy inspected the HTTP method and returned 403. POST is not in
the read-only preset. Your agent can read code from GitHub but cannot
create issues, push commits, or modify anything.
Exit the sandbox:
exitopenshell logs demo --level warn --since 5ml7_decision=deny dst_host=api.github.com l7_action=POST l7_target=/repos/octocat/hello-world/issues l7_deny_reason="POST /repos/octocat/hello-world/issues not permitted by policy"
The log captures the exact HTTP method, path, and deny reason. In production, pipe these logs to your SIEM for a complete audit trail of every request your agent makes.
openshell sandbox delete demo| State | What happens |
|---|---|
| Default deny | All outbound traffic blocked — nothing gets out |
| L7 read-only | GET to api.github.com allowed, POST blocked |
| Audit trail | Every request logged with method, path, decision |
The policy hot-reloads in seconds and gives
you verifiable, fine-grained control over what your agent can access —
without --dangerously-skip-permissions.
- Customize the policy: Change
access: read-onlytoread-writeor add explicitrulesfor specific paths. See the security policy reference. - Scope to an agent: Replace the
binariessection with your agent's binary (e.g.,/usr/local/bin/claude) instead ofcurl. - Add more endpoints: Stack multiple policies in the same file to allow PyPI, npm, or your internal APIs.
- Try audit mode: Set
enforcement: auditto log violations without blocking, useful for building a policy iteratively.