What a profile controls
Each of the last five keys takes this shape:
processes.list and processes.kill take session, the default, for the
session’s own processes, or workspace, for every session’s.
kill cannot reach past list, and a bare scope such as
processes: workspace sets both. max counts every pipeline stage,
subshell and background job the session has running; it is 128 unless a
block sets it, and null lifts it. Starting a process past max fails with
Resource temporarily unavailable.
The workspace takes the same processes block beside profiles, for every
session. A profile’s block overrides it field by field: here a batch
session holds 512 processes, and every other session 64, with the
workspace’s scopes.
network takes the workspace’s grammar, and a session’s lines are held to
both: the profile can narrow where they connect, never widen it. A mount
still connects under the workspace’s network, since it serves every
session. A profile’s routes replace the workspace’s for the same address:
see Route Network.
Example: a research agent
This profile lets an agent browse and search/data, keeps it read-only,
and hides /data/private.
workspace.yaml
profile: researcher selects
the default for new sessions; another session can use a different named
profile.
The allow list covers shell builtins too. This profile omits cd and echo,
so a session under it is refused both with Permission denied. The session’s
own shell functions are exempt, because each line in a function body passes
the same check.
Use a profile
Name the profile when you create a session:
A session that names no profile gets the one
profile: selects. Without
profile:, it gets the profile named default if the workspace defines one,
and no restrictions otherwise.
To change a profile while the workspace runs, add a profile or replace what
a name means, then move sessions onto it. Use the
CLI or the Python or
TypeScript API. Over HTTP,
Set profile adds or replaces a
profile.
- A session keeps its old rules until you move it onto the new profile.
- A moved session keeps its working directory, variables and history.
workspace.yamlis never rewritten.- An agent cannot do this from inside a session: no builtin, MCP tool or RPC method changes a profile.
Rule matching
Commands
- A pattern matches token prefixes:
git pushmatchesgit push origin main. - Spelling and order matter:
rm -rfdoes not matchrm -r -forrm --recursive --force. - A
*token matches any token. A trailing*adds nothing. - Without
commands.allow, there is no allow list.allow: []allows no installed tool. commands.allowdoes not cover shell functions, executed paths or direct VFS calls.- The shell’s tests count as commands under their own names. An allow list
admits
[ ]only if it names[, and[[ ]]only if it names[[. - A path rule judges the file operand of
-e,-for-ntthe same way intest,[and[[, however the operand is spelled.
Ask and deny
Ask and deny rules can name commands, paths, or map a command to its paths:- For each path, the rule with the deeper literal path wins. At equal depth, deny wins.
- Here
rm /data/review/note.txtasks for approval despite the broader deny, andrm /data/other.txtis denied. - A command with several paths must pass the check for each one.
- Ask and deny control what a command may do. The paths stay visible.
- a line whose working directory or paths are in that mount;
- a walk or a move of a directory that holds the mount (
grep -r,ls -R,mv); - each entry inside the mount that a line reaches on its own (
cp -r,chmod -R).
Hide and show paths
paths.hidemakes a path look absent.paths.showreopens a more specific path inside it.paths.hideand ask and deny path entries take absolute paths or slashless name patterns such as*.pem, in mount sections too.paths.showtakes absolute paths only.- A literal path covers everything under it. Globs use
*,?and[, and*can cross/. - A rule that names only
pathsalso applies to direct VFS calls. - Moving a directory that holds a hidden path, or one the session reaches
only through a
paths.showbelow it, is refused withPermission denied. The hidden content would come into view.
What a hidden path hides
- Command output: commands and CLIs leave it out of what they print from
their own data, and out of their counts and totals.
git status,git ls-files,git diff --statandgit log --name-onlyneither name nor count a file another session staged under it. - Links: a link that leads into it is hidden too.
readlink -f,test -L,ln,touch -h,chown -Randfind -newermeet it as a name that is not there. - Git history: no revision ever held it.
git show HEAD:secret.txtanswers as for a path never committed.git log -Sfinds nothing in it.- An object id stored only there names nothing.
- Checkouts and branch switches: they leave a hidden file as it is.
- An edit to it does not block the switch, and git’s refusal does not name it.
- A switch that would remove it, by writing a file where its directory stands, is refused. The refusal names only the visible path.
git log -p, git show HEAD:secret.txt), as it refuses
the file, and still lists the name.
Use Explain to check a rule before running a line.
For conditions beyond these patterns, attach a policy script.