Skip to main content

Policy Syntax Reference

ThoughtGate supports two levels of policy configuration: YAML governance rules (primary) and Cedar policies (advanced).

YAML Governance Rules​

The primary way to configure ThoughtGate.

Structure​

governance:
defaults:
action: forward # Default when no rule matches
rules:
- match: "pattern"
action: forward|approve|deny|policy
approval: workflow-name # For action: approve
policy_id: policy-name # For action: policy

Pattern Syntax​

Patterns use glob matching:

PatternMatches
delete_userExact match
delete_*delete_user, delete_file, etc.
*_dangerousvery_dangerous, somewhat_dangerous
tool_?tool_a, tool_1 (single char)
[abc]_toola_tool, b_tool, c_tool

Actions​

ActionBehavior
forwardSend to upstream immediately
denyReturn -32003 PolicyDenied error
approveCreate SEP-1686 task, post to Slack
policyEvaluate Cedar policy for decision

Cedar Policies (Advanced)​

For complex access control logic that can't be expressed in YAML.

When to Use Cedar​

  • Conditional logic based on argument values
  • Complex boolean expressions
  • Role-based access control
  • Multi-factor decisions

Structure​

permit|forbid(
principal,
action == Action::"<action>",
resource
) when {
<conditions>
};

Cedar Actions​

ActionResult
Action::"Forward"Send to upstream immediately
Action::"Approve"Require human approval
Action::"Deny"Return error

Resource Attributes​

For tools/call requests:

AttributeTypeDescription
resource.tool_nameStringName of the tool being called
resource.argumentsObjectTool arguments (JSON)
resource.methodStringMCP method name

Conditions​

String Comparisons​

when { resource.tool_name == "delete_user" }
when { resource.tool_name != "safe_tool" }
when { resource.tool_name like "admin_*" }

Numeric Comparisons​

when { resource.arguments.amount > 10000 }
when { resource.arguments.count >= 5 }
when { resource.arguments.priority < 3 }

Boolean Logic​

when {
resource.tool_name == "transfer" &&
resource.arguments.amount > 1000
}

when {
resource.tool_name == "delete" ||
resource.tool_name == "remove"
}

Evaluation Order​

  1. All forbid policies evaluated first
  2. If any forbid matches → Deny
  3. All permit policies evaluated
  4. First matching permit → Use that action
  5. No match → Implicit deny

Examples​

YAML: Basic Governance​

governance:
defaults:
action: forward
rules:
# Block admin tools
- match: "admin_*"
action: deny
# Approve destructive ops
- match: "delete_*"
action: approve
- match: "drop_*"
action: approve

YAML: Multiple Approval Channels​

governance:
rules:
- match: "delete_*"
action: approve
approval: ops
- match: "transfer_*"
action: approve
approval: finance

approval:
ops:
adapter: slack
channel: "#ops"
timeout: 5m
finance:
adapter: slack
channel: "#finance"
timeout: 10m

Cedar: Value-Based Routing​

// Low-value transfers: forward
permit(
principal,
action == Action::"Forward",
resource
) when {
resource.tool_name == "transfer_funds" &&
resource.arguments.amount <= 1000
};

// High-value transfers: approve
permit(
principal,
action == Action::"Approve",
resource
) when {
resource.tool_name == "transfer_funds" &&
resource.arguments.amount > 1000
};

Cedar: Deny with Exceptions​

// Allow specific safe admin tools
permit(
principal,
action == Action::"Forward",
resource
) when {
resource.tool_name == "admin_read_config"
};

// Deny all other admin tools
forbid(
principal,
action,
resource
) when {
resource.tool_name like "admin_*"
};

Validation​

YAML Validation​

# Check YAML syntax
cat thoughtgate.yaml | python -c "import sys, yaml; yaml.safe_load(sys.stdin)"

Cedar Validation​

# Validate Cedar syntax
cedar validate --policies policy.cedar

Hot Reload​

ThoughtGate watches configuration files and reloads on changes. No restart required.

# Modify config file
vim thoughtgate.yaml

# ThoughtGate logs: "Configuration reloaded successfully"