Skip to main content
ctrlrun.yaml is one document: a schema, an actions map, and from ctrlrun.policy/v3 the mode, environment and authority keys, and from v4 version and controls. Every key set is closed: an unknown key anywhere is a load error, never ignored, because a key an older reader silently dropped would be a typo that never surfaced. A test asserts this page names every key and operator the loader accepts.

Top level

A file that cannot be read, does not parse, or fails any check above raises PolicyError, and no Control is built. There is no empty permissive policy.
runnable

An action entry

Exactly one of decision and rules, plus the optional keys.

A rule

Conditions

A condition key is <subject>_<operator>. The subject is an argument name, or the derived data_scope; the operator is one of seven, and there is no other syntax. Amounts and every other numeric operand are integers: float is refused everywhere in CTRLRun. Bound both ends of a numeric band. amount_lte: 50000 alone matches a negative amount, and a refund of a negative amount is a charge. Reserved subjects. Ten names are refused as condition subjects at load, in a document of every schema version. Five are the principal — agent, user, claims, issuer, expires_at — because the policy cannot see who is asking. Five more name the action itself rather than an argument of it: action_id, environment, principal, resource and data_scope. So when: { resource_eq: "payment:1" } is a load error, not a rule that never matches. data_scope is the one derived subject: it is the set of labels the supplied arguments carry, data_scope_in: [phi] means the set intersects the list, and data_scope_eq / data_scope_neq compare the whole set. data_scope may not be an argument’s name, an effect placeholder, or a protected function’s parameter.
runnable

What a version adds

That is why the key sets are closed by version: each key is refused by every reader that would misread it.

Next