Hosted single-tenant DICOM pseudonymization API wrapping dcm-anonymizer (PS3.15 Basic Profile)
Project description
dcm-anon-vault
A hosted, single-tenant DICOM pseudonymization API. Upload DICOM files over HTTP, get back a ZIP of pseudonymized outputs plus a tamper-evident audit log. Stripe billing is built in for pay-as-you-go tier enforcement.
Wording note — In DICOM and clinical-research practice the process is colloquially called "anonymization" (and the upstream engine ships as
dcm-anonymizer). Under EU GDPR (Recital 26 + WP29 Opinion 05/2014 + EDPB Guidelines 01/2025) the OUTPUT of PS3.15 Basic Profile is pseudonymized personal data, NOT anonymized. We use "pseudonymization" throughout this README and in all customer-facing copy; "anonymize" appears only as the technical verb on DICOM tags.
1. What it is
dcm-anon-vault wraps the dcm-anonymizer
engine (PS3.15 Basic Confidentiality Profile, PyPI:
dcm-anonymizer) in a
FastAPI service. You deploy one instance per customer (single-tenant),
point it at a SQLite volume, and give each customer an API key. The
service enforces per-tier file quotas, logs every pseudonymization
event with a SHA-256 audit chain, and integrates with Stripe for
upgrade billing.
All PHI scrubbing is done by dcm-anonymizer, which implements the
DICOM PS3.15 Basic Application Level Confidentiality Profile. No data
ever leaves the machine you deploy on.
UID re-mapping is deterministic per customer (we use
SHA-256(api_key) as the engine salt), so re-running the same source
study produces the same target SOPInstanceUID / PatientID — enabling
longitudinal cohort linkage that random UIDs would destroy.
2. Why hosted vs running the CLI locally
| Concern | CLI (pip install dcm-anonymizer) |
Vault (hosted) |
|---|---|---|
| Audit log retention | You manage the JSON file | Persisted to SQLite, queryable |
| Multi-user access | Manual key sharing | Per-customer API keys + tiers |
| Billing | Manual invoicing | Stripe Checkout built in |
| Deployment | Per-workstation install | Deploy once on Fly.io or your VPS |
| CI / pipeline integration | Possible but fragile | POST /v1/anonymize from any client |
If you process fewer than 50 DICOMs per month, the free tier is sufficient. If you need auditability across a team, or want to gate access by subscription, the hosted vault is the right tool.
3. Tiers (subject to change pre-1.0)
| Tier | Price | Quota | Isolation |
|---|---|---|---|
| Free | €0/mo | 50 files/month | Shared single-tenant |
| Pro | €99/mo | Fair-use 10K files/mo | Single-tenant (your own deploy) |
| Annual | €999/yr | Fair-use 10K files/mo | Same as Pro; ~17 % off |
| Enterprise | Contact | SLA, BAA, async jobs, SSO | Isolated VPS, dedicated support |
Free-tier customers receive 429 Too Many Requests with a Retry-After
header once they hit the monthly cap. Upgrading via Stripe Checkout
flips the tier in the database on signed-webhook receipt.
A 14-day Pro trial is enabled by default (STRIPE_TRIAL_DAYS=14).
4. Deploy in 5 minutes on Fly.io
Prerequisites: flyctl installed and authenticated.
git clone https://github.com/Ces107/dcm-anon-vault
cd dcm-anon-vault
fly apps create dcm-anon-vault
fly volumes create vault_data --size 1 --region cdg
fly secrets set \
DCM_API_KEYS="customer1:$(openssl rand -hex 32)" \
STRIPE_API_KEY="sk_test_REPLACE_ME" \
STRIPE_PRICE_ID="price_REPLACE_ME" \
STRIPE_PRICE_ID_ANNUAL="price_REPLACE_ME" \
STRIPE_WEBHOOK_SECRET="whsec_REAL_SECRET_HERE"
fly deploy
curl https://dcm-anon-vault.fly.dev/health
Required Stripe configuration before any paid customer:
- Monthly price →
STRIPE_PRICE_ID. - Optional annual price →
STRIPE_PRICE_ID_ANNUAL. - Webhook endpoint at
https://<your-app>/v1/billing/webhookpointing at the events:checkout.session.completed. Copy the signing secret intoSTRIPE_WEBHOOK_SECRET— the service refuses to process unsigned events.
5. API quick reference
# Pseudonymize one or more DICOMs (returns ZIP)
curl -X POST https://<host>/v1/anonymize \
-H "X-API-Key: <your-key>" \
-F "files=@scan.dcm" \
--output result.zip
# Check usage / quota for the current UTC month
curl https://<host>/v1/usage -H "X-API-Key: <your-key>"
# Start a Stripe Checkout upgrade (monthly or annual)
curl -X POST https://<host>/v1/billing/checkout-session \
-H "X-API-Key: <your-key>" \
-H "Content-Type: application/json" \
-d '{
"success_url":"https://example.com/success",
"cancel_url":"https://example.com/cancel",
"plan":"annual",
"customer_email":"buyer@example.com"
}'
Response headers from /v1/anonymize carry X-Files-Processed,
X-Files-Failed, X-Files-Rejected-BurnedIn, and X-Audit-Sha256.
6. Local development
cp .env.example .env # fill in real values
pip install -e ".[dev]"
uvicorn dcm_anon_vault.app:app --reload --port 8080
python -m pytest -q
python -m ruff check src tests
python -m mypy --strict src
7. Scope, disclaimers, regulatory posture
This is a research utility. It is NOT a medical device.
dcm-anon-vault is intended for the preparation of DICOM datasets for
research, software development, and educational use. It is not
intended to inform clinical diagnosis or therapeutic decisions and is
not a medical device under Regulation (EU) 2017/745 (MDR) Art 2(1)
nor under 21 CFR Part 820. If you intend to use it as a pre-processing
step in a clinical pipeline, the obligation to perform conformity
assessment falls on you as the deployer / modifier.
GDPR posture. PS3.15 Basic Profile is a pseudonymization
operation, not anonymization, per EDPB Guidelines 01/2025 on
Pseudonymisation and WP29 Opinion 05/2014. Output remains personal
data and must be handled accordingly. The hosted plane receives and
briefly processes raw PHI on the customer's behalf, which makes the
operator a processor under GDPR Art 4(8). A Data Processing Agreement
(DPA) is required before any EU customer can be onboarded; we publish
a template DPA at /legal/dpa (work in progress).
HIPAA posture. Receiving raw DICOM from a US Covered Entity makes the operator a Business Associate by operation of law (45 CFR 160.103), regardless of contract. Until a BAA programme is in place, the hosted service is not available to US Covered Entities. Self-host the service inside your own HIPAA-compliant environment instead.
Storage at rest. The audit log is stored in SQLite without
disk-level encryption. Customers requiring encryption-at-rest should
deploy on an encrypted volume (LUKS, KMS-backed EBS, Fly encrypted
volumes) and / or substitute Postgres with TDE via DCM_DB_URL.
What this software does NOT do. No burned-in pixel-data PHI
removal; we reject files declaring BurnedInAnnotation==YES with HTTP
422 rather than silently leak PHI. No SR (Structured Report) text-item
deep redaction (only the engine's PS3.15 actions). No IHE BIR / ATNA
audit message emission. No KMS-backed encryption. No Conformance
Statement (PS3.4 §2.2) — write to us if you need one for a hospital
procurement evaluation.
This README is the canonical scope statement. Marketing copy MUST match it.
Contact: plusultra.dev@proton.me · Issues: https://github.com/Ces107/dcm-anon-vault/issues
Copyright © 2026 César Pereiro García — MIT License. See NOTICE.md
for upstream attribution; SECURITY.md for vulnerability reporting;
CHANGELOG.md for release history.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file dcm_anon_vault-0.2.0.tar.gz.
File metadata
- Download URL: dcm_anon_vault-0.2.0.tar.gz
- Upload date:
- Size: 25.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cd4c848a0f38be076c37d8d6675b3af7fc290451db2ca08886275ae21caa6c1e
|
|
| MD5 |
8c75eeaf2eae898d184bfcf3b6e7660a
|
|
| BLAKE2b-256 |
8eadc01f80ba8fa3243f029123b2fe1e611e6a23c94812e2f67a2ae3088e2dc8
|
File details
Details for the file dcm_anon_vault-0.2.0-py3-none-any.whl.
File metadata
- Download URL: dcm_anon_vault-0.2.0-py3-none-any.whl
- Upload date:
- Size: 21.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.0
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2b2dc155408c82b6a5e6ea3b2661685016baae4177f15c2cbc20f2e2d80e2c5c
|
|
| MD5 |
18d1f2efda0fa0a71688ea2efafc9a8d
|
|
| BLAKE2b-256 |
23257021c3f0bdd0190c8d235bf13409359e773496b781cf8fb6733bc2495e44
|