agentgateway CEL policy pack
Package: @jiffylabs/agentgateway-policy-pack · Version: 0.1.0 · License: Apache-2.0
The agentgateway policy pack ships eight reference CEL (Common Expression Language) bundles that map Jiffy trust decisions — tier, ail_level, jts_score, framework_codes, parent_invalidated — into agentgateway's native policy engine. Paste a bundle into your agentgateway control plane and it will permit, warn, or block MCP tool calls statically at the gateway, while still delegating dynamic verdicts to the Sprint 52 Jiffy webhook (https://trust.jiffylabs.app/v1/agentgateway/guardrail).
- Static CEL pre-filter — declarative rules your SREs can read, diff, and promote through
kubectl apply. - Webhook fallback — live tier and AIL evaluation via the Jiffy trust endpoint, same shape as
@jiffylabs/agentgateway-adapter. - Eight seed bundles covering malicious-block, SOC 2 finance restriction, tier-1-only, agentic approval, framework-code coverage, and blast-radius parent invalidation.
Bundle catalog
| Slug | Default decision | Webhook required | Framework codes | CEL |
|---|---|---|---|---|
malicious-block | permit | yes | OWASP-Agentic-2026, MITRE-ATLAS | decision.tier == "CRITICAL" ? "block" : "permit" |
critical-only | permit | yes | OWASP-Agentic-2026, NIST-CSF-2.0 | (decision.tier in ["RISKY", "CRITICAL"]) && decision.ail_level >= 4 ? "block" : "permit" |
finance-restricted | permit | yes | SOC2-CC6.1, OWASP-Agentic-2026, NIST-CSF-2.0 | startsWith(request.mcp_server, "prod-") ? (decision.tier == "CAUTION" ? "warn" : (decision.tier in ["RISKY", "CRITICAL"] ? "block" : "permit")) : "permit" |
dev-permissive | warn | yes | (none — dev only) | decision.tier == "CRITICAL" ? "block" : "warn" |
agentic-only-with-approval | permit | yes | OWASP-Agentic-2026, NIST-CSF-2.0 | decision.ail_level >= 4 ? "warn" : "permit" |
tier1-only | block | yes | SOC2-CC6.1, NIST-CSF-2.0 | decision.tier == "TRUSTED" ? "permit" : "block" |
framework-codes-soc2 | permit | yes | SOC2-CC6.1, OWASP-Agentic-2026, NIST-CSF-2.0 | contains(decision.framework_codes, "OWASP-Agentic-2026") && contains(decision.framework_codes, "NIST-CSF-2.0") ? "permit" : "block" |
blast-radius-guard | permit | yes | OWASP-Agentic-2026, MITRE-ATLAS | has(decision.parent_invalidated) && decision.parent_invalidated == true ? "block" : "permit" |
All eight bundles invoke the Jiffy guardrail webhook for live tier and AIL resolution. requires_webhook: false is reserved for future bundles that are fully self-contained in CEL.
Bundle details
1. malicious-block — Block malicious tier
Blocks any MCP tool call whose Jiffy trust tier evaluates to CRITICAL. The customer-facing framing is "malicious"; the Jiffy webhook wire vocabulary uses CRITICAL as the highest-risk tier.
Default MCP route binding: mcp-malicious-block → mcp.github.com
decision.tier == "CRITICAL" ? "block" : "permit"
Verdict table
decision.tier | Verdict |
|---|---|
CRITICAL | block |
RISKY | permit |
CAUTION | permit |
TRUSTED | permit |
PENDING | permit |
2. critical-only — Block RISKY/CRITICAL at AIL 4+
Blocks tool calls that combine a high trust risk (RISKY or CRITICAL) with a high agentic-impact level (>= 4). Everything else permits.
Default MCP route binding: mcp-critical-only → mcp.linear.app
(decision.tier in ["RISKY", "CRITICAL"]) && decision.ail_level >= 4 ? "block" : "permit"
Verdict table
decision.tier | decision.ail_level | Verdict |
|---|---|---|
CRITICAL | 5 | block |
CRITICAL | 3 | permit |
RISKY | 4 | block |
CAUTION | 5 | permit |
TRUSTED | any | permit |
3. finance-restricted — SOC 2 finance restriction
Warn on CAUTION tier, block on RISKY or CRITICAL, but only for production finance MCP servers (mcp_server starts with prod-). Non-prod traffic passes through unaffected.
Default MCP route binding: mcp-finance-restricted → prod-payments-mcp
startsWith(request.mcp_server, "prod-") ? (decision.tier == "CAUTION" ? "warn" : (decision.tier in ["RISKY", "CRITICAL"] ? "block" : "permit")) : "permit"
Verdict table
request.mcp_server | decision.tier | Verdict |
|---|---|---|
prod-payments-mcp | CRITICAL | block |
prod-payments-mcp | CAUTION | warn |
prod-payments-mcp | TRUSTED | permit |
dev-sandbox-mcp | CRITICAL | permit |
4. dev-permissive — Dev sandbox, warn-only
Permits every call with a warn annotation except CRITICAL tier. Intended for local dev / sandbox use; do not ship to production. Empty framework_codes — this is not a compliance posture.
Default MCP route binding: mcp-dev-permissive → dev-sandbox-mcp
decision.tier == "CRITICAL" ? "block" : "warn"
Verdict table
decision.tier | Verdict |
|---|---|
CRITICAL | block |
| any other | warn |
5. agentic-only-with-approval — High agentic impact → require approval
For calls with agentic-impact level >= 4, emit a warn verdict with reason manual_approval_required so the agentgateway operator can route the call into its approval system. Low-impact calls pass through.
Default MCP route binding: mcp-agentic-approval → mcp.notion.com
decision.ail_level >= 4 ? "warn" : "permit"
Verdict table
decision.ail_level | Verdict |
|---|---|
| 5 | warn (reason: manual_approval_required) |
| 4 | warn (reason: manual_approval_required) |
| 3 | permit |
| 1 | permit |
6. tier1-only — TRUSTED tier only
Strictest posture in the seed pack: permit only TRUSTED-tier traffic; everything else (CAUTION, RISKY, CRITICAL, PENDING) is blocked. Use for air-gapped or tier-1 data classifications.
Default MCP route binding: mcp-tier1-only → mcp.github.com
decision.tier == "TRUSTED" ? "permit" : "block"
Verdict table
decision.tier | Verdict |
|---|---|
TRUSTED | permit |
| any other | block |
7. framework-codes-soc2 — SOC 2 framework-code coverage
Asserts framework coverage rather than artifact risk: permit only when the decision carries both OWASP-Agentic-2026 and NIST-CSF-2.0 framework codes. Used for SOC 2 CC6.1 evidence trails.
Default MCP route binding: mcp-framework-codes-soc2 → mcp.linear.app
contains(decision.framework_codes, "OWASP-Agentic-2026") && contains(decision.framework_codes, "NIST-CSF-2.0") ? "permit" : "block"
Verdict table
decision.framework_codes | Verdict |
|---|---|
| contains both OWASP-Agentic-2026 and NIST-CSF-2.0 | permit |
| missing either | block |
8. blast-radius-guard — Parent-artifact invalidation
Blocks tool calls whose parent artifact has been invalidated by a Sprint-22-linked disclosure event. Reads the webhook decision flag parent_invalidated — absent or false means permit.
Default MCP route binding: mcp-blast-radius-guard → mcp.notion.com
has(decision.parent_invalidated) && decision.parent_invalidated == true ? "block" : "permit"
Verdict table
decision.parent_invalidated | Verdict |
|---|---|
true | block |
false | permit |
| missing | permit |
CEL subset reference
Sprint 53 ships a frozen in-tree CEL validator and evaluator (packages/agentgateway-policy-pack/src/cel/). The supported subset is deliberately small — any bundle needing more is rewritten, not extended.
Literals
- Integer:
0,4,5 - String:
"CRITICAL","prod-payments-mcp" - Boolean:
true,false
Operators
| Category | Operators | ||
|---|---|---|---|
| Equality | ==, != | ||
| Ordering | <, <=, >, >= (integers only) | ||
| Membership | in (left scalar, right list literal: tier in ["RISKY", "CRITICAL"]) | ||
| Logical | &&, ` | , !` | |
| Ternary | cond ? a : b |
Field paths
Dotted paths only; single bracketed string-key access permitted for request.headers. Deeper nesting is not supported.
decision.tier—TRUSTED | CAUTION | RISKY | CRITICAL | PENDINGdecision.ail_level— integer 1–5decision.jts_score— integer 0–100decision.parent_invalidated— booleandecision.framework_codes— string listrequest.tool— tool identifier stringrequest.agent— agent identifier stringrequest.mcp_server— MCP server hostnamerequest.headers["X-Agent-Id"]— single header lookup
Built-in functions
Three only; no user-definable functions.
startsWith(s, prefix)→ booleancontains(list_or_string, needle)→ booleanhas(decision.field)→ boolean (true when the field is present and non-null)
Anything outside this subset is a CEL_VALIDATION_ERROR. Validator and evaluator share the same parser, so a policy that validates is guaranteed to evaluate (or throw a typed runtime error if the context is missing a referenced field).
Webhook URL customization
All eight bundles invoke the Jiffy trust webhook for live tier and AIL decisions. The default URL is https://trust.jiffylabs.app/v1/agentgateway/guardrail. Override it at emit time:
jiffy export-agentgateway-policy malicious-block \
--webhook-url=https://hook.internal.example/jiffy-guardrail
The emitted YAML's Webhook resource embeds the URL verbatim; no other substitution is performed. See the Sprint 52 webhook docs for the request/response schema, authentication, and idempotency guarantees expected by agentgateway operators.
How to deploy
- Install the CLI:
pnpm add -g @jiffylabs/jiffy-cli(or run from source viapnpm -F @jiffylabs/jiffy-cli start). - Emit the bundle YAML:
jiffy export-agentgateway-policy <slug>. - Apply to your agentgateway control plane:
kubectl apply -f -.
Example: jiffy export-agentgateway-policy malicious-block
# Generated by @jiffylabs/jiffy-cli (agentgateway-policy-pack 0.1.0)
# Bundle: malicious-block @ 1.0.0
# CEL SHA-256 pin: 724d5fe3f592e8b64b207dd8fff072d5a771fa55b3cfdca974e0d6481b01da4d
# Frameworks: OWASP-Agentic-2026, MITRE-ATLAS
---
apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
name: mcp-malicious-block-gateway
annotations:
jiffy.io/bundle: "malicious-block"
spec:
gatewayClassName: agentgateway
listeners:
- name: mcp
port: 443
protocol: HTTPS
---
apiVersion: gateway.networking.k8s.io/v1
kind: HTTPRoute
metadata:
name: mcp-malicious-block
spec:
parentRefs:
- name: mcp-malicious-block-gateway
rules:
- backendRefs:
- name: mcp.github.com
port: 443
---
apiVersion: agentgateway.dev/v1alpha1
kind: Policy
metadata:
name: malicious-block
labels:
jiffy.io/bundle: "malicious-block"
jiffy.io/pack-version: "0.1.0"
spec:
targetRefs:
- kind: HTTPRoute
name: mcp-malicious-block
defaultDecision: permit
cel: |
decision.tier == "CRITICAL" ? "block" : "permit"
---
apiVersion: agentgateway.dev/v1alpha1
kind: Webhook
metadata:
name: malicious-block-jiffy-guardrail
spec:
url: https://trust.jiffylabs.app/v1/agentgateway/guardrail
forwardHeaderMatches:
- X-Agent-Id
policyRef:
name: malicious-block
Exit codes
The CLI aligns with the existing jiffy export-mdm exit-code map:
| Code | Meaning |
|---|---|
0 | success |
1 | generic failure (reserved) |
2 | unknown bundle slug |
3 | malformed CLI input (missing slug, bad flag value, CEL validation error) |
Hardening bundle integration
Sprint 54 (F70) — composes the CEL policy above with a hardening bundle's Gateway + HTTPRoute metadata into a single kubectl apply-ready YAML.
Each Jiffy hardening template (see /templates) can optionally carry an agentgateway_bundle manifest. When set, the template pairs a CEL slug from this pack with a Gateway name + optional route overrides + optional webhook URL override. The CLI command jiffy export-agentgateway-bundle <bundle-slug> emits the combined deployment YAML.
Seed bundle pairings
The four seed hardening bundles ship with these default CEL pairings (migration 049 + web/src/lib/hardening-templates/seed.ts):
| Hardening bundle | CEL policy slug | Gateway name | Status |
|---|---|---|---|
soc2-claude-code | framework-codes-soc2 | soc2-claude-code-gateway | deployable |
cursor-enterprise-restricted | finance-restricted | cursor-enterprise-gateway | deployable |
amazonq-finance-regulated | finance-restricted | amazonq-finance-gateway | deployable |
claude-desktop-locked-down | tier1-only | claude-desktop-locked-gateway | deployable |
The 2-of-4 deployable gate (per the Sprint 54 contract): soc2-claude-code and cursor-enterprise-restricted are the formal deployable gate; the other two are expected to pass CRD validation but are not gate-blocking.
CRD schema pin
The emitter validates emitted documents against vendored upstream agentgateway CRD schemas at packages/jiffy-cli/src/vendored/agentgateway-crd-schemas.json. The file's pinned_sha field pins the upstream commit from https://github.com/agentgateway/agentgateway we validated against. Bumping the pin is a deliberate, separate sprint step.
Web surface
- Customer-facing:
/templates/[slug]renders a collapsible "agentgateway deployment" section below the file manifest whenagentgateway_bundleis set. ADownload YAMLbutton hits/api/v1/templates/[id]/agentgateway.yaml(Content-Typetext/yaml; charset=utf-8). - Admin-facing:
/admin/templatesper-row expandable details showfile_manifest+agentgateway_bundleJSON in read-only panels.
Related
- Webhook schema —
../../packages/agentgateway-adapter/README.md(Sprint 52; request/response shape, header forwarding, idempotency). - Policy pack source —
../../packages/agentgateway-policy-pack/README.md. - CLI source (single policy) —
../../packages/jiffy-cli/src/commands/export-agentgateway-policy.ts. - CLI source (bundle) —
../../packages/jiffy-cli/src/commands/export-agentgateway-bundle.ts. - Shared emitter —
../../packages/jiffy-cli/src/lib/agentgateway-yaml.ts. - Upstream agentgateway docs — https://agentgateway.dev