Skip to content

Permission rules

Localix evaluates a permission policy before an agent tool call reaches its implementation. Use this page to trace which rule decides that call.

Terminology

TermMeaningBoundary
Permission ruleAn ordered policy record evaluated for one tool call.A rule can match only the facts extracted for that call.
Call factsThe tool name, mutation class, paths, commands, and workspace root.Extraction is tool-specific.
Session grantA process-memory approval for one program, path, folder, or declared resource.Dangerous commands and credential paths ignore it.
Workspace rootThe project or Localix-created session directory.It is a policy boundary, not an OS sandbox.

Permission contract

MechanismBehaviorBoundary
Rule evaluationThe LAST rule that matches the call decides.A call no rule matches is allowed.
Whose rulesThe agent taking the turn owns them; there is no scope above it.A narrower role is a different agent, not a narrower scope.
Shipped guardsEvery role includes outside-workspace and dangerous-shell question rows.They follow its general tool allowances; later rows can still decide.
Session grantThis session remembers the chosen breadth in the API process.A process restart or a different session asks again.
Permanent ruleAlways writes that breadth's rule into the asking agent's own list.Offered only when the rule would allow the call it answers.

How it works

Plain text
the session's permission mode rewrites the agent's rules (once per run)
extract call facts + the tool's own class of the call (`AgentTool.classify`)
  -> host-root guard (hard deny, before every rule)
  -> evaluate the rewritten rules, in order
  -> allow, block, or request confirmation

The host-root guard

The host-root guard blocks extracted paths into the Localix installation or source tree, unless that tree contains the session's own project directory. Agents ordinarily work in ~/.localix and in the session project. Rules are the user's to edit, and a Bypass session stops asking, so this boundary is not a rule: the gate refuses the call in code before any rule is evaluated, and no preset, project rule or permission mode lifts it. One folder inside the installation is open to reading: the mini app kit, whose SDK declarations a mini app is written against. The Read and Search tools may open it; writes, edits and shell commands stay blocked there.

QuestionAnswer
Which directoriesDevelopment: the checkout root (dev staging under .turbo included). Packaged: the server bundle's directory, its resources/ or server/ container, and the macOS .app.
Never a host rootThe filesystem root, the home directory or anything above it, and anything that contains or sits inside ~/.localix.
Which paths are judgedFile-tool targets, the bash cwd, a mini app tool's declared permissionPaths, and every absolute or .. path token in a shell command, symlinks resolved.
The one exemptionA host root that contains the session's own project directory: opening the Localix checkout as a project keeps working on it.
What the model is toldThe path belongs to the Localix application itself; work in ~/.localix and the project folder, and do not retry.

The roots are computed once per process from the server module's own location (host-roots.ts), the same self-location rule the staged migrations/ and mini-app-kit roots use. For the same reason a tool or mini app host given no cwd starts in ~/.localix/workspace, never in the API's launch directory, which is the host application; a source lint keeps process.cwd() out of agent-core.

Rule fields

FieldMeaningBoundary
permissionMatches one tool name, or *.A glob, anchored — tool:ba* is a family, not a prefix scan.
patternMatches a path, command, declared resource, or a named class.Matching is lexical after any tool-specific path resolution.
actionallow, ask, or deny.None of the three is terminal; position decides.
messageSupplies the explanation shown for an ask or deny.It does not change evaluation.

Rules are evaluated in order; the last match wins. Specificity has no separate priority: a broad rule placed later can override a narrow one. A deny before a narrower allow creates an allowlist with an exception, as in the shipped presets.

For example, these rows in Permission rules allow ordinary Git commands while asking about dangerous commands:

PositionCapabilityResourceEffect
1tool:Bash*ask
2tool:Bashgit *allow
3tool:Bashdangerousask

git status matches rows 1 and 2, so it is allowed. git push --force also matches row 3, so it asks. Moving row 2 below row 3 would allow that force push. These rows illustrate ordering; keep the preset's other guards when editing your list.

The editor stores these columns as action, resource, and effect. For live tool calls, Localix projects them to the permission, pattern, and action fields above, removing the tool: prefix from the tool name.

A skill's allowed-tools are Claude Code rules, read as Claude Code reads them. A path rule anchors at the working directory (Read(src/**)), the project (/src/**), the filesystem root (//tmp/**) or your home folder (~/notes/**); a bare name such as .env matches at any depth. Edit(...) also covers writes and Read(...) covers searches. WebFetch(domain:example.com) names a host for research that reads known URLs, Agent(Explore) an agent type, Skill(deploy) a skill and a bare mcp__github every tool of that server.

These preapprovals are evaluated separately after the original ordered policy has decided. They can answer an ordinary matching tool or argument question; they cannot reorder the policy, reopen a denied tool or lift a call-class confirmation such as outside-workspace or dangerous. A deny followed by an authored allow remains an allowlist with an exception, even when a skill declares Bash(git *).

Pattern matching

PatternMatch
*Every call selected by permission.
outside-workspaceA resolved path outside the workspace root.
dangerousA shell command classified as dangerous.
mutatesA call whose effect is anything but read or view.
effect:<effect>A call the tool classes as read, view, session, files, host or external.
irreversibleA call the tool says cannot be undone.
needs-humanA call that waits on a person.
documentation-filesEvery mutation target is a documentation file within the workspace.
test-filesEvery mutation target is a test/spec file or belongs to a test/fixture directory.
prototype-filesEvery mutation target belongs to a mockup, design or prototype artifact.
git *The git command, with or without arguments.
npm i*An anchored glob — * stands for any run of characters.

"Inside" is the session workspace plus the parts of ~/.localix agents author — workspace/, tmp/, mini-apps/ (the person's own mini apps, where a local install puts them), plugins/ (installed Claude Code plugins) and skills/ — for reads and writes alike, in every role. The rest of ~/.localix (the database, the vault, the captured login-shell environment, backups) is outside, so touching it asks (workspace.ts).

outside-workspace, dangerous and mutates are named CLASSES: they are computed from the call's facts, not compared against its text. That is what makes them robust where a glob is not — rm -fr, rm -r -f and rm --recursive --force are one class and three different strings.

The rules editor suggests the classes declared in capability-resource-classes.ts. That list defines their predicates and the capability kinds they apply to.

An artifact's trust class (bundled, opt-in, untrusted) is in the same list and works the same way in a rule, but it is a fact about the ARTIFACT rather than the call — so it applies to plugin:, mini-app:, mcp: and agent: rules, never to tool:.

For a batch of shell commands, an ask or deny applies as soon as ONE command matches, while an allow must cover EVERY command — and a line whose commands cannot be read (command substitution) can never establish an allow. Read-confined tools resolve real paths, including symlinks, before containment checks; a write or edit through a linked folder that points outside the workspace counts as outside it. Mini app tools expose their targets through permissionPaths, and any tool can declare native resources through permissionResources.

The guards a shipped role carries

Every shipped role includes these rows after its general tool allowances:

PermissionPatternActionPurpose
tool:*outside-workspaceaskConfirm reads or writes beyond the session workspace.
tool:BashdangerousaskConfirm commands matched by the shell danger classifier.

The guards follow the roster's general tool allowances. This order makes an outside-workspace read ask even though an earlier row allows the Read tool. The guards win because they come later, rather than because class patterns have a higher priority.

They are not necessarily the final rows. Role-specific restrictions follow them; a matching later restriction can refuse a call instead of asking.

What Always writes is placed to keep that true. It must come after the rule that raised the card, or it silences nothing. Appended at the very end it would also put tool:Bash git * allow after tool:Bash dangerous ask, and the last match for git push --force would then be the allow. So the new row goes in just before the trailing dangerous, catastrophic and secrets guards that could decide the same calls: past everything else, never past a guard.

You can explicitly override a guard with a later row naming the same class, such as tool:Bash dangerous allow. Permanent approvals preserve the trailing guards so approving one command does not silently allow every dangerous command.

The shell classifier recognizes specific destructive families, privileged execution, writes to real device paths, force pushes, power commands, and common download-to-shell forms. It is a lexical safety layer, not a shell interpreter.

Rule effects

EffectPermission effectBoundary
allowThe call runs.Attributed in the UI when a rule produced it, silent when nothing matched.
askThe call raises a confirmation card for the user.With no human reachable, an unattended run refuses rather than proceeding.
denyThe call is refused.A later rule in the same list can still except it; nothing outside can.

A blanket {*, mutates, ask} rule is what "confirm every change" means, and it is also what makes a run schedule its file tools sequentially, so the confirmations arrive one at a time.

A conversation's permission mode never reaches the gate as a flag. capabilities/permission-mode-policy.ts rewrites the agent's rules once per run: Bypass removes tool questions about call classes and converts questions about named tools or commands to allow in place, then adds a catastrophic-call denial. Autonomous uses the same rewrite and adds a denial for calls needing a person. Plan appends file, host, and external-effect denials. Each mode preserves the relative order of the retained agent rules.

Skill preapprovals never reorder this policy or override a denial. MCP hooks use the same tool policy and call gate; an unattended hook cannot execute a call that requires confirmation. Without classify, a tool with mutates: false is a read. A true or omitted mutation flag yields external, which Plan refuses.

Claude Code permission rules

A run also follows the permissions of the Claude Code settings files for its folder, so rules you wrote for Claude Code keep working: ~/.claude/settings.json (or $CLAUDE_CONFIG_DIR/settings.json), the folder's .claude/settings.json, .claude/settings.local.json at the repository root, and your organization's managed settings. They are read on every run, so an edit applies from the next message, and Localix never writes them.

Claude Code ruleIn a Localix run
denyRefuses the call, whatever the agent's own rules allow. A bare tool (WebFetch, mcp__github, Configure) is also taken out of the model's view, ToolSearch's catalog included.
askRaises a confirmation card for a call the agent's rules allow. Bypass and Autonomous stop asking, as bypassPermissions does.
allowAnswers the agent's catch-all question (* with ask), like a skill's allowed-tools. It never lifts the agent's own denial.

Rules combine as in Claude Code: a deny in any file beats an ask in any file, which beats an allow; the order of files and rules does not matter. Bash(…) rules match each subcommand of a compound line after timeout, nice, nohup and similar wrappers; Read(…) and Edit(…) paths use Claude Code's anchors (//abs, ~/home, / for the settings file's own folder, relative for the working directory) and gitignore matching with ! carve-outs; WebFetch(domain:…) matches the pages Research reads; mcp__<server> and mcp__<server>__<tool> match MCP tools; Agent(…) matches the agent type a task is delegated to (general-purpose when the call names none) and Skill(…) a skill.

A shell line meets Read and Edit rules through the files it names: the operands of cat, head, tail, grep, sed, tee, cp, mv, rm and similar commands, sed -i, and redirections (> file, < file). A command that opens files without naming them is not covered, as in Claude Code. permissions.additionalDirectories lists further working directories: a path in one is not outside the workspace, so the agent's outside-workspace question does not arise.

The allow rules and additionalDirectories of a committed .claude/settings.json apply only to a folder you trusted in Claude Code (hasTrustDialogAccepted in ~/.claude.json), as Claude Code holds them back until then. allowManagedPermissionRulesOnly in managed settings leaves only the managed rules.

Configuration

Edit rules in Settings → Permissions. The picker selects whose list you are editing; it opens on the agent of the chat you came from, and the default assistant (localix) also answers for work happening beside a turn rather than inside one. Below the editor, From Claude Code settings shows, read-only, the rules, the default mode and the bypass lock of your Claude Code settings files with the file each came from, and How rules combine says which verdict wins. Older links to the Permission rules tab open this page.

The confirmation card offers Approve and Deny, a duration (Once, This session, Always) and, on a path card, the folder level from which down not to ask; see Respond to human-in-the-loop requests. A path card lists every path of the call, outside-workspace paths first, and a session grant approves the call only when it covers all of them. The desktop permission chip and the /permissions command can inspect or revoke current session grants. When no client can receive a confirmation request, an ask decision is denied with an unattended-run explanation.

Limits

  • Permission rules are application policy, not an operating-system sandbox. A tool process retains the access granted to Localix by the host system.
  • Shell path and danger detection is lexical and intentionally conservative. Indirection inside a script or executable can be invisible to the classifier.
  • A path rule can act only on paths extracted for that tool. Mini app tools without permissionPaths do not expose their targets to path matching.
  • A tool's mutation classification is code-defined. An incorrect classifier can send a call down the wrong policy branch.
  • A session grant is scoped to its requester and program, path, folder, or declared resource, capped in memory, and does not survive an API process restart. A folder level is never offered at or above the home folder.
  • Core Write and Edit calls under Localix-owned state receive a narrow host carve-out, and it still requires the rules to allow the call. Read tools do not receive that carve-out.

Source of truth