Karnak: Open-Source DICOM De-identification Gateway

Karnak is a DICOM gateway for data de-identification and DICOM attribute normalization. It is distributed as a containerized and portable application, making it easy to deploy and use across various environments.

Karnak β€” DICOM gateway Volume imaging, radiography, ultrasound, video, pathology, signal and report objects flow into the Karnak DICOM gateway and come out de-identified, tag-morphed and conformant. DICOM DATA From multiple sources Volume imaging (CT, MRI, PET, SPECT) Radiography (CR, DX, XA, MG) Ultrasound (US, echo, Doppler) Video (endoscopy, surgery) Photography (clinical, dermatology) Pathology (macro / micro) Signal (ECG, EEG waveforms) Reports (SR, dose report) Many other object types Karnak DICOM GATEWAY Between modalities and archives DICOM (C-STORE) Transformed DICOM data (C-STORE, STOW-RS) Karnak DICOM GATEWAY Rules engine profiles in YAML DICOM library dcm4che & standard Scalable with your hardware Integration DICOM & DICOMweb TRANSFORMED DATA De-identified. Normalized. Validated. De-identification Basic confidentiality profile Pseudonyms per project Pixels masked, CT defaced Tag morphing Standard and private tags Normalized and consistent Driven by your conditions Conformance report Checked against the standard Malformed values flagged Report emailed per study FOR RESEARCH Reproducible, shareable datasets De-identification follows DICOM PS3.15 Deterministic UIDs from a project secret Pseudonyms from CSV, DICOM tags or an API Date shifting or reduced date precision Conditional rules inside each profile Portable app for collaborative projects FOR IMAGING IT A standard node in your network C-STORE in, C-STORE or STOW-RS out Single sign-on with OpenID Connect Per-destination filters and transcoding Echo, worklist and DICOMweb diagnostics Virtual destination: validate, don't send Docker or portable deployment FOR PATIENT PRIVACY No identifiers in tags or pixels Automatic annotation removal with OCR CT defacing (experimental) If masking fails, nothing is sent Email summaries of transfers and errors Monitoring: original vs de-identified values Diagnostic value of the image preserved

Open the diagram at full size

Karnak receives studies through a DICOM listener and forwards them to DICOM (C-STORE) or DICOMweb (STOW-RS) destinations.

Get started

New to Karnak? How Karnak works shows a typical deployment with a research repository outside the institution and the pipeline each study goes through.

  1. Install Karnak with Docker Compose on a server, or download the portable distribution to run it on a single machine without installation.
  2. Create a forward node with its sources and destinations.
  3. Create a project and assign it a de-identification profile.

Key Features

  • DICOM Gateway: Acts as a proxy between DICOM modalities/workstations and PACS/Archives.
  • Flexible Connectivity: Supports standard DICOM protocols (C-STORE) and DICOMweb (STOW-RS).
  • De-identification: Based on the DICOM standard, with configurable actions in YAML profiles.
  • Clean pixels: Remove burned-in annotations with hand-defined masks or automatically through OCR service.
  • Pseudonymization: Apply pseudonyms from DICOM fields, by importing CSV, or with external web services.
  • Tag Morphing: Normalizes DICOM attributes to ensure consistency across your workflow.
  • Conditional Processing: Apply de-identification and tag morphing rules based on customizable conditions.
  • Conformance Reports: Validate each study against the DICOM standard and email a conformance report, with optional report-only (virtual) destinations.
  • Monitoring: Follow transfers in a destination, study and series tree, compare original and de-identified values, and export the activity as CSV.
  • Notification System: Email notifications for successful transfers and errors.
  • Diagnostic Tools: Test and probe DICOM nodes and DICOMweb endpoints β€” echo, capabilities, network and service checks.
  • Project Management: Organize de-identification rules and secrets by project.
  • Single Sign-On: Delegate the web portal login to an OpenID Connect provider such as Keycloak.
  • Web Interface: User-friendly web portal for configuration, profile building and monitoring.
  • Portable Distribution: Run Karnak as a portable application without installation.

Karnak de-identifies and forwards studies; it is not a viewer and not an archive. The companion DICOM viewer from the same project is Weasis, which reads the studies a Karnak destination receives β€” including from a DICOMweb archive.

Subsections of Home

How the Karnak DICOM Gateway Works

Karnak is a DICOM gateway: it receives studies through a DICOM listener, transforms them according to the profiles configured on each destination, and forwards the result over DICOM (C-STORE) or DICOMweb (STOW-RS). This page gives the big picture; the Installation, Profiles and User guide sections cover each part in detail.

Deployment workflow

A typical deployment feeds a research repository that lives outside the hospital or imaging center: another institution, a research network or the cloud.

Karnak deployment workflow Modalities, PACS and workstations inside the hospital network send studies to Karnak with DICOM C-STORE. Karnak de-identifies them and forwards them, optionally to an on-site archive with C-STORE, and across the network boundary to a research repository outside the institution with DICOMweb STOW-RS over HTTPS. Only de-identified studies leave the network. DEPLOYMENT WORKFLOW From the imaging network to a research repository HOSPITAL OR IMAGING CENTER NETWORK Trusted zone: DICOM traffic never leaves it Modalities CT, MR, US, XA, MG, … PACS / VNA Archive routing rules Workstations Manual or scripted send DICOM C-STORE C-STORE On-site archive Research PACS inside the network Karnak DICOM GATEWAY Web portal Configuration and monitoring RESEARCH REPOSITORY Another institution, a research network or the cloud Only de-identified studies get out Pseudonyms, shifted dates, new UIDs, masked pixels DICOMweb archive STOW-RS endpoint behind TLS OAuth 2 bearer token or Basic auth Research PACS or cloud archive Research team Viewers, analysis pipelines and AI training on a reproducible, shareable dataset DICOMweb STOW-RS over HTTPS NETWORK BOUNDARY Firewalls WHY DICOMweb FOR DESTINATIONS OUTSIDE THE NETWORK DICOM C-STORE is for trusted networks It has no authentication or encryption by default: never expose a DICOM port on the Internet. STOW-RS rides on HTTPS TLS encryption, OAuth 2 or Basic authentication, reverse proxies and firewalls apply as usual. Outbound only Karnak opens a single outbound HTTPS connection; no inbound port is required on the hospital side.

Open the diagram at full size

Inside the network, the DICOM protocol is used as usual. Modalities, the PACS or a workstation send studies to a Karnak forward node with C-STORE: a forward node is an AE Title served by the Karnak DICOM listener, and it can restrict the callers to a list of sources identified by AE Title and hostname.

Across the network boundary, Karnak sends the de-identified studies to the repository with a STOW destination, that is DICOMweb STOW-RS over HTTPS. This is the recommended protocol for any destination outside the trusted network:

  • DICOM C-STORE is made for trusted networks. It has no authentication or encryption by default, so a DICOM port must never be exposed on the Internet.
  • STOW-RS rides on HTTPS. The transfer is encrypted with TLS and authenticated with an OAuth 2 bearer token or Basic auth; reverse proxies and firewalls apply as usual.
  • Outbound only. Karnak opens a single outbound HTTPS connection to the repository; no inbound port is required on the hospital side.

An on-site archive, such as a research PACS in the same network, can still be reached with a plain DICOM destination, and both kinds of destinations can be combined on the same forward node. Only what the destination’s profile lets through leaves the network: pseudonyms instead of identities, shifted dates, re-mapped UIDs and cleaned pixels. Kheops is an example of a DICOMweb repository that receives shared albums this way.

Processing pipeline

The forward node addressed by the called AE Title checks the calling source, then routes each accepted instance to one or more destinations, where it goes through the pipeline below once per destination. Every step is configured on the destination.

%%{init: {"theme": "base", "themeVariables": {"fontSize": "15px", "textColor": "#0b1f3f", "primaryTextColor": "#0b1f3f", "lineColor": "#334155", "edgeLabelBackground": "#ffffff", "labelTextColor": "#0b1f3f", "clusterBkg": "#f4f8fd", "clusterBorder": "#2f6fb5", "titleColor": "#0b1f3f"}}}%%
flowchart TB
    src["Modality, PACS<br>or workstation"]:::ext
    src -->|"<b>DICOM C-STORE</b>"| fwd

    subgraph karnak["Karnak gateway"]
        direction TB
        fwd["Forward node<br><i>DICOM listener, AE Title</i><br>routes to one or more destinations"]:::karnak
        auth{"Source<br>authorized?"}:::karnak
        reject["Instance refused<br>not authorized"]:::stop
        fwd --> auth
        auth -->|"<b>no</b>"| reject
        subgraph dest["For each destination of the forward node"]
            direction TB
            filter["Conditions and<br>SOP class filter"]:::step
            skip["Instance<br>skipped"]:::stop
            profile["Profile<br>de-identification<br>or tag morphing"]:::step
            pixels["Pixel cleaning<br>masks, OCR, defacing"]:::step
            ts["Transfer syntax<br>adaptation"]:::step
            project[("Project secret<br>Pseudonyms from cache,<br>DICOM tag or API")]:::data
            filter -.->|"<b>no match</b>"| skip
            filter --> profile --> pixels --> ts
            project -.-> profile
        end
        auth ==>|"<b>yes</b>"| filter
    end

    ts -->|"<b>DICOM C-STORE</b>"| pacs["On-site PACS<br>or archive"]:::dicom
    ts -->|"<b>STOW-RS over HTTPS</b>"| web["DICOMweb repository<br>outside the network"]:::web
    ts -.-> report["Monitoring, notifications,<br>conformance report"]:::ext

    classDef ext fill:#334155,stroke:#1e293b,stroke-width:1.5px,color:#ffffff,font-weight:bold;
    classDef karnak fill:#0b4a8f,stroke:#062f5e,stroke-width:1.5px,color:#ffffff,font-weight:bold;
    classDef step fill:#2f6fb5,stroke:#1d4f8f,stroke-width:1.5px,color:#ffffff,font-weight:bold;
    classDef data fill:#4c3fb5,stroke:#332a80,stroke-width:1.5px,color:#ffffff,font-weight:bold;
    classDef stop fill:#b91c1c,stroke:#7f1d1d,stroke-width:1.5px,color:#ffffff,font-weight:bold;
    classDef dicom fill:#1d4f8f,stroke:#0b1f3f,stroke-width:1.5px,color:#ffffff,font-weight:bold;
    classDef web fill:#0b7a6e,stroke:#064e46,stroke-width:1.5px,color:#ffffff,font-weight:bold;
    style karnak fill:#e3edf9,stroke:#1d4f8f,stroke-width:1.5px,color:#0b1f3f
    style dest fill:#ffe9b8,stroke:#d9932e,stroke-width:1.5px,color:#0b1f3f
    linkStyle 0,9 stroke:#1d4f8f,stroke-width:2.5px
    linkStyle 10 stroke:#0b7a6e,stroke-width:2.5px
    linkStyle 2,3 stroke:#b91c1c,stroke-width:2px
    linkStyle 11 stroke:#1e293b,stroke-width:2.5px
    linkStyle 7 stroke:#4c3fb5,stroke-width:2.5px
    linkStyle 1,8 stroke:#0b4a8f,stroke-width:3px
  1. Source check. If the forward node declares sources, only those AE Titles (and optionally hostnames) are accepted; any other caller gets the DICOM status Not authorized and nothing is forwarded.
  2. Filtering. A destination can restrict the forwarded SOP Classes and evaluate a condition on the DICOM attributes. Instances that do not match are skipped for that destination only; the other destinations of the forward node still receive them.
  3. Profile. The destination applies either a de-identification profile or a tag-morphing profile, never both. A de-identification profile chains profile elements: the DICOM basic confidentiality profile, tag actions, date shifting, UID re-mapping and the pseudonymization keyed by the project secret. The pseudonym itself comes from the Karnak cache (entered in the portal or imported from CSV), from a DICOM tag, or from an external API.
  4. Pixel cleaning. Burned-in annotations are removed with hand-defined masks or automatically through the OCR service, and head CT studies can be defaced. If masking fails, nothing is sent.
  5. Transfer syntax. The image is transcoded when the destination requires another transfer syntax.
  6. Send and report. The instance is sent with C-STORE or STOW-RS. The transfer is tracked in Monitoring, summarized in email notifications, and optionally validated in a DICOM conformance report. A virtual destination runs the whole pipeline and produces the report without sending anything.

Install Karnak with Docker Compose

Karnak is distributed as a container image on Docker Hub: nroduit/karnak (multi-arch, linux/amd64 and linux/arm64). It requires two third-party services:

  • PostgreSQL for the persistence of the Karnak configuration (sources, destinations, projects, profiles, monitoring)
  • Redis for the cache (pseudonym mappings, API call results)

The recommended way to run Karnak in production is Docker Compose. This page describes how to write the docker-compose.yml file and the associated configuration from scratch.

Info

Karnak is also available as a portable distribution that runs without Docker, with an embedded database and an in-memory cache. It is intended for a single machine and local use.

Prerequisites

  • Docker Engine 20.10 or later with the Compose plugin (docker compose v2). See the Docker installation guide.
  • The commands below target Linux. On Windows or macOS, use Docker Desktop and adapt the shell commands.

1. Create the project layout

Create a folder (for example /opt/karnak) with the following structure:

karnak/
β”œβ”€β”€ docker-compose.yml
β”œβ”€β”€ karnak.env
└── secrets/
    β”œβ”€β”€ karnak_login_password
    β”œβ”€β”€ karnak_postgres_password
    └── karnak_db_encryption_key

2. Generate the secrets

Karnak reads sensitive values from files through the environment variables ending with _FILE, which is compatible with Docker secrets. Three secrets are required:

Secret file Purpose
karnak_login_password Password of the web portal administrator (login defined by KARNAK_LOGIN_ADMIN, admin by default)
karnak_postgres_password Password of the PostgreSQL user, shared by the database container and Karnak
karnak_db_encryption_key Key used by PostgreSQL (pgcrypto) to encrypt sensitive columns such as the client secrets of the API authentication configurations
mkdir -p secrets
openssl rand -base64 32 | tr -d '=+/' > secrets/karnak_postgres_password
openssl rand -base64 48 | tr -d '=+/' > secrets/karnak_db_encryption_key
read -rsp "Web portal password: " pwd; echo; printf '%s' "$pwd" > secrets/karnak_login_password
chmod 600 secrets/*
Warning

Back up karnak_db_encryption_key together with the database. The key must stay identical for the whole life of the database: if it is lost or changed, the encrypted values stored in PostgreSQL can no longer be decrypted.

3. Write the docker-compose.yml file

services:
  karnak:
    image: nroduit/karnak:v2.0.0
    container_name: karnak
    restart: unless-stopped
    ports:
      # DICOM listener: host port 11112 -> container port 11119 (DICOM_LISTENER_PORT)
      - "11112:11119"
      # Web portal (KARNAK_WEB_PORT, 8080 by default in the container)
      - "8080:8080"
    env_file: karnak.env
    environment:
      # Third-party services (container names on the Compose network)
      DB_HOST: karnak-db
      DB_PORT: "5432"
      DB_NAME: karnak
      DB_USER: karnak
      DB_PASSWORD_FILE: /run/secrets/karnak_postgres_password
      DB_ENCRYPTION_KEY_FILE: /run/secrets/karnak_db_encryption_key
      REDIS_HOST: karnak-cache
      REDIS_PORT: "6379"
      # Web portal administrator
      KARNAK_LOGIN_ADMIN: admin
      KARNAK_LOGIN_PASSWORD_FILE: /run/secrets/karnak_login_password
    secrets:
      - karnak_login_password
      - karnak_postgres_password
      - karnak_db_encryption_key
    depends_on:
      karnak-db:
        condition: service_healthy
      karnak-cache:
        condition: service_healthy
    logging:
      driver: json-file
      options:
        max-size: "10m"
        max-file: "5"
    networks:
      - karnak-net

  karnak-db:
    image: postgres:17-alpine
    container_name: karnak-db
    restart: unless-stopped
    environment:
      POSTGRES_DB: karnak
      POSTGRES_USER: karnak
      POSTGRES_PASSWORD_FILE: /run/secrets/karnak_postgres_password
    volumes:
      - karnak-db-data:/var/lib/postgresql/data
    secrets:
      - karnak_postgres_password
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U karnak -d karnak"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - karnak-net

  karnak-cache:
    image: redis:7-alpine
    container_name: karnak-cache
    restart: unless-stopped
    healthcheck:
      test: ["CMD", "redis-cli", "ping"]
      interval: 10s
      timeout: 5s
      retries: 5
    networks:
      - karnak-net

secrets:
  karnak_login_password:
    file: secrets/karnak_login_password
  karnak_postgres_password:
    file: secrets/karnak_postgres_password
  karnak_db_encryption_key:
    file: secrets/karnak_db_encryption_key

volumes:
  karnak-db-data:

networks:
  karnak-net:

Notes:

  • Pin the Karnak image to a release tag (v2.0.0). The latest tag follows the most recent release and the master tag is the development build.
  • Only the ports of the karnak service are published. The database and the cache are reachable from Karnak only, through the internal Compose network.
  • DB_PORT is the port inside the Compose network (the default PostgreSQL port), not a port published on the host.
  • Karnak does not need a writable volume: the configuration is stored in PostgreSQL and the logs are written to the standard output (see Configuring Logs).

4. Write the karnak.env file

This file holds the gateway options. All variables are optional; the values below are the defaults. Uncomment and adapt what you need.

#  --------------------------------------------------------
#  DICOM listener
#  --------------------------------------------------------
DICOM_LISTENER_AET=KARNAK-GATEWAY
DICOM_LISTENER_PORT=11119
### Listen with DICOM TLS. When true, the TLS keystore and truststore below are required.
DICOM_LISTENER_TLS=false
#TLS_KEYSTORE_PATH=
#TLS_KEYSTORE_SECRET=
#TLS_TRUSTSTORE_PATH=
#TLS_TRUSTSTORE_SECRET=

#  --------------------------------------------------------
#  SMTP server (email notifications and conformance reports)
#  --------------------------------------------------------
#MAIL_SMTP_HOST=
#MAIL_SMTP_PORT=
#MAIL_SMTP_SENDER=
### Authentication type, "SSL" or "STARTTLS". Leave empty for an SMTP server without authentication.
#MAIL_SMTP_TYPE=
#MAIL_SMTP_USER=
#MAIL_SMTP_SECRET=

#  --------------------------------------------------------
#  Automatic pixel de-identification (external OCR service)
#  --------------------------------------------------------
#OCR_URL=http://localhost:8000

#  --------------------------------------------------------
#  Identity provider. IDP=oidc enables OpenID Connect, any other
#  value keeps the built-in administrator account.
#  --------------------------------------------------------
IDP=undefined
#OIDC_CLIENT_ID=
#OIDC_CLIENT_SECRET=
#OIDC_ISSUER_URI=

#  --------------------------------------------------------
#  Logging
#  --------------------------------------------------------
### Path of a custom Logback configuration file mounted in the container
#LOGBACK_CONFIGURATION_FILE=

### Additional JVM options, for example the heap size
#JAVA_OPTS=-Xmx4g

5. Start Karnak

Run the commands from the folder containing docker-compose.yml:

Action Command
Download or update the images docker compose pull
Start in the background docker compose up -d
Follow the logs docker compose logs -f
Stop docker compose down
Stop and delete the data (reset the database) docker compose down -v

On the first start, Karnak creates the database schema automatically. The database schema is also migrated automatically when you upgrade to a newer image.

Once started, with the configuration above:

  • Web portal: http://localhost:8080, login admin with the password stored in secrets/karnak_login_password
  • DICOM listener: AE Title KARNAK-GATEWAY, port 11112 on the Docker host

The next steps are described in the user guide: create a forward node with its sources and destinations, a project and a de-identification profile.

Environment variables

Web portal and database

Variable Default Description
KARNAK_WEB_PORT 8080 Port of the web portal inside the container (the portable package uses 8081)
KARNAK_LOGIN_ADMIN admin Login of the built-in administrator account
KARNAK_LOGIN_PASSWORD / KARNAK_LOGIN_PASSWORD_FILE undefined Password of the administrator account, as a value or as a file (Docker secret).
DB_HOST localhost Hostname of the PostgreSQL server
DB_PORT 5432 Port of the PostgreSQL server
DB_NAME / DB_NAME_FILE karnak Name of the database
DB_USER / DB_USER_FILE karnak Database user
DB_PASSWORD / DB_PASSWORD_FILE karnak Database password
DB_ENCRYPTION_KEY / DB_ENCRYPTION_KEY_FILE undefined Key used to encrypt sensitive columns in PostgreSQL. Required in production.
REDIS_HOST localhost Hostname of the Redis server
REDIS_PORT 6379 Port of the Redis server
JAVA_OPTS Additional options passed to the Java virtual machine

A variable and its _FILE counterpart are exclusive: setting both makes the container exit with an error.

DICOM listener

Variable Default Description
DICOM_LISTENER_AET KARNAK-GATEWAY AE Title of the DICOM listener (calling AE Title used by Karnak toward the destinations)
DICOM_LISTENER_PORT 11119 Listening port inside the container
DICOM_LISTENER_TLS false Accept only DICOM TLS associations
TLS_KEYSTORE_PATH / TLS_KEYSTORE_SECRET Keystore and its password, required when TLS is enabled
TLS_TRUSTSTORE_PATH / TLS_TRUSTSTORE_SECRET Truststore and its password, required when TLS is enabled

Email

Email is used by the destination notifications and by the conformance reports.

Variable Description
MAIL_SMTP_HOST SMTP server hostname
MAIL_SMTP_PORT SMTP server port
MAIL_SMTP_SENDER Sender address of the emails
MAIL_SMTP_TYPE SSL or STARTTLS to enable authentication. Leave empty for a server without authentication.
MAIL_SMTP_USER / MAIL_SMTP_SECRET Credentials used when MAIL_SMTP_TYPE is set

Identity provider

By default, Karnak uses the built-in administrator account. Set IDP=oidc to delegate the authentication to an OpenID Connect provider such as Keycloak, and configure it with OIDC_CLIENT_ID, OIDC_CLIENT_SECRET (or OIDC_CLIENT_SECRET_FILE) and OIDC_ISSUER_URI.

Karnak reads the user’s roles from the resource_access.karnak.roles claim of the access token, so the provider must be configured to put the roles of the karnak client in the access token (in Keycloak: client scope roles, with the Add to access token mapper enabled). Three roles are recognized; any other role is ignored and logged as a warning:

Role Access
admin Every page of the web portal
user The External pseudonym and Help pages only
investigator Accepted and mapped, but currently gives access to no page; reserved for future use

The Authentication Config page is unrelated to the portal login: it stores OAuth 2.0 credentials for the API calls made by profiles.

Other services

Variable Default Description
OCR_URL http://localhost:8000 Base URL of the de-identification image API used by the automatic mask generation and by the burned-in text check of the conformance report. Deploy this service separately (its repository provides a Docker Compose file) and point OCR_URL to it, for example http://image-ocr-identifier:8000 when it runs on the same Docker network.
LOGBACK_CONFIGURATION_FILE Path of a custom Logback configuration, see Configuring Logs

Run Karnak as a systemd service

To start Karnak automatically at boot, create a systemd unit that drives Docker Compose. The example below assumes the files are in /opt/karnak.

# /etc/systemd/system/karnak.service
[Unit]
Description=Karnak DICOM gateway (Docker Compose)
Requires=docker.service
After=docker.service network-online.target

[Service]
Type=oneshot
RemainAfterExit=yes
WorkingDirectory=/opt/karnak
ExecStart=/usr/bin/docker compose up -d
ExecStop=/usr/bin/docker compose down
TimeoutStartSec=0

[Install]
WantedBy=multi-user.target

Then enable and start it:

sudo systemctl daemon-reload
sudo systemctl enable --now karnak.service
sudo systemctl status karnak.service

Reverse proxy

The web portal can be served behind a reverse proxy (HTTPS termination). Karnak honors the X-Forwarded-* headers, so make sure the proxy forwards them (X-Forwarded-Proto, X-Forwarded-Host, X-Forwarded-For) and that WebSocket connections are allowed, as the user interface relies on server push.

Upgrade

  1. Back up the database volume (docker compose exec karnak-db pg_dump -U karnak karnak > karnak.sql) and the secrets/ folder.
  2. Change the image tag in docker-compose.yml to the new release.
  3. Run docker compose pull then docker compose up -d. The database schema is migrated automatically at startup.

Build the image yourself

To build the image from the source code instead of using Docker Hub, run from the root of the repository:

# Full build inside Docker (requires no local JDK)
docker build -t local/karnak:latest -f Dockerfile .

# Or build the application first, then package it
mvn clean install -P production
docker build -t local/karnak:latest -f src/main/docker/Dockerfile .

Then replace image: nroduit/karnak:v2.0.0 by image: local/karnak:latest in docker-compose.yml.

Other installation pages

  • Configuring Logs

    Inject your own Logback configuration to control log levels, format, rotation and where Karnak writes application and DICOM transfer logs.

Subsections of Installation

Configure Karnak Logging with Logback

Karnak offers the possibility of injecting your own log configuration to customize how logs are generated and stored.

The logging system used by Karnak is Logback. For detailed information about Logback configuration, see the official manual.

Available Variables

The following variables can be used in your custom Logback configuration file to capture specific de-identification context:

Variable Description
issuerOfPatientID Issuer of patient ID before de-identification
PatientID Patient ID before de-identification
SOPInstanceUID SOP Instance UID before de-identification
DeidentifySOPInstanceUID SOP Instance UID after de-identification
SeriesInstanceUID Series Instance UID before de-identification
DeidentifySeriesInstanceUID Series Instance UID after de-identification
TagMorphingSOPInstanceUID SOP Instance UID after tag morphing
TagMorphingSeriesInstanceUID Series Instance UID after tag morphing
ProjectName Project name used for de-identification or tag morphing
ProfileName Profile name used for de-identification or tag morphing
ProfileCodenames Concatenated list of profile items applied during de-identification or tag morphing

These variables are set (as MDC values, used with %X{name} in a pattern) while a profile is applied to an instance; the Deidentify* / TagMorphing* values, ProjectName, ProfileName and ProfileCodenames are available on the DEBUG event emitted with the CLINICAL marker once the profile has been applied. See the clinical logs example below.

Inject the Logback Configuration File

Using Docker

Set the environment variable LOGBACK_CONFIGURATION_FILE with the path to your Logback configuration file. This will override the default log configuration.

Example:

If you create a custom configuration file named my-logback.xml in the same directory as your docker-compose.yml:

  1. Create a volume to mount the file inside the Karnak container
  2. Define the LOGBACK_CONFIGURATION_FILE environment variable with the container path
services:
  karnak:
    container_name: karnak
    image: nroduit/karnak:latest
    volumes:
      - ./my-logback.xml:/logs/my-logback.xml
    environment:
      LOGBACK_CONFIGURATION_FILE: /logs/my-logback.xml

Using Java

If you run Karnak directly from the JAR file, add the following parameter at startup: -Dlogging.config=my-logback.xml

Default Logback Configuration

The default logback configuration file selects its appenders from the active Spring profile:

Standard installation (Docker or JAR)

  • Logs everything at the INFO level to the console (standard output) only; no log file is written, so no writable volume is needed and the Docker logs can be read with docker logs or collected by your log driver
  • Log levels can be adjusted with the standard Spring logging.level.* properties (e.g. the LOGGING_LEVEL_ORG_KARNAK=DEBUG environment variable)

Portable distribution

  • Logs the org.karnak packages at the INFO level to the console and to the rolling file logs/karnak.log in the extracted directory
  • The rotation is controlled by the KARNAK_LOGS_MAX_FILE_SIZE (default 50MB), KARNAK_LOGS_MIN_INDEX (default 1) and KARNAK_LOGS_MAX_INDEX (default 10) variables set in run.cfg

Clinical logs

Each de-identification or tag-morphing operation emits a DEBUG event carrying the CLINICAL marker and the variables above. The default configuration does not write these events to a dedicated file. To keep an audit trail, add an appender filtered on the marker to your custom configuration and enable the DEBUG level for the org.karnak package:

<configuration>
  <appender name="CLINICAL_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
    <file>logs/clinical.log</file>
    <rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
      <fileNamePattern>logs/clinical_%i.log</fileNamePattern>
      <minIndex>1</minIndex>
      <maxIndex>10</maxIndex>
    </rollingPolicy>
    <triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
      <maxFileSize>50MB</maxFileSize>
    </triggeringPolicy>
    <!-- Keep only the events carrying the CLINICAL marker -->
    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
      <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
        <marker>CLINICAL</marker>
      </evaluator>
      <onMismatch>DENY</onMismatch>
      <onMatch>NEUTRAL</onMatch>
    </filter>
    <encoder>
      <pattern>%d SOPInstanceUID_OLD=%X{SOPInstanceUID} SOPInstanceUID_NEW=%X{DeidentifySOPInstanceUID} SeriesInstanceUID_OLD=%X{SeriesInstanceUID} SeriesInstanceUID_NEW=%X{DeidentifySeriesInstanceUID} ProjectName=%X{ProjectName} ProfileName=%X{ProfileName} ProfileCodenames=%X{ProfileCodenames}%n</pattern>
    </encoder>
  </appender>

  <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
    <!-- Exclude the clinical events from the console -->
    <filter class="ch.qos.logback.core.filter.EvaluatorFilter">
      <evaluator class="ch.qos.logback.classic.boolex.OnMarkerEvaluator">
        <marker>CLINICAL</marker>
      </evaluator>
      <onMismatch>NEUTRAL</onMismatch>
      <onMatch>DENY</onMatch>
    </filter>
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>

  <logger name="org.karnak" level="DEBUG" />

  <root level="INFO">
    <appender-ref ref="STDOUT" />
    <appender-ref ref="CLINICAL_FILE" />
  </root>
</configuration>

With Docker, mount a writable volume for the logs folder (the image runs as a non-root user, so mount it at a path such as /tmp/logs or make the target directory writable).

Info

Clinical logs provide detailed tracking of the de-identification process, including which profiles were applied and how patient data was transformed. This is useful for auditing and compliance purposes.

DICOM De-identification Profiles

  • Profile Structure

    The YAML format of a de-identification profile: header fields, the profile elements, the order they are evaluated in, and the DICOM Basic Profile.

  • Actions on tags

    Select DICOM attributes by tag, group or pattern inside a profile and apply keep, remove, replace or add actions, including the default value used for each VR.

  • Actions on dates

    Apply date actions to DA, DT and TM attributes: shift by a fixed offset, shift per patient, or remove them, while keeping the studies of one patient consistent.

  • Expressions

    Compute a tag value with a Spring Expression Language expression: the context Karnak exposes, the helper functions available, and worked profile examples.

  • API Actions

    Call an external web service from a profile to replace tag values or shift dates: the request Karnak sends, the response it expects, and failure handling.

  • Image modifications

    Protect identity in pixel data: hand-defined masks applied per SOP class or image size, and automatic text detection through the OCR service.

  • Conditions

    Gate a profile element with an expression evaluated against each DICOM instance: the syntax, the attributes and operators available, and worked examples.

  • De-identification Process

    Technical reference for the de-identification pipeline: UID generation, date shifting, pseudonym sources, and how the DICOM confidentiality profile is applied.

Subsections of Profiles

Karnak Profile YAML Structure

The Profiles page allows you to manage de-identification profiles for your projects in the Karnak interface. This page explains the structure of these profiles and how to create or modify them using YAML files.

Overview

A profile defines how DICOM attributes should be modified during de-identification or tag morphing. It consists of one or more profile elements, each specifying:

  • Which DICOM attributes to target
  • What action to apply (e.g., remove, replace, keep)
  • Optional conditions for applying the action

Important behavior:

  • Profile elements are applied sequentially in the order they appear in the YAML file
  • Only the first applicable profile element modifies each DICOM attribute
  • Once modified, subsequent profile elements won’t affect that attribute
Info

Profiles must be YAML files that conform to the YAML specification and follow Karnak’s profile structure defined here.

Profile Metadata

Metadata fields help identify and manage your profiles. While optional, we recommend defining at least name and version for clarity.

Field Description Required
name Profile name displayed in the UI Optional (recommended)
version Profile version number Optional (recommended)
minimumKarnakVersion Minimum Karnak version required Optional
defaultIssuerOfPatientID Legacy field, accepted for compatibility but ignored. The default Issuer of Patient ID is configured on the destination Optional
profileElements List of profile elements to apply Required

Profile Element Structure

Each profile element defines a specific de-identification rule.

Required Fields

Field Description
name Descriptive name for this profile element
codename Type identifier (see available types in this documentation)

Optional Fields

Field Description When Required
condition Boolean expression to conditionally apply this element Optional
action Action type to perform (K, X, Z, U, D; see Actions on tags) For certain codenames
option Single configuration value For certain codenames
arguments Key-value pairs for advanced configuration For certain codenames
tags DICOM attributes this element should target For certain codenames
excludedTags DICOM attributes to skip (can be modified by later elements) Optional

DICOM Tag Formats

Tags can be specified in multiple formats:

Format Example Description
Parentheses (0010,0010) Standard DICOM notation
Comma-separated 0010,0010 Without parentheses
Concatenated 00100010 No separators
Pattern (0010,XXXX) All tags in group 0010
Wildcard (XXXX,XXXX) All DICOM attributes

A tag written in one of these formats matches the attribute wherever it appears in the instance: at the top level and inside the items of any sequence.

Tag Paths (Sequences)

To restrict a tag to a given location, write a dot-separated path: the last segment is the tag itself and the preceding segments are the sequences enclosing it. A leading dot anchors the path to the top-level dataset; without it, the named sequences may themselves be nested anywhere.

Path Matches
.(0040,0009) The tag at the top level only
(0040,0275).(0040,0009) The tag directly inside an item of (0040,0275), the sequence being at any depth
.(0040,0275).(0040,0009) Same, but the sequence must be at the top level
*.(0040,0009) The tag exactly one sequence below its enclosing dataset
**.(0040,0009) The tag at any depth except the top level
(0040,0275).* Any tag directly inside an item of (0040,0275)

Each segment accepts the X wildcard digits (e.g., (0040,0275).0040XXXX). When several entries of the same element match a tag, a path takes precedence over a plain tag, and the most specific path wins. Paths are accepted by tags and excludedTags of the elements working on user-defined tags, and by Add new tags (literal paths only).

Conditions

Add optional conditions to control when a profile element applies. The expression is evaluated for each matching tag.

For complete syntax and examples, see the Conditions page.

Basic DICOM Profile

The Basic DICOM Profile is the DICOM standard’s reference profile for removing Personally Identifying Information (PII) from DICOM files. The conditions to apply rules for a specific tag depend on its attribute type (Type 1, Type 2, etc.) based on the Information Object Definition (IOD). Karnak automatically determines the attribute type for each tag during de-identification.

Important

We strongly recommend including this profile as the foundation for de-identification.

Why Use It?

  • Removes all attributes containing identifiable patient information
  • Maintains DICOM conformance
  • Industry-standard approach to de-identification

How to Include

Reference it by codename in your profile:

- name: "DICOM basic profile"
  codename: "basic.dicom.profile"

Complete Example

Below is a minimal but complete profile that applies only the Basic DICOM Profile:

name: "Dicom Basic Profile"
version: "1.0"
minimumKarnakVersion: "0.9.2"
defaultIssuerOfPatientID:
profileElements:
  - name: "DICOM basic profile"
    codename: "basic.dicom.profile"
Info

This profile should be placed at the end of your profile elements to ensure all other rules are applied first. The profile editor automatically moves it to the last position, and it can appear only once in a profile (as can the Clean Pixel Data and Defacing elements).

Keep, Remove or Replace DICOM Tags

This page describes profile elements that apply actions to DICOM tags. These actions allow you to selectively keep, remove, or add tags during de-identification.

Actions on specific tags

This profile element applies an action to a tag or group of tags defined by the user.

Required Parameters

Parameter Description
name Description of the action applied
codename action.on.specific.tags
action Keep (K), Remove (X), Replace with an empty value (Z), Replace with a new UID (U) or Replace with a dummy value (D)
tags List of tags the action should be applied to

Optional Parameters

Parameter Description
condition Defines a condition to evaluate if this profile element should be applied to this DICOM instance
excludedTags List of tags that will be ignored by this action

Example

In this example, all tags starting with 0028 will be removed except (0028,1199) which will be kept.

- name: "Remove tags"
  codename: "action.on.specific.tags"
  action: "X"
  tags:
    - "(0028,xxxx)"
  excludedTags:
    - "(0028,1199)"

- name: "Keep tags 0028,1199"
  codename: "action.on.specific.tags"
  action: "K"
  tags:
    - "0028,1199"

Actions on private tags

This profile element applies an action to private tags or groups of private tags defined by the user. This action is only applied if the tag is private (where the group number is odd).

Required Parameters

Parameter Description
name Description of the action applied
codename action.on.privatetags
action Keep (K), Remove (X), Replace with an empty value (Z), Replace with a new UID (U) or Replace with a dummy value (D)

Optional Parameters

Parameter Description
condition Defines a condition to evaluate if this profile element should be applied to this DICOM instance
tags List of tags the action should be applied to. If not specified, the action is applied to all private tags
excludedTags List of tags that will be ignored by this action

Example

In this example, all tags starting with 0009 will be kept and all other private tags will be removed.

- name: "Keep private tags starting with 0009"
  codename: "action.on.privatetags"
  action: "K"
  tags:
    - "(0009,xxxx)"

- name: "Remove all private tags"
  codename: "action.on.privatetags"
  action: "X"

Replace UIDs

This profile element applies a UID action to a list of tags defined by the user. It is typically used to generate new, consistent UIDs for the UID attributes that the Basic DICOM profile does not cover, or to remove or empty them.

Required Parameters

Parameter Description
name Description of the action applied
codename replace.uid
action Replace with a new UID (U), Remove (X) or Replace with an empty value (Z). Any other action is refused when the profile is loaded.
tags List of tags (or tag paths) the action should be applied to

Optional Parameters

Parameter Description
condition Defines a condition to evaluate if this profile element should be applied to this DICOM instance
excludedTags List of tags that will be ignored by this action

Example

In this example, the Frame of Reference UID and every Referenced SOP Instance UID (at any depth, including inside sequences) are replaced by new UIDs derived from the project secret, so that the references between the de-identified instances remain consistent.

- name: "New UIDs for the references"
  codename: "replace.uid"
  action: "U"
  tags:
    - "(0020,0052)"
    - "(0008,1155)"

Add new tags

This profile element adds a tag if it is not already present in the instance. This action is ignored if the tag already exists.

Required Parameters

Parameter Description
name Description of the action applied
codename action.add.tag
arguments Contains:
β€’ value: Value to set for the tag
tags Must contain exactly one tag - the tag to add, or a tag path to add it inside a sequence

Optional Parameters

Parameter Description
condition Defines a condition to evaluate if this profile element should be applied to this DICOM instance

Behavior

When the tag is not present:

  • The tag will be added by this action
  • Further profile element actions that match this tag won’t be applied

When the tag already exists:

  • The add action is ignored
  • A subsequent profile element action can be applied to that tag

Adding a tag inside a sequence:

The tag can be written as a literal path naming every enclosing sequence, e.g. (0040,0275).(0040,0009) (wildcards such as *, ** or X are not allowed, and the path is always read from the top-level dataset). A missing or empty sequence is created with one item; when the sequence holds several items, each of them receives the attribute. Such an element is applied once all the other profile elements have been applied, so the added value is not modified by them.

Info

The tag must exist in the DICOM Standard, otherwise the profile validation will fail. The VR is retrieved from the Standard. Every enclosing tag of a path must be a sequence.

If this profile element is applied to a SOP class that doesn’t contain this tag (at this location), it won’t be added to prevent corrupting the DICOM instance. A warning will be generated in the logs.

Use Case

This feature is especially useful when applying masks to non-compliant SOPs by using the Burned In Annotation attribute. See the Cleaning Data Pixel Exceptions page for a complete example.

Example

In this example, we add the optional tag Recognizable Visual Features (0028,0302).

- name: "Add Recognizable Visual Features tag"
  codename: "action.add.tag"
  arguments:
    value: "YES"
  tags:
    - "(0028,0302)"

Add new private tags

This profile element adds a private tag if it is not already present in the instance. This action is ignored if the tag already exists or if there is a collision with a different Private Creator ID.

Required Parameters

Parameter Description
name Description of the action applied
codename action.add.private.tag
arguments Contains:
β€’ value: Value to set for the tag
β€’ vr: Value Representation of the tag
β€’ privateCreator: (Optional) Value of the PrivateCreatorID
tags Must contain exactly one tag - the tag to add

Optional Parameters

Parameter Description
condition Defines a condition to evaluate if this profile element should be applied to this DICOM instance

PrivateCreator Management

The privateCreator argument is optional but recommended for consistency.

When the PrivateCreatorID tag does not exist:

  • The privateCreator argument must be specified
  • The PrivateCreatorID tag will be created automatically

When the PrivateCreatorID tag exists:

  • If privateCreator is not specified: The tag will be added as part of the existing PrivateCreatorID
  • If privateCreator is specified: The existing value is matched against the argument
    • If they match: The new tag is added
    • If they don’t match: A collision occurs and the tag is not added (a warning is logged)
Info

A private tag can only be added to the top-level dataset: tag paths are not supported by this element.

For details about private tag management and element numbers, see the DICOM Standard Part 5 Section 7.8.

Example

In this example, we add a private tag containing the value “sample-project” linked to the PrivateCreatorID “KARNAK-PRIVATE”.

- name: "Add Private Tag"
  codename: "action.add.private.tag"
  arguments:
    value: "sample-project"
    vr: "LO"
    privateCreator: "KARNAK-PRIVATE"
  tags:
    - "(0057,1000)"

What happens:

  • If the PrivateCreatorID tag (0057,0010) doesn’t exist, it’s created with the value “KARNAK-PRIVATE” and the tag (0057,1000) is added
  • If (0057,0010) exists with the value “KARNAK-PRIVATE”, the tag (0057,1000) is added
  • If (0057,0010) exists with a different value, the tag (0057,1000) is not added to prevent linking it to the wrong PrivateCreatorID

Complete Profile Examples

Example 1: Basic De-identification Profile

This profile applies the following operations in order:

  1. Remove tags matching (0008,00XX) and (0010,00XX) except (0008,0008) and (0008,0013)
  2. Keep the previously excluded tags specifically (so they won’t be removed by subsequent profile elements)
  3. Remove all private tags
  4. Apply the Basic DICOM Profile to all remaining tags
name: "De-identification profile"
version: "1.0"
minimumKarnakVersion: "0.9.2"
defaultIssuerOfPatientID:
profileElements:
  - name: "Remove tags"
    codename: "action.on.specific.tags"
    action: "X"
    tags:
      - "(0008,00XX)"
      - "0010,00XX"
    excludedTags:
      - "0008,0008"
      - "0008,0013"

  - name: "Keep tags"
    codename: "action.on.specific.tags"
    action: "K"
    tags:
      - "0008,0008"
      - "0008,0013"

  - name: "Remove all private tags"
    codename: "action.on.privatetags"
    action: "X"

  - name: "DICOM basic profile"
    codename: "basic.dicom.profile"

Example 2: Conditional Profile with Private Tags

This profile demonstrates conditional actions and preserving specific private tags:

  1. Keep Study Description tag only if it contains ‘R2D2’
  2. Keep the Philips PET private group
  3. Apply the Basic DICOM Profile
Info

The tag patterns (7053,xx00) and (7053,xx09) are defined in Philips PET Private Group by DICOM.

name: "Profile Example"
version: "1.0"
minimumKarnakVersion: "0.9.7"
profileElements:
  - name: "Keep StudyDescription tag according to a condition"
    codename: "action.on.specific.tags"
    condition: "tagValueContains(#Tag.StudyDescription, 'R2D2')"
    action: "K"
    tags:
      - "(0008,1030)"

  - name: "Keep Philips PET Private Group"
    codename: "action.on.privatetags"
    action: "K"
    tags:
      - "(7053,xx00)"
      - "(7053,xx09)"

  - name: "DICOM basic profile"
    codename: "basic.dicom.profile"

Shift or Redact DICOM Dates and Times

This profile element applies a specific action to tags containing dates. These actions can only be applied to the following Value Representations: Age String (AS), Date (DA), Date Time (DT), and Time (TM).

Parameters

Required parameters

  • name: Description of the action applied
  • codename: Must be set to action.on.dates
  • option: The action to be applied (see options below)
  • arguments: Additional parameters depending on the chosen option

Optional parameters

  • condition: Defines a condition to evaluate whether this profile element should be applied to the DICOM instance
  • tags: List of tags the action should be applied to. If not specified, the action applies to all tags with AS, DA, DT, or TM Value Representation
  • excludedTags: List of tags that will be ignored by this action

Available Options

The option parameter can have one of the following values:

  • shift - Apply a fixed time shift
  • shift_range - Apply a random time shift within specified ranges
  • shift_by_tag - Apply a shift based on values from another DICOM tag
  • shift_from_api - Apply a shift based on values from an external API
  • date_format - Remove date precision (day and/or month)

Shift Option

The shift option applies a fixed shift to dates using the following required arguments:

  • seconds: Integer representing the number of seconds for the shift operation
  • days: Integer representing the number of days for the shift operation

Behavior by Value Representation:

  • Age String (AS): The days are added to the existing value (converted to the unit of the age: years, months, weeks or days); seconds are ignored
  • Date (DA): The days are subtracted from the existing value; seconds are ignored
  • Time (TM): The seconds are subtracted from the existing value; days are ignored
  • Date Time (DT): Days and seconds are subtracted from the existing value

Example

This example shifts all tags starting with 0010 that have AS, DA, DT, or TM Value Representation by 30 seconds and 10 days:

- name: "Shift Date"
  codename: "action.on.dates"
  arguments:
    seconds: 30
    days: 10
  option: "shift"
  tags:
    - "0010,XXXX"

Shift Range Option

The shift_range option applies a random shift to dates within user-defined ranges.

Arguments

  • max_seconds (required): Upper bound for the number of seconds to shift
  • max_days (required): Upper bound for the number of days to shift
  • min_seconds (optional): Lower bound for the number of seconds to shift (defaults to 0)
  • min_days (optional): Lower bound for the number of days to shift (defaults to 0)

The random operation is deterministic and reproducible based on the patient and project.

Example

This example shifts all tags starting with 0008,002 that have AS, DA, DT, or TM Value Representation randomly within a range of 0 to 60 seconds and 50 to 100 days:

  - name: "Shift Range Date"
    codename: "action.on.dates"
    arguments:
      max_seconds: 60
      min_days: 50
      max_days: 100
    option: "shift_range"
    tags:
      - "0008,002X"

Date Format Option

The date_format option removes date precision by deleting specific date components.

This action can only be applied to Date (DA) and Date Time (DT) Value Representations.

Arguments

  • remove: Specifies which date components to remove
    • day: Removes the day information, replacing it with 01
    • month_day: Removes both month and day information, replacing them with 01 for each

Example: Remove Day

This example removes the day from all tags starting with 0008,003 that have DA or DT Value Representation:

  - name: "Date format"
    codename: "action.on.dates"
    arguments:
      remove: "day"
    option: "date_format"
    tags:
      - "0008,003X"

For example, if the value contained in the tag is 20230512, the output value will be 20230501.

Shift By Tag Option

The shift_by_tag option applies a shift based on values contained in other DICOM tags.

Arguments

Both arguments are optional; an omitted argument applies no shift on its unit:

  • seconds_tag: Tag containing the number of seconds for the shift operation
  • days_tag: Tag containing the number of days for the shift operation

The referenced tags are read from the original values of the instance (before de-identification), from the dataset being visited outwards, so a date nested in a sequence can use shift values stored at the study level.

Warning

If a referenced tag is missing or does not contain an integer, the date is not shifted by this element: it is left to the following profile elements (typically removed by the Basic DICOM Profile). A warning is logged.

Example

This example shifts all tags starting with 0010 that have AS, DA, DT, or TM Value Representation by the number of days stored in the private tag (0015,0011):

- name: "Shift Date By Tag"
  codename: "action.on.dates"
  arguments:
    days_tag: "(0015,0011)"
  option: "shift_by_tag"
  tags:
    - "0010,XXXX"

Shift From API Option

The shift_from_api option applies a shift based on values from an external API call.

Arguments

  • url (required): URL of the API to query. Can contain runtime parameters (see URL and Body Arguments).
  • daysPath (required): JSON path to the number of days for the shift operation, using JSON Pointer syntax. Empty string means the entire response value will be used.
  • secondsPath (optional): JSON path to the number of seconds for the shift operation, using JSON Pointer syntax. Empty string means the entire response value will be used.
  • method (optional): HTTP method: GET or POST. Defaults to GET.
  • body (optional): Request body for POST requests in JSON format. Can contain runtime parameters (see URL and Body Arguments).
  • authConfig (optional): Identifier of an existing Authentication Configuration for authenticating the call. No authentication used if not specified.

The runtime parameters of url and body are evaluated against the original values of the instance (before de-identification). If the API cannot be reached, returns an error, or the response holds no integer at daysPath (or secondsPath), the transfer of the instance is aborted.

Example

This example shifts all tags starting with 0010 that have AS, DA, DT, or TM Value Representation by the number of days and seconds contained in the response of the API call:

- name: "Shift Date From API"
  codename: "action.on.dates"
  option: "shift_from_api"
  arguments:
    daysPath: "/shift/days"
    secondsPath: "/shift/seconds"
    url: "http://example.com/{{getString(#Tag.PatientID)}}/shift"
  tags:
    - "0010,XXXX"

Complete Profile Example

This example demonstrates a complete de-identification profile that applies multiple date actions in sequence:

  1. Shift Range: Randomly shift tags matching (0008,003X) and (0008,0012) (except (0008,0030) and (0008,0032)) by 0 to 60 seconds and 10 to 50 days
  2. Date Format: Remove month and day from tags (0008,0023) and (0008,0021)
  3. Fixed Shift: Shift tags matching (0010,XXXX) (except (0010,0010)) by 30 seconds and 10 days
  4. Basic Profile: Apply the Basic DICOM Profile to all remaining tags
name: "De-identification profile"
version: "1.0"
minimumKarnakVersion: "0.9.2"
defaultIssuerOfPatientID:
profileElements:
  - name: "Shift Range Date with arguments"
    codename: "action.on.dates"
    arguments:
      max_seconds: 60
      min_days: 10
      max_days: 50
    option: "shift_range"
    tags:
      - "0008,0012"
      - "0008,003X"
    excludedTags:
      - "0008,0030"
      - "0008,0032"
      
  - name: "Date Format"
    codename: "action.on.dates"
    arguments:
      remove: "month_day"
    option: "date_format"
    tags:
      - "0008,0023"
      - "0008,0021"

  - name: "Shift Date with arguments"
    codename: "action.on.dates"
    arguments:
      seconds: 30
      days: 10
    option: "shift"
    tags:
      - "0010,XXXX"
    excludedTags:
      - "0010,1010"

  - name: "DICOM basic profile"
    codename: "basic.dicom.profile"

SpEL Expressions for DICOM Tag Values

Actions on specific tags

This profile element applies an expression to a tag or a group of tags defined by the user. An expression is based on Spring Expression Language (SpEL) and returns a value or an action according to a certain condition.

Parameters

This profile element requires the following parameters:

Parameter Required Description
name Yes Description of the action applied
codename Yes Must be expression.on.tags
arguments Yes Definition of the expression with the expr argument
tags Yes List of tags the action should be applied to
condition No Optional condition to evaluate if this profile element should be applied to this DICOM instance
excludedTags No List of tags that will be ignored by this action

Expression Context

The expression is contained in the expr argument. When executed, it can either:

  • Return an action to be executed
  • Return a null value (does nothing and moves to the next profile element)

Available Variables

The following variables are available in the expression context, containing information about the current attribute:

Variable Description Example
tag Current attribute tag (0010,0010)
vr Current attribute VR PN
stringValue Current element value 'John^Doe'

Constants

Constants improve readability and reduce errors:

Constant Description Example
#Tag Retrieve any tag integer value from the DICOM standard #Tag.PatientBirthDate returns (0010,0030)
#VR Retrieve any VR value from the DICOM standard #VR.LO returns the Long String type

Utility Functions

The following utility functions help work with tags:

Function Description Return Value
getString(int tag) Returns the value of the given tag, looked up from the dataset being visited outwards (from an attribute nested in a sequence, the tags of the enclosing datasets are visible) String value or null if tag is not present
tagIsPresent(int tag) Checks if the tag exists anywhere in the DICOM instance, at any nesting level true if present, false otherwise

The values read by the expression (stringValue, getString) are the original values of the instance, before any profile element modified it.

Available Actions

Actions are defined as functions and set the operation to perform on the current tag:

Action Description
ReplaceNull() Sets the current tag value to empty
Replace(String dummyValue) Replaces the current tag value with the specified value
Remove() Removes the tag from the DICOM instance
Keep() Keeps the tag unchanged
UID() Replaces the current tag value with a newly generated UID and sets the tag’s VR to UI
ComputePatientAge() Replaces the current tag value with a computed patient’s age at the time of the exam
ExcludeInstance() Interrupts the transfer of this instance (appears as Excluded in monitoring)

Examples

Conditional replacement based on tag value and type

Replace the Patient Name with the Institution Name if the current value is ‘John’, otherwise keep it unchanged:

  - name: "Expression"
    codename: "expression.on.tags"
    arguments:
      expr: "stringValue == 'John' and tag == #Tag.PatientName? Replace(getString(#Tag.InstitutionName)) : Keep()"
    tags: 
      - "(xxxx,xxxx)"
Info

This example applies to all attributes in the DICOM instance. For each tag, it either applies Keep or Replace, which prevents further profile elements from modifying those tags.

Remove tags with undefined values

Remove tags with ‘UNDEFINED’ value, otherwise keep them:

  - name: "Expression"
    codename: "expression.on.tags"
    arguments:
      expr: "stringValue == 'UNDEFINED'? Keep() : Remove()"
    tags: 
      - "(0010,0010)" #PatientName
      - "(0010,0212)" #StrainDescription

Concatenate multiple tag values

Replace Study Description with a concatenation of Institution Name and Station Name:

- name: "Expression"
  codename: "expression.on.tags"
  arguments:
    expr: "Replace(getString(#Tag.InstitutionName) + '-' + getString(#Tag.StationName))"
  tags: 
    - "(0008,1030)" #StudyDescription

Compute patient age

Calculate and set the patient’s age at the time of the exam:

- name: "Expression"
  codename: "expression.on.tags"
  arguments:
    expr: "ComputePatientAge()"
  tags: 
    - "(0010,1010)" #PatientAge

Exclude instances conditionally

Exclude all instances that contain a name in the image (where Specimen Label In Image is set to YES):

  - name: "Expression"
    codename: "expression.on.tags"
    arguments:
      expr: "getString(#Tag.SpecimenLabelInImage) == 'YES' ? ExcludeInstance() : null"
    tags: 
      - "(xxxx,xxxx)"

Replace DICOM Tag Values from an External API

Actions with API calls

This profile element uses a call to an API to replace the value of a tag or a group of tags defined by the user.

Another variant of this action regarding shifting dates using values retrieved from an external API is described in Shift Dates from API.

Parameters

Parameter Type Description
name Required Description of the action applied
codename Required Must be set to action.replace.api
arguments Required Definition of the API call configuration (see Arguments below)
tags Required List of tags the action should be applied to
condition Optional Condition to evaluate if this profile element should be applied to this DICOM instance
excludedTags Optional List of tags that will be ignored by this action

Arguments

Argument Type Description
url Required URL of the API to query. Can contain runtime parameters (see URL and Body Arguments).
responsePath Required JSON path of the value used as replacement, using JSON Pointer syntax. Empty string means the entire response value will be used.
method Optional HTTP method: GET or POST. Defaults to GET.
body Optional Request body for POST requests in JSON format (mandatory when method is POST). Can contain runtime parameters (see URL and Body Arguments).
authConfig Optional Identifier of an existing Authentication Configuration for authenticating the call. No authentication used if not specified.
defaultValue Optional Fallback value used when an error occurs during the API call (e.g., Unauthorized, value not found, authConfig not found). If not specified, the transfer will be aborted with an error displayed in monitoring. When specified, the default value is used and transfer continues.

URL and Body Arguments

The URL and body may require values directly linked to the instance being transferred, such as the Patient ID.

A specific syntax allows this runtime injection. Parts evaluated at runtime must be enclosed in double brackets. The syntax uses Spring Expression Language (SpEL), with functions and constants defined similarly to Expressions.

URL examples:

http://example.com/{{getString(#Tag.PatientID)}}/endpoint
http://example.com/{{getString(#Tag.ClinicalTrialSponsorName)}}/patient/{{getString(#Tag.PatientID)}}/endpoint

{ 
  "patientId": "{{getString(#Tag.PatientID)}}", 
  "project": "{{getString(#Tag.ClinicalTrialSponsorName)}}" 
}

In the body, hence the JSON format, the expression must be enclosed in double quotes if the expected value is a String.

The expressions are evaluated against the original values of the instance, before any profile element modified it (for example getString(#Tag.PatientID) returns the Patient ID received by Karnak, not the pseudonymized one). An expression that evaluates to null is replaced by an empty string.

Response Caching

The responses of the API are cached by URL, body and authentication configuration, so that the same call is not repeated for every instance of the same patient. The cache lifetime is defined by the cache-api.ttl property of application.yml (15 minutes by default).

Examples

Example 1: Replace Patient ID with API response

The profile element below replaces the value of the Patient ID tag with the value retrieved from the url, it extracts the String value from the response at the specified path. Since it is not specified, the HTTP method is GET.

The authConfig argument contains the identifier of the Authentication Configuration used to authenticate the API call.

If an error occurs during the call, this transfer will be aborted. If the defaultValue was specified, it would have been used as the replacement value and the transfer would not have been blocked.

  - name: "Replace Patient ID from API call"
    codename: "action.replace.api"
    arguments:
      url: "http://example.com/{{getString(#Tag.PatientID)}}/endpoint"
      responsePath: "/_embedded/0/value"
      authConfig: "endpoint-auth-config"
    tags:
      - "(0010,0020)"

Example 2: POST request with body and default value

  - name: "Replace Patient Name with fallback"
    codename: "action.replace.api"
    arguments:
      url: "http://example.com/patients"
      method: "POST"
      body: '{"patientId": "{{getString(#Tag.PatientID)}}"}'
      responsePath: "/name"
      authConfig: "endpoint-auth-config"
      defaultValue: "ANONYMOUS"
    tags:
      - "(0010,0010)"

Remove Burned-in Annotations from DICOM Images

This page describes the profile elements available in Karnak for modifying DICOM image data to remove identifying information.

Overview

Karnak provides two main approaches to protect patient identity in DICOM images:

  1. Defacing: Automated removal of facial features from CT images
  2. Pixel Data Cleaning: User-defined masks to remove burned-in annotations and identifying information

Both methods ensure that visual identifying information is removed from images while preserving the diagnostic value of the data.


Defacing

Defacing automatically removes facial features from CT images to protect patient identity.

Supported Images

This profile can only be applied to images with Axial orientation in the following SOP Classes:

  • 1.2.840.10008.5.1.4.1.1.2 - CT Image Storage
  • 1.2.840.10008.5.1.4.1.1.2.1 - Enhanced CT Image Storage
  • 1.2.840.10008.5.1.4.1.1.2.2 - Legacy Converted Enhanced CT Image Storage
Info

Images with non-axial orientation will be skipped. Currently, the CT images that do not represent the head are not automatically excluded. It is recommended to use conditions to restrict defacing to head CT images only.

Configuration

This profile element requires the following parameters:

Parameter Description Required
name Description of the action applied Yes
codename Must be clean.recognizable.visual.features Yes
condition Condition to evaluate if this profile element should be applied No

Pixel Data Cleaning

Pixel data cleaning applies masks to DICOM images to remove identifying information burned into the pixels, such as patient or institution information. Masks can be defined manually per station name (see Masks Definition) or generated automatically by an external OCR service (see Automatic Pixel Data De-identification).

Condition for Automatic Application

This profile is automatically applied to the following SOP Classes:

  • 1.2.840.10008.5.1.4.1.1.6.1 - Ultrasound Image Storage
  • 1.2.840.10008.5.1.4.1.1.6.2 - Enhanced US Volume Storage
  • 1.2.840.10008.5.1.4.1.1.7 - Secondary Capture Image Storage
  • 1.2.840.10008.5.1.4.1.1.7.1 - Multiframe Single Bit Secondary Capture Image Storage
  • 1.2.840.10008.5.1.4.1.1.7.2 - Multiframe Grayscale Byte Secondary Capture Image Storage
  • 1.2.840.10008.5.1.4.1.1.7.3 - Multiframe Grayscale Word Secondary Capture Image Storage
  • 1.2.840.10008.5.1.4.1.1.7.4 - Multiframe True Color Secondary Capture Image Storage
  • 1.2.840.10008.5.1.4.1.1.3.1 - Ultrasound Multiframe Image Storage
  • 1.2.840.10008.5.1.4.1.1.77.1.1 - VL Endoscopic Image Storage

Or if the tag Burned In Annotation (0028,0301) is set to "YES"

Configuration

This profile element requires the following parameters:

Parameter Description Required
name Description of the action applied Yes
codename Must be clean.pixel.data Yes
condition Condition to evaluate if this profile element should be applied No
arguments automaticMasksGeneration: set to "true" to generate the masks with the external OCR service instead of the manual masks (see Automatic Pixel Data De-identification) No

Using Conditions

The condition parameter allows you to apply cleaning selectively. For example, to exclude images from a specific machine that does not add burned-in annotations, you can use the following configuration:

profileElements:
  - name: "Clean pixel data"
    codename: "clean.pixel.data"
    condition: "!tagValueContains(#Tag.StationName,'ICT256')"

Masks Definition

Masks define rectangular regions to be filled with a solid color, removing any identifying information in those areas.

Mask Parameters

Parameter Description Required
stationName Station name to match against DICOM tag (0008,1010). Use * to match any station Yes
color Fill color in hexadecimal format (e.g., ffff00 for yellow) Yes
rectangles List of rectangles defining the areas to mask Yes

Optional Parameters

Parameter Description
imageWidth Apply mask only to images with this width in pixels (matches Columns tag 0028,0011)
imageHeight Apply mask only to images with this height in pixels (matches Rows tag 0028,0010)
Info

When specifying image dimensions, both imageWidth and imageHeight must be set. Defining only one parameter is not supported.

Rectangle Definition

Each rectangle is defined by four values:

Parameter Description
x X coordinate of the upper-left corner
y Y coordinate of the upper-left corner
width Width of the rectangle in pixels
height Height of the rectangle in pixels

The coordinate system starts at (0, 0) in the upper-left corner of the image.

The diagram below illustrates a rectangle with parameters (25, 75, 150, 50):

Rectangle definition example Rectangle definition example

Mask Selection Process

When processing a DICOM instance, Karnak selects the appropriate mask using the following algorithm:

  1. Extract image attributes: Retrieve the Rows (0028,0010), Columns (0028,0011), and Station Name (0008,1010) values from the instance
  2. Exact match: Search for a mask matching all three values (width, height, and station name)
  3. Station match: If no exact match is found, ignore image dimensions and match only on station name
  4. Default mask: If no station match is found, use the mask with stationName: "*"

Example Configuration

masks:
  - stationName: "*"
    color: "ffff00"
    rectangles:
      - "25 75 150 50"
  - stationName: "R2D2"
    color: "00ff00"
    rectangles:
      - "25 25 150 50"
      - "350 15 150 50"
  - stationName: "R2D2"
    imageWidth: 1024
    imageHeight: 1024
    color: "00ffff"
    rectangles:
      - "50 25 100 100"

Complete Example: Equipment-Specific Cleaning

This example demonstrates a profile that handles specific pixel data cleaning for equipment that burns patient information into the images and does not match the conditions for automatic application.

This profile below performs the following actions for images from a machine with Station Name containing “ICT256”:

  1. Ensures the Burned In Annotation tag is present and set to “YES”
  2. Applies pixel data cleaning with a station-specific mask
  3. Applies the basic DICOM de-identification profile
name: "Clean pixel data"
version: "1.0"
minimumKarnakVersion: "0.9.2"
defaultIssuerOfPatientID:
profileElements:
  - name: "Add tag BurnedInAnnotation if does not exist"
    codename: "action.add.tag"
    condition: "tagValueContains(#Tag.StationName, 'ICT256') && !tagIsPresent(#Tag.BurnedInAnnotation)"
    arguments:
      value: "YES"
    tags:
      - "(0028,0301)"

  - name: "Set BurnedInAnnotation to YES"
    codename: "expression.on.tags"
    condition: "tagValueContains(#Tag.StationName, 'ICT256')"
    arguments:
      expr: "Replace('YES')"
    tags:
      - "(0028,0301)"

  - name: "Clean pixel data"
    codename: "clean.pixel.data"

  - name: "DICOM basic profile"
    codename: "basic.dicom.profile"

masks:
  - stationName: "*"
    color: "ffff00"
    rectangles:
      - "25 75 150 50"
  - stationName: "ICT256"
    color: "00ff00"
    rectangles:
      - "25 25 150 50"
      - "350 15 150 50"

Automatic Pixel Data De-identification

Instead of defining mask rectangles by hand for each station name, Karnak can automatically detect and mask sensitive patient data that are burned into the DICOM pixel data (text overlays such as patient name, accession number, or dates). When this option is enabled, Karnak sends each eligible image to an external de-identification image API that runs OCR, detects sensitive text, and returns the mask areas to apply.

Automatic mask generation is an option of the Clean Pixel Data profile element.

How It Works

  1. A destination uses a de-identification profile that contains a Clean Pixel Data element with automatic mask generation enabled.
  2. For every eligible instance (the SOP Classes eligible for pixel data cleaning, or any image flagged with Burned In Annotation (0028,0301)), Karnak extracts the pixel data and a set of sensitive tag values and sends them to the external API.
  3. The API returns one or more mask areas (rectangles, each with a color). Karnak draws them on the image before forwarding it to the destination.
  4. If the API detects no sensitive data, no mask is applied and the image is forwarded unchanged. If the call fails, the instance is not forwarded (see Fail-closed Behavior).

The example below shows an ultrasound frame before and after automatic pixel de-identification: the burned-in patient identifiers are detected and masked while the diagnostic image content is preserved.

Before de-identification After de-identification
Original ultrasound image with burned-in PHI Original ultrasound image with burned-in PHI De-identified ultrasound image with masked PHI De-identified ultrasound image with masked PHI
Info

When automatic mask generation is enabled, the statically configured station-name masks are not used. Manual masks (see Masks Definition) only apply when automatic generation is disabled.

Sensitive Tags Sent to the API

The values of the following tags are sent so the API can detect them in the image. No pixel data leaves Karnak beyond the single frame being analyzed.

AccessionNumber, InstitutionName, OperatorsName, PatientAge, PatientBirthDate, PatientID, PatientName, PatientSex, PerformingPhysicianName, PerformedProcedureStepID, ReferringPhysicianName, StudyDate.

Fail-closed Behavior

When automatic mask generation is enabled and the API is unreachable or returns an error, Karnak produces no mask and the instance is not forwarded. This prevents un-masked PHI from leaking to the destination if the service is down.

Warning

A missing or failing de-identification image API stops eligible instances from being forwarded. Check the Karnak logs for the cause (client error, server error, or unreachable service).

Enabling the Option in a Profile

Via the UI

  1. Open the profile editor and add (or edit) a Clean Pixel Data element.
  2. Enable Automatic masks generation.
  3. Make sure the profile also contains the DICOM basic profile element.
  4. Save and assign the profile to the destination’s de-identification project.

Via a YAML Profile

Set the automaticMasksGeneration argument to "true" on the clean.pixel.data element:

name: "Automatic De-identification Karnak Profile"
version: "1.0"
minimumKarnakVersion: "2.0.0"
profileElements:
  - name: "Clean pixel data"
    codename: "clean.pixel.data"
    arguments:
      automaticMasksGeneration: "true"
  - name: "DICOM basic profile"
    codename: "basic.dicom.profile"

Configuring the External API Endpoint

Karnak calls the de-identification image API at the URL configured by the OCR_URL environment variable (default http://localhost:8000). It maps to the ocr.url property in application.yml:

ocr:
  url: ${OCR_URL:http://localhost:8000}
Variable Default Description
OCR_URL http://localhost:8000 Base URL of the de-identification image API.

The API contract is documented in the deidentification-api.yaml specification.

Deploying the External De-identification Service

The de-identification image API is a separate service. Deploy and run it so that it is reachable from Karnak at OCR_URL.

To deploy it alongside the standard installation:

  1. Deploy the de-identification image API following its deployment guide.
  2. Point Karnak to it by setting OCR_URL to the service base URL.
  3. Restart Karnak so the new configuration is picked up.
Info

For the portable distribution, the de-identification service ships as a separate plugin that is started automatically as a sidecar. See the portable distribution page for the installation steps.

Troubleshooting

Symptom Likely cause Action
Images with automatic masking are not forwarded API unreachable or returning errors Check the API is running and reachable at OCR_URL; inspect the Karnak logs.
Cannot reach de-identification image API ... in the logs Wrong URL or service down Verify OCR_URL and the service health.
Client error ... / Server error ... from de-identification image API in the logs Malformed request (4xx) or API failure (5xx) Verify the API version and health; check the request format against deidentification-api.yaml.
The SOP Instance UID in the API response ... does not match ... in the logs API returned a response for a different instance Verify the API version and that it echoes back the request sop_instance_uid.
The SOP Instance UID in the API response is null in the logs API response omitted sop_instance_uid Verify the API version returns sop_instance_uid in its JSON response.
No mask applied although PHI is visible The API detected no sensitive text, or the tag values are absent from the metadata Confirm the sensitive tags are populated in the source metadata.

Conditions in a De-identification Profile

Overview

A condition is an expression evaluated against a DICOM instance that returns a boolean value (true or false). Conditions are used in profiles to determine whether specific actions or transformations should be applied to DICOM elements based on the content of the instance.

Quick Reference

Function Purpose Example
tagValueIsPresent Exact match tagValueIsPresent(#Tag.Modality, "CT")
tagValueContains Substring match tagValueContains(#Tag.StudyDescription, "cardiac")
tagValueBeginsWith Prefix match tagValueBeginsWith(#Tag.SeriesNumber, "1")
tagValueEndsWith Suffix match tagValueEndsWith(#Tag.StationName, "GE")
tagIsPresent Tag existence tagIsPresent(#Tag.PatientName)

Constants

To improve readability and reduce errors, the following constants are available:

  • #Tag - Retrieves DICOM tag identifiers by their standard names

    • Example: #Tag.PatientBirthDate β†’ 0010,0030
    • Example: #Tag.StudyDescription β†’ 0008,1030
    • Example: #Tag.Modality β†’ 0008,0060
  • #VR - Retrieves DICOM Value Representation types

    • Example: #VR.LO β†’ Long String
    • Example: #VR.DA β†’ Date
    • Example: #VR.PN β†’ Person Name

Using these constants is recommended over hardcoded tag values to make profiles more maintainable and self-documenting.

Available Functions

All utility functions are detailed below with their parameters, return types, and usage examples.

tagValueIsPresent

tagValueIsPresent(int tag, String value) or tagValueIsPresent(String tag, String value)

This function will retrieve the tag value of the DICOM and check if the value parameter is the same as the tag value.

//Check if the study description is equals to "755523-st222-GE"
tagValueIsPresent(#Tag.StudyDescription, "755523-st222-GE")
tagValueIsPresent("0008,1030", "755523-st222-GE")

//Check if the study description is not equals to "755523-st222-GE"
!tagValueIsPresent(#Tag.StudyDescription, "755523-st222-GE")
!tagValueIsPresent("0008,1030", "755523-st222-GE")

tagValueContains

tagValueContains(int tag, String value) or tagValueContains(String tag, String value)

This function will retrieve the tag value of the DICOM and check if the value parameter appears in the tag value.

//Check if the study description contains "st222"
tagValueContains(#Tag.StudyDescription, "st222")
tagValueContains("0008,1030", "st222")

//Check if the study description does not contain "st222"
!tagValueContains(#Tag.StudyDescription, "st222")
!tagValueContains("0008,1030", "st222")

tagValueBeginsWith

tagValueBeginsWith(int tag, String value) or tagValueBeginsWith(String tag, String value)

This function will retrieve the tag value of the DICOM and check if the tag value begins with the parameter value

//Check if the study description begins with "755523"
tagValueBeginsWith(#Tag.StudyDescription, "755523")
tagValueBeginsWith("0008,1030", "755523")

//Check if the study description does not begin with "755523"
!tagValueBeginsWith(#Tag.StudyDescription, "755523")
!tagValueBeginsWith("0008,1030", "755523")

tagValueEndsWith

tagValueEndsWith(int tag, String value) or tagValueEndsWith(String tag, String value)

This function will retrieve the tag value of the DICOM and check if the tag value ends with the parameter value

//Check if the study description ends with "GE"
tagValueEndsWith(#Tag.StudyDescription, "GE")
tagValueEndsWith("0008,1030", "GE")

//Check if the study description does not end with "GE"
!tagValueEndsWith(#Tag.StudyDescription, "GE")
!tagValueEndsWith("0008,1030", "GE")

tagIsPresent

tagIsPresent(int tag) or tagIsPresent(String tag)

This function will check if the tag is present in the DICOM instance.

//Check if the tag study description is present in the DICOM file
tagIsPresent(#Tag.StudyDescription)
tagIsPresent("0008,1030")

//Check if the tag study description is not present in the DICOM file
!tagIsPresent(#Tag.StudyDescription)
!tagIsPresent("0008,1030")

Tags Inside Sequences

A condition is evaluated for each visited attribute, including the attributes nested in the items of a sequence. The tag given to a function is looked up from the dataset being visited outwards: inside a sequence item, the item’s own value is used when the item holds the tag, otherwise the value of the enclosing dataset (up to the top level). A condition on a study-level tag such as Modality therefore holds at every level, while a condition on a tag that only exists inside the items of a sequence is true only while those items are visited.

Combining Conditions

Multiple conditions can be combined using logical operators.

&& corresponds to the AND logical operator

|| corresponds to the OR logical operator

//Check if the tag study description ends with "GE" and if the station name is "CT1234"
tagValueEndsWith(#Tag.StudyDescription, "GE") && tagValueContains(#Tag.StationName, "CT1234")

//Check if the tag study description ends with "GE" or if the station name is "CT1234"
tagValueEndsWith(#Tag.StudyDescription, "GE") || tagValueContains(#Tag.StationName, "CT1234")

How Karnak De-identifies DICOM Studies

This page provides technical details about how Karnak performs DICOM de-identification, including the algorithms used for UID generation, date shifting, and pseudonymization.

Overview

Karnak is a gateway that receives DICOM files and forwards them to one or multiple destinations using DICOM or DICOMweb protocols. Each destination can be linked to a project that defines the de-identification method and a secret used to generate deterministic values.

Basic Profile

The Basic Profile for de-identifying DICOM objects is provided by the DICOM standard. This profile defines an exhaustive list of DICOM tags and their related actions for proper de-identification.

De-identification Actions

Five different actions are defined in the DICOM standard:

Action Description
D Replace with a dummy value
Z Set to null
X Remove
K Keep
U Replace with a new UID

Multiple Actions for IOD Conformance

The DICOM type is often dependent on the Information Object Definition (IOD) of the instance. To avoid DICOM corruption, multiple actions can be defined for a tag, ensuring that destructive actions like REMOVE won’t be applied on Type 1 or Type 2 attributes.

Combined actions:

Action Behavior
Z/D Z unless D is required to maintain IOD conformance (Type 2 versus Type 1)
X/Z X unless Z is required to maintain IOD conformance (Type 3 versus Type 2)
X/D X unless D is required to maintain IOD conformance (Type 3 versus Type 1)
X/Z/D X unless Z or D is required to maintain IOD conformance (Type 3 versus Type 2 versus Type 1)
X/Z/U* X unless Z or replacement of contained instance UIDs (U) is required to maintain IOD conformance. (Type 3 versus Type 2 versus Type 1 sequences containing UID references)

Action Selection:

Karnak loads the SOPs and attributes as specified in the DICOM Standard. Based on the tag’s type in the current instance, the proper action is set and applied.

Info

If the tag cannot be identified in the SOP or its type cannot be inferred, the strictest action will be applied (U/D > Z > X).

Examples of action resolution:

  • Z/D, X/D, X/Z/D β†’ apply action D
  • X/Z β†’ apply action Z
  • X/Z/U, X/Z/U* β†’ apply action U

Action D: Replace with Dummy Value

The action D replaces the tag value with a dummy one that is consistent with the Value Representation (VR) of the tag.

Default Values by VR

Karnak uses these default values based on the VR when no specific dummy value is defined:

Value Representation Default Value Notes
AE, CS, LO, LT, PN, SH, ST, UN, UT, UC, UR "UNKNOWN" Text-based values
DS, IS "0" Numeric strings
AS, DA, DT, TM Generated date/time Uses Shift Date
UI Generated UID Uses Action U
FL, FD, SL, SS, UL, US Null Binary values set to null

Date Generation

For date and time VRs (AS, DA, DT, TM), the shiftRange() function generates a random value within configurable limits:

  • Default maximum days: 365
  • Default maximum seconds: 86400

Action U: Generate a New UID

For each U action, Karnak hashes the input value using a one-way function to ensure it’s not possible to revert to the original UID. The function hashes the input UID and generates a new deterministic UID from the result.

Context and Project Secrets

A DICOM study may be de-identified multiple times using different methods. Karnak ensures deterministic UID generation to maintain data quality and usability.

Requirements:

  1. A project must be created and associated with the destination
  2. The project defines a de-identification method and a secret
  3. The project’s secret is used as the key for the HMAC algorithm

Project Secret Format

Info

The secret is 16 bytes long (32 hexadecimal characters) and randomly generated when the project is created.

Users can provide their own secret, but it must be exactly 16 bytes, written as 32 hexadecimal characters.

Hash Function

The algorithm used is “Message Authentication Code” (MAC). Karnak uses MAC as a one-way function rather than for message authentication.

According to the Java Mac class documentation:

A MAC provides a way to check the integrity of information transmitted over or stored in an unreliable medium, based on a secret key. Typically, message authentication codes are used between two parties that share a secret key in order to validate information transmitted between these parties.

A MAC mechanism that is based on cryptographic hash functions is referred to as HMAC. HMAC can be used with any cryptographic hash function, e.g., SHA256 or SHA384, in combination with a secret shared key. HMAC is specified in RFC 2104.

Karnak’s HMAC Configuration:

  • Hash function: SHA256
  • Secret key: Project’s secret (16 bytes)

UID Generation Process

Karnak generates a new DICOM UID that starts with the OID root "2.25" followed by a decimal representation of a UUID derived from the HMAC hash.

The value after “2.25.” is the straight decimal encoding of the UUID as an integer. It must be a direct decimal encoding of the single integer, all 128 bits. See How do you create an OID?

UUID Generation Algorithm

The generated UUID uses the first 16 bytes (128 bits) from the hash value as a UUID type 4 with variant 1.

Pseudocode to ensure correct UUID type and variant:

// Version
uuid[6] &= 0x0F
uuid[6] |= 0x40

// Variant
uuid[8] &= 0x3F
uuid[8] |= 0x80

Final UID format:

uuid = β€œ2.25.” + HashedValue[0:16].toPositiveDecimal()

Shift Date: Generate a Random Date

Karnak implements randomized date shifting that is consistent per patient and project, ensuring data consistency across all instances for the same patient.

Algorithm

The random shift uses the HMAC function (defined above) with a configurable range of days or seconds.

Default values:

  • Minimum: 0 (if not specified)
  • Maximum: User-defined

Process:

  1. The Patient ID is hashed using the project’s secret
  2. The hash is converted to a numeric value within the specified range
  3. The same shift is applied to all date fields for that patient

Pseudocode:

scaleHash(PatientID, scaledMin, scaledMax):
    patientHashed = hmac.hash(PatientID)
    scale = scaledMax - scaledMin

    shift = (patientHashed[0:6].toPositiveDecimal() * scale) + scaledMin
Info

The Patient ID combined with the project’s secret ensures that date shifts are deterministic per patient while remaining unpredictable across different patients.

Pseudonymization

This section explains how Karnak handles Patient ID generation to prevent data leakage across different de-identification methods.

The Problem

A patient participating in multiple research projects may encounter different de-identification methods. Most patient identifying information is contained in the Patient Module.

Risk scenario:

If the same pseudonym is used across projects with different de-identification profiles, data can be leaked when studies are reconciled.

Example: Data Leakage Risk

In this example, a patient’s study falls within the scope of two different projects:

  • Project 1: Removes the patient birthdate
  • Project 2: Keeps the patient birthdate

If the patient pseudonym is used as patient identification and the data is reconciled, the birthdate will be leaked.

flowchart TB
    patient["Patient's original<br>DICOM study<br><i>(contains birthdate)</i>"]
    gateway["Karnak<br>Gateway"]:::karnak
    table[("Pseudonym<br>mapping table<br>patient &harr; pseudonym")]
    p1["<b>Project 1</b><br>Profile:<br><b>remove</b> birthdate"]
    p2["<b>Project 2</b><br>Profile:<br><b>keep</b> birthdate"]
    s1["De-identified study<br>birthdate <b>removed</b>"]
    s2["De-identified study<br>birthdate <b>kept</b>"]
    pm["<b>Patient Module</b><br>the <b>same pseudonym</b> identifies the<br>patient in both studies &rarr; the kept<br>birthdate leaks when they are reconciled"]:::leak

    patient -->|"(1) DICOM send"| gateway
    gateway <-->|"(2) Look up the<br>patient's pseudonym"| table
    gateway -->|"(3) Route to Project 1"| p1
    gateway -->|"(5) Route to Project 2"| p2
    p1 -->|"(4) Apply Profile 1"| s1
    p2 -->|"(6) Apply Profile 2"| s2
    s1 -->|"Identification"| pm
    s2 -->|"Identification"| pm

    classDef karnak fill:#f9a825,stroke:#e08e00,color:#000;
    classDef leak fill:#ffe0e0,stroke:#e03e3e,color:#000;

Because both studies are identified by the same pseudonym, reconciling them re-associates the kept birthdate (Project 2) with the patient whose birthdate Project 1 removed.

The Solution: Project-Specific Patient IDs

Karnak generates a unique Patient ID based on the pseudonym and project-specific characteristics. This prevents reconciliation across projects and eliminates data leakage.

flowchart TB
    patient["Patient's original<br>DICOM study<br><i>(contains birthdate)</i>"]
    gateway["Karnak<br>Gateway"]:::karnak
    table[("Pseudonym<br>mapping table<br>patient &harr; pseudonym")]
    p1["<b>Project 1</b><br>Profile:<br><b>remove</b> birthdate"]
    p2["<b>Project 2</b><br>Profile:<br><b>keep</b> birthdate"]
    id1["Patient ID<br>specific to Project 1"]:::idbox
    id2["Patient ID<br>specific to Project 2"]:::idbox
    s1["De-identified study<br>birthdate <b>removed</b>"]
    s2["De-identified study<br>birthdate <b>kept</b>"]
    pm1["<b>Patient Module</b><br>Patient ID unique to Project 1<br>&rarr; cannot be reconciled"]:::safe
    pm2["<b>Patient Module</b><br>Patient ID unique to Project 2<br>&rarr; cannot be reconciled"]:::safe

    patient -->|"(1) DICOM send"| gateway
    gateway <-->|"(2) Look up the<br>patient's pseudonym"| table
    gateway -->|"(3) Route to Project 1"| p1
    gateway -->|"(6) Route to Project 2"| p2
    p1 -->|"(4) Derive a project-<br>specific Patient ID"| id1
    p2 -->|"(7) Derive a project-<br>specific Patient ID"| id2
    id1 -->|"(5) Apply Profile 1"| s1
    id2 -->|"(8) Apply Profile 2"| s2
    s1 -->|"Identification"| pm1
    s2 -->|"Identification"| pm2

    classDef karnak fill:#f9a825,stroke:#e08e00,color:#000;
    classDef safe fill:#e0f5e0,stroke:#2ab218,color:#000;
    classDef idbox fill:#eef3ff,stroke:#5b7cc2,color:#000;

Each project derives its own Patient ID from the pseudonym and the project’s secret, so the two studies can no longer be reconciled β€” the kept birthdate stays isolated to Project 2.

PatientID Generation

The de-identified Patient ID is generated as follows:

  1. The patient’s pseudonym is retrieved from an external service or mapping table
  2. The pseudonym is hashed using the HMAC function and the project’s secret
  3. The Patient ID is set to the first 16 bytes of the hashed pseudonym (in hexadecimal format)
Info

This makes the Patient ID unique and deterministic within the context of the project, preventing cross-project reconciliation.

Patient Name:

Once the profile has been applied, the Patient’s Name is always set to the pseudonym.

Attributes Added by Karnak

Karnak automatically sets certain attributes during de-identification to maintain compliance and traceability.

SOP Common Module

The following attributes are set in the SOP Common Module:

Tag Attribute Name Value Format
(0008,0013) Instance Creation Time Time the SOP instance was created TM (HHMMSS.FFFFFF)
(0008,0012) Instance Creation Date Date the SOP instance was created DA (YYYYMMDD)

Patient Module

The following attributes are set in the Patient Module:

Tag Attribute Name Value Notes
(0010,0020) Patient ID Hashed pseudonym See PatientID Generation
(0010,0010) Patient Name Pseudonym Always set after the profile is applied
(0012,0062) Patient Identity Removed YES Indicates de-identification
(0012,0063) De-identification Method Removed Replaced by the code sequence below
(0012,0064) De-identification Method Code Sequence One item per DICOM de-identification option applied See codes below

De-identification Method Code Sequence

Each item holds a code from the DCM coding scheme. Only the profile elements matching a DICOM de-identification option add an item (each code appears once):

Code Value Code Meaning Profile element codename
113100 Basic Application Confidentiality Profile basic.dicom.profile
113101 Clean Pixel Data Option clean.pixel.data
113102 Clean Recognizable Visual Features Option clean.recognizable.visual.features
113107 Retain Longitudinal Temporal Information Modified Dates Option action.on.dates
113111 Retain Safe Private Option action.on.privatetags

Clinical Trial Subject Module

The following attributes are set in the Clinical Trial Subject Module:

Tag Attribute Name Value
(0012,0010) Clinical Trial Sponsor Name Project name
(0012,0020) Clinical Trial Protocol ID Profile name
(0012,0021) Clinical Trial Protocol Name Null
(0012,0030) Clinical Trial Site ID Null
(0012,0031) Clinical Trial Site Name Null
(0012,0040) Clinical Trial Subject ID Pseudonym
(0012,0042) Clinical Trial Subject Reading ID Removed
(0012,0081) Clinical Trial Protocol Ethics Committee Name Removed
(0012,0082) Clinical Trial Protocol Ethics Committee Approval Number Removed

Karnak Web Interface User Guide

This user guide provides a comprehensive overview of the Karnak web interface, covering configuration, management, and operational features. Most of the sections below correspond to specific functionalities accessible from the left-hand menu in the main Karnak interface.

  • Gateway

    Configure a forward node β€” the DICOM application entity that receives instances β€” and route what it arrives to destinations, each with its own profile.

  • Profiles

    Create, import, edit and delete profiles in the Karnak portal and see which projects use them. The DICOM Basic Profile is available out of the box.

  • Projects

    A project links a de-identification profile to the secret used to derive pseudonyms and UIDs. Create and manage projects, and what the secret controls.

  • External pseudonym

    Supply your own pseudonyms instead of generated ones: CSV import or manual entry, and how Karnak matches them to patients during transfer.

  • Monitoring

    Follow transfers in progress and recent history: filters, transfer statuses, the study hierarchy tree with a detail panel, the dashboard and CSV export.

  • DICOM Web Tools

    Test connectivity and query endpoints from Karnak: C-ECHO, DICOM worklist, QIDO-RS, WADO and STOW-RS checks, plus the reusable DICOM node configurations.

  • Conformance Report

    Validate every study sent to a destination against the DICOM standard and get an HTML report by email: what is checked, how to enable it, how to read it.

  • Authentication Config

    Register the credentials Karnak uses to call an external service β€” client secret and endpoint β€” stored encrypted and reused by profiles.

  • Kheops

    Configure a Kheops album as a STOW-RS destination: create the album token, set its permissions, and switch between albums from the same forward node.

  • Help

    The Help page gathers links to this documentation and reports the running version and runtime details β€” the information to quote when reporting an issue.

  • Portable distribution

    The portable distribution runs the gateway on a single machine with no installation: what it contains, how to start it, and what it is and is not suited for.

Subsections of User guide

Forward Nodes: DICOM Gateway Configuration

The Gateway page allows you to configure Forward Nodes in Karnak. A Forward Node represents a DICOM Application Entity (AE) that receives DICOM instances and routes them to one or more destinations. See How Karnak works for the deployment workflow and the pipeline each instance goes through.

Forward Node

This page lists all the forward nodes configured in Karnak and allows you to create, edit, and delete them.

Gateway and Forward Node

1. Create a Forward Node

Click the New forward node button. A popup will appear:

  1. Enter a unique value in the Forward AETitle field. AE Titles must not exceed 16 characters and cannot contain control characters or a backslash.
  2. Click the Add button

New Forward Node New Forward Node

The new forward node appears in the list and is automatically selected. Optionally, add a description and save your changes.

Info

The Forward AETitle must be unique across all forward nodes in the Karnak instance.

2. Create a group

When the list grows, you can organize forward nodes into groups (a single level of folders) to keep related nodes together:

  • Click Add group to create a new group and give it a name.
  • Drag a forward node onto a group to assign it to that group.
  • Right-click a group to Rename group or Delete group; deleting a group only removes the folder β€” the nodes it contained move back out, they are not deleted.
  • Right-click a node and choose Remove from group to take it out of its group.

Groups are purely organizational; they do not change how forward nodes behave. The same grouping is available for Projects and Profiles.

Group Group

3. Forward Node List

All configured forward nodes and groups are displayed in the left panel.

Select a forward node from the list to view and manage its configuration in the right panel.

Info

The copy icon next to each forward node allows you to quickly copy its DICOM configuration (AE Title, host and the listener port β€” DICOM_LISTENER_PORT, 11119 by default, see the installation page) to the clipboard for use in DICOM clients. Adapt the host if the sending node reaches Karnak through another address.

4. Forward Node Parameters

In the details view, you can modify:

  • Forward AETitle: The Application Entity Title of the forward node
  • Description: An optional description to help identify the node’s purpose

Click the Save button to apply your changes.

5. Sources and Destinations

Each forward node can be configured with:

  • Sources: Control which DICOM nodes are authorized to send data to this forward node
  • Destinations: Define where received DICOM instances should be forwarded
Forward Node details

5.1 Navigation

Use the tabs to switch between the Destinations and Sources views for the selected forward node.

5.2 Filtering

Destinations tab: a free-text filter matching a destination’s description, AE Title, hostname, port, URL, headers and notification settings.

Sources tab: a free-text filter matching a source’s description, AE Title and hostname.

5.3 List

All destinations or sources associated with the forward node are displayed here.

Click any item in the list (or a New button) to open its configuration form. The form replaces the list panel in place; click Cancel to return to the list.

5.4 Actions

The available action buttons depend on the active tab.

Destinations tab:

Create a new destination using either the DICOM or DICOM WEB (STOW) protocol. See the Destinations page for detailed configuration instructions. The portable distribution shows an additional LOCAL button that creates a destination storing the instances in its local DICOM folder.

Sources tab:

Create a new source to control which DICOM nodes can send data to this forward node. See the Sources page for detailed configuration instructions.

6. Forward Node Actions

Three action buttons are available:

Action Description
Save Saves changes made to the forward node parameters
Delete Deletes the selected forward node and all associated configurations
Cancel Reverts unsaved changes to the forward node parameters
Info

While a transfer is running through the forward node, Save and Delete are disabled and read Transfer in progress; they become available again once the transfer is complete.

Warning

Deleting a forward node will also remove all associated sources and destinations, as well as the related entries in the Monitoring view. This action cannot be undone.

Subsections of Gateway

DICOM and DICOMweb Destinations

This page allows you to configure destinations for your forward nodes. Destinations define where DICOM instances are sent and how they are processed during transfer.

Info

To access this page, first select a forward node from the Gateway page, then open the Destinations tab (it is selected by default).

Depending on the protocol used, two types of destinations can be created:

DICOM Destination

A DICOM Destination uses the traditional DICOM protocol to forward instances to another DICOM node.

Creating a DICOM Destination

AETitle, Hostname and Port are mandatory when creating a DICOM Destination.

DICOM destination form DICOM destination form

Configuration Options

1. Destination Parameters

These fields define where Karnak should send the DICOM instances.

  • AETitle: The Application Entity Title that identifies the destination DICOM node (16 characters max, no whitespace).
  • Description: An optional label shown in the destination list.
  • Hostname: The IP address or hostname of the destination server.
  • Port: The network port used by the destination DICOM service (must be between 1 and 65535).
  • Concurrent connections: The number of parallel DICOM associations Karnak opens to this destination (between 1 and 50; 1 means a single connection). Increasing this value improves throughput when forwarding large studies, but must stay within the destination PACS’s concurrent-association limit.
  • Condition (labeled Condition (Leave blank if no condition)): An optional field that can contain an expression. If the condition is met, the destination will be activated. See Conditions for more details.

2. Transfer Syntax

This field defines the transfer syntax used when sending DICOM instances to this destination. The transfer syntax determines the encoding and compression format of the DICOM data.

It is recommended to choose “Keep original transfer syntax” unless you have specific requirements for compression or compatibility with the destination system.

When selecting a specific transfer syntax, ensure that the destination system supports it to avoid decompression or compatibility issues. When you select a compressed transfer syntax, the Transcode only uncompressed checkbox becomes available and is checked automatically so that instances that are already compressed are not re-encoded; uncheck it only if every instance must be transcoded. The checkbox is disabled for Keep original transfer syntax and Explicit VR - Little Endian.

Info

Transcoding may increase processing time and resource usage, especially for large datasets. Use it judiciously based on your workflow needs.

3. Use AETitle Destination

When this option is checked, use the destination AETitle as the calling AETitle instead of the Forward Node AETitle during the DICOM association.

4. Notifications

Check Activate notification to receive automatic email summaries of forwarded instances, including success and error reports.

Notifications Notifications

Configuration fields (on screen each label is prefixed with Notif.: and shows its default inline, e.g. Notif.: error subject prefix (Default: **ERROR**)):

Field Description Default Value
List of emails Comma-separated list of recipient email addresses (required when notifications are active) None
Error subject prefix Prefix added to email subjects when an error occurs **ERROR**
Rejection subject prefix Prefix added when an instance is rejected due to filters or criteria **REJECTED**
Subject pattern Email subject template using Java String Format [Karnak Notification] %s %.30s
Subject values DICOM attributes to inject into the subject pattern (PatientID, StudyDescription, StudyDate, StudyInstanceUID) PatientID,StudyDescription
Interval Time in seconds to wait before sending notification (aggregates multiple instances) 45
Info

The interval setting helps reduce email volume by aggregating notifications. All instances received within the specified interval are summarized in a single email.

Info

Each email reports only what is new since the previous notification for a given series (the delta), so outcomes that were already reported are not counted again when the series is touched by a later transfer. A series touched only by retries or duplicate re-sends has nothing new to report and therefore does not trigger a notification.

5. Virtual Destination

When Virtual destination (report only, discard DICOM) is checked, the destination does not forward anything to a real node. Each study is still received and validated, but the only output is the DICOM conformance report, which is emailed. Delivery settings (transfer syntax, notifications, etc.) do not apply, and the conformance report is mandatory and cannot be turned off while the destination is virtual.

This is useful to validate DICOM conformance β€” for testing, validation-only deployments, or audit purposes β€” without sending any data.

Info

On screen, the Virtual destination and Build DICOM conformance report options share a single box, displayed right after Notifications and before Tag Morphing and De-identification.

6. DICOM Conformance Report

Enable Build DICOM conformance report to validate every study sent to this destination against the DICOM standard and email an HTML report. When enabled, options appear to set the report’s recipient emails, to also check value content conformity (VR rules), to perform deep sequence validation, and to check for identifying data burned into the image (this last option runs OCR through the external de-identification image service, which must be reachable at OCR_URL, http://localhost:8000 by default).

DICOM conformance report options DICOM conformance report options

See the dedicated Conformance Report page for what each option does, when the report is sent, and what it contains.

7. Tag Morphing

Tag morphing allows you to modify specific DICOM tag values defined in a profile. With this mode, it is not mandatory to de-identify patient information like in De-identification mode.

Prerequisites:

Configuration:

  1. Select the project from the dropdown
  2. The applied profile and its version are displayed below the selection

If no project exists yet, the same no-project popup shown for de-identification appears, letting you create one or cancel.

Tag Morphing Tag Morphing

Info

Activate tag morphing and Activate de-identification are mutually exclusive options. Only one can be active at a time.

8. De-identification

De-identification removes or replaces patient-identifying information in DICOM instances according to configurable rules and profiles. It is not possible to preserve the patient identity in the profile when this option is activated.

Info

Activate de-identification and Activate tag morphing are mutually exclusive options. Only one can be active at a time.

Prerequisites:

To activate de-identification, you must create a project first.

If no project exists:

A popup will appear with two options, either create a project or continue without activating de-identification.

No-project popup No-project popup

If a project exists:

Configure de-identification as shown below:

Configure de-identification Configure de-identification

Project Selection

Select the project that defines the de-identification profile to apply. The profile name and version are displayed below the selection.

Each project is associated with:

  • A de-identification profile
  • A secret key used for pseudonymization and UID generation

See the Projects page for more information about project configuration.

Pseudonym Type

Pseudonyms replace patient identifiers while maintaining the ability to link de-identified data back to the original patient (when necessary and authorized).

Choose how Karnak should retrieve or generate pseudonyms:

Type Description Use Case
Pseudonym is already stored in KARNAK Queries the internal pseudonym cache When using External Pseudonym
Pseudonym is in a DICOM tag Extracts pseudonym from a specified DICOM tag When pseudonyms are pre-populated in DICOM data
Pseudonym from external API Retrieves pseudonym via API call When using external pseudonymization services
Pseudonym is already stored in KARNAK

This option queries the pseudonym stored in the internal cache as explained in External Pseudonym.

Behavior:

  • Karnak queries its internal cache using the Patient ID and Issuer of Patient ID
  • If a pseudonym is found, it is used for de-identification
  • If no pseudonym is returned, the transfer of the DICOM instance is aborted. Ensure pseudonyms are correctly populated in External Pseudonym.
Pseudonym is in a DICOM tag

This option retrieves the pseudonym from a specified DICOM tag within the instance itself.

Pseudonym is in a DICOM tag Pseudonym is in a DICOM tag

Required configuration:

  • Tag: The DICOM tag containing the pseudonym (e.g., Clinical Trial Subject ID (0012,0040))

Optional configuration (both must be set together):

  • Delimiter: Character used to split the tag value
  • Position: Which part to use after splitting (zero-based index)

Example:

If tag (0012,0040) contains "SITE01-PSN12345" and you set:

  • Delimiter: -
  • Position: 1

The pseudonym will be "PSN12345".

If no delimiter and position are specified, the entire tag value is used as the pseudonym.

Pseudonym from external API

This option makes an API call to retrieve the pseudonym from an external service.

Pseudonym from API Pseudonym from API

The detailed usage of all the fields is explained in the API Actions page. The behavior is identical to profile API actions.

You can reference an Authentication Configuration to securely manage API credentials for OAuth 2.0.

Issuer of Patient ID by default

This field (labeled Issuer of Patient ID by default) provides a default value for the Issuer of Patient ID when it’s not present in the DICOM instance. It is only shown when the pseudonym type is Pseudonym is already stored in KARNAK.

Usage:

This value is used when retrieving the pseudonym using the External Pseudonym cache. The pseudonym is queried based on:

  • Patient ID (from DICOM tag 0010,0020)
  • Issuer of Patient ID (from DICOM tag 0010,0021 or this default value)

The combination of Patient ID and Issuer ensures unique patient identification across different healthcare systems.

Ignore the Issuer of Patient ID (cache lookup)

This option is only available when the pseudonym type is Pseudonym is already stored in KARNAK. On screen it is the Ignore Issuer of Patient ID checkbox. When checked, the Issuer of Patient ID of the incoming image is ignored when building the key used to look up the pseudonym in the cache.

It is enabled by default, so the same Patient ID is matched even when it is registered under a different (or missing) issuer in the External Pseudonym cache than in the images you receive. Uncheck it if you need the issuer to be part of the lookup key. When this option is checked, the Issuer of Patient ID by default field is cleared and disabled, since it no longer takes part in the lookup.

9. Authorized SOPs

Filter which DICOM instance types (SOP Classes) are forwarded to this destination.

SOP Filter SOP Filter

Configuration:

  • Enabled: Only instances matching the specified SOP Classes are transferred. At least one SOP Class must be selected, otherwise the destination cannot be saved.
  • Disabled: All SOP Classes are transferred without restriction

10. Enable the Destination

The Enable destination checkbox allows you to temporarily disable a destination without deleting it. The state is also visible in the destination list with a green (enabled) or red (disabled) indicator.

Use this feature to:

  • Temporarily pause forwarding during maintenance
  • Test configurations without affecting production destinations
  • Keep backup destination configurations ready but inactive

11. Action Buttons

Three actions are available to manage the destination:

Button Description
Save Saves all changes made to the destination configuration
Delete Permanently deletes the selected destination
Cancel Reverts all unsaved changes to the last saved state
Info

While a transfer is running through the forward node, Save and Delete are disabled and read Transfer in progress; the Activity column of the destination list shows a spinner during that time.

Warning

Deleting a destination cannot be undone. All configuration settings will be permanently removed.


STOW Destination

A STOW Destination forwards instances over DICOMweb (STOW-RS) to a web server such as a DICOMweb-enabled PACS or a Kheops instance.

Creating a STOW Destination

The URL is required for STOW Destination creation. Most configuration options are identical to DICOM Destinations. Only the differences are detailed below.

STOW destination form STOW destination form

Configuration Differences

1. Destination Parameters

URL: The DICOMweb STOW-RS endpoint where DICOM instances will be sent (e.g., https://pacs.example.com/dicomweb/studies).

Condition: An optional expression that activates the destination when met. See Conditions for more details.

Use HTTP/2

When Use HTTP/2 is checked, STOW-RS uploads use HTTP/2 instead of HTTP/1.1.

Warning

Leave this unchecked (HTTP/1.1) when the archive sits behind a reverse proxy that caps the number of HTTP/2 requests per connection (e.g. KHEOPS / nginx http2_max_requests=1000), which can silently drop instances beyond the limit.

HTTP Headers

The Headers field contains HTTP headers added to each STOW-RS request.

Format: Headers are defined using XML-style key-value tags as shown below:

<key>Header-Name</key>
<value>Header-Value</value>

<key>Another-Header</key>
<value>Another-Value</value>
Generate Authorization Header

Click the Generate Authorization Header button to automatically generate properly formatted authentication headers.

Warning

If a header with <key>Authorization</key> already exists, an error will be displayed. The generator will append to existing headers if they don’t conflict.

Supported Authorization Types: the dialog has an Authorization Type selector (Basic Auth or OAuth 2) and a Generate Headers button that appends the generated header.

Basic Auth

Use for simple username/password authentication:

Basic Auth header generator Basic Auth header generator

Generated headers:

<key>Authorization</key>
<value>Basic dXNlcm5hbWU6cGFzc3dvcmQ=</value>

The username and password are Base64-encoded automatically.

OAuth 2

Use for token-based authentication:

OAuth 2 header generator OAuth 2 header generator

Generated headers:

<key>Authorization</key>
<value>Bearer 1234567890</value>

2. Switching to different Kheops albums

If the destination is a Kheops endpoint, it is possible to configure multiple albums by selecting the option “Switching in different KHEOPS albums”.

Explanations for configuring Kheops-related parameters can be found in the Kheops section.

Restrict Which DICOM Sources May Send

The Sources tab displays all configured sources for the selected forward node.

Sources view Sources view

A source is used to control which entities can send DICOM data to the forward node. It validates that received DICOM instances come from a known Application Entity Title (AET).

Info

If no sources are defined, the forward node will accept DICOM instances from any AET without restrictions.

Create a source

To create a new source, click the Source button and configure the following fields:

Creation source Creation source

Field Description Required
AETitle Application Entity Title of the source DICOM node (16 characters max) Yes
Description Optional description to identify the source No
Hostname Hostname or IP address of the source DICOM node No*

*Fill in the Hostname when you enable the “Check the hostname” option. When enabled, Karnak validates both the AET and the hostname (or IP address) of the calling node during the DICOM association, and refuses the connection when the hostname does not match.

Source validation behavior

When a DICOM instance is received by the forward node:

  • With sources configured: Only instances from registered AETs (and hostnames, if enabled) are accepted. Instances from any other node are refused with the DICOM status Not Authorized (0124H) and are listed in the Monitoring view as not transferred, with the reason Source not authorized: .
  • Without sources configured: All instances are accepted.

This provides flexible security control for your DICOM workflow.

Manage De-identification Profiles in Karnak

This page lists all profiles configured in Karnak and lets you create, import, edit and delete them. A profile defines the actions to apply to a DICOM instance before it is sent.

The Dicom Basic Profile is available by default and cannot be edited or deleted. It is the reference de-identification profile based on the DICOM standard. For more details, see De-identification Process

profile page profile page

The page has two areas:

  • a left panel that lists the profiles, with the buttons to create, import and group them;
  • a right panel that opens when you select a profile, with two editing tabs β€” Profile elements (a visual editor for the most common actions) and YAML editor (the full profile as YAML).
Info

The Profile elements tab is a convenience editor that covers the major element types only β€” it does not expose every configuration option. For the full capabilities (for example pixel-data masks, API-based replacement, or any option the visual editor does not show), use the YAML editor or import a YAML file. Both tabs describe the same profile; the complete structure is in the Profiles reference.

Left panel: create, import and group profiles

New profile

Click New profile to open a popup. Provide a Name, a Version and the Min Karnak version (optional), then click Create. An empty, editable profile is created and opened so you can add its elements.

Import a profile

To reuse an existing profile, use the upload area below the buttons: click Upload File… or drag a YAML file onto Drag and drop your profile here. The file is loaded and analyzed; if it is valid, a new profile is created from it and added to the list. If it contains errors, the profile is not added and the errors are shown in the right panel (see Import errors).

Info

For how to write a valid profile YAML file, see the Profile Structure documentation.

Profile list and groups

All profiles are listed in the left panel. A profile can evolve over time and appear several times under the same name with different versions. Selecting a profile shows its details on the right.

Click Add group to organize the list into a single level of groups: create a group, drag a profile onto it to assign it, and right-click a group or a profile to Rename group, Delete group or Remove from group. Grouping is purely organizational and is described in more detail on the Gateway page.

Right panel: editing a profile

When you select a profile, the right panel shows its name in a header with the Edit metadata, download and delete actions, above the two tabs Profile elements and YAML editor.

Profile metadata

The header shows the profile name, with a secondary line recalling its Version and Min Karnak version. Click Edit metadata (edit icon) to change them: a popup titled Edit profile opens with the Name, Version and Min Karnak version fields; edit the values and click Save.

Download and delete

Two buttons next to the profile name apply to the whole profile and are available from either tab:

  • the download button exports the profile as a YAML file (for backup or to share with another Karnak instance);
  • the delete (trash) button removes the profile. If the profile is used by one or more projects, a warning is shown to prevent accidental deletion.

Profile actions Profile actions

Profile elements tab

The Profile elements tab is a visual editor for the most common actions. It lists the elements that make up the profile, in the order they are applied. Each row shows its position (#), Name, Type (the element’s codename, e.g. action.on.specific.tags) and a short Summary, with per-row up / down / edit / delete actions.

Info

The order of the elements matters: they are applied from top to bottom. Use the up/down arrows on each row to reorder them.

Profile actions Profile actions

A profile should normally include the Basic DICOM confidentiality profile element; if it is missing, a warning is shown. This element is always applied last β€” it is pinned to the end of the list and cannot be reordered.

Info

This visual editor covers the major element types listed below. An element whose type it does not support (for example pixel-data masks or API-based replacement) still appears in the list and can be reordered or deleted, but its edit action is disabled β€” configure it from the YAML editor instead.

Adding and editing an element

Edit profile element Edit profile element

Click Add element (or the edit action on a row) to open the Add element / Edit element dialog. Fill in:

  • Name β€” a label for the element.

  • Type β€” the kind of action to perform (the dropdown shows each type followed by its codename in parentheses). The types are offered in this order:

    • Apply action to specific tags
    • Shift / format dates
    • Replace UIDs
    • Basic DICOM confidentiality profile
    • Clean pixel data
    • Defacing (clean recognizable visual features)
    • Add a tag
    • Apply action to private tags

    The types that may appear only once in a profile (Basic DICOM confidentiality profile, Clean pixel data and Defacing) are no longer offered once the profile contains them.

  • Type-specific fields β€” the middle of the dialog adapts to the selected type:

    • Apply action to specific tags / Apply action to private tags: an Action (see the table below), a Tags picker and an Excluded tags picker.
    • Replace UIDs: an Action (Generate a new UID, Remove or Replace with null) and a Tags picker.
    • Shift / format dates: an Option (shift, shift_range, shift_by_tag or date_format) that reveals its own fields, plus an optional Tags picker.
    • Add a tag: a Tag picker and a Value.
    • Basic DICOM confidentiality profile, Clean pixel data and Defacing need no extra fields. (To define the mask regions of Clean pixel data, use the YAML editor β€” see pixel-data masks.)
  • Condition (optional) β€” an expression that limits when the element is applied; see Conditions.

The Action (for the tag-based types) decides what to do with the matched tags:

Action Code Meaning
Keep K Keep the value unchanged
Remove X Remove the tag
Replace with null Z Replace with an empty value
Replace with a dummy value D Replace with a dummy value consistent with the tag’s VR (see default values by VR)
Generate a new UID U Generate a new, deterministic UID

The tag fields use a tag picker that lets you search or browse the DICOM dictionary instead of typing tag numbers.

Tag picker Tag picker

Click Save to validate and add the element, or Cancel to discard. The reference for every type, action and option β€” and the equivalent YAML β€” is in the Profiles reference section.

YAML editor tab

Yaml Profile Editor Yaml Profile Editor

The YAML editor tab edits the entire profile as YAML β€” the same format as the import and export files. It gives access to the full set of profile capabilities, including options the visual editor does not expose, such as pixel-data masks and API-based replacement. It provides syntax highlighting and a line-number gutter, and adapts to the light or dark theme.

  • Save parses and validates the YAML and applies it in place, keeping the profile so the projects that reference it are preserved. On success a Profile saved notification appears; if the YAML is invalid, the errors are listed in a red box and nothing changes.
  • Reset discards your edits and reloads the last saved YAML.

The built-in Dicom Basic Profile is shown read-only and cannot be edited here.

Import errors

When a YAML file is imported successfully, the right panel shows the details of the newly created profile.

If errors occur during import, the profile is not added to the list. The errors are shown in the right panel with details about their cause, as illustrated below.

profile error profile error

Projects and De-identification Secrets

This page lists all projects configured in Karnak and lets you create, edit, and delete them. A project is linked to a profile and contains a secret used for de-identification.

Projects page

1. Create a project

To create a new project:

  1. Click the New project button to open the creation popup.
  2. Enter a name.
  3. Select a profile.
  4. Click Add.

New Project New Project

The project is added to the list and its details appear in the right panel.

2. Create a group

When the list grows, you can organize projects into groups (a single level of folders).
Click Add group to create one, drag a project onto a group to assign it, and right-click a group or project to rename, delete or remove it from its group. Grouping is purely organizational and is described in more detail on the Gateway page.

3. Project list

All available projects are listed in the left panel.
Selecting a project displays its details on the right.

4. Project details

In the details view you can:

  • Change the project name.
  • Change the de-identification profile.

Click Update to save your changes.

5. Project secret

The project secret is central to Karnak’s de-identification process.
It is a 16-byte value written as 32 hexadecimal characters.

  • A secret is automatically generated when the project is created.
  • In the details view, the secret is shown along with its creation date and time.

You can generate a new secret by clicking Generate Secret, or type/paste your own 32-character hexadecimal value into the secret field. When you do this:

  • Previous secrets are kept in the database.
  • You can select any previous secret to use again.

Project secret history Project secret history

Warning

Changing the project secret can cause data consistency issues between old and new de-identified DICOM instances. Use this feature with caution and only when necessary.

Since this modification can break the de-identification process, a warning is displayed to confirm this change. Project secret warning Project secret warning

A destination is associated with a project for de-identification, as described in the Destination configuration.

To generate new values like UIDs and pseudonymize some patient information, Karnak uses a hash function seeded with the project secret. This makes the generated values unique per project and deterministic as long as the secret does not change.

Implications of changing the secret:

  • If a DICOM instance is de-identified twice using the same project, secret, and profile, the resulting de-identified instances are identical.
  • If the secret changes, de-identifying the same DICOM instance again with the same project and profile but the new secret will produce different de-identified instances.

Details about the algorithm and UID generation using the project secret are available here.

6. Action buttons

  • Click Update to save any change made to a project. If the project is already used by a destination, a confirmation is requested before the change is applied.
  • Click Delete to delete the selected project.

If the project is associated with a destination, deletion fails and an error message is displayed.

Delete error Delete error

Import Pseudonyms from a CSV File

This page allows you to create or import pseudonyms that Karnak will use during de-identification. The de-identification process and how pseudonyms are used is detailed in the Pseudonym chapter.

De-identification is activated in the Destination configuration.

Info

Pseudonyms created or imported on this page are stored in a cache with a maximum lifetime of 7 days.

With the portable version of Karnak, the cache is persistent only the time the application is running. Restarting the application will clear the cache.

External Pseudonym Page

1. Choose a project

External pseudonyms are linked to a specific project. This allows Karnak to properly handle cases where:

  • A patient participates in multiple clinical studies with different pseudonyms
  • Potential pseudonym collisions occur between projects

2. Upload a CSV file

You can upload a CSV file containing external pseudonyms by:

  • Clicking the Upload File… button
  • Dragging and dropping a file onto the upload area

2.1 CSV separator configuration

After selecting a file, a dialog appears asking for the CSV separator character. The default value is a comma (,).

Click Open CSV to proceed to the import configuration.

External pseudonym separator dialog External pseudonym separator dialog

2.2 Preview and configuration

The CSV data is displayed in a grid for review and configuration.

External Pseudonym CSV Import

1. From line: This field defines the starting row for the import. Use this to skip header rows or other non-data lines at the beginning of the file.

2. Column assignments: Map each CSV column to the corresponding attribute to ensure that the data is properly imported.
These fields are initially empty. This mechanism allows flexibility on the content of the CSV file being imported.
Only the Patient ID and External Pseudonym fields are required. Other fields are optional.

3. Data preview: The data that will be imported as pseudonyms is displayed in the table.

2.3 Import validation

Click Upload CSV to import the data. The External Pseudonym and Patient ID columns must be assigned; otherwise an error lists the missing fields and nothing is imported.

Entries are identified by their Patient ID (and Issuer of patient ID) within the selected project. A row whose patient already exists is skipped and listed in a WARNING: Duplicate data dialog; the other rows are imported normally. The same check applies when a patient is added manually.

3. Pseudonym Actions

3.1 Add a new pseudonym

You can also add external pseudonyms manually. Click the Add patient button above the table to open a popup containing the following fields:

  • External Pseudonym (required)
  • Patient ID (required)
  • Patient first name (optional)
  • Patient last name (optional)
  • Issuer of patient ID (optional)

Fill in the fields and click Add in the popup to add the entry to the external pseudonyms table.

External pseudonym add External pseudonym add

3.2 Delete all patients

The Delete all patients button removes, after confirmation, all external pseudonyms for the selected project only. Pseudonyms linked to other projects are not affected.

4. Pseudonym management

4.1 Edit or delete individual entries

A filter row under the column headers narrows the table to the matching entries.

Each pseudonym row has action buttons:

  • Edit: Modify the patient fields inline, then click Save or Cancel
  • Delete: Remove the pseudonym from the cache

External pseudonym edit External pseudonym edit

4.2 Bulk operations

Select multiple rows using the checkboxes on the left side of each row. Click Delete selected patients to remove all selected entries and confirm the action in the dialog.

Monitor DICOM Transfers and Their Status

monitoring view monitoring view

The Monitoring view tracks transfers in progress and the recent transfer history. Instead of a flat, paginated log of individual instances, activity is aggregated as a Destination β†’ Study β†’ Series hierarchy, so you can see at a glance how each study was forwarded and drill down only where there are problems.

Info

What changed in version 2.0. The Monitoring module was redesigned. The earlier flat, paginated log of individual instances is replaced by a tree down to the series level (Destination β†’ Study β†’ Series), with one error line per failing series. Transfers are now aggregated per series instead of stored one row per instance, which keeps the view compact and makes problems easier to locate. As a result, retention is now time-based (a number of days) rather than a fixed maximum number of transfers.

A shared filter bar at the top applies to two tabs:

  • Activity β€” the Destination / Study / Series tree, with the details of any selected element shown on the right.
  • Dashboard β€” per-forward-node activity totals.
Info

By default, monitoring records are kept for 30 days; an automatic cleanup runs every hour to remove older entries. Because entries are now aggregated at the series level (rather than one row per instance), this history can span much longer than before for the same storage. The retention period is configurable by the administrator with the monitoring.max-history-days setting.

Filters

The filter bar is shared by both tabs:

Filter Description
Range A time range for the activity: Last 5 minutes, Last 15 minutes, Last hour, Last 24 hours, Today, Last 7 days, Last 15 days, All, or Custom (pick your own start/end). Last 24 hours is selected by default.
Status The outcome to show: All, Sent, Not Sent, Excluded, or Error.
Study UID Filter on a Study Instance UID (matches the original or de-identified value).
Series UID Filter on a Series Instance UID (matches the original or de-identified value).

Transfer statuses

The status of each element is color-coded:

  • OK (green): the instances were successfully sent.
  • Excluded (orange): some instances were not sent because of configuration rules β€” the SOP Class filter, destination conditions, or ExcludeInstance() in the profile. The badge shows the number of excluded instances; expand the series to see one line per reason.
  • Error (red): some instances were not sent because of an unexpected error. The badge shows the number of errors; expand the series to see one line per error reason.

Activity tab

The Activity tab is split between the hierarchy tree (left) and the detail panel (right).

monitoring view monitoring view

Hierarchy tree

Each row in the tree shows the Destination / Study / Series name and the counts of Studies, Series, Instances, Retries and Errors, plus an overall Status badge. Expand a destination to see its studies, and a study to see its series; failing series expose one line per error reason.

The outcomes are counted in separate buckets rather than a single error total:

  • Instances β€” the number of instances received for that element.
  • Retries β€” instances re-received after already being seen (duplicate re-sends); they are counted here instead of as new work.
  • Errors β€” instances that could not be sent because of an unexpected error.

Instances excluded by configuration (filters or aborted transfers) are reflected in the Status badge and reported as a separate Excluded count in the detail panel and on the dashboard.

The toolbar below the tree provides, from left to right:

  • Expand errors β€” expand the tree down to every failing series so problems are immediately visible (the button then becomes Collapse errors).
  • Export β€” see Export below.
  • Refresh β€” reload the tree with the latest data.
  • Delete All β€” permanently remove every monitoring entry (a confirmation is required; this cannot be undone).
Info

Delete All also clears the per-series baseline used for notification reporting, which is stored alongside the monitoring history. Email reporting simply restarts from zero afterwards: no previously forwarded outcome is re-notified, but a new transfer to a series that existed before the deletion is reported as new. The automatic retention cleanup removes aged-out entries the same way.

Detail panel

Select any element in the tree to see its details on the right:

  • For a destination: the forward AE Title, the destination label, and the Studies / Series / Instances / Retries / Sent / Errors / Excluded counts.
  • For a study: patient and study attributes shown as original vs sent (de-identified) pairs β€” Patient ID, Study UID, Accession number, Description and Study date β€” followed by the same Series / Instances / Retries / Sent / Errors / Excluded counts.
  • For a series or an error: the corresponding identifiers, the per-outcome counts and the error reason.

A Copy button copies the whole detail block to the clipboard.

Below are two examples, the first one successfully de-identified and sent at the study level, and the second one, not sent because an error occurred.

Monitoring detail success Monitoring detail error
Info

When a value was changed by de-identification, the panel shows the original value and a second (de-identified) line with the value that was actually sent, so you can compare the two.

Dashboard tab

The Dashboard summarizes activity per forward node over the selected range. Summary cards at the top total the Studies, Series, Instances, Retries, Sent, Errors, Excluded, De-identified and Tag-morphed, and a grid breaks the same figures down by Forward AETitle.

Monitoring dashboard tab Monitoring dashboard tab

Export

You can export the activity to a CSV file. The export respects the currently active filters, so only the matching transfers are included. One row is written per series, with the original and de-identified identifiers, the Instances / Retries / Sent / Errors / Excluded counts, the outcome reasons and the first/last-seen timestamps.

Click Export to open the Export CSV dialog. The only setting is:

  • Separator: the single character used to separate fields (a comma by default).

Monitoring export dialog Monitoring export dialog

Then click Download to generate and download the CSV file.

DICOM and DICOMweb Test Tools

The DICOM Web Tools module groups the utilities used to test connectivity, query servers, manage reusable endpoint configurations, and diagnose DICOM and DICOMweb nodes. The modules are accessed through tabs at the top of the page:

  • DICOM Echo β€” test a single DICOM node and probe its capabilities
  • DICOM Worklist β€” query a Modality Worklist server and display the results
  • Manage DICOM Nodes β€” store, group and import/export DICOM node configurations
  • DICOMweb β€” test a single DICOMweb (STOW-RS / QIDO-RS / WADO-RS …) endpoint
  • Manage DICOMweb β€” store, group and import/export DICOMweb endpoints
  • Monitor β€” run health checks against a whole group of DICOM nodes or DICOMweb destinations at once
Info

DICOM nodes and DICOMweb endpoints are persisted in the database and managed directly from the web interface (the Manage tabs). The active Gateway destinations are also exposed read-only in these tools, so you can echo or probe them without re-typing their connection details.

DICOM Echo

This tool tests DICOM communication with a single Application Entity (AE) and, on the same screen, lets you probe what the node accepts.

Configuration fields

Field Description Required
Calling AE Title Identity of the calling DICOM entity (Karnak) Yes
Called AE Title Identity of the DICOM entity to test Yes
Called Hostname Hostname or IP address of the DICOM node Yes
Called Port Port number of the DICOM node (1–65535) Yes

The Check DICOM Node button stays disabled until all fields are valid.

Use Select DICOM Node to pick from the stored DICOM nodes (and the gateway destinations) instead of typing the values; the Called AE Title, Hostname and Port fields are filled automatically.

DICOM Echo test and result DICOM Echo test and result

First select a Group to filter the nodes. All the defined groups are displayed as well as All Worklist nodes and All Workstation nodes, grouping the nodes by type. The group Gateway destinations contains all the nodes configured in the Forward Nodes destinations. Then select the node from the DICOM node dropdown.

Reset Form clears the form.

Running the test

Click Check DICOM Node to run the test. The result is shown in a grid; select the row to view the details. Each result combines two checks:

  • DICOM Echo β€” the C-ECHO (verification) result, with the DICOM status, the connection time and the execution time (in milliseconds). A rejected association or a node that does not support verification is reported with the reason.
  • Check Network β€” a low-level reachability check run alongside the echo: hostname (DNS) resolution, ping result, and whether the host is listening on the port.

DICOM Echo test and result DICOM Echo test and result

Below the current result, a History panel lists the previous checks (most recent first, with a count in its title). It is stored server-side in the database, so checks run earlier β€” or in another session β€” remain visible after a page reload; select a row to view that check’s details. Only the most recent checks are kept (200 by default, tunable by the administrator with application.echo.history.limit).

DICOM Capabilities

From the result grid, the Probe button (in the Capabilities column) runs a non-invasive probe that asks the node which SOP Classes and transfer syntaxes it accepts β€” no image data is exchanged. A dialog opens and lists the accepted SOP classes (grouped by category) with the negotiated transfer syntaxes, along with the maximum PDU length and the remote implementation (version name and class UID). This is useful to confirm a destination will accept the modalities you intend to send before configuring a forwarding pipeline.

DICOM capabilities probe DICOM capabilities probe

DICOM Worklist

This tool retrieves and displays the content of a Modality Worklist (MWL) server to test connectivity and data retrieval.

Worklist configuration

Field Description Required
Calling AE Title Identity of the calling DICOM entity Yes
Worklist AE Title Identity of the worklist server to query Yes
Worklist Hostname Hostname or IP address of the worklist server Yes
Worklist Port Port number of the worklist server Yes

Optional filter fields narrow the returned entries; if no filter is specified, all worklist entries are retrieved. Use Select Worklist Node to fill the connection fields from a stored worklist node.

Click Run Query to execute the query. Results are displayed in a sortable table β€” click a column header to sort by that column.

DICOM Worklist query results DICOM Worklist query results

The details of a worklist entry can be displayed by clicking on that row.

DICOM Worklist query results details DICOM Worklist query results details

The full DICOM attributes of the selected worklist entry can be displayed in a dialog by clicking the View DICOM Details button. It can also be downloaded as text (Download Text) or as a DICOM file (Download DICOM).

DICOM Worklist dicom details DICOM Worklist dicom details

Manage DICOM Nodes

This tab is the central place to store the DICOM nodes you test or monitor regularly.

DICOM node management DICOM node management

The grid lists each node with its Description, AE Title, Hostname, Port, Node Type and Group, plus per-row Edit and Delete actions.

Info

The active Gateway destinations appear in this grid as read-only rows. They reflect your forwarding configuration and cannot be edited, deleted, or replaced by an import.

Adding and editing a node

DICOM node management DICOM node management

Click Add Node (or the edit action on a row) to open the editor:

Field Description
Description A human-readable name for the node
AE Title The node’s Application Entity Title
Hostname Hostname or IP address
Port DICOM port
Node Type What the node is used for (e.g. workstation)
Group Optional β€” pick an existing group or type a new one

The Group is an organizational label: it lets you collect related nodes together and check them all at once from the Monitor tab.

Import / Export

DICOM node management DICOM node management

The Import / Export button opens a dialog to move node configurations in and out of Karnak as CSV:

  • Import β€” upload a CSV file. Optionally choose an Import into group (leave blank to use the group column from the file). Tick Replace existing in scope to delete the nodes currently in the target group before importing. Replacing without a target group deletes every node (worklist nodes included) and asks for an explicit confirmation first. An import report summarizes how many nodes were imported or removed and lists any skipped rows.
  • Export β€” download the current nodes as dicom-nodes.csv. Choose an Export group to limit the file to one group, or leave it blank to export every group.

DICOMweb

This tool tests a single DICOMweb endpoint and reports its reachability, TLS certificate and per-service availability.

Configuration

Field Description
DICOMweb base URL The base URL of the service, e.g. https://host:443/dicom-web
Services to probe Which DICOMweb services to check; leaving all unchecked means all services
Saved endpoint Optional β€” pick a stored endpoint to fill the form
Group Optional filter shown when more than one endpoint group exists

The probed services are the DICOMweb (PS3.18) RESTful services:

Service Role
STOW-RS Store
QIDO-RS Query
WADO-RS Retrieve
WADO-URI Retrieve (legacy)
UPS-RS Worklist
Capabilities Discovery

Running the check

Click Check URL to probe the single endpoint, or Check group to probe a whole group of managed endpoints (including the gateway STOW-RS destinations) at once. An existing endpoint can be selected in the field Saved endpoint if a group (other than Gateway destinations) is selected, to test a single endpoint rather than the entire group.
The result panel shows an overall Reachable / Unreachable badge followed by the details:

  • TCP β€” whether a TCP connection to the host and port succeeded.
  • HTTP β€” the status returned by the OPTIONS request, or a note if the endpoint did not respond.
  • TLS (HTTPS only) β€” the negotiated protocol, the certificate subject/issuer, the expiry date and the number of days until expiry, and whether the certificate is trusted. An expired certificate makes the check fail.
  • Services β€” one line per probed service indicating whether it is supported.
  • Authentication β€” for endpoints with an authentication configuration, whether an OAuth 2.0 access token could be obtained.

DICOMweb endpoint check result DICOMweb endpoint check result

Click Save as endpoint to store the current URL and selected services as a reusable endpoint (see Manage DICOMweb).

Manage DICOMweb

This tab stores the DICOMweb endpoints you test or monitor regularly.

The grid lists each endpoint with its Description, URL, Services and Group, plus per-row Edit and Delete actions. As with DICOM nodes, the gateway STOW-RS destinations appear as read-only rows and belong to the group Gateway destinations.

Manage DICOMweb endpoints Manage DICOMweb endpoints

Adding and editing an endpoint

Add a new DICOMweb endpoints Add a new DICOMweb endpoints

Click Add Endpoint (or the edit action on a row) to open the editor:

Field Description
Description A human-readable name for the endpoint
DICOMweb base URL The base URL of the service
Services to probe Which services to check (none selected means all)
Group Optional β€” pick an existing group or type a new one

Import / Export

Import DICOMweb endpoints Import DICOMweb endpoints

The Import / Export button behaves like the DICOM nodes one: import endpoints from a CSV (optionally into a chosen group, optionally replacing the existing endpoints in scope), or export the current endpoints to dicomweb-endpoints.csv.

Monitor

The Monitor tab runs the same checks as the Echo and DICOMweb tools, but against a whole group at once β€” ideal for regular health checks of your DICOM infrastructure. A second row of tabs switches between DICOM Nodes and DICOMweb.

DICOM Nodes

Monitor DICOM Nodes Monitor DICOM Nodes

  1. Pick a Group of nodes to check.
  2. Enter the Calling AE Title (defaults to PACSMONITOR).
  3. Click Check DICOM Nodes.

The result grid shows one row per node with its DICOM Echo status, connection and execution times, and network reachability. The Probe button (in the Capabilities column) is available on each row to probe a node’s accepted SOP classes.

DICOMweb

Monitor DICOM Nodes Monitor DICOM Nodes

  1. Optionally pick a Group of DICOMweb destinations (leave blank to check all).
  2. Click Check DICOMweb.

The result grid shows one row per destination with its reachability, HTTP status and TLS certificate state. The gateway STOW-RS destinations are included unless a specific managed group is selected.

Info

The Monitor tool helps you spot connectivity, certificate or capability issues before they affect production transfers.

Administrator settings

The timeouts used by the Echo, Monitor and DICOMweb checks can be tuned with the following application properties (milliseconds):

Setting Default Description
dicom-tools.echo.connect-timeout-ms 3000 TCP connection timeout of the C-ECHO and capabilities probe.
dicom-tools.echo.accept-timeout-ms 5000 Association acceptance timeout of the C-ECHO and capabilities probe.
dicom-tools.network.ping-timeout-ms 3000 Per-reply timeout of the ping in the network check.
dicom-tools.network.port-timeout-ms 3000 TCP timeout of the DICOM port reachability probe.
dicom-tools.web.timeout-ms 5000 Connect / TLS / HTTP timeout of the DICOMweb probe.

DICOM Conformance Reports by Email

Karnak can validate every study sent to a destination against the DICOM standard and email an HTML conformance report. This helps detect silent conformance problems β€” unsupported SOP classes, malformed values, inconsistent attributes across a study β€” before or while data is forwarded.

The feature is configured per destination. It can be combined with normal forwarding (validate and send), or used on its own with a virtual destination that validates and reports only, without sending anything.

Enabling the report

Conformance reporting is enabled in the destination editor. See the Destinations page for the exact location of the settings.

DICOM conformance report options DICOM conformance report options

Setting Description
Build DICOM conformance report Turns the feature on for the destination.
Conformance report: list of emails Comma-separated recipients. Leave empty to reuse the destination’s notification email list.
Check value content conformity (VR rules) Also report values that violate their VR length or format rules (PS3.5) β€” e.g. an over-long string or a malformed date.
Deep sequence validation (SR, functional groups) Recurse the checks through every sequence level (e.g. the SR content tree or enhanced multiframe functional groups) instead of only the first one. The recursion is bounded by a defined depth limit (conformance-report.max-sequence-depth, 8 by default). Off by default, as deeper recursion increases memory use.
Check for identifying data burned into the image Run OCR on each forwarded image through the external de-identification image service and list, in the report, which patient-identifying DICOM tag values are still visible in the pixel data. The OCR runs on the image actually sent (after any masking), so it verifies that no identifying text leaves Karnak; to audit the incoming image instead, enable it on a virtual destination. Off by default. Requires the service to be reachable at OCR_URL (default http://localhost:8000); when it is not, the images are reported as not analyzed and the rest of the report is unaffected.

The report is delivered by email, so Karnak’s outgoing mail server must be configured, as for the destination notifications.

Info

A virtual destination is report-only: it always builds a conformance report β€” that is its only output β€” so the report cannot be turned off while the destination is virtual.

When the report is sent

Reports are generated per study. Karnak accumulates the validation results of every instance of a study as they are processed, and emails one report once the study’s transfer goes idle β€” i.e. no new instance has arrived for that study within the idle timeout (5 minutes by default). A study is also flushed if it exceeds a maximum lifetime (4 hours by default), to guard against studies that never complete, and pending reports are flushed when Karnak shuts down. Studies are grouped per source forward node, destination and Study Instance UID β€” independently of the DICOM associations used β€” so a source sending to several destinations gets one report per destination, and instances arriving after a study’s report was sent produce a small follow-up report.

Info

If a study has no recipient email β€” neither a conformance report list nor a fallback notification list β€” its report is dropped (and the event is logged). Make sure at least one email address is configured.

These timings are tunable by the administrator with the following settings:

Setting Default Description
conformance-report.idle-timeout-seconds 300 Idle delay after the last instance of a study before the report is sent.
conformance-report.max-study-lifetime-seconds 14400 Maximum lifetime of a study batch before it is flushed regardless of idleness.
conformance-report.max-sequence-depth 8 Maximum sequence recursion depth used when Deep sequence validation is enabled.

How the report is built

Each instance is validated against the IOD of its SOP Class. An Information Object Definition (IOD) is the part of the DICOM standard that specifies, for a given object type (e.g. an MR Image, a CT Image, a Structured Report), which modules and attributes the object must contain and how strict each one is.

The checks use a bundled, machine-readable copy of the DICOM standard extracted by the Innolitics dicom-standard project (the list of SOP Classes, their modules, and every attribute with its VR, value multiplicity and Type), complemented by a curated rule set maintained in Karnak for the parts the standard only expresses as prose (enumerated values and a subset of machine-evaluable conditions).

IOD structure (modules, tags, VR, Type)

For the instance’s SOP Class, Karnak resolves the applicable modules and checks:

  • Module presence β€” a mandatory (usage M) module whose required attributes are all absent is reported as missing.
  • Attribute Type β€” each attribute’s requirement is enforced:
    • Type 1 β€” must be present and carry a value (present-but-empty is an error).
    • Type 2 β€” must be present, but may be empty.
    • Type 3 β€” optional.
  • VR (Value Representation) β€” the encoded VR must match the one the standard defines for the attribute.
  • VM (Value Multiplicity) β€” the number of values must fall within the range the standard allows.

Conditional requirements (Type 1C / 2C)

Some attributes are conditionally required (Type 1C / 2C, or attributes inside a usage-C module) β€” mandatory only when a condition holds (for example, “required if the image is a localizer”). Karnak evaluates these when possible: when a curated, machine-evaluable condition resolves to true for the instance, the attribute is treated as mandatory (1C is checked like Type 1, 2C like Type 2).

Info

Limitation. In the standard, these conditions are free text and many are clinical in nature, so they cannot all be evaluated automatically. When a condition has no curated machine-evaluable rule β€” or it evaluates to false/unknown β€” the attribute is only validated if it is actually present; its conditional requirement is not enforced. Karnak therefore never raises a false conformance error on a condition it cannot reliably evaluate.

Private tags

Private data elements are checked for block structure only (PS3.5 Β§7.8): every private data element must belong to a block reserved by a non-empty Private Creator element. A private element with no reserving creator, or an empty Private Creator, is reported.

Info

Limitation. Private values are not interpreted or validated β€” Karnak does not carry vendor-specific private dictionaries, so only the standard-defined block structure is verified.

Other checks

Beyond the IOD structure, Karnak runs a set of value-level and cross-attribute checks modeled on those of David Clunie’s dciodvfy verifier. The table lists what each one verifies and the severity it raises; a few are optional (value-conformity option) or deep (deep-sequence option), as noted.

Check What it verifies Severity
Enumerated values / Defined Terms Value lies within a closed Enumerated Values set; an unexpected Defined Term is a softer signal. Error / Warning
Plausible values No value of zero where it is physically impossible β€” structural counts & geometry vs. acquisition/physics parametersΒΉ β€” and no negative Spacing Between Slices (Nuclear Medicine excepted). Error / Warning
Pixel geometry Pixel Aspect Ratio of 1:1 is omitted; Pixel Spacing matches Imager / Nominal Scanned Pixel Spacing unless a Pixel Spacing Calibration Type explains the difference. Error / Warning
Image & patient orientation Image Orientation (Patient) is two unit, orthogonal direction-cosine vectors; Patient Orientation uses valid biped codes, with no opposing directions in one value and distinct row/column directions. Error
Laterality A paired (non-midline) Body Part Examined carries a Laterality, unless one is already conveyed or the object type makes it inapplicable. Warning
Coded entries A Code Value uses only the characters its Coding Scheme Designator allows (SNOMED / DICOM) and does not denote the “Unknown” concept. Warning
Identifier reuse The SOP Instance, Series, Study and Frame of Reference UIDs all differ from one another. Error
Residual identifiers No direct identifier remains after de-identification (telephone, address, other names, institution / physician names, occupation, comments…).Β² Warning
Standard Extended SOP Class A standard attribute present but not part of the object’s IOD. Info
Retired constructs Attributes (Info), SOP Classes or transfer syntaxes (Warning) retired in the current standard. Info / Warning
Value content conformity (optional) String values obey their VR length and format rules (PS3.5 Β§6.2). Error / Warning
Enhanced multi-frame & segmentation Per-frame Functional Groups item count equals Number of Frames; Segment Numbers increase monotonically from one (LABELMAP excepted). (deep) Dimension Index Values match the Dimension Index Sequence, and no functional group appears in both the Shared and a Per-frame group. Error
Study-level consistency Across the closed study: one Study Instance UID, one Patient ID & Name, consistent Frame of Reference, and Modality ↔ SOP Class coherence. Error / Warning
Info

ΒΉ Zero is an error for structural counts and geometry (Rows, Columns, Bits Allocated/Stored, Samples per Pixel, Number of Frames, Pixel/Imager Spacing, Slice Thickness, Rescale Slope) and a warning for acquisition/physics parameters where it is merely implausible (KVP, X-Ray Tube Current, Exposure, source distances, Echo/Repetition Time, Magnetic Field Strength, Flip Angle, Patient Weight/Size…).

Β² The residual-identifier check is specific to the de-identification use case (it goes beyond dciodvfy); its attribute list mirrors the identifiers removed by dciodvfy’s companion de-identifier, dcanon, and it is most useful on a de-identifying destination.

Scope notes

  • VR is not checked for Implicit VR Little Endian datasets, where the VR comes from the dictionary (the check would be circular).
  • By default only the top level and the first sequence level are validated; enabling Deep sequence validation walks deeper, bounded by the conformance-report.max-sequence-depth limit. Two enhanced multi-frame checks (Dimension Index Values count, and a functional group present in both the Shared and a Per-frame group) require this option, because they read per-frame functional groups.
  • Deep validation checks the encoding and the structural rules of nested attributes; it does not verify SR-specific semantics (template/TID conformance, content-item value-type rules, parent/child relationships) or whole-slide tile relationships.
  • SOP Classes the bundled standard does not define (unknown or retired) are flagged, and their IOD module checks are skipped.

What the report contains

The HTML email summarizes, for the study:

  • Study and patient context β€” patient ID and name, study date, description and accession number, source AE Title, and whether the data was de-identified.
  • Counts β€” number of series, instances, and failed instances, plus the totals of errors, warnings and informational findings, and an overall pass/fail.
  • Composition β€” the modalities, SOP Class UIDs and transfer syntaxes encountered.
  • Per-series summary β€” each series with its modality, SOP classes and instance count.
  • Findings β€” conformance findings grouped by SOP class (with an example instance and a count), and study-level consistency findings for attributes that should be identical across the study but are not.
  • Identifying data on image β€” only when Check for identifying data burned into the image is enabled: the patient-identifying tags whose value was found burned into the pixel data, with the number of images affected; otherwise how many images were analyzed, or could not be analyzed because the de-identification image service was unreachable.

Examples

The reports below are real, de-identified examples β€” three studies that fail conformance and one that passes β€” that illustrate the kinds of findings the validator produces. Each opens in a new tab as the actual HTML email.

Info

In every example the patient name shows as (hidden β€” destination not de-identified). The report only redacts the name when the destination’s data is not de-identified, to avoid emailing identifying information in clear text; the patient ID and other attributes are still shown.

Example 1 β€” missing module, pixel geometry and private-block issues

Ultrasound study Β· 6 series Β· 6 instances Β· FAILED β€” 3 errors, 6 warnings, 4 info

View report 1

A multi-frame ultrasound study that trips several unrelated rules at once. The errors are a missing mandatory module (the required Cine attributes are all absent) and two pixel-geometry violations (a Pixel Aspect Ratio of 1:1 is encoded when the standard says to omit it). The warnings cover three private blocks used without a Private Creator, a missing Type 2 Image Type, a retired SOP Class, and a mis-formatted date; the informational findings flag retired and non-standard (standard-extended) attributes. A spread like this usually reflects the acquisition device’s encoding conventions.

Example 2 β€” a passing study

Computed Radiography (CR) study Β· 1 series Β· 1 instance Β· PASSED β€” 0 errors, 8 warnings, 17 info

View report 2

This is what a study that meets the standard looks like: with no errors the overall result is PASSED, even though the report still lists warnings and informational findings. The warnings are implausible zero values β€” physics parameters such as KVP, Exposure and Patient’s Weight recorded as 0 β€” and a missing Laterality on a paired body part; none of these breaks the standard, so none fails the study. The informational findings are mostly standard-extended attributes (present but not part of this object’s IOD) plus one retired attribute. A “passed with warnings and info” result like this is normal and usually needs no action, though the warnings can be worth a look.

Example 3 β€” empty Type 1/1C, a VM violation and UID-format problems

Digital X-Ray (DX) study Β· 1 series Β· 1 instance Β· FAILED β€” 7 errors, 4 warnings, 1 info

View report 3

A good illustration that a single instance can surface several unrelated rule categories at once. The errors span presence (a missing mandatory module and four Type 1 / 1C attributes present but empty), value multiplicity (Patient Orientation carrying the wrong number of values) and an image-orientation rule. The warnings add a missing recommended attribute and three UID-format problems (identifiers that don’t follow the DICOM UID syntax). When one object trips this many distinct rules, the cause is usually upstream encoding rather than the individual values.

Example 4 β€” missing Type 1 attributes inside SR sequences

SR + CR study Β· 2 series Β· 2 instances Β· FAILED β€” 16 errors, 4 warnings, 3 info

View report 4

A mixed-modality study β€” a Basic Text Structured Report sent alongside a Computed Radiography image β€” whose SR is missing many mandatory elements. Fifteen of the sixteen errors are Type 1 attributes missing inside the SR content sequences (Graphic Data, Concept Code Sequence, Temporal Range Type…); the remaining error is an empty Type 1 LUT Data in the CR image. The warnings note two missing Type 2 attributes, a value-format issue and a missing Laterality, and the informational findings list retired and non-standard attributes. A deeply incomplete SR like this points to the application that generated it.

Relation to other DICOM validators

Karnak’s conformance report is not meant to replace the established standalone validators β€” it serves a different purpose. It validates automatically, inside the forwarding pipeline, and per study, so conformance is checked continuously without a separate tool or manual step. Because it sees the whole study batch, it can also run cross-instance consistency checks (single Study Instance UID, consistent Patient ID / Name, Modality ↔ SOP Class coherence, Frame of Reference) that single-file tools cannot.

For the per-object checks, Karnak builds on the same approach as the pydicom/dicom-validator: both derive the IOD requirements from a machine-readable copy of the DICOM standard (Karnak uses the Innolitics dicom-standard extraction) and both evaluate conditional (Type 1C/2C) requirements only when the condition can be evaluated, treating them as optional otherwise β€” so neither raises false errors on clinical, free-text conditions.

For the value-level and cross-attribute checks, Karnak additionally mirrors a substantial subset of dciodvfy: zero/ implausible values, pixel and image geometry, patient orientation, laterality, coded-entry validity, UID reuse and enhanced multi-frame / segmentation consistency (see Other checks). dciodvfy nonetheless remains the more exhaustive single-object reference β€” notably for byte-level encoding and full SR content-tree semantics, which Karnak does not attempt.

For an exhaustive, authoritative check of an individual object, or for interactive/standalone validation, the well-known dedicated tools remain the reference and complement Karnak well:

  • dciodvfy (David Clunie’s dicom3tools) β€” the long-standing reference single-object validator, with the most thorough encoding and value-level checks.
  • dcm4che dcmvalidate β€” validates against a supplied XML IOD definition.
  • DVTk β€” a toolkit that validates against editable DICOM definition files.
  • IHE Gazelle β€” the IHE testing platform, whose validation services can run several DICOM validators so their results can be compared.

Store Credentials for Karnak API Calls

This page allows you to configure authentication credentials for API calls. Since this information is sensitive, it is stored in an encrypted database table.

Authentication Configuration Page

1. Create an Authentication Configuration

To create a new authentication configuration:

  1. Click the New authentication config button to open the creation popup.
  2. Enter a unique identifier.
  3. Click Add.

authconfig create authconfig create

Requirements:

  • The identifier must be unique (an error will appear if a duplicate exists)
  • We recommend avoiding spaces in the identifier for easier reference in profiles

Click Add to proceed. The popup closes and an empty, editable configuration form appears in the right panel. The configuration is not saved yet β€” fill in the OAuth 2.0 fields described below and click Save to create it. Only then is it persisted and added to the list on the left. Click Cancel to discard it.

OAuth 2.0

Currently, OAuth 2.0 is the only supported authentication type.

Required fields:

Field Description
Access Token URL The OAuth 2.0 token endpoint
Scope Required scopes (separate multiple scopes with whitespace)
Client Secret OAuth 2.0 client secret
Client ID OAuth 2.0 client identifier

Once all required fields are filled, click Save to create the configuration and add it to the list; click Cancel to discard it.

How it works:

When an API call references this configuration (via the authConfig parameter):

  1. Karnak checks if a valid access token is available
  2. If yes, the token is added to the request for authentication
  3. If no, a new token is requested using the configuration details
  4. The API call proceeds with the authenticated token
Warning

Authentication configurations cannot be modified after creation. To make changes, you must delete and recreate the configuration.
The fields Client Secret and Client ID are masked in the interface for security purposes.

2. Authentication Configuration List

All available authentication configurations are displayed in the left panel. Select a configuration to view its details on the right.

3. Delete Configuration

To delete a configuration:

  1. Select it from the list
  2. Click the red trash bin icon next to its identifier
  3. Confirm the deletion in the popup

authconfig delete authconfig delete

Info

Deleting a configuration in use may cause errors in your workflows. Karnak does not verify whether a configuration is referenced by profiles or de-identification processes before deletion. Ensure the configuration is not in use before deleting it, or transfers will fail.

4. Configuration Details

The details view displays all configuration information in read-only mode. The fields Client Secret and Client ID are masked for security purposes.

To modify a configuration, you must delete it and create a new one.

Send De-identified Studies to a Kheops Album

Create a destination album

To create a Kheops album as a destination, configure the following values:

  • Protocol: STOW
  • DICOM endpoint: /api/studies

For detailed instructions on creating an album, refer to the official Kheops documentation.

Once the album is created, follow these steps to configure it as a destination in Karnak:

1. Create a new token

New token New token

2. Configure token permissions

Give WRITE permission to the token and set the expiration date.

Token permissions Token permissions

3. Copy the authentication token

Copy the authentication token value to use in the header of your Karnak destination.

Copy token Copy token

For complete instructions on creating and configuring a STOW destination in Karnak, see Destinations.

Switching to different Kheops albums

When a destination points to a Kheops album, data can be propagated to underlying albums within the same Kheops instance.

This feature is useful for sharing specific studies with research groups without exposing the entire album. For example, you can send a cohort of studies to collaborators while maintaining data isolation.

Info

Studies cannot be shared between different Kheops instances. You must configure a separate destination in Karnak for each Kheops instance.

This functionality leverages the Kheops API to distribute data to multiple locations without creating additional destinations in Karnak. Data is automatically split according to rules defined in Kheops, ensuring that authorized users access only relevant data.

The following diagram illustrates how data flows through Karnak and is distributed to multiple Kheops albums:

graph LR;
  A(DICOM Data) --> B[Karnak]
  
  subgraph Kheops
    D[Main Album] --> E[Album X]
    D --> F[Album Y]
  end
  
  B --> D

First, a DICOM instance is received by Karnak. After processing it, it sends the instance to the main Kheops album. Depending on existing rules and conditions, the instance will also be shared to the album X and Y. Sharing is done per series once the instance has been transferred; when the destination de-identifies, the series is shared with the de-identified Study and Series Instance UIDs.

Create a switching Kheops album

This feature is configured inside a STOW destination, not on a separate page. Open the STOW destination in Karnak and tick the Switching in different KHEOPS albums checkbox to reveal the entry form and the table of configured albums.

To share a DICOM instance in different Kheops albums, the following fields must be filled and validated by clicking on Add button.

Switching Switching

Fields Description
Url API The url of the Kheops API
Valid token of destination The token to write to the album destination. Need WRITE permission
Valid token of source The token to shared from the album source. Need READ, SEND (Sharing in the Kheops UI) permission

When Url API is filled, both tokens are checked against the Kheops API when you click Add: the destination token must have at least the write scope and the source token the read and send scopes, otherwise the album is not added. Configured albums are listed in a table where each row can be modified (Edit, then Validate or Cancel) or deleted (Remove).

The condition field defines a condition to enable sharing an instance to a specific album if it is evaluated to true.

The condition syntax and usage are detailed in the Conditions page.

Karnak Version and Runtime Information

The Help page is the last entry in the left-hand menu. It gathers quick links to this documentation and shows the version and runtime information of the running Karnak instance β€” useful when reporting an issue or checking what a deployment is running.

The top of the page links back to the online documentation, each opening in a new tab:

  • General documentation β€” the documentation home page.
  • User guide: configuration and administration β€” this User guide.
  • Build your own profile for de-identification or for tag morphing β€” the Profiles reference.

About

The About section lists the versions of Karnak and of its main runtime components, read from the running application:

  • Karnak β€” the application version (shows Development when run from a non-packaged build).
  • Vaadin, Spring, Spring Boot β€” the framework versions in use.
  • Java β€” the JVM version and vendor.
  • System β€” the operating system name, version and architecture.
Info

When opening a support request, copy the values from this section so the exact build and runtime can be identified.

Run Karnak Without Installing It

Karnak Gateway can be distributed as a portable application, allowing you to run it without installing it on your system.

The purpose of this distribution is to provide an easy way to use Karnak Gateway for de-identification tasks on a local machine. It is typically used by researchers in collaborative projects that require a consistent method for de-identifying DICOM data with a shared de-identification profile.

Warning

The portable distribution is intended for personal use only on a single machine. For multiple gateway configurations, when using Karnak as a production service, please use the standard installation method on a server and adapt the resource allocation accordingly.

Download Karnak portable distribution

System Arch. Size Portable package Comments
 Linux x86-64 194.7 MB karnak-linux-x86-64-jdk25-2.0.0.zip Requires GLIBC_2.17
 macOS arm-64 169.5 MB karnak-macosx-aarch64-jdk25-2.0.0.zip Requires macOS 12 or higher (Apple Silicon)
 Windows x86-64 185.3 MB karnak-windows-x86-64-jdk25-2.0.0.zip Requires Windows 10 or higher

Run Karnak Gateway portable

No installation is needed: extract the downloaded archive anywhere (for example in your Documents folder) and launch Karnak from the extracted folder.

​

Double-click on run.bat. A console window opens and shows the startup messages.

Open a Terminal in the extracted folder (in the Finder, right-click on the folder and choose Services β†’ New Terminal at Folder, or drag the folder onto the Terminal icon in the Dock) and run ./run.sh. There is no double-clickable launcher on macOS: Gatekeeper refuses to open a script downloaded from the Internet, whereas running it from the Terminal works without any warning.

Double-click on run.sh (choose Run in Terminal if your desktop asks) or run ./run.sh in a terminal opened in the folder.

What happens at launch:

  1. First launch only: the launcher asks whether to download the optional OCR service used by the automatic pixel de-identification. Answer Y to download it (an Internet connection is required), or n to skip it. Karnak works without it; only the automatic masking of text burned into the images is unavailable.
  2. Karnak starts, and after a few seconds the web portal opens in your default browser at http://localhost:8081. If it does not, open this address yourself.
  3. Log in with the default credentials:
    • Username: admin
    • Password: karnak

To stop Karnak, close the launcher window (or press Ctrl+C in the terminal).

Info

This web portal is accessible only from the local machine where it is running.

If the system already uses port 8081, you can change it by modifying the KARNAK_WEB_PORT value in the run.cfg file located in the extracted directory. Set KARNAK_OPEN_BROWSER=false in the same file if you do not want the browser to open automatically.

For advanced configuration options, you can edit the run.cfg file to adjust other settings (see below).

Specific folders in the extracted directory

logs: Contains the log files generated by Karnak Gateway during its execution (karnak.log, rotated by size).
dicom: Default folder where DICOM instances are stored when using the “LOCAL” destination.
data: Contains the embedded H2 database files used by Karnak Gateway to store its configurations. The database file is encrypted with a key generated on first launch and stored in the hidden .db_pwd file next to run.sh / run.bat. If you download a new version of the portable distribution, copy both the data folder and the .db_pwd file to retain your existing configurations.

Configuration (run.cfg)

The run.cfg file is read by run.sh / run.bat at startup; every uncommented NAME=value line is exported to Karnak. The main settings are:

Variable Default Description
KARNAK_WEB_PORT 8081 Port of the web portal (bound to localhost only).
KARNAK_OPEN_BROWSER true Open the web portal in the default browser once Karnak is started.
DICOM_LISTENER_AET / DICOM_LISTENER_PORT KARNAK-GATEWAY / 11112 AE Title and port of the gateway DICOM listener that receives the studies to forward.
DICOM_LISTENER_TLS false Listen in DICOM TLS; requires TLS_KEYSTORE_PATH, TLS_KEYSTORE_SECRET, TLS_TRUSTSTORE_PATH and TLS_TRUSTSTORE_SECRET (JKS files).
LOCAL_NODE_STORAGE_PATH ./dicom Folder of the local DICOM storage node used by the LOCAL destination. The local node is started only when both LOCAL_NODE_STORAGE_PATH and LOCAL_NODE_PORT are set.
LOCAL_NODE_AE_TITLE / LOCAL_NODE_PORT KARNAK-LOCAL / 11115 AE Title and port of the local storage node.
LOCAL_NODE_FILEPATH_PATTERN {00100010}/{00080060}/{0020000E}/{00080018}.dcm Path pattern of the stored files, built from DICOM tag values.
MAIL_SMTP_HOST, MAIL_SMTP_PORT, MAIL_SMTP_SENDER, MAIL_SMTP_TYPE, MAIL_SMTP_USER, MAIL_SMTP_SECRET (unset) Outgoing mail server used by the destination notifications and the conformance report. MAIL_SMTP_TYPE is SSL or STARTTLS when authentication is used.
DB_USER / DB_PASSWORD karnak / karnak User credentials of the embedded H2 database (the file encryption key is the generated .db_pwd).
KARNAK_LOGS_MAX_FILE_SIZE, KARNAK_LOGS_MIN_INDEX, KARNAK_LOGS_MAX_INDEX 50MB, 1, 10 Size and number of the rotated logs/karnak.log files.
OCR_ENABLED, OCR_AUTO_INSTALL, OCR_VERSION, OCR_MODEL, OCR_URL, OCR_SERVICE_NAME see below De-identification image (OCR) service.

User guide

For detailed instructions on how to use Karnak Gateway, please refer to the Karnak Gateway User Guide. This distribution includes new features in the Forward Node view that are not covered in the main user guide:

Forward node view of the portable distribution

Add a local destination

Click the LOCAL button A to add a destination that saves DICOM instances to the dicom folder in the portable distribution’s extracted directory.

Portable Local Destination Portable Local Destination

This button automatically configures a DICOM destination for local storage. For other options related to de-identification and forwarding rules, refer to the main user guide.

Upload local folder

Click the Upload local folder button B to send DICOM instances from a local folder through the forward node’s active destinations. The forward node must already have at least one active destination β€” otherwise a notification is shown and nothing happens. The button opens a dialog titled Upload DICOM files from a local folder where you type the absolute path to the folder in the Folder Path field.

Portable Local Folder Portable Local Folder

Automatic pixel de-identification service

The portable distribution can run the automatic pixel data de-identification feature, which masks burned-in patient data detected by an external OCR service (image-ocr-identifier). This service is not included in the portable archive, because it is large and optional.

Install the service

The easiest way is to answer Y when the launcher asks for it on the first launch: the launcher downloads the release of the service that matches your Karnak version (pinned by OCR_VERSION in run.cfg) and installs it in the image-ocr-identifier folder next to run.sh / run.bat. Karnak then starts it automatically at every launch.

If you answered n, you can install it later by setting OCR_AUTO_INSTALL=always in run.cfg and restarting Karnak, or manually:

  1. Open the releases page of the service and pick the release named in OCR_VERSION.
  2. Download the archive for your system: image-ocr-identifier-<model>-windows-x86_64.zip, image-ocr-identifier-<model>-macos-arm64.tar.gz or image-ocr-identifier-<model>-linux-x86_64.tar.gz, where <model> is the value of OCR_MODEL (PP-OCRv5_mobile by default).
  3. Extract it next to run.sh / run.bat. The archive contains a single image-ocr-identifier folder holding the service executable and its resources.

After installation the layout looks like:

karnak-portable/
β”œβ”€β”€ run.sh (or run.bat)
β”œβ”€β”€ run.cfg
└── image-ocr-identifier/
    β”œβ”€β”€ ...                    (service resources)
    └── image-ocr-identifier   (executable, image-ocr-identifier.exe on Windows)
Info

A prebuilt service is published for Windows (x86-64), macOS (Apple Silicon) and Linux (x86-64). On other systems the launcher skips the download and the automatic masking is unavailable.

Configure the service

The service is controlled in run.cfg:

OCR_ENABLED=true
### What to do when the service is not installed: ask | always | never
OCR_AUTO_INSTALL=ask
### Release of the service compatible with this version of Karnak (do not change)
OCR_VERSION=v0.1.0
### OCR model bundled in the downloaded service
OCR_MODEL=PP-OCRv5_mobile
### Base URL Karnak uses to reach the service
OCR_URL=http://localhost:8000
OCR_SERVICE_NAME=image-ocr-identifier
Variable Default Description
OCR_ENABLED true Start the de-identification service with Karnak. Set to false to disable it (and to stop being asked to download it).
OCR_AUTO_INSTALL ask When the service is missing: ask before downloading it, always download it without asking, never do nothing.
OCR_VERSION (pinned) Release of the service downloaded by the launcher. It is chosen to match the Karnak version; keep the value shipped in run.cfg.
OCR_MODEL PP-OCRv5_mobile OCR model bundled in the downloaded archive.
OCR_URL http://localhost:8000 Base URL Karnak uses to reach the service.
OCR_SERVICE_NAME image-ocr-identifier Name of the service folder and executable. The service lives in <app>/<name>/ and its binary is named <name> (<name>.exe on Windows).

When enabled, run.sh / run.bat starts the service on launch; on Linux and macOS, run.sh also stops it when Karnak exits (on Windows the service runs in its own minimized window, to be closed with it). If the binary is missing and the download is declined, the step is skipped and a message is logged.

Info

For details about the feature itself β€” eligible images, sensitive tags, fail-closed behavior, and enabling it in a profile β€” see Automatic Pixel Data De-identification.