Instructions for use
These are FerroFED’s instructions for use under Regulation (EU) 2025/327 on the European Health Data Space (the EHDS Regulation). Art 30(1)(d) has every EHR system accompanied, free of charge, by “clear and complete instructions for use”; Art 32(2)(d) has them cover “its maintenance, in accessible formats”; and Annex III, point 1(j), puts “the instructions for use for the user and, where applicable, installation instructions” in the technical documentation. Annex II, point 1.2, asks that the system “can be supplied and installed, taking into account the instructions and information provided by the manufacturer, without adversely affecting its characteristics and performance during its intended use”.
This page is the entry point. It says what to do in order and links the
reference page that holds each detail. The
information sheet names the
manufacturer, the version, the intended purpose, the data categories and the
standards. Every quotation is from the Official Journal text, vendored at
docs/specs/eu-ehds/reg-eu-2025-327-en.xhtml.
Who reads which part
| You are | Read |
|---|---|
| The operator who installs and runs a deployment | every section below |
| The developer of a client application clinicians use | Intended use, Reading an answer safely, Limitations, and The client contract |
| A deployment’s data protection officer or counsel | Configuration for a deployment in the EU, Limitations, Data protection and Regulatory status |
Intended use
FerroFED is used by healthcare providers, through the client applications their health professionals use in patient care, to read and write a patient’s openEHR records across the clinical data repositories (CDRs) of a federation. It answers an ordinary AQL query with one result set that names the CDR each row came from, and routes follow-up reads and writes to the CDR that holds the record (Intended purpose).
Use it only for that. FerroFED computes no score, flags no finding and recommends nothing. A client application, a stored query an operator publishes, or a change to FerroFED’s code can bring a purpose of its own, which is outside the manufacturer’s intended purpose and is assessed on its own (The medical device question).
Before you install
- Check the platform. FerroFED runs on Linux,
x86_64oraarch64, as a container image or a static binary; nothing is built for Windows (Supported platforms). The gateway holds little in memory: about 2 MiB at rest in the measurement on Memory footprint. Each concurrent query holds its members’ answers, up tofederation.max_node_answer_byteseach (The answer bound), so size memory from the sizing below. - Gather the services it consumes: two or more openEHR CDRs that serve ITS-REST 1.1.0, an identity provider for your callers, a PIX Manager, and an Audit Record Repository with the FHIR Feed of ITI-20 (What you need).
- Agree the conditions of membership with each CDR’s operator: a unique
system_id, version-4 UUIDehr_ids never reused or adopted, eachehr_idfed to the PIX Manager, and consent enforced at the CDR (Prepare each CDR).
Installation
Follow A production deployment in order. Its steps are the installation instructions:
- Verify the image or binary against its attestation and pin it by digest (Install).
- Write the registry with every endpoint suspended (The registry).
- Trust your callers’ issuer (Client authentication).
- Give each member its onward credential and make the signing key (Onward credentials).
- Configure identity resolution (Identity resolution).
- Send the access records to your Audit Record Repository (The access log).
- Put TLS in front of the gateway, or on its listener (TLS and the public address).
- Run
config check, start the gateway, and admit each member withferrofed admission checkbefore you take its endpoint out of suspension (Check, start and admit). - Send a first query and read its
meta.federation(The first federated query).
ferrofed config check reads the configuration exactly as serve does and
exits 78, naming the key, on anything it refuses. Run it before every
start and every upgrade. Work through the hardening checklist
before the gateway reaches real patient data.
Configuration for a deployment in the EU
Four settings decide whether a deployment meets the EHDS Regulation where the gateway can meet it. Add them to the configuration of the production guide:
# ferrofed.toml
[federation.consent]
disclose = false # Art 8: a restriction is never shown to a provider
[[auth.issuer]] # the issuer of the production guide's step 4
issuer = "https://idp.example.org/realms/ferrofed"
jwks_uri = "https://idp.example.org/realms/ferrofed/protocol/openid-connect/certs"
[auth.issuer.assurance]
claim = "acr"
minimum = "substantial" # Annex II 3.1; "high" for cross-border from 26 March 2032
substantial = ["urn:example:loa:substantial"]
high = ["urn:example:loa:high"]
[audit]
destination = "repository" # the access records of Annex II 3.2
[access_log.templates]
"Example Lab Report.v1" = ["Laboratory-Reports"]
"Example Discharge.v1" = ["Discharge-Reports"]
[access_log.retention]
years = 3 # Art 9(2): at least three years from each access
[federation.consent] disclose = false. Art 8 reads: “The fact that a natural person has restricted access … shall not be visible to healthcare providers.” The default,true, reports a member a consent pre-filter excludes asconsent-denied, which the Federation Tier requires (N27a). In the EU setfalse(Withholding consent exclusions).- The assurance level, per issuer. Annex II, point 3.1, asks for
“reliable mechanisms for the identification and authentication of health
professionals”. Set
[auth.issuer.assurance]for every issuer whose tokens reach patient data, withminimum = "substantial"; Implementing Regulation (EU) 2026/2099 Art 6(3) raises a cross-border exchange to"high"from 26 March 2032. List the values your issuer writes; no specification says whichacrvalues stand for which level (The assurance level). - The access records to a repository. Every access to patient data is
recorded with the verified caller, the patient, the categories and the
origins, and an access whose record cannot be stored is refused
503 access-unrecorded(The access log). - A category map. FerroFED ships none. Author one in
[access_log]from the templates your members hold, or every access is recordedehds-unclassified([access_log]). - The retention periods. Art 9(2) keeps the information on each access
“for at least three years from each date of access”. Declare in
[access_log.retention]any longer period your national law sets for a category or an origin, and keep each record at your repository until itsehds-retention-ends(How long a record is kept). - The patient namespaces. Art 9(1) gives the person information on
“any access”. Name in
[access_log] patient_namespacesthe namespaces your Member State’s access service searches the log by, so the record of a request addressed byehr_idnames the patient (The patient behind anehr_id). - The emergency purposes. Art 11(5) asks that an access to restricted
data in the vital interests of the patient be logged “in a clear and
understandable format”. FerroFED names no emergency purpose of use.
Declare in
[[access_log.emergency_purpose]]the codes your issuers put in the token for it, such as the HL7 v3ActReasoncodeBTG, or no access is marked (Emergency access). - The receiving members. Annex II, points 2.2 and 2.3, ask that an EHR system “be able to receive” data in the European exchange format. This release has no receive path, so there is no receiving member to configure; it is planned with a member declared per category (#665).
Keep two defaults, which the claims review assessed against Annex II, point 2.5:
- Keep
[federation] best_effortat its default,true. Withfalse, a client can no longer ask for a partial answer, so one silent member blocks every answer (Completeness). - List each member’s consent refusal codes. A member’s consent refusal
fails the whole query
424until its codes are in the registry’sconsent_refusal_codesfor that endpoint (Consent). Ask each member’s operator for them at admission.
Sizing
The budgets and limits bound how long one request or one slow member can hold the gateway. Set too low, they delay access; set too high, one slow member holds the clinician’s answer. Size them from what you measure:
| Setting | Default | How to size it |
|---|---|---|
federation.per_node_timeout_ms | 10000 | above the slowest member’s 99th percentile on ferrofed_node_request_duration_seconds, measured under your load (Metrics) |
federation.overall_timeout_ms | 25000 | above per_node_timeout_ms plus the resolver’s and the localizer’s time on their duration histograms (Timeouts) |
server.request_timeout_ms | 30000 | more than one second above overall_timeout_ms; config check refuses less |
server.max_concurrent_requests | 512 | the queries your members can take at once divided by the number of members each query asks (The concurrency limit) |
federation.max_in_flight_per_node | 64 | the requests the smallest member can serve at once; past it a member is reported time-out (The per-member cap) |
[server.caller_rate] | off | set only where one caller can crowd out others (The per-caller rate) |
After a change, watch ferrofed_overload_refusals_total and the
FerroFEDOverloaded, FerroFEDMemberCapSaturated and FerroFEDSlowAnswers
alerts (Dashboard and alert rules).
Reading an answer safely
These are the instructions a client application passes on to the clinicians who use it. Each one is a control of the clinical safety risk file.
- Read
meta.federation.completebefore the rows.falsemeans a member that may hold the patient’s data contributed nothing: it was not resolved, it was excluded on consent, or, under a partial answer, it did not answer. Show that the answer is incomplete, and name the members that did not contribute;meta.federation.endpointslists each with its status (What a client gets back). - Ask for a partial answer only where an incomplete record is safe to
show. By default a member that does not answer fails the query with no
rows.
openEHR-federation-completeness: partialreturns the other members’ rows withcomplete: false(Completeness). - Expect the same fact from two members. Two CDRs can hold the same entry, for example a medication both recorded. The gateway returns both rows, each with its endpoint, unless the client asks for de-duplication by version identity, which removes only copies of one version. Show the origin of each row, and never add up quantities across members without checking for duplicates.
- Show the origin. The
openEHR-federation-endpointresponse header names the members that contributed rows, and a query that selects theENDPOINTattributes gets each row’s origin in the row (Provenance columns). A follow-up read or write goes to the CDR that holds the record. - Treat
not-resolvedas unknown. In a deployment withdisclose = false, a member the patient restricted reads exactly as a member that does not know the patient. Neither means the patient has no data there. - Read a
503 access-unrecordedafter a write as an unknown outcome. The node may have stored the write before the gateway failed to record the access. Read the resource before writing it again (Failing closed).
Maintenance and its frequency
Art 30(1)(k) has the manufacturer inform users “of any mandatory preventive maintenance of the EHR systems and its frequency”. This release sets no maintenance on a fixed calendar. The maintenance it needs is triggered by an event, and its frequency is the event’s:
| Maintenance | When | Reference |
|---|---|---|
| Upgrade to the newest release | at each release, and as soon as you can after a security advisory: security fixes go to the latest release only | Upgrading, SECURITY.md |
Run config check | before every start, reload and upgrade | Running it |
Re-run ferrofed admission check for a member | when the member changes its CDR product, its version, its system_id or how it creates ehr_ids | Admitting a node |
| Update the category map | when a member adds or changes a template | [access_log] |
| Watch the alerts | continuously, on the shipped alert rules | Dashboard and alert rules |
| Clear a quarantined audit record | when ferrofed_audit_quarantined rises, after reading why the repository refused it | The audit spool |
| Rotate the signing key | on your key policy’s schedule, and at once when it may have leaked | Rotating the signing key |
| Renew TLS certificates and onward credentials | before each expires | Hardening |
| Re-read this page and the information sheet | at each release | the changelog |
When a release makes maintenance mandatory, the manufacturer says so in its upgrade notes and through the channels of Complaints and incidents.
Limitations
Art 28(b) forbids “failing to inform the professional user of likely limitations related to interoperability or security features of the EHR system in relation to its intended purpose”. The full list is Limitations; the ones a deployment in the EU must plan for are:
- The European interoperability software component is not served. The gateway produces and receives no document in the European exchange format (Annex II, points 2.1 to 2.3); the component is a library, and its open hazards are in the clinical safety risk file.
- The logging component is read at your Audit Record Repository. The gateway has no review route of its own: the records are read there with ITI-81, and the repository sets who may read them and keeps each for the period it states (Annex II, points 3.3 and 3.4; Reading the log). A query over many patients’ data names no patient in its record, so a search by the person’s identifier does not find it, and the records carry no label for access rights by origin and category (#797).
- The professional’s identification and assurance level are not yet in the access record (#742).
- No EU declaration of conformity has been drawn up, and no release carries the CE marking (Regulatory status).
- The Federation Tier specification is a release candidate, v0.9.0.
Accessible formats
Recital 37 asks for instructions “in accessible formats for persons with
disabilities”. These instructions are HTML text with headings, lists and
tables with header rows; no step is carried by an image or by colour
alone. The source is plain Markdown,
website/book/src/operate/instructions-for-use.md,
and the book’s print page renders every page as one document. Ask the
single point of contact for
another format.
Problems and complaints
When the gateway refuses to start, answers with an error code, or logs a failure, look the symptom up on Troubleshooting: it leads from the status, the code or the log line to the cause and the setting that fixes it.
Report a problem, a complaint or a possible serious incident through the channels on Complaints and incidents. Never send patient data.