Skip to content
WIP

This guide is a work in progress — it is incomplete and parts are out of date.

Bounds

A bound restricts a session. It removes tools, write access, file paths, shell commands, and network destinations 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 AgentBoundSchema in sdk/lib/src/bounds/types.ts. It rejects unknown keys at every level, so a misspelled field fails instead of denying nothing.

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. 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

For example, this bound denies writes and pushes, and blocks secret files:

deniedAccess: [write]
files:
deny: ["*.env", "~/.ssh/**"]
network:
actions: [git:push]
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.

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.

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 partial actions list such as [write] still relies on the guards

Under sandbox, an absent allowDomains means an empty allowlist: no egress except to localhost. Under guard, allowDomains does nothing, and Barry logs a warning when the list is not empty.

A session’s bound merges every source:

  1. The bounds its traits carry, by name (bounds:) or inline (bound:).
  2. The session’s named bound, from --bound.
  3. The identity’s default bound.
  4. 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 Intersection of identical strings. An absent list has no effect; an empty list permits nothing
network.enforce sandbox wins over guard
Command Effect
barry -r Starts a session with the read trait instead of the trait picker. The trait grants Barry’s builtin namespaces and carries the readonly bound, which also restricts the default traits
barry --bound <name> Starts a session with a named bound. An unknown name exits with the list of bounds instead of starting
barry identity bound set <name> Sets the identity’s default bound. For an identity directory, this copies the bound’s rules into identity.yaml, so later edits to the named bound do not follow
barry identity bound clear Removes the identity’s default bound
barry bound list Lists every named bound and what it restricts

For example:

Terminal
barry bound list
barry --bound no-network-write --traits coding
barry identity bound set no-secrets

A bag’s trait carries a bound with bounds: or bound:; see Packing bags. An identity carries one inline under bound: in identity.yaml. To add named bounds, list them in bounds.yaml in your config repository and run barry config import.

Barry ships these in sdk/cli/seed/bounds.builtin.yaml:

Name Rules
readonly deniedTools: [Bash, Write, Edit], deniedAccess: [write], network.actions: [write]
no-bash deniedTools: [Bash]
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: []
slack-self-dm-only Denies the Slack tools that can choose a recipient

Barry enforces a bound on the tools it serves. The agent’s own native tools, such as Claude Code’s Bash, are a separate route, and whether a bound reaches them depends on the agent and on how the session started.

Rule Barry’s tools Agent’s native tools
deniedTools Removed from the tool list Naming Bash, WebFetch, WebSearch, or AskUserQuestion requests denial of native Bash, WebFetch, and WebSearch together. Other names do not reach native tools
deniedAccess Write tools removed from the tool list Not applied. Barry denies native Write and Edit separately, as described after this table, but native Bash can still write files
files.deny Checked on Read, Write, Edit, MultiEdit, Glob, Grep, and LS, not on Bash Not applied. cat .env in either shell gets past it
files.allowOnly Checked on the same tools Requests denial of native Bash, WebFetch, WebSearch, Read, Glob, and Grep
bash.deny, bash.denyPrograms Checked on Barry’s Bash Requests denial of native Bash, WebFetch, and WebSearch
network.actions Network tools removed from the list, and Bash commands classified Requests denial of native Bash, WebFetch, and WebSearch
network.domains URLs checked in Bash commands and in WebFetch Requests denial of native Bash, WebFetch, and WebSearch
network.allowDomains Not applied Enforced only by the egress sandbox
enforce: sandbox Not applicable Egress proxy, only where the next table says so

Where a native denial request takes effect:

Session Claude Code, on any endpoint Cursor Codex or OpenCode
Terminal session that barry starts Only for the bound named with --bound, or a database identity’s default bound. A bound from a trait, including -r, or from identity.yaml restricts Barry’s tools only Same as Claude Code, except Glob and LS, which Cursor cannot deny Never. Barry prints a warning and the native tools stay available
Session that Barry’s session API runs The bound does not reach native tools. Native Bash is always denied. Outside development, native Read, Write, Edit, Glob, and Grep are too, unless the identity sets allow_native_tools. Native WebFetch and WebSearch stay available Same as Claude Code, except Glob, which Cursor cannot deny Never. Barry logs a warning

Barry denies native Write and Edit apart from any bound: in every Claude Code session, even with allow_native_tools, and in every terminal Cursor session. Codex and OpenCode never deny them.

Where enforce: sandbox takes effect:

Session Claude Code on its own endpoint or z.ai Claude Code on Ollama Codex, Cursor, or OpenCode
Terminal session that barry starts Not applied: the guards only Not applied Not applied
Session that Barry’s session API runs Egress proxy Guards only, with a logged warning Guards only, with a logged warning

Other limits:

  • Tools that bags serve make their own network requests in Barry’s process. Traits, deniedTools, and deniedAccess govern them; network rules do not.
  • network.domains reads 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.

For the agent capabilities behind these tables, see Adapters.