Permission model
Every tool call is checked against the active permission policy before
execution. The policy is layered: a per-tool check trait, a structured
decision protocol, persistent allow/deny/ask rules, and a bash-compound
splitter that classifies each && / | / ;-joined segment
independently.
The five user-facing modes
| Mode | Behavior |
|---|---|
default | Prompt for file changes and shell access. |
plan | Read-only — file-mutating tools are blocked. |
acceptEdits | Auto-approve workspace edits; prompt for shell access. |
bypassPermissions | Unrestricted tool access. |
dontAsk | Auto-approve everything without prompting (allow/deny rules still apply). |
Set the mode at launch:
lime --permission-mode planlime --dangerously-skip-permissions # forces bypassPermissions…or inside the REPL:
/permissions acceptEdits…or in user/local config:
{ "permissions": { "defaultMode": "acceptEdits" } }Per-tool allow / deny / ask
Project config can shape the prompt-vs-auto behavior per tool:
{ "permissions": { "allow": ["read_file", "glob_search", "grep_search"], "ask": ["write_file", "edit_file"], "deny": ["bash"] }}Values can be:
- A bare tool name —
"bash"matches everybashinvocation. - A patterned form —
"Bash(git status:*)"matches thebashtool with any input startinggit status. - A scoped tool form —
"Edit(.git/**)"matches edits whose path matches the glob.
Patterns support * / ? glob wildcards or plain substring matching
against the tool name, raw input, or the combined tool_name:input string.
Bash compound splitter
bash invocations are pre-split: each segment between &&, ||, |,
or ; is classified independently. A compound like
git status && rm -rf node_modules evaluates git status (allowed) and
rm -rf node_modules (potentially denied) separately.
Plan mode
plan mode blocks every WriteWorkspace and FullAccess tool except
writes to the active plan file. The model can still read the workspace,
search, and reason; it cannot mutate. To leave plan mode, the model calls
exit_plan_mode, which surfaces a confirmation prompt.
The trusted-settings pin
Two settings always resolve from User + Local config (and the optional Policy layer), never from project config:
permissions.defaultModemcpServers(the startup MCP set)
This prevents a checked-in .lime.json from silently lowering trust on a
teammate’s machine. If a project file defines either field, Lime emits a
diagnostic warning and ignores the project value for trust resolution.
The non-trust portion of the project config (allow/deny/ask, hooks,
plugins, sandbox) merges normally.
Resolution order
The merged policy resolves in this order, highest precedence last:
- Policy — managed enterprise/policy file at
LIME_MANAGED_SETTINGS. - User —
~/.lime/settings.json,~/.lime/config.toml. - Project —
.lime.json,.lime/settings.json,.lime/config.toml. - Local —
.lime/settings.local.json,.lime/settings.local.toml. - CLI flags —
--settings FILE_OR_JSON,--permission-mode,--dangerously-skip-permissions.
Within each scope, TOML wins over JSON on overlap. Use
--setting-sources to restrict which scopes are merged (useful for tests
and reproducing other people’s environments).
Decision protocol
When a tool call requires approval, the policy returns a structured
PermissionDecision with one of:
allow— proceed.deny— abort with the recorded reason.ask— surface a UI prompt to the user.
Each decision carries a reason and a source (which rule, which scope)
so /status and /config can explain why a particular call was treated
the way it was.
See also
- Exec rules — persistent allow/deny rules outside the config file.
- Sandboxing — process isolation for the most dangerous calls.
- Hooks —
PreToolUsehooks can override the policy at call time.