# OPL Language

This page lists the parts of OPL that the Octet policy engine decides today. The **Studio** column says whether the Magistrate studio writes it yet. A policy that uses a part the studio doesn't write yet can still be decided by the engine.

## Grammar

| Construct | Syntax | Example | Studio |
|---|---|---|---|
| Policy | `policy <name> { <clauses> }` | `policy p_... { ... }` | Yes |
| Rule | `rule permit\|deny when <condition>` | `rule deny when outside(country("AT"))` | `deny` only |
| Condition | `within(<region>)`, `outside(<region>)` | `within(subdivision("US-CA"))` | Yes |
| Region union | `<region> union <region>` | `country("CU") union country("IR")` | Yes |
| Region intersection | `<region> intersect <region>` | `country("US") intersect disc(40.71, -74.01, 50km)` | No |
| Region difference | `<region> difference <region>` | `country("US") difference subdivision("US-NJ")` | No |
| Grouping | `( <region> )` | `(country("AT") union country("DE")) difference ...` | No |
| Comment | `// ...` or `/* ... */` | `// Sanctions list, reviewed 2026-10` | No |

`intersect` binds tighter than `difference`, which binds tighter than `union`. Use parentheses when in doubt.

## Keywords

### Policy clauses

| Keyword | Type | Description |
|---|---|---|
| `rule` | `permit` or `deny`, then `when <condition>` | Applies its effect when the condition holds. A rule can end with `reason "<label>"`, a label for people reading the policy. It isn't returned in the decision |
| `combine` | `deny_overrides` | Any applicable `deny` rule wins. These docs cover only `deny_overrides`, the default |
| `default` | `permit` or `deny` | The decision when no rule applies |
| `on_indeterminate` | `deny` | The decision when the proofs can't settle a rule. `deny` is the default, and the studio doesn't write the clause. These docs cover only `deny` |
| `freshness` | duration | The oldest proof that counts. The studio always writes `5m`. Without the clause, 300 seconds |
| `requires proof_level` | comparison and level | The least precise proof the policy accepts. Studio: No |

### Conditions

| Keyword | Description | Studio |
|---|---|---|
| `within(<region>)` | The device is inside the region | Yes |
| `outside(<region>)` | The device is outside the region | Yes |

A condition is true, false or unknown. It is unknown when the proofs can't settle it, for example when the device proved only its country and the region is a state.

### Regions

| Region | Syntax | Description | Studio |
|---|---|---|---|
| Country | `country("<code>")` | An ISO 3166-1 alpha-2 code, for example `"AT"` | Yes |
| Subdivision | `subdivision("<code>")` | An ISO 3166-2 code, for example `"US-CA"` or `"UA-43"` (Crimea) | US states and the regions in the sanctions lists |
| Disc | `disc(<lat>, <lon>, <radius>)` | A circle around a point, in decimal degrees. The radius must be greater than zero | No |

### Proof levels

For `requires proof_level`, from least to most precise. OPL has more precise levels than these three, which these docs don't cover yet:

| Level | The proof says |
|---|---|
| `on_earth` | The device is on Earth, nothing more |
| `country` | Which country the device is in |
| `subdivision` | Which state or region the device is in |

Compare with `>=`, `>`, `==`, `<=` or `<`. For example, `requires proof_level >= subdivision` refuses to decide on a proof that names only a country. It answers UNKNOWN even when the answer looks obvious, such as a phone in London that proves only `GB`.

## Types

| Type | Example | Notes |
|---|---|---|
| Country code | `"AT"` | ISO 3166-1 alpha-2. Lower case is read as upper case. Territories have their own codes, so `"US"` excludes `"PR"` |
| Subdivision code | `"US-CA"` | ISO 3166-2. Lower case is read as upper case |
| Latitude, longitude | `48.2082`, `16.3738` | Decimal degrees |
| Distance | `2000m`, `2km` | Units `m` and `km` |
| Duration | `300s`, `5m` | Units `ms`, `s`, `m`, `h`, `d` |
| String | `"E-304"` | In double quotes |

## Publishing checks

The engine checks a policy when it is published and refuses it if:

- it doesn't parse, including a disc whose latitude, longitude or radius is out of range
- a name is defined twice or used without being defined
- a region code doesn't exist
- it uses a region kind the engine can't decide yet, such as `city`

See [Errors](/docs/magistrate/reference/errors/) for the exact messages.

## Not yet supported

OPL has more than this page lists, for example time windows, named regions and obligations. The engine doesn't decide those for Magistrate policies yet. See [What's Next](/docs/magistrate/whats-next/).
