Negation and missing facts
Positive conditions check that a fact or rule derivation is present. Negated conditions (not) check that a disqualifying relationship or state is absent, such as allowing edits only when a document is not archived.
Requiring the absence of a match
Section titled “Requiring the absence of a match”The not operator succeeds when no matching stored fact or rule derivation makes the inner statement true:
declare is_owner(User, Document);declare is_archived(Document);
allow(user: User, "read", document: Document) if is_owner(user, document) and not is_archived(document);When checking allow(User:alice, "read", Document:handbook), is_owner(User:alice, Document:handbook) must hold and is_archived(Document:handbook) must have no matching proof in the policy’s facts or rules. Once is_archived(Document:handbook) is recorded, not is_archived(document) fails and this rule no longer grants access.
Missing data vs. explicit false
Section titled “Missing data vs. explicit false”Verdict does not fill in default false values for missing facts. Compare not is_archived(document) with a rule that checks an explicit boolean attribute:
explicitly_unarchived(document: Document) if has_attribute(document, "archived", false);When a newly created document has no archive facts recorded yet, the two checks behave differently:
Recorded facts for Document:handbook | not is_archived(Document:handbook) | explicitly_unarchived(Document:handbook) |
|---|---|---|
Neither is_archived nor has_attribute recorded | True (no archive marker present) | False (no "archived", false fact) |
has_attribute(Document:handbook, "archived", false) recorded | True | True |
is_archived(Document:handbook) and has_attribute(..., "archived", true) recorded | False | False |
- Use
not is_archived(document)when the normal state is the absence of a marker, so your application only writes a fact when a document is archived. - Use
has_attribute(document, "archived", false)(ordocument.archived = false) when you want the check to fail unless your application has explicitly recordedfalse.
Variable binding in negation
Section titled “Variable binding in negation”Negation tests whether a condition is absent for known values; it cannot generate values for an unbound variable on its own. Bind variables in the rule head or in positive conditions (like is_owner(user, document)) before referencing them inside not.
Any variable introduced only inside a not condition stays local to that negated check and cannot supply bindings to conditions outside it. See the Language Reference and Policy Diagnostics for complete safety rules.
Scope of negation across rules
Section titled “Scope of negation across rules”A not condition only constrains the rule in which it appears; it does not override other rules in your policy. If one allow rule checks not is_archived(document) while another allow rule grants access without that check, users matching the second rule can still access archived documents.
To apply a restriction like not is_archived(document) across every grant path for a resource type, attach it to a resource-qualified allow rule as shown in Permissions.
If you instead need an unconditional override that eliminates access across every grant path regardless of which allow rule matched (such as revoking access for a suspended user), Verdict Server supports top-level deny rules. See Designing Policies.
Next steps
Section titled “Next steps”- Apply resource-qualified restrictions across all grant paths in Permissions.
- Model scalar properties and comparisons in Attributes.
- Verify positive grants and negative boundaries in Testing & Validating Policies.