Concepts
Permissions and safety
How LoopCode decides what it may do on its own — approval modes, auto-review, permission rules, the OS sandbox, and running unattended.
Approval modes
| Mode | Setting value | What happens |
|---|---|---|
| Auto-review (default) | auto | Routine work goes ahead. Each action no rule covers is judged by a reviewer; you are asked only when it should ask |
| Auto-approve edits | acceptEdits | File edits go through; commands still ask |
| Ask before changes | default | Every edit and command waits for you |
| Plan mode | plan | Read-only. LoopCode explores and proposes a plan for you to approve |
Change the mode with shift tab in the terminal, the mode picker in the app, --mode <value> on the command line, or defaultMode in settings. The composer always shows the mode in force.
Whatever the mode, file edits are shown as diffs you can undo, and git commits, branch changes and other repository writes ask for approval.
Auto-review
Asking about every command is what makes an agent slow; approving them in bulk is what makes it dangerous. Auto-review judges each action instead:
- A fixed policy first. Known-safe reads and checks go through at once. A handful of catastrophic commands never do.
- Then a reviewer model, in two stages. A quick check that leans towards blocking, and — only for what it flags — a careful one that weighs the action's risk against what you actually asked for. The reviewer sees your messages and the agent's actions, but not the agent's own reasoning or its tools' output, so a malicious file or web page cannot talk it into anything.
- When in doubt, ask. If the reviewer is unavailable or its answer is unclear, LoopCode asks you. It never falls back to allowing.
Tune the policy
See the baseline policy:
loopcode auto-review defaults
It lists the trusted environment, what is blocked unless you asked for exactly that, and what is allowed even so. Add to it in your settings:
{
"autoReview": {
"environment": ["github.com/your-org", "s3://your-team-bucket"],
"block": ["dropping any database table"],
"allow": ["deploying to the staging environment"]
}
}A repository's committed .loopcode/settings.json may only add to block. What you trust, and what is excepted, come from your own files.
Answering a permission request
When LoopCode asks, you can:
- Allow once — this call only.
- Allow for this session — calls like it, until the session ends.
- Always allow — written as a rule to
.loopcode/settings.local.json, your personal file for this project, where you can read and edit it. - Deny — and LoopCode takes another approach.
For a command, the remembered rule covers the runner and its subcommand — npm test, git commit — or the program alone, never an arbitrary command that happens to start the same way.
Permission rules
Rules live in settings files, so a team can commit the ones that apply to everyone and keep personal ones local. They use the syntax Claude Code uses:
{
"permissions": {
"allow": ["Bash(npm run test:*)", "Bash(npm run lint)", "Edit(src/**)"],
"ask": ["Bash(git push:*)"],
"deny": ["Read(./.env)", "Read(./secrets/**)", "WebFetch(domain:internal.example.com)"],
"additionalDirectories": ["../shared-config"]
}
}| Rule | Matches |
|---|---|
Bash | Every use of the tool |
Bash(npm run test:*) | A command prefix. :* and a trailing * mean the same thing |
Read(./.env) | A path, in gitignore syntax. A bare file name matches it anywhere |
Edit(src/**) | Everything under a directory |
WebFetch(domain:docs.example.com) | A host, including its subdomains |
Browser(action:open) | A top-level input parameter |
Rules are evaluated deny → ask → allow, and the first match in that order decides. A broad deny therefore beats a narrow allow. A command prefix rule never matches a command that chains or substitutes other commands (;, &&, |, `, $(…), redirection).
Rules from all settings files accumulate rather than replace each other, so a project can forbid something that a personal file cannot allow back. /permissions lists the rules in force, and where each came from.
Sandbox
The sandbox lets the operating system — not the model, and not a prompt — decide what a shell command can touch. A command that cannot leave the workspace or reach the network does not need a person to vet it.
{
"sandbox": {
"enabled": true,
"allowUnsandboxedCommands": true,
"filesystem": { "allowWrite": ["/tmp/build-cache"], "denyRead": ["~/.ssh"] },
"network": { "enabled": false }
}
}| Field | Default | Meaning |
|---|---|---|
enabled | false | Run shell commands inside the sandbox |
allowUnsandboxedCommands | true | Let a command the sandbox refused be retried outside it, with the usual approval |
filesystem.allowWrite | — | Extra paths commands may write to, beyond the workspace |
filesystem.denyRead | — | Paths commands may not read |
filesystem.disabled | — | Turn the filesystem restriction off |
network.enabled | false | Allow network access. Off by default: most builds and tests need none, and sending data out is the real risk |
macOS uses the built-in Seatbelt framework, so there is nothing to install. Linux uses bubblewrap when it is installed. Elsewhere the sandbox reports itself unavailable, and commands go through the normal approval flow rather than running unprotected.
Protected by default
- File listings and searches skip sensitive paths, dependency folders and version-control internals.
WebFetchrefuses private, loopback and cloud-metadata addresses.- Custom language servers are read only from your own
~/.loopcode/settings.json, never from a repository's files. See Code intelligence.
Running unattended
In headless mode, nobody is there to answer, so a permission no rule or mode covers is denied. Give a CI job the rules it needs rather than switching checks off.
| Flag | Effect |
|---|---|
-y, --yes | Approve every permission a headless run asks for |
--dangerously-skip-permissions | Nothing asks before acting, in any mode |