Skip to content

Files, shell and git

Three modules give an agent hands on a machine: files, a shell and git. They run only on a daemon, each off until you name it with what it may reach, and everything they touch goes through the daemon's sandbox. An organization in the cloud gets them from a daemon it connects as its runner.

Each module names its roots: the folders it works under, each read only or writable.

  • Files only below the roots. .., other absolute paths and links that point out are refused.
  • Processes under the operating system’s sandbox (Seatbelt on macOS, Landlock and seccomp on Linux), reading the roots and the system’s programs, writing only the writable roots.
  • The network only with the network grant.

A runner can also name paths that no module of the daemon reaches, whatever the roots say.

[[builtins]]
id = "tools-fs"
grants = ["filesystem"]
filesystem = [{ path = "/Users/you/project", write = true }]
[[builtins]]
id = "tools-shell"
grants = ["filesystem", "spawn"]
filesystem = [{ path = "/Users/you/project", write = true }]
config = { path = "/usr/local/bin:/usr/bin:/bin:/opt/homebrew/bin" }
[[builtins]]
id = "tools-git"
grants = ["filesystem", "spawn"]
filesystem = [{ path = "/Users/you/project", write = true }]
config = { author_name = "Agent", author_email = "agent@example.com" }

The rest of daemon.toml: Configure.

Tool What
read a text file with line numbers, or a range of its lines
write a whole file, created with its folders or replaced, at once
edit replace one exact piece of text that appears once in a file
list_dir a folder: folders first, then files with their sizes
glob find files by pattern (**/*.rs), skipping hidden and ignored ones; 1,000 unless asked
grep search contents by pattern or literal text, with lines around each match; 100 matches unless asked for more

Reading and listing are read actions; writing and editing are write.

Tool What
bash run a command to its end and get the end of its output and how it ended; 120 seconds unless asked, 600 at most
tmux_start start a long-running command (a server, a watcher) in a tmux session
tmux_read the last lines of a session, and whether its command ended
tmux_send type into a session, then Enter
tmux_list the module’s sessions
tmux_kill end a session and its command

Running and typing are external actions: the team’s trust decides whether they wait for a person.

Settings: path, the PATH of its commands (empty: the system’s folders), and env, other variables a toolchain needs (CARGO_HOME, PROTOC). HOME and the temporary folders are the sandbox’s own.

Tool What
git_status the working tree’s changes
git_diff the patch of the unstaged or the staged changes, or of one path
git_log the commits of a revision, newest first; 20 unless asked, 500 at most
git_branches the local branches
git_branch make a branch, and check it out unless told not to
git_switch check out a branch
git_commit commit every change, or some paths, with a message
git_branch_commits the commits on a branch that its base lacks
git_worktree_add a worktree on a branch, made or the one already there
git_push push one branch to the remote, never forced

Settings: path, the PATH of git; author_name and author_email, together or neither; and push.

Pushing is off until push is set:

  • remote: empty, origin;
  • branches: the branches it may push, as patterns; empty, task/*, the branches a team’s task works on;
  • ssh_auth_sock: the socket of the ssh-agent that holds the key. The key never enters the sandbox: git reaches it only through the agent;
  • known_hosts: the file the remote’s key is checked against; empty, the key is accepted the first time and checked after.

A push needs the network grant and is an external action. Protect your main branches on the git host as well, with a key that may not push them: the branches above are pcx’s own check, the host’s is the second.