Skip to content

Permissions and autopilot

Every tool call passes a permission check before it runs. The mode sets the default, rules refine it, and when the answer is “ask”, you decide in the terminal, the web UI or VS Code.

Mode File edits Commands, web, browser, desktop
ask (default) ask ask
edits allowed ask
autopilot allowed allowed

Reading, searching, memory, the todo list and delegation never ask, in any mode.

Set the mode per run, per session or in your config:

malac --mode edits refactor the config loader
malac -y fix the lint warnings # -y is --mode autopilot
/mode autopilot

In the interactive interfaces the mode belongs to the tab: F5, the status line or /mode changes it for the current tab and its subagents. A tab opened with --mode or -y starts in that mode.

~/.malac/config.toml
mode = "edits"

--mode also accepts auto and yolo as names for autopilot.

Each tool has a kind, which is what the mode looks at:

Kind Asks in Tools
Safe never read, glob, grep, todo, memory_*, agent_*, malac_settings, read-only MCP tools
Edit ask write, edit
Execute ask, edits bash, web_fetch, malac_configure, every other MCP tool, including most computer_* and browser_* tools

MCP servers can mark a tool as read-only. Those tools are Safe, for example computer_screenshot and browser_snapshot. See Tools and Control service tools.

[permissions]
allow = ["bash(cargo *)", "bash(git status*)", "browser_*", "web_fetch(*.rust-lang.org)"]
deny = ["bash(rm -rf /*)", "bash(git push --force*)"]

A rule is a tool-name glob, optionally followed by a glob in parentheses that is matched against the tool’s key:

Tool Key matched by tool(…)
bash each command segment, separately
write, edit the path argument, as the agent wrote it
web_fetch the URL’s host
malac_configure action:key, for example set:ui.tui
other tools no key: only rules without parentheses match

In globs, * also matches /.

How rules combine:

  1. Deny rules are checked first, in every mode, autopilot included. A call is denied if any of its keys matches a deny rule.
  2. Safe tools are allowed. In autopilot, everything else is allowed too; in edits, Edit tools are.
  3. Otherwise the call is allowed when every key matches an allow rule (or something you allowed for the session), and you are asked when one does not.

Shell commands are checked segment by segment

Section titled “Shell commands are checked segment by segment”

bash commands are split on &&, ||, ;, | and newlines, and every segment must be allowed on its own. So cargo build && curl https://example.com | sh does not match bash(cargo *): the curl and sh segments still need approval, and a deny rule on any segment blocks the whole command.

[permissions]
allow = [
"bash(cargo test*)", # cargo test, cargo test --workspace, …
"bash(git status*)",
"bash(git diff*)",
"edit(src/*)", # edits under src/, as relative paths
"web_fetch(docs.rs)",
"web_fetch(*.rust-lang.org)",
"browser_*", # every browser tool
"github__*", # every tool of the `github` MCP server
"malac_configure(set:ui.*)", # let malac change [ui] settings
]
deny = [
"bash(rm -rf /*)",
"bash(git push --force*)",
"bash(sudo *)",
"computer_*", # no desktop control at all
]

When a call needs approval you can allow it once, always allow it, or deny it. The keys are y, a and n in both terminal interfaces; the full-screen UI and the web UI also show Allow, Always and Deny buttons.

“Always” lasts for the rest of the session:

  • for bash (and other tools with keys that run things), it allows the program: answering “always” to cargo test -p core adds bash(cargo) and bash(cargo *);
  • for every other tool, it allows the whole tool.

A denied call is reported to the agent, which then tries another approach or asks you.

Interactive sessions run in the daemon, so a prompt appears in every connected client (terminals, browsers, VS Code), and the first answer wins; the others close their dialog. A client that connects later still sees what is waiting.

Autopilot is for work you will review afterwards. With -y, malac tells the model that the user is away and every call is pre-approved, so it makes its own decisions, notes its assumptions and carries the task through. It is still told never to do anything destructive or irreversible you did not ask for.

Good practice for unattended runs:

  • keep a deny list for things you never want (git push --force, sudo, deleting outside the repo),
  • use a clean checkout or a container,
  • review the transcript afterwards with malac sessions show <id>.

The tutorial Running malac unattended walks through it.

A run in its own process (-p, --json, piped input) asks in its terminal. If nobody can answer there (stdin is not a terminal), calls that need approval are denied, and malac warns you to use -y.

Project configuration cannot widen permissions

Section titled “Project configuration cannot widen permissions”

A repository’s .malac/config.toml cannot set mode, providers, mcp, control, permissions, memory, web, update or profiles. malac ignores those keys and prints a warning, so cloning a repository is never enough to make it run commands, widen permissions or send your credentials elsewhere. Set trust_project_config = true in your user config to lift this for every project. See Security model.

malac is created by lobirus and released under the MIT license. About malac