`sct` CHANGELOG and release announcements

Hi Open Health Hubbers,

sct has moved quite quickly since I first posted about it, so I am starting this topic as a plain-English changelog. I will add a reply here whenever a new release has something useful to say, concentrating on what people can now do rather than the internal plumbing.

The current release is v0.19.0. This is a snapshot of what sct can do today.

What sct can do now

  • Start with a raw SNOMED CT RF2 release, including a .zip straight from NHS TRUD, and turn it into something much easier to work with locally.
  • Produce a single readable NDJSON data file, a searchable SQLite database, Parquet for data analysis, or a folder of Markdown files. These are all local files that you control and can regenerate at any time.
  • Find concepts quickly by code, term or synonym, with exact search, typo-tolerant search, search-as-you-type and optional meaning-based search.
  • Browse concept hierarchies and relationships, run ECL queries, draw diagrams and compare two SNOMED CT releases to see what changed.
  • Work with reference sets, historical replacements and mappings between SNOMED CT, CTV3, Read v2, ICD-10 and OPCS-4.
  • Build and validate clinical code lists as ordinary text files that can live in Git, while still importing and exporting common formats.
  • Explore SNOMED CT through a terminal interface or local web interface instead of having to learn every command first.
  • Run a local FHIR R4 terminology server, either directly or with Docker.
  • Give AI assistants such as Claude and Cursor safe, local access to terminology lookup and search through MCP.
  • Use the same terminology engine from Rust applications through the new Rust SDK.
  • Install prebuilt releases on Linux, macOS and Windows, including Homebrew, Scoop, AUR, Cargo and the shell installers.

The important bit is that the terminology stays on your machine. There is no Java or Elasticsearch requirement, and ordinary use does not depend on a remote terminology server. sct does not bundle SNOMED CT content itself, so you still need access to an appropriately licensed release.

Questions, bug reports, ideas and examples of how you are using it are all very welcome in this category.

Here’s my original video on sct from back in March - it’s a reasonable intro, but I will be updating it soon with a new video as there has been a LOT of further work optimising and improving sct since then.

sct v0.20.1 is out.

The headline change is a much leaner sct ndjson: RF2 input and concept output are now streamed instead of building several complete in-memory copies. On the 837,930-concept UK Monolith, peak memory fell from 6.42 GiB to 3.73 GiB (42% lower), while the canonical output remained byte-identical. The same paired run completed in 44 seconds rather than 59 seconds.

This should make full-release builds much more practical on 8 GB machines such as Raspberry Pis, and avoid the swap thrashing that can make conversion look stuck on slower disks.

Release: Release v0.20.1 · pacharanero/sct · GitHub

Benchmark details: Benchmarks - sct

sct 0.21.0

Release notes and binaries · Documentation

Two user-facing changes worth knowing about, plus a batch of robustness fixes.

One-step TRUD setup

Getting a new machine ready used to mean creating ~/.config/sct/, creating config.toml, and hand-writing a [trud] section before your first download. Now:

sct trud auth < my-trud-key.txt

It checks the key against TRUD before storing it, so a typo is caught immediately rather than on your first download, and it writes the file with owner-only permissions. If you already have a config.toml, only the api_key line changes - your comments and other settings are left exactly as they were. Piping the key (or --api-key-file) keeps it out of your shell history; passing it as an argument works too, with a warning.

Artefact names now follow the release through the pipeline

Previously sct ndjson named its output after the RF2 release, and then every later command threw that away, so you ended up with a generic snomed.db and no way to tell which release it came from. Every build command now names its output after its input:

uk-monolith-42.ndjson  ->  uk-monolith-42.db
                           uk-monolith-42.parquet
                           uk-monolith-42.fst
                           uk-monolith-42-embeddings.arrow

The name chosen is printed as Output: … so it is never a surprise, and sct still finds your database automatically with no --db flag - sct paths will show you which file it picked and why.

Breaking change, worth checking if you script sct: commands no longer write a fixed snomed.db unless the input is itself named snomed.ndjson. If you have a script that builds from a differently named NDJSON and then refers to snomed.db, either pass --output snomed.db explicitly or read the printed name. Interactive use is unaffected.

Robustness fixes

  • ECL parser: deeply nested or very long expressions could overflow the stack and abort the process. Pathological input now returns a clean parse error.
  • MCP server: a malformed Content-Length header silently ended the session, and the message size was unbounded. Framing is now validated and capped, malformed JSON-RPC gets a proper error response instead of silence, and an unterminated line can no longer drive an unbounded allocation.
  • TRUD: your API key could leak into error output, because it forms part of the request URL and transport errors print the URI. It is now redacted.
  • Read v2 import: a malicious or corrupt zip could be decompressed without limit. Entry sizes are now capped.

Also

sct runs on Android under Termux, and there is now a page about it. The published linux-aarch64 binary is statically linked, so it cannot use Android’s DNS resolver and sct trud fails on it; building from source in Termux with cargo install sct-rs works. The error message now tells you this rather than leaving you guessing.

We also corrected a claim in our own documentation: sct told users that NHS TRUD has maintenance windows “weekdays 18:00-08:00 and midnight-06:00”. TRUD publishes no downtime schedule at all - the only thing it says is to run automation on weekdays between 8am-6pm or midnight-6am. Our message had inverted one of the recommended windows into a maintenance window. It now quotes the guidance and cites the source.

sct v0.22.0

Released 2026-08-07. Full release notes · compare v0.21.0…v0.22.0

Highlights:

  • sct codelist export --format ecl - export a codelist’s active members as a compact, exact ECL expression, reusing the existing sct ecl compress engine. Round-trips cleanly with sct codelist add --ecl (the active member set survives export → re-import unchanged - now covered by an integration test).
  • New SNOMED CT primer in the docs - a plain-language introduction to core concepts (concepts, descriptions, relationships, refsets, ECL) for newcomers.
  • New FST / search-internals diagram with worked SNOMED examples, for anyone curious how the lexical index works under the hood.
  • sct trud downloads are now fsync’d before being persisted, closing a small window where a crash or power loss right after download could leave a truncated file mistaken for a complete one.
  • sct serve now warns when bound to a non-loopback address, since the FHIR endpoint has no built-in authentication.
  • Routine dependency updates (GitHub Actions + Cargo).

Install/upgrade: the usual channels are live - binaries, cargo install sct-rs, cargo binstall, the Homebrew tap, Scoop bucket, Docker Hub/GHCR images, and Python wheels via PyPI. The AUR package update is delayed this release - the AUR was down for maintenance at publish time - and will follow once it’s back up.

sct v0.23.1

Released 2026-08-14. Release notes · compare v0.22.0…v0.23.1

Highlights:

  • sct history brings together the inactive-concept story: why a code was retired and the SNOMED CT replacement association, with the same lifecycle context available from search, refset output, FST indexes, embeddings, the Rust SDK, and FHIR $validate-code.
  • sct bench is a local, user-facing self-benchmark. It can compare CLI and SDK query paths and emit text, Markdown, JSON, or a standalone HTML report without exposing host-identifying details.
  • The local FHIR R4 terminology server’s ValueSet/$expand now supports activeOnly, displayLanguage, includeDesignations, includeDefinition, and check-system-version. Unsupported input is refused rather than silently widening an expansion.
  • sct lookup now validates SNOMED CT identifiers with the Verhoeff check digit; the Rust SDK adds proximal primitive supertypes.
  • The release pipeline now builds the pinned Rust toolchain correctly on every supported target, so the public binaries and packages are reproducible across Linux, macOS, and Windows.

Install/upgrade: see the Installation guide.