Skip to content
Registry StackDocsLatest

FHIR source adapter

View as Markdown

Registry Notary can read source data from FHIR R4 APIs to support configured Notary claims. It does not become a FHIR server and it does not use the FHIR Evidence resource as its claim model.

In the current implementation, FHIR access runs through the governed (signed, verified configuration bundle) source adapter runtime; see Configuration trust and integrity. Notary configs still use connector: source_adapter_sidecar; FHIR request construction, Bundle parsing, graph traversal, and projection stay inside that governed runtime bundle.

The current prototype supports a bounded FHIR R4 GET graph:

  • one anchor resource, such as Patient;
  • explicit relation searches, such as Coverage by beneficiary;
  • search parameter values from the primary lookup value, named query fields, literals, or a prior node reference;
  • token, reference, string, date, and code search value encoding;
  • JSON Pointer projection into Registry Data API-shaped rows;
  • single reads and records:batchMatch batch reads;
  • optional upstream bearer-token authorization from a sidecar-held environment variable;
  • Data-Purpose forwarding to the upstream FHIR server;
  • cardinality signaling through existing RDA row counts.

The adapter runtime never returns raw FHIR Bundles to Notary. It returns:

{ "data": [{ "national_id": "person-123", "coverage_status": "active" }] }

Notary then evaluates ordinary claim rules over that projected row.

The current governed prototype keeps a compact, use-case-led claim set with synthetic fixtures:

Eligibility and intake:

  • patient-record-exists
  • not-recorded-deceased, including explicit true, explicit false, and missing deceased[x]
  • age-over-18
  • coverage-active
  • coverage-eligibility-confirmed
  • enrolled-in-program

Care navigation and service routing:

  • provider-affiliated-with-facility
  • facility-offers-service
  • requester-guardian-confirmed
  • referral-active
  • appointment-booked

Service completion and follow-up:

  • encounter-completed
  • lab-result-available
  • vaccination-recorded

Payer and assurance workflows:

  • prior-authorization-approved
  • source-trace-available, using FHIR Provenance

For batch evaluation, Notary sends the existing source adapter batch contract with a query_signature and ordered item values. The adapter maps those values back to named FHIR query inputs and returns per-item data arrays or sanitized per-item errors.

The first governed prototype is coverage-active:

target.id
-> Patient.identifier
-> Coverage.beneficiary
-> project Coverage.status
-> Notary CEL: coverage_status == 'active'

Inactive coverage returns a projected row with coverage_status: inactive, so the claim evaluates to false. Missing or ambiguous patient or coverage graph matches continue to use existing Notary matching errors.

Profiles can use named query fields for requester or relationship-derived inputs. Notary still owns the requester, relationship, and purpose policy; the FHIR profile only receives the minimized field values it is configured to use.

Example:

target.id + requester identifier
-> Patient.identifier
-> RelatedPerson.patient + RelatedPerson.identifier
-> project relationship code

The sidecar profile uses value_from_query for named inputs:

search:
- param: identifier
type: token
system: https://example.gov/id/requester-id
value_from_query: requester_id
sources:
fhir_coverage:
dataset: health_registry
entity: coverage
engine: fhir
allow_insecure_localhost: true
allowed_base_urls:
- http://127.0.0.1:8080/fhir
fhir:
version: R4
base_url: http://127.0.0.1:8080/fhir
bearer_token_env: FHIR_UPSTREAM_TOKEN
anchor:
id: patient
resource_type: Patient
cardinality: one
search:
- param: identifier
type: token
system: https://example.gov/id/national-id
value_from_lookup: true
relations:
- id: coverage
resource_type: Coverage
cardinality: one
search:
- param: beneficiary
type: reference
value_from_node: patient.reference
project:
national_id:
node: patient
pointer: /identifier/0/value
coverage_status:
node: coverage
pointer: /status
smoke_lookup:
field: national_id
value: smoke-person
fields: [national_id]
purpose: startup-smoke

FHIR projections can declare a scalar default for absent JSON Pointer values. The not-recorded-deceased prototype uses this for missing Patient.deceased[x]:

deceased:
node: patient
pointer: /deceasedBoolean
default: false
source_connections:
fhir_sidecar:
base_url: http://127.0.0.1:9191
allow_insecure_localhost: true
retry_on_5xx: false
bulk_mode: source_adapter_sidecar_batch
token_env: FHIR_SIDECAR_TOKEN
expected_sidecar:
product: registry-notary-source-adapter-sidecar
instance_id: demo
environment: staging
stream_id: source-adapter-sidecar-runtime
config_hash: sha256:0000000000000000000000000000000000000000000000000000000000000000
require_expression_hashes_verified: true
require_runtime_verified: true
require_smoke_verified: true
claims:
- id: coverage-active
source_bindings:
coverage:
connector: source_adapter_sidecar
connection: fhir_sidecar
required_scope: health_registry:evidence_verification
dataset: health_registry
entity: coverage
lookup:
input: target.id
field: national_id
op: eq
cardinality: one
fields:
coverage_status:
field: coverage_status
type: string
required: true
rule:
type: cel
expression: source.coverage.coverage_status == 'active'

The config_hash in this example is illustrative. Governed deployments must pin the hash produced for the signed source adapter runtime bundle.

The repository also includes a parse-checked demo Notary config at demo/config/fhir-coverage-registry-notary.yaml.

Run the deterministic FHIR adapter fixture tests:

Terminal window
cargo test -p registry-notary-source-adapter-sidecar --test fhir_contract --locked

Run the governed Notary integration test:

Terminal window
cargo test -p registry-notary-server --features registry-notary-cel \
governed_fhir_sidecar_e2e_evaluates_coverage_active_with_pinned_assurance \
--locked

Both tests use synthetic in-process FHIR data. They do not call a public FHIR server.

Validate the demo Notary config:

Terminal window
cargo test -p registry-notary-server --test demo_config \
fhir_coverage_demo_config_loads_validates_and_builds_router \
--locked

Public FHIR test servers are useful for exploring real resource shapes before turning them into local fixtures. They are not suitable for deterministic CI because public data can change, be purged, or be temporarily unavailable.

Recommended no-auth R4 endpoints:

EndpointBase URLBest use
SMART public R4https://r4.smarthealthit.orgFirst exploration target for linked synthetic patient data.
HAPI public R4https://hapi.fhir.org/baseR4Broad resource-shape checks. Less deterministic because the server is regularly reloaded.
Firely public R4https://server.fire.ly/R4Fallback open R4 server when SMART or HAPI do not expose a needed shape.

Useful probes:

Terminal window
curl 'https://r4.smarthealthit.org/Patient?_summary=count'
curl 'https://r4.smarthealthit.org/Coverage?_count=5'
curl 'https://r4.smarthealthit.org/RelatedPerson?_count=5'

If you capture a public-server shape as a local fixture, record the endpoint, date, and query path alongside it. Do not record real patient data, bearer tokens, or production identifiers.

  • FHIR support currently runs through the source adapter runtime; there is no connector: fhir.
  • FHIR searches use GET only.
  • FHIR source profiles must declare allowed_base_urls; cleartext HTTP also requires allow_insecure_localhost: true and is accepted only for loopback/local test endpoints.
  • Projection uses JSON Pointer only.
  • Relation traversal is explicit and non-recursive.
  • Batch matching is sequential per item inside the adapter runtime.
  • The adapter trusts entry.search.mode == "match" and ignores include and outcome entries for cardinality.