Write a policy that allows traffic
A fresh enforcing install denies everything. This is how you turn default-deny into a working allow list without opening the door wider than you meant to.
Before you begin#
- A running control plane — see Install with Helm.
- A capability that at least one healthy agent declares.
Confirm you are actually being denied by policy rather than something else:
curl -s http://127.0.0.1:8080/v1/run \
-H 'Content-Type: application/json' \
-d '{"tenant":"retail","capability":"refund-processing","input":{"message":"test"}}'
{"error":{"code":"policy_denied",
"message":"Policy denied the request: missing_policy"}}
missing_policy means policy is enforcing and nothing granted an explicit allow.
Write the rules#
Rules are Helm values. The chart renders them into a ConfigMap and mounts them read-only into the policy service.
policy:
rules:
defaultDecision: deny
allowedTenants:
- retail
allowedCapabilities:
- refund-processing
helm upgrade agentfleet ./agentfleet \
--namespace agentfleet-system --reuse-values \
--set policy.rules.defaultDecision=deny \
--set policy.rules.allowedTenants[0]=retail \
--set policy.rules.allowedCapabilities[0]=refund-processing
helm upgrade agentfleet ./agentfleet \
--namespace agentfleet-system --reuse-values -f my-values.yaml
Rules are read once at startup, so the chart puts a checksum of them on the pod template — changing rules rolls the pod and the new rules take effect. Editing the ConfigMap by hand does not: the running process keeps what it loaded.
Verify#
The allowed combination now succeeds:
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/v1/run \
-H 'Content-Type: application/json' \
-d '{"tenant":"retail","capability":"refund-processing","input":{"message":"test"}}'
# 200
And anything outside the list is still refused:
curl -s -o /dev/null -w '%{http_code}\n' http://127.0.0.1:8080/v1/run \
-H 'Content-Type: application/json' \
-d '{"tenant":"finance","capability":"refund-processing","input":{"message":"test"}}'
# 403
A rule set that only ever returns 200 has not been shown to enforce anything. Always test the denial too.
Two behaviours that will bite you#
A request with no tenant is not matched by allowedTenants — and is not rejected by it
either. It falls through to defaultDecision. This is safe while the default is
deny. If you set defaultDecision: allow, a request missing the fields you filter on
is allowed. Keep the default at deny.
Unknown fields are rejected rather than ignored, so allowedTennants stops the policy pod with
an error instead of silently leaving your restriction unenforced. Check the pod logs after a rules change.
Narrowing further#
The same rule set restricts models, tools, regions, and cost. Deny rules are evaluated before allow rules, so an explicit deny always wins:
policy:
rules:
defaultDecision: deny
allowedTenants: [retail]
allowedCapabilities: [refund-processing]
allowedModelProviders: [anthropic, openai]
deniedToolTypes: [database]
maxCostUsd: 5.00
Every available key is listed in the Helm values reference.
Next#
- Require approval for an action — pause instead of allowing outright.
- AgentPolicy — how evaluation is ordered.