Building Block View - CTN Components
Component Overview
Section titled “Component Overview”The 2026 delivery is centred on the Association Register (ASR): participant management, identifier enrichment, VAD issuance, endpoint registration, and discovery. Authorisation stays local at the data provider. There is no central CTN policy decision point in 2026; each provider validates the VAD and applies its own policy.
Components for later phases — Orchestration Register, dossier-bound VOD tokens, OPA-based policy engine, orchestrator portal federation — have their design notes parked under docs/_out-of-scope-2026/ (gitignored). See the Roadmap for when they come back into scope.
Core Registers
Section titled “Core Registers”Association Register - Detailed Architecture
Section titled “Association Register - Detailed Architecture”The CTN Association Register system consists of three main building blocks providing member management, self-service portals, and API services.
System Overview (Level 0)
Section titled “System Overview (Level 0)”graph TB
subgraph "CTN Association Register"
A[Admin Portal]
B[Participant Portal]
C[API Layer]
D[Database]
end
E[CTN Admin Users] -->|Manage Members| A
F[CTN Member Users] -->|Self-Service| B
A --> C
B --> C
C --> D
G[Keycloak] -.->|Authentication| A
G -.->|Authentication| B
H[KvK API] -.->|Verification| C
Level 0 Components
Section titled “Level 0 Components”| Component | Responsibility | Technology |
|---|---|---|
| Admin Portal | CTN staff interface for participant management | Frontend stack to be decided (see Coding Standards → Open Questions); a React + TypeScript SPA is one candidate |
| Participant Portal | Self-service interface for participant organisations | Same — frontend stack TBD |
| API Layer | Business logic, data access, external integrations | Quarkus on Java 25+ LTS, packaged as OCI image |
| Database | Persistent data storage | PostgreSQL 15+ (managed; Azure Flexible Server in the reference deployment, RDS / Crunchy as alternatives) |
Level 1: Admin Portal
Section titled “Level 1: Admin Portal”graph LR
subgraph "Admin Portal"
A[Authentication Module]
B[Member Management]
C[Legal Entity Management]
D[Identifier Management]
E[Contact Management]
F[Endpoint Management]
G[Audit Log Viewer]
end
A --> B
A --> C
A --> D
A --> E
A --> F
A --> G
B --> H[API Client]
C --> H
D --> H
E --> H
F --> H
G --> H
I[Keycloak] -.-> A
Admin Portal Modules
Section titled “Admin Portal Modules”Authentication Module
Section titled “Authentication Module”Responsibility: User login, session management, token refresh
Key Components (framework-agnostic):
- Auth state/context — holds the current session
- Keycloak integration module (OIDC)
- Route guards for protected views
Dependencies:
- Keycloak OIDC adapter for the chosen frontend stack (e.g.
keycloak-jsfor a JavaScript portal) - Keycloak realm/client configuration
Member Management Module
Section titled “Member Management Module”Responsibility: Member CRUD operations, status changes, approval workflows
Key Components:
- Participants grid — data table for participant listing
- Participant detail view
- Approval workflow — multi-step approval process
API Endpoints:
GET /v1/participants— list participants (paginated)GET /v1/participants/{participantId}— get participant detailsPOST /v1/participants— create participantPUT /v1/participants/{participantId}— update participantDELETE /v1/participants/{participantId}— delete participant
Legal Entity Management Module
Section titled “Legal Entity Management Module”Responsibility: Legal entity verification, KvK integration, international registries
Key Components:
- Legal-entity form — entity creation/editing
- KvK lookup — KvK number search widget
- Verification-status indicator
API Endpoints:
POST /v1/legal-entities- Create legal entityGET /v1/legal-entities/{legalEntityId}- Get entity detailsPUT /v1/legal-entities/{legalEntityId}/verify- Trigger KvK verification
Level 1: Participant Portal
Section titled “Level 1: Participant Portal”graph LR
subgraph "Participant Portal"
A[Authentication Module]
B[Organization Profile]
C[Contact Management]
D[Endpoint Management]
E[Identifier Management]
F[Support Page]
end
A --> B
A --> C
A --> D
A --> E
A --> F
B --> G[API Client]
C --> G
D --> G
E --> G
H[Keycloak] -.-> A
Participant Portal Modules
Section titled “Participant Portal Modules”Organization Profile Module
Section titled “Organization Profile Module”Responsibility: View and update organization information
Key Components:
- Profile view — organisation profile display
- Profile edit form — editable organisation fields
- Identifier list — display registered identifiers
Features:
- Read-only fields: KvK number, legal entity name
- Editable fields: Address, phone, email
- Auto-save on field blur
Endpoint Management Module
Section titled “Endpoint Management Module”Responsibility: Register and manage API endpoints
Key Components:
- Endpoint grid — list of registered endpoints
- Endpoint form — add/edit endpoint details
- Health-status indicator
Validation:
- URL format validation (HTTPS required)
- Duplicate endpoint detection
- Version format validation (semver)
Level 1: API Layer
Section titled “Level 1: API Layer”graph TB
subgraph "API Layer (Quarkus on Java 25+)"
A[REST Resources]
B[Authentication quarkus-oidc]
C[Business Logic Services]
D[Data Access Layer Panache]
E[External Integrations]
end
F[Admin Portal] --> A
G[Participant Portal] --> A
A --> B
B --> C
C --> D
C --> E
D --> H[PostgreSQL]
E --> I[KvK API]
E --> J[BDI Framework]
K[Keycloak] -.->|Token Validation| B
REST Resources
Section titled “REST Resources”graph LR
subgraph "REST Resources (JAX-RS)"
A[ParticipantResource]
B[LegalEntityResource]
C[IdentifierResource]
D[ContactResource]
E[EndpointResource]
end
G[HTTP Request] --> A
G --> B
G --> C
G --> D
G --> E
ParticipantResource:
GET /v1/participants- List/search participantsGET /v1/participants/{participantId}- Retrieve single participantPOST /v1/participants- Register new participantPUT /v1/participants/{participantId}- Modify participant detailsDELETE /v1/participants/{participantId}- Remove participant (cascade delete)
LegalEntityResource:
POST /v1/legal-entities- Register legal entityGET /v1/legal-entities/{legalEntityId}- Retrieve entityPUT /v1/legal-entities/{legalEntityId}/verify- Trigger KvK verification
IdentifierResource:
POST /v1/legal-entities/{legalEntityId}/identifiers- Register identifier (KvK, EUID, LEI, etc.)GET /v1/legal-entities/{legalEntityId}/identifiers- List identifiers for a legal entityPUT /v1/legal-entities/{legalEntityId}/identifiers/{identifierId}- Modify identifier metadataDELETE /v1/legal-entities/{legalEntityId}/identifiers/{identifierId}- Remove identifier
Token (VAD) endpoints:
GenerateVAD- Generate VAD tokenGET /.well-known/jwks.json- Retrieve RSA public keys for verification (JWKS)
Authentication (quarkus-oidc)
Section titled “Authentication (quarkus-oidc)”Responsibilities:
- Validate the bearer JWT against Keycloak (signature, expiry) as an OIDC resource server
- Extract user claims (roles, email, organization)
- Enforce
@Authenticated/@RolesAllowedon resources - Return 401 Unauthorized if validation fails
Implementation:
quarkus-oidcresource-server configuration (see Coding Standards §4.3)- JWKS caching (~10 minutes; rotation handled via Keycloak’s key-rotation events)
- Role extraction for RBAC
Business Logic Services
Section titled “Business Logic Services”graph TB
subgraph "Services Layer"
A[ParticipantService]
B[LegalEntityService]
C[IdentifierService]
D[ContactService]
E[EndpointService]
F[TokenService]
end
A --> G[Database Repository]
B --> G
C --> G
D --> G
E --> G
F --> H[Crypto Service]
F --> I[JWT Service]
B --> J[KvK Integration]
Service Responsibilities:
| Service | Responsibility | External Dependencies |
|---|---|---|
ParticipantService | Participant CRUD, status management, approval workflow | None |
LegalEntityService | Entity creation, KvK verification | KvK API |
IdentifierService | Identifier registration, validation, deduplication | KvK API (for KvK numbers) |
ContactService | Contact management, email validation | None |
EndpointService | Endpoint registration, health checks | None |
TokenService | VAD token generation, signature verification | None (crypto / HSM-backed key) |
Data Access Layer
Section titled “Data Access Layer”graph LR
subgraph "Database Repository (Panache)"
A[ParticipantRepository]
B[LegalEntityRepository]
C[IdentifierRepository]
D[ContactRepository]
E[EndpointRepository]
end
A --> F[PostgreSQL Connection Pool]
B --> F
C --> F
D --> F
E --> F
F --> G[PostgreSQL Database]
Repository Pattern:
- Each repository handles CRUD for one entity
- Parameterized queries (never string concatenation)
- Transaction support for multi-table operations
- Connection pooling (max 20 connections)
Example: ParticipantRepository (Panache)
@ApplicationScopedpublic class ParticipantRepository implements PanacheRepositoryBase<Participant, UUID> {
public List<Participant> search(ParticipantFilters filters, Page page) { ... } // findById, persist, update and delete are provided by Panache}External Integrations
Section titled “External Integrations”KvK API Integration:
sequenceDiagram
participant API
participant KvKService
participant KvKAPI
API->>KvKService: verifyKvKNumber(kvkNumber)
KvKService->>KvKAPI: GET /basisprofiel/{kvkNumber}
KvKAPI-->>KvKService: Company Details
KvKService->>KvKService: Map to LegalEntity
KvKService-->>API: Verified Entity Data
Responsibilities:
- Verify KvK numbers exist
- Fetch company details (name, address, status)
- Map KvK response to internal data model
- Cache KvK responses (24-hour TTL)
- Handle rate limiting (max 100 req/min)
BDI Framework Integration:
sequenceDiagram
participant Client
participant BDIService
participant CryptoService
participant Database
Client->>BDIService: Generate VAD Token
BDIService->>Database: Fetch Member Data
Database-->>BDIService: Member Details
BDIService->>BDIService: Build JWT Claims
BDIService->>CryptoService: Sign with RSA Private Key
CryptoService-->>BDIService: Signed Token
BDIService-->>Client: VAD Token
Responsibilities:
- Generate VAD tokens (JWT with custom claims)
- Manage RSA key pairs (private key in Key Vault / HSM)
- Implement BDI specification v2.0 compliance (VAD portion)
Database Schema (Level 1)
Section titled “Database Schema (Level 1)”The ASR data model has its own document: see ASR Data Model for the full entity reference, the Keycloak ownership boundary (per ADR-00003), and the meta-field convention. A zoom-friendly, ERD-only companion is at ctn-asr-data-model-erd.md.
The high-level view below shows the core entities and their links to Keycloak. Client credentials live in Keycloak; the ASR holds only a reference (no secret). Dashed lines are logical cross-system links (resolved by ID, no database FK).
erDiagram
participant ||--o{ legal_entity : "has"
legal_entity ||--o{ legal_entity_identifier : "has"
legal_entity ||--o{ contact : "has"
participant ||--o{ endpoint : "registers"
participant ||--o{ verification : "verified by"
participant ||--o{ participant_role : "plays"
participant ||--o{ m2m_client_ref : "owns"
participant |o--o{ onboarding_request : "results in"
participant ||..|| kc_organization : "org-id link (no FK)"
m2m_client_ref ||..|| kc_m2m_client : "client-id link (no FK)"
participant {
uuid participant_id PK
varchar keycloak_organization_id UK
varchar status
}
legal_entity {
uuid legal_entity_id PK
uuid participant_id FK
varchar primary_legal_name
}
legal_entity_identifier {
uuid identifier_id PK
uuid legal_entity_id FK
varchar identifier_type
varchar validation_status
}
contact {
uuid contact_id PK
uuid legal_entity_id FK
varchar contact_type
}
endpoint {
uuid endpoint_id PK
uuid participant_id FK
varchar lifecycle_status
}
verification {
uuid verification_id PK
uuid participant_id FK
varchar type
varchar status
}
participant_role {
uuid role_id PK
uuid participant_id FK
varchar role
}
m2m_client_ref {
uuid m2m_client_ref_id PK
uuid participant_id FK
varchar keycloak_client_id UK
}
onboarding_request {
uuid request_id PK
uuid participant_id FK
varchar status
}
kc_organization {
varchar org_id PK
varchar alias
}
kc_m2m_client {
varchar client_id PK
varchar secret "in Keycloak only"
}
Every ASR table carries the same meta fields — created_at, updated_at, created_by, updated_by, is_deleted — and UUID primary keys. Full per-entity detail, including the audit_record log and the Keycloak user / organization-group entities, is in the ASR Data Model document.
Deployment View (Simplified)
Section titled “Deployment View (Simplified)”graph TB
subgraph "Azure West Europe"
subgraph "Static Web Apps"
A[Admin Portal]
B[Participant Portal]
end
subgraph "Kubernetes (AKS)"
C[ASR API Quarkus]
end
subgraph "Database"
D[PostgreSQL Flexible Server]
end
subgraph "Security"
E[Keycloak]
F[Azure Key Vault]
end
end
A --> C
B --> C
C --> D
A -.->|Auth| E
B -.->|Auth| E
C -.->|Secrets| F
G[Internet Users] --> A
G --> B
Association Register Components (2026)
Section titled “Association Register Components (2026)”- SPI & Seam Architecture (as-built) — Developer-facing IST view of the implemented SPIs and seams (onboarding providers, lifecycle, claim verification), with per-element implemented/stubbed/shape-only status. Companion to this (SOLL) document.
- Association Register — Participant management system.
- ASR Data Model — Target (SOLL) database model and ERD, with the Keycloak ownership boundary (ERD-only view).
- Member Onboarding Workflow — Vetting, verification, and credential issuance.
- DNS Record Validation — Alternative authentication method for domain ownership.
- Register Format and Data Service — API specifications and data formats, including endpoint registration.
- Trust Lists Architecture — Trust list management and distribution.
- Endpoint Lifecycle — Registration, validation, suspension and retirement of provider endpoints.
- Identifier Enrichment Architecture — Automated KvK / KBO / VIES / GLEIF enrichment.
Related Diagrams
Section titled “Related Diagrams”The Association Register data model and ERD are maintained as Mermaid diagrams in ASR Data Model and the ERD-only view. Trust-list internals are described in Trust Lists Architecture.
Local authorisation (2026)
Section titled “Local authorisation (2026)”There is no central policy decision component in the 2026 architecture. Each data provider:
- Validates the consumer’s VAD against the ASR’s JWKS endpoint.
- Applies its own local policy (participant status, jurisdiction, data product, requester tier).
- Returns the data shaped by that policy, or denies the request.
The BDI Connector is a reference implementation that providers can adopt to get VAD validation and policy hooks out of the box.
Supporting Components
Section titled “Supporting Components”Authentication Components
Section titled “Authentication Components”- Centralised Auth — OAuth provider services for user authentication.
- Keycloak M2M Authentication — Self-hosted M2M authentication via OAuth 2.0 client credentials, per ADR-00002 and ADR-00008.
Integration Components
Section titled “Integration Components”- BDI Connector — CTN integration connector.
Data Consumer
Section titled “Data Consumer”- A lean 2026 specification for the Visibility Achterland Containerlogistiek portal will be added here. The earlier draft (which assumed federated Orchestration Registers and an OPA PDP) is parked under
docs/_out-of-scope-2026/.
Component Architecture Layers
Section titled “Component Architecture Layers”graph TB
subgraph "Presentation Layer"
AdminPortal[Admin Portal]
MemberPortal[Participant Portal]
DataConsumerPortal[Data-Consumer Portal<br/>Visibility]
end
subgraph "Business Logic Layer"
AssocReg[Association Register]
VADGen[VAD Generator]
DNS[DNS Validator]
Enrich[Identifier Enrichment<br/>KvK / KBO / VIES / GLEIF]
EndpointReg[Endpoint Registry & Discovery]
end
subgraph "Data Layer"
PG[(PostgreSQL)]
end
subgraph "Infrastructure Layer"
KeyVault[Secret Store / HSM-backed keys]
IdP[OAuth / OIDC Server<br/>Keycloak]
end
AdminPortal --> AssocReg
MemberPortal --> AssocReg
DataConsumerPortal --> AssocReg
AssocReg --> VADGen
AssocReg --> DNS
AssocReg --> Enrich
AssocReg --> EndpointReg
AssocReg --> PG
VADGen --> KeyVault
AssocReg --> IdP
Component Interactions
Section titled “Component Interactions”VAD Issuance Flow
Section titled “VAD Issuance Flow”- The participant’s authorised representative authenticates via eHerkenning.
- The ASR runs the enrichment + verification checks (KvK / KBO / VIES / GLEIF).
- On success the IdP issues an OAuth client credential.
- When the consumer requests a VAD via the OAuth client-credentials grant, the ASR signs and returns the VAD (JWS).
Data Access Flow (local authorisation)
Section titled “Data Access Flow (local authorisation)”sequenceDiagram
participant Consumer
participant ASR as Association Register
participant Provider as Data Provider
Consumer->>ASR: OAuth client-credentials grant
ASR-->>Consumer: VAD (JWS-signed JWT)
Consumer->>Provider: GET /data-product (Bearer VAD)
Provider->>ASR: GET /.well-known/jwks (cached)
ASR-->>Provider: JWKS
Provider->>Provider: Validate signature, claims, expiry
Provider->>Provider: Apply local policy
alt Authorised
Provider-->>Consumer: 200 OK + data
else Denied
Provider-->>Consumer: 403 Forbidden
end
Component Specifications
Section titled “Component Specifications”Standard Interfaces
Section titled “Standard Interfaces”- REST APIs: OpenAPI 3.x specifications.
- Tokens: JWS-signed JWTs (RS256).
- Health: standard health-check endpoints.
- Discovery: ASR exposes a discovery API listing registered endpoints and their OpenAPI specs.
Common Patterns
Section titled “Common Patterns”- Idempotency: safe retries on POST.
- Circuit breaker: resilience for external calls (KvK, KBO, VIES, GLEIF).
- Audit logging: structured JSON for every state change and authorisation decision.
Component Dependencies
Section titled “Component Dependencies”graph LR
subgraph "External Dependencies"
IDP[OAuth / OIDC IdP]
KVK[Chamber of Commerce]
KBO[KBO Belgium]
GLEIF[LEI Registry]
VIES[VIES VAT]
EH[eHerkenning]
end
subgraph "CTN Components"
AssocReg[Association Register]
end
AssocReg --> KVK
AssocReg --> KBO
AssocReg --> GLEIF
AssocReg --> VIES
AssocReg --> IDP
AssocReg --> EH
Configuration Management
Section titled “Configuration Management”Association Register
Section titled “Association Register”association: verification: kvk_enabled: true kbo_enabled: true vies_enabled: true gleif_enabled: true dns_enabled: true token: signing_algorithm: RS256 validity_hours: 24 re_verification: interval_months: 12 # at least annually high_risk_interval_months: 3 # quarterly where risk requiresDeployment Considerations
Section titled “Deployment Considerations”Scaling Requirements (2026)
Section titled “Scaling Requirements (2026)”| Component | Min Instances | Max Instances | Scaling Metric |
|---|---|---|---|
| Association Register | 2 | 10 | CPU > 70% |
| Discovery API | 2 | 10 | Request rate |
| IdP (Keycloak) | 2 | 8 | CPU + active sessions |
Resource Allocation
Section titled “Resource Allocation”- Database: General Purpose tier with read replicas.
- Kubernetes pods (AKS): right-sized requests/limits with autoscaling.
- Storage: hot tier for active documents.
Security Considerations
Section titled “Security Considerations”Component Security
Section titled “Component Security”- Managed Identity: service-to-service authentication.
- Private Endpoints: network isolation for databases.
- Key Vault Integration: HSM-backed secret management for signing keys.
- TLS Everywhere: encrypted communication, TLS 1.3.
Token Security
Section titled “Token Security”- Private Key Storage: HSM-backed keys in Key Vault
- Certificate Rotation: Automated 90-day rotation
- Token Expiration: Time-limited validity
- Signature Validation: JWKS endpoint verification
Next Steps
Section titled “Next Steps”For implementation details, refer to:
- Individual component documentation (linked above)
- Runtime scenarios for dynamic behavior
- Deployment view for infrastructure
- Cross-cutting concepts for patterns
← Back to Solution Strategy | ↑ Back to Index | Next: Runtime View →
In samenwerking met




