OPL Overview
OPL, the Octet Policy Language, is how a Magistrate policy is written down. The studio writes it for you, and shows it in its code pane. Read this page to review a policy before you sign it off, or to understand a decision afterwards.
This documentation covers the part of OPL that the Magistrate studio and the Octet policy engine support today. What's Next lists what comes later.
A policy
policy p_3b41af1558a1448b8845fa2821871b5d {
combine deny_overrides
rule deny when outside(country("AT")) reason "E-304"
default permit
freshness 5m
}
In words: a device outside Austria gets deny. Every other device gets permit. If the proofs can't settle where the device is, the answer is deny. Proofs older than 5 minutes don't count.
Policy structure
A policy is a name and a body in braces. The body is a list of clauses, in any order.
| Clause | Required | Description |
|---|---|---|
policy <name> |
Yes | The policy's name. Policies from the studio are named p_ followed by a UUID. The studio shows a separate, human title |
rule |
Yes, one or more | An effect, permit or deny, and the condition under which it applies. See Policy clauses |
combine |
No | How the rules combine into one decision. These docs cover only deny_overrides, the default |
default |
No | The decision when no rule applies, permit or deny. Without it, no matching rule means deny |
on_indeterminate |
No | The decision when the proofs can't settle a rule. Defaults to deny, and these docs cover only deny. The studio doesn't write this clause |
freshness |
No | How old a proof may be and still count, for example 5m. Without it, 300 seconds |
requires |
No | A minimum precision for the proofs, for example requires proof_level >= country |
How a policy is decided
The engine verifies the device's proofs, then evaluates the rules:
%%{init: {"flowchart": {"nodeSpacing": 30, "rankSpacing": 40}}}%%
flowchart LR
Q(Are the proofs precise<br/>enough for the policy?) -- yes --> D(Does a deny<br/>rule apply?)
D -- no --> P(Does a permit<br/>rule apply?)
P -- no --> DEF[the default<br/>answer]
D -- yes --> DENY[deny]
P -- yes --> PERMIT[permit]
Q -- no --> U[UNKNOWN: the<br/>on_indeterminate answer]
D -- can't tell --> U
P -- can't tell --> U
- The proofs must be precise enough. If the policy has a
requiresclause and the proofs don't meet it, the policy can't be decided and gets itson_indeterminateanswer, withcauseother. A proof older thanfreshnessdoesn't stop the decision. It only leaves the regions it would have settled unknown, so a deny rule that other proofs settle still wins. - Deny wins. If any
denyrule applies, the decision isdeny. - A rule that can't be settled stops the decision. If a
denyrule might apply but the proofs can't tell, the decision is theon_indeterminateanswer. For example, a device that proves only that it is in Ukraine can't settle a rule about Crimea. - Then permit. If a
permitrule applies, the decision ispermit. - Then the default. If no rule applies, the decision is
default, ordenywithout one.
The engine returns only permit or deny. When the answer came from on_indeterminate, the decision also carries a cause saying why the location couldn't be settled. See Decision Response.
Defaults to know
- No proof is not a pass. A device with no current proof can't satisfy a rule, so the policy gives its
on_indeterminateanswer. Every policy the studio writes usesdeny. - Countries follow ISO 3166 strictly.
country("US")doesn't include Puerto Rico or the other territories, which have their own codes. List them if you mean them. - No matching rule and no default is
deny. - A policy decides one question. To check several at once, use
decideManyin the Magistrate module.