Learning Mode
Greywall uses a deny-by-default filesystem model: reads and writes are blocked unless explicitly allowed. Rather than manually figuring out which paths a command needs, learning mode traces the command's actual filesystem access and generates a config template automatically.
How it works
- Run with
--learning: greywall relaxes the filesystem sandbox and traces every file operation. On Linux it usesstraceto follow syscalls (openat,creat,mkdir, and so on). On macOS it useseslogger, the Endpoint Security framework's command-line client, to streamopen,create,write,unlink,truncate,rename,link, andforkevents for the sandboxed process tree. - Use the command normally — interact with it as you would outside the sandbox. The trace captures every file read and write.
- Template generated on exit — greywall analyzes the trace, collapses paths into minimal directory sets, filters out system defaults and sensitive files, and saves a JSONC config template.
- Next run auto-loads the template — when you run the same command again (without
--learning), greywall automatically loads the learned template.
Quick start
# Step 1: Run in learning mode
greywall --learning -- opencode
# Step 2: Review what was generated
greywall profiles show opencode
# Step 3: Run normally — the profile auto-loads
greywall -- opencode
The --learning flag
greywall --learning -- <command>
This runs the command inside the sandbox with:
- Relaxed filesystem: reads and writes are allowed so the tracer can observe the full access pattern.
- Filesystem tracing:
straceon Linux,esloggeron macOS. On macOS the tracer itself runs undersudobecause the Endpoint Security framework requires it; the sandboxed command runs as your normal user. - Network still sandboxed: network isolation remains in effect.
When the command exits, greywall:
- Parses the trace log for file access patterns.
- Filters out system paths (already allowed by default), the current working directory (auto-included), and sensitive paths (SSH keys,
.envfiles, etc.). - Collapses paths into minimal directory sets using application directory detection (e.g., multiple files under
~/.cache/opencode/become a single~/.cache/opencodeentry). - Generates a JSONC template with
allowRead,allowWrite,denyWrite, anddenyReadsections. - Saves it to
~/.config/greywall/learned/<command>.json.
Security note: The sandbox filesystem is relaxed during learning. Do not use --learning with untrusted code.
Managing profiles
List all profiles
greywall profiles list
This lists both built-in profiles (shipped with greywall) and any learned profiles under ~/.config/greywall/learned.
Show a profile
greywall profiles show opencode
This prints the full JSONC content of the profile, including the generated comments.
Edit a profile
greywall profiles edit opencode
Opens the profile in $EDITOR for direct modification.
Use a specific profile
# Auto-load: greywall looks for a profile matching the command name
greywall -- opencode
# Explicit: use a specific profile regardless of command name
greywall --profile opencode -- ./my-custom-editor
The --profile flag takes priority over auto-detection. If the specified profile doesn't exist, greywall exits with an error.
Template format
A generated template looks like this:
// Learned template for "opencode"
// Generated by: greywall --learning -- opencode
// Review and adjust paths as needed
{
"filesystem": {
"allowRead": [
"~/.cache/opencode",
"~/.config/opencode"
],
"allowWrite": [
".",
"~/.cache/opencode",
"~/.local/state/opencode"
],
"denyWrite": [
"~/.bashrc",
"~/.bash_profile",
"~/.zshrc",
"~/.profile"
],
"denyRead": [
"~/.ssh/id_*",
"~/.gnupg/**",
".env",
".env.*"
]
}
}
Key features of generated templates:
allowRead— paths beyond system defaults and CWD that the command accessed. Only populated when deny-by-default reads are enabled.allowWrite— collapsed write paths. Always includes.(current directory). Uses tilde-relative paths for home directory entries.denyWrite— shell startup files and other dangerous targets are always denied.denyRead— SSH keys, GPG data, and.envfiles are always denied.- Sensitive paths (SSH keys,
.env, GPG) are never included in allow lists, even if the command accessed them.
Workflow
First-time setup for a new tool
# 1. Learn what the tool needs
greywall --learning -- opencode
# 2. Review the generated profile
greywall profiles show opencode
# 3. Edit if needed
greywall profiles edit opencode
# 4. Run normally from now on
greywall -- opencode
Re-learning after changes
Running --learning again overwrites the existing profile for that command:
# Update the profile after installing new plugins
greywall --learning -- opencode
Using profiles across commands
If multiple commands need the same permissions, use --profile:
# Learn from one command
greywall --learning -- code
# Apply to similar commands
greywall --profile code -- cursor
greywall --profile code -- windsurf
Path collapsing
Learning mode intelligently collapses file paths to avoid overly verbose templates:
- Well-known parents — paths under
~/.cache/,~/.config/,~/.local/share/,~/.local/state/,~/.local/lib/, and~/.data/are grouped by application directory. For example,~/.cache/opencode/fooand~/.cache/opencode/barbecome~/.cache/opencode. - Sub-path deduplication — if
~/.cache/opencodeis included,~/.cache/opencode/subfolderis removed. - Home directory safety — paths directly under
$HOMEare never collapsed to~/to avoid overly broad access.
Troubleshooting
"strace: attach: ptrace: Operation not permitted" (Linux)
Learning mode on Linux requires ptrace support. This may fail in:
- Docker containers without
--cap-add=SYS_PTRACE - Environments with restrictive
kernel.yama.ptrace_scopesettings
"sudo: a password is required" (macOS)
eslogger talks to the Endpoint Security framework and needs root, so greywall runs it through sudo at the start of learning mode. If sudo prompts for a password and you dismiss it, learning mode aborts. Run a sudo command beforehand to cache credentials, or configure a passwordless sudo entry for /usr/bin/eslogger if you use learning mode often.
Template is too broad
Review the generated template and remove paths you don't want to allow. The template is a starting point — you can edit it directly or extend it in your main config:
{
"extends": "./learned/opencode.json",
"filesystem": {
"denyRead": ["~/sensitive-project/**"]
}
}
Learning mode hangs after command exits
This can happen if the traced command spawns long-lived child processes (LSP servers, file watchers). Greywall includes a monitor that detects when the main command exits and terminates the tracer, but in rare cases you may need to press Ctrl+C.
A word of caution
Learning mode is driven by heuristics. It watches what a command happens to touch during one session and extrapolates a profile from that. Paths get collapsed into parent directories when they look application-specific, system defaults are filtered out, and sensitive locations (SSH keys, .env, and so on) are deliberately excluded even if the traced command reached for them. These rules cover most real workflows, but they are rules of thumb, not guarantees, and the profile they generate is a starting point rather than a finished contract.
When you run the same command later without --learning, the sandbox enforces exactly what the learned profile allows. If the command uses a path the profile does not cover, it will not see a clean "permission denied" and move on. Many tools assume unrestricted filesystem access and fail in surprising ways when they hit an unexpected block: hangs while waiting for a lockfile that is silently unwritable, crashes on missing cache directories, retries that never converge, or plugins that simply do not load. If something feels off after a learning session, re-run with -m to see what is being blocked, then widen the profile.
If you build a profile that works well for a tool other people are likely to use, please contribute it back. Open a pull request against the greywall repository to add or refine a default profile so everyone benefits.
See also
- Concepts — filesystem model and deny-by-default behavior
- Configuration —
defaultDenyRead,allowRead, and other filesystem fields - Templates — built-in config templates (different from learned templates)