Enforce: Policy Configuration
Author, dry-run, and deploy enforcement policies with Compose
Goal
Author an enforcement policy in Compose — describe it in plain language, review the Cedar it compiles to, dry-run it against real traffic, and deploy. By the end you'll be able to:
- Compose a policy from plain language and read the Cedar it emits
- Dry-run a policy before it touches a single request
- Deploy in observe mode, then flip to enforce
- Reach for the right Cedar pattern for common scenarios
Prerequisites: You should have either Gateway or Middleware enforcement already set up.
Time Estimate
15 minutes
How policies work
A Checkpoint policy is a Cedar policy with an allow-by-default, carve-out posture: every deployed bundle starts by permitting all traffic, and each rule you add is a forbid that carves out a stricter verdict (BLOCK, CHALLENGE, REDIRECT, INSTRUCT) for the requests it matches. Cedar is forbid-overrides — when multiple rules match, the restriction wins. There's no priority list to maintain; you just write rules for the traffic you want to restrict. See the Policies reference for the full model.
Steps
Open Compose
- Log into your Checkpoint dashboard
- Select your project
- Go to Policy → Compose (
/policy/compose)
Describe the rule in plain language
Type what you want in the composer and press ⌘⏎ to compile. For example:
Block unverified agents from checkout, but let Claude and ChatGPT browse the catalog.
Compose parses the intent, emits Cedar, and flags any assumptions it had to make so you can confirm them.
Review the Cedar
Compose shows the generated rules as editable sentence chips alongside the raw Cedar — what you see is what runs. The example above compiles to something like:
@id("block-unverified-on-checkout")
@verdict("BLOCK")
forbid ( principal, action, resource )
when { resource.path like "/checkout*" }
unless { principal.delegation == "verified"
|| principal.name == "Claude" || principal.name == "ChatGPT" };Hand-edit the Cedar directly in the pane if you need to fine-tune it.
Run a dry test
Click Run dry test. Checkpoint replays the policy against representative agents (or your last 7 days of traffic) through the real engine — dry run · no traffic affected — and shows the verdict and the reason for each request. Confirm the rules do what you intended before deploying.
Deploy — observe first, then enforce
Once you've reviewed the dry-run and confirmed Compose's assumptions, click Authorize & deploy. The policy is enforcing on the gateway within about a minute, and the surface shows an ● Enforcing on the gateway badge.
Roll out safely: deploy in observe mode first — the policy records what it would do (the dashboard shows "Would have been: Block(…)") without affecting traffic. Watch for a week or two, then flip the project to enforce. See Enforce vs. observe.
Test the deployed policy
With the policy enforcing, verify the HTTP behavior with curl:
# An unverified agent hitting checkout — should be blocked
curl -I -H "User-Agent: Mozilla/5.0 (compatible; GPTBot/1.0)" \
https://your-domain.com/checkout
# Expected: 403 Forbidden
# A normal visitor — should pass through
curl -I https://your-domain.com/
# Expected: 200 OKReview decisions
Open the policy from the Policy list and check its Decisions tab for the live allow / block / challenge log, or use Dashboard Analytics for aggregate policy activity across the project.
Common patterns
Describe these in plain language in Compose; the Cedar below is what it emits. See the Policies reference for the full catalog.
Block a specific agent on one path ("Block GPTBot on /pricing"):
@id("block-scraper-on-pricing")
@verdict("BLOCK")
forbid ( principal, action, resource )
when { resource.path like "/pricing*" && principal.name == "GPTBot" };Redirect scrapers to an AI portal ("Send scrapers to /for-ai"):
@id("redirect-scrapers")
@verdict("REDIRECT")
@redirect("/for-ai")
forbid ( principal, action, resource )
when { principal.category == "scraper" };Require consent for an agent on a path — unless it already has the scopes ("Challenge ChatGPT on /account unless it has settings access"):
@id("challenge-chatgpt-on-account")
@verdict("CHALLENGE")
@scopes("settings:read settings:write")
forbid ( principal, action, resource )
when { (resource.path == "/account" || resource.path like "/account/*") && principal.name == "ChatGPT" }
unless { context.granted_scopes.contains("settings:read") && context.granted_scopes.contains("settings:write") };Require a 2-of-N approval quorum on a sensitive action ("Require two approvers for transfers"):
@verdict("CHALLENGE")
@quorum("2")
@approvers("did:web:acme:approvers:alice did:web:acme:approvers:bob")
forbid ( principal, action, resource )
when { resource.path == "/transfer" };Match a single agent with principal.name == "X"; match a set with an OR-disjunction
(principal.name == "X" || principal.name == "Y"). Cedar's in operator is for entity
hierarchies, not string sets.
Best practices
- Observe before you enforce. Deploy in observe mode, review the "would have been" decisions for real traffic, then flip to enforce.
- Dry-run every change. Run the dry test before each deploy — it replays against the real engine with no traffic affected.
- Redeploy to apply edits. Saving a draft (or editing on the detail page) doesn't change what's live — the gateway keeps enforcing the last deployed version until you Authorize & deploy again.
- Keep rules targeted. Because policies are allow-by-default, write narrow
forbidrules for exactly the traffic you want to restrict rather than broad ones you then have to carve exceptions out of. - Exempt verified agents with an
unless { principal.delegation == "verified" }carve-out so agents that prove identity aren't caught by a restriction.
Troubleshooting
| Symptom | Cause | Fix |
|---|---|---|
| Changes aren't taking effect | Saved as a draft but not redeployed | Authorize & deploy — a draft doesn't change the live policy |
| Still enforcing the old rules | The gateway caches the deployed policy (~1 min) | Wait a minute, or purge the cache from the policy detail page |
| Everything is being blocked | A forbid rule is too broad | Narrow the when {…} conditions; remember traffic is allowed by default |
| Wrong verdict on a request | A different rule matched (forbid-overrides) | Run dry test — it shows which rule matched and why |
| A verified agent is blocked | No delegation carve-out | Add unless { principal.delegation == "verified" } |
What You Learned
- How to author a policy in Compose from plain language and review the Cedar it emits
- How to dry-run a policy against real traffic before deploying
- The observe → enforce rollout
- Common Cedar patterns for real-world enforcement
Next Steps
| Goal | Action |
|---|---|
| Set up detection first | Gateway or Middleware |
| Authorize AI agents | Govern Overview |
| Monitor traffic | Dashboard Analytics |