Permissions & Security
Dynamo's permission system balances safety with speed.
Permission Levels
| Level | Behavior | Examples |
|---|---|---|
| Safe | Auto-execute, no prompt | read_file, list_files, search_files, git_status, memory_list |
| Write | Prompts once per tool, per model (persists for the session) | write_file, edit_file, git_commit, web_fetch |
| Dangerous | Prompts every time. "Allow every" is never offered, and no earlier grant covers it. | run_command (every command — ls as much as rm), move_file, rename_file, delete_file, checkpoint_restore, job_kill; git_push to a protected branch, with a force flag, deleting a branch, or to a remote this repo doesn't have; git_lfs_unlock with force |
| Blocked | Never runs, in any mode | Shell strings with sudo, command substitution ($(), backticks), parameter expansion (${}), or process substitution (<(…), >(…)); any force-push to a protected branch, by run_command or git_push |
Some calls ask every time, even in Auto Mode: writes to protected files (see below), checkpoint_restore, checkpoint_delete, job_kill, turning a skill back on, a force-push to a protected branch you have allowed, and edits during a compile-error cascade.
Protected branches
A protected branch is the repository's default branch (the remote's HEAD; main or master when that is unknown), any branch matching a permissions.protected_branches pattern in dynamo.yaml (names or globs like release/*), or a branch GitHub marks protected.
Any force-push to a protected branch is blocked: -f, --force, --force-with-lease, --force-if-includes, a + refspec, --mirror or a forced --all, in any argument order, through git_push or run_command. Auto Mode and grants can't run it. To allow it, set permissions.allow_force_push_protected: true in dynamo.yaml. It then asks every time instead, even in Auto Mode, and never offers "Allow every".
A plain push to a protected branch is dangerous: it asks every time, but Auto Mode approves it.
Grants are stored per tool + model. A grant never covers a dangerous call. git_push and git_lfs_unlock set their level per call, so a grant you give on an ordinary push still asks again before a push to a protected branch like main, a force-push, a branch deletion, or a push to a remote this project doesn't know. A grant on git_lfs_unlock still asks before unlocking a file someone else locked. Grants survive /resume.
Approval Menu

When a tool requires approval, you see:
- Human-readable description of what the tool will do
- Expandable raw call details (press → to expand)
- Diff view for file edits with line numbers and colored +/- lines
- Allow / Allow every … (model) / Deny options. "Allow every" is not offered when the call is dangerous or always asks.
"Allow every" is scoped per tool, per model. Allowing file writes for Claude doesn't allow them for GPT. Switching models and switching back keeps your approvals. Approvals persist across session resume.
Auto Mode


dynamo -y
dynamo --autoAuto Mode approves the whole dangerous tier, not just routine writes — every run_command sits in that tier, ls as much as rm, because Dynamo doesn't rank shell commands against each other. Turning it on is one trust decision about the shell.
Auto Mode's own description: "Changes run without asking. Dynamo still asks before it touches protected files like .env or keys, restores or deletes a checkpoint, stops a job, turns a skill back on, force-pushes to a protected branch you allowed, and during a compile-error cascade. Blocked commands never run."
- Blocked commands — never run.
- Protected files like
.envor keys — still ask. - Restoring or deleting a checkpoint — always asks.
- Stopping a job (
job_kill) — always asks. - Turning a skill back on — always asks.
- Force-pushing to a protected branch you allowed — always asks.
- A compile-error cascade — editing tools ask for the rest of the turn.
Per-Model Permissions
Permissions are tracked as a per-model map. Each model has its own set of "Allow every" grants — granting write_file for Claude doesn't auto-grant it for GPT, so a more powerful model never inherits a weaker one's approvals. Switching models doesn't wipe any model's grants: switch away and back and your previous approvals are still there. Grants also persist across session resume.
Sandbox Mode
Controls filesystem access for all tools including shell commands.
| Mode | read_file | write/edit | list/search | run_command |
|---|---|---|---|---|
| project | project only | project only | project only | blocks paths outside the project |
| full (default) | project only | project only | anywhere | blocks paths outside the project |
| unrestricted | anywhere | anywhere | anywhere | no path checks (still permission gated) |
Set via CLI (--sandbox project), config (ai.sandbox: project), or at runtime (/sandbox).
In project and full mode, shell commands that reference paths outside the project are blocked. So are inline interpreter code (python -c, node -e, heredocs) and eval, because their paths can't be checked. Only unrestricted lifts this. sudo is always blocked regardless of sandbox mode.
Checkpoints
Dynamo snapshots your work before risky operations so you can roll back.
- Auto-snapshot — taken once per prompt, before the first
write_file/edit_fileand before a destructiverun_command - Rollback modes —
code(restore files only),conversation(rewind history only), orboth - Retention — checkpoints are stored under your config dir, per project, and pruned after 30 days
- Manage — run
/checkpointfor an interactive overlay to list, create, or restore. The model can also callcheckpoint_list/checkpoint_create/checkpoint_restore.
Security Hardening
- Command injection detection — command substitution (
$(), backticks), parameter expansion (${}), and process substitution (<(…),>(…)) are always blocked - Compound command splitting —
cmd1 && cmd2— each part checked independently - Protected files — writes to
.env,.env.*,.git/config,.git/hooks/*,.gitconfig,.bashrc,.zshrc,.profile,.bash_profile,.ssh/*,.npmrc,credentials.json,id_rsa*,*.pemand*.keyalways ask first, even in Auto Mode. Most patterns are anchored at the project root, so a nestedapps/api/.envis not covered (*.pem/*.keymatch by suffix at any depth). - Secret scanning — secrets are blocked outright in
memory_save, warned onwrite_file, and noted onread_file