Checkpoint Docs
Cookbooks

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

  1. Log into your Checkpoint dashboard
  2. Select your project
  3. 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 OK

Review 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 forbid rules 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

SymptomCauseFix
Changes aren't taking effectSaved as a draft but not redeployedAuthorize & deploy — a draft doesn't change the live policy
Still enforcing the old rulesThe gateway caches the deployed policy (~1 min)Wait a minute, or purge the cache from the policy detail page
Everything is being blockedA forbid rule is too broadNarrow the when {…} conditions; remember traffic is allowed by default
Wrong verdict on a requestA different rule matched (forbid-overrides)Run dry test — it shows which rule matched and why
A verified agent is blockedNo delegation carve-outAdd 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

GoalAction
Set up detection firstGateway or Middleware
Authorize AI agentsGovern Overview
Monitor trafficDashboard Analytics