The deterministic Rust runtime at the core of RegelRecht that evaluates machine-readable law.
The execution engine is the core of RegelRecht: a deterministic Rust runtime that evaluates machine-readable Dutch law.
packages/engine/The engine has a layered architecture:
| Module | Purpose |
|---|---|
service.rs | LawExecutionService - top-level API, cross-law orchestration |
engine.rs | ArticleEngine - single article execution |
resolver.rs | RuleResolver - law registry, output→article indexing, IoC lookup |
context.rs | RuleContext - execution state, variable resolution with priority chain |
operations.rs | 21 operation types (arithmetic, comparison, logical, conditional, date) |
uri.rs | regelrecht:// URI parsing for cross-law references |
trace.rs | Execution tracing with box-drawing visualization |
priority.rs | Lex superior / lex posterior resolution for competing implementations |
data_source.rs | External data registry for non-law data lookups |
config.rs | Security limits (max laws, YAML size, recursion depth) |
When the engine resolves a $variable, it checks these sources in order:
referencedate, referencedate.year, etc.FOREACHArticles can define multiple outputs (e.g., heeft_recht_op_zorgtoeslag and hoogte_zorgtoeslag). You can request several of them in one call.
Callers must explicitly list the outputs they need. There’s no “return all” mode. The engine returns requested outputs plus any causally-entailed outputs from hooks and overrides (a beschikking is legally indivisible, Awb consequences like motivering and bezwaartermijn cannot be stripped).
If the requested outputs live in the same article, it runs once. Outputs from different articles trigger one execution per article, then merge.
Each output is tagged with how it was produced:
| Provenance | Meaning |
|---|---|
Direct | Produced by the article’s own actions |
Reactive | Produced by a hook (e.g., Awb firing on BESCHIKKING) |
Override | Produced by a lex specialis override (RFC-007) |
The output_provenance field appears in ArticleResult, WASM results, CLI output, and the Execution Receipt. It’s omitted when empty (e.g., simple articles with no hooks).
The output_name (singular) field is still accepted for backward compatibility.
The engine supports 21 schema operations for expressing legal logic:
| Category | Operations |
|---|---|
| Comparison (5) | EQUALS, GREATER_THAN, LESS_THAN, GREATER_THAN_OR_EQUAL, LESS_THAN_OR_EQUAL |
| Arithmetic (4) | ADD, SUBTRACT, MULTIPLY, DIVIDE |
| Aggregate (2) | MAX, MIN |
| Logical (3) | AND, OR, NOT |
| Conditional (1) | IF (cases: [{when, then}] + default; SWITCH is an accepted alias) |
| Collection (2) | IN, LIST |
| Date (4) | AGE, DATE_ADD, DATE, DAY_OF_WEEK |
Negation is expressed by wrapping a positive operation in NOT: NOT around EQUALS for “not equal”, NOT around IN for “not in”. A null check is EQUALS against value: null (wrap it in NOT for “is not null”). For backward compatibility the engine also accepts the aliases NOT_EQUALS, IS_NULL, NOT_NULL, and NOT_IN, but these are not part of the schema: YAML using them executes correctly yet fails schema validation, so new laws should use the NOT / EQUALS null forms instead.
See RFC-004 for the full specification.
Laws reference each other via source on input fields:
The engine automatically loads the referenced law, executes it with the specified parameters, and caches the result. Circular references are detected and raise an error.
Higher laws declare open_terms that lower regulations fill via implements. At execution time, the engine:
implements declarations at law load timeopen_termcalculation_date) and scope (gemeente_code, etc.)default if no implementation foundSee RFC-003 for the full pattern.
Every execution can produce a full trace tree showing how each value was computed:
The trace includes: which articles were executed, which inputs were resolved (and from where), which operations ran, and the result of each step.
The engine compiles to WebAssembly for browser and Node.js execution.
WASM limitations. Open term resolution (
open_terms/implementsIoC pattern) is not yet available in the WASM build. Cross-law references work when all referenced laws are pre-loaded vialoadLaw().
The engine enforces compile-time security limits to prevent DoS:
| Limit | Value | Purpose |
|---|---|---|
MAX_LOADED_LAWS | 100 | Prevent memory exhaustion |
MAX_YAML_SIZE | 1 MB | Prevent YAML bombs |
MAX_ARRAY_SIZE | 1,000 | Prevent large array DoS |
MAX_RESOLUTION_DEPTH | 50 | Internal reference nesting |
MAX_CROSS_LAW_DEPTH | 20 | Cross-law reference nesting |
MAX_OPERATION_DEPTH | 100 | Operation nesting |
The engine can produce an Execution Receipt: a JSON document that captures everything needed to reproduce a specific execution result. The receipt includes engine_version, schema_version, and regulation_hash alongside the regular ArticleResult fields. This allows independent verification of past decisions.
Use LawExecutionService.build_receipt() to construct a receipt programmatically, or pass --receipt to the CLI (see below).
See RFC-013 for the design rationale.
Benchmarks are available via:
Key benchmarks: URI parsing, variable resolution, operations, article evaluation, law loading, priority resolution, and end-to-end service execution.
An exploration by Bureau Architectuur of the Dutch Ministry of the Interior into the possibilities of transparent, executable legislation.
Bureau Architectuur
Ministry of the Interior and Kingdom Relations