# Permission System for Bash Tool Execution and Destructive Command Approval in Reasonix

> Discover Reasonix's robust permission system for bash execution. Control destructive commands with global modes and fine-grained policies for enhanced security and automated approvals.

- Repository: [YHH/DeepSeek-Reasonix](https://github.com/esengine/DeepSeek-Reasonix)
- Tags: internals
- Published: 2026-08-07

---

**Reasonix employs a two-layer permission system combining global tool-approval modes (Ask/Auto/Yolo) with fine-grained policy rules to govern bash execution, ensuring destructive commands require explicit user consent while allowing harmless operations to proceed automatically.**

The DeepSeek-Reasonix repository implements a robust security model to prevent accidental workspace modifications when executing shell commands. This article examines the **permission system for bash tool execution and destructive command approval in Reasonix**, detailing how the engine balances automation with safety through runtime modes and configurable policy rules defined in [`internal/permission/permission.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/permission/permission.go).

## Two-Layer Permission Architecture

Reasonix protects workspace integrity through complementary mechanisms: global runtime modes that establish baseline behavior, and per-tool policy rules that provide granular control over specific command patterns.

### Tool-Approval Modes (Ask, Auto, and Yolo)

The system defines three global modes that determine how the `bash` tool and other workspace-modifying utilities are handled:

- **Ask**: Every controlled tool call triggers an approval card in the UI. Users may select *Allow once*, *Allow for session*, *Always allow*, or *Deny*. This mode is ideal for unfamiliar repositories or production environments.
- **Auto**: Ordinary tool calls execute without interruption unless an explicit `ask` or `deny` rule matches, or the tool carries a `destructiveHint` flag. This supports daily coding workflows while maintaining guards for dangerous operations.
- **Yolo**: Skips ordinary prompts entirely; only explicit `deny` rules and sandbox checks remain active. Intended for bulk mechanical edits on disposable branches.

Users select modes via the Desktop composer or keyboard shortcuts (`Ctrl+Y` / `Cmd+Y`). These modes operate independently of collaboration modes (Plan/Goal), which control task sequencing rather than permission boundaries.

### Fine-Grained Policy Rules

Beneath the global mode, the permission engine evaluates specific command patterns against a `Policy` struct containing three rule slices: `Allow`, `Ask`, and `Deny`. The core decision logic resides in `DecideSubject` and `Decide` methods within [`internal/permission/permission.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/permission/permission.go) (lines 149–262).

The engine resolves decisions through this priority order:

1. **Allow rules**: If the command matches an explicit allow pattern, execution proceeds immediately.
2. **Ask rules**: If the command matches an ask pattern, the system requires user confirmation.
3. **Deny rules**: If the command matches a deny pattern, execution is blocked.

If no rules match, the **default decision is `Ask`**, ensuring any unrecognized writer-type tool pauses for confirmation. The `Decision` type is defined as:

```go
type Decision int
const (
    Allow Decision = iota // default for reads
    Ask                    // conservative fallback
    Deny                   // explicit block
)

```

## Destructive Command Handling

Destructive operations—such as `git push`, `rm -rf`, or any command marked with `destructiveHint`—receive special scrutiny regardless of the global approval mode.

### The destructiveHint Flag

According to [`docs/TOOL_APPROVAL_MODES.md`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/docs/TOOL_APPROVAL_MODES.md), the `destructiveHint` attribute in a tool's contract indicates operations that modify external state or destroy data. In **Auto** mode, the system does not automatically prompt for approval solely because a command is destructive; however, any tool entry carrying this flag still requires an explicit `ask`/`deny` rule or fresh user approval before execution.

The built-in **`bash`** tool is explicitly marked as **not read-only** (`false` in [`docs/TOOL_CONTRACT.md`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/docs/TOOL_CONTRACT.md)), automatically subjecting it to the `Ask` default unless permitted by policy rules.

### Command Decomposition and Evaluation

When processing a bash command, Reasonix parses the JSON payload and decomposes the command string into segments (e.g., pipelines, redirects) using logic tested in [`internal/permission/bash_decompose_test.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/permission/bash_decompose_test.go). Each segment is evaluated separately against the policy:

```go
// Conceptual flow based on permission.go implementation
func evaluateBashCommand(command string, policy Policy) Decision {
    segments := decomposeCommand(command) // bash_decompose logic
    for _, seg := range segments {
        decision := policy.Decide("bash", false, seg)
        if decision == Ask || decision == Deny {
            return decision // Most conservative wins
        }
    }
    return Allow
}

```

If any segment resolves to `Ask`, the entire call requires approval. If the final decision is `Deny`, Reasonix returns an error without executing the command, and `SessionGuard` (in [`internal/tool/builtin/session_guard.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/tool/builtin/session_guard.go)) surfaces a warning about the guarded state reference.

## Implementation Details

### Permission Engine Core

The [`internal/permission/permission.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/permission/permission.go) file implements the authoritative decision engine. The `Decide` method accepts the tool name, a read-only flag, and the command payload, returning one of the three decision constants. This design allows the system to handle nested tool calls and indirect bash execution consistently.

Key implementation characteristics:

- **Rule matching**: Pattern matching against command subjects supports exact strings and configurable wildcards.
- **Caching**: User decisions from approval cards (e.g., *Allow for session*) are cached according to the selected scope to prevent repetitive prompts.
- **Sandbox integration**: Even in Yolo mode, sandbox-level restrictions provide a final safety net.

### Bash Contract and Read-Only Status

The [`docs/TOOL_CONTRACT.md`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/docs/TOOL_CONTRACT.md) file defines the schema for built-in tools. The `bash` tool explicitly sets `readOnly: false`, automatically placing it under the permission system's jurisdiction. This contract validation ensures that policy rules in [`reasonix.toml`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/reasonix.toml) conform to the tool's capabilities.

## Configuration Examples

### Setting Approval Modes via CLI

```bash

# Require explicit approval for all controlled tools

reasonix mode set ask

# Auto-approve ordinary calls, respect destructiveHint rules

reasonix mode set auto

# Minimal prompts, deny rules only

reasonix mode set yolo

```

### Defining Policy Rules in TOML

Create or modify [`reasonix.toml`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/reasonix.toml) in your workspace root:

```toml
[permissions]

# Allow harmless status checks without prompting

allow = [
  { tool = "bash", subject = "git status" },
  { tool = "bash", subject = "ls -la" }
]

# Require explicit approval for destructive operations

ask = [
  { tool = "bash", subject = "git push" },
  { tool = "bash", subject = "git reset --hard" }
]

# Block dangerous commands entirely

deny = [
  { tool = "bash", subject = "rm -rf /" },
  { tool = "bash", subject = "mkfs.*" }
]

```

### Programmatic Permission Checks

Developers integrating with Reasonix can invoke the permission engine directly:

```go
package main

import (
    "encoding/json"
    "github.com/esengine/DeepSeek-Reasonix/internal/permission"
)

func validateCommand(cmd string) (permission.Decision, error) {
    // Marshal command to expected JSON payload format
    payload, _ := json.Marshal(map[string]string{
        "command": cmd,
    })
    
    // Load current session policy
    pol := permission.LoadCurrentPolicy()
    
    // Evaluate: tool name, readOnly flag, payload
    decision := pol.Decide("bash", false, payload)
    return decision, nil
}

```

## Summary

- **Two-layer protection**: Global modes (Ask/Auto/Yolo) establish baseline behavior, while policy rules provide command-specific controls.
- **Default Ask posture**: Any bash command not explicitly permitted defaults to requiring user approval, preventing accidental execution.
- **Destructive command handling**: The `destructiveHint` flag and explicit `deny` rules ensure dangerous operations like `git push` or `rm -rf` cannot run without consent.
- **Segmented evaluation**: Complex commands are decomposed into segments, with the most restrictive decision applied to the entire operation.
- **Source locations**: Core logic resides in [`internal/permission/permission.go`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/internal/permission/permission.go), with contract definitions in [`docs/TOOL_CONTRACT.md`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/docs/TOOL_CONTRACT.md) and mode documentation in [`docs/TOOL_APPROVAL_MODES.md`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/docs/TOOL_APPROVAL_MODES.md).

## Frequently Asked Questions

### What is the default behavior for bash commands in Reasonix?

By default, bash commands require user approval because the tool is marked as non-read-only in the contract. If no explicit `allow` rule matches a command, the permission engine returns `Ask`, triggering an approval card in the UI or requiring programmatic confirmation.

### How does Auto mode handle destructive commands?

In **Auto** mode, ordinary tool calls execute without prompting, but commands flagged with `destructiveHint` or matching an explicit `ask` rule still require approval. Auto mode does not automatically prompt for destructive commands unless they are explicitly configured in the policy or carry the destructive flag.

### Can I configure specific git commands to require approval while allowing others?

Yes. Define granular rules in [`reasonix.toml`](https://github.com/esengine/DeepSeek-Reasonix/blob/main/reasonix.toml) under the `[permissions]` section. For example, place `git status` in the `allow` array and `git push` in the `ask` array. The permission engine evaluates these rules before applying the global mode, ensuring pushes require confirmation while status checks proceed automatically.

### What happens when the permission system blocks a bash command?

When the engine returns `Deny`, Reasonix immediately returns an error to the calling context without executing the command. The UI surfaces a warning through `SessionGuard` indicating the command references a guarded state. The user must modify the command or update the policy rules to permit the operation.