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-localized with 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.subject at any node. A returned subject column 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-resolved outcomes 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 on ehr_id, not on subject. (§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. WHERE clauses execute within each node. LIMIT, OFFSET and ORDER BY also 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. DISTINCT and ORDER BY MUST 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 openEHR object_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 is active, offline, time-out, node-error, not-resolved, consent-denied, excluded and not-localized. A node that was reached and answered with a failure MUST be reported node-error, not offline or active. Registry members not in scope SHOULD also appear, as excluded or not-localized. (§11.1.)

Result-set shape

  • N17. The result set MUST be a conformant openEHR RESULT_SET as 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, including rows as an ordered array of values per column and the optional name for stored queries, and this specification does not restate them. A gateway MUST populate q, columns and rows, and MUST carry the federation additions of §9.1 nested under a single unprefixed meta.federation object, never as direct members of meta and never using the openEHR-reserved prefix (§9.1). Two federation-specific constraints apply: when no ENDPOINT attribute is selected, rows MUST NOT contain endpoint columns (single-CDR compatibility); and columns[] 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 Endpoint and Organization resources are RECOMMENDED for the registry. An Endpoint SHOULD carry a defined openEHR Query-API connection type, so implementers MUST register a real code such as openehr-rest-query, or bind a specified system value; an informal string does not satisfy this. Every Endpoint MUST have a stable unique identifier used in directives. (§8, §9, §15.)

  • N20. Every Endpoint MUST have exactly one managing Organization (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 observed creating_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/VERSION identified in a result row, routing to the owning CDR by (in priority order) the creating_system_id embedded in the OBJECT_VERSION_ID, else a carried endpoint_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 where system_id == creating_system_id of the target object, and MUST reject writes it cannot unambiguously route. New-object creation MUST target an explicitly chosen node, via FROM ENDPOINT or the openEHR-federation-endpoint header. 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/openehr is 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 form WHERE e/ehr_id/value = …, and SHOULD accept FROM EHR e[ehr_id/value=…]. The forms are semantically equivalent, and this spec’s use of the WHERE predicate 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 least id, organisation and status. 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 the openEHR-federation-endpoint response header and SHOULD return the node’s system_id in openEHR-federation-system-id. Location and ETag MUST 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 Implemented or routed to a single explicitly chosen node, and MUST be declared under N30. node_id, endpoint_id and system_id MUST 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_id alone. The gateway MUST NOT dispatch any directly identifying identifier (§5.1) in any of those positions, including a WHERE/SELECT/ORDER BY path over PARTY_IDENTIFIED.identifiers / DV_IDENTIFIER, PARTY_RELATED or an ENTRY-level subject, 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_ref or an ENTRY-level subject PARTY_IDENTIFIED/DV_IDENTIFIER predicate, 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 over COMPOSITION.composer, EVENT_CONTEXT.health_care_facility, PARTICIPATION.performer or ATTESTATION.committer that 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_id alone, and the node’s environment MUST provide a way to exchange a patient identifier for that ehr_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 (the openEHR-federation-endpoint request 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 with 400, 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 has system_id == creating_system_id, it MUST reject with 409 Conflict naming the controlling system instead of forking the object. Suppressed endpoints MUST remain visible in meta.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, with 504 for timeout or unreachability and 424 Failed Dependency for a node error, 504 taking precedence where both occur, instead of returning partial rows. A failing response MUST still carry meta.federation.endpoints[] with the per-node status and meta.federation.complete: false, so the client can see which node failed. meta.federation.complete MUST be present in every mode and MUST be true only when every in-scope node reached status active. A node reported not-localized or excluded was not in scope and MUST NOT clear it (§11.1). A node reported not-resolved or consent-denied does clear it but MUST NOT fail the query (§11.3, N6, N27). A best-effort mode, returning partial rows with complete: false and HTTP 200, 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-out instead 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 via Prefer: wait=, and a gateway MUST NOT extend beyond its own budget on request. A time-out endpoint means unknown, never no data. (§11.5.)

  • N39. For ORDER BY with LIMIT n, the gateway MUST dispatch LIMIT n per node and then re-apply ORDER BY and LIMIT n across the merged rows, with deterministic tie-breaking. For OFFSET > 0 it MUST either reject (400), compute the page correctly from k + n rows per node, or serve it from a materialised ordered result, and MUST declare which. It MUST NOT push OFFSET down 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 BY groups spanning nodes MUST be merged, or the query rejected. (§11.6, N14.)

  • N40. meta.federation.endpoints[] MUST carry, per endpoint, id, status, error on error, and the gateway-observed latency_ms for every endpoint the gateway actually dispatched a query to (active, offline, time-out, node-error). latency_ms MUST be omitted, never invented, where the status was settled before dispatch (excluded, not-localized, not-resolved, a pre-filtered consent-denied). It SHOULD carry node_id, system_id, organisation, product, version and row_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_id to its owning node in priority order: explicit target on the request, then a held resolution binding, then an ehr_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 with 400 and require an explicit target. (§12.5.1.)

  • N42. (Gateway, at request time.) If the same ehr_id is claimed by more than one node, the gateway MUST NOT choose between them. It MUST fail with 409 Conflict listing 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_id generation (UUID-v4 or equivalent), non-reuse of ehr_id s, non-adoption of foreign ehr_id s on import, and federation-wide uniqueness of system_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 second PUT to 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 carries FROM ENDPOINT targeting, 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-REST name member, naming the gateway’s stored query. Where no registry is offered, N43's single-node rule is unchanged. (§12.7.)