1. Overview

1.1. Accessing Swagger UI

Many services expose a Swagger UI on port 80 and the path
/swagger-ui/index.html. Use docker ps to find the host port mapping:

docker ps --format 'table {{.Names}}\t{{.Ports}}'

If a container’s port 80 is mapped to 9130 you would access Swagger using the
URL http://localhost:9130/swagger-ui/index.html.

1.2. Folder Structure

This project has the following folders:

Folder Content Description

client/

Client-side components

Simulated client applications

├─ card-terminal-client-simservice/

Electronic health card terminal simulator

└─ vsdm-client-simservice-java/

VSDM2 client simulator

server/

Server-side components

Backend services and proxies

├─ popp-server-mockservice/

PoPP token generation service

├─ vsdm-server-simservice/

VSDM2 data provider

├─ zeta-pdp-server-mockservice/

Zero Trust Policy Decision Point

└─ zeta-pep-server-mockservice/

Zero Trust Policy Enforcement Point

lib/

Reusable libraries

Shared functionality across services

├─ card-client-lib-java/

Card terminal operations library

├─ popp-client-lib-java/

PoPP authentication client library

├─ vsdm-fhir-lib-java/

FHIR resource handling for VSDM

└─ zeta-client-lib-java/

ZeTA integration utilities

test/

Test suites

Integration and end-to-end tests

└─ vsdm-testsuite/

VSDM2 workflow test suite

doc/

Documentation and scripts

Build and deployment automation

├─ bin/vsdm/

Build and Docker Compose scripts

├─ docker/vsdm/

Docker Compose configuration

└─ k8s/

Kubernetes deployment manifests

images/

Static assets

Images for README documentation

jenkinsfiles/

CI/CD pipelines

Jenkins pipeline definitions

2. Example Workflows using Curl

2.1. VSDM Data Retrieval

Listing 1. Retrieve data through the client
curl -X GET 'http://localhost:8220/vsdm/data' \
    -H 'Authorization: Bearer <ACCESS_TOKEN>'

2.2. Card Terminal Operations

Listing 2. Load a card
curl -X POST 'http://localhost:8000/card/load' \
    -H 'Content-Type: application/json' \
    -d '{"cardPath": "cards/egk/valid-egk.xml"}'

2.3. Token Exchange (OAuth 2.0 RFC 8693)

Listing 3. Exchange SMC-B token for ZeTA token
curl -X POST 'http://localhost:9100/token' \
    -H 'Content-Type: application/x-www-form-urlencoded' \
    -d 'grant_type=urn:ietf:params:oauth:grant-type:token-exchange&subject_token=<SMC_B_TOKEN>&subject_token_type=urn:ietf:params:oauth:token-type:access_token'

3. Configuration

3.1. Environment Variables

Each service can be configured via environment variables. Key configuration options:

3.1.1. ZeTA PEP Services

  • PROXY_HTTP_URL - Backend HTTP URL to proxy requests to

  • PROXY_WS_URL - Backend WebSocket URL to proxy requests to

  • PROXY_POPP_REQUIRED - Whether PoPP token validation is required (true/false)

  • WK_ISSUER - OAuth 2.0 issuer URL

  • WK_AUTH_EP - Authorization endpoint

  • WK_TOKEN_EP - Token endpoint

3.1.2. ZeTA PDP Services

  • AUTHZ_SEC_STORE_PATH - Path to PKCS12 keystore file

  • AUTHZ_SEC_STORE_PASS - Keystore password

  • AUTHZ_SEC_KEY_ALIAS - Key alias in keystore

  • AUTHZ_SEC_KEY_PASS - Private key password

3.1.3. Client Services

  • POPP_HTTP_URL - PoPP service HTTP URL

  • VSDM_RESOURCE_SERVER_URL - VSDM service HTTP URL

3.2. Docker Compose Profiles

The Testhub supports different startup profiles for various use cases:

Profile Description

(default)

Backend + Tiger-Proxies + Clients via proxy. Use this for normal development and testing.

perf

Backend + Tiger-Proxies + Clients with direct backend access. Use this for performance testing as it bypasses the Tiger-Proxy.

backend-only

Backend services only (no Tiger-Proxies, no clients). Use this when you want to run your own client implementation against the backend.

3.2.1. Starting with Profiles

Use Docker Compose with the appropriate profile:

# Build Docker images first
./mvnw clean install -Pdocker -DskipTests

# Start full stack (default)
docker compose -f doc/docker/compose-local.yaml up -d

# Start with performance profile (clients bypass Tiger-Proxy)
docker compose -f doc/docker/compose-local.yaml --profile perf up -d

# Start backend only (no clients)
docker compose -f doc/docker/compose-local.yaml --profile backend-only up -d

# Stop all containers (all profiles)
docker compose -f doc/docker/compose-local.yaml --profile perf --profile backend-only down -v

3.3. Custom Configuration

To customize service configuration:

  1. Edit doc/docker/compose-local.yaml for Docker Compose setup

  2. Or create custom application-<profile>.yaml files in each service’s src/main/resources directory

4. Architecture

The Testhub implements a Zero Trust Architecture with multiple layers of services communicating through authenticated channels.

4.1. Key Components

The Testhub consists of several interconnected services:

Client Services:

  • VSDM Client SimService - Simulates VSDM2 client operations

  • Card Terminal Client SimService - Simulates electronic health card (eGK) terminal operations

Server Services:

  • VSDM Server SimService - Provides VSDM2 data for testing

  • PoPP Server MockService - Simulates Proof of Possession authentication server

  • ZeTA PDP Server MockService - Policy Decision Point for Zero Trust Architecture

  • ZeTA PEP Server MockService - Policy Enforcement Point proxy service

Libraries:

  • Card Client Library - Reusable card terminal operations

  • PoPP Client Library - PoPP authentication client functionality

  • VSDM FHIR Library - FHIR resource handling for VSDM

  • ZeTA Client Library - ZeTA integration utilities

All components are designed as mock/simulation services for development and testing purposes, not for production use.

4.2. Component Overview (Stufe 2)

An architecture diagram of Testhub

4.3. Service Descriptions

Service Purpose

Card Terminal Client

Simulates electronic health card (eGK, SMC-B, HBA) terminal operations with PACE protocol support

VSDM Client

Simulates VSDM2 client operations including data retrieval and PoPP token validation

ZeTA PEP PoPP

Policy Enforcement Point proxy for PoPP validates tokens and forwards requests

ZeTA PDP PoPP

Policy Decision Point for PoPP performs OAuth 2.0 token exchange (RFC 8693)

PoPP Server

Backend PoPP service generates and validates Proof of Possession tokens

VSDM Server

Backend VSDM service provides VSDM2 data from YAML test fixtures

Tiger Proxy UI

Local Tiger Proxy between PS-Simulation and PoPP and VSDM-Servers

The services are exposed on different ports to the local environment.
To access the containers when applicable, use the following script to find the host port mapping:

docker ps --format 'table {{.Names}}\t{{.Ports}}'

5. How to integrate custom PS (Primärsystem)

ZETA is only configured for the VSDM-Server, not the PoPP-Server.

Integrating a custom Primärsystem (PS) is a primary design goal of the TI 2.0
Testhub. Currently, however, it requires some manual steps:

  • Start the Testhub with the backend-only profile to disable the built-in PS-Simulators (VSDM Client and Card Terminal Client):

docker compose -f doc/docker/compose-local.yaml --profile backend-only up -d
  • The custom PS has to be started (e.g. add as a new service in
    doc/docker/backend/compose-local.yaml, but could simply be started outside of
    docker compose as well)

  • Optional: To use the PS with the VSDM-Testsuite it has to implement the same
    API as the existing vsdm-client (which can be seen at
    http://localhost:8220/v3/api-docs)

You can connect to the resource servers as follows:

VSDM-Server

Use the port that is forwarded to the internal port 443 of
vsdm-zeta-ingress. If the port is 9119 you can connect from the Docker host
using https://localhost:9119/.

PoPP-Server

Use the port that is forwarded to the internal port 80 of
popp-ingress.

The usage of the Zeta-SDK can be seen in the existing
vsdm-client-simservice-java implementation. The class ZetaSdkClientAdapter.java
uses the client while VsdmZetaSdkClientConfig.java configures it.

6. How to integrate VSDM services

6.1. Scenario 1: Replace the Resource Server behind ZetaGuard

If you only want to replace the Resource Server (keeping ZetaGuard as PEP/proxy), follow these steps.

6.1.1. Prerequisites

  • The external resource server must be started and reachable from the Testhub instance.

6.1.2. Steps

  • Make sure the vsdm server simulation is not started in the compose setup.

# File: compose-vsdm-server.yaml (or the compose file that contains the vsdm server)
# Example: comment out the block for the vsdm-server
# vsdm-server:
#   image: ...
#   ports:
#     - "80:8080"
  • In the Testhub compose settings: under the block that describes the PEP (e.g. vsdm-zeta-pep), remove or comment out the dependency on the internal vsdm-server (depends_on).

# Example: in compose-vsdm-server.yaml or compose-local.yaml
# under vsdm-zeta-pep:
#   depends_on:
#     - vsdm-server
# => comment out or remove
  • Configure the PEP so that it forwards to the external (real) Resource Server.

Edit the vsdm-zeta-pep.conf file and replace the proxy_pass line:

# Before (local vsdm-server in compose):
proxy_pass http://vsdm-server:80/;

# After (real resource server):
proxy_pass http://URL_TO_REAL_SERVER/;

Tip: Pay attention to the trailing / and to TLS (https://…​;) when required.

6.2. Scenario 2: Replace the complete domain service (ZetaGuard + Resource Server)

If the complete domain service (ZetaGuard and the Resource Server) is operated externally, follow these steps.

6.2.1. Prerequisites

  • The external domain service (ZetaGuard + Resource Server) must be started and reachable from the Testhub instance.

6.2.2. Steps

  • Do not start the internal simulations: both the vsdm server simulation and the internal ZetaGuard must not be started in the compose configuration.

# In compose-local.yaml: comment out the include of compose-vsdm-server.yaml
# - include: compose-vsdm-server.yaml
  • Replace the vsdm-zeta-ingress entry in tiger.yaml (or the file that defines the ingress / routing for vsdm) with the URL of the domain service.

# File: tiger.yaml
# Before:
vsdm-zeta-ingress: http://vsdm-zeta-ingress
# After:
vsdm-zeta-ingress: http://URL_TO_DOMAIN_SERVICE
  • Adjust the client configuration: in vsdm-client/application-local.yaml configure the resourceServerUrl to the URL of the external vsdm-zeta-pep service or directly to the domain service, depending on your architecture.

# File: vsdm-client/application-local.yaml
resourceServerUrl: "http://URL_TO_VSDM_ZETA_PEP/"

Note: If the external domain service uses TLS, use https://…​; and make sure certificates / CA trust are configured accordingly.

6.3. Examples and useful commands

  • Search the compose files for vsdm-server, vsdm-zeta-pep or vsdm-zeta-ingress to quickly find the relevant blocks:

grep -R "vsdm-server\|vsdm-zeta-pep\|vsdm-zeta-ingress" -n .
  • Example: simple change with sed (local; check with git diff first):

# Replace proxy_pass line in vsdm-zeta-pep.conf (create backup first!)
cp vsdm-zeta-pep.conf vsdm-zeta-pep.conf.bak
sed -i.bak "s|proxy_pass http://vsdm-server:80/;|proxy_pass http://URL_TO_REAL_SERVER/;|g" vsdm-zeta-pep.conf

6.4. Notes / Troubleshooting

  • After changes to compose files always restart the affected containers (e.g. docker compose down && docker compose up -d for the changed services).

  • Test reachability of the external service from the Testhub instance (e.g. curl from the host/container).

  • Pay attention to CORS, authentication and TLS settings if clients report errors when accessing the external service.

6.5. Summary

This document shows the minimal changes required to either replace only the Resource Server behind ZetaGuard or to operate the complete domain service (ZetaGuard + Resource Server) externally.
The main changes are:

  • Commenting out/removing the internal simulations in the compose files

  • Adjusting proxy_pass in vsdm-zeta-pep.conf

  • Replacing ingress/URL references in tiger.yaml and vsdm-client/application-local.yaml

7. FAQ / Troubleshooting

7.1. Testhub

7.1.1. Why are there so many files in the SMC-B ZIP file?

The ZIP file structure is used for many other processes connected with the Gematik.
It’s currently not possible to change this process for Testhub.

7.1.2. An Ant BuildException has occured…​

This is usually caused by a configuration issues when building the Docker images.
Look further up for the relevant error message around the task create-docker-image and check the Docker section below.

7.1.3. E2E tests are not starting with port exception

When running the E2E tests you can run into an error claiming that a port is taken:

***************************
APPLICATION FAILED TO START
***************************

Description:

Web server failed to start. Port 6800 was already in use.

Action:

Identify and stop the process that's listening on port 6800 or configure this application to listen on another port.

TGR Workflow UI is active, please press quit in browser window...
TGR Destroying spring boot context after testrun...
TGR Tiger shut down orderly
[ERROR] Failed to start bean 'webServerStartStop'

This happens when you are using the Tiger Workflow UI and you didn’t press the power off button in the Workflow UI. To fix the issus:

  • Press the red power button in the Workflow UI and try again

  • Kill the process listening on the port mentioned by YOUR error message. Note that port configurations can change and the above error message is for illustration only.

7.1.4. The Tiger Workflow UI runs into timeouts on startup

Due to performance constraints it’s possible that there is not enough time for the Workflow UI and E2E tests to start in a timely manner:

15:29:57.034 [main ] INFO  d.g.t.t.c.w.TigerBrowserUtil - Starting Workflow UI via 'xdg-open http://localhost:6800'
15:29:57.052 [main ] INFO  d.g.t.t.c.w.TigerBrowserUtil - Workflow UI http://localhost:6800
15:29:57.052 [main ] INFO  d.g.t.t.l.TigerDirector - Waiting 10 seconds for workflow Ui to fetch status...

You can try:

  • to increase the setting lib.workflowUiStartTimeoutInSeconds in the file tiger.yaml.

  • to set lib.activateWorkflowUi: false in the file tiger.yaml.

For more information on how to configure Tiger check the Tiger User Manual.

7.2. Docker

7.2.1. The name "xyz" is already in use by container …​

Occasionally you might encounter an error like:

Error response from daemon: Conflict. The name "vsdm-client" is already in use by container f9e5798a82e0.
  1. It’s possible that you are running a container with the same name.
    You have to rename or remove it.

  2. It’s possible that a previous Testhub start wasn’t cleaned properly.
    First remove the remaining containers and then restart Testhub:

    docker compose -f ./doc/docker/compose-local.yaml down -v
    docker compose -f ./doc/docker/compose-local.yaml --profile full up -d --remove-orphans

7.2.2. Authentication for pulling Docker images

Testhub components are povided as public Docker images.
Authentication with a registry is not neccessary.
Depending on your Docker configuration you may run into authentication issues such as these:

ERROR: (gcloud.auth.docker-helper) There was a problem refreshing your current auth tokens: Reauthentication failed. cannot prompt during non-interactive execution.
Please run:

  $ gcloud auth login

This issue is NOT related to Testhub.
To fix the problem either:

  • Authenticate using your credential helper such as the gcloud CLI

  • Remove the relevant settings from your docker configuration

7.2.3. No feedback from workflow Ui, aborting!

If you see this error, the tiger framework was not able to reach the browser within the expected time.
This usually happens when the Testhub is started on a machine with limited resources or the browser setup is somewhat slow.

You should have a browser window open with the Testhub UI, but it might stay blank for a while.
If this happens try to reload that tab.
If you don’t see this tab at all, check if the Testhub is running and try to open http://localhost:6800/ in your browser.
The timeout is set to 120 seconds, so if you can open the UI within that time frame, you should be able to start the workflow without any issues.
If you are still running into problems, try to increase the timeout in the file tiger.yaml (search for "workflowUiStartTimeoutInSeconds" and increase the value).

7.2.4. permission denied while trying to connect to the Docker daemon socket…​

Ensure you are following the post-install steps as outlined by dockerdocs, especially on how to use Docker as a non-root user:
https://docs.docker.com/engine/install/linux-postinstall/#manage-docker-as-a-non-root-user

7.3. Windows PowerShell

7.3.1. Issues with running commands with parameters in PowerShell

If you are running into issues with running commands with parameters in PowerShell, try to use PowerShell’s passthrough mechanism for arguments with the "stop parsing" symbol --% to prevent PowerShell from interpreting the parameters as its own.

For example, instead of running

./mvnw -pl test/vsdm-testsuite/ -Dit.test="Vsdm*IT" -Dskip.inttests=false verify

which may lead to

[ERROR] Unknown lifecycle phase "Vsdm*IT". You must specify a valid lifecycle phase [...]

try to run it with the passthrough --% symbol

./mvnw --% -pl test/vsdm-testsuite/ -Dit.test="Vsdm*IT" -Dskip.inttests=false verify

which should prevent PowerShell from interpreting the -Dit.test parameter as its own and instead pass it correctly to the mvnw command.