Cerbos policies

Cerbos is the production policy engine; StaticPolicyEngine is for tests. Every tool dispatch goes through Cerbos. An engine error halts the run with fuze.policy.engine_error=true, there is no allow-on-error path.

What you'll build: a YAML+CEL Cerbos policy that allows public tools, gates personal data on lawful basis, and denies special-category data. Prerequisites: Human-in-the-loop so suspend/resume is wired up. Next: wire an MCP server into the same evidence pipeline.

Install

bash
npm install @fuze-ai/agent-policy-cerbos

The package ships an embedded WASM build of Cerbos. Production deployments use a Cerbos pod, see Operations.

Write a policy

Create policies/agent.tools.yaml:

yaml
apiVersion: api.cerbos.dev/v1
resourcePolicy:
  version: default
  resource: agent.tool
  rules:
    - actions: ['invoke']
      effect: EFFECT_ALLOW
      roles: ['operator']
      condition:
        match:
          all:
            of:
              - expr: request.resource.attr.tool_name == "greet"
              - expr: request.resource.attr.classification == "public"

    - actions: ['invoke']
      effect: EFFECT_ALLOW
      roles: ['operator']
      condition:
        match:
          all:
            of:
              - expr: request.resource.attr.tool_name == "lookup_user"
              - expr: request.resource.attr.classification == "personal"
              - expr: request.principal.attr.lawful_basis in ['consent', 'contract']

    - actions: ['invoke']
      effect: EFFECT_DENY
      roles: ['operator']
      condition:
        match:
          expr: request.resource.attr.classification == "special_category"

The default in this bundle is deny: any tool not matched is rejected.

Wire it in

ts
import { CerbosPolicyEngine } from '@fuze-ai/agent-policy-cerbos'

const policy = await CerbosPolicyEngine.fromBundle({
  bundlePath: './policies/bundle.wasm',
})

await runAgent(
  { definition: agent, policy, evidenceSink },
  ctx,
)

The bundle is built ahead of time:

bash
cerbos compile policies/ -o policies/bundle.wasm

The bundle hash is included in the evidence export.

Decision evidence

Every dispatch emits a fuze.policy span:

json
{
  "span": "fuze.policy",
  "attributes": {
    "fuze.policy.decision": "allow",
    "fuze.policy.bundle_hash": "<sha256>",
    "fuze.policy.rule_id": "agent.tools.public-tools",
    "fuze.policy.engine_error": false
  }
}

Engine errors are fail-stop

If Cerbos throws, returns malformed output, or times out, the loop halts:

json
{
  "fuze.policy.engine_error": true,
  "fuze.run.status": "halted"
}

Do not introduce an allow-on-error path; the rule is fail-closed on engine error by default.

Next: wire up an MCP server.