How OpenClaude's Permission System Controls Tool Execution: A Deep Dive into the SDK Architecture
OpenClaude employs a layered permission model centered in src/entrypoints/sdk/permissions.ts that validates every tool execution against user-defined modes, emitting permission_request messages for human approval when automatic clearance is unavailable.
The permission system in the Gitlawb/openclaude repository acts as a security gatekeeper for all tool operations, determining whether commands like Bash, PowerShell, or MCP-provided tools may execute during a session. This architecture ensures that potentially dangerous operations require explicit user consent while allowing trusted workflows to proceed automatically. Understanding how this system controls tool execution is essential for both SDK consumers and host implementers building on top of the OpenClaude platform.
Understanding the Permission Architecture
The core permission logic resides in src/entrypoints/sdk/permissions.ts, where the SDK constructs a tool-permission context at session initialization. This context captures the active permission mode and any custom allow or deny rules that govern subsequent execution attempts.
Permission Context Construction
When a session starts, the SDK invokes buildPermissionContext() to establish the security boundary for the current interaction. According to the source code at src/entrypoints/sdk/permissions.ts#L4-L5, this initialization captures the current permission mode—which may be default, acceptEdits, bypass-permissions, fullAccess, or plan—alongside any granular allow or deny rules configured by the user. This context object becomes the authoritative source for all subsequent permission checks during the session lifecycle.
Mode Mapping and Validation
User-supplied permission strings are mapped to the internal PermissionMode enum defined in src/types/permissions.ts. The SDK validates these mappings strictly: if a user requests an unsupported bypass mode, the system raises a clear error unless the allowDangerouslySkipPermissions flag is explicitly set. As implemented in src/entrypoints/sdk/permissions.ts#L183-L204, this validation layer prevents accidental escalation to dangerous modes like fullAccess without proper administrative acknowledgment.
How the canUseTool Wrapper Enforces Security
Every tool in the OpenClaude ecosystem calls the canUseTool function before executing its core logic. This wrapper function, created via createDefaultCanUseTool() from src/utils/permissions/permissions.ts, implements the decision engine that determines whether execution proceeds, pauses for approval, or terminates with a denial.
Automatic Approval Flow
When the active permission mode pre-approves a specific tool category, the canUseTool wrapper returns an immediate approval decision without user intervention. For example, when operating in acceptEdits mode, file modification tools receive automatic clearance while system commands still trigger permission requests. This conditional logic at src/entrypoints/sdk/permissions.ts#L255-L259 allows high-trust workflows to maintain speed while preserving security boundaries for sensitive operations.
Interactive Permission Requests
If a tool lacks pre-approval from the current mode, the SDK emits a permission_request message to the host stream. Host implementations—whether CLI clients, VS Code extensions, or remote servers—receive this message and present an interactive prompt to the user. The host then responds with a permission_response message containing the decision and the original request_id. This architecture decouples the permission policy engine from the user interface, allowing diverse host implementations to present appropriate confirmation dialogs.
Timeout Handling
The permission system implements defensive timeouts to prevent indefinite blocking. If the host fails to respond to a permission_request within the default 30-second window, the SDK automatically emits a permission_timeout message as defined at src/entrypoints/sdk/permissions.ts#L36-L42. Upon timeout, the tool execution receives a denial decision, ensuring that hung or unresponsive hosts cannot leave dangerous operations pending indefinitely.
Implementation Details for Developers
Developers integrating with the OpenClaude SDK can configure permission behavior through the query() function or interact directly with the low-level permission API.
To execute a query with automatic edit acceptance:
import { query } from '@openclaude/sdk';
// Accept any file-edit tool without prompting
await query({
prompt: 'Rename the file "old.txt" to "new.txt".',
permissionMode: 'acceptEdits', // ← auto-accept edits
});
For custom tool implementations requiring explicit permission checks:
import { createDefaultCanUseTool } from '../../utils/permissions/permissions.js';
import { buildPermissionContext } from '../../entrypoints/sdk/permissions.js';
const permCtx = buildPermissionContext({ permissionMode: 'default' });
const canUseTool = createDefaultCanUseTool(permCtx);
// Example tool implementation
async function runBash(cmd: string) {
const decision = await canUseTool({ name: 'bash', input: { cmd } });
if (!decision.approved) throw new Error('Permission denied');
// ...execute the command...
}
Host developers handling permission requests must listen for the dedicated event:
engine.on('permission_request', async msg => {
const answer = await promptUser(`Allow ${msg.tool_name}?`);
engine.send({
type: 'permission_response',
request_id: msg.request_id,
decision: answer ? 'allow' : 'deny',
});
});
Security-By-Default Design
OpenClaude implements a secure-by-default posture where tools execute only when explicitly allowed by the active mode or through direct user approval. When no canUseTool callback is supplied and the permission request cannot resolve—either through timeout or host failure—the system denies execution with a clear error message as implemented at src/entrypoints/sdk/permissions.ts#L702-L708.
Dangerous modes including bypass-permissions and fullAccess require the --allow-dangerously-skip-permissions CLI flag and an explicit confirmation handler to activate. This gating mechanism at src/entrypoints/sdk/permissions.ts#L166-L173 ensures that unrestricted execution capabilities cannot be enabled accidentally through configuration errors.
Summary
- Layered Validation: The permission system constructs a context at
src/entrypoints/sdk/permissions.ts#L4-L5that combines mode selection with custom rules to govern tool execution. - Mode Mapping: User-facing permission strings map to internal enums with strict validation that rejects dangerous modes unless
allowDangerouslySkipPermissionsis enabled. - Request/Response Protocol: Unapproved tools trigger
permission_requestmessages to hosts, which must respond within 30 seconds or face automatic timeout denial. - Default Denial: When permission status cannot be determined, the system defaults to denying execution to maintain security boundaries.
- Host Decoupling: The architecture separates policy enforcement from UI presentation, allowing CLI, IDE, and remote implementations to handle prompts appropriately.
Frequently Asked Questions
What permission modes does OpenClaude support?
OpenClaude supports six primary permission modes defined in the source: default (interactive approval required), acceptEdits (automatic approval for file modifications), plan (read-only operations), bypass-permissions (dangerous, requires explicit flag), fullAccess (unrestricted execution, requires explicit flag), and custom configurations with specific allow or deny rules. Each mode maps to specific tool categories that may execute without interactive prompting.
How does the permission timeout mechanism work?
The SDK enforces a 30-second timeout window for all permission requests emitted to hosts. If the host does not return a permission_response message within this window, the system automatically emits a permission_timeout event and denies the tool execution. This prevents indefinite blocking and ensures that network issues or unresponsive hosts cannot leave sensitive operations in a pending state.
Can I bypass the permission system for automated workflows?
Yes, but only through explicit opt-in mechanisms. The bypass-permissions and fullAccess modes disable interactive checks, but activating these requires setting the allowDangerouslySkipPermissions flag to true and supplying an explicit confirmation handler. As implemented in the codebase, these modes are gated behind multiple validation checks to prevent accidental activation in production environments.
Where is the permission logic implemented in the codebase?
The primary implementation resides in src/entrypoints/sdk/permissions.ts, which handles context construction, mode validation, and request emission. Low-level permission evaluation logic lives in src/utils/permissions/permissions.ts, while type definitions for PermissionMode and related structures are defined in src/types/permissions.ts. UI components for mode selection appear in src/components/permissions/rules/PermissionModeTab.tsx.
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 →