05. Permissions, Plan Mode, and Safety
Sandbox
The sandbox is optional OS-level isolation around the Matcha CLI process and the commands it starts. Permissions decide what the model may request. The sandbox decides what the process can do after a request is approved.
Applied at startup
A non-off profile is applied to the entire process at startup (Landlock on Linux, Seatbelt on macOS). It is irreversible for that process. The assistant cannot relax it later. Child processes inherit the filesystem limits.
Off is the default. You opt in.
Built-in profiles
| Profile | FS read | FS write | Child network | Typical use |
|---|---|---|---|---|
off (default) | Unrestricted | Unrestricted | Unrestricted | Trusted machine, no isolation |
workspace | Everywhere | CWD + ~/.matcha/ + temp dirs | Allowed | Everyday development |
devbox | Everywhere | Top-level dirs except /data (and /proc, /sys, /dev) | Allowed | Disposable VMs |
read-only | Everywhere | ~/.matcha/ + temp dirs | Blocked on Linux | Review, exploration |
strict | CWD + system paths | CWD + ~/.matcha/ + temp dirs | Blocked on Linux | Untrusted trees |
Temp dirs include /tmp, /var/tmp, and on macOS the real TMPDIR under /private/var/folders. Child-network blocking uses seccomp on Linux only. On macOS restrict_network is a no-op.
workspace, read-only, and strict write-deny the user-global hook sources (~/.matcha/hooks/, ~/.matcha/hooks-paths, and absolute targets listed there) so a sandboxed session cannot rewrite those hooks. devbox does not apply that protection.
Custom profiles
Custom profiles live in ~/.matcha/sandbox.toml or .matcha/sandbox.toml. They extends a built-in, then add read_only, read_write, restrict_network, and a kernel-enforced deny list (globs such as **/.env). A custom name cannot reuse a built-in name: --sandbox devbox always runs the built-in. If user and project files define the same custom name differently, the user file wins and startup warns; /doctor shows both paths.
When a non-off profile is requested, the assistant stays in-process (no shared leader). matcha workspace start / restart / resume are unavailable; pause, stop, and status still work.
A built-in profile that cannot apply (old kernel, missing entitlements) warns and continues without enforcement, but still refuses the leader. A custom profile that cannot apply (unknown name, bad sandbox.toml, or Linux deny without bubblewrap) refuses to start.
The profile is stored with the session. Resume restores it. --sandbox on resume must match the saved profile or Matcha CLI errors; start a new session to change isolation.
When to use it
Use workspace when you want the assistant to read the system but only write in this project and Matcha CLI’s own home. Use read-only or strict on a tree you do not trust. Skip the sandbox when the assistant must install packages or write outside the working directory.
Permissions plus sandbox is stronger than either alone: a deny rule stops the model from asking; the kernel stops a process that tries anyway.
Start with a profile
matcha --sandbox workspace
matcha --sandbox read-only
matcha --sandbox strictMATCHA_SANDBOX selects the same profile (GROK_SANDBOX is the registered alias). Resolution for a new session:
- A managed requirement that pins a profile
--sandbox/MATCHA_SANDBOX[sandbox] profilein configoff
Custom profile example
# ~/.matcha/sandbox.toml
[profiles.project]
extends = "workspace"
restrict_network = true
read_only = ["/data"]
read_write = ["/tmp/scratch"]
deny = ["/data/shared-secrets", "**/.env", "**/*.pem"]matcha --sandbox projectdeny entries with *, ?, or [ are globs. On macOS they apply at runtime (files created later still match). On Linux they expand at launch — later matches are not covered; name exact paths when Linux must be airtight. Unsupported glob syntax refuses to start on both platforms.
Resume
matcha --continue
matcha --resume <id>Omit --sandbox to keep the saved profile. Passing a different profile is refused.
Shell environment
[shell_environment_policy] filters variables inherited by tool commands so a secret sitting in your shell does not leak into a subprocess. This is related, not the kernel sandbox. Default is leave-the-environment-alone (inherit = "all").
Events
Violations and profile-applied records go to ~/.matcha/sandbox-events.jsonl.