Skip to main content
The policy engine receives an action request and returns one of three decisions: allow, block, or require_approval. Policies are matched in priority order — the first match wins.

Policy structure

Policies are stored in the policies table. The TypeScript shape used by the engine:
interface Policy {
  id: string;
  workspaceId: string;
  name: string;
  tool: string;              // "github" | "linear" | "slack" | "notion" | "internal_api" | "mcp"
  actionType?: string | null; // legacy field, use actionPattern
  actionPattern: string;     // glob-style, e.g. "pull_request.*" or "*"
  resourceType?: string | null;
  riskLevel?: "low" | "medium" | "high" | "critical" | null;
  mode: "allow" | "block" | "require_approval";
  conditions: PolicyConditions;
  priority: number;          // lower number = higher priority
  enabled: boolean;
  toolName?: string | null;  // MCP only: exact tool name match
  toolNamePattern?: string | null; // MCP only: wildcard pattern, e.g. "delete_*"
}

interface PolicyConditions {
  minRiskLevel?: "low" | "medium" | "high" | "critical";
  maxRiskLevel?: "low" | "medium" | "high" | "critical";
  allowedAgents?: string[];
  blockedAgents?: string[];
  allowedResourceTypes?: string[];
  blockedResourceTypes?: string[];
  customRules?: Record<string, unknown>;
}

Evaluation order

Matching logic

A policy matches when all of the following are true:
  1. tool equals the request tool (or policy tool is "*")
  2. actionPattern matches the action string (exact match or wildcard with * suffix, e.g. pull_request.*)
  3. resourceType matches when set
  4. riskLevel matches when set (policy risk level ≤ request risk level, based on minRiskLevel / maxRiskLevel in conditions)
  5. conditions.allowedAgents includes the agent ID when set
  6. conditions.blockedAgents does not include the agent ID when set
  7. For MCP: toolName exactly matches or toolNamePattern matches the MCP tool name

Risk levels

Risk levels are ordered: low < medium < high < critical.
LevelExamples
lowGET requests, read operations, list operations
mediumCreate issue, create comment, update issue, Linear issue create
highCreate pull request, push commit, create release
criticalDelete branch, force-push, delete repository
For MCP tools, risk level is inferred from the tool name:
  • Name starts with get, list, readlow
  • Name contains delete, destroy, drop, removehigh
  • Everything else → medium

Default behavior

If no policy matches, the engine returns allow. To lock down your workspace, add a catch-all block policy at a high priority number (e.g. 999) and add specific allow or require_approval policies at lower numbers.
// Example: block all, then allow reads
[
  {
    "name": "Allow all reads",
    "tool": "github",
    "actionPattern": "read",
    "mode": "allow",
    "priority": 10
  },
  {
    "name": "Require approval for PRs",
    "tool": "github",
    "actionPattern": "pull_request.create",
    "mode": "require_approval",
    "priority": 20
  },
  {
    "name": "Block everything else",
    "tool": "github",
    "actionPattern": "*",
    "mode": "block",
    "priority": 999
  }
]
Lower priority numbers are evaluated first. Set your most specific rules at low numbers, your catch-all rules at high numbers.