Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Conformance statement

FerroFED claims the Federation-Gateway profile of the Federation Tier with AQL specification, at the version and commit the claim table names. This page is that claim. It names the specification text the claim is made against, the node products the tests ran against, every conformance point of §17 and every test track of §16.3 with how each is scored, and every deferral with its reason.

The part between the statement markers is generated by scripts/conformance/matrix.sh --statement-write from the conformance matrix, docs/VERSIONS.md, the vendored specification and the // conformance: markers on the tests. The docs build and the conformance-matrix guard both fail when it disagrees with them, so the statement cannot claim more than the matrix holds. The conformance matrix shows the same points by requirement, and the obligations checklist covers every normative statement of the text.

What the claim covers

  • Scored by a test means at least one test carries the point’s marker and CI runs it. The conformance tests page lists each test by point and track. A test in an e2e module runs in the CI e2e job, with FERROFED_E2E=1, against the node products below.
  • The Federation-Gateway profile only. FerroFED holds no clinical data and is not a CDR, so it makes no Federation-Node claim. A point whose actor is Node or Operator is scored against that actor, and a gateway is not marked down for it (§17, §16.2). FerroFED assists those actors with the node profile checks and the admission check, and the last section of the generated part records what each found.
  • CP-20 and CP-39 are tested without a marker. A marker on a Node or Operator point belongs to a harness check under tools/, which writes the finding the report reads. The gateway’s part of each is tested in its own crate: the registry load refuses an endpoint with a FHIR connection type (CP-20, app/ferrofed-registry/tests/it/refusal.rs::a_fhir_rest_connection_type_is_refused), and the book answers each §13.4 deployment decision in a section of its own (CP-39, app/ferrofed-server/tests/it/deployment_decisions.rs).
  • A deferral is the owner’s decision, recorded on the issue its row names, with the reason the specification gives for it.

The claim

ProfileFederation-Gateway (§16.2)
SpecificationFederation Tier with AQL 0.9.0, release candidate of 2026-09-13
Sourcesyntaric/openehr-federation-spec at 7162d0c760d23105d62a743bf0ad1073c45fdb85
Rendered texthttps://syntaric.github.io/openehr-federation-spec/federation-aql/0.9/
Gateway points34 of 35 scored by a test, 1 deferred, 0 planned
Node points3, scored against the member CDR
Operator points3, scored against the federation operator
Test tracks10 of 11 scored by a test, 1 deferred, 0 planned
Requirements46: 46 reached by a point, 0 by a track only, 0 by neither
Marked tests961, carrying 1234 point and track markers

The nodes

ProductPinnedScores
FerroEHR 4.3.3ghcr.io/ferrohealth/ferroehr:4.3.3@sha256:1a5580b510dca1e49418e4c83431d19b92656d06ea0961d28d7df03d518b941fthe Connectathon tracks, the differential run and the node profile
EHRbase 2.36.0ehrbase/ehrbase:2.36.0@sha256:c8e642264b73637e0576ec01b5c73f5dc9be6f34eb3644f0ced890c5f916640athe node profile
The reference implementationsyntaric/openehr-federation-ref at 92aff3cb1d8738ea0ce0e013b5a8fc2942438fd5the differential run, as evidence and never as an oracle

The scored matrix

PointActorRequirementsTracksStatusScored by
CP-1GatewayN11covered9 tests
CP-2GatewayN2, N51, 2covered4 tests
CP-38GatewayN332, 10covered5 tests
CP-3GatewayN32covered13 tests
CP-4GatewayN72covered3 tests
CP-5GatewayN4, N103, 4covered49 tests
CP-6GatewayN113covered20 tests
CP-7GatewayN52covered2 tests
CP-8GatewayN135covered55 tests
CP-9GatewayN155covered46 tests
CP-10GatewayN145covered45 tests
CP-11GatewayN164covered4 tests
CP-12GatewayN6, N164covered15 tests
CP-13GatewayN19, N20, N216covered19 tests
CP-14GatewayN226deferreddeferred
CP-15GatewayN236covered31 tests
CP-16GatewayN247covered28 tests
CP-17GatewayN257covered152 tests
CP-18NodeN267node-profilethe member CDR, by 4 harness checks
CP-19NodeN27, N27a7node-profilethe member CDR, by 2 harness checks
CP-20OperatorN196operatorthe federation operator
CP-21GatewayN289covered10 tests
CP-22GatewayN299covered12 tests
CP-23GatewayN309covered35 tests
CP-24GatewayN319covered23 tests
CP-25GatewayN329covered14 tests
CP-26GatewayN3310covered139 tests
CP-27NodeN3410node-profilethe member CDR, by 6 harness checks
CP-28GatewayN353covered18 tests
CP-29GatewayN365, 6covered5 tests
CP-30GatewayN374covered34 tests
CP-31GatewayN38, N404covered19 tests
CP-32GatewayN39, N14, N95covered145 tests
CP-33GatewayN41, N426, 11covered41 tests
CP-33aOperatorN42a11operatorthe federation operator, assisted by 3 harness checks
CP-34GatewayN439covered30 tests
CP-35GatewayN17, N181covered13 tests
CP-36GatewayN82, 7covered41 tests
CP-37GatewayN123covered19 tests
CP-39OperatorN257operatorthe federation operator
CP-40GatewayN449covered49 tests
TrackTitlePointsStatusScored by
1TransparencyCP-1, CP-2, CP-35covered3 tests
2subject → ehrId resolutionCP-2, CP-38, CP-3, CP-4, CP-7, CP-36covered7 tests
3Directed / endpoint pinCP-5, CP-6, CP-28, CP-37covered5 tests
4Partial resultsCP-5, CP-11, CP-12, CP-30, CP-31covered7 tests
5Dedup + DISTINCTCP-8, CP-9, CP-10, CP-29, CP-32covered4 tests
6Follow-up read/write routingCP-13, CP-14, CP-15, CP-20, CP-29, CP-33covered4 tests
7Auth conveyance + consent-denyCP-16, CP-17, CP-18, CP-19, CP-36, CP-39covered6 tests
8PMIR merge/split (ITI-93/94) (provisional)-deferreddeferred
9REST surface & self-descriptionCP-21, CP-22, CP-23, CP-24, CP-25, CP-34, CP-40covered7 tests
10Identifier leakage (adversarial)CP-38, CP-26, CP-27covered25 tests
11Integrity: ehr_id collision & admission conditionsCP-33, CP-33acovered4 tests

Deferrals

CP-14

  • Actor: Gateway
  • Requirements: N22, in track 6
  • Decision: section 12.3 and N22 route a version read on creating_system_id, while section 12a.1 route-ehr, section 12.5.1 and N41 route every request under a path ehr_id by that ehr_id; FerroFED follows N41 for every request under a path ehr_id by the owner decision on #64 (2026-10-03), the contradiction is reported on #212, and it is revisited at the 1.0 re-pin (#354)
  • Recorded on: #64, #212, #354

Track 8

  • Actor: Gateway: no conformance point scores the track (§16.3), and N3 falls in the Federation-Gateway profile (§16.2)
  • Requirements: N3
  • Decision: provisional in section 16.3, and section 18 lets its subject matter be deferred; the binding lifetime, the invalidation hook and the PMIR ITI-94 subscription that drives it from ITI-93 merges are built (#48, #147), and propagation to a later resolution is not claimed
  • Recorded on: #48, #147

Points scored against another actor

  • CP-18, Node: a Node obligation, scored against the harness CDR products and never the gateway (section 16.2); the gateway’s node profile checks assist it, which the harness runs against its products and conformance run –node-profile against each member of a deployment: they ask FerroEHR and EHRbase for an EHR they do not hold and for an unparsable query and read the ITS-REST error status, and read whether an EHR the node withholds from one principal is refused on GET /ehr/{ehr_id} and on both ehr_id-scoped query forms while it is served to another: FerroEHR started with Basic authentication and authz.rbac.ehr_access_default restricted withholds every EHR from a clinician, and EHRbase started with Basic authentication withholds the request path of one EHR from its user role through security.additionalAuthorizations, its narrowest access control; FerroEHR from 4.3.3 refuses that EHR on the read and on both query forms (FerroEHR #3562, #566), and EHRbase refuses the read and releases the EHR’s rows on both query forms; the verdicts are the report node class per product, never a gateway pass, and no specification governs the choice of products: our own design (#83, #93, #549, #573).
  • CP-19, Node: a Node obligation, scored against the harness CDR products and never the gateway (section 16.2); the node profile consent check reads a consent refusal an operator arranges at the node, on the read and on both ehr_id-scoped query forms; the harness arranges none at FerroEHR or at EHRbase, which records no consent decision, and ITS-REST defines no consent resource, so the point reads node-not-observable for both; the gateway half is CP-36, and no specification governs the choice of products: our own design (#83, #93, #549, #573).
  • CP-20, Operator: verified at admission or in the registry, not on a request (section 16.2); the gateway assists: its registry load refuses an endpoint whose connectionType is hl7-fhir-rest or no defined code (#74).
  • CP-27, Node: a Node obligation, scored against the harness CDR products and never the gateway (section 16.2); the gateway’s node profile checks assist it, which the harness runs against its products and conformance run –node-profile against each member of a deployment, against FerroEHR and against EHRbase: whether the node serves an EHR, its EHR_STATUS and its compositions by the ehr_id alone, with no patient identifier in any request the capturing proxy journals, whether an EHR created with no EHR_STATUS is read and queried by its ehr_id, and the ehr_id exchange the admission check reports over the harness PIX Manager; the verdicts are the report node class per product, never a gateway pass, and no specification governs the choice of products: our own design (#93, #549, #573).
  • CP-33a, Operator: verified at admission or in the registry, not on a request (section 16.2); the gateway assists: ferrofed admission check reports each identifier-integrity condition of section 12b.2 as pass, fail or cannot-check with its evidence, and the registry load refuses a second member with an existing system_id; the harness applies the check as the operator of track 11 to a FerroEHR candidate sharing a member’s system_id (#91); the node profile runs the same check against a FerroEHR member and an EHRbase member, each under a system_id of its own, and records each condition with its evidence (#93, #549) (#79, #91, #93, #549).
  • CP-39, Operator: verified at admission or in the registry, not on a request (section 16.2); FerroFED’s own answers to the five section 13.4 obligations, each naming the configuration that changes it, and the operator’s template are the book page website/book/src/operate/deployment-decisions.md, held to one section per obligation by app/ferrofed-server/tests/it/deployment_decisions.rs (#84).

Not observable

Some obligations cannot be observed from outside the node that holds them.

  • CP-18 and CP-19 in a deployment run. ferrofed conformance run --node-profile records the CP-18 access check and the CP-19 consent check as not-observable. The refusal under test is the node’s own access policy or consent decision, which an operator cannot arrange through ITS-REST, and ITS-REST defines no consent resource (Scoring a deployment).
  • CP-18 in the harness. The harness starts FerroEHR and EHRbase with an access policy that withholds one EHR from one principal, and its checks read the node’s answer on the read and on both ehr_id-scoped query forms.
  • CP-19 in the harness. Neither product records a consent decision, so the harness arranges no consent refusal and the point reads node-not-observable for both. The gateway’s own part of consent is CP-36 and track 7, both scored by tests (N27, N27a).
  • Track 10 in a deployment run. The run reports it not-run, because only node-side wire capture shows what reached a node (§16.3). The harness scores it with a capturing proxy in front of each node.

Where the specification disagrees with itself

CP-14 scores N22: a follow-up read of a version is routed on its creating_system_id first. The EHR-scoped rule of §12a.1 routes every request under {base}/v1/ehr/{ehr_id}/… on the ehr_id binding, “not on creating_system_id”, and N41 orders the steps for every path ehr_id (§12.5.1). Every ITS-REST version read sits under a path ehr_id, so the two rules disagree for every such read. FerroFED routes by N41, scored under CP-13 and CP-33, and defers CP-14 until the text settles it. The other half of N22, that a uid is never rewritten, is held by tests the obligations checklist names.

Every defect, contradiction or silence found in a specification while scoring is reported on the standing upstream-reports issue, #212. That includes the findings of the differential run (#94), which sent the same Connectathon requests to FerroFED and to the reference implementation over the same FerroEHR nodes and judged each difference against the specification text.

Rerunning the suite

The record, offline: the matrix against the vendored specification, every marker, and the generated pages.

bash scripts/checks/conformance-matrix.sh

The tests, and then the per-track and per-point report of a run over the whole workspace with the e2e harness, which needs Docker. The report lands in target/conformance/report.md.

cargo nextest run --workspace --locked --all-features
scripts/conformance/report.sh --run

To score your own deployment, run ferrofed conformance run (Scoring a deployment).

After a change to a status, a marker or a pin, render the pages again:

scripts/conformance/matrix.sh --render-write
scripts/conformance/matrix.sh --statement-write

When the specification is re-pinned

The pinned text is a release candidate. When the 1.0 release is pinned (#17), scripts/conformance/matrix.sh --derive refreshes the derived columns. A point new to the text arrives planned, and the guard refuses it until the re-pin scores it with a test or records the owner’s deferral. This statement then names the new version and commit. CP-14 and the other choices the reports on #212 bear on are revisited then (#354).