How Reasonix Evaluates Permission Rules (Allow, Ask, Deny) for Tool Calls

Reasonix evaluates tool call permissions through a hierarchical 9-step gate implemented in internal/planmode/policy.go that checks tool names, safety classifications, read-only flags, and administrator overrides to decide whether to allow, block, or conditionally permit each invocation.

When the DeepSeek-Reasonix model requests a tool invocation, the request passes through a strict permission rules evaluation system that determines if the call proceeds, requires user confirmation, or is rejected. This security gate operates on a fail-closed principle, ensuring that only explicitly trusted read-only operations execute during autonomous planning phases.

The Permission Gate Architecture

The core decision logic resides in the Policy.Decide method defined in internal/planmode/policy.go. This method accepts a Call struct containing the tool name, arguments, read-only status, and safety classification, then returns a Decision struct containing a Blocked boolean and a human-readable Message explaining any denial.

The evaluation follows a strict priority-ordered hierarchy of nine distinct checks, processed sequentially until a definitive verdict is reached.

The 9-Step Evaluation Hierarchy

The Decide method implements the following rule chain:

  1. Bash Special Handling: If the tool name is "bash", control transfers to decideBash (lines 81-85), which parses the command using shellsafe.CommandIsReadOnly to verify the operation is read-only and rejects unsafe arguments.

  2. Known Blocked Tools: If the tool appears in knownBlockedTools (including write_file, complete_step, and other write-capable tools), the call is blocked unconditionally in plan mode.

  3. Unsafe Safety Classification: If the tool's PlanModeClassifier reports PlanSafetyUnsafe (lines 49-51), the call is blocked regardless of other flags.

  4. Always-Allowed Set: Tools in the unconditional allow list (ask, todo_write) are allowed immediately without further checks (lines 89-92).

  5. Self-Reported Safe Read-Only: If the tool reports PlanSafetySafe and ReadOnly() == true, the call is allowed (lines 55-60).

  6. Trusted Read-Only Tools: If the tool is read-only and the flag is trusted (not marked Untrusted), the call is allowed. This covers built-in read-only tools and first-party MCP overrides (lines 61-66).

  7. Administrator Allow-List: If the tool name appears in the policy's AllowedTools slice (populated from plan_mode_allowed_tools in reasonix.toml), the call is allowed (lines 66-69).

  8. Untrusted Read-Only Hints: If the tool is read-only but the flag came from an untrusted external source (Untrusted == true), the call is blocked with a message explaining that the hint must be pre-seeded via trusted_read_only_tools or declared in plan_mode_allowed_tools (lines 70-75).

  9. Default Deny: If none of the above conditions match, the call is blocked as a writer tool, requiring the user to finish planning with read-only tools before approving writes (lines 76-79).

Special Handling for Bash Commands

The bash tool receives unique treatment due to its ability to execute arbitrary shell commands. The decideBash function inspects the command string and arguments to ensure no write operations, background processes, or unsafe flags are present. Any command capable of modifying the filesystem or spawning detached processes results in a blocked decision with an explanatory message.

Configuration Overrides

Administrators can bypass default restrictions by configuring the plan_mode_allowed_tools array in reasonix.toml. Tools listed in this configuration are added to the policy's AllowedTools slice, granting them explicit permission to run during plan mode regardless of their default classification.

Code Examples

Allowing a built-in read-only tool:

call := planmode.Call{
    Name:      "read_file",
    ReadOnly:  true,
    Untrusted: false,
    Safety:    planmode.PlanSafetyUnknown,
}
decision := policy.Decide(call)
// decision.Blocked == false

Denying a writer tool without explicit permission:

call := planmode.Call{
    Name:      "write_file",
    ReadOnly:  false,
    Untrusted: false,
    Safety:    planmode.PlanSafetyUnknown,
}
decision := policy.Decide(call)
// decision.Blocked == true
// Message: "blocked: \"write_file\" is a writer tool ..."

Configuring explicit permission via reasonix.toml:

plan_mode_allowed_tools = ["git"]

With this configuration, the git tool is permitted during plan mode:

call := planmode.Call{
    Name:      "git",
    ReadOnly:  true,
    Untrusted: false,
    Safety:    planmode.PlanSafetyUnknown,
}
decision := policy.Decide(call) // Allowed due to config override

Validating bash commands:

args := json.RawMessage(`{"command":"git log --oneline","run_in_background":false}`)
call := planmode.Call{
    Name: "bash",
    Args: args,
}
decision := policy.Decide(call) // Allowed if command is read-only

If the command contains write operations (e.g., git commit), decideBash returns a blocked decision.

Summary

  • The permission gate is implemented in internal/planmode/policy.go via the Policy.Decide method.
  • A 9-step hierarchical evaluation determines whether to allow, block, or deny each tool call.
  • Read-only tools are allowed only if their flags are trusted or explicitly configured.
  • Bash commands undergo additional parsing to verify they are read-only operations.
  • Administrators can override defaults using the plan_mode_allowed_tools configuration in reasonix.toml.
  • The system operates on a fail-closed principle: any ambiguous tool is treated as a writer and blocked.

Frequently Asked Questions

What happens if a tool claims to be read-only but comes from an untrusted MCP server?

If a remote MCP server marks a tool as read-only but sets Untrusted == true, the permission gate treats it as a writer tool and blocks the call. To allow such tools, administrators must either add them to trusted_read_only_tools or list them explicitly in plan_mode_allowed_tools within reasonix.toml.

Why does Reasonix block the bash tool differently from other tools?

The bash tool can execute arbitrary shell commands, making it inherently risky. Instead of relying on metadata flags, decideBash parses the actual command string using shellsafe.CommandIsReadOnly to detect write operations or background process spawning, ensuring only safe, read-only commands execute during plan mode.

Can I allow a normally blocked writer tool during plan mode?

Yes. While writer tools like write_file are blocked by default, you can add them to the plan_mode_allowed_tools array in your reasonix.toml configuration file. This populates the policy's AllowedTools slice, bypassing the default restrictions for that specific tool.

What is the difference between PlanSafetySafe and the ReadOnly flag?

ReadOnly is a boolean property indicating whether a tool modifies system state, while PlanSafetySafe is a classification from the tool's PlanModeClassifier indicating explicit opt-in for plan-mode execution. A tool must typically satisfy both conditions (or meet other override criteria) to pass the permission gate.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →