Docs/Agentic Tool-Call Policy Schema

Agentic Tool-Call Policy Schema v1

A distinct policy category. OPA/Rego is generic policy. Cedar is authorization. Kyverno is K8s admission. Sentinel is Terraform. This is the schema for the shape none of them target: rules that govern what an AI agent is allowed to do when it invokes a tool (file edit, shell, HTTP, MCP tool call, workflow action).

Rules speak two dialects: JSON for runtime evaluation, Cedar for portability. Both semantically equivalent. Import existing Cedar policies from AWS Verified Permissions; export any Conduct pack back to Cedar. No lock-in.

Why a distinct category

Policy engines already exist. None target the shape we need. The industry has good tools for adjacent problems:

  • OPA/Rego — generic policy over arbitrary JSON input
  • Cedar — authorization (who can do what to which resource)
  • Kyverno — Kubernetes admission control
  • Sentinel — HashiCorp infra provisioning
  • XACML — enterprise access control (legacy)
  • MITRE ATLAS / OWASP Agentic Top 10 — threat catalogs, not enforceable rules

Agentic Tool-Call Policy is its own shape. The actor is an autonomous AI agent, the object is a tool invocation, decisions land in milliseconds pre-execution, and portability across enforcement surfaces (proxy, hooks, MCP, runtime) is table-stakes. This schema names that shape. See the mapping table below for how our vocabulary aligns with each of the above.

Why not just adopt Cedar (or OPA) directly?

Fair question — and the one we asked ourselves before writing a line of schema. We adopted every standard that fit and named only the gap they leave. Here is the honest audit.

What Cedar cannot express without heavy encoding

  • Non-binary decisions. Cedar has permit and forbid. Agentic policy needs warn (proceed but flag), inject (prepend context to prompt), audit (record decision), approval (pause for human). Encoding these as Cedar obligations pushes semantics out of the policy language and into runtime convention — the exact anti-pattern Cedar was built to avoid.
  • Free-text pattern matching. Cedar's like is glob, not regex. Its condition grammar targets structural attribute checks, not scanning LLM prompts for PANs, secrets, or PII. Regex over tool_input.prompt is our common case; wedging it into Cedar loses linter and IDE support.
  • Enforcement-surface metadata. Our enforcement.{proxy, hook, mcp, runtime} declares which of four surfaces can hard-block vs advise for a given rule. Cedar assumes one PDP; it has no concept of "which enforcement point evaluates me."
  • Compliance frameworks as first-class. Buyers query "show me all rules tagged NIST_AI_RMF:MG-2.6." Cedar can carry that as @annotation, but annotations are opaque strings — no schema, no validation, no searchable field.
  • Pack container. We ship bundles — slug, version, tier, UI hints, rules array. Cedar policies are individual documents. Pack-as-unit is what customers install, version, and publish.

What we adopted instead of inventing

  • JSON Schema 2020-12 as the meta-format — linters, IDEs, and CI validators just work.
  • Cedar as a first-class export target. Any pack renders as annotated Cedar text.
  • Cedar as an import source. Bring your Verified Permissions policies in, roundtrip them back out unchanged.
  • Existing compliance vocabularies verbatim — MITRE ATLAS, OWASP Agentic Top 10, NIST AI RMF, ISO 42001, PCI DSS, SOC 2, HIPAA, GDPR, SR 11-7 — as tag prefixes on the frameworks[] array.
  • Namespaced annotations map so metadata from OPA, Kyverno, Cedar, or your own tooling roundtrips through Conduct without loss.

The one-liner

Cedar is the right interchange format. It is not the right authoring format for agentic tool-call policy. The vocabulary mismatch is real, not stylistic — you cannot express warn or inject in Cedar without inventing runtime conventions Cedar itself would consider a bug. So we authored a schema for the shape none of the incumbents target, and made Cedar a peer for portability. No lock-in either direction.

Flip condition. If AWS ships Cedar language extensions for agent primitives — first-class tool-call resources, prompt content matchers, non-binary decisions — we flip Cedar to canonical and generate the JSON view from it. Watch Bedrock AgentCore and Verified Permissions release notes. Until then, this schema is the honest floor.

Why two dialects

Different audiences read policies differently. Engineers want grep-friendly JSON that embeds cleanly in pack files. Auditors want human-readable Cedar with recognizable annotations. You want a promise: nothing here is stuck in a proprietary language.

  • Runtime evaluates JSON — every pack file under apps/api/app/modules/guard/skill_packs/*.json ships as JSON.
  • Cedar is a view — rendered on demand from the same source. Auditors read it, security teams import policies from Cedar-native tools using it.
  • Bidirectional and lossless — round-trip a rule through Cedar and back with no information loss on schema v1.

Same rule, two forms

One PCI-DSS rule from the conduct-pci-dss pack.

JSON (runtime)

{
  "id": "pci_pan_guard",
  "description": "Block card number patterns (PCI DSS Req 3)",
  "match_tool": "edit,write,bash",
  "match_pattern": "\\b4[0-9]{12}(?:[0-9]{3})?\\b|\\b5[1-5][0-9]{14}\\b",
  "action": "block",
  "message": "Card number detected — never log or store PANs in plaintext.",
  "severity": "critical",
  "frameworks": ["PCI_DSS:3.4", "SOC2:CC6.1", "GDPR:Art32"],
  "iso_control": "A.8.11"
}

Cedar (portable)

@id("pci_pan_guard")
@description("Block card number patterns (PCI DSS Req 3)")
@message("Card number detected — never log or store PANs in plaintext.")
@severity("critical")
@iso_control("A.8.11")
@compliance("PCI_DSS:3.4", "SOC2:CC6.1", "GDPR:Art32")
forbid (
    principal is Agent,
    action in [Action::"edit", Action::"write", Action::"bash"],
    resource
)
when {
    context.prompt matches "\\b4[0-9]{12}(?:[0-9]{3})?\\b|\\b5[1-5][0-9]{14}\\b"
};

Get the live Cedar rendering with GET /guard/registry/packs/{slug}/cedar or click ⤓ Cedar on any pack tile at /packs.

Bidirectional interchange

Cedar → Guard

Import Cedar JSON policies from AWS Verified Permissions, Dogwood, or any Cedar-native IAM stack.

POST /guard/registry/import-cedar

Guard → Cedar

Render any installed pack as Cedar text — annotated with severity, ISO control, and compliance framework tags.

GET /guard/registry/packs/{slug}/cedar

Framework tags

The frameworks array is free-form but conventionally uses these prefixes for compliance-surface parity.

PrefixExampleStandard
PCI_DSS:PCI_DSS:3.4PCI Data Security Standard
SOC2:SOC2:CC6.1SOC 2 Trust Services Criteria
ISO_27001:ISO_27001:A.8.24ISO 27001 Annex A
ISO_42001:ISO_42001:8.24ISO 42001 (AI management)
GDPR:GDPR:Art32GDPR article
HIPAA:HIPAA:164.308HIPAA Security Rule
NIST_AI_RMF:NIST_AI_RMF:MG-2.6NIST AI Risk Management Framework
MITRE_ATLAS:MITRE_ATLAS:AML.T0051MITRE ATLAS (adversarial ML)
OWASP_AGENTIC:OWASP_AGENTIC:A01OWASP Agentic Top 10
SR_11_7:SR_11_7:V.A.1Fed Reserve model risk

Mapping to other policy languages

The underlying concepts overlap. What differs is the shape of the object being governed — none of the standards below were designed for agentic tool invocations. This table shows how our fields correspond so teams already using these engines see the alignment.

Conduct (Agentic Tool-Call Policy)

what this schema targets

Agent invokes tool. Rule matches on tool + pattern. Decision fires pre-execution.

Rule id
id
Decision
action
Actor
persona_affinity + match.mcp_tool
Resource
match.tool + match.path_pattern
Match cond.
match.pattern (regex)
Severity
severity enum
Compliance
frameworks[] tags
Metadata
annotations.<namespace>
Enforcement pt.
enforcement.{proxy,hook,mcp,runtime}

OPA/Rego

Generic policy over arbitrary JSON input

Rule id
package + rule
Decision
deny / allow sets
Actor
input.subject
Resource
input.resource
Match cond.
contains / startswith
Severity
annotation
Compliance
annotation
Metadata
package doc
Enforcement pt.
evaluator context

Kyverno

K8s admission control

Rule id
policy metadata name
Decision
validate.deny / mutate
Actor
resource kind
Resource
match.resources.kinds
Match cond.
match.resources.selector
Severity
policy.severity
Compliance
policy.categories
Metadata
annotations
Enforcement pt.
policy webhook

Sentinel

HashiCorp infra provisioning (Terraform)

Rule id
policy name
Decision
main = rule bool
Actor
input.subject
Resource
input.resource
Match cond.
matches function
Severity
metadata
Compliance
metadata
Metadata
scope description
Enforcement pt.
Terraform stage

Cedar

Authorization (who can do what)

Rule id
policy id
Decision
forbid / permit
Actor
principal is <Type>
Resource
resource
Match cond.
when { ... } clause
Severity
@severity annotation
Compliance
@compliance annotation
Metadata
@<name> annotation
Enforcement pt.
authorization boundary

XACML

Enterprise access control (legacy)

Rule id
PolicyId
Decision
Effect Deny/Permit
Actor
Subject
Resource
Resource
Match cond.
Condition element
Severity
Obligation
Compliance
Obligation reference
Metadata
attributes
Enforcement pt.
PDP context

MITRE ATLAS

Adversarial ML threat catalog

Rule id
technique ID
Decision
control category
Actor
technique target
Resource
attack surface
Match cond.
detection signature
Severity
severity rating
Compliance
technique IDs
Metadata
technique references
Enforcement pt.
detection layer

What this is saying: vocabulary overlap is high; problem shape differs. OPA governs arbitrary JSON. Kyverno governs K8s resources. Sentinel governs Terraform plans. Cedar governs authorization requests. Ours governs agentic tool invocations — an object shape none of the above are optimized for.

What we don't try to do: we're not building a general policy engine. Runtime enforcement stays Conduct-specific. What ships is a portable representation — via Cedar for interchange, JSON Schema for tooling, and namespaced annotations for round-tripping foreign metadata.

Extensible (v1.1)

Two optional additions on top of v1. Backward compat: every v1 rule stays valid.

Extensible match map

Put every match dimension under a single map. Custom keys (e.g. mcp_server, webhook_source) allowed; surfaces that don't understand a key ignore it.

{
  "id": "block_prod_writes",
  "action": "block",
  "match": {
    "tool":         "write,edit,bash",
    "pattern":      "PROD_SECRET",
    "path_pattern": "^prod/config\\.yaml$",
    "http_method":  "POST",
    "mcp_tool":     "guard_check"
  }
}

Namespaced annotations map

Free-form metadata by namespace. Runtime ignores unknown namespaces; exporters pass them through (Cedar → @annotation, OPA → package comments, custom → your own tooling).

{
  "id": "block_prod_writes",
  "action": "block",
  "annotations": {
    "cedar":       { "principal_type": "Agent" },
    "opa":         { "package": "conduct.pci" },
    "kyverno":     { "match_kinds": ["Pod"] },
    "custom.acme": "internal-tracker-#1234"
  }
}
Agentic Tool-Call Policy Schema — Docs | Conduct