Skip to content

About

Open-source Da Vinci CRD payer server in Go and .NET. Passes the Inferno CRD Server suites v2.2.1 and v2.0.1 with 0 failures.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Nirmitee Da Vinci CRD Server

CI License: Apache-2.0 CRD 2.2.1

An open-source, payer-side implementation of the HL7 Da Vinci Coverage Requirements Discovery (CRD) Implementation Guide, written twice with identical behavior: once in Go and once in .NET 10. Both implementations pass the Inferno Da Vinci CRD Server test suites for CRD 2.2.1 and CRD STU 2.0.1 with zero failures.

Why this exists

The CMS Interoperability and Prior Authorization Final Rule (CMS-0057-F) requires impacted payers to offer a Prior Authorization API, with most API requirements starting in January 2027, and CMS recommends the Da Vinci Burden Reduction implementation guides for it: CRD, DTR (Documentation Templates and Rules) and PAS (Prior Authorization Support).

CRD is the first step in that flow. While a clinician books an appointment or signs an order, the EHR calls the payer's CDS Hooks service, and the payer answers in real time: is this covered, is prior authorization needed, what documentation is required, and where to go next. This repository is that payer service. It is meant as a working reference that payers, EHR vendors and integrators can read, run, test against and build on.

Inferno results

Tested with the Inferno Da Vinci CRD Test Kit v0.14.2, JWT algorithm ES384, on 12 September 2026. Report summaries are in docs/results/.

Implementation Suite Run Pass Fail Skip (optional) Report
Go CRD v2.2.1 local, HTTP 135 0 * 7 summary
.NET CRD v2.2.1 local, HTTP 135 0 * 7 summary
Go CRD v2.0.1 local, HTTP 85 0 * 6 summary
.NET CRD v2.0.1 local, HTTP 85 0 * 6 summary
Go CRD v2.2.1 public inferno.healthit.gov, HTTPS 136 0 7 summary, session
Go CRD v2.0.1 public inferno.healthit.gov, HTTPS 86 0 6 summary, session
.NET CRD v2.2.1 public inferno.healthit.gov, HTTPS 136 0 7 summary, session
.NET CRD v2.0.1 public inferno.healthit.gov, HTTPS 86 0 6 summary, session

* Local runs use plain HTTP, so test 1.01 (TLS 1.2 or higher) cannot pass there. The raw local reports list it as the only failure and it is excluded from the count. On the public Inferno instance the servers sit behind an HTTPS tunnel and test 1.01 passes, which is why the public pass counts are one higher.

Screenshots from the public Inferno sessions

Captured on inferno.healthit.gov (CRD test kit v0.14.2) from the sessions linked in the table above.

CRD v2.2.1: whole suite passed (Go left, .NET right)

Go .NET
Go server passing the Da Vinci CRD Server v2.2.1 test suite on Inferno .NET server passing the Da Vinci CRD Server v2.2.1 test suite on Inferno

CRD v2.2.1: Discovery group, including test 1.01 TLS 1.2 or higher

Go .NET
Go server passing the CRD v2.2.1 Discovery group including the TLS test .NET server passing the CRD v2.2.1 Discovery group including the TLS test

CRD v2.2.1: Specific Coverage Responses (technical issues, no member, coverage not found, no active coverage)

Go .NET
Go server passing all four CRD v2.2.1 specific coverage response groups .NET server passing all four CRD v2.2.1 specific coverage response groups

CRD v2.0.1: whole suite and Discovery with TLS passed

Go .NET
Go server passing the Da Vinci CRD Server v2.0.1 test suite on Inferno .NET server passing the Da Vinci CRD Server v2.0.1 test suite on Inferno
Go server passing the CRD v2.0.1 Discovery group including the TLS test .NET server passing the CRD v2.0.1 Discovery group including the TLS test

The hook groups under "3 Hooks" are marked Optional by the test kit (a server chooses which hooks it supports), so Inferno shows them with a lighter check; every one of them passed.

The optional skips are validations for card types the sample payer rules do not return on the secondary hooks (Launch SMART Application, Request Form Completion and Propose Alternate Request cards on encounter-start, order-select and encounter-discharge). Passing Inferno is a conformance signal, not a certification.

Features

  • Six CDS Hooks services for each CRD version: appointment-book, encounter-start, encounter-discharge, order-select, order-sign and order-dispatch. CRD 2.2.1 is served under /v221/cds-services and STU 2.0.1 under /v201/cds-services, side by side in one process.
  • Discovery with prefetch templates for every service, plus the 2.2.1 davinci-crd.version and davinci-crd.configuration-options extensions.
  • All CRD response types: Coverage Information system action (ext-coverage-information), Instructions, External Reference, Launch SMART Application, Request Form Completion (Task plus Questionnaire), Propose Alternate Request, Additional Orders and Create or Update Coverage Information.
  • Coverage information on the primary hooks (appointment-book, order-sign, order-dispatch) for every focus resource: covered status, prior authorization need, documentation need and purpose, coverage assertion ids and satisfied-PA ids. Secondary hooks never ask for clinical or administrative information.
  • Specific coverage outcomes tested by v2.2.1 group 4: technical issues (EHR FHIR server errors), no member found, coverage not found and no active coverage.
  • Data acquisition: prefetch first, then parallel reads from the EHR fhirServer using fhirAuthorization, with a per-fetch timeout and classified failures. Echoed resources keep their original JSON key order.
  • Client authentication per CDS Hooks "Trusting CDS Clients": bearer JWTs signed with ES384 or RS384 (ES256 and RS256 are also accepted), an issuer allowlist, keys from the issuer JWKS (jku only under a trusted issuer), aud checked against the service URL including tunnels and proxies, exp and iat with clock skew, and a jti replay cache. Failures return 401.
  • Configuration options: coverage-info and per-topic options (limits, guideline, appropriate-use, therapy-alternatives-opt, clinical-reminder, insurance); unknown options, context fields and top-level fields are ignored.
  • Request validation: 400 invalid_request or invalid_context for malformed calls, 422 unprocessable_context for unusable context.
  • Rules as data: payer decisions, the synthetic member and card templates live in spec/payer-rules.json (16 sample rules), shared by both implementations.
  • Audit log: one JSON line per hook call (request, response, fetches, JWT iss/kid/jti, outcome, matched rules) in a daily file, with access tokens redacted.
  • Library plus host: no global state and a configurable route prefix, so the CRD module can be mounted next to DTR and PAS in one process later. Docker images run as a non-root user.

Architecture

Each implementation is a CRD library, a shared package for cross-cutting concerns and a thin host.

flowchart LR
  EHR["EHR as CDS client<br/>(Inferno during testing)"]
  RULES[("spec/payer-rules.json")]
  FHIR[("EHR FHIR server")]
  JWKS[("Client JWKS")]

  subgraph proc["CRD server process"]
    direction TB
    HOST["Host<br/>Go: cmd/crd-server<br/>.NET: Nirmitee.DaVinci.Crd.Host"]
    subgraph lib["CRD library (Go: crd, .NET: Nirmitee.DaVinci.Crd)"]
      DISC["Discovery"]
      VAL["Request validation"]
      ENG["Engine: focus resources,<br/>data, determination"]
      RESP["Response builder:<br/>cards and system actions"]
    end
    subgraph shared["Shared (Go: internal/shared, .NET: Nirmitee.DaVinci.Common)"]
      AUTH["JWT and JWKS auth,<br/>jti replay cache"]
      FCL["FHIR client and JSON helpers"]
      AUD["JSONL audit log"]
    end
  end

  EHR -- "CDS Hooks call with JWT" --> HOST
  HOST --> lib
  VAL --> AUTH
  ENG --> FCL
  ENG --> RULES
  RESP --> AUD
  AUTH -. "fetch keys" .-> JWKS
  FCL -. "when prefetch is missing" .-> FHIR
Loading

The same design carries over to the DTR and PAS servers, which are planned as separate repositories. The shared package will be extracted into a small module that all three use, and a combined host can then mount every module under its own prefix.

flowchart TB
  subgraph repos["One repository and deployable server per IG"]
    CRD["davinci-crd-server<br/>(this repository)"]
    DTR["davinci-dtr-server<br/>(planned)"]
    PAS["davinci-pas-server<br/>(planned)"]
  end
  COMMON["Shared module<br/>JWT and JWKS auth, FHIR helpers, audit log, config"]
  COMBINED["Combined Burden Reduction server (planned)<br/>/crd, /dtr and /pas in one process"]

  CRD --> COMMON
  DTR --> COMMON
  PAS --> COMMON
  CRD --> COMBINED
  DTR --> COMBINED
  PAS --> COMBINED
Loading

Mounting the CRD module in your own host:

cfg, err := crd.LoadConfig("", os.Getenv)
if err != nil { log.Fatal(err) }
cfg.Prefix = "/crd"
handler, err := crd.NewHandler(cfg)
if err != nil { log.Fatal(err) }
mux.Handle("/crd/", handler) // /crd/v221/cds-services, /crd/v201/cds-services
builder.Services.AddCrd(builder.Configuration);
app.MapCrd("/crd");   // /crd/v221/cds-services, /crd/v201/cds-services, /crd/health

The full behavior contract both implementations follow is spec/SPEC.md. Implementation details are in go/README.md and dotnet/README.md.

Quick start

git clone https://github.com/Nirmitee-tech/davinci-crd-server.git
cd davinci-crd-server

Go (native)

Requires Go 1.26.

cd go
go run ./cmd/crd-server
curl http://localhost:8081/v221/cds-services

.NET (native)

Requires the .NET 10 SDK.

cd dotnet
dotnet run --project src/Nirmitee.DaVinci.Crd.Host -- --urls http://0.0.0.0:8082
curl http://localhost:8082/v221/cds-services

Docker

Build from the repository root so the spec files are in the build context.

docker build -f go/Dockerfile -t davinci-crd-server-go .
docker run --rm -p 8081:8081 davinci-crd-server-go

docker build -f dotnet/Dockerfile -t davinci-crd-server-dotnet .
docker run --rm -p 8082:8082 davinci-crd-server-dotnet

Inside a container http://localhost is the container itself. When the CDS client hands out http://localhost/... issuer or FHIR URLs (as a local Inferno does), rewrite them: -e CRD_URL_REWRITES="http://localhost|http://host.docker.internal" for Go, or -e Crd__FetchUrlRewrites__0__From=http://localhost -e Crd__FetchUrlRewrites__0__To=http://host.docker.internal for .NET.

A first hook call

Hook calls require a JWT from a trusted issuer. For a quick local try, start the server with authentication off (development only) and post one of the synthetic request bodies:

cd go
CRD_AUTH_DISABLED=true go run ./cmd/crd-server &
curl -s -X POST http://localhost:8081/v221/cds-services/order-sign-crd \
  -H 'Content-Type: application/json' \
  --data @../testdata/v221/hooks/order-sign/01-prefetch-hospital-bed-e0250.json

The sample bodies point fhirServer at a local Inferno instance for the resources that are not prefetched. Without it the server cannot read them and answers with the technical-issues determination: a Coverage Information system action stating that requirements could not be determined. The covered path with cards needs the mock EHR data, which the unit tests (a fake FHIR server) and the Inferno runs provide.

Configuration

Environment variables shared by both implementations:

Variable Default Meaning
PORT 8081 (Go), 8082 (.NET) listen port
CRD_RULES_PATH ../spec/payer-rules.json payer rules file
CRD_DATA_DIR ./data data root; the audit log goes to {dir}/audit/YYYY-MM-DD.jsonl
CRD_TRUSTED_ISSUERS local and public Inferno CRD issuers comma-separated trusted iss values
CRD_ISSUER_JWKS empty JWKS overrides as iss=url;iss2=url2; default JWKS is {iss}/jwks.json
CRD_REQUIRE_DISCOVERY_AUTH false require a client JWT on discovery
CRD_AUTH_DISABLED false skip JWT checks on hooks; development only
CRD_PUBLIC_BASE_URLS empty comma-separated external base URLs accepted in aud, for example a tunnel URL
CRD_FHIR_TIMEOUT_MS 3000 timeout for each EHR FHIR fetch
CRD_CLOCK_SKEW_SECONDS 60 JWT time leeway

The default trusted issuers are http://localhost/custom/crd_server, http://localhost/custom/crd_server_v221, https://inferno.healthit.gov/suites/custom/crd_server and https://inferno.healthit.gov/suites/custom/crd_server_v221. Replace them with your real CDS clients in production.

Implementation-specific settings:

Setting Go .NET
Config file -config path or CRD_CONFIG_FILE (YAML or JSON) appsettings.json, Crd section
Route prefix CRD_PREFIX Crd__PathPrefix
Full listen address CRD_LISTEN_ADDR --urls or ASPNETCORE_URLS
Outbound URL rewrites CRD_URL_REWRITES (from|to pairs) Crd__FetchUrlRewrites__N__From, __To
Log level CRD_LOG_LEVEL Logging__LogLevel__Default
Discovery documents CRD_DISCOVERY_V221_PATH, CRD_DISCOVERY_V201_PATH built in, tested against spec/discovery-*.json

The Docker images set CRD_RULES_PATH=/app/spec/payer-rules.json and CRD_DATA_DIR=/data.

Running the Inferno suites yourself

The test kit is not part of this repository. You need Docker and Python 3.

# 1. clone and start the Inferno CRD test kit in the repository root (the folder is gitignored)
git clone https://github.com/inferno-framework/davinci-crd-test-kit.git
(cd davinci-crd-test-kit && docker compose up -d)

# 2. let the Inferno worker reach http://localhost callbacks
docker compose -f davinci-crd-test-kit/docker-compose.yml \
  -f harness/docker-compose.localhost-proxy.yml up -d worker-localhost-proxy

# 3. run a server natively on the host (Go on 8081 or .NET on 8082), then run the suites
python3 harness/run_suite.py --version 2.2.1 --base-url http://host.docker.internal:8081/v221
python3 harness/run_suite.py --version 2.0.1 --base-url http://host.docker.internal:8081/v201

Useful options: --groups 1,3.4,4.1 to run selected groups, --alg RS384 to switch the JWT algorithm, and --inferno https://inferno.healthit.gov/suites to drive the public instance. Reports are written to harness/reports/ and summarized in harness/LATEST.md. harness/build_testdata.py rebuilds testdata/ from the kit presets.

To pass test 1.01 (TLS), expose the server over HTTPS, for example with ngrok http 8081, start the server with CRD_PUBLIC_BASE_URLS=https://<ngrok-host>, and run the suites on the public Inferno instance with base URL https://<ngrok-host>/v221 or /v201. The step-by-step guide is harness/tls_public_run.md.

Repository layout

go/             Go implementation: crd/ library, cmd/crd-server host, internal/shared auth, FHIR, audit, config
dotnet/         .NET 10 implementation: Nirmitee.DaVinci.Crd library, Crd.Host, Common, xUnit tests
spec/           SPEC.md behavior contract, payer-rules.json, discovery documents for 2.2.1 and 2.0.1
testdata/       synthetic hook request bodies, group 4 scenarios and mock EHR bundles (v221, v201)
harness/        scripts that run the Inferno suites headlessly and write reports
docs/results/   curated Inferno report summaries

Roadmap

  • DTR server: payer-side Da Vinci Documentation Templates and Rules server (questionnaire package and adaptive $next-question), Go and .NET, tested against the Inferno DTR server suites v2.2.0 and v2.0.1.
  • PAS server: Da Vinci Prior Authorization Support server ($submit, $inquire, subscriptions and notifications), Go and .NET, tested against the Inferno PAS server suites v2.2.1 and v2.0.1.
  • Shared module: extract JWT and JWKS auth, FHIR helpers and the audit log into a module used by all three servers.
  • Combined server: one Burden Reduction host that mounts CRD, DTR and PAS under their own prefixes.

Contributing

Contributions are welcome. Behavior changes start in spec/SPEC.md and land in both implementations. See CONTRIBUTING.md for setup, tests and the pull request checklist, and follow the Code of Conduct. Report security issues privately as described in SECURITY.md.

All data in this repository is synthetic. Please never post real patient data in issues or pull requests.

License

Apache License 2.0. See LICENSE and NOTICE.

HL7, FHIR and the FHIR [FLAME DESIGN] are the registered trademarks of Health Level Seven International and their use does not constitute endorsement by HL7. The Inferno test kit is developed by the Inferno Framework team and is not redistributed here.

Built by Nirmitee.io.

About

Open-source Da Vinci CRD payer server in Go and .NET. Passes the Inferno CRD Server suites v2.2.1 and v2.0.1 with 0 failures.

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages