6. SPECIFICATION - normative requirements (N1–N44)
Requirement language is RFC 2119. Where a requirement names an IHE profile, that profile is a proposed binding (§ Requirement language): the obligation is normative, the named profile is a proposal.
Transparency & façade
-
N1. A client MUST be able to query, and to perform follow-up interactions with, the Federation Tier with no indication that it is federated; the interface MUST be a conformant openEHR Query API.
-
N2. The Federation Tier MUST accept an openEHR-compliant AQL query that identifies the patient via
ehr_status/subject, MUST treat that predicate as a façade convenience only, and MUST NOT execute that subject predicate at any node. (§7.)
Patient resolution (Step 1)
-
N3. Before dispatch, the Tier MUST resolve
<patientId> → {node, local ehr_id}via an identifier cross-reference service (proposed binding: IHE PIXm$ihe-pix). Resolution MUST occur outside AQL. (§5.2.) -
N4. For an undirected query, the Tier MUST obtain candidate nodes from a localization / record-locator service (proposed binding: IHE XCPD). The service’s internals are out of scope. Localization answers where the patient’s data might be. It is not required to answer whether that data may be released, which is consent (N27); a localizer MAY carry that answer but MUST NOT be assumed to. Where a localizer is configured but does not answer, the default MUST be fail-closed: an empty candidate set, every registry member reported
not-localizedwith the error, and no ask-all fallback. A deployment MAY offer fail-open-to-ask-all instead, which MUST be declared under N30. (A deployment with no localizer configured is a different case, and its ask-all node selection is unaffected.) (§14, §14.1.) -
N5. The Tier MUST NOT require a directly identifying identifier of the patient (§5.1) to be stored in
EHR_STATUS.subjectat any node. A returnedsubjectcolumn MUST be the re-injected resolution input. This constrains what the federation may require of a node, and is not a prohibition on what a node stores (N34). (§5, §7.) -
N6.
not-resolvedoutcomes per node MUST be recorded in metadata and MUST NOT fail the whole query. (§11, N27.)
Dispatch & rewrite (Step 2)
-
N7. The Tier MUST dispatch standard, non-federated AQL to each eligible node, scoped to that node’s resolved
ehr_id- keyed onehr_id, not onsubject. (§7.) -
N8. The Tier MUST dispatch only to nodes where the patient is resolved (via the cross-reference service), minus any nodes removed by an optional Step-1 consent pre-filter (N27a). Dispatching to a node is not an assertion that consent permits release; the node decides that (N27). (§5.2.)
-
N9.
WHEREclauses execute within each node.LIMIT,OFFSETandORDER BYalso execute within each node, but per-node execution alone is not a correct federated answer, and the Tier MUST additionally apply the merge rules of N39. (Cross-node pagination is not guaranteed; §11.6, §18.) -
N10. The Tier MUST accept an undirected query that identifies only the patient; the node set is derived from localization (N4).
-
N11. The Tier MUST accept a directed query that names Endpoints, and SHOULD accept one that names Organizations. The directive selects the node set and is independent of patient resolution. Per-node scope is still the resolved
ehr_id. (§8.)
Combine / annotate / dedup (Step 3)
-
N12. The Tier MUST be able to add Endpoint and Organization attributes to rows when those attributes are selected. (§9.)
-
N13.
DISTINCTandORDER BYMUST be supported at the Federation Tier. -
N14. The Tier MUST block undirected aggregate queries (
SUM,COUNT,MIN,MAX,AVG) unless cross-node correctness is guaranteed; a directed single-node aggregate is permitted. (§7.) -
N15. By default the Tier MUST NOT deduplicate, and MUST make rows distinguishable via endpoint provenance, relying on
DISTINCT. It SHOULD offer an opt-in VERSION-identity dedup keyed on the openEHRobject_id, keeping the originating copy and recording suppressed endpoints. (§10.) -
N16. Unresponsive nodes MUST NOT contribute rows and MUST appear in
meta.federation.endpoints[]with a status and error. The status set isactive,offline,time-out,node-error,not-resolved,consent-denied,excludedandnot-localized. A node that was reached and answered with a failure MUST be reportednode-error, notofflineoractive. Registry members not in scope SHOULD also appear, asexcludedornot-localized. (§11.1.)
Result-set shape
-
N17. The result set MUST be a conformant openEHR
RESULT_SETas defined by the Query API of ITS-REST Release-1.1.0. Its members, their types and their obligation levels are those of that specification, includingrowsas an ordered array of values per column and the optionalnamefor stored queries, and this specification does not restate them. A gateway MUST populateq,columnsandrows, and MUST carry the federation additions of §9.1 nested under a single unprefixedmeta.federationobject, never as direct members ofmetaand never using the openEHR-reservedprefix (§9.1). Two federation-specific constraints apply: when no ENDPOINT attribute is selected, rows MUST NOT contain endpoint columns (single-CDR compatibility); andcolumns[]MUST be the gateway’s own rendering of the client’s submitted AQL, independent of any column paths a node reports, so a gateway MUST NOT pass a node’s rendering through. _(§9.1, §9, §9.2.) -
N18. ENDPOINT attribute aliases MUST resolve name collisions with EHR-derived columns. (§9.)
-
N19. The FHIR
EndpointandOrganizationresources are RECOMMENDED for the registry. AnEndpointSHOULD carry a defined openEHR Query-API connection type, so implementers MUST register a real code such asopenehr-rest-query, or bind a specified system value; an informal string does not satisfy this. EveryEndpointMUST have a stable unique identifier used in directives. (§8, §9, §15.) -
N20. Every
EndpointMUST have exactly one managingOrganization(1..1), which need not be the organization that physically exposes the endpoint. -
N21. The Tier MUST maintain a registry mapping Organizations, Endpoints, and openEHR
system_id/creating_system_id→ CDR base URLs, which is the follow-up routing table. The registry MUST map every observedcreating_system_id, not only node `system_id`s, because a CDR may hold versions created elsewhere. (§12, §15.)
Follow-up routing (reads & writes)
-
N22. The Tier MUST support follow-up reads of a specific
COMPOSITION/VERSIONidentified in a result row, routing to the owning CDR by (in priority order) thecreating_system_idembedded in theOBJECT_VERSION_ID, else a carriedendpoint_id, else an ask-all fallback. The Tier MUST NOT mutate openEHR uids. (§12.) -
N23. The Tier MUST route a versioned write (
CONTRIBUTION/commit to an existing object) to the single controlling CDR, the one wheresystem_id == creating_system_idof the target object, and MUST reject writes it cannot unambiguously route. New-object creation MUST target an explicitly chosen node, viaFROM ENDPOINTor theopenEHR-federation-endpointheader. Creation spanning multiple nodes is disallowed in v1. (§12, §2.3.) -
N24. On every routed follow-up (read or write) the Tier MUST convey the authenticated client identity to the source CDR, for audit records and access decisions. (§13.)
AuthN / AuthZ & security handoff
-
N25. The client MUST authenticate to the Federation Tier. The Tier authenticates onward and MUST propagate the client identity to nodes (default mechanism: OAuth 2.0 client-credentials with a signed JWT client assertion per RFC 7523, keys published as a JWKS per RFC 7517; a regional federated-identity stack MAY be used instead, see Annex B). A deployment MUST also document its answers to each obligation of §13.4: which identity is verified across the trust boundary and against which authentic source, who authenticates the end user and where that trust stops, how purpose of use is conveyed, what the access token is bound to, and which risks are addressed technically and which by agreement. (Intermediaries White Paper Conformance Point #2; §13, §13.4.)
-
N26. Access decisions that need information not visible to the gateway MUST be delegated to the source CDR; the gateway MUST NOT be the sole authority for releasing sensitive data. (§13.)
-
N27. Consent enforcement is the node’s obligation. Each node MUST check consent (or opt-out) before releasing data, and MUST do so whether or not anything upstream pre-filtered on consent. A gateway MUST NOT assume that a node appearing in a localization result is consent-cleared. (§13.2.)
-
N27a. Consent pre-filtering at Step 1 is OPTIONAL. Where a deployment provides a consent service, standalone or bundled into localization (N4), the Tier MAY use its decision to exclude nodes before dispatch, and MUST then report each excluded node as
consent-denied(N16, which feeds N6). A deployment with no such service is fully conformant: Step 1 simply carries no consent signal and N27 is the sole gate. Pre-filtering never discharges the node’s obligation. (§13.2, §14.3.)
REST façade & addressing
-
N28. The federation surface is the openEHR ITS-REST API relative to a deployment-chosen base URL. This specification mandates and reserves no path prefix. In particular,
/rest/openehris a vendor deployment convention and is not part of it. A client MUST take the base URL from the registry or service discovery and MUST NOT hard-code a prefix. (§15.) -
N29. A gateway MUST support the canonical path form
{base}/v1/ehr/{ehr_id}for every ITS-REST resource it exposes, MUST accept the AQL predicate formWHERE e/ehr_id/value = …, and SHOULD acceptFROM EHR e[ehr_id/value=…]. The forms are semantically equivalent, and this spec’s use of theWHEREpredicate in examples is presentational. (§7.1.) -
N30. A gateway MUST implement
OPTIONS {base}/, describing the supported ITS-REST areas (federated / routed / unsupported), the dedup mode, the timeout policy, and the member endpoints behind the gateway with at leastid,organisationandstatus. The endpoint list MUST NOT require a patient identifier. (§7a.2.) -
N31. On every request routed or dispatched to a single node - including
POST/PUT/DELETE- the gateway MUST return the acting endpoint in theopenEHR-federation-endpointresponse header and SHOULD return the node’ssystem_idinopenEHR-federation-system-id.LocationandETagMUST be passed through unmodified. (§7a.3, §9.6.) -
N32. A gateway MUST NOT federate the openEHR DEMOGRAPHIC API; identity is resolved through the identity binding of N3. Unsupported ITS-REST areas MUST be answered
501 Not Implementedor routed to a single explicitly chosen node, and MUST be declared under N30.node_id,endpoint_idandsystem_idMUST be distinct namespaces and the registry MUST resolve each unambiguously. (§7a.1, §12a.1.)
Identifier hygiene at dispatch
-
N33. In the parts of an outbound request the gateway composes (the dispatched AQL, the request path, the query string and the headers) a node MUST be located by the
ehr_idalone. The gateway MUST NOT dispatch any directly identifying identifier (§5.1) in any of those positions, including aWHERE/SELECT/ORDER BYpath overPARTY_IDENTIFIED.identifiers/DV_IDENTIFIER,PARTY_RELATEDor anENTRY-levelsubject, and including the echoed query text. The gateway MUST consume such values in resolution and strip them, or reject the query (400). This does not extend to client-supplied write payloads: a commit body is archetyped clinical content, passes through unmodified (N22), and its contents are governed by the node’s own validation, authorization and consent. A gateway MUST accept the patient identifier in either carrier,EHR_STATUS.subject.external_refor anENTRY-levelsubjectPARTY_IDENTIFIED/DV_IDENTIFIERpredicate, and MUST resolve on whichever the client used. Neither is optional, and the choice is not a deployment declaration. The outbound rule above is unchanged either way. This requirement governs values that directly identify the patient, not paths: a predicate overCOMPOSITION.composer,EVENT_CONTEXT.health_care_facility,PARTICIPATION.performerorATTESTATION.committerthat identifies a clinician or facility identifies no patient, and MUST NOT be rejected or stripped on the strength of its path alone. (§5.4, §5.4.3.) -
N34. A node in a federation MUST be invocable on its local
ehr_idalone, and the node’s environment MUST provide a way to exchange a patient identifier for thatehr_id, the cross-reference role of N3. Whether the node, its organization’s MPI, or a regional service fills that role is immaterial to the federation. This specification does not mandate where a directly identifying identifier is stored and does not forbid a node from holding one, which is the node’s own governance concern. (§5.5.)
Targeting mechanisms
-
N35. A gateway MUST support endpoint targeting both in AQL (
FROM ENDPOINT/ORGANISATION) and outside it (theopenEHR-federation-endpointrequest header, never a query parameter). A client MUST support at least one. Both carry stable registry endpoint identifiers, never URLs. If both are present and the node sets differ, the gateway MUST reject with400, and MUST NOT merge them or silently prefer one. Only the out-of-AQL form applies to non-AQL requests. (§8.4.)
Dedup and write safety
-
N36. A write derived from a de-duplicated row MUST be routed on the target version’s
creating_system_id, not on the endpoint the surviving row was read from. A gateway MUST NOT route a versioned write to a node that merely holds a copy. If no reachable node hassystem_id == creating_system_id, it MUST reject with409 Conflictnaming the controlling system instead of forking the object. Suppressed endpoints MUST remain visible inmeta.federation.dedup. (§10.3.)
Completeness, timeouts, ordering
-
N37. The default fan-out strategy is all-or-nothing. Where an in-scope node was asked and did not answer (
offline,time-out) or answered with an error (node-error), the gateway MUST fail the query, with504for timeout or unreachability and424 Failed Dependencyfor a node error,504taking precedence where both occur, instead of returning partial rows. A failing response MUST still carrymeta.federation.endpoints[]with the per-node status andmeta.federation.complete: false, so the client can see which node failed.meta.federation.completeMUST be present in every mode and MUST betrueonly when every in-scope node reached statusactive. A node reportednot-localizedorexcludedwas not in scope and MUST NOT clear it (§11.1). A node reportednot-resolvedorconsent-denieddoes clear it but MUST NOT fail the query (§11.3, N6, N27). A best-effort mode, returning partial rows withcomplete: falseand HTTP200, MAY be offered, but MUST be selected per request (openEHR-federation-completeness: partial) and declared under N30. A gateway not offering it MUST reject the request instead of ignoring the header. Both strategies apply to reads only. (§11.4.) -
N38. A gateway MUST apply both a per-node timeout and an overall query budget, abandoning and marking
time-outinstead of failing the query. Both values MUST be discoverable under N30, and the observed per-endpoint elapsed time MUST be reported (N40). A client MAY shorten the budget viaPrefer: wait=, and a gateway MUST NOT extend beyond its own budget on request. Atime-outendpoint means unknown, never no data. (§11.5.) -
N39. For
ORDER BYwithLIMIT n, the gateway MUST dispatchLIMIT nper node and then re-applyORDER BYandLIMIT nacross the merged rows, with deterministic tie-breaking. ForOFFSET > 0it MUST either reject (400), compute the page correctly fromk + nrows per node, or serve it from a materialised ordered result, and MUST declare which. It MUST NOT pushOFFSETdown and present the result as a correct global page. An undirected aggregate MUST be rejected (400) with a reason, and never answered with per-node aggregate rows. Decomposable aggregates MAY be supported only where exactly correct and declared.GROUP BYgroups spanning nodes MUST be merged, or the query rejected. (§11.6, N14.) -
N40.
meta.federation.endpoints[]MUST carry, per endpoint,id,status,erroron error, and the gateway-observedlatency_msfor every endpoint the gateway actually dispatched a query to (active,offline,time-out,node-error).latency_msMUST be omitted, never invented, where the status was settled before dispatch (excluded,not-localized,not-resolved, a pre-filteredconsent-denied). It SHOULD carrynode_id,system_id,organisation,product,versionandrow_count. A gateway MUST NOT invent product/version values it does not know. (§9.5.)
EHR-scoped routing, collisions, definitions
-
N41. A gateway MUST resolve a path
ehr_idto its owning node in priority order: explicit target on the request, then a held resolution binding, then anehr_id→ node index, then, for reads only, an ask-all probe. For writes, ask-all MUST NOT be used. If the earlier steps do not yield exactly one node, the gateway MUST reject with400and require an explicit target. (§12.5.1.) -
N42. (Gateway, at request time.) If the same
ehr_idis claimed by more than one node, the gateway MUST NOT choose between them. It MUST fail with409 Conflictlisting the claimants, and MUST report the collision as a federation integrity incident. (§12.5.2.) -
N42a. (Federation operator, at admission.) A federation MUST define admission conditions that a candidate node satisfies before it is accepted as a member, and those conditions MUST cover
ehr_idgeneration (UUID-v4 or equivalent), non-reuse ofehr_ids, non-adoption of foreignehr_ids on import, and federation-wide uniqueness ofsystem_id. These are identifier-integrity conditions only, not a complete admission profile. (§12b.) -
N43. A gateway MUST route each
{base}/v1/definition/…request to a single explicitly chosen node, and MUST NOT present a union of nodes' templates as one catalogue. It MAY support fan-out template upload, which MUST be opt-in and explicit, reported per node, non-atomic (partial success reported, successes not rolled back, overall success never reported while a node failed) and declared under N30. Template upload is the only permitted fan-out write, and fan-out creation of clinical objects remains disallowed (N23). Where a gateway offers the stored-query registry of N44, that requirement governs stored queries, while this one continues to govern templates and to serve as the fallback for a gateway with no registry. (§12.6.) -
N44. A gateway MAY offer a federated stored-query registry. If it does: the gateway MUST be authoritative for the definition (
PUT {base}/v1/definition/query/{name}/{version}stores at the gateway;POST {base}/v1/query/{name}expands the stored AQL and fans it out under §7); it MUST version definitions with ITS-REST’s own semver path segment and MUST treat a stored version as immutable, refusing a secondPUTto a{name}/{version}it already holds; it MUST declare the registry under N30; where it also fans the definition out to nodes it MUST follow N43's discipline unchanged (opt-in, per-node reporting, non-atomic, declared); it MUST NOT fan out a definition whose AQL carriesFROM ENDPOINTtargeting, which is meaningless at a node; it MUST NOT silently execute against a node whose copy of a definition differs from the registry version it is answering under, and SHOULD be able to report per-node divergence; and it MUST emit the ITS-RESTnamemember, naming the gateway’s stored query. Where no registry is offered, N43's single-node rule is unchanged. (§12.7.)