Prior to this RFC, the engine has no concept of organizational boundaries at execution time; it loads all laws and executes everything in-process. RFC-010 (Federated Corpus) addresses where laws come from but not who executes what.
In reality, Dutch law assigns specific computations to specific organizations. The Healthcare Allowance Act (Wet op de zorgtoeslag) assigns execution to Allowances Service (Dienst Toeslagen). The Participation Act (Participatiewet) assigns execution to the municipality (gemeente). The tax inspector (inspecteur) alone determines the assessed income (toetsingsinkomen). When one law references another law’s output, the question is: does the executing organization compute that value itself, or does it accept another organization’s individual decision (beschikking)?
The General Administrative Law Act (Awb) art. 1:3 defines a decision (besluit) as a written decision by an administrative body (bestuursorgaan) containing a public-law legal act. An individual decision (beschikking) is a decision that is not of general scope, directed at a specific person or case. Only a competent authority (bevoegd gezag) can issue an individual decision (beschikking), and other organizations are legally bound by it.
A calculation (berekening), such as applying a formula, evaluating a definition, or computing an intermediate value, is not a decision (besluit). It has no independent legal force. Anyone with the rules and data can perform it.
This distinction determines the execution boundary. The schema already captures it through two fields:
competent_authority (RFC-002): which authority is competent to issue the decisionproduces.legal_character: whether the article’s output constitutes an individual decision (BESCHIKKING), assessment (TOETS), or informational output (INFORMATIEF)When an article produces a BESCHIKKING and declares a competent_authority, only that authority can produce it authoritatively. All other articles (definitions, formulas, sub-checks) can be executed by anyone.
Without this RFC, organizations exchange semi-finished products through registries like the Income Registry (Basisregistratie Inkomen, BRI) and infrastructure like Suwinet. This model has well-documented problems:
The law defines the boundaries. The engine does not invent execution boundaries. It reads competent_authority from the law YAML and derives who must execute what.
Identity determines behavior. The engine knows which organization it represents. When it encounters a cross-law reference to an article that produces an individual decision (beschikking) with a different competent_authority, it accepts rather than executes. Articles that produce definitions or calculations are always executed locally, regardless of which regulation they belong to.
Everyone can simulate. The competent-authority boundary only applies to authoritative (voor-recht-geldende) execution. Anyone can run the full chain locally for analysis, policy exploration, or scenario testing. The difference is in the legal status of the output; the engine’s capability is the same either way.
Transparency by default. Every execution produces a trace showing which values were computed locally and which were accepted from other organizations, enabling end-to-end auditability across organizational boundaries.
When the engine resolves a cross-law reference, it operates in one of two modes:
Execute: load the rules, run them locally, produce the value. Used when the executing organization is competent, or when no specific competent authority is declared (pure definitions and calculations (berekeningen)).
Accept: take another organization’s authoritative determination as input. Used when another organization is the declared competent authority for that output.
The mode is not declared in the YAML. It is derived at runtime from three things:
produces.legal_character, does this article produce an individual decision (beschikking)?competent_authority, who is competent for that decision?The produces.legal_character field is the primary trigger, not competent_authority alone. This is essential because a single regulation can contain both articles that produce individual decisions (beschikkingen) and articles that define terms or calculate intermediate values. For example, the Income Tax Act (Wet inkomstenbelasting 2001) has competent_authority: Belastingdienst at regulation level, but article 2.18 (aggregate income (verzamelinkomen)) is a pure definitional formula; anyone can compute it. Only the articles that produce the actual tax assessment (aanslag), which is the individual decision (beschikking), require the Tax Authority (Belastingdienst).
When an article does produce a BESCHIKKING, all its outputs are part of that single determination. In Dutch administrative law, the individual decision (beschikking) is indivisible: both the right (recht) and the amount (hoogte) are constitutive elements of one decision. You cannot accept the right from the authority but re-calculate the amount yourself.
For CATEGORY authorities (RFC-002), the engine resolves the category against its identity. A municipality (gemeente) engine with identity GM0363 matches competent_authority: { name: "college van burgemeester en wethouders", type: CATEGORY }.
The engine receives an identity at startup:
When type is CATEGORY_MEMBER, the engine matches against any competent_authority with type: CATEGORY and the same category name. This resolves the RFC-002 open question of how categorical authorities work at runtime.
With a self-signed identity (the default), the engine runs in solo simulation: it executes everything locally, regardless of competent_authority.
The engine has two independent dimensions, selectable at startup:
Connectivity: Solo or Federated
competent_authority, it executes the rules locally anyway (simulation) or errors if it cannot (authoritative without the required dependency).competent_authority. If the other engine is unreachable, the engine errors instead of silently falling back to local execution.Legal status: Simulation or Authoritative
These combine into four modes:
| Solo | Federated | |
|---|---|---|
| Simulation | Execute everything locally, self-signed identity, unsigned outputs. For citizens, policy analysis, development. | Execute with identity, call other orgs (or demo orgs with self-signed identities), unsigned outputs. For integration testing, demo setups. |
| Authoritative | Execute with identity, sign outputs. No cross-org calls, only for laws fully within this org’s competence. | Execute with identity, sign outputs, accept signed individual decisions (beschikkingen) from other orgs via FSC. Production mode. |
Solo simulation is the default. It is the mode used for citizen-facing scenario tools, policy analysis, and development.
Federated authoritative is the production mode. The engine identifies as a specific organization, executes its own competence, accepts individual decisions (beschikkingen) from other competent authorities, and signs its outputs.
Solo authoritative is for organizations executing laws that have no cross-org dependencies, the entire computation is within their own competence. The engine signs its outputs but makes no external calls.
Federated simulation is for integration testing. The engine has an identity and makes real calls to other engines (which may run with fake identities in a demo setup), but outputs are not authoritative. This tests the full inter-org chain without producing legally binding results.
In federated mode (both simulation and authoritative), the engine needs to know where to reach other organizations’ engines (or registries). This RFC adopts Federated Service Connectivity (FSC) as the connectivity standard.
FSC is an open, jurisdiction-independent standard for federated API connectivity. Originally developed by VNG (Association of Dutch Municipalities) as part of Common Ground, it is now managed by Logius and is being adopted as the national standard for government API exchange (mandatory for Digikoppeling REST API services from 2027).
FSC is not Dutch-specific. Its core concepts are generic:
| FSC concept | What it does | RegelRecht use |
|---|---|---|
| Peer | An organization that provides and/or consumes services | Each org running a RegelRecht engine is a Peer |
| Group | A set of Peers that share a Trust Anchor | All RegelRecht-participating orgs form a Group |
| Directory | Central service discovery point for a Group | Engines register their law evaluation API; consuming engines discover competent authorities (bevoegde gezagen) |
| Contract | Signed agreement between Peers defining allowed interactions | Encodes which org may request which outputs, derived from the legal basis in the law |
| Grant | Permission within a Contract (ServiceConnectionGrant, DelegatedServiceConnectionGrant) | Org A may connect to org B’s engine |
| Trust Anchor | Root CA that all Peers trust | Any agreed CA for the jurisdiction |
FSC provides exactly what multi-org law execution needs:
competent_authority name in the law YAML maps to a Peer in the Directory, which maps to an endpoint.Because FSC is jurisdiction-independent, the same multi-org execution model works outside the Netherlands. A Belgian or German jurisdiction could form its own FSC Group with its own Trust Anchor, or join a cross-border Group for EU-wide law execution.
For development, testing, and the working demo, a local service-registry.yaml stands in for the FSC Directory:
The consuming engine doesn’t know or care whether the providing endpoint runs a live RegelRecht engine or serves pre-computed individual decisions (beschikkingen) from a registry. That’s the providing org’s implementation choice. Both respond to the same protocol (§5).
When an engine in authoritative mode needs to accept a value, it calls the competent organization’s endpoint:
Request:
Response:
The response includes:
The signature is what makes the output authoritative. In simulation mode, the engine produces unsigned outputs. In authoritative mode, outputs are signed with the engine’s identity. A consuming engine can verify that an accepted value was indeed signed by the competent authority.
Authentication is handled through FSC. FSC uses x.509 certificates signed by the Group’s Trust Anchor. All connections between Peers use mutual TLS (mTLS). Each jurisdiction chooses its own Trust Anchor and certificate infrastructure.
A RegelRecht engine in production:
For development: self-signed certificates and the local service-registry.yaml replace the FSC Directory.
Connectivity: FSC Contracts with Grants establish which organizations may connect to each other’s engines. Authentication as a verified government organization is sufficient basis for a Grant, FSC handles the “can org A talk to org B” question.
Request-level authorization, whether org A may request specific data about a specific citizen, is a separate concern that operates at a different layer. This is out of scope for this RFC and will be addressed in a future RFC.
Event-driven re-evaluation: when upstream individual decisions (beschikkingen) change (e.g., the tax inspector (inspecteur) corrects an assessed income (toetsingsinkomen)), the providing engine can notify consuming engines. This triggers re-evaluation of downstream individual decisions (beschikkingen), addressing the staleness problem in the current semi-finished products model. The notification mechanism is out of scope for this RFC but FSC’s contract model supports it.
What changes:
What changes:
competent_authorityEach organization’s engine produces its own trace for the computation it performs. Traces are not shared between organizations. When engine A accepts a value from engine B, engine A’s trace records the accepted value and a reference, not engine B’s internal computation. This is privacy by design: engine B’s trace may contain data about the citizen that engine A has no legal basis to see.
This principle aligns with the Logboek Dataverwerkingen standard: each organization logs its own data processing, logs are not exchanged between organizations, and the citizen can request their logs from each org independently.
The existing trace uses PathNodeType to identify what each trace node represents. Cross-law calls use CrossLawReference, IoC resolution uses OpenTermResolution. For cross-org boundaries, a new node type is introduced:
AuthorityCall: the engine needs a value that is another organization’s competence (bevoegdheid)This follows the same parent-child pattern as CrossLawReference and OpenTermResolution: the parent node explains what is happening, the child node holds the result.
Example: healthcare allowance (zorgtoeslag) trace in federated authoritative mode
The AuthorityCall node:
Resolve node with the result, the authority that produced it, the signature status, and a reference identifierThe reference is a trace_id following the W3C Trace Context standard, as prescribed by the Logboek Dataverwerkingen standard. When engine A calls engine B via FSC, the trace_id propagates across the organizational boundary. Both orgs log their side of the interaction using the same trace_id, but neither shares its log entries with the other.
The providing org does not share its trace, it stores it in its own Logboek Dataverwerkingen.
The citizen (interested party (belanghebbende)) can request their trace from each organization that contributed to their decision, using their right of access (inzagerecht, AVG art. 15):
AuthorityCall nodes with trace_id referencestrace_id, showing how the assessed income (toetsingsinkomen) was determinedEach org authenticates the citizen independently and provides only the trace for that citizen’s computation. A citizen portal could aggregate these by following trace_id references across orgs, presenting a complete cross-org chain without any org needing to share traces with another org.
In solo simulation, the engine executes everything locally but marks cross-org boundaries:
The AuthorityCall node is still present (so org boundaries are visible) but instead of an accepted value, it contains the full local computation subtree, marked as simulated.
In solo simulation (the default), the engine ignores competent_authority entirely. It loads all laws and executes everything locally. This is:
In federated simulation, the engine has an identity and makes real calls to other engines via FSC, but outputs are non-authoritative. This is for integration testing: the full inter-org chain is exercised, including service discovery, authentication, and the inter-engine protocol. If a called engine is unreachable, this is an error; there is no silent fallback to local execution. Demo setups use self-signed identities.
This RFC addresses which organization executes which rules and how organizations exchange individual decisions (beschikkingen). It does not address the underlying data layer: how organizations obtain the raw input data they need to execute those rules.
In Dutch law, there is no general legal basis for one government organization to request data from another. Each inter-organizational data exchange requires its own specific legal basis, either through a base registry (basisregistratie) with mandatory use provisions (verplicht gebruik) in its own law, a domain-specific law (e.g., Wet SUWI art. 62 for the work and income domain, Participation Act (Participatiewet) art. 64 for social assistance), or a bilateral statutory provision.
The policy principle of “single request, multiple use” (eenmalige uitvraag, meervoudig gebruik) aspires to prevent citizens from having to provide the same data to multiple government organizations. For data in base registries (basisregistraties), this is legally enforced through per-registry mandatory use provisions. For data outside the base registries, it remains a policy aspiration without a general legal mechanism. There is no general statutory obligation for one government organization to share data with another.
Machine-readable law partially mitigates this: when org B can load org A’s published rules and execute them locally using data org B already legitimately holds, the need for inter-org data exchange is reduced. But it does not eliminate it, there remain cases where org B needs data that only org A has, and no registry or specific law covers the exchange.
The data layer, how engines obtain input data, which registries they connect to, what legal basis each data flow requires, and how this interacts with the execute/accept model, is out of scope for this RFC and will be addressed in a future RFC.
Derived from law, not configured. The execute/accept boundary comes from competent_authority in the law YAML, not from per-reference configuration. Adding a new law doesn’t require configuring execution boundaries.
Backward compatible. Solo simulation is the default. Existing users, tests, and deployments are unaffected.
Transparent. Every trace shows which values were computed locally and which were accepted from other organizations. This addresses the opacity problem identified by the Court of Audit (Algemene Rekenkamer) and the Parliamentary Committee (POK).
Verifiable. Any organization can verify an accepted value by switching to solo simulation and re-executing locally. This satisfies Awb art. 3:9 duty to verify (vergewisplicht).
Legally grounded. The execute/accept distinction maps to individual decision (beschikking) vs calculation (berekening) in Dutch administrative law, using produces.legal_character and competent_authority, both already in the schema.
Privacy by design. In authoritative mode, data only flows where the law says it should. Accepted values are individual outputs (assessed income: €32.000), not bulk data.
Requires competent_authority to be declared. Laws without competent_authority on their articles default to “anyone can execute”, which is correct for most definitions and calculations (berekeningen), but means the quality of the execute/accept boundary depends on the completeness of authority annotations.
Inter-org latency. Federated mode introduces network calls where solo mode has none. Critical-path accepted values add latency. Mitigation: caching with TTL based on the providing org’s update frequency.
Requires FSC infrastructure. Organizations need certificates from the Group’s Trust Anchor and must onboard to the FSC Directory. The overhead per participating org is low (certificate + registration), but establishing the Group and Trust Anchor is a one-time coordination effort.
Trust model. An accepted value is trusted because it’s signed by the competent authority. If the signing key is compromised, the trust model breaks. Mitigation: standard x.509 certificate revocation via the Trust Anchor.
Alternative 1: Mode declared per source reference in YAML
source reference would declare mode: execute or mode: acceptcompetent_authority already expresses. The YAML would need to encode the same information in two places, creating inconsistency risk. The law doesn’t say “fetch this value”, it says “this is determined by [org].” The engine should derive the mode, not have it configured.Alternative 2: Centralized execution (one engine runs everything)
Alternative 3: Full delegation (call another org’s engine for any cross-law reference)
source.regulation triggers a remote call to the org that authored the referenced lawEngineIdentity struct with name, organisation_id, type, optional gemeente_codeConnectivity enum: Solo, FederatedLegalStatus enum: Simulation, AuthoritativeSoloSimulation mode: default behavior, no changesresolve_external_input_internal(): before executing the target article, check competent_authorityAuthoritative mode and competent authority ≠ engine identity → route to Accept pathSimulation → always execute locally (solo) or execute locally and annotate trace (federated)Federated + Simulation → make real calls to other engines but mark outputs as non-authoritativeSolo + Authoritative → error if cross-org dependency is encountered that cannot be resolved locallyservice-registry.yaml (development stand-in for FSC Directory)PathNodeType::AuthorityCall for cross-org boundary nodesauthority, signed, trace_id fields to PathNode (optional, used only for AuthorityCall children)trace_id, span_id) across inter-engine calls, aligning with the existing OpenTelemetry instrumentation and the Logboek Dataverwerkingen standardAuthorityCall wraps a local computation subtree instead of an accepted value| File | Change |
|---|---|
packages/engine/src/service.rs | EngineIdentity, ExecutionMode, accept routing in resolve_external_input_internal() |
packages/engine/src/resolver.rs | No change (already resolves laws by id) |
packages/engine/src/trace.rs | authority, signed, reference fields on PathNode |
packages/engine/src/types.rs | PathNodeType::AuthorityCall variant |
packages/engine/src/priority.rs | No change |
New: packages/engine/src/identity.rs | EngineIdentity, matches_authority(), identity matching logic |
New: packages/engine/src/federation.rs | ServiceRegistry, InterEngineClient, request/response types |
New: packages/engine/src/signature.rs | Output signing and verification |
Schema: schema/v0.5.0/schema.json | No schema change needed, competent_authority already exists |
competent_authority at article levelopen_terms + implements for delegation (unchanged, always execute locally)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