Skip to main content

Overview

Craft Agents uses permissions.json files to customize what the agent can do in Explore mode. Rules are defined at multiple levels and merged additively to create the final permission set.
Permissions are additive — custom configs extend defaults, making rules more permissive. You cannot restrict below the app defaults.

Configuration Levels

Permissions are loaded from three locations in order:
1

App Defaults

~/.craft-agent/permissions/default.jsonBundled patterns for common commands (git, ls, cat, etc.). Auto-synced on app updates.
2

Workspace Overrides

~/.craft-agent/workspaces/{id}/permissions.jsonWorkspace-specific rules that apply to all sessions in this workspace.
3

Source Overrides

~/.craft-agent/workspaces/{id}/sources/{slug}/permissions.jsonPer-source rules for specific MCP servers or API integrations.

Merging Strategy

// From permissions-config.ts
function buildMergedConfig(context: PermissionsContext): MergedPermissionsConfig {
  // Start with hardcoded defaults
  const merged = { ...SAFE_MODE_CONFIG };
  
  // Load and apply app defaults
  const appDefaults = loadDefaultPermissions();
  if (appDefaults) applyDefaultConfig(merged, appDefaults);
  
  // Add workspace customizations
  const workspace = loadWorkspacePermissions(context.workspaceRootPath);
  if (workspace) applyCustomConfig(merged, workspace);
  
  // Add source customizations (with auto-scoping)
  for (const sourceSlug of context.activeSourceSlugs) {
    const source = loadSourcePermissions(context.workspaceRootPath, sourceSlug);
    if (source) applySourceConfig(merged, source, sourceSlug);
  }
  
  return merged;
}

File Format

Schema

// From mode-types.ts
export interface PermissionsConfigFile {
  version?: string;                    // ISO date: "2026-03-04"
  allowedBashPatterns?: Pattern[];     // Regex for bash commands
  allowedMcpPatterns?: Pattern[];      // Regex for MCP tool names
  allowedApiEndpoints?: ApiEndpointRule[]; // Method + path rules
  allowedWritePaths?: Pattern[];       // Glob patterns for writable paths
  blockedTools?: Pattern[];            // Additional tools to block
}

type Pattern = string | { pattern: string; comment?: string };

interface ApiEndpointRule {
  method: 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS';
  path: string;        // Regex for API path
  comment?: string;
}

Example Configuration

{
  "version": "2026-03-04",
  "allowedBashPatterns": [
    {
      "pattern": "^git\\s+(status|log|diff|show|branch|remote)\\b",
      "comment": "Git read-only operations"
    },
    {
      "pattern": "^npm\\s+list\\b",
      "comment": "List installed packages"
    },
    "^pwd\\b"
  ],
  "allowedMcpPatterns": [
    "^list",
    "^get",
    "^search"
  ],
  "allowedApiEndpoints": [
    {
      "method": "POST",
      "path": "^/v1/issues$",
      "comment": "Create issues"
    },
    {
      "method": "PATCH",
      "path": "^/v1/issues/[^/]+$",
      "comment": "Update issues"
    }
  ],
  "allowedWritePaths": [
    "~/workspace/docs/**/*.md",
    "/tmp/**"
  ]
}
All regex patterns must be valid JavaScript regex. Invalid patterns are silently skipped with a warning in logs.

Rule Types

1. Allowed Bash Patterns

Regex patterns for bash commands that are safe to run in Explore mode.
allowedBashPatterns
Pattern[]
Array of regex patterns matching safe bash commands
Pattern matching:
// From mode-manager.ts
function isReadOnlyBashCommand(command: string): boolean {
  // 1. Check for dangerous control characters
  if (hasDangerousControlChars(command)) return false;
  
  // 2. Parse with AST-based validator
  const astResult = validateBashCommand(command, patterns);
  if (!astResult.allowed) return false;
  
  // 3. For compound commands, validate each part
  // "git status && git log" → validate ["git status", "git log"]
  
  return true;
}
Example patterns:
[
  "^git\\s+(status|log|diff|show)\\b",
  "^ls\\b(?!.*>).*",  // ls without redirect
  "^cat\\s+[^|>]+$",   // cat without pipes/redirects
  "^npm\\s+(list|view|info)\\b",
  "^docker\\s+ps\\b"
]

2. Allowed MCP Patterns

Regex patterns for MCP tool names that are allowed in Explore mode.
allowedMcpPatterns
Pattern[]
Array of regex patterns matching MCP tool names
How it works: MCP tools are named mcp__<sourceSlug>__<toolName>. Patterns match against the full name.
// Workspace-level pattern
"^list"  // Matches: mcp__linear__list, mcp__github__listIssues

// Source-level pattern (auto-scoped)
"list"   // User writes this
"mcp__linear__.*list"  // System converts to this
Source-level MCP patterns are automatically scoped to prevent cross-source leakage. A pattern in sources/linear/permissions.json only affects Linear tools.

3. Allowed API Endpoints

Fine-grained rules for API requests beyond GET (which is always allowed).
allowedApiEndpoints
ApiEndpointRule[]
Array of method + path patterns for allowed API calls
Endpoint rule structure:
{
  method: 'POST' | 'PUT' | 'PATCH' | 'DELETE' | 'HEAD' | 'OPTIONS',
  path: string,     // Regex matching request path
  comment?: string  // Human-readable description
}
Example rules:
[
  {
    "method": "POST",
    "path": "^/v1/comments$",
    "comment": "Create comments on issues"
  },
  {
    "method": "PATCH",
    "path": "^/v1/issues/[a-z0-9-]+$",
    "comment": "Update specific issues"
  },
  {
    "method": "DELETE",
    "path": "^/v1/drafts/[a-z0-9-]+$",
    "comment": "Delete draft items"
  }
]

4. Allowed Write Paths

Glob patterns defining which directories/files can be written in Explore mode.
allowedWritePaths
Pattern[]
Array of glob patterns for writable paths
Supported glob syntax:
  • * — Matches any characters except /
  • ** — Matches any characters including / (recursive)
  • ? — Matches single character
  • ~ — Expands to home directory
Example patterns:
[
  "~/workspace/docs/**/*.md",        // All markdown in docs/
  "~/workspace/test/**",             // Entire test directory
  "/tmp/**",                          // System temp directory
  "~/.craft-agent/workspaces/*/sessions/*.json"  // Session files
]
Implementation:
// From mode-manager.ts
function matchesAllowedWritePath(filePath: string, allowedPaths: string[]): boolean {
  const normalized = normalizeForComparison(expandHome(filePath));
  
  for (const pattern of allowedPaths) {
    const regex = globToRegex(pattern);
    if (regex.test(normalized)) return true;
  }
  
  return false;
}
Write paths bypass the hardcoded Write/Edit tool block. Use carefully to avoid unintended file modifications.

5. Blocked Tools

Additional tools to block beyond the hardcoded defaults.
blockedTools
Pattern[]
Array of tool name patterns to block
Hardcoded blocked tools (cannot be overridden):
// From mode-types.ts
const BLOCKED_TOOLS = new Set([
  'Write',
  'Edit', 
  'MultiEdit',
  'NotebookEdit'
]);
Example additional blocks:
[
  "^mcp__docker__",      // Block all Docker MCP tools
  "^mcp__.*__delete",    // Block delete operations across sources
  "Bash"                 // Block bash tool entirely (strict mode)
]

Creating Workspace Permissions

1

Create file

touch ~/.craft-agent/workspaces/{your-workspace-id}/permissions.json
2

Add rules

{
  "version": "2026-03-04",
  "allowedBashPatterns": [
    "^make\\s+(test|lint)\\b"
  ],
  "allowedWritePaths": [
    "~/workspace/docs/**/*.md"
  ]
}
3

Test in Explore mode

Open a session, switch to Explore mode (SHIFT+TAB), and try running:
make test  # Should be allowed
make build # Should be blocked

Creating Source Permissions

Source-level permissions are useful for allowing specific operations on individual integrations.
File: ~/.craft-agent/workspaces/{id}/sources/linear/permissions.json
{
  "version": "2026-03-04",
  "allowedMcpPatterns": [
    "create_issue",
    "update_issue",
    "add_comment"
  ],
  "allowedApiEndpoints": [
    {
      "method": "POST",
      "path": "^/graphql$",
      "comment": "GraphQL mutations"
    }
  ]
}
MCP patterns are auto-scoped: "create_issue" becomes "mcp__linear__.*create_issue"

Validation

Validate your permissions.json file before deploying:
import { validatePermissionsConfig } from '@craft-agent/shared/agent';
import { readFileSync } from 'fs';

const content = readFileSync('permissions.json', 'utf-8');
const config = JSON.parse(content);
const errors = validatePermissionsConfig(config);

if (errors.length > 0) {
  console.error('Validation errors:', errors);
  // Example: "allowedBashPatterns[2]: Invalid regex pattern: ^git\s+("
}
Common validation errors:
  • Invalid regex syntax (unmatched parentheses, invalid escapes)
  • Unknown HTTP methods in API endpoint rules
  • Invalid glob patterns in allowedWritePaths

Migration

Permissions files use semantic versioning. When app updates include new patterns, they’re automatically merged:
// From permissions-config.ts
function ensureDefaultPermissions(): void {
  const installed = readPermissionsFile(destPath);
  const bundled = readPermissionsFile(bundledPath);
  
  if (bundled.version > installed.version) {
    const merged = migratePermissions(installed, bundled);
    writePermissionsFile(destPath, merged);
  }
}
Migration behavior:
  • New patterns are added to your config
  • Existing patterns are preserved (no removal)
  • Version is updated to latest
  • User customizations remain intact
Migrations only affect ~/.craft-agent/permissions/default.json. Workspace and source configs are never auto-modified.

Troubleshooting

Command Blocked in Explore Mode

If a command is unexpectedly blocked:
# Error message will show:
# - What pattern was attempted
# - Where matching failed
# - Relevant patterns that might match
# - Paths to permissions.json files
Check diagnostics:
import { getBashRejectionReason } from '@craft-agent/shared/agent';

const reason = getBashRejectionReason('git -C /path status', config);
console.log(reason);
// {
//   type: 'no_safe_pattern',
//   command: 'git -C /path status',
//   matchedPrefix: 'git ',
//   failedAtPosition: 4,
//   failedToken: '-C',
//   suggestion: 'Run from within the repo directory instead'
// }

Patterns Not Taking Effect

1

Check file location

Verify file is in correct directory:
  • Workspace: ~/.craft-agent/workspaces/{id}/permissions.json
  • Source: ~/.craft-agent/workspaces/{id}/sources/{slug}/permissions.json
2

Validate JSON syntax

cat permissions.json | jq .
3

Check regex escaping

In JSON, backslashes must be double-escaped:
"^git\\s+status"  // Correct
"^git\s+status"   // Incorrect (invalid escape)
4

Restart session

Permissions are cached. Restart the session to reload:
permissionsConfigCache.invalidateWorkspace(workspaceRootPath);

API Reference

Load Permissions

import { permissionsConfigCache } from '@craft-agent/shared/agent';

const merged = permissionsConfigCache.getMergedConfig({
  workspaceRootPath: '/path/to/workspace',
  activeSourceSlugs: ['linear', 'github']
});

Invalidate Cache

// Invalidate workspace
permissionsConfigCache.invalidateWorkspace(workspaceRootPath);

// Invalidate source
permissionsConfigCache.invalidateSource(workspaceRootPath, 'linear');

// Clear all
permissionsConfigCache.clear();

Best Practices

Start Narrow

Begin with strict patterns and gradually expand based on actual needs.

Use Comments

Add comments to patterns to document intent and improve error messages.

Test Patterns

Validate regex patterns with an online tester before adding to config.

Version Control

Commit workspace permissions.json to your repo for team consistency.

Next Steps

Permission Modes

Learn about Explore, Ask, and Execute modes

Security Details

Understand encryption, OAuth, and security architecture

Build docs developers (and LLMs) love