HEXARCH|Denial Case · One Field Changes Everything
Comparison

Same policy. Same action. Different IP.
This is why unverifiable logs can’t explain a denial.

requestDENY
actionread
resource/api/billing/invoices
actor_iduser-7714
actor_typeuser
user.roleadmin
request.ip203.0.113.9
decision reason
acme-ip-whitelist ✗ — 203.0.113.9 not in [10.0.0.0/8, 172.16.0.0/12]. failure_mode FAIL_CLOSED → DENY
requestALLOW
actionread
resource/api/billing/invoices
actor_iduser-0042
actor_typeservice
user.roleadmin
request.ip10.0.1.44
decision reason
acme-admin-check ✓ · acme-ip-whitelist ✓ — all rules passed
Policy
idpol_acme_billing
nameacme-billing-access
scopeTENANT
scope_valueacme-corp
failure_modeFAIL_CLOSED
rule_ids["rule_acme_admin","rule_acme_ip_whitelist"]
Rules
resolved rules
rule_acme_admin{"field":"user.role","operator":"eq","value":"admin"}
rule_acme_ip_whitelist{"field":"request.ip","operator":"in","value":["10.0.0.0/8","172.16.0.0/12"]}
evaluation trace
acme-admin-checkPERMISSION
user.role = "admin" → eq "admin" → ✓
acme-ip-whitelistCONSTRAINT
203.0.113.9 → in [10.0.0.0/8, 172.16.0.0/12] → ✗
Audit
audit_idaudit_0083
created_at2026-02-01T16:45:22Z
actionEVALUATE
entity_typePolicy
entity_idpol_acme_billing
actor_iduser-7714
prev_hashd4e71b3…
entry_hasha1f93c7…
changes{"decision":"DENY","rules_evaluated":["acme-admin-check","acme-ip-whitelist"],"failing_rule":"acme-ip-whitelist","resource":"/api/billing/invoices"}
Why the decision flips

Both requests hit the same policy and both pass the PERMISSION rule. The only thing that changes is request.ip. The CONSTRAINT rule is a whitelist; outside the range, it fails. Because failure_mode = FAIL_CLOSED, any failing rule denies the whole request.

Critical field
DENY request.ip203.0.113.9
ALLOW request.ip10.0.1.44
Without verifiable context binding, a denial looks like a mystery.
With a proof, the failing field is undeniable.
TakeawayOne input field explains the outcome — but only if the system can prove which input it actually saw.