Permissions & --yolo

Fred asks before every tool call by default. Approving means "run this," denying means "don't, explain why instead." The system is two orthogonal axes: approval mode (the prompt gate) and sandbox mode (the kernel-enforced ground truth). Together with permission rules, that gives you a knob for every level of trust from "ask me everything" to "go for it."

The prompt

● bash: pytest tests/test_auth.py -q
  [y]es / [n]o / [a]ll / [d]etails ›
KeyEffect
yRun this one call, ask again next time.
nRefuse. Fred sees the refusal + reason in the message stream.
aApprove all calls of this kind for the rest of the session — and persist as an allow rule.
dShow full args (file diffs, command stdin, etc.) before deciding.

Read-only tools (view, grep, glob) skip the prompt entirely — they can't change anything. Bash, file writes (str_replace, create_file), and shell-affecting tools all need approval before they run.

Permission categories

Every tool declares a requires_permission category. The dispatcher consults that flag before doing anything user-visible.

  • Read-only — auto-approved, no prompt. view, grep, glob, repo_map, request_rule.
  • Permission-required — prompts unless a rule allows. bash, str_replace, create_file, MCP tools, anything that mutates state.

Approval modes

Approval mode controls the y/n/a/d panel. Three settings, ported from Codex CLI's same axis:

ModeBehavior
untrustedAlways prompt. Even an explicit allow-rule still shows the panel. Use when you don't trust the prompt yet.
on_requestDefault. Prompt unless a rule auto-allows. The friendly middle ground.
neverSkip prompts entirely. Subset of --yolo; rules and the sandbox are your only defenses.

Set via:

  • --approval <mode> flag at startup
  • FRED_APPROVAL_MODE env var
  • /sandbox approval <mode> in the REPL

Resolution priority: explicit flag > env var > default (on_request). Even in never mode, deny rules still apply — they're a hard floor that no approval setting overrides.

Sandbox modes

Sandbox mode controls what bash commands can actually do at the kernel level. macOS only for now (via sandbox-exec / Seatbelt — same primitive Codex CLI uses).

ModeFilesystem
roRead-only filesystem. Bash can read but not write anywhere outside cwd's repo metadata.
workspace-writeDefault. Writes inside cwd allowed; can't escape upward. Network is denied.
fullNo sandbox. Full trust. The world is yours; act accordingly.

Set via:

  • --sandbox <mode> flag at startup
  • FRED_SANDBOX_MODE env var
  • /sandbox <mode> in the REPL

Bash invocations get wrapped in /usr/bin/sandbox-exec -p <profile> before spawning. The path to sandbox-exec is hardcoded — defense against PATH-injection attacks. When the kernel denies an op, you'll see a sandbox-exec: prefix in stderr.

Permission rules

Rules are an allowlist/denylist of Tool(specifier) patterns. Stored in ~/.dsc/settings.local.json (and project .dsc/settings.json for team-shared rules). Three lists, evaluated in this order:

  • deny — short-circuits. If a pattern matches, the tool refuses without ever asking.
  • ask — always prompts. Useful to shadow a previous "allow always" pick — drop a rule into ask and the next call gets the panel back.
  • allow — auto-yes. Skips the panel.

Manage rules via slash commands:

/perms                                 # list all active rules
/perms add Bash(npm run *) allow       # append to allow list
/perms add Bash(rm *) deny             # append to deny list
/perms add Bash(curl *) ask            # always prompt for curl
/perms remove Bash(npm run *)          # remove from any list

Rules are loaded by walking from cwd up toward $HOME, stopping at the first .git boundary. Project rules (settings.json) are committed and shared; local rules (settings.local.json) are gitignored and personal.

--yolo flag

fred --yolo

Alias for --approval never. Prompts skipped, all tools auto-approved up to sandbox limits. Useful for:

  • Greenfield projects where there's nothing to lose.
  • Well-tested CI pipelines.
  • When you trust both the prompt and the model and want to watch it move.

Don't use it on a machine that has access to anything you can't afford to lose. The sandbox is your remaining defense — pair --yolo with --sandbox workspace-write at minimum.

Common rule patterns

Pattern syntax is Tool(specifier) — same grammar across the rule engine, hooks, and /perms. Globs use the standard * wildcard.

PatternMatches
Bash(git status)One literal command. Exact match.
Bash(git *)Any git invocation. Won't match shell-chained commands.
Bash(npm test)Just npm test. Common safe one to allowlist.
Bash(pytest)Just pytest with no args.
Bash(pytest *)Pytest with any args.
View(src/*)Read anything under src/. Probably already auto-allowed; explicit just to be safe.
Bash(rm *)Any rm. Usually want this in deny or ask.
Bash(curl *)Any curl. Network egress — usually ask at minimum.

Important: an allow-rule glob like Bash(git *) does NOT authorize git status; rm -rf /. Bash specifiers containing shell-control metacharacters (;, &&, ||, |, &, backticks, $(), newlines) are never authorized by an allow-glob — the panel falls through and you get a chance to look. Deny-rules deliberately still match these strings (so you can deny a quoting bypass intentionally).

Persisting choices

When the panel asks y/n/a/d and you hit a, two things happen:

  • The tool name + specifier is added to the in-session always-allow set so the same call doesn't prompt again this session.
  • The rule is written to ~/.dsc/settings.local.json so the prompt doesn't repeat next session either.

Persistence is best-effort — a write failure (read-only home, perms, etc.) just means the in-session set still covers the rest of THIS run. Picking y approves once; only a persists.

Bash safety

bash calls run with a 30s timeout. Stdout + stderr are captured (up to 4KB) and returned to the model so it can react to errors. The model never sees secrets in your shell — only what the command prints.

Combined with sandbox mode, this means even an aggressive bash command is constrained: a workspace-write sandbox can't escape cwd, can't reach the network, can't write to ~/.ssh. The kernel says no before the model sees a result.

Heads up

--yolo + --sandbox full is fully autonomous. Use it on disposable VMs or new branches, not on your main repo with uncommitted work. There's no /restore from a force-push.

Tip

If you're constantly answering "yes" to the same prompt, hit a once and Fred remembers — both for this session and next time.

See /docs/configuration for the settings.json file shape, and /docs/checkpoints for the rollback story when permissions weren't strict enough.