This directory contains Cedar schema and policies for authorizing OpenClaw agent tool executions.
schema.cedarschema- Cedar schema defining entity types, actions, and contextpolicies.cedar- Sample authorization policies demonstrating common patternsentities.json- Entity store (agents and tools) - see belowconfig.json- Cedar-agent configuration - see below
# Option A: Using Homebrew (macOS/Linux)
brew install cedar
# Option B: Using Cargo
cargo install cedar-policy-cli
# Option C: Download binary from GitHub releases
# https://github.com/cedar-policy/cedar/releasesCreate entities.json with agent and tool entities:
[
{
"uid": {
"type": "OpenClaw::Agent",
"id": "agent-abc123"
},
"attrs": {
"role": "developer",
"securityLevel": "medium"
},
"parents": []
},
{
"uid": {
"type": "OpenClaw::Tool",
"id": "bash"
},
"attrs": {
"category": "shell",
"riskLevel": "high"
},
"parents": []
},
{
"uid": {
"type": "OpenClaw::Tool",
"id": "read"
},
"attrs": {
"category": "file_operation",
"riskLevel": "low"
},
"parents": []
},
{
"uid": {
"type": "OpenClaw::Tool",
"id": "write"
},
"attrs": {
"category": "file_operation",
"riskLevel": "medium"
},
"parents": []
}
]cedar validate --schema schema.cedarschema --policies policies.cedarCreate a test request file test-request.json:
{
"principal": "OpenClaw::Agent::\"agent-abc123\"",
"action": "OpenClaw::Action::\"ToolExec::Bash\"",
"resource": "OpenClaw::Tool::\"bash\"",
"context": {
"toolCallId": "toolu_123",
"command": "ls -la",
"sessionKey": "session-xyz"
}
}Test with Cedar CLI:
cedar authorize \
--schema schema.cedarschema \
--policies policies.cedar \
--entities entities.json \
--request test-request.jsonIf you have cedar-agent installed:
# Start cedar-agent on port 8180
cedar-agent \
--schema schema.cedarschema \
--policies policies.cedar \
--entities entities.json \
--port 8180Create a simple HTTP wrapper around Cedar (example in Node.js):
// pdp-server.js
import { createServer } from 'http';
import { spawn } from 'child_process';
const server = createServer(async (req, res) => {
if (req.method !== 'POST' || req.url !== '/authorize') {
res.writeHead(404);
res.end();
return;
}
let body = '';
for await (const chunk of req) {
body += chunk;
}
const authzRequest = JSON.parse(body);
// Convert to Cedar CLI format
const cedarRequest = {
principal: authzRequest.principal,
action: authzRequest.action,
resource: authzRequest.resource,
context: authzRequest.context || {}
};
// Call cedar CLI
const cedar = spawn('cedar', [
'authorize',
'--schema', 'schema.cedarschema',
'--policies', 'policies.cedar',
'--entities', 'entities.json',
'--request-json', JSON.stringify(cedarRequest)
]);
let output = '';
cedar.stdout.on('data', (data) => output += data);
await new Promise((resolve) => cedar.on('close', resolve));
const decision = output.includes('ALLOW') ? 'Allow' : 'Deny';
res.writeHead(200, { 'Content-Type': 'application/json' });
res.end(JSON.stringify({
decision,
diagnostics: { reason: [], errors: [] }
}));
});
server.listen(8180, () => {
console.log('Cedar PDP listening on http://localhost:8180');
});Run it:
node pdp-server.jsAdd to your openclaw.json5:
{
authz: {
pdp: {
enabled: true,
endpoint: "http://localhost:8180/authorize",
timeoutMs: 2000,
failOpen: false // fail-closed: deny on PDP errors
}
}
}permit(
principal,
action in [
OpenClaw::Action::"ToolExec::Read",
OpenClaw::Action::"ToolExec::Glob"
],
resource
);
Allows all agents to use read-only tools without restrictions.
permit(
principal,
action == OpenClaw::Action::"ToolExec::Write",
resource
)
when {
context.filePath.startsWith("/tmp/")
};
Allows write operations only to specific directories using context attributes.
forbid(
principal,
action == OpenClaw::Action::"ToolExec::Bash",
resource
)
when {
context.command.contains("rm -rf")
};
Blocks dangerous commands using pattern matching on context.
permit(
principal,
action == OpenClaw::Action::"ToolExec::Bash",
resource
)
when {
context.command.startsWith("git ")
};
Allows only specific command patterns (e.g., git operations).
curl -X POST http://localhost:8180/authorize \
-H "Content-Type: application/json" \
-d '{
"principal": "OpenClaw::Agent::\"agent-abc123\"",
"action": "OpenClaw::Action::\"ToolExec::Read\"",
"resource": "OpenClaw::Tool::\"read\"",
"context": {
"toolCallId": "toolu_001",
"filePath": "/home/user/code/main.py",
"sessionKey": "session-123"
}
}'Expected: Allow (policy-1-allow-readonly)
curl -X POST http://localhost:8180/authorize \
-H "Content-Type: application/json" \
-d '{
"principal": "OpenClaw::Agent::\"agent-abc123\"",
"action": "OpenClaw::Action::\"ToolExec::Write\"",
"resource": "OpenClaw::Tool::\"write\"",
"context": {
"toolCallId": "toolu_002",
"filePath": "/etc/passwd",
"sessionKey": "session-123"
}
}'Expected: Deny (policy-3-deny-system-writes)
curl -X POST http://localhost:8180/authorize \
-H "Content-Type: application/json" \
-d '{
"principal": "OpenClaw::Agent::\"agent-abc123\"",
"action": "OpenClaw::Action::\"ToolExec::Write\"",
"resource": "OpenClaw::Tool::\"write\"",
"context": {
"toolCallId": "toolu_003",
"filePath": "/tmp/output.txt",
"sessionKey": "session-123"
}
}'Expected: Allow (policy-2-allow-tmp-writes)
curl -X POST http://localhost:8180/authorize \
-H "Content-Type: application/json" \
-d '{
"principal": "OpenClaw::Agent::\"agent-abc123\"",
"action": "OpenClaw::Action::\"ToolExec::Bash\"",
"resource": "OpenClaw::Tool::\"bash\"",
"context": {
"toolCallId": "toolu_004",
"command": "rm -rf /",
"sessionKey": "session-123"
}
}'Expected: Deny (policy-5-deny-dangerous-bash)
curl -X POST http://localhost:8180/authorize \
-H "Content-Type: application/json" \
-d '{
"principal": "OpenClaw::Agent::\"agent-abc123\"",
"action": "OpenClaw::Action::\"ToolExec::Bash\"",
"resource": "OpenClaw::Tool::\"bash\"",
"context": {
"toolCallId": "toolu_005",
"command": "git status",
"sessionKey": "session-123"
}
}'Expected: Allow (policy-4-allow-safe-bash OR policy-6-allow-git-ops)
Cedar evaluates policies in this order:
- Forbid policies are evaluated first
- If any forbid policy matches → Deny
- Permit policies are evaluated
- If any permit policy matches → Allow
- If no policy matches → Deny (default deny)
This means forbid policies always win over permit policies.
-
Update
schema.cedarschemawith new action:action "ToolExec::MyNewTool" appliesTo { principal: [Agent], resource: [Tool], context: ToolContext }; -
Add policies for the new action in
policies.cedar
-
Update entity type in
schema.cedarschema:entity Agent = { "role"?: String, "department"?: String, // new attribute }; -
Update
entities.jsonwith new attributes -
Reference in policies:
permit(...) when { principal.department == "engineering" };
-
Update
ToolContextinschema.cedarschema:type ToolContext = { "toolCallId": String, "timestamp"?: Long, // new attribute }; -
Update OpenClaw PDP client to include new context
-
Reference in policies:
permit(...) when { context.timestamp > 1234567890 };
cedar validate --schema schema.cedarschema --policies policies.cedarCheck for:
- Typos in entity type names
- Missing action definitions
- Invalid attribute types
- Check entity UIDs match exactly (case-sensitive)
- Verify action name matches schema
- Test with Cedar CLI to see which policies match
- Add debug logging in cedar-agent
- Keep policies simple (avoid complex string operations)
- Use indexed lookups where possible
- Consider caching authorization decisions for identical requests
- Monitor PDP response times and adjust timeout