A pure-Rust FHIR terminology server for SNOMED CT, LOINC, ICD-10, ICD-11,
RxNorm, UCUM, and any FHIR CodeSystem, served from one binary over an index
built once per release and read at startup. No JVM, no Elasticsearch, no
database to run.
Five minutes to a running server · What it serves · The console · The API · Do you need a commercial licence?
The image serves UCUM, BCP 47, BCP 13, and ISO 3166-1 with no configuration, so the first call needs nothing beyond Docker:
$ docker run --rm -p 8080:8080 ghcr.io/ferrohealth/ferroterm:0.1.8
$ curl 'http://localhost:8080/r4b/CodeSystem/$lookup?system=http://unitsofmeasure.org&code=mg/dL'{
"resourceType": "Parameters",
"parameter": [
{ "name": "name", "valueString": "Unified Code for Units of Measure (UCUM)" },
{ "name": "version", "valueString": "2.2" },
{ "name": "display", "valueString": "mg/dL" },
{ "name": "property", "part": [
{ "name": "code", "valueCode": "canonical" },
{ "name": "value", "valueCode": "m-3.g" } ] }
]
}A clinical code system is a release you hold a licence for, built once into an
index by ferroterm-build (in the same image and every release tarball) and
served read-only. With the release compose.yaml and a SNOMED CT release zip
in ./releases (any other code system builds the same way, with its own
ferroterm-build flag, and SNOMED CT is optional):
$ curl -LO https://github.com/FerroHEALTH/FerroTERM/releases/latest/download/compose.yaml
$ docker compose run --rm build --rf2 /releases/SnomedCT_Release.zip --out /data/index/snomed
$ docker compose up
$ curl 'http://localhost:8080/r4b/CodeSystem/$lookup?system=http://snomed.info/sct&code=404684003&displayLanguage=nl'What each code system costs to build and serve, and how fast each operation answers, is in the speed and footprint table below, rendered from the benchmark records. The book's Install and run and Loading code systems pages cover every system and the binary distribution.
SNOMED CT, LOINC, UCUM, BCP 47, BCP 13, ISO 3166-1, ICD-10 (WHO), ICD-10-NL,
ICD-10-CM, ICD-11 MMS, ICD-11 ICF, ICD-11 Foundation, ATC/DDD, ICPC-2, RxNorm,
the DHD Diagnosethesaurus and Verrichtingenthesaurus, the G-Standaard, the
Nederlandse Labcodeset, the NHG ICPC-1 to SNOMED CT map, and any FHIR
CodeSystem, ValueSet, and ConceptMap resources (HL7 Terminology's 900+
systems load this way).
The code systems page of the book is the one list: per system the canonical URI, the versions and editions handled, the build command, and the licence position. You bring the release you are licensed for; UCUM and the registries are vendored into the binary and need nothing. Every system reaches the operations through one provider seam, so nothing in an operation is a special case for one system.
Measured by the benchmark harness on one machine, one record per
code system, warm p50 per operation; nothing here is typed by hand, and CI fails
when the table drifts from the records under bench/records/.
The set below was taken on 2026-09-22 with the 0.1.4 release binaries, natively on an Apple M2 with nothing else running: a record taken on a busy machine measures the operating system rather than the server. The resident and peak-build figures are the server's and the build tool's own, and the set's README states the machine's condition and the instrument behind each figure.
| Code system | Release | Concepts | Build | Peak build memory | Index on disk | Resident | $lookup |
$validate-code |
$subsumes |
$expand (small) |
$expand (large) |
Search | Snowstorm |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ICD-10-CM | 2026 | 98,827 | 1.51 s | 359 MB | 44 MB | 91 MB | 105 µs | 60 µs | 58 µs | n/a | n/a | n/a | not run |
| ICD-10-NL | 2021 | 42,769 | 880.52 ms | 221 MB | 21 MB | 66 MB | 96 µs | 60 µs | 64 µs | n/a | n/a | n/a | not run |
| ICD-11 MMS | 2026-01 | 37,211 | n/a | n/a | 44 MB | 99 MB | 138 µs | 59 µs | n/a | 129 µs | 116 µs | 139 µs | not run |
| LOINC | 2.83 | 257,266 | 8.15 s | 2.54 GB | 310 MB | 440 MB | 136 µs | 56 µs | n/a | 126 µs | 7.23 ms | 283 µs | not run |
| RxNorm (prescribable subset) | 09082026 | 81,468 | 4.58 s | 667 MB | 77 MB | 133 MB | 388 µs | 114 µs | n/a | n/a | n/a | n/a | not run |
| SNOMED CT (International edition) | 20260901 | 535,502 | 14.12 s | 3.06 GB | 559 MB | 943 MB | 217 µs | 66 µs | 59 µs | 215 µs | 1.7 ms | 353 µs | not run |
| SNOMED CT (Netherlands edition) | 20260630 | 548,949 | 17.66 s | 3.88 GB | 864 MB | 1.14 GB | 222 µs | 66 µs | 59 µs | 285 µs | 2.28 ms | 817 µs | not run |
Warm p50 over 200 HTTP round trips on one machine (Apple M2, 17.18 GB, macos/aarch64), FerroTERM 0.1.4 serving FHIR R4B, taken 2026-09-22. The records are under bench/records/; the benchmarks page has the method, the cold and tail latencies, and how to reproduce a record.
The FerroTERM Console is the browser interface, its own image beside the
server. Start it with the quickstart's viewer profile and open
http://localhost:3000:
docker compose --profile viewer upIt renders every page on its own server and reaches FerroTERM over the same public FHIR API any client uses, so anything it does, your own client can do. It is one of the FerroHEALTH consoles and draws with their shared components.
- The dashboard counts what the deployment serves, lists the writes it recorded most recently, and leads into each operation.
- Code systems, value sets and concept maps are listings you filter, sort and page, each opening a detail screen with the definition, what the server can do with it, and the resource as JSON.
- Look up, check, expand and translate run
$lookup,$validate-codeand$subsumes,$expand, and$translate, offering what the deployment publishes and putting every parameter in the address. - The concept browser walks a hierarchy with the keyboard and offers only what the served version declares it can do.
Reading is open. Signing in, at your OpenID Provider through the console's own server, is for editing; the browser never holds a token. The console page has the screens and the configuration.
FHIR R4 under /r4, R4B under /r4b, R5 under /r5, and the R6 ballot under
/r6, each in its own version's shapes:
CodeSystem/$lookup, CodeSystem/$validate-code,
CodeSystem/$subsumes, ValueSet/$expand (paging, filter, version
pins, nested contains, inline and request-scoped value sets),
ValueSet/$validate-code,
ConceptMap/$translate (loaded, inline, and the SNOMED implicit concept maps),
$closure (on /r4, /r4b, and /r5; the R6 ballot defines none),
POST [base] with a batch Bundle, GET ValueSet/{id} and
GET ValueSet?url=, $versions, $cache-control, metadata, and
metadata?mode=terminology. A deployment that names a database in
FERROTERM_RESOURCES also serves POST, PUT, GET, DELETE, _history
reads, and ?url= search of CodeSystem, ValueSet, and ConceptMap, with
ETag and If-Match. Every failure is an OperationOutcome, never a bare
500, and a terminology failure carries a tx-issue-type coding.
Conformance is measured rather than claimed. CI runs the HL7 terminology
ecosystem suite against every pull request and holds a committed pass list per
served version: 637 of the 670 general cases on R5, 635 on R4, and 628 on
R4B. A case that newly passes is added to the list in the change that earned
it, and a case on the list that stops passing fails the build, so the figure
only ratchets. What the remaining cases are waiting on is on the tracker,
cluster by cluster: most are a parameter no published OperationDefinition
declares, or a suite mode that pins an edition no release centre distributes.
Every route answers FHIR JSON or FHIR XML, by _format or Accept.
v0.1.8 is the current release. The tracker's milestones are the roadmap, and the open issues under each are the worklist:
- v0.1.9: the viewer restructure and its design, the differential check against the Nictiz Nationale Terminologieserver for the Dutch variants, the benchmark record set retaken after the memory change with the published table re-rendered from it, a shorter sync run record, the rest of the FerroHEALTH organisation setup with both SPDX lines on every first-party file, the documentation narrowed to FerroTERM's own with the family book linked for the rest, and the NHG tables for licence holders once Nictiz grants that licence.
- v0.3.0: the suite cases that are waiting on an upstream ruling rather than on work here, the two unmaintained advisories in the viewer's dependency tree, and the OpenSSF Best Practices badge.
The design, with its citations, is docs/architecture.md.
The short form:
- Offline once, online from precomputed structures.
ferroterm-buildturns a release into aredbstore with its dense columns (concepts, designations, properties), a CSR is-a adjacency with roaring transitive-closure bitmaps (hierarchy.bin), and anfstword index (text.bin). The server reads them at startup and answers from memory: subsumption is a bitmap test and a descendant set is a bitmap. Nothing is memory-mapped, because mapping a file takesunsafeand the workspace forbids it. - FHIR is generated, never hand-written. The model is the
fhir-typescrate, emitted from the pinned HL7 packages (R4 4.0.1, R4B 4.3.0, R5 5.0.0, R6 ballot 5, HL7 Terminology) so each version's operation surface is right by construction. The FerroBRIDGE repository generates and publishes it; this one consumes it from crates.io at a pinned version. - The engine is code-system-neutral. Providers own the semantics
(
crates/fhir-terminology); the operations talk to the seam. - Supply chain. Releases are built in a reusable workflow to SLSA Build
Level 3: signed provenance, an SBOM per artifact (CycloneDX for the
binaries, SPDX for the image),
cargo auditablebinaries, a distroless image forlinux/amd64andlinux/arm64, verifiable withgh attestation verify.
Production use is free for Non-Commercial Purposes and needs a commercial
licence otherwise. LICENSE is the authority; this table is the same boundary
in the order people ask about it.
| What you are doing | What you need | Why |
|---|---|---|
| Reading, building, modifying, or redistributing the source | Free | The licence grants this without a fee and without asking anyone; only production use is restricted. |
| Development, testing, evaluation, prototyping | Free | None of these is production use, which is the only thing the Additional Use Grant limits. |
| Personal use | Free | Named in the grant's definition of Non-Commercial Purposes. |
| Academic or scientific research, or teaching | Free | Named in the grant's definition of Non-Commercial Purposes. |
| Production use by a non-profit or public body that is not in the course of a business, does not deliver a service for payment, and is not for commercial advantage | Free | The grant's definition of Non-Commercial Purposes, in the licence's own words. |
| Treating patients, or delivering any other service for payment | Commercial licence | The grant ends with "any other production use, including the delivery of health care or any other service for payment, requires a commercial license from the Licensor". |
| Any production use by a vendor or integrator | Commercial licence | A business's production use is not a Non-Commercial Purpose, so the same closing sentence of the grant applies. |
| Offering FerroTERM, or a work derived from it, to third parties as a hosted, managed, or embedded terminology service | Commercial licence | Carve-out (a) of the grant, which the licence defines as a service through which anyone other than you and your affiliates stores, manages, or queries terminology, code system content, or health data. |
| Selling, sublicensing, or otherwise distributing it for a fee, on its own or inside another product | Commercial licence | Carve-out (b) of the grant. |
The last two rows hold whoever you are: hosting it for third parties and distributing it for a fee need a commercial licence in every case, including for an organisation the free rows above would otherwise cover.
Each version becomes Apache License 2.0 four years after that version is published. A commercial licence is arranged with Cadasto B.V., the Licensor, which handles the business side of FerroTERM: write to info@cadasto.com or use https://www.cadasto.com/contact/. Technical questions go to the maintainer named in MAINTAINERS.md.
The software is source-available under the Business Source License 1.1
(LICENSE, NOTICE), with no open-core tier: the
engine, the server, and the tools are in this repository under the one
licence, and nothing is held back to be sold back to you.
The licence lets you read, build, modify, and redistribute the source without
a fee and without asking anyone, and it covers every non-production use:
development, testing, evaluation, and prototyping. Production use is free for
Non-Commercial Purposes, which the licence defines as personal use, academic or
scientific research, teaching, and use by a non-profit organisation or public
body that is not in the course of a business, does not deliver a service for
payment, and is not for commercial advantage. Any other production use needs a
commercial licence from the Licensor: a hospital, clinic, or care provider
running FerroTERM for its patients needs one, and so does a vendor, integrator,
or any company running it in production. Offering FerroTERM, or a work derived
from it, to third parties as a hosted, managed, or embedded terminology
service, and selling, sublicensing, or otherwise distributing it for a fee on
its own or inside another product, need a commercial licence in every case.
Each version becomes Apache License 2.0 four years after that version is
published. One crate is outside all of this: rf2 (the SNOMED CT release file
reader) is Apache 2.0 on crates.io, so any Rust project can use it without a
licence conversation. A commercial licence is arranged with Cadasto B.V., the
Licensor, which handles the business side of FerroTERM: write to
info@cadasto.com or use
https://www.cadasto.com/contact/. Technical questions go to the maintainer
named in MAINTAINERS.md.
The code systems are licensed separately: SNOMED CT by SNOMED International, LOINC by Regenstrief, ICD by WHO, RxNorm by NLM, and the repository ships none of their content. You bring the release you are licensed for; UCUM and the IANA and Unicode registries are vendored under their own licences, recorded beside the data.
CONTRIBUTING.md has the rules; the open issues are the
worklist. Every change ships with tests, and conformance-facing behaviour cites
the FHIR or SNOMED specification it implements.