Build1 publisher3 min readPublished
A pure-Go DICOM stack takes CGO out of the hospital build pipeline
go-dicom covers files, pixels and the full DIMSE network surface with no C dependencies, which restores static binaries and cross-compilation. Conformance evidence is not published yet.
The Engineer · Build desk
Drafted by a language model from the sources cited here and checked against its claim ledger before publication. How we use AISend a correction

What happened
- go-dicom, by Amr Shadid, is described as a complete DICOM implementation in pure Go covering files, pixels and the network protocol, with no C dependencies at all. Installed via `go get github.com/amrshadid/go-dicom`.
- `CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build` works and produces one static binary with no shared library dependencies, enabling a FROM scratch container, an ARM edge device next to a modality, or a Lambda function, all from a laptop with no cross-compilation toolchain and no C headers.
- Prior options for handling medical images in Go were: bind to dcmtk or DCMTK-derived C libraries and accept CGO, losing static builds, easy cross-compilation and deployment simplicity; shell out to a Python process running pydicom; or write the needed parts of the DICOM standard yourself.
- The author states that writing your own parser is how a lot of hospital integrations quietly end up with a half-finished parser in an internal repo, and that he built go-dicom after hitting that wall on a medical imaging platform he maintains.
- File support covers standard .dcm, Siemens .ima, DICOMDIR and raw data sets with no meta header (what modalities produce and what travels on the wire), across 37 transfer syntaxes, implicit and explicit VR, little and big endian, and deflated.
Compiled by The EngineerSomething wrong?How this is made
Why it matters
Amr Shadid has released go-dicom, a DICOM implementation written entirely in Go covering files, pixel data and the network protocol, with no C dependencies [1]. The interesting part is not the feature list but the build: according to the author, `CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build` produces one static binary with no shared library dependencies, which is what makes a `FROM scratch` container, an ARM box sitting next to a modality, or a Lambda function reachable from a laptop with no C headers to hunt down [2].
That is the actual problem being solved. Until now a Go team handling medical images could bind to DCMTK or a DCMTK-derived C library and accept CGO, losing static builds and easy cross-compilation, shell out to a Python process running pydicom, or write the parts of the standard it needed by hand [3]. The author says the third path is how a lot of hospital integrations quietly end up with a half-finished parser in an internal repo, and that he hit the wall himself on a medical imaging platform he maintains [4].
The claimed surface is wide. On files: standard `.dcm`, Siemens `.ima`, DICOMDIR, and raw data sets with no meta header, which is what modalities emit and what travels on the wire, across 37 transfer syntaxes including implicit and explicit VR, both endiannesses, and deflated [5]. Tag dictionaries cover 5,000-plus standard tags and 10,500-plus private vendor tags for GE, Siemens, Philips and Toshiba with O(1) lookup [6], roughly 15,500 entries in total [7]. Networking is the full DIMSE set as both SCU and SCP, including C-GET, all six N-DIMSE services, Storage Commitment, Modality Worklist, MPPS and UPS [8], with association negotiation covering presentation contexts, extended negotiation, async operations, role selection and user identity, plus TLS on both ends [9]. Above that sit structured reports with SNOMED-CT and LOINC coding, ECG and EEG waveforms with QRS detection, overlays and ROI analysis, and 169 storage SOP classes [10]. Text is decoded to UTF-8 on read across 30-plus encodings including ISO 2022, CJK, Cyrillic, Arabic and Hebrew [11].
The de-identification detail is the one that suggests someone has been bitten before. It implements the PS3.15 Annex E profiles, basic, clean descriptors, clean graphics and the retain-* variants for longitudinal studies, and it descends into sequences [12] because an object stripped at the top level that keeps its Referenced SOP Instance UIDs still links back to the original [13]. Idiomatically, the SCP spawns a goroutine per association, C-FIND results stream on a channel rather than accumulating in a list, and everything takes a `context.Context` [14]; the client API is shaped roughly like pynetdicom's `AE().associate()` [15], with handler types for echo, storage, query/retrieve and worklist, plus a composite handler and an embeddable BaseHandler [16].
Two things to check before you commit. The pixel decoder list names five codecs, JPEG Baseline, Extended, Lossless, JPEG-LS and RLE Lossless, with encoders for only RLE and JPEG-LS [17]; JPEG 2000 does not appear in it [18], so match that against what your archive actually holds before assuming 37 parsed transfer syntaxes means 37 renderable ones. And the announcement carries no benchmarks, no test coverage figure, no conformance statement and no license [19]. The install script does verify the SHA256 against the release SHA256SUMS, prefers a directory already on PATH so nothing needs sudo, and clears the macOS quarantine flag [20], which is more care than most `curl | sh` installers take, but it is still `curl | sh`.
Everything above is the author's own account of his own library [21]. Worth watching for an independent conformance run against a real PACS, and for whether the private tag dictionaries hold up against vendor data that was never meant to be read by anyone else.