Documentation

Deploying Clinical Extract as one container in your environment.

Clinical Extract is installed inside your environment. We build the image from the release source with the Dockerfile, keep it in your container registry and configure the EHR connection. It runs as one container on your compute, under your access controls. The signing key is kept in your key store, output is written to your storage, and the only outbound connections go to your EHR.

1 The install

One container, built and installed by us.

We install Clinical Extract inside your environment, in your cloud account or your data center. Each release is source with a Dockerfile: a node:22-alpine image that installs production dependencies and runs node src/server.js on port 3000, with a health check on /healthz. We build the image where it runs, keep it in your container registry when your platform needs one, and configure the EHR connection. The build stamps the release and the build time into the image, and /api/version reports them.

Deployment in your cloudA dashed boundary labelled your environment. Our engineers, outside it, install and configure Clinical Extract: they bring each release in as source with its Dockerfile, build the image inside the boundary, keep it in your container registry and configure the EHR connection. Inside the boundary, Clinical Extract runs as one container on your compute. It reads its RSA private key from your key store to sign the RS384 JWT, and writes the NDJSON downloads, study.csv and data-dictionary.json to your study storage. Its settings are the EHR’s OAuth base (the token endpoint and FHIR base derive from it), the client ID, the private key as a file or base64, each study’s Group ID, and the dashboard user and password. Its only outbound calls, highlighted, go over HTTPS on port 443 to your EHR, which sits on the boundary: POST to the token endpoint, GET Group/{id}/$export, GET the status URL and GET each file URL. The EHR holds the backend app registration: the client ID and Clinical Extract’s JWKS URL or public key.YOUR ENVIRONMENTOur engineersinstall and configureYour container registryimage built by our engineersClinical Extractone container, your computeYour EHRcertified bulk FHIR APIYour key storeRSA private keyStudy storageNDJSON, study.csv,data-dictionary.jsoneach release, source and Dockerfileimagesigning key, RS384writes the outputCLINICAL EXTRACT SETTINGSOAuth base (token, FHIR base)client IDprivate key, file or base64Group ID, per studydashboard user and passwordthe only outbound callsPOST {token endpoint}GET /Group/{id}/$exportGET {status-url}GET {file-url}HTTPS, port 443backend app:client ID,JWKS URL orpublic key
Deployment in your cloudA dashed boundary labelled your environment. Our engineers, outside it, install and configure Clinical Extract: they bring each release in as source with its Dockerfile, build the image inside the boundary, keep it in your container registry and configure the EHR connection. Inside the boundary, Clinical Extract runs as one container on your compute. It reads its RSA private key from your key store to sign the RS384 JWT, and writes the NDJSON downloads, study.csv and data-dictionary.json to your study storage. Its settings are the EHR’s OAuth base (the token endpoint and FHIR base derive from it), the client ID, the private key as a file or base64, each study’s Group ID, and the dashboard user and password. Its only outbound calls, highlighted, go over HTTPS on port 443 to your EHR, which sits on the boundary: POST to the token endpoint, GET Group/{id}/$export, GET the status URL and GET each file URL. The EHR holds the backend app registration: the client ID and Clinical Extract’s JWKS URL or public key.YOUR ENVIRONMENTOur engineersinstall and configureYour container registryimage built by our engineersClinical Extractone container, your computeKey storeRSA private keyStudy storageNDJSON, CSV,dictionaryYour EHRcertified bulk FHIR APIeach releaseimagekeywritesSETTINGSOAuth base (token, FHIR base)client IDprivate key, file or base64Group ID, per studydashboard user and passwordthe only outbound callsHTTPS, port 443, to your EHRPOST {token endpoint}GET /Group/{id}/$exportGET {status-url}GET {file-url}backend app:client ID,JWKS URL or public key
Figure 1. Deployment in your cloud. We install Clinical Extract inside your environment: our engineers build the image from each release with the Dockerfile and keep it in your container registry. It runs as one container on your compute, with its signing key in your key store and its output in your storage. Its outbound calls go to your EHR alone, over HTTPS.

2 Where

The same image on a VM, a managed container service or a small instance.

A VM with Docker Compose
The container binds to localhost; your reverse proxy (Caddy or nginx) terminates TLS, publishes the JWKS path and puts basic auth in front of everything else. The key is a file mounted read-only.
AWS App Runner
The image from your ECR repository, port 3000, health check /healthz; the key and the dashboard login as secrets; TLS and scaling included.
AWS Lightsail containers
A flat-rate container service; one script builds, pushes and deploys, with the same secrets.
A small EC2
The VM pattern on an instance in your AWS account, with the same Compose file and proxy.

3 Settings

Six settings per EHR connection.

Table 1. The settings, what each is, and where it comes from. One set per EHR connection.
SettingWhat it isWhere it comes from
OAuth baseThe EHR’s OAuth base for the connection; the token endpoint and the FHIR base derive from it (or the two are given as returned by the vendor).The EHR page for your vendor
Client IDThe backend app’s client ID the vendor issued at registration.The vendor’s app registration
Private keyThe RSA key that signs every token request, as a file mounted read-only or a base64 secret your platform injects.Generated in your environment at install; it stays there
Group IDThe study cohort’s FHIR Group, one per study.Your EHR team, or the cohort builder
Dashboard loginA user and password for the built-in login, when no reverse proxy fronts the container.You
Poll pacingThe minimum status-poll interval and the cap; Retry-After from the EHR wins when present.Defaults; change for testing

The table scrolls sideways.

4 Keys and the JWKS

The private key never leaves your environment.

The RSA key pair is generated where the container runs, during the install. The private key signs every token request (RS384) and is read from a file mounted read-only or from a secret your platform injects at boot. The public key is served at /.well-known/jwks.json with a key ID derived from the key itself, so the JWKS and the JWT header always agree; the vendor registers that URL, or takes the certificate as an upload. The JWKS answers with a five-minute cache header, so a rotated key is picked up promptly; the server log records every fetch of it.

5 Network, storage, log

Outbound only to your EHR, with one public inbound path.

Outbound
HTTPS on port 443 to your EHR: the token endpoint, Group/{id}/$export, the status URL, each file URL. Nothing reaches us.
Inbound
The JWKS path, public, for the EHR vendor; the dashboard and its API behind your proxy's authentication or the built-in login.
Storage
The data directory, mounted from your storage: the NDJSON downloads and each study's folder with study.csv and data-dictionary.json.
Log
A size-rotated server log in the same directory with each run step, token request and JWKS fetch, yours to keep and ship to your SIEM.

6 Questions

Questions about deployment

Where does the image come from?

We build it inside your environment. Each release ships as source with its Dockerfile (node:22-alpine). On a VM, we build and start it with docker compose up -d --build; for App Runner, Lightsail or EC2, we build it and push it to your container registry. The image stays in your environment.

What does the container reach out to?

Your EHR, over HTTPS on port 443: the token endpoint, the Group kickoff, the status URL and each file URL. Nothing else. The one inbound path that is public is the JWKS, which the EHR vendor fetches to verify the signed JWT.

How do we rotate the key?

We set up key rotation with your team during the install. To rotate, generate a new key pair in your environment, restart the container, and the vendor picks up the new key ID from the JWKS URL. Keep the previous key in the set briefly while assertions are in flight, then drop it. A vendor that took an uploaded certificate takes the new one the same way.

Where do the files go?

Into the container’s data directory, mounted from your storage: the NDJSON downloads, each study’s folder with study.csv and data-dictionary.json, and the size-rotated server log.

How do we know what is deployed?

/api/version answers with the release and the build time stamped into the image at build; /healthz answers {"ok":true} for your health checks.

Next

See Clinical Extract run on one of your studies.

Tell us which EHR you run and what the study or registry needs. We reply within one business day to set a meeting time.

Request a demo