Policies and scoped access
Grant an agent read access to one secret path, then use deny rules and priority to carve exceptions out of a broad grant.
A fresh agent has zero access. Its JWT carries an empty scope list until a human writes a policy. Policies tie a principal to path patterns and permissions, so you decide exactly what an agent can touch.
- * matches one path segment
- ** matches zero or more segments (any depth)
- read: GET a value and list metadata
- write: PUT and DELETE at matching paths
When you create, update, or delete an agent's policy, all of that agent's active JWTs are revoked immediately. The agent must re-exchange its API key to pick up the new scope.
- 1
Export your keys and IDs. Use the values saved from Lesson 1.
bashexport ONECLAW_API_KEY="1ck_your_key_here" export VAULT_ID="your-vault-uuid-here" export AGENT_ID="your-agent-uuid-here" - 2
Create a policy that lets the agent read only api-keys/openai. The pattern is a single path, so the agent cannot reach anything else in the vault.
bashcurl -s -X POST https://api.1claw.co/v1/vaults/$VAULT_ID/policies \ -H "Authorization: Bearer $ONECLAW_API_KEY" \ -H "Content-Type: application/json" \ -d '{"principal_type":"agent","principal_id":"'$AGENT_ID'","permissions":["read"],"secret_path_pattern":"api-keys/openai"}' - 3
Re-exchange the agent key for a fresh JWT. The old token was revoked by the policy change.
bashTOKEN=$(curl -s -X POST https://api.1claw.co/v1/auth/agent-token \ -H "Content-Type: application/json" \ -d '{"agent_id":"'$ONECLAW_AGENT_ID'","api_key":"'$ONECLAW_AGENT_KEY'"}' | jq -r .access_token) - 4
Read the allowed path (works), then try a different path. Both 403 (forbidden) and 404 (not found) confirm the agent cannot access that path.
bashcurl -s https://api.1claw.co/v1/vaults/$VAULT_ID/secrets/api-keys/openai \ -H "Authorization: Bearer $TOKEN" curl -s https://api.1claw.co/v1/vaults/$VAULT_ID/secrets/api-keys/stripe \ -H "Authorization: Bearer $TOKEN"
Policies also support runtime conditions: an IP allowlist (specific IPs or CIDR ranges) and a time window (allowed hours and days). Add these when an agent runs from known infrastructure.
Your agent can now read one secret path and nothing else, and any policy change takes effect on the very next request.
Policy Engine v2 (v0.47) added three fields that turn policies from a flat allow-list into a real evaluation model: effect, priority, and attribute_conditions.
- effect: "allow" or "deny". A deny rule lets you punch a hole in a broad allow.
- priority: an integer; higher wins when two policies both match the same path.
- attribute_conditions: JSONB for fine-grained matching beyond path, IP, and time.
- 1
Grant the agent broad read access across the vault.
bashcurl -s -X POST https://api.1claw.co/v1/vaults/$VAULT_ID/policies \ -H "Authorization: Bearer $ONECLAW_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "principal_type": "agent", "principal_id": "'$AGENT_ID'", "path_pattern": "app/**", "permissions": ["read"], "effect": "allow", "priority": 10 }' - 2
Now carve out the one prefix it must never read, with a higher priority so it wins the overlap.
bashcurl -s -X POST https://api.1claw.co/v1/vaults/$VAULT_ID/policies \ -H "Authorization: Bearer $ONECLAW_TOKEN" \ -H "Content-Type: application/json" \ -d '{ "principal_type": "agent", "principal_id": "'$AGENT_ID'", "path_pattern": "app/billing/**", "permissions": ["read"], "effect": "deny", "priority": 100 }' - 3
Verify. The first read succeeds, the second is denied by the higher-priority deny rule.
bash1claw secret get app/search/api-key # allowed 1claw secret get app/billing/stripe # denied
Priority decides overlaps, not effect. A deny rule does not automatically beat an allow: give it the higher priority, or the broad allow may win.
Decide
An agent needs to read everything under app/ except the billing prefix. You write an allow on app/** with priority 10, and a deny on app/billing/** with priority 5. In testing the agent reads app/billing/stripe successfully.
What is wrong?
Check your understanding
4 questionsWhat access does an agent have before any policy is written?
In a policy pattern, what does ** match?
What happens to active JWTs when an agent's policy changes?
An allow policy on app/** has priority 10 and a deny policy on app/billing/** has priority 5. What happens on a read of app/billing/stripe?