Policy reference (CEL)

flowifai adjudicates every tool call your broker forwards against your org’s policy rules. This page is the exact contract: what a rule is, how matching works, which effect wins, and how the engine fails.

What a rule is

A policy rule has:

Field Meaning
Tool pattern The exact tool name to match, or * for any tool.
Argument predicate Optional CEL expression over the call’s arguments. Empty means “always matches”.
Effect allow, needs_approval, or deny.
Enabled Disabled rules never match.
Approval requirement For needs_approval rules only: how many approvals (n-of-m), which approver groups are eligible, and whether approvals must span at least two groups.

Rules are org-level. Tool names come from the schemas your registered MCP servers expose — the console’s policy view shows which registered server provides each tool, so you can scope a rule to a server’s tools by naming them exactly.

Matching

A rule matches a call when both hold:

  1. its tool pattern matches the called tool (exact name, or *), and
  2. its argument predicate — if present — evaluates to true.

Predicates are CEL expressions evaluated with two variables:

Examples:

has(args.query) && type(args.query)==string &&
  (args.query.contains("DROP")||args.query.contains("DELETE")||args.query.contains("TRUNCATE"))
has(args.amount) && type(args.amount)==double && args.amount > 0.0
has(args.path) && has(args.recursive) && args.recursive == true

Predicates are compiled and validated when you save the rule; a rule with a predicate that does not compile is rejected.

Most restrictive wins, default deny

All enabled rules are evaluated against the call. Among the rules that match, the most restrictive effect wins:

deny  >  needs_approval  >  allow

If no rule matches, the decision is deny. There is no implicit allow anywhere: you must write an allow rule for the tools you want to pass ungated.

When the winning decision is needs_approval and several needs_approval rules matched, the approval requirement is the strictest merge across them: the maximum approvals-required count, the union of eligible approver groups, and cross-group required if any matching rule requires it. The matched rules are surfaced to approvers as the “why gated” provenance on the ticket.

Fail-closed behavior

The engine treats every abnormal condition as a denial:

Because an erroring predicate denies the whole call, write broad (*-pattern) predicates type-guarded so they can only ever evaluate to true or false:

has(args.command) && type(args.command)==string && args.command.contains("deploy")

The has(...) and type(...) guards ensure the predicate never errors regardless of the argument shape a tool sends.

Policy templates

The console ships a curated catalog of commit-point templates (destructive SQL, payment/charge, outbound send, recursive filesystem delete, deploy/migrate/apply, git force-push). Every template uses tool pattern * with a type-guarded predicate and effect needs_approval. Templates key on guessed argument field names — real MCP tools use arbitrary schemas — so treat an applied template as a starting point and verify it matches your tool’s actual fields (for example, your DB tool may call the query field sql or statement instead of query).

Schema drift and stale rules

Enrollment registers your MCP server’s tool schema. If the live schema drifts from the registered one, the broker blocks all calls to that server until you re-run the app-generated flowifai connect command. Additionally, argument-level rules that reference a changed schema are disabled (fail closed) until re-confirmed — a renamed or re-typed field can never quietly turn a gate into a pass.

Who can edit policy

Policy create/delete requires a policy-admin user, which is a separate capability from approval eligibility. A single approver therefore cannot rewrite policy to bypass n-of-m or separation-of-duty requirements.

Managing rules

Manage rules in the console under Policies, or from the CLI:

flowifai policy list
flowifai policy add --name "gate deploys" --tool "*" \
  --predicate 'has(args.command) && type(args.command)==string && args.command.contains("deploy")' \
  --effect needs_approval
flowifai policy delete <id>

Rule changes are recorded in the audit log like every other security-relevant event.

View this page as markdown · All docs as one file