Pinned floors
Some action verbs are dangerous enough that HESO pins a minimum safety bar on them. Your policy can tighten that floor but never bypass it, and a policy that tries is rejected when it loads.
A policy is an ordered list of rules, and the first one that matches a subject, verb, scope, and conditions decides the action. Pinned floors stop that flexibility from being used to wave through something dangerous — by accident or on purpose.
Which verbs carry a floor
Four verbs are floored. Each one’s minimum decision is the same: it cannot resolve without a human.
| Verb | What it covers | Floor |
|---|---|---|
payment | Money moves out | require_approval |
delete | Records get destroyed | require_approval |
account_change | Credentials or settings change | require_approval |
data_export | Data leaves the system | require_approval |
A floor is a built-in minimum the engine already knows — you do not write it. For these four lanes, no rule can make the decision weaker than require_approval. That human co-signature is what lifts a receipt from L0 to L1.
Tighten, never bypass
A floor sets a minimum, not a maximum. You can make a floored lane stricter; you cannot make it looser. Tightening is what most real policies do:
- Require approval with named approvers, or a quorum where more than one person must co-sign.
- Lower the threshold so approval kicks in earlier — route every payment over $1,000 instead of over $5,000.
- Block outright — a
blockdecision is stricter than approval, so it is always allowed. - Add a shorter
sla_minutesso an approval cannot sit unanswered.
The one move a floor forbids is a rule that blanket-allows a floored lane in a way that removes its protection — resolving it to allow with no human in the loop.
Accepted vs rejected
This rule keeps require_approval and adds two approvers and a shorter SLA. The engine accepts it because it asks for more, not less:
# Accepted: the floor on payments is require_approval.
# This rule TIGHTENS it — two approvers, a faster SLA.
[[rule]]
id = "pay-large"
order = 10
enabled = true
subject = { kind = "any" }
verb = "payment"
scope = "*"
conditions = [
{ field = "amount_usd", op = "gt", value = 5000, display = "amount over $5,000" },
]
decision = "require_approval"
approvers = ["finance-lead", "cfo"]
sla_minutes = 30This rule auto-allows every payment. It bypasses the floor, so the engine rejects the whole policy at load:
# Rejected at load: payment is a floored lane, so a rule
# cannot resolve it to "allow" with no human in the loop.
[[rule]]
id = "pay-auto"
order = 10
enabled = true
subject = { kind = "any" }
verb = "payment"
scope = "*"
conditions = []
decision = "allow"The [FLOOR_BYPASS] error
When a rule would allow-without-approval a floored lane, loading fails with a [FLOOR_BYPASS] error that names the offending rule id and verb, so the fix is unambiguous:
[FLOOR_BYPASS] rule "pay-auto" cannot allow verb "payment" without approvalThe check runs at policy load, not when an action arrives, so a floor-breaking policy never goes live. The control plane re-runs the same check, and because the browser, Node, and Python all call the one Rust core, the reason string you read in the editor is byte-identical to the one the server produces. A bypassing file cannot slip into production.
A [FLOOR_BYPASS] aborts the load, so the policy is never activated. (A separate [PARSE] error covers malformed TOML and never reaches the floor check.) Both stop the policy from loading.
A floor guarantees a loaded policy can never auto-allow a dangerous lane: a person must co-sign before it proceeds. It constrains authorization, not the outcome — it does not say whether the co-signer approved wisely.