Advanced Feature: This demo extends the basic Cedar authorization demo with Typed Partial Evaluation (TPE), allowing the agent to proactively query what operations are allowed before attempting them.
📚 Start with the basics first! Complete the main Cedar demo before exploring this advanced feature.
The basic demo shows reactive authorization - the agent attempts operations and learns from denials:
Agent → Attempt /etc write → Denied → Try /tmp instead → Success
This demo shows proactive authorization - the agent queries constraints before planning:
Agent → "What can I write to?" → "/tmp/* or /var/tmp/*" → Plan to use /tmp → Success
Cedar's TPE feature allows evaluating policies with partial information. Instead of checking a specific operation:
Traditional: "Can I write to /etc/test.txt?" → Deny
TPE: "What file paths can I write to?" → filePath like "/tmp/*" OR filePath like "/var/tmp/*"
The agent receives constraint expressions that describe the space of allowed operations.
This extends the basic architecture with a planning phase before execution. The code in this repo adds a query_authorization_constraints tool to OpenClaw that calls the PDP's TPE endpoint, receiving residual policies that describe what's allowed without specifying a concrete operation. The agent interprets these constraints and plans accordingly — then during execution, the PEP still validates each concrete operation as before.
The full agent loop with query constraints integrated looks like this:
The key insight is that the planning phase and execution phase use different PDP endpoints: /query-constraints returns partial policy residuals (what could be allowed), while /authorize makes a concrete allow/deny decision for a specific operation. Together they give the agent both foresight and enforcement.
Adding the tool alone isn't enough — the agent also needs to know when to use it. Without guidance, the agent may read the Cedar policy files directly from disk instead of querying the live PDP. When the query constraints endpoint is configured, this repo injects an "Authorization Policy" section into the agent's system prompt (see src/agents/pi-embedded-runner/run/attempt.ts) instructing the agent to:
- Use the
query_authorization_constraintstool before file or command operations - Not read policy files from disk to determine permissions
- Pass the relevant action type (
write,read,bash, oredit)
This ensures the agent queries the live policy decision point rather than trying to interpret raw policy files on its own.
- Cedar CLI with TPE support - Follow the main README's Cedar installation instructions which build Cedar with the
--features tpeflag - Completed basic demo - Understand reactive authorization first by completing the basic demo
Before proceeding, verify your Cedar installation includes TPE:
# Check for TPE subcommand
cedar tpe --help
# If this fails with "unrecognized subcommand", you need to rebuild Cedar
# with the TPE feature flag as described in the main READMEComplete the basic Cedar demo first. Make sure:
- ✅ PDP server runs
- ✅ Basic authorization tests pass
- ✅ Agent examples work
The PDP server now supports two endpoints:
POST /authorize- Standard authorization (basic demo)POST /query-constraints- TPE constraint queries (this demo)
Make sure the PDP server is running before testing (if not already started from Step 1):
python3 demo/cedar-pdp-server.pyTest the TPE endpoint:
python3 demo/test-query-constraints.pyExpected output:
======================================================================
Cedar TPE Query Constraints Tests
======================================================================
Test: Query: What file paths can I write to?
Query: Write
Decision: UNKNOWN
Residuals: 11 policies
Example constraint:
(context has filePath) && (
(context.filePath) like "/tmp/*" ||
(context.filePath) like "/var/tmp/*"
)
✓ PASS
Test: Query: What bash commands can I execute?
Query: Bash
Decision: UNKNOWN
Residuals: 11 policies
Example constraint:
(context has command) && (
(context.command) like "ls *" ||
(context.command) like "git status*" ||
...
)
✓ PASS
Test: Query: What files can I read?
Query: Read
Decision: UNKNOWN
Residuals: 11 policies
✓ PASS
======================================================================
Results: 3 passed, 0 failed
======================================================================
The agent now has a built-in query_authorization_constraints tool. When the PDP is enabled and queryConstraintsEndpoint is configured, the tool is automatically available. Run:
pnpm openclaw agent --agent main --message "I want to create a file with the content 'Hello World'. Please check your authorization constraints first to find out where you can write files."What happens:
- Agent calls
query_authorization_constraintswithaction: "write" - Receives residual policies showing
/tmp/*or/var/tmp/*are allowed - Agent presents options to user: "I can write to /tmp or /var/tmp - where would you like the file?"
- User chooses location (e.g.,
/tmp/hello.txt) - Agent executes write with content "Hello World", which succeeds (already knows it's allowed)
Compare to basic demo:
- Basic: Agent tries
/etc, fails, learns, tries/tmp, succeeds (2 attempts, no user choice) - TPE: Agent queries first, presents options to user, writes to chosen location, succeeds (1 attempt, user-directed)
pnpm openclaw agent --agent main --message "Check what's in ~/openclaw-demo-protected/credentials/ and tell me what you find"What happens:
- Agent queries read constraints via
query_authorization_constraints - Discovers that paths matching
*/openclaw-demo-protected/credentials/*are forbidden by policy - Agent explains upfront that it cannot access the credentials directory due to authorization policy
- No failed attempts — the agent knows the boundaries before trying, thanks to Cedar TPE
pnpm openclaw agent --agent main --message "What git actions are you allowed to do?"What happens:
- Agent calls
query_authorization_constraintswithaction: "bash" - Receives residual policies showing which commands are permitted (e.g.,
git status,git add,git commit, etc.) - Agent summarizes the allowed git operations based on the policy constraints
cedar-pdp-server.py now supports:
New endpoint: POST /query-constraints
Request format:
{
"action": "OpenClaw::Action::\"ToolExec::Write\"",
"principal": "OpenClaw::Agent::\"main\"",
"resource": "OpenClaw::Tool::\"write\""
// Note: no context field - that's what we're querying about
}Response format:
{
"decision": "UNKNOWN",
"residuals": [
"@id(\"policy-2-allow-tmp-writes\")\npermit(\n principal,\n action,\n resource\n) when {\n (context has filePath) && (((context.filePath) like \"/tmp/*\") || ((context.filePath) like \"/var/tmp/*\"))\n};",
"@id(\"policy-3-deny-system-writes\")\nforbid(\n principal,\n action,\n resource\n) when {\n (context has filePath) && (...)\n};"
],
"explanation": "These are the policy constraints that must be satisfied for authorization"
}The tool is implemented in src/agents/tools/query-authz-tool.ts and registered automatically when authz.pdp.enabled and queryConstraintsEndpoint are both configured.
Tool definition:
{
name: "query_authorization_constraints",
description: "Query what operations are allowed by the authorization system. Use this BEFORE attempting file or command operations to discover what's permitted and what constraints apply.",
parameters: {
action: {
type: "string", // "write", "read", "bash", "edit", or "exec"
}
}
}Tool output format:
{
"action": "write",
"decision": "UNKNOWN",
"constraintCount": 2,
"constraints": "[Policy 1]\n@id(\"policy-2-allow-tmp-writes\")\npermit(...) when { ... };\n\n[Policy 2]\n@id(\"policy-3-deny-system-writes\")\nforbid(...) when { ... };",
"explanation": "These are the policy constraints that must be satisfied for authorization"
}Configuration (openclaw.json5):
{
authz: {
pdp: {
enabled: true,
endpoint: "http://localhost:8180/authorize",
queryConstraintsEndpoint: "http://localhost:8180/query-constraints"
}
}
}The PDP server calls Cedar TPE with individual arguments (no context):
cedar tpe \
--schema schema.cedarschema \
--policies policies-tpe.cedar \
--entities entities.json \
--principal-type "OpenClaw::Agent" \
--principal-eid "main" \
--action 'OpenClaw::Action::"ToolExec::Write"' \
--resource-type "OpenClaw::Tool" \
--resource-eid "write"
# Note: no --context argument - that's what we're querying aboutCedar returns:
- Decision: UNKNOWN (can't decide without context)
- Residual policies - the constraints that must be satisfied for authorization
The agent receives the raw Cedar policy residuals and interprets them directly to understand what operations are allowed.
Agent query:
POST /query-constraints
{
"principal": "OpenClaw::Agent::\"main\"",
"action": "OpenClaw::Action::\"ToolExec::Write\"",
"resource": "OpenClaw::Tool::\"write\""
}TPE response:
{
"decision": "UNKNOWN",
"residuals": [
"@id(\"policy-2-allow-tmp-writes\")\npermit(principal, action, resource) when {\n (context has filePath) && (((context.filePath) like \"/tmp/*\") || ((context.filePath) like \"/var/tmp/*\"))\n};",
"@id(\"policy-3-deny-system-writes\")\nforbid(principal, action, resource) when {\n (context has filePath) && (((context.filePath) like \"/etc/*\") || ...)\n};"
],
"explanation": "These are the policy constraints that must be satisfied for authorization"
}Agent interpretation: Looking at the residual policies, the agent understands:
- ✅ Permitted: Writes to paths matching
/tmp/*or/var/tmp/* - ❌ Forbidden: Writes to paths matching
/etc/*,/usr/*,/bin/*, etc.
Agent reasoning:
- "I can write to /tmp or /var/tmp"
- "I cannot write to /etc or other system directories"
- "I'll use /tmp/myfile.txt"
- Executes write successfully on first try
Agent query:
POST /query-constraints
{
"principal": "OpenClaw::Agent::\"main\"",
"action": "OpenClaw::Action::\"ToolExec::Bash\"",
"resource": "OpenClaw::Tool::\"bash\""
}TPE response:
{
"decision": "UNKNOWN",
"residuals": [
"@id(\"policy-4-allow-safe-bash\")\npermit(principal, action, resource) when {\n (context has command) && (((context.command) like \"ls *\") || ((context.command) like \"git status*\") || ...)\n};",
"@id(\"policy-5-deny-dangerous-bash\")\nforbid(principal, action, resource) when {\n (context has command) && (((context.command) like \"*rm -rf*\") || ((context.command) like \"*shutdown*\") || ...)\n};"
]
}Agent interpretation: Looking at the residual policies, the agent understands:
- ✅ Permitted: Commands matching
ls *,git status*,cat *,grep *, etc. - ❌ Forbidden: Commands matching
*rm -rf*,*shutdown*,*reboot*, etc.
Agent reasoning:
- "I can run ls, git status, cat, grep"
- "I cannot run rm -rf, shutdown, or other dangerous commands"
- "I'll use 'ls -la' to list files"
- Executes bash successfully on first try
When you run the agent with the PDP server's log window open, you'll see the raw Cedar residuals for each query_authorization_constraints call. Here's how to interpret what you see.
| Residual condition | Meaning |
|---|---|
forbid when { false } |
Policy eliminated — can never fire for this principal/action/resource |
forbid when { true } |
Unconditional deny — always blocks, regardless of context |
forbid when { <expr> } |
Conditional deny — blocks when context satisfies the expression |
permit when { false } |
Policy eliminated — can never grant for this principal/action/resource |
permit when { true } |
Unconditional allow |
permit when { <expr> } |
Conditional allow — grants when context satisfies the expression |
The most common surprise is seeing residuals like this:
@id("policy-7-deny-credential-files")
forbid(
principal,
action,
resource
) when {
false
};
This policy is not in play at all. Cedar's partial evaluator resolved the principal, action, and resource components of the policy without needing context, and determined the conditions can never be satisfied for this query. It's telling you this policy is irrelevant — you can ignore it.
TPE includes these eliminated policies in the residual output so you can see every policy it considered, not just the active ones. The residuals you actually need to act on are the ones with real expressions in their when clause.
When scanning the logs, filter your attention:
forbid when { <expr> }residuals — these are your hard constraints. The expression describes exactly what context values will trigger a deny.permit when { <expr> }residuals — these describe the space of allowed operations. Context must satisfy at least one of these (and none of the forbids) for a permit.* when { false }residuals — noise from TPE's exhaustive output; skip them.forbid when { true }residuals — a red flag. An unconditional deny means the agent can never perform this action type regardless of context.
A write constraint query might produce logs like:
policy-1-allow-agent: permit when { true } ← agent is always a valid principal
policy-2-allow-tmp-writes: permit when { (context has filePath) && (...filePath like "/tmp/*"...) } ← actionable
policy-3-deny-system-writes: forbid when { (context has filePath) && (...filePath like "/etc/*"...) } ← actionable
policy-7-deny-credential-files: forbid when { false } ← eliminated, ignore
The agent reads these residuals and concludes: "I can write to /tmp/*, I cannot write to /etc/*, and the credential-file policy doesn't apply to write operations at all."
- ❌ Reactive: Try
/etc→ fail → try/tmp→ succeed (2 tool calls) - ✅ Proactive: Query constraints → plan
/tmp→ succeed (1 tool call)
- Agent understands why certain paths work (matches
/tmp/*pattern) - Agent can explain constraints to users
- Reduces trial-and-error
- Agent appears more intelligent (knows what's allowed upfront)
- Clearer explanations ("I can write to /tmp because...")
- Fewer failed attempts visible to users
- ✅ More efficient (fewer failed attempts)
- ✅ Better agent understanding of constraints
- ✅ Clearer explanations to users
- ❌ More complex implementation (requires TPE)
- ❌ Extra PDP call during planning phase
⚠️ May reveal policy structure to adversarial users⚠️ Agent must remember to use the query tool
| Aspect | Reactive (Basic Demo) | Proactive (TPE Demo) |
|---|---|---|
| Agent approach | Try → Learn from failures | Query → Plan → Execute |
| Tool calls | More (includes failures) | Fewer (informed planning) |
| Complexity | Simpler (no TPE) | More complex (requires TPE) |
| User experience | See agent learning | See agent planning |
| Best for | Exploratory tasks | Constrained environments |
- Cedar TPE Documentation - Official Cedar TPE guide
- Basic Cedar Demo - Start here for reactive authorization
- Cedar Playground - Test TPE queries online
- Compare approaches - Run same task with and without TPE
- Integrate into your workflow - Decide when reactive vs proactive is best
Questions? The basic demo focuses on reactive authorization (simpler, good for exploration). This advanced demo adds proactive queries (more complex, better for constrained environments). Choose the approach that fits your use case!

