Bounds
A bound restricts a session. It removes tools, write access, file paths, shell commands, network destinations, and nodes from what the session’s traits granted, and it never grants anything. A session with no bound has everything its traits grant.
The schema is BoundSchema in
sdk/src/bounds/types.ts.
It rejects unknown keys at every level, so a misspelled field fails instead of
denying nothing. Bound fields lists every
key with its type; this page explains how the patterns, enforcement, and
merging behave.
Fields
Section titled “Fields”| Field | Type | Restricts |
|---|---|---|
deniedTools |
list of strings | Tools. Each entry is a namespace or a tool name |
deniedAccess |
list of strings | Write access: write for every tool, or a namespace or tool name |
files.deny |
list of patterns | Paths that match any pattern |
files.allowOnly |
list of patterns | Every path that matches no pattern. Each pattern starts with / or ~. deny wins when a path matches both |
bash.deny |
list of strings | Shell commands that contain the string. Prefer denyPrograms |
bash.denyPrograms |
list of strings | Shell commands that run the program, or program:subcommand |
network.actions |
list of action tags | Network operations. See network actions |
network.domains |
list of host patterns | Destinations to deny |
network.allowDomains |
list of host patterns | The only destinations the egress sandbox lets through. Needs enforce: sandbox |
network.enforce |
guard or sandbox |
Enforcement level. Default guard. See enforcement levels |
nativeTools |
map of harness ID to tool names | That harness’s own tools, by the names the harness uses. See enforcement coverage |
nodes.allow |
list of node IDs | Every installation the list doesn’t name: only the named ones run the work. local or this installation’s ID names this machine |
nodes.deny |
list of node IDs | The installations it names |
For example, this bound denies writes and pushes, and blocks secret files:
deniedAccess: [write]files: deny: ["*.env", "~/.ssh/**"]network: actions: [git:push]Patterns
Section titled “Patterns”| Where | Pattern | Matches |
|---|---|---|
files |
*.ext |
Any path that ends in .ext |
files |
dir/** or dir/* |
Any path that contains dir/. A leading **/ means any depth |
files |
Another pattern with * |
* matches any characters, anywhere in the path |
files |
No * |
Any path that contains the text |
files |
Leading ~ |
Your home directory |
domains |
example.com or *.example.com |
example.com and every subdomain |
allowDomains |
example.com |
Only example.com |
allowDomains |
*.example.com |
Every subdomain, not example.com itself |
denyPrograms |
git |
Every git command |
denyPrograms |
gh:pr:create |
Only that subcommand |
denyPrograms |
gh:api:write |
gh api with -X POST, PUT, PATCH, or DELETE, or with -f or -F fields |
Barry checks file patterns against both the path as written and its absolute form.
denyPrograms matches every command in a line, across &&, ||, ;, |,
subshells, and command substitutions. It looks through wrappers such as env,
sudo, xargs, timeout, sh -c, npx, and uvx. Barry denies a command
it cannot parse. It has no exception syntax: to deny most of a program, list each
subcommand.
Network actions
Section titled “Network actions”A parent tag denies all of its children.
| Tag | Parent | Denies |
|---|---|---|
all |
None | Every tag in this table |
write |
all |
git:push, http:write, and ssh:write |
read |
all |
git:fetch, http:read, ssh:read, and dns |
git:push |
write |
git push, and the git bag’s push tool |
http:write |
write |
curl, wget, and httpie with a write method or a body, and mutating gh api calls |
ssh:write |
write |
scp, rsync, and sftp, in either direction |
git:fetch |
read |
git fetch, pull, clone, ls-remote, and submodule, and the git bag’s pull, fetch, and remotes tools |
http:read |
read |
curl, wget, and httpie reads, read-only gh api calls, WebFetch, and WebSearch |
ssh:read |
read |
ssh |
dns |
read |
dig, nslookup, and host |
Barry denies a command it cannot parse. A program that none of these tags
names, such as nc or python, runs.
Enforcement levels
Section titled “Enforcement levels”enforce |
What enforces the bound | Strength |
|---|---|---|
guard (default) |
Barry filters its tool list and checks each call to its tools before the tool runs | Cooperative. A script written to disk, or a program the classifier does not know, gets past it |
sandbox |
The guards, plus the agent runtime’s egress proxy, which filters every outbound connection from the agent’s process tree by hostname | The agent cannot lift it. The proxy knows hosts, not HTTP methods, so a hosted session refuses to start when actions splits reads from writes, such as [write] alone |
Under sandbox, an absent allowDomains means an empty allowlist: no egress
except to localhost. Barry rejects a bound that sets allowDomains without
enforce: sandbox.
How bounds merge
Section titled “How bounds merge”A session’s bound merges every source:
- The bounds its selected traits carry, by name (
bounds:) or inline (bound:), including the traits they include. - The required bounds of Barry’s base identity. An identity turns an
inherited required bound off with
optional: true. - The identity’s required bounds, under
bounds:inidentity.yaml. - The session’s named bounds, from
--boundor-r. - The identity’s inline bound, under
bound:inidentity.yaml. - An inline bound on the session record.
Merging only moves toward more restriction:
| Field | Merge rule |
|---|---|
| Every list except the allowlists | Union |
files.allowOnly, network.allowDomains, nodes.allow |
Intersection of identical strings. An absent list has no effect; an empty list permits nothing |
network.enforce |
sandbox wins over guard |
Apply a bound
Section titled “Apply a bound”| Command | Effect |
|---|---|
<identity> session start -r |
Starts a session with every packed bag’s tools under the readonly bound, skipping the trait picker |
<identity> session start --bound <name> |
Starts a session with a named bound. An unknown name exits with the list of bounds instead of starting |
<identity> identity bounds add <name> |
Adds a required bound to the identity, applied to every session it starts |
<identity> identity bounds remove <name> |
Removes one of the identity’s required bounds |
<identity> identity bounds clear |
Removes every required bound from the identity |
barry bound list |
Lists every named bound and what it restricts |
For example:
barry bound list<identity> session start --bound no-network-write --traits coding<identity> identity bounds add no-secretsA bag’s trait carries a bound with bounds: or bound:; see
Packing bags. An identity carries its required bounds
under bounds: in identity.yaml.
Builtin bounds
Section titled “Builtin bounds”Barry’s base identity defines these in its
identity.yaml,
each with optional: true. Every identity can select them, and none applies
until a session names one with --bound or -r, a selected trait carries it,
or the identity requires it. no-bash comes from the bash bag:
| Name | Rules |
|---|---|
readonly |
deniedTools: [Bash, Write, Edit], deniedAccess: [write], network.actions: [write] |
no-bash |
deniedTools: [Bash, BashOutput, KillShell] |
no-secrets |
files.deny: *.env, .env.*, .ssh/**, *.pem, *.key, *.p12, *.pfx |
no-network |
network.actions: [all] |
no-network-write |
network.actions: [write] |
sandboxed-offline |
network.actions: [all], enforce: sandbox, allowDomains: [] |
These bounds also take back native tool categories. readonly takes back the
native shell, file writes, and network. no-secrets, like any bound with a
files rule, takes back the native shell, file reads, and file writes, so a
session under it reads and writes files only through Barry’s file tools.
The enforcement coverage section explains why.
Enforcement coverage
Section titled “Enforcement coverage”Barry enforces a bound on the tools it serves. The agent’s own native tools,
such as Claude Code’s Bash or Codex’s exec_command, run outside Barry’s
guards, so Barry governs them by category instead. A native tool stays off
wherever the session’s mode can control it, a trait turns it on, and a bound
takes it back. The rules live in
native-policy.ts.
| Rule | Barry’s tools | Native tools it takes back |
|---|---|---|
nativeTools |
Not applied | The tools it names for that harness |
deniedTools |
Removed from the tool list. Names match in any case | Bash: shell. Read, Glob, or Grep: file reads. Write or Edit: file writes. filesystem: file reads and file writes. WebFetch or WebSearch: network. A harness’s own tool name: that tool |
deniedAccess |
Write tools removed from the tool list | write: shell and file writes |
files.deny, files.allowOnly |
Checked on the path fields of Barry’s file tools. Barry’s Bash leaves the tool list |
Shell, file reads, and file writes |
bash.deny, bash.denyPrograms |
Checked on Barry’s Bash |
Shell |
network.actions |
Network tools removed from the list, and Bash commands classified |
Shell and network |
network.domains |
URLs checked in Bash commands and in WebFetch |
Shell and network |
network.allowDomains |
Not applied | Shell and network |
enforce: sandbox |
Not applicable | Shell and network, plus the egress proxy where the mode table says so |
Each harness’s adapter bag names the tools in each category, as
native-<category> traits that grant them and no-<category> bounds that take
them back. When a harness bag lacks a category’s bound, taking that category
back takes back every native tool the harness has.
| Harness | native-shell |
native-file-reads |
native-file-writes |
native-network |
|---|---|---|---|---|
| Antigravity (experimental) | run_command, send_command_input, command_status |
view_file, grep_search, find_by_name, list_dir |
write_to_file, replace_file_content, multi_replace_file_content, sed_file |
read_url_content, search_web |
| Claude Code | Bash |
Read, Glob, Grep |
Write, Edit, NotebookEdit |
WebFetch, WebSearch |
| Codex | exec_command, write_stdin |
view_image |
apply_patch |
web_search |
| Cursor | Shell, WriteShellStdin |
Read, Grep, List, Glob |
Write, Edit, Delete |
WebFetch, WebSearch, Fetch |
| OpenCode | shell |
read, grep, glob |
edit, write, patch |
webfetch, websearch |
| Pi | bash, powershell |
read, grep, find, ls |
edit, write |
None |
Coverage by harness and mode
Section titled “Coverage by harness and mode”How firmly a native tool stays off depends on the harness and on the session mode. Each mode declares how it gates native calls:
- Per-call: the harness asks Barry before each call, and Barry answers.
- Static: a denylist fixed at launch.
- Hooks: a Barry hook inside the harness can refuse a call.
| Harness | Mode | Native gate | Native tools it cannot turn off | Operator override | Egress sandbox |
|---|---|---|---|---|---|
| Antigravity (experimental) | Hosted | Hooks | None | None | No |
| Antigravity (experimental) | Instrumented | Hooks | None | /hooks turns Barry’s gate off |
No |
| Antigravity (experimental) | Guest | Hooks | None | /hooks turns Barry’s gate off |
No |
| Claude Code | Hosted | Per-call | None | None | Yes. Ollama and llama.cpp endpoints lose it |
| Claude Code | Instrumented | Static | None | None | No |
| Claude Code | Guest | Static | None | None | No |
| Codex | Hosted | Hooks | None | None | No |
| Codex | Instrumented | Hooks | None | /hooks turns Barry’s gate off until the next turn starts |
No |
| Codex | Guest | Hooks | None | /hooks turns Barry’s gate off for the rest of the session |
No |
| Cursor | Hosted | Per-call | Grep, List, Glob, SemanticSearch, Fetch |
None | No |
| Cursor | Instrumented, macOS only | Hooks | WebFetch, WebSearch, Glob |
None | No |
| Cursor | Guest | Hooks | WebFetch, WebSearch, Glob |
None | No |
| OpenCode | Hosted | Per-call | None | None | No |
| OpenCode | Instrumented | Not available | |||
| OpenCode | Guest | Static | None | None | No |
| Pi | Hosted | Per-call | None | None | No |
| Pi | Instrumented and guest | Not available |
What each harness adds:
- Antigravity is experimental: its hosted checks run against a scripted Gemini endpoint, not Gemini’s real API. In every mode, its gate refuses a forced native call when the policy script goes missing, throws, or times out.
- Claude Code hosted rules each call in one shared hook, which refuses a call it cannot rule on before its deadline. Instrumented and guest run no Barry hook, so the native tool list and denial settings that Barry writes at launch keep native tools off.
- Codex runs Barry’s hook launcher in every mode, and the launcher refuses
a forced native call. A missing launcher, a missing Node, or Codex’s outer hook
deadline leaves a gap. In instrumented, Barry turns its gate back on at the
start of each turn, so a call made before then runs ungated. A
web_searchgrant changes only at thread start or resume. - Cursor hosted refuses an ungranted native call through the workspace’s
denylist before Cursor asks Barry. Cursor runs
Grep,List,Glob, andSemanticSearchover ACP with no permission request and no hook, and runsFetchwith no permission check. In the terminal modes, Cursor approvesWebFetchandWebSearchunder--force, and Barry has no verified hook for them or forGlob. - OpenCode hosted puts each call to Barry and offers the model no native tool that a trait does not grant. Guest denies ungranted native tools in its configuration at launch.
- Pi hosted puts every tool call, nested calls included, to Barry, and a ruling that fails refuses the call. Pi has no terminal modes, because a Pi terminal signs in with the user’s own login.
At launch:
- A tool the mode cannot turn off stays in the session’s tool list even without a grant, and Barry warns. A bound that would take one back stops the launch.
- Some tools turn on only together, such as Cursor’s
WriteandEdit, plusDeletein hosted, and OpenCode’sedit,write, andpatch. A trait that grants part of a group gets none of it. - A grant the mode cannot honor stays off, and Barry warns.
- Where the mode has an operator override, Barry prints it.
Only Claude Code’s hosted mode declares the egress sandbox, and the model endpoint has the final say: z.ai keeps it, while Ollama and llama.cpp do not. A hosted session whose bound asks for the sandbox on a harness or endpoint without it refuses to start.
Other limits:
- Tools that bags serve make their own network requests in Barry’s process.
Traits,
deniedTools, anddeniedAccessgovern them;networkrules do not. network.domainsreads URLs as written. It misses URLs built at runtime and hosts reached by IP address.- A program the classifier does not know runs under
guard. Only the sandbox covers it.
Related
Section titled “Related”Adapters covers the agent capabilities behind these tables.
Policy resolution explains how bounds
join traits in one policy, and
Distributed nodes explains
where nodes rules apply.