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.
npm install @fuze-ai/agent-policy-cerbos
The package ships an embedded WASM build of Cerbos. Production deployments use a Cerbos pod, see Operations.
Create policies/agent.tools.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.
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:
cerbos compile policies/ -o policies/bundle.wasm
The bundle hash is included in the evidence export.
Every dispatch emits a fuze.policy span:
{
"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
}
}
If Cerbos throws, returns malformed output, or times out, the loop halts:
{
"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.