{"id":49002535,"url":"https://github.com/ma3u/minimumviablehealthdataspacev2","last_synced_at":"2026-04-18T19:02:07.111Z","repository":{"id":343064221,"uuid":"1176132614","full_name":"ma3u/MinimumViableHealthDataspacev2","owner":"ma3u","description":"The demo models a concrete EHDS secondary-use scenario: a clinical research organisation  wants to run a drug-repurposing study using synthetic patient data held by a hospital, with access governed by a Health Data Access Body. All five layers of this scenario live in a single Neo4j knowledge graph.","archived":false,"fork":false,"pushed_at":"2026-04-15T09:32:03.000Z","size":45088,"stargazers_count":4,"open_issues_count":11,"forks_count":0,"subscribers_count":2,"default_branch":"main","last_synced_at":"2026-04-15T11:27:21.668Z","etag":null,"topics":["dataspace","dcat-ap","did","edc","ehds","fhir","h7","health","ssi"],"latest_commit_sha":null,"homepage":"https://ma3u.github.io/MinimumViableHealthDataspacev2/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ma3u.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":".github/CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"docs/security/bsi-c5-gap-analysis.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-03-08T16:59:33.000Z","updated_at":"2026-04-15T09:32:07.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/ma3u/MinimumViableHealthDataspacev2","commit_stats":null,"previous_names":["ma3u/minimumviablehealthdataspacev2"],"tags_count":3,"template":false,"template_full_name":null,"purl":"pkg:github/ma3u/MinimumViableHealthDataspacev2","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ma3u%2FMinimumViableHealthDataspacev2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ma3u%2FMinimumViableHealthDataspacev2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ma3u%2FMinimumViableHealthDataspacev2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ma3u%2FMinimumViableHealthDataspacev2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ma3u","download_url":"https://codeload.github.com/ma3u/MinimumViableHealthDataspacev2/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ma3u%2FMinimumViableHealthDataspacev2/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31980783,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-18T17:30:12.329Z","status":"ssl_error","status_checked_at":"2026-04-18T17:29:59.069Z","response_time":103,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["dataspace","dcat-ap","did","edc","ehds","fhir","h7","health","ssi"],"created_at":"2026-04-18T19:01:59.896Z","updated_at":"2026-04-18T19:02:07.088Z","avatar_url":"https://github.com/ma3u.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# EHDS Integration Hub\n\n[![GitHub](https://img.shields.io/badge/GitHub-Repository-181717?logo=github\u0026logoColor=white)](https://github.com/ma3u/MinimumViableHealthDataspacev2) [![CI Tests](https://github.com/ma3u/MinimumViableHealthDataspacev2/actions/workflows/test.yml/badge.svg)](https://github.com/ma3u/MinimumViableHealthDataspacev2/actions/workflows/test.yml) [![Coverage 94%](https://img.shields.io/badge/coverage-94%25-brightgreen)](docs/test-coverage-report.md) [![1490 Tests](https://img.shields.io/badge/tests-1490%20passed-brightgreen)](docs/test-coverage-report.md) [![Playwright 778](https://img.shields.io/badge/E2E-778%20tests-brightgreen)](docs/e2e-test-report.md) [![Azure](https://img.shields.io/badge/Azure-Deployed-0078D4?logo=microsoftazure\u0026logoColor=white)](docs/azure-deployment-guide.md)\n\n[![EHDS Compliant](https://img.shields.io/badge/EHDS-Compliant-0ea5e9)](https://health.ec.europa.eu/ehealth-digital-health-and-care/european-health-data-space_en) [![FHIR R4](https://img.shields.io/badge/FHIR-R4-orange)](https://hl7.org/fhir/R4/) [![OMOP CDM](https://img.shields.io/badge/OMOP-CDM%20v5.4-yellow)](https://ohdsi.github.io/CommonDataModel/) [![EEHRxF](https://img.shields.io/badge/EEHRxF-HL7%20Europe-148F77)](https://hl7.eu/fhir/) [![Neo4j 5](https://img.shields.io/badge/Neo4j-5%20Community-008CC1?logo=neo4j\u0026logoColor=white)](https://neo4j.com/) [![Next.js 14](https://img.shields.io/badge/Next.js-14-black?logo=next.js\u0026logoColor=white)](https://nextjs.org/) [![Eclipse EDC](https://img.shields.io/badge/Eclipse-EDC--V-blue)](https://eclipse-edc.github.io/docs/) [![DSP Dataspace Protocol 2025-1](https://img.shields.io/badge/DSP-Dataspace%20Protocol%202025--1-6366f1)](https://docs.internationaldataspaces.org/ids-knowledgebase/v/dataspace-protocol) [![DCP Decentralized Claims Protocol v1.0](https://img.shields.io/badge/DCP-Decentralized%20Claims%20Protocol%20v1.0-7c3aed)](https://projects.eclipse.org/projects/technology.dataspace-dcp/releases/1.0.0) [![DPS](https://img.shields.io/badge/DPS-Data%20Plane%20Signaling-0891b2)](https://projects.eclipse.org/proposals/eclipse-data-plane-core) [![SIMPL](https://img.shields.io/badge/SIMPL-EU%20Cloud%20Federation-e11d48)](https://simpl-programme.eu/) [![License: Apache 2.0](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](LICENSE)\n\n## Table of Contents\n\n- [EHDS Integration Hub](#ehds-integration-hub)\n  - [Table of Contents](#table-of-contents)\n  - [Why This Project Exists](#why-this-project-exists)\n  - [What It Does](#what-it-does)\n  - [Architecture](#architecture)\n  - [UI Views](#ui-views)\n  - [Project Structure](#project-structure)\n  - [Quick Start](#quick-start)\n    - [Step 1 — Prerequisites](#step-1--prerequisites)\n    - [Step 2 — Clone](#step-2--clone)\n    - [Step 3 — Start Neo4j](#step-3--start-neo4j)\n    - [Step 4 — Initialise Schema](#step-4--initialise-schema)\n    - [Step 5 — Load Seed Data](#step-5--load-seed-data)\n    - [Step 6 — Register DSP Marketplace Chain](#step-6--register-dsp-marketplace-chain)\n    - [Step 7 — Register EEHRxF Profile Alignment](#step-7--register-eehrxf-profile-alignment)\n    - [Step 8 — Install UI Dependencies](#step-8--install-ui-dependencies)\n    - [Step 9 — Start the UI](#step-9--start-the-ui)\n  - [Quick Start — Full Dataspace (JAD Stack)](#quick-start--full-dataspace-jad-stack)\n    - [Prerequisites](#prerequisites)\n    - [One-Command Start](#one-command-start)\n    - [Verify Services](#verify-services)\n    - [Start the UI](#start-the-ui)\n    - [Run Seeding Separately](#run-seeding-separately)\n    - [Tear Down](#tear-down)\n  - [Testing](#testing)\n  - [Development](#development)\n    - [Run pre-commit checks](#run-pre-commit-checks)\n    - [Neo4j driver note](#neo4j-driver-note)\n    - [JAD Stack (EDC-V + CFM + DCore)](#jad-stack-edc-v--cfm--dcore)\n    - [All Docker Service Endpoints](#all-docker-service-endpoints)\n  - [Documentation](#documentation)\n  - [Implementation Status](#implementation-status)\n    - [Phase 1 — Infrastructure Migration](#phase-1--infrastructure-migration)\n    - [Phase 2 — Identity \\\u0026 Trust](#phase-2--identity--trust)\n    - [Phase 3 — Health Knowledge Graph](#phase-3--health-knowledge-graph)\n    - [Phase 4 — Dataspace Integration](#phase-4--dataspace-integration)\n    - [Phase 5 — Federated Queries \\\u0026 Natural Language Search](#phase-5--federated-queries--natural-language-search)\n    - [Phase 6 — Web Application \\\u0026 Participant Portal](#phase-6--web-application--participant-portal)\n    - [Phase 7 — Protocol Compliance Testing](#phase-7--protocol-compliance-testing)\n    - [Phase 8 — Automated Testing](#phase-8--automated-testing)\n    - [Phase 9 — Documentation \\\u0026 Navigation](#phase-9--documentation--navigation)\n    - [Phase 10 — Tasks Dashboard](#phase-10--tasks-dashboard)\n    - [Phase 11 — System Topology View](#phase-11--system-topology-view)\n    - [Phase 12 — Data Query Fix \\\u0026 Policy Seeding](#phase-12--data-query-fix--policy-seeding)\n    - [Container Inventory](#container-inventory)\n      - [Tier 0 — Infrastructure Foundations (no dependencies, start first)](#tier-0--infrastructure-foundations-no-dependencies-start-first)\n      - [Tier 1 — Core Identity \\\u0026 UI (depend on Tier 0)](#tier-1--core-identity--ui-depend-on-tier-0)\n      - [Tier 2 — EDC-V Core + Identity Services (depend on Tier 0 + 1)](#tier-2--edc-v-core--identity-services-depend-on-tier-0--1)\n      - [Tier 3 — Data Planes, Proxy, Provisioning \\\u0026 CFM Agents (depend on Tier 2)](#tier-3--data-planes-proxy-provisioning--cfm-agents-depend-on-tier-2)\n      - [Tier 4 — Seed Job (runs once after all services are ready)](#tier-4--seed-job-runs-once-after-all-services-are-ready)\n      - [Dependency Graph](#dependency-graph)\n  - [Contributing](#contributing)\n  - [Background](#background)\n  - [Security](#security)\n  - [License](#license)\n\n---\n\n## Why This Project Exists\n\nThe [European Health Data Space (EHDS)](https://health.ec.europa.eu/ehealth-digital-health-and-care/european-health-data-space_en) regulation creates a legal framework for sharing health data across the EU, but turning that regulation into running software is an unsolved integration challenge. A hospital in Berlin that wants to share de-identified patient cohorts with a pharmaceutical researcher in Amsterdam needs to navigate five layers of technology: dataspace governance contracts, standardised metadata catalogues, clinical data formats, research-grade analytics schemas, and biomedical terminologies. Today, no single reference implementation shows how these layers connect end-to-end.\n\nThis project builds that missing reference. It takes the [Eclipse Dataspace Components](https://eclipse-edc.github.io/docs/) (open-source building blocks for sovereign data exchange) and wires them to a health-domain knowledge graph that speaks FHIR R4, OMOP CDM, and HealthDCAT-AP natively. The result is a **self-contained local demo** you can run on your laptop in under five minutes, without any cloud account or real patient data.\n\nThe motivation comes from a practical gap: the Eclipse [JAD (Joint Architecture Demo)](https://github.com/Metaform/jad) shows how EDC-V, DCore, and CFM work together for generic cloud-provider deployments, but it has no health-domain content. Conversely, FHIR servers and OMOP databases exist in isolation, disconnected from dataspace governance. This project bridges that gap it puts EHDS governance contracts, HealthDCAT-AP catalogue metadata, FHIR patient journeys, OMOP research analytics, and SNOMED/LOINC ontologies into a single queryable graph, all accessible through the Dataspace Protocol.\n\n![MVD Health](docs/images/social-preview.svg)\n\nFor the full background, see the companion article: [European Health Dataspaces, Digital Twins: A Journey from FHIR Basics to Intelligent Patient Models](https://www.linkedin.com/pulse/european-health-dataspaces-digital-twins-journey-fhir-buchhorn-roth-8t51c/).\n\n- **Live Demo:** Static UI at [ma3u.github.io/MinimumViableHealthDataspacev2](https://ma3u.github.io/MinimumViableHealthDataspacev2/)\n- **Azure live stack** at [ehds.mabu.red](https://ehds.mabu.red) (online **Mon–Fri 07:00–20:00 Europe/Berlin** — see [ADR-016](docs/ADRs/ADR-016-aca-off-hours-scaledown.md))\n\n---\n\n## What It Does\n\nThe demo models a concrete EHDS secondary-use scenario: a **clinical research organisation (CRO)** in Amsterdam wants to run a drug-repurposing study using synthetic patient data held by a **hospital (Clinic)** in Berlin, with access governed by a **Health Data Access Body (HDAB)** in Germany. All five layers of this scenario live in a single Neo4j knowledge graph:\n\n- **Layer 1 — DSP Marketplace**: DataProduct / OdrlPolicy / Contract / HDABApproval nodes model the full EHDS Article 45–53 secondary-use access flow, enforced by the Dataspace Protocol (DSP 2025-1).\n- **Layer 2 — HealthDCAT-AP Catalogue**: Dataset / Distribution / DataService / Organization nodes expose W3C HealthDCAT-AP 1.0 metadata — the mandatory standard for health dataset discovery across EU Health Data Access Bodies.\n- **Layer 3 — FHIR R4 Clinical Data**: 127 synthetic patients generated by [Synthea](https://github.com/synthetichealth/synthea) with Encounters, Conditions, Observations, MedicationRequests, and Procedures — loaded as first-class graph nodes with full provenance chains.\n- **Layer 4 — OMOP CDM Research Layer**: FHIR clinical events are transformed into OMOP v5.4 nodes (Person, ConditionOccurrence, DrugExposure, ProcedureOccurrence, Measurement), enabling cohort-level analytics without moving data out of the graph.\n- **Layer 5 — Biomedical Ontology Backbone**: SNOMED CT, ICD-10-CM, RxNorm, LOINC, and CPT-4 concept nodes link clinical events to standardised terminologies via `CODED_BY` edges.\n\n![Architecture Diagram](docs/images/architecture.svg)\n\nOn top of these five layers, **EEHRxF Profile Alignment** nodes map the six EHDS priority categories (Patient Summary, ePrescription, Laboratory Results, Hospital Discharge, Medical Imaging, Rare Disease) to HL7 Europe FHIR Implementation Guides, with dynamic coverage scores computed against the loaded data.\n\nA **Next.js 14 application** provides six purpose-built views to explore all of this: an interactive graph explorer, a HealthDCAT-AP dataset catalogue, a DSP compliance chain inspector, a patient journey timeline, an OMOP research analytics dashboard, and an EEHRxF profile gap analysis view.\n\n---\n\n## Architecture\n\nAll five layers are persisted as a single **Neo4j 5 knowledge graph**. Each layer is a distinct\nset of labelled nodes; cross-layer `GOVERNS`, `DESCRIBES`, `MAPS_TO`, and `CODED_BY` relationships\nform the connective tissue that makes the graph queryable end-to-end — from a governance contract\nall the way down to a SNOMED code on a patient condition.\n\n| Layer           | Nodes                                                                           | Technology                     |\n| --------------- | ------------------------------------------------------------------------------- | ------------------------------ |\n| 1 · Marketplace | DataProduct, OdrlPolicy, Contract, AccessApplication, HDABApproval              | Eclipse EDC-V, DSP, CFM, DCore |\n| 2 · Catalogue   | Catalogue, Dataset, Distribution, DataService, Organization                     | HealthDCAT-AP / DCAT-AP 3      |\n| 3 · Clinical    | Patient, Observation, Condition, Encounter, MedicationRequest, Procedure        | FHIR R4 / Synthea              |\n| 4 · Research    | PersonNode, ConditionOccurrence, DrugExposure, ProcedureOccurrence, Measurement | OMOP CDM v5.4                  |\n| 5 · Ontology    | OntologyConcept (SNOMED CT, ICD-10-CM, RxNorm, LOINC, CPT-4)                    | Biomedical Terminologies       |\n\nThe schema below shows the node labels and relationship types as rendered by Neo4j Browser's\n`CALL db.schema.visualization()` after the seed data is loaded:\n\n![Knowledge Graph Schema](docs/images/graph-schema.png)\n\n---\n\n## UI Views\n\nThe Next.js 14 app is served at \u003chttp://localhost:3000\u003e and provides six purpose-built views,\neach backed by a dedicated API route that queries Neo4j directly over Bolt.\n\n| View             | Route         | Description                                                                                                     |\n| ---------------- | ------------- | --------------------------------------------------------------------------------------------------------------- |\n| Graph Explorer   | `/graph`      | Force-directed graph of all 5 layers; click a node to highlight its neighbourhood and view details.             |\n| Data Catalogue   | `/catalog`    | HealthDCAT-AP dataset cards with publisher, license, temporal coverage, and expandable detail panels.           |\n| Compliance Chain | `/compliance` | Trace a DSP contract from DataProduct → OdrlPolicy → Contract → HDABApproval in one query.                      |\n| Patient Journey  | `/patient`    | Time-ordered FHIR R4 timeline (encounters, conditions, medications, procedures) alongside the OMOP CDM mapping. |\n| OMOP Analytics   | `/analytics`  | Cohort-level stat cards (patients, conditions, drugs, procedures), gender breakdown, and top-15 bar charts.     |\n| EEHRxF Profiles  | `/eehrxf`     | EU FHIR profile alignment with EHDS priority category coverage, HL7 Europe IG inventory, and gap analysis.      |\n\n---\n\n## Demo Users \u0026 Roles\n\nThe JAD stack comes with **seven** pre-configured Keycloak demo users in the **EDCV realm**.\nSign in at `http://localhost:3003/auth/signin` — password equals username in local dev.\n\n| Username     | Organisation           | EHDS Role         | Keycloak Role(s)                      | Graph persona |\n| ------------ | ---------------------- | ----------------- | ------------------------------------- | ------------- |\n| `edcadmin`   | Dataspace Operator     | Operator          | `EDC_ADMIN`                           | `edc-admin`   |\n| `clinicuser` | AlphaKlinik Berlin     | Data Holder       | `EDC_USER_PARTICIPANT`, `DATA_HOLDER` | `hospital`    |\n| `lmcuser`    | Limburg Medical Centre | Data Holder       | `EDC_USER_PARTICIPANT`, `DATA_HOLDER` | `hospital`    |\n| `researcher` | PharmaCo Research AG   | Researcher        | `EDC_USER_PARTICIPANT`, `DATA_USER`   | `researcher`  |\n| `regulator`  | MedReg DE              | HDAB Authority    | `HDAB_AUTHORITY`                      | `hdab`        |\n| `patient1`   | AlphaKlinik Berlin     | Patient / Citizen | `PATIENT`                             | `patient`     |\n| `patient2`   | Limburg Medical Centre | Patient / Citizen | `PATIENT`                             | `patient`     |\n\n\u003e **Returning users** (switching personas): the UserMenu **\"Returning users\"** section lets you\n\u003e switch between demo accounts. Each switch redirects to Keycloak — you must enter the target\n\u003e user's password. Trust Center operators use the `hdab` graph persona and\n\u003e `/compliance#trust-center`.\n\n### Navigation Groups per Role\n\nEach role sees a **single primary menu** tailored to their daily work. \"Explore\" is replaced\nby persona-specific menus for researchers (My Researches) and patients (My Health).\n\n| Role           | Primary Menu     | Graph Center         | EHDS Articles |\n| -------------- | ---------------- | -------------------- | ------------- |\n| Public         | Explore          | Health Dataspace     | —             |\n| Patient        | My Health        | My Health            | Art. 3-12     |\n| Data Holder    | Explore+Exchange | Our Data Offerings   | Art. 33-37    |\n| Researcher     | My Researches    | My Researches        | Art. 46-49    |\n| HDAB Authority | Governance       | Govern the Dataspace | Art. 45-53    |\n| Trust Center   | Governance       | Privacy Operations   | Art. 50-51    |\n| EDC Admin      | Manage           | Manage Dataspace     | Art. 33       |\n\n**Researcher workflow (My Researches menu — EHDS Art. 46-49):**\n\n| Step | Route            | Label             | EHDS Article | What happens                                         |\n| ---- | ---------------- | ----------------- | ------------ | ---------------------------------------------------- |\n| 1    | `/graph`         | Research Overview | —            | Knowledge graph with researcher focus                |\n| 2    | `/catalog`       | Browse Catalogs   | Art. 47      | HealthDCAT-AP dataset catalog                        |\n| 3    | `/data/discover` | Discover Datasets | Art. 47      | Federated search across participant catalogs         |\n| 4    | `/negotiate`     | Request Access    | Art. 48      | Submit access application with purpose + legal basis |\n| 5    | `/tasks`         | My Applications   | Art. 49      | Track HDAB approval status                           |\n| 6    | `/data/transfer` | Retrieve Data     | Art. 50      | Transfer approved data to Secure Processing Env      |\n| 7    | `/analytics`     | Run Analytics     | Art. 50/53   | OMOP cohort analytics in SPE                         |\n| 8    | `/query`         | Query \u0026 Export    | Art. 50      | NLQ/federated queries, export aggregate results only |\n\n**Menu items per role:**\n\n| Route                           | Public | Patient | Data Holder | Researcher | HDAB | EDC Admin |\n| ------------------------------- | :----: | :-----: | :---------: | :--------: | :--: | :-------: |\n| `/graph`                        |   ✅   |    —    |     ✅      |     ✅     |  ✅  |    ✅     |\n| `/catalog`                      |   ✅   |    —    |     ✅      |     ✅     |  ✅  |    ✅     |\n| `/catalog/editor`               |   —    |    —    |     ✅      |     —      |  —   |    ✅     |\n| `/patient`                      |   ✅   |   ✅    |     ✅      |     ✅     |  ✅  |    ✅     |\n| `/patient/profile`              |   —    |   ✅    |      —      |     —      |  —   |     —     |\n| `/patient/research`             |   —    |   ✅    |      —      |     —      |  —   |     —     |\n| `/patient/insights`             |   —    |   ✅    |      —      |     —      |  —   |     —     |\n| `/analytics`                    |   —    |    —    |      —      |     ✅     |  ✅  |    ✅     |\n| `/query` (NLQ)                  |   —    |    —    |      —      |     ✅     |  ✅  |    ✅     |\n| `/eehrxf`                       |   ✅   |    —    |     ✅      |     ✅     |  ✅  |    ✅     |\n| `/compliance`                   |   —    |    —    |      —      |     —      |  ✅  |    ✅     |\n| `/credentials`                  |   —    |    —    |     ✅      |     ✅     |  ✅  |    ✅     |\n| `/data/share`                   |   —    |    —    |     ✅      |     —      |  —   |    ✅     |\n| `/data/discover`                |   —    |    —    |      —      |     ✅     |  ✅  |    ✅     |\n| `/negotiate`                    |   —    |    —    |     ✅      |     ✅     |  —   |    ✅     |\n| `/tasks`                        |   —    |    —    |     ✅      |     ✅     |  ✅  |    ✅     |\n| `/data/transfer`                |   —    |    —    |     ✅      |     ✅     |  ✅  |    ✅     |\n| `/admin` + components + tenants |   —    |    —    |      —      |     —      |  —   |    ✅     |\n| `/admin/policies` + audit       |   —    |    —    |      —      |     —      |  ✅  |    ✅     |\n| `/onboarding`, `/settings`      |   —    |    —    |     ✅      |     ✅     |  ✅  |    ✅     |\n| `/docs`                         |   ✅   |   ✅    |     ✅      |     ✅     |  ✅  |    ✅     |\n\n### Graph Explorer — Persona Views\n\nThe graph center shows a **value-centric starting node** per persona, with nodes\narranged in concentric rings by relevance (not by technical layer).\n\n| Persona                | URL param               | Center node          | Primary question                                       |\n| ---------------------- | ----------------------- | -------------------- | ------------------------------------------------------ |\n| Default                | `?persona=default`      | Health Dataspace     | What does the full dataspace look like?                |\n| Hospital / Data Holder | `?persona=hospital`     | Our Data Offerings   | What data do we offer? Who is using it?                |\n| Researcher / Data User | `?persona=researcher`   | My Researches        | Which datasets can I use? How do I get access?         |\n| HDAB Authority         | `?persona=hdab`         | Govern the Dataspace | What approvals are pending? Which policies govern use? |\n| Trust Center Operator  | `?persona=trust-center` | Privacy Operations   | Which pseudonym flows am I running?                    |\n| EDC Admin              | `?persona=edc-admin`    | Manage Dataspace     | Who are my participants? What contracts are live?      |\n| Patient / Citizen      | `?persona=patient`      | My Health            | What health data do I have? Who is using it?           |\n\n**Persona-specific filter presets** (sidebar questions per role):\n\n| Persona    | Filter questions                                                                                             |\n| ---------- | ------------------------------------------------------------------------------------------------------------ |\n| Patient    | Who is using my data? · Which research programme? · Show my data · Show health interests and risks           |\n| Researcher | Which datasets can I use? · How do I get access? · What analytics? · Clinical data? · Where is it processed? |\n| Hospital   | Which data do we offer? · Who is using our data? · What contracts? · Are we compliant? · Clinical data?      |\n| HDAB       | What approvals are pending? · Which policies? · What contracts? · Credentials valid? · Trust Center?         |\n\n**Node role colours** (warm/vivid accents — distinct from cool/muted layer colours):\n\n| Node type         | Colour              | Role                                             |\n| ----------------- | ------------------- | ------------------------------------------------ |\n| `Participant`     | 🟠 Orange `#F97316` | Dataspace actors (data holders, researchers)     |\n| `TrustCenter`     | 🔴 Red `#EF4444`    | EHDS Art. 50 pseudonym authority                 |\n| `HDABApproval`    | 🩷 Pink `#EC4899`   | HDAB access decisions                            |\n| `SPESession`      | 🟡 Amber `#F59E0B`  | Active secure processing sessions                |\n| `PatientConsent`  | 🟣 Purple `#A855F7` | Patient consent for secondary use (EHDS Art. 10) |\n| `ResearchInsight` | 🩵 Cyan `#06B6D4`   | Personalised insights from research studies      |\n\n---\n\n## Project Structure\n\nThe repository is intentionally minimal. Neo4j Cypher scripts in `neo4j/` build the graph in\nlayers; the `ui/` directory is a standalone Next.js app that only requires the Neo4j container to\nbe running. No build step is needed for the graph itself.\n\n```text\nMinimumViableHealthDataspacev2/\n├── README.md\n├── docker-compose.yml            # Neo4j 5 with APOC + n10s plugins\n├── docker-compose.jad.yml        # JAD stack: 19 EDC-V/CFM/DCore services\n├── docker-compose.live.yml       # Live-mode UI override (port 3003)\n├── LICENSE\n├── docs/\n│   ├── planning-health-dataspace-v2.md   # 7-phase implementation roadmap\n│   ├── health-dataspace-graph-schema.md  # 5-layer Neo4j schema reference\n│   └── images/\n│       ├── social-preview.svg            # GitHub social preview / OG image\n│       ├── architecture.svg              # 5-layer architecture diagram\n│       ├── graph-schema.png              # Knowledge graph schema screenshot\n│       ├── synthetic-patient-journey.png # Full patient journey screenshot\n│       └── ui-screenshot.png             # Graph Explorer UI screenshot\n├── jad/                          # JAD stack configuration\n│   ├── edcv-assets/              # Phase 4a: EDC-V asset + policy + contract JSON\n│   ├── openapi/                  # OpenAPI specs for all JAD services\n│   ├── keycloak-realm.json       # Keycloak realm import\n│   ├── bootstrap-vault.sh        # Vault JWT auth + data plane keys\n│   ├── init-postgres.sql         # 8-database PostgreSQL init\n│   └── *.yaml / *.env            # Per-service configuration files\n├── neo4j/\n│   ├── init-schema.cypher                 # Neo4j constraints and indexes\n│   ├── insert-synthetic-schema-data.cypher # L1–L5 seed data (Synthea-derived)\n│   ├── register-dsp-marketplace.cypher    # Phase 3e: DSP marketplace chain\n│   ├── register-eehrxf-profiles.cypher    # Phase 3h: EEHRxF profile alignment\n│   ├── register-ehds-credentials.cypher   # Phase 4: EHDS verifiable credentials\n│   ├── register-fhir-dataset-hdcatap.cypher # Phase 3: HealthDCAT-AP catalogue\n│   ├── fhir-to-omop-transform.cypher      # FHIR → OMOP CDM mapping\n│   └── seed-audit-provenance.cypher       # Audit trail seed data\n├── services/\n│   └── neo4j-proxy/              # DCore ↔ Neo4j bridge (TypeScript/Express)\n│       ├── src/index.ts          # 6 endpoints: FHIR, OMOP, HealthDCAT-AP\n│       ├── Dockerfile            # Multi-stage Node.js 20 build\n│       └── package.json\n├── scripts/                      # Utility and data-prep scripts\n│   ├── bootstrap-jad.sh          # Start JAD stack with health checks\n│   └── generate-synthea.sh       # Generate synthetic FHIR data\n└── ui/                           # Next.js 14 application\n    └── src/app/\n        ├── graph/                # Graph Explorer\n        ├── catalog/              # HealthDCAT-AP Catalogue\n        ├── compliance/           # Compliance Chain Inspector\n        ├── patient/              # Patient Journey\n        ├── analytics/            # OMOP Analytics Dashboard\n        └── eehrxf/               # EEHRxF Profile Alignment\n```\n\n---\n\n## Quick Start\n\nThe full stack runs locally in under five minutes. You need Docker for Neo4j and Node.js for the\nUI — no cloud account or external services required.\n\n### Step 1 — Prerequisites\n\nMake sure the following tools are installed and available on your `$PATH`:\n\n- **OrbStack** (or Docker Desktop ≥ 24) — runs the Neo4j 5 container with APOC and n10s plugins.\n- **Node.js ≥ 20 with npm** — required to run the Next.js UI.\n- **Git** — to clone the repository.\n\n### Step 2 — Clone\n\nFetch the repository and enter the project root:\n\n```bash\ngit clone https://github.com/ma3u/MinimumViableHealthDataspacev2.git\ncd MinimumViableHealthDataspacev2\n```\n\n### Step 3 — Start Neo4j\n\nThe `docker-compose.yml` at the root starts a Neo4j 5 Community container named\n`health-dataspace-neo4j` and exposes Bolt on port **7687** and the browser UI on port **7474**.\nAPOC and n10s plugins are pre-configured via environment variables.\n\n```bash\ndocker compose up -d\n```\n\nVerify the container is healthy, then open Neo4j Browser at \u003chttp://localhost:7474\u003e and log in\nwith `neo4j` / `healthdataspace`.\n\n### Step 4 — Initialise Schema\n\nThis step creates all uniqueness constraints and indexes for the five layers. Running it before\nloading data ensures fast lookups and prevents duplicate nodes from being created on re-runs.\n\n```bash\ncat neo4j/init-schema.cypher | \\\n  docker exec -i health-dataspace-neo4j \\\n  cypher-shell -u neo4j -p healthdataspace\n```\n\nYou can verify the schema was applied by running `CALL db.schema.visualization()` in Neo4j\nBrowser — the meta-graph should show all five layer labels.\n\n### Step 5 — Load Seed Data\n\nThis script inserts a complete synthetic scenario: eight patients with FHIR clinical events (Layer 3) already transformed into OMOP CDM node equivalents (Layer 4) and linked to biomedical ontology\ncodes (Layer 5). The HealthDCAT-AP catalogue entry (Layer 2) referencing the dataset is also\ncreated here.\n\n```bash\ncat neo4j/insert-synthetic-schema-data.cypher | \\\n  docker exec -i health-dataspace-neo4j \\\n  cypher-shell -u neo4j -p healthdataspace\n```\n\nAfter loading, the cross-layer patient journey is visible in Neo4j Browser:\n\n![Full Synthetic Patient Journey](docs/images/synthetic-patient-journey.png)\n\n### Step 6 — Register DSP Marketplace Chain\n\nThis script wires up the full EHDS data-access governance chain (Layer 1): a `DataProduct` is\nlinked to an `OdrlPolicy`, which is referenced by a `Contract`. An `AccessApplication` and\n`HDABApproval` node complete the chain and are connected back to the seed dataset via a\n`GRANTS_ACCESS_TO` relationship. This models Article 45–52 EHDS compliance in the graph.\n\n```bash\ncat neo4j/register-dsp-marketplace.cypher | \\\n  docker exec -i health-dataspace-neo4j \\\n  cypher-shell -u neo4j -p healthdataspace\n```\n\n### Step 7 — Register EEHRxF Profile Alignment\n\nThis script creates EEHRxFCategory and EEHRxFProfile nodes representing the six EHDS priority\ncategories and their corresponding HL7 Europe FHIR Implementation Guide profiles. Coverage\nscores are computed dynamically against the loaded FHIR resources.\n\n```bash\ncat neo4j/register-eehrxf-profiles.cypher | \\\n  docker exec -i health-dataspace-neo4j \\\n  cypher-shell -u neo4j -p healthdataspace\n```\n\n### Step 8 — Install UI Dependencies\n\nThe UI is a standard Next.js 14 application in the `ui/` directory. It connects to Neo4j over\nBolt using the credentials from `.env.local` (matching the local container defaults). Install\nnpm packages once:\n\n```bash\ncd ui \u0026\u0026 npm install\n```\n\n`npm install` automatically creates `ui/.env.local` from `.env.example` on first run\n(with a random `NEXTAUTH_SECRET`). No manual copy step is needed. To customise Neo4j\nor Keycloak connection settings, edit `ui/.env.local` directly.\n\n### Step 9 — Start the UI\n\nStart the development server. Hot-reload is enabled, so any UI changes are reflected immediately without restarting Neo4j or reloading data.\n\n```bash\nnpm run dev\n```\n\nOpen \u003chttp://localhost:3000\u003e in your browser. The home page links to all six views.\n\nTo access the protected **Portal** views (which simulate dataspace participation, policy management, and onboarding), use any of the following pre-configured Keycloak test accounts:\n\n| Username     | Password     | Persona / Role                                    |\n| ------------ | ------------ | ------------------------------------------------- |\n| `edcadmin`   | `edcadmin`   | Dataspace Administrator (`EDC_ADMIN`)             |\n| `clinicuser` | `clinicuser` | Hospital Participant (`EDC_USER_PARTICIPANT`)     |\n| `regulator`  | `regulator`  | Health Data Access Body / HDAB (`HDAB_AUTHORITY`) |\n\n![Graph Explorer UI](docs/images/ui-screenshot.png)\n\n---\n\n## Quick Start — Full Dataspace (JAD Stack)\n\nThe full EHDS-compliant dataspace runs 19+ services locally using the\n[JAD (Joint Architecture Demo)](https://github.com/Metaform/jad) container images.\nThis brings up EDC-V, DCore, CFM, IdentityHub, IssuerService, Keycloak, Vault, NATS,\nand all supporting infrastructure.\n\n### Prerequisites\n\n- **OrbStack** (or Docker Desktop ≥ 24) with **≥ 8 GB RAM** allocated\n- **Ports available:** 80, 4222, 5432, 7474, 7687, 8080, 8090, 8200, 8222, 9090,\n  10013, 11002, 11003, 11005, 11006, 11007, 11012\n- **Node.js ≥ 20** (for the UI)\n- **Python 3** (for seed script parsing)\n\n### One-Command Start\n\nThe bootstrap script handles image pulls, startup ordering, health checks, identity\nprovisioning, and full dataspace seeding:\n\n```bash\ngit clone https://github.com/ma3u/MinimumViableHealthDataspacev2.git\ncd MinimumViableHealthDataspacev2\n./scripts/bootstrap-jad.sh\n```\n\nThis takes approximately 5–10 minutes on first run (image pulls). Subsequent runs are\nfaster. When complete, all 20 services are healthy, the live UI is running on\n\u003chttp://localhost:3003\u003e, and the dataspace is seeded with:\n\n- **5 participants** — AlphaKlinik Berlin, Limburg Medical Centre, PharmaCo Research AG,\n  MedReg DE, Institut de Recherche Santé\n- **10 Verifiable Credentials** (EHDSParticipantCredential, DataProcessingPurposeCredential)\n- **9 data assets** (FHIR R4, OMOP CDM, HealthDCAT-AP)\n- **Contract negotiations** — PharmaCo↔AlphaKlinik (FHIR data), MedReg↔LMC (catalog metadata)\n- **Active data transfers** via DCore data planes\n\n### Verify Services\n\n```bash\n./scripts/bootstrap-jad.sh --status\n```\n\n### Start the UI\n\n**Option A — Development mode** (hot reload, ideal for code changes):\n\n```bash\ncd ui \u0026\u0026 npm install \u0026\u0026 npm run dev\n```\n\nOpen \u003chttp://localhost:3000\u003e. Log in with `edcadmin` / `edcadmin` (admin),\n`clinicuser` / `clinicuser` (hospital), or `regulator` / `regulator` (HDAB).\n\n**Option B — Live Docker container** (production build connected to JAD cluster):\n\nThe bootstrap script (`./scripts/bootstrap-jad.sh`) automatically builds and starts\nthe live UI on port 3003. To rebuild manually after code changes:\n\n```bash\ndocker compose -f docker-compose.yml \\\n               -f docker-compose.jad.yml \\\n               -f docker-compose.live.yml \\\n               up -d --build graph-explorer\n```\n\nOpen \u003chttp://localhost:3003\u003e. This runs the production-built UI inside Docker,\nconnected to the live Neo4j, Keycloak, and EDC-V services in the cluster.\n\n| Port | Mode   | Compose Files                                  | Description                 |\n| ---- | ------ | ---------------------------------------------- | --------------------------- |\n| 3000 | Static | `docker-compose.yml` only                      | Mock/static data, no JAD    |\n| 3003 | Live   | `docker-compose.yml` + `jad` + `live` overlays | Full JAD cluster, live data |\n\n\u003e **Rebuild after UI code changes:** \u003e `docker compose -f docker-compose.yml -f docker-compose.jad.yml -f docker-compose.live.yml up -d --build graph-explorer`\n\n### Run Seeding Separately\n\nIf the stack is already running, you can re-seed without restarting:\n\n```bash\n# Re-run the full bootstrap seed pipeline (identity fixup + definitions + dataspace)\n./scripts/bootstrap-jad.sh --seed\n\n# Or run individual seed phases:\n\n# 1. Seed IssuerService credential definitions (idempotent, runs from host)\n./jad/seed-issuer-defs.sh\n\n# 2. Full dataspace seed pipeline (tenants → credentials → policies → assets → …)\n./jad/seed-all.sh\n\n# Resume from a specific step (e.g. step 5 = negotiations)\n./jad/seed-all.sh --from 5\n\n# Run only one step\n./jad/seed-all.sh --only 3\n```\n\n**Seed dependency order:** IssuerService definitions must exist _before_\nrunning `seed-all.sh`, because the CFM onboarding agent needs credential\ndefinitions to issue Verifiable Credentials during tenant onboarding.\nThe bootstrap script handles this automatically.\n\nSeed-all steps: (1) health tenants, (2) EHDS credentials, (3) ODRL policies,\n(4) data assets, (5) contract negotiations, (6) federated catalog, (7) data transfers.\n\n### Bootstrap Phases\n\nThe bootstrap script (`./scripts/bootstrap-jad.sh`) orchestrates startup in the\ncorrect dependency order with health checks at each phase:\n\n| Phase | Services                                                                    | Health Check                           |\n| ----- | --------------------------------------------------------------------------- | -------------------------------------- |\n| 1     | PostgreSQL, Vault, Keycloak, NATS                                           | HTTP readiness / health endpoints      |\n| 2     | vault-bootstrap (sidecar)                                                   | Log polling for success message        |\n| 3     | Traefik reverse proxy                                                       | —                                      |\n| 4     | Control Plane, Data Plane FHIR, Data Plane OMOP, IdentityHub, IssuerService | Management API readiness (accepts 401) |\n| 4b    | Neo4j Query Proxy                                                           | `/health` endpoint                     |\n| 5     | Tenant Manager, Provision Manager, 4× CFM agents                            | —                                      |\n| 6     | Neo4j                                                                       | —                                      |\n| 6b    | Graph Explorer Live UI (port 3003)                                          | Docker build + start                   |\n| 7     | JAD seed (jad-seed container) — best-effort                                 | Exit code (non-fatal)                  |\n| 8     | IssuerService identity fixup (SQL → restart → DID verification)             | DID document check                     |\n| 8b    | IssuerService attestation + credential definitions (`seed-issuer-defs.sh`)  | HTTP 200/409 per definition            |\n| 9     | Dataspace seeding (`seed-all.sh`: 7 phases)                                 | Exit code                              |\n\nThe script is **idempotent** — safe to re-run on an existing stack. It performs\n`docker compose down --remove-orphans` at the start to clean up stale containers.\n\n### Troubleshooting Seeding\n\nIf participants don't appear in the UI or EDC-V API after bootstrap:\n\n1. **Check IssuerService credential definitions exist:**\n\n   ```bash\n   docker exec health-dataspace-postgres psql -U issuer -d issuerservice \\\n     -c \"SELECT id, credential_type FROM credential_definitions;\"\n   ```\n\n   If empty, run: `./jad/seed-issuer-defs.sh`\n\n2. **Check participant contexts in EDC-V:**\n\n   ```bash\n   curl -s http://localhost:11003/api/mgmt/v5alpha/participants | python3 -m json.tool\n   ```\n\n   If empty, participants haven't been onboarded yet — re-run `./jad/seed-all.sh --only 1`.\n\n3. **Re-run full seed pipeline:**\n\n   ```bash\n   ./scripts/bootstrap-jad.sh --seed\n   ```\n\n### Verify Deployment (E2E Tests)\n\nAfter bootstrap completes, run the end-to-end test suite to verify all\ninfrastructure, dataspace state, and API routes:\n\n```bash\n./scripts/run-e2e-tests.sh\n```\n\nExpected result: **166 PASS**, 0 FAIL (Keycloak auth tests require SSO and are skipped in static mode).\n\n### Tear Down\n\n```bash\n./scripts/bootstrap-jad.sh --down     # Stop all services\n./scripts/bootstrap-jad.sh --reset    # Stop + remove volumes (full reset)\n```\n\n---\n\n## Testing\n\nThe project has comprehensive test coverage across unit, API-route, and end-to-end tests.\nSee the full **[Test Coverage Report](docs/test-coverage-report.md)** for detailed metrics and inventory.\n\n| Suite      | Framework    |     Tests |  Files | Status      |\n| ---------- | ------------ | --------: | -----: | ----------- |\n| Unit + API | Vitest + RTL |     1,490 |     78 | ✅ All pass |\n| E2E        | Playwright   |       166 |     18 | ✅ All pass |\n| **Total**  |              | **1,656** | **96** | ✅          |\n\n**Code coverage** (v8): 93.78% statements · 81.65% branches · 89.57% functions · 94.73% lines\n\n```bash\n# Run unit tests\ncd ui \u0026\u0026 npx vitest run\n\n# Run with coverage report\nnpx vitest run --coverage\n\n# Run Playwright E2E tests (requires dev server on :3000)\nnpx playwright test\n\n# Regenerate the test report markdown\n../scripts/generate-test-report.sh\n```\n\nCI runs on every push and PR via [`.github/workflows/test.yml`](.github/workflows/test.yml).\nCoverage reports and Playwright HTML reports are uploaded as GitHub Actions artifacts.\n\n**Published Reports (GitHub Pages):**\n\n| Report                | URL                                                                                                                |\n| --------------------- | ------------------------------------------------------------------------------------------------------------------ |\n| Unit Test Coverage    | [test-reports/](https://ma3u.github.io/MinimumViableHealthDataspacev2/test-reports/)                               |\n| Playwright E2E Report | [e2e-report/](https://ma3u.github.io/MinimumViableHealthDataspacev2/e2e-report/)                                   |\n| EHDS Journey Report   | [e2e-report/ehds-journey.html](https://ma3u.github.io/MinimumViableHealthDataspacev2/e2e-report/ehds-journey.html) |\n| CI E2E Report         | [ci-e2e-report/](https://ma3u.github.io/MinimumViableHealthDataspacev2/ci-e2e-report/)                             |\n| EHDS User Journey     | [docs/FULL_USER_JOURNEY.md](docs/FULL_USER_JOURNEY.md)                                                             |\n\n\u003e **Note:** `test-reports/` and `ci-e2e-report/` are generated during CI only\n\u003e (by `pages.yml`). The committed `e2e-report/` in `ui/public/` is the source\n\u003e of truth for the live Playwright report — it includes results from both\n\u003e chromium and live (JAD stack) test projects.\n\n**Generating the E2E Report Locally:**\n\nThe Playwright report (including the EHDS Journey Report) is generated\nautomatically when you run E2E tests. To produce a report that includes\nlive JAD stack tests:\n\n```bash\n# 1. Start the full JAD stack (includes live UI on :3003)\n./scripts/bootstrap-jad.sh\n\n# 2. Run both chromium (mock) and live (JAD) E2E test projects\ncd ui\nPLAYWRIGHT_BASE_URL=http://localhost:3003 npx playwright test --project=chromium --project=live\n\n# 3. View the report locally\nopen playwright-report/index.html          # interactive Playwright report\nopen playwright-report/ehds-journey.html   # EHDS journey report\n```\n\n**Publishing to GitHub Pages:**\n\nTo update the committed E2E report that appears on GitHub Pages:\n\n```bash\n# Copy the local report to the committed public/ folder\nrm -rf ui/public/e2e-report\ncp -r ui/playwright-report ui/public/e2e-report\n\n# Commit and push — pages.yml will deploy it\ngit add ui/public/e2e-report\ngit commit -m \"Update E2E report from local JAD stack run\"\ngit push\n```\n\nThe report includes a screenshot for every test (captured automatically), traces on retries,\nand video recordings when tests are retried — making visual regression and debugging easy.\n\n**GitHub Pages Deployment:** The site is deployed by\n[`.github/workflows/pages.yml`](.github/workflows/pages.yml) using the\n\"GitHub Actions\" source (not \"Deploy from a branch\"). The repo Pages settings\nmust use **Source: GitHub Actions** — if switched to \"Deploy from a branch\",\na built-in Jekyll workflow will overwrite the Next.js static export with\nthe rendered README.md.\n\n---\n\n## Development\n\nThis project uses [pre-commit](https://pre-commit.com/) hooks to keep Markdown, YAML, and JSON\nsources consistently formatted. Hooks run automatically on every `git commit` and fix files in place (e.g. trailing whitespace, missing newlines, Prettier formatting).\n\n### Run pre-commit checks\n\nRun all hooks against every file without making a commit:\n\n```bash\npre-commit run --all-files\n```\n\n\u003e **Tip:** If a commit fails because a hook auto-fixed a file, stage the fixed file and retry:\n\u003e\n\u003e ```bash\n\u003e git add \u003cfile\u003e \u0026\u0026 git commit\n\u003e ```\n\n### Neo4j driver note\n\nThe UI driver (`ui/src/lib/neo4j.ts`) is configured with `{ disableLosslessIntegers: true }`. By default the JavaScript Neo4j driver wraps 64-bit integers as `{ low, high }` objects; this flag converts them to native JavaScript numbers so stat card values render correctly instead of showing `[object Object]`.\n\n### JAD Stack (EDC-V + CFM + DCore)\n\nThe full dataspace connector stack is defined in `docker-compose.jad.yml` using container images from the [JAD (Joint Architecture Demo)](https://github.com/Metaform/jad). This runs 19 services including EDC-V Control Plane, dual DCore Data Planes (FHIR PUSH + OMOP PULL), Neo4j Query Proxy, IdentityHub (DCP v1.0), IssuerService, Keycloak, Vault, NATS, and CFM agents.\n\n```bash\n# Start full JAD stack (+ Neo4j from base compose)\n./scripts/bootstrap-jad.sh\n\n# Or manually with docker compose\ndocker compose -f docker-compose.yml -f docker-compose.jad.yml up -d\n\n# Check status\n./scripts/bootstrap-jad.sh --status\n\n# Tear down\n./scripts/bootstrap-jad.sh --down\n```\n\nService endpoints after startup:\n\n| Service           | URL                       | Credentials       |\n| ----------------- | ------------------------- | ----------------- |\n| Traefik Dashboard | http://localhost:8090     | —                 |\n| Keycloak Admin    | http://keycloak.localhost | admin / admin     |\n| Vault UI          | http://vault.localhost    | token: root       |\n| Control Plane     | http://cp.localhost       | OAuth2 (Keycloak) |\n| Data Plane FHIR   | http://dp-fhir.localhost  | OAuth2 (Keycloak) |\n| Data Plane OMOP   | http://dp-omop.localhost  | OAuth2 (Keycloak) |\n| Neo4j Query Proxy | http://proxy.localhost    | internal (DCore)  |\n| Identity Hub      | http://ih.localhost       | OAuth2 (Keycloak) |\n| Issuer Service    | http://issuer.localhost   | OAuth2 (Keycloak) |\n| Tenant Manager    | http://tm.localhost       | OAuth2 (Keycloak) |\n| Provision Manager | http://pm.localhost       | OAuth2 (Keycloak) |\n| NATS Monitor      | http://localhost:8222     | —                 |\n\nConfiguration files are in the `jad/` directory. OpenAPI specs are in `jad/openapi/`. EDC-V asset registration payloads (Phase 4a) are in `jad/edcv-assets/`.\n\n### All Docker Service Endpoints\n\nComplete list of all services and their direct `localhost` port mappings when the full stack\n(base + JAD + UI) is running in Docker / OrbStack:\n\n| Service                 | Port(s)                       | URL / Endpoint         | Description                                               |\n| ----------------------- | ----------------------------- | ---------------------- | --------------------------------------------------------- |\n| **Graph Explorer UI**   | 3000                          | http://localhost:3000  | Next.js 14 app — mock/static mode (base compose only)     |\n| **Graph Explorer Live** | 3003                          | http://localhost:3003  | Next.js 14 app — live mode with JAD cluster data          |\n| **Neo4j Browser**       | 7474                          | http://localhost:7474  | Neo4j Browser (primary instance, `neo4j/healthdataspace`) |\n| **Neo4j Bolt**          | 7687                          | bolt://localhost:7687  | Bolt driver endpoint (primary instance)                   |\n| **Neo4j SPE-2 Browser** | 7475                          | http://localhost:7475  | Neo4j Browser (second participant)                        |\n| **Neo4j SPE-2 Bolt**    | 7688                          | bolt://localhost:7688  | Bolt driver endpoint (second participant)                 |\n| **Neo4j Query Proxy**   | 9090                          | http://localhost:9090  | DCore ↔ Neo4j bridge (NLQ + federated queries)           |\n| **Traefik Proxy**       | 80                            | http://localhost       | Reverse proxy — routes `*.localhost` domains              |\n| **Traefik Dashboard**   | 8090                          | http://localhost:8090  | Traefik admin dashboard                                   |\n| **Keycloak**            | 8080, 9000                    | http://localhost:8080  | IAM / OAuth2 provider (`admin/admin`)                     |\n| **Vault**               | 8200                          | http://localhost:8200  | HashiCorp Vault (token: `root`)                           |\n| **Control Plane**       | 11003                         | http://localhost:11003 | EDC-V Management API (DSP + DCP)                          |\n| **Data Plane FHIR**     | 11002                         | http://localhost:11002 | DCore Data Plane — FHIR PUSH                              |\n| **Data Plane OMOP**     | 11012                         | http://localhost:11012 | DCore Data Plane — OMOP PULL                              |\n| **Identity Hub**        | 11005                         | http://localhost:11005 | DCP v1.0 Decentralized Claims                             |\n| **Issuer Service**      | 10013                         | http://localhost:10013 | Verifiable Credential issuer                              |\n| **Tenant Manager**      | 11006                         | http://localhost:11006 | Multi-tenant management API                               |\n| **Provision Manager**   | 11007                         | http://localhost:11007 | Resource provisioning API                                 |\n| **NATS**                | 4222 (client), 8222 (monitor) | http://localhost:8222  | Message bus — monitoring dashboard                        |\n| **PostgreSQL**          | 5432                          | localhost:5432         | Shared database (8 schemas)                               |\n| CFM EDC-V Agent         | —                             | internal               | Connector Framework Module agent                          |\n| CFM Keycloak Agent      | —                             | internal               | Keycloak integration agent                                |\n| CFM Onboarding Agent    | —                             | internal               | Participant onboarding agent                              |\n| CFM Registration Agent  | —                             | internal               | Service registration agent                                |\n\n\u003e **Note:** Inside Docker, services communicate using Docker DNS names (e.g. `bolt://neo4j:7687`).\n\u003e The `localhost` ports above are the host-mapped ports for external access from your browser or\n\u003e development tools.\n\n---\n\n## Azure Deployment\n\nThe full stack is deployed to **Azure Container Apps** for shared team access and CI/CD validation.\n\n### Live demo\n\n\u003e **[https://ehds.mabu.red](https://ehds.mabu.red)**\n\n**Availability — Mon–Fri 07:00–20:00 Europe/Berlin** (outside these hours all 13 Container\nApps are scaled to 0 replicas and PostgreSQL is stopped to keep the deployment under the\n**50 EUR/month** personal Azure credit). Pick any of the [7 demo personas](#demo-users--roles)\nto sign in — e.g. `matthias` / `password`.\n\n| Window          | Mon–Fri           | Sat–Sun           |\n| --------------- | ----------------- | ----------------- |\n| 07:00–20:00 CET | 🟢 Online         | 🔴 Offline (cost) |\n| 20:00–07:00 CET | 🔴 Offline (cost) | 🔴 Offline (cost) |\n\nCold-start after the Monday scale-up takes ~90 seconds (Neo4j + Keycloak warmup + Vault\nre-bootstrap). If the URL is unreachable, you're either outside the working window or in\nthe morning cold-start — try again shortly, or run the stack locally via\n[Quick Start](#quick-start).\n\nSee [ADR-016: ACA Off-Hours Scale-Down](docs/ADRs/ADR-016-aca-off-hours-scaledown.md) for\nthe cost model and scheduling rationale. ADR-015 documents the single-VM fallback path.\n\n### Topology\n\n| Resource       | Details                                                                                                                              |\n| -------------- | ------------------------------------------------------------------------------------------------------------------------------------ |\n| Environment    | `mvhd-env` (West Europe, consumption plan)                                                                                           |\n| Container Apps | 13 (UI, Neo4j, Keycloak, Vault, NATS, PostgreSQL, Neo4j Proxy, EDC-V, DCore, IdentityHub, IssuerService, CFM TManager, CFM PManager) |\n| ACA Jobs       | 3 (bootstrap, schema, seed)                                                                                                          |\n| CI/CD          | GitHub Actions with OIDC federation — no stored credentials                                                                          |\n| E2E Validation | Playwright runs against Azure after every deploy                                                                                     |\n\n**Guides:**\n\n- [Azure Deployment Guide](docs/azure-deployment-guide.md) — full setup, endpoints, and troubleshooting\n- [Deploy workflow](.github/workflows/deploy-azure.yml) — CI/CD pipeline\n- [Deployment scripts](scripts/azure/) — 11 scripts for provisioning and lifecycle management\n- [ACA off-hours schedule (ADR-016)](.github/workflows/aca-schedule.yml) — nightly / weekend scale-down workflow\n- [Personal dev VM fallback (ADR-015)](scripts/azure-vm/README.md) — single-VM variant (superseded by ADR-016)\n\n---\n\n## Documentation\n\nDetailed reference documents live in the `docs/` directory. The UI documentation is also\navailable online at **[ma3u.github.io/MinimumViableHealthDataspacev2/docs](https://ma3u.github.io/MinimumViableHealthDataspacev2/docs)**.\n\n| Document                                                        | Description                                                                 |\n| --------------------------------------------------------------- | --------------------------------------------------------------------------- |\n| [Implementation Roadmap](docs/planning-health-dataspace-v2.md)  | Full planning document: 12 phases, 9+ ADRs, architecture decisions.         |\n| [Architecture Decision Records](docs/ADRs/)                     | Standalone ADRs (001–013): data storage, testing, Azure, WCAG, security.    |\n| [Azure Deployment Guide](docs/azure-deployment-guide.md)        | Azure Container Apps setup, endpoints, post-deploy configuration.           |\n| [Graph Schema Reference](docs/health-dataspace-graph-schema.md) | Full 5-layer Neo4j schema: node labels, properties, indexes, relationships. |\n| [E2E Test Report](docs/e2e-test-report.md)                      | Playwright E2E results: 778 tests across 29 spec files.                     |\n| [Unit Test Coverage](docs/test-coverage-report.md)              | Vitest coverage: 1,490 tests, 94% statement coverage across 78 files.       |\n| [SIMPL-Open Gap Analysis](docs/simpl-ehds-gap-analysis.md)      | Alignment assessment with EU SIMPL programme requirements.                  |\n| [Quality Gates](docs/quality-gates.md)                          | CI quality standards: lint, type-check, test thresholds.                    |\n| [Full User Journey](docs/FULL_USER_JOURNEY.md)                  | EHDS 8-step journey from onboarding to analytics with sequence diagram.     |\n| [OpenAPI Specs](jad/openapi/)                                   | OpenAPI specs for all JAD services (Management, Identity, Issuer APIs).     |\n| [Bruno API Collection](bruno/MVHDv2/)                           | Bruno collection covering all 38 Next.js API routes with 3 environments.    |\n| [Interactive API Reference](ui/public/openapi.yaml)             | OpenAPI 3.1 spec rendered as Swagger UI at `/docs/developer/api`.           |\n\n---\n\n## Implementation Status\n\nAll 12 phases are **✅ Complete** — from infrastructure migration through Azure cloud deployment.\n\n| Phase | Description              | Key Deliverables                                                                |\n| ----- | ------------------------ | ------------------------------------------------------------------------------- |\n| 1     | Infrastructure Migration | 22-service Docker Compose (EDC-V + DCore + CFM), Vault, NATS, Traefik           |\n| 2     | Identity \u0026 Trust         | DID:web for 5 participants, 15 Verifiable Credentials (DCP v1.0), Keycloak SSO  |\n| 3     | Health Knowledge Graph   | 5-layer Neo4j schema, 127 Synthea patients, FHIR→OMOP pipeline, EEHRxF profiles |\n| 4     | Dataspace Integration    | DSP 2025-1 contract negotiation, DCore FHIR/OMOP transfers, audit trail         |\n| 5     | Federated Queries        | Multi-site federation, k-anonymity, Text2Cypher NLQ (9 templates + LLM)         |\n| 6     | Web Application          | 22 Next.js pages, 36 API routes, 7 personas, role-based navigation              |\n| 7     | Protocol Compliance      | DSP TCK (140+ tests), DCP suite, EHDS domain tests (Art. 53 enforcement)        |\n| 8     | Automated Testing        | 1,490 unit tests (94% coverage), 778 E2E tests (Playwright)                     |\n| 9     | Documentation            | 4 in-app doc pages, 5 interactive architecture diagrams, navigation restructure |\n| 10    | Tasks Dashboard          | Aggregated contract/transfer pipeline view with step indicators                 |\n| 11    | System Topology          | Per-participant service health view, component info catalog                     |\n| 12    | Policy Seeding           | QuerySpec fix, 14 EHDS access policies across 5 organisations                   |\n\n**Additional capabilities:**\n\n- **WCAG 2.2 AA** — Zero accessibility violations, automated axe-core enforcement ([ADR-010](docs/ADRs/ADR-010-wcag-accessibility.md))\n- **Security Testing** — 50 automated OWASP/BSI checks, Trivy CVE scanning, Gitleaks ([ADR-011](docs/ADRs/ADR-011-security-testing.md))\n- **Azure Deployment** — 13 Container Apps + 3 Jobs with CI/CD and E2E validation ([ADR-012](docs/ADRs/ADR-012-azure-container-apps.md))\n- **SIMPL-Open Alignment** — 5/7 EU programme requirements met ([ADR-013](docs/ADRs/ADR-013-simpl-open-alignment.md))\n- **Weekly Demo Reset** — Idempotent Sunday-night re-seed of Neo4j, Vault, and Postgres for a clean Monday-morning demo ([ADR-014](docs/ADRs/ADR-014-weekly-demo-reset.md))\n- **ACA Off-Hours Scale-Down** — Mon–Fri 07:00–20:00 Europe/Berlin schedule keeps the full ACA topology under the personal €50/month credit ([ADR-016](docs/ADRs/ADR-016-aca-off-hours-scaledown.md), supersedes [ADR-015](docs/ADRs/ADR-015-single-vm-dev-deployment.md))\n- **Persistent Storage on ACA** — Azure Files volumes for Neo4j (`/data`, `/logs`) and Vault (`/vault/data`); knowledge graph and Vault secrets survive revision restarts and morning scale-up ([ADR-017](docs/ADRs/ADR-017-persistent-storage-aca.md))\n\nFor detailed phase descriptions, sub-tasks, and architecture decisions, see the full **[Implementation Roadmap](docs/planning-health-dataspace-v2.md)** and **[Architecture Decision Records](docs/ADRs/)**.\n\n### Container Inventory\n\nThe full stack comprises **22 containers** across two Docker Compose files. Containers are\ngrouped by startup tier — Docker Compose launches each tier in parallel once all dependencies\nin the previous tier report healthy. The `neo4j-spe2` and `jad-seed` containers only start\nwhen their respective profiles (`federated`, `seed`) are explicitly activated.\n\n![ORB K8s cluster with the EHDS Integration Hub](image-1.png)\n\n```\nTier 0 ──► Tier 1 ──► Tier 2 ──► Tier 3 ──► Tier 4\n```\n\n#### Tier 0 — Infrastructure Foundations (no dependencies, start first)\n\n| #   | Service        | Container Name                | Compose File             | Port(s)    | Description                                                                                                                                       |\n| --- | -------------- | ----------------------------- | ------------------------ | ---------- | ------------------------------------------------------------------------------------------------------------------------------------------------- |\n| 1   | `neo4j`        | `health-dataspace-neo4j`      | `docker-compose.yml`     | 7474, 7687 | Neo4j 5 Community — primary graph database with APOC + n10s plugins. Stores the 5-layer knowledge graph.                                          |\n| 2   | `postgres`     | `health-dataspace-postgres`   | `docker-compose.jad.yml` | 5432       | PostgreSQL 17 — multi-database server (8 schemas: controlplane, identityhub, issuerservice, dataplane, dataplane_omop, keycloak, cfm, redlinedb). |\n| 3   | `vault`        | `health-dataspace-vault`      | `docker-compose.jad.yml` | 8200       | HashiCorp Vault (dev mode) — secret management for signing keys, AES keys, and data plane token keys.                                             |\n| 4   | `nats`         | `health-dataspace-nats`       | `docker-compose.jad.yml` | 4222, 8222 | NATS JetStream — async messaging bus for contract negotiation and transfer process state machine events.                                          |\n| 5   | `traefik`      | `health-dataspace-traefik`    | `docker-compose.jad.yml` | 80, 8090   | Traefik v3 reverse proxy — routes `*.localhost` domains to internal services; replaces K8s Gateway API.                                           |\n| 6   | `neo4j-spe2` ¹ | `health-dataspace-neo4j-spe2` | `docker-compose.yml`     | 7475, 7688 | Neo4j 5 Community — second Secure Processing Environment for federated query testing (Phase 5).                                                   |\n\n\u003e ¹ Only starts with `--profile federated`.\n\u003e ³ Port 3003 is exposed when using the `docker-compose.live.yml` overlay for live JAD cluster data.\n\n#### Tier 1 — Core Identity \u0026 UI (depend on Tier 0)\n\n| #   | Service          | Container Name              | Compose File             | Port(s)      | Dependencies | Description                                                                                                                                                        |\n| --- | ---------------- | --------------------------- | ------------------------ | ------------ | ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| 7   | `keycloak`       | `health-dataspace-keycloak` | `docker-compose.jad.yml` | 8080, 9000   | `postgres`   | Keycloak — OAuth2/OIDC identity provider. Hosts `edcv` realm with 3 demo users, PKCE client, and role mappings.                                                    |\n| 8   | `graph-explorer` | `health-dataspace-ui`       | `docker-compose.yml`     | 3000, 3003 ³ | `neo4j`      | Next.js 14 UI — Graph Explorer, Catalogue, Analytics, Portal views. Connects to Neo4j (Bolt) and Keycloak (OIDC). Port 3003 via `docker-compose.live.yml` overlay. |\n\n#### Tier 2 — EDC-V Core + Identity Services (depend on Tier 0 + 1)\n\n| #   | Service           | Container Name                     | Compose File             | Port(s) | Dependencies                            | Description                                                                                                                              |\n| --- | ----------------- | ---------------------------------- | ------------------------ | ------- | --------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |\n| 9   | `vault-bootstrap` | `health-dataspace-vault-bootstrap` | `docker-compose.jad.yml` | —       | `vault`, `keycloak`                     | Init job (runs once, then exits) — configures JWT auth backend, secrets engine, policies, and signing keys in Vault.                     |\n| 10  | `controlplane`    | `health-dataspace-controlplane`    | `docker-compose.jad.yml` | 11003   | `postgres`, `vault`, `nats`, `keycloak` | EDC-V Control Plane — DSP protocol engine, management API, contract negotiation state machine, ODRL policy evaluation.                   |\n| 11  | `identityhub`     | `health-dataspace-identityhub`     | `docker-compose.jad.yml` | 11005   | `postgres`, `vault`, `keycloak`         | DCP v1.0 Identity Hub — stores Verifiable Credentials, handles DID resolution, and credential presentation requests.                     |\n| 12  | `issuerservice`   | `health-dataspace-issuerservice`   | `docker-compose.jad.yml` | 10013   | `postgres`, `vault`, `keycloak`         | Verifiable Credential Issuer — trust anchor that issues MembershipCredential, EHDSParticipantCredential, and DataQualityLabelCredential. |\n| 13  | `tenant-manager`  | `health-dataspace-tenant-manager`  | `docker-compose.jad.yml` | 11006   | `postgres`, `keycloak`                  | CFM Tenant Manager — multi-tenant participant lifecycle (create, activate, deactivate dataspace tenants).                                |\n\n#### Tier 3 — Data Planes, Proxy, Provisioning \u0026 CFM Agents (depend on Tier 2)\n\n| #   | Service                  | Container Name                            | Compose File             | Port(s) | Dependencies                                                     | Description                                                                                                                                            |\n| --- | ------------------------ | ----------------------------------------- | ------------------------ | ------- | ---------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| 14  | `dataplane-fhir`         | `health-dataspace-dataplane-fhir`         | `docker-compose.jad.yml` | 11002   | `postgres`, `vault`, `controlplane`                              | DCore Data Plane (FHIR PUSH) — transfers FHIR R4 Bundle JSON to consumer endpoints via HttpData-PUSH protocol.                                         |\n| 15  | `dataplane-omop`         | `health-dataspace-dataplane-omop`         | `docker-compose.jad.yml` | 11012   | `postgres`, `vault`, `controlplane`                              | DCore Data Plane (OMOP PULL) — serves OMOP CDM aggregate query results via HttpData-PULL protocol.                                                     |\n| 16  | `neo4j-proxy`            | `health-dataspace-neo4j-proxy`            | `docker-compose.jad.yml` | 9090    | `controlplane`                                                   | Node.js/Express bridge — translates DCore HTTP requests into Cypher queries; serialises results as FHIR JSON, OMOP JSON/CSV, or HealthDCAT-AP JSON-LD. |\n| 17  | `provision-manager`      | `health-dataspace-provision-manager`      | `docker-compose.jad.yml` | 11007   | `postgres`, `keycloak`, `controlplane`                           | CFM Provision Manager — automated resource provisioning for new tenants (Vault keys, DB schemas, EDC-V registrations).                                 |\n| 18  | `cfm-agents`             | `health-dataspace-cfm-keycloak-agent`     | `docker-compose.jad.yml` | —       | `keycloak`, `tenant-manager`                                     | CFM Keycloak Agent — watches tenant events and provisions Keycloak client registrations and role mappings.                                             |\n| 19  | `cfm-edcv-agent`         | `health-dataspace-cfm-edcv-agent`         | `docker-compose.jad.yml` | —       | `controlplane`, `tenant-manager`                                 | CFM EDC-V Agent — provisions connector resources (data plane selectors, asset definitions) for new tenants.                                            |\n| 20  | `cfm-registration-agent` | `health-dataspace-cfm-registration-agent` | `docker-compose.jad.yml` | —       | `identityhub`, `issuerservice`, `tenant-manager`                 | CFM Registration Agent — registers DID documents and requests Verifiable Credentials from IssuerService for new tenants.                               |\n| 21  | `cfm-onboarding-agent`   | `health-dataspace-cfm-onboarding-agent`   | `docker-compose.jad.yml` | —       | `controlplane`, `identityhub`, `issuerservice`, `tenant-manager` | CFM Onboarding Agent — orchestrates the full onboarding sequence: DID creation → VC issuance → connector setup → catalog entry.                        |\n\n#### Tier 4 — Seed Job (runs once after all services are ready)\n\n| #   | Service      | Container Name              | Compose File             | Port(s) | Dependencies                                                                          | Description                                                                                                                                    |\n| --- | ------------ | --------------------------- | ------------------------ | ------- | ------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |\n| 22  | `jad-seed` ² | `health-dataspace-jad-seed` | `docker-compose.jad.yml` | —       | `controlplane`, `identityhub`, `issuerservice`, `tenant-manager`, `provision-manager` | One-shot seed job — initialises IssuerService credential definitions, creates demo tenants, and triggers provisioning. Exits after completion. |\n\n\u003e ² Only starts with `--profile seed`.\n\n#### Dependency Graph\n\n```\n                    ┌─────────┐\n                    │  neo4j  │\n                    └────┬────┘\n                         │\n                ┌────────▼────────┐\n                │  graph-explorer  │\n                └─────────────────┘\n\n┌──────────┐  ┌──────────┐  ┌──────┐  ┌─────────┐\n│ postgres │  │  vault   │  │ nats │  │ traefik │\n└────┬─────┘  └────┬─────┘  └──┬───┘  └─────────┘\n     │             │            │\n     ├─────────────┼────────────┤\n     │             │            │\n     ▼             ▼            │\n┌──────────┐  ┌──────────────┐  │\n│ keycloak │  │vault-bootstrap│ │\n└────┬─────┘  └──────────────┘  │\n     │                          │\n     ├──────────────────────────┤\n     │                          │\n     ▼                          ▼\n┌──────────────┐  ┌──────────────┐  ┌──────────────┐  ┌────────────────┐\n│ controlplane │  │ identityhub  │  │issuerservice │  │ tenant-manager │\n└──────┬───────┘  └──────┬───────┘  └──────┬───────┘  └───────┬────────┘\n       │                 │                 │                   │\n       ├─────────────────┼─────────────────┼───────────────────┤\n       │                 │                 │                   │\n       ▼                 ▼                 ▼                   ▼\n┌───────────────┐ ┌───────────────┐ ┌───────────┐ ┌─────────────────────┐\n│dataplane-fhir │ │dataplane-omop │ │neo4j-proxy│ │ provision-manager   │\n└───────────────┘ └───────────────┘ └───────────┘ └─────────────────────┘\n                                                  ┌─────────────────────┐\n                                                  │ cfm-agents (×4)     │\n                                                  └─────────────────────┘\n```\n\n---\n\n## Contributing\n\nContributions are welcome — whether that is a bug fix, a new UI view, an improved Cypher query, or additional documentation. Please open an issue first to discuss larger changes before sending a pull request. See [.github/CONTRIBUTING.md](.github/CONTRIBUTING.md) for coding conventions,\nschema change rules, and the PR checklist.\n\n---\n\n## Background\n\n- **Background LinkedIn Article:** [European Health Dataspaces, Digital Twins: A Journey from FHIR Basics to Intelligent Patient Models](https://www.linkedin.com/pulse/european-health-dataspaces-digital-twins-journey-fhir-buchhorn-roth-8t51c/)\n\n---\n\n## Security\n\n- **No patient data** — all clinical records are synthetic (Synthea-generated) and are excluded from the repository via `.gitignore`. Never commit real patient data.\n- **Local credentials only** — the default Neo4j credentials (`neo4j` / `healthdataspace`) are intentionally weak and suitable only for local development. They must not be used in any internet-facing deployment.\n- **Environment files** — production deployments should inject credentials via `.env` files or secret managers. `.env` is excluded from version control via `.gitignore`.\n\n---\n\n## License\n\n[Apache 2.0](LICENSE) © 2026 Matthias Buchhorn-Roth\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fma3u%2Fminimumviablehealthdataspacev2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fma3u%2Fminimumviablehealthdataspacev2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fma3u%2Fminimumviablehealthdataspacev2/lists"}