Checkpoint Docs
Enforce

Policies

Author enforcement policies in Cedar — from plain language to deployed

Overview

A Checkpoint policy is a Cedar policy that decides what happens when the Gateway or Middleware classifies a request. You author policies in Compose: describe the rule in plain language, Checkpoint compiles it to Cedar, you dry-run it against real traffic, then Authorize & deploy.

Migrating from the legacy policy config? Earlier versions of Checkpoint used a structured JSON config (default_action, block_threshold, allow_list / deny_list, path_rules). That model is superseded by Cedar — everything it expressed is now a Cedar rule (see Common patterns). A project with no deployed Cedar policy falls back to a permissive baseline until you deploy one.

How policies evaluate

Checkpoint uses an allow-by-default, carve-out posture. Every deployed policy starts from a baseline that permits all traffic:

@id("baseline-allow")
@verdict("ALLOW")
permit ( principal, action, resource );

Each rule you add is a forbid that carves out a stricter verdict — BLOCK, CHALLENGE, REDIRECT, or INSTRUCT — for the requests it matches. Cedar is forbid-overrides: when more than one rule applies to a request, the restriction wins. So a policy reads as "allow everything, except…" — you never enumerate the traffic you want to let through.

A rule matches on facts the detection engine supplies about each request:

FactExampleMeaning
resource.pathresource.path like "/checkout*"The request path (== exact, like glob)
principal.nameprincipal.name == "GPTBot"The detected agent's name
principal.categoryprincipal.category == "scraper"Class: ai_agent, scraper, etc.
principal.reputationprincipal.reputation.lessThan(decimal("0.7"))Agent reputation, 0.01.0
principal.delegationprincipal.delegation == "verified"Whether a valid KYA-OS delegation was presented
context.granted_scopescontext.granted_scopes.contains("settings:read")Consent scopes the agent has been granted

Use when { … } for the facts that make a rule apply and unless { … } for carve-outs (for example, exempting an agent that presents a verified delegation).

Authoring in Compose

Compose (Policy → Compose, /policy/compose) turns a plain-language description into a reviewable Cedar policy.

  1. Describe the rule. For example: "Block unverified agents from checkout, but let Claude and ChatGPT browse the catalog." Compose parses the intent and emits Cedar (⌘⏎ to compile).
  2. Review the Cedar. Compose shows the generated rules as editable sentence chips alongside the raw Cedar — what you see is what runs. Hand-edit the Cedar directly if you need to.
  3. Run dry test. The dry-run 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.
  4. Authorize & deploy. Once you've reviewed the dry-run and confirmed Compose's assumptions, Authorize & deploy promotes the policy to production. It's enforcing on the gateway within about a minute.

Draft vs. deployed

Save as draft (and edits made on a policy's detail page) update the draft only — the gateway keeps enforcing the last deployed version until you redeploy. A live policy shows an ● Enforcing on the gateway badge; a policy with saved-but-not-redeployed changes shows ● Enforcing a previous version — redeploy to apply. Deploying is what flips a project into engine enforcement.

Managing a policy

The policy list lives at Policy (/policy). Open a policy for its detail page, which has four tabs:

TabWhat it does
OverviewThe authentication & consent experience this policy produces
EditHand-edit this policy's raw Cedar block
BuilderEdit the policy visually as a graph
DecisionsThe allow / block / challenge log for this policy

Verdicts

A rule's @verdict(...) sets what happens to a matching request:

VerdictDescriptionHTTP response
ALLOWLet the request through (the baseline; also re-permits a subset)Proxied to origin
BLOCKReject the request403 Forbidden
REDIRECTSend to another URL (absolute or same-origin path)302 Redirect
CHALLENGERequire consent or step-up approval before proceeding401 (consent / approval flow)
INSTRUCTRequire a cryptographic KYA-OS identity401 with WWW-Authenticate: KYA

CHALLENGE and INSTRUCT are the consent / cryptographic paths and are enforced at the edge by the Gateway. In-app middleware enforces ALLOW / BLOCK / REDIRECT directly; challenge support varies by SDK — see Middleware and the .NET Cedar cookbook.

Choosing a verdict

Match the verdict to the threat model of the endpoint. Because policies are allow-by-default, you write rules for the traffic you want to restrict.

BLOCK

Return a 403 and refuse to serve the request. Use it when the endpoint is strictly not for agents — competitor scraping of proprietary content, admin surfaces, endpoints that cost money per request — or as a short-term block during an incident (e.g. an AI crawler hammering your origin).

Not a good fit for a general content site: a blanket block will catch legitimate AI agents your users are delegating to. Prefer REDIRECT or INSTRUCT for soft-to-cryptographic enforcement.

REDIRECT

Return a 302 to a URL you choose — a consent page, a sign-in flow, a pricing page, or your hosted Bouncer consent. It's soft enforcement: "you can get what you need, just through this other flow." The target accepts absolute URLs (https://acme.com/for-ai) and same-origin paths (/ai-welcome), so one policy can cover staging and production.

Not a good fit for sensitive endpoints — a 302 is trivial for a sophisticated agent to ignore. Use INSTRUCT there.

CHALLENGE

Require the agent to satisfy a consent or approval step before proceeding. Two shapes:

  • Consent — the agent must carry the scopes the rule demands (@scopes(...) + a context.granted_scopes carve-out). Use it to gate specific tools or data behind explicit user consent.
  • Step-up approval — the request must collect a quorum of approvals from named approvers (@approvers(...) + @quorum("N")). Use it for high-stakes actions like fund transfers.

INSTRUCT

Return a 401 KYA-OS challenge that requires a cryptographic identity on retry. Use it for sensitive work — payments, data exports, authenticated APIs, admin actions — where you want cryptographic assurance rather than best-effort detection. It is bypass-proof for agents that cannot forge a valid signature, and it defeats tool-delegation evasion (a request proxied through a third-party tool cannot carry the proof).

INSTRUCT requires the Gateway in front of your origin. Pair it with REDIRECT per path — INSTRUCT on /api/payments/*, REDIRECT on / — rather than as a site-wide default, so non-cooperating agents on public surfaces aren't stranded on a 401. See KYA-OS Enforcement for the full flow.

Common patterns

Real Cedar for the rules teams write most often. In Compose you'd describe these in plain language; the Cedar below is what it emits.

Block a specific agent everywhere:

@id("block-gptbot")
@verdict("BLOCK")
forbid ( principal, action, resource )
when { principal.name == "GPTBot" };

Block an agent on one path (allow it elsewhere):

@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:

@id("redirect-scrapers")
@verdict("REDIRECT")
@redirect("/for-ai")
forbid ( principal, action, resource )
when { principal.category == "scraper" };

Gate low-reputation agents behind a challenge:

@verdict("CHALLENGE")
forbid ( principal, action, resource )
when { principal.reputation.lessThan(decimal("0.7")) };

Require consent (scopes) for an agent on a path — unless it already has them:

@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:

@verdict("CHALLENGE")
@quorum("2")
@approvers("did:web:acme:approvers:alice did:web:acme:approvers:bob")
forbid ( principal, action, resource )
when { resource.path == "/transfer" };

Exempt verified agents from a restriction — add an unless carve-out so agents that present a valid delegation pass through:

unless { principal.delegation == "verified" }

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, so principal.name in [...] will not match.

Enforce vs. observe

Enforcement has an orthogonal mode that controls whether a matched verdict actually acts:

  • Observe — evaluate the policy and record what would have happened (the dashboard shows "Would have been: Block(…)"), but let every request through. Nothing is blocked.
  • Enforce — apply the verdict for real (403 for BLOCK, the consent/challenge flow for CHALLENGE/INSTRUCT, the 302 for REDIRECT).

The recommended rollout is to deploy in observe, watch the dashboard for a week or two, then flip to enforce once the verdicts look right. The enforcement toggle is a per-project switch, separate from deploying a policy.

Some Cedar facts — principal.delegation == "verified", context.granted_scopes.contains(...) — come from the identity layer, not detection. That layer (auth methods, providers, per-tool consent) is configured under Govern as Protections; it produces the facts your Cedar policy evaluates. A common pattern is "known, verified agents welcome; unknown agents challenged or blocked", expressed with an unless { principal.delegation == "verified" } carve-out on an otherwise-restrictive rule.

Testing a policy

  1. Compose the rule and Run dry test — replays it against the real engine with no traffic affected.
  2. Authorize & deploy in observe mode and review the "would have been" decisions in the dashboard.
  3. Tune the rules against real traffic, then flip to enforce when you're confident.

Next Steps