How Permission Rules Work for Bash Commands and File Operations in Reasonix
Reasonix enforces fine-grained permission rules through a centralized PermissionsView object that matches every Bash command and file operation against explicit allow, ask, and deny lists before execution.
The esengine/DeepSeek-Reasonix repository implements a deterministic, prompt-aware security model that protects the host environment by intercepting potentially dangerous operations at the bridge layer. Understanding how these permission rules function is essential for developers building plugins or configuring sandboxed environments.
Permission Architecture Overview
Reasonix employs a three-tier permission model that combines frontend state, backend sandbox configuration, and a central bridge layer. Every tool invocation—whether a Bash command or file system operation—must pass through this validation pipeline before reaching the execution kernel.
The system relies on four key components working in concert:
PermissionsView– Defines the global mode (ask,allow, ordeny) and maintains explicit rule lists for each category.SandboxView– Configures backend-specific restrictions like Bash enforcement status and automatically permitted write directories.- Bridge Layer – Mediates all tool requests in
desktop/frontend/src/lib/bridge.tsand applies the rule-matching algorithm. - UI Settings – Provides a Permissions tab for real-time rule management that syncs with the global state.
Core Components
PermissionsView (Frontend State)
The permission state lives in desktop/frontend/src/lib/types.ts and defines the contract for rule-based access control:
export interface PermissionsView {
mode: string; // "ask" | "allow" | "deny"
allow: string[]; // explicit allow rules
ask: string[]; // explicit ask-before-execute rules
deny: string[]; // explicit deny rules
}
Each rule follows the pattern <Tool>(<action>:<pattern>). For example, Bash(rm:*) targets any rm command regardless of arguments, while ReadFile(/etc/passwd) matches specific file read operations.
SandboxView (Backend Configuration)
The backend sandbox configuration, also defined in desktop/frontend/src/lib/types.ts, provides additional hard constraints:
export interface SandboxView {
bash: string; // "enforce" | "off"
allowWrite: string[]; // write-allowed directory roots
// … other sandbox flags
}
When bash is set to "enforce", every Bash command undergoes permission checking. The allowWrite array defines directory roots that are implicitly writable without triggering prompts, even when the global mode is set to ask.
Bridge Layer (Rule Matching)
All tool requests funnel through desktop/frontend/src/lib/bridge.ts, which constructs canonical rule strings and evaluates them against the current permission configuration. The bridge builds rules dynamically from the command context—for instance, parsing Bash(${command.split(' ')[0]}:${args.join('*')}) to create matchable patterns.
Rule Matching Algorithm
When a tool is invoked, Reasonix applies a deterministic decision flow with strict precedence:
- Rule Construction – The bridge generates a canonical string (e.g.,
Bash(rm:*)forrm /tmp/file). - Ordered Lookup – The system searches for matches in sequence: first
deny, thenallow, thenask. - Immediate Action – If a match is found in any list, the corresponding action executes immediately without checking remaining categories.
- Global Fallback – If no explicit rule matches, the current
modevalue determines the outcome:allow– Execute the operation silently.deny– Block the operation and return apermission deniederror.ask– Display a confirmation dialog for user approval.
The bridge also validates file writes against sandbox.allowWrite roots, rejecting operations outside approved directories unless explicitly permitted by the rule lists.
Practical Implementation Examples
Adding Explicit Permission Rules
Configure the permission state programmatically to control specific operations:
// settings.permissions is the PermissionsView instance
settings.permissions.allow.push('Bash(ls)');
settings.permissions.deny.push('Bash(rm:*');
settings.permissions.ask.push('ReadFile(/etc/passwd)');
settings.permissions.mode = 'ask'; // Default fallback for unmatched rules
This configuration automatically permits ls commands, blocks any rm invocation, and prompts before reading sensitive system files.
Handling Bash Commands
Plugin code invoking tools through the bridge automatically respects permission boundaries:
import { invokeTool } from '@reasonix/bridge';
// Blocked by the deny rule defined above
await invokeTool('Bash', 'rm /tmp/evil.tmp')
.catch(err => console.error(err.message)); // → "permission denied"
// Allowed to proceed
await invokeTool('Bash', 'ls -la /home/user');
Restricting File Write Operations
File operations outside the sandbox write roots require explicit permission:
import { writeFile } from '@reasonix/fs';
// Fails unless /etc is in sandbox.allowWrite or matched by an allow rule
await writeFile('/etc/hosts', '127.0.0.1 localhost')
.catch(err => console.error(err.message)); // → "permission denied"
The backend Bash execution in workers/crash-report/src/shell.ts only receives commands after the bridge has validated permissions, ensuring no unapproved operations reach the system shell.
Summary
- Permission rules in Reasonix follow the pattern
<Tool>(<action>:<pattern>)and are evaluated indeny→allow→askorder. - The
PermissionsViewinterface indesktop/frontend/src/lib/types.tsstores explicit rule lists and the global mode setting. - The bridge layer (
desktop/frontend/src/lib/bridge.ts) constructs canonical rule strings and mediates every tool request before backend execution. SandboxViewprovides hard constraints on write directories and Bash availability through theallowWritearray andbashenforcement flag.- Unmatched rules fall back to the global mode, with
askmode displaying interactive confirmation dialogs to the user.
Frequently Asked Questions
What happens if no permission rule matches a Bash command?
If no explicit rule matches the constructed canonical string, the system consults the global mode value in PermissionsView. When mode is ask, the bridge displays a confirmation dialog; when allow, it executes silently; when deny, it throws a permission denied error.
How do sandbox write roots interact with permission modes?
The allowWrite array in SandboxView defines directories that are implicitly writable regardless of the global mode. Even under ask mode, writes within these roots proceed without prompting, while writes outside require explicit allow rules or user confirmation.
Can plugins bypass the permission system in Reasonix?
No. All tool invocations route through the bridge layer in desktop/frontend/src/lib/bridge.ts, which applies rule matching before forwarding requests. The backend shell in workers/crash-report/src/shell.ts only receives already-validated commands, making bypass impossible through standard APIs.
Where is the permission state stored in the codebase?
The permission state is defined by the PermissionsView interface in desktop/frontend/src/lib/types.ts and persisted in the global settings store (typically managed through desktop/frontend/src/lib/settings.ts). The UI synchronizes this state in real-time via the Permissions tab settings panel.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →