12a. Which identifier routes what

The four identifiers (node_id, endpoint_id, system_id, creating_system_id) are defined in § The four identifiers. This section is the normative part: which one governs which request. Getting it wrong produces the worst class of bug in this architecture, a follow-up write delivered to the wrong CDR, and that bug succeeds silently.

12a.1 Routing rules (normative)

  • A follow-up read of a specific VERSION is EHR-scoped in ITS-REST ({base}/v1/ehr/{ehr_id}/composition/{uid}, {base}/v1/ehr/{ehr_id}/versioned_composition/{versioned_object_uid}/version/{version_uid}) and MUST be routed per route-ehr, on the path ehr_id. creating_system_id is a routing source only at step 4 of §12.5.1, after the explicit target, the binding and the index have named no node (§12.3, N22). If the located node holds an imported copy, the client receives that copy, and can tell because the openEHR-federation-system-id response header (§7a.3) then differs from the creating_system_id in the uid it requested.

  • A versioned write MUST be located per route-ehr and then committed only at the CDR that controls the target object. Control is established by system_id == creating_system_id of the target version - i.e. the CDR where the version was created, not merely a CDR that holds a copy of it. If the located node’s system_id differs, the gateway MUST reject the write with 409 Conflict naming the controlling system, and MUST NOT redirect it to the owner under the holder’s ehr_id (§12.4, N23, and the imported-copy hazard in §10.3).

  • An EHR-scoped request ({base}/v1/ehr/{ehr_id}/…), including a version read and a versioned write, MUST be routed on the path ehr_id: on the ehr_id → node binding established during resolution (§5.2) or carried explicitly by the client (§12.5), in the order of §12.5.1. creating_system_id describes versions, not EHRs, so it enters that order only at step 4, when the path carries an OBJECT_VERSION_ID and the earlier steps named no node.

  • A fan-out query is dispatched per endpoint_id (§8), and every contributing endpoint MUST be reported in meta.federation.endpoints[] under that same identifier (§9.5).

  • A result row that will be used for follow-up SHOULD carry both endpoint_id (where the row came from) and the uid (which carries creating_system_id). They answer different questions: endpoint_id says who told me, creating_system_id says who owns it. When they disagree, the row is a copy - see §10.3.

  • node_id, endpoint_id and system_id MUST be distinct namespaces. A gateway MUST NOT accept a system_id where an endpoint_id is expected (or vice-versa) merely because the strings happen to coincide, and the registry MUST be able to answer each lookup unambiguously. (N32.)

12a.2 Summary table for implementers

Question node_id endpoint_id system_id creating_system_id

Where do I send this fan-out query?

-

✔

-

-

Which node contributed this row?

✔ (via endpoint)

✔

✔

-

Where do I read this VERSION from?

-

✔ (from the ehr_id binding)

via registry (step 4)

step 4 / copy test

Where do I commit a new version of this object?

-

✔ (located by the ehr_id)

✔ (must equal)

✔ (must equal)

Where do I send {base}/v1/ehr/{ehr_id}/…?

-

✔ (from the ehr_id binding)

-

-

Who is accountable / audited?

✔

-

-

-

Who is admitted to the federation?

✔

-

-

-