Skip to content

Configuration Import

EUDIPLO supports importing configurations from JSON files on application startup. This feature allows you to pre-configure credentials, issuance workflows, and presentation verification rules without using the API.

Overview

The configuration import system automatically loads and validates JSON configuration files from the config/ directory when the application starts. This is particularly useful for:

  • Development environments - Pre-load test configurations
  • Production deployments - Bootstrap with standard configurations
  • CI/CD pipelines - Automated environment setup
  • Multi-tenant scenarios - Bulk import tenant-specific configurations

When running locally with nodejs, the default directory is assets/config/.

Configuration

Key Type Notes
CONFIG_IMPORT_MODE string Startup configuration reconciliation mode. Replaces CONFIG_IMPORT and CONFIG_IMPORT_FORCE.
CONFIG_IMPORT boolean Deprecated: enable startup config import when CONFIG_IMPORT_MODE is unset (default: false)
CONFIG_IMPORT_FORCE boolean Deprecated: select upsert instead of create when CONFIG_IMPORT_MODE is unset (default: false)
CONFIG_FOLDER string Path to config import folder (default: /path/to/config/folder)
CONFIG_VARIABLE_STRICT alternatives Strict mode for config import. (default: skip)

Key Points

  • Tenant isolation: Each tenant has its own folder (e.g., tenant1, company-xyz)
  • Configuration types: types of configurations are supported
  • File naming: Not relevant since the id is taken from the content of the file. For key chains, the id is specified in the JSON file's id field.
  • Nested structure: Credentials and issuance configs are grouped under issuance/
  • Key chain management: Unified key chains (keys + certificates) are stored in the key-chains/ directory and will be imported automatically. Keep a provisioning file in place while it is intended to own the resource. Some KMS providers do not permit private-key export; portable resources use an external KMS reference in that case.
  • Ownership: A successfully provisioned resource is marked file-managed. API and web-client edits are rejected until the resource is explicitly detached. See Configuration Portability and Versioning.
  • Versioning: Bare JSON is accepted for backward compatibility. Versioned resources use the apiVersion, kind, metadata, and spec envelope and are upgraded through sequential config migrations before validation.

Configuration Types

Key Chains

Location: config/{tenant}/key-chains/*.json

Import unified key chains that combine cryptographic keys and their certificates.

Example Structure (Standalone - self-signed):

{
    "id": "039af178-3ca0-48f4-a2e4-7b1209f30376",
    "description": "Attestation signing key chain",
    "usageType": "attestation",
    "key": {
        "kty": "EC",
        "x": "pmn8SKQKZ0t2zFlrUXzJaJwwQ0WnQxcSYoS_D6ZSGho",
        "y": "rMd9JTAovcOI_OvOXWCWZ1yVZieVYK2UgvB2IPuSk2o",
        "crv": "P-256",
        "d": "rqv47L1jWkbFAGMCK8TORQ1FknBUYGY6OLU1dYHNDqU",
        "alg": "ES256"
    }
}

Example Structure (With Rotation - internal CA):

{
    "id": "039af178-3ca0-48f4-a2e4-7b1209f30376",
    "description": "HAIP-compliant attestation key chain",
    "usageType": "attestation",
    "key": {
        "kty": "EC",
        "x": "pmn8SKQKZ0t2zFlrUXzJaJwwQ0WnQxcSYoS_D6ZSGho",
        "y": "rMd9JTAovcOI_OvOXWCWZ1yVZieVYK2UgvB2IPuSk2o",
        "crv": "P-256",
        "d": "rqv47L1jWkbFAGMCK8TORQ1FknBUYGY6OLU1dYHNDqU",
        "alg": "ES256"
    },
    "rotationPolicy": {
        "enabled": true,
        "intervalDays": 90,
        "certValidityDays": 365
    }
}

When rotationPolicy.enabled is true:

  • The imported key becomes the root CA key
  • A new leaf signing key is automatically generated
  • The leaf certificate is signed by the imported CA key
  • Supports automatic key rotation

Example Structure (With Provided Certificate):

{
    "id": "039af178-3ca0-48f4-a2e4-7b1209f30376",
    "description": "Key chain with external certificate",
    "usageType": "access",
    "key": {
        "kty": "EC",
        "x": "...",
        "y": "...",
        "crv": "P-256",
        "d": "...",
        "alg": "ES256"
    },
    "crt": [
        "-----BEGIN CERTIFICATE-----\nLEAF_CERT...\n-----END CERTIFICATE-----",
        "-----BEGIN CERTIFICATE-----\nCA_CERT...\n-----END CERTIFICATE-----"
    ]
}

Alternative Structure (Base64 DER Without \\n Escapes):

{
    "id": "039af178-3ca0-48f4-a2e4-7b1209f30376",
    "description": "Key chain with base64 certificates",
    "usageType": "access",
    "key": {
        "kty": "EC",
        "x": "...",
        "y": "...",
        "crv": "P-256",
        "d": "...",
        "alg": "ES256"
    },
    "crt": ["MIIBqjCCAU+gAwIBAgIBATAKBggqhkjOPQQDAjAq...", "MIIB..."]
}

Key Features:

  • Unified model: Keys and certificates are managed together
  • Usage types: access, attestation, trustList, statusList, encrypt
  • Rotation support: Optional internal CA with automatic leaf key generation
  • Algorithm support: ES256 (ECDSA P-256)
  • Certificate chain: Optional certificates in crt (leaf first), each entry can be PEM or base64 DER
  • Validation: Full schema validation during import

Usage Types:

Usage Type Purpose
access OAuth/OIDC access token signing and authentication
attestation Credential/attestation signing (SD-JWT VC, mDOC)
trustList Trust list signing
statusList Status list (credential revocation) signing
encrypt Encryption (JWE)

PEM Format Notes:

  • Use \n escape sequences for line breaks in JSON
  • Include both -----BEGIN CERTIFICATE----- and -----END CERTIFICATE----- headers
  • The certificate content should be base64-encoded between the headers
  • Order: leaf certificate first, then intermediate/root CA certificates
  • You can provide each crt item either as PEM or as plain base64 DER

Credential Configurations

Location: config/{tenant}/issuance/credentials/*.json

Define credential templates and schemas.

Schema Reference: Credential Config API

Issuance Configurations

Location: config/{tenant}/issuance/issuance/*.json

Define issuance workflows and authentication requirements.

Schema Reference: Issuance Config API

Presentation Configurations

Location: config/{tenant}/presentation/*.json

Define verification requirements for credential presentations.

Schema Reference: Presentation Config API

Trust List Configurations

Location: config/{tenant}/trust-lists/*.json

Define trust lists for credential verification. Trust lists specify which issuers and revocation services are trusted when verifying credentials during presentation flows.

Schema Reference: Trust List API

For detailed information on trust lists and their role in credential verification, see Trust Framework.

Status List Configurations

Location: config/{tenant}/status-lists/*.json

Pre-create status lists for credential revocation and suspension tracking. Status lists are used to track the status of issued credentials without revealing which specific credential is being checked.

Schema Reference: Status List API

For detailed information on status lists and their role in credential lifecycle management, see Status Management.

Client Configurations

Location: config/{tenant}/clients/*.json

Define client-specific configurations, including client IDs, secrets, and permissions.

Client ID format

Client IDs in imported configuration files must be non-empty and may only contain letters, numbers, and the characters ., _, :, and -. Whitespace and other special characters are rejected.

Schema Reference: Client Config API

Image Configuration

Location: config/{tenant}/images/*

Uploads the images to be used in in the issuer display information or for credential configs. The reference to the image is done via the filename, e.g. logo.png instead of a full URL like:

{
    "display": [
        {
            "name": "PID",
            "description": "PID Credential",
            "locale": "en-US",
            "background_color": "#FFFFFF",
            "text_color": "#000000",
            "background_image": {
                "uri": "identity-card.jpg"
            },
            "logo": {
                "uri": "logo.jpg"
            }
        }
    ]
}

When the image can not be found, the image reference is ignored.

Import Process

Startup Validation

During application startup, EUDIPLO:

  • Checks the startup mode - Imports unless CONFIG_IMPORT_MODE=disabled
  • Scans tenant directories - Processes each tenant folder independently
  • Builds a versioned bundle - Wraps legacy JSON and runs schema migrations
  • Plans the change - Validates generations, references, and ownership
  • Applies in dependency order - Uses the same plan/apply pipeline as API bundle imports

Startup reconciliation mode

Use one setting for both enablement and duplicate handling:

CONFIG_IMPORT_MODE=create
Mode Startup behavior
disabled Do not scan or apply tenant configuration folders
create Create missing resources and skip existing resources without adopting them
upsert Create missing resources and update existing resources from the folder
replace Upsert, then remove resources previously managed by the same tenant folder but no longer present

replace never deletes the tenant itself and does not prune resources owned by another bundle or folder source.

CONFIG_IMPORT and CONFIG_IMPORT_FORCE remain available for one compatibility cycle. When CONFIG_IMPORT_MODE is unset, they map as follows and emit a deprecation warning:

Legacy settings Effective mode
CONFIG_IMPORT=false disabled
CONFIG_IMPORT=true, CONFIG_IMPORT_FORCE=false create
CONFIG_IMPORT=true, CONFIG_IMPORT_FORCE=true upsert

Validating Before Deployment with the CLI

Configuration errors are easier to detect before EUDIPLO starts, especially in CI/CD pipelines, Kubernetes deployments, and multi-tenant installations. The EUDIPLO CLI can validate tenant config-import files without starting the backend, connecting to a database, or changing any deployment state:

# Validate a single tenant directory
eudiplo config validate tenant ./assets/config/root

# Validate every tenant under a configuration root
eudiplo config validate tenants ./assets/config

# Machine-readable output for CI
eudiplo config validate tenants ./assets/config --format json

The validator:

  • Discovers tenant directories and requires a valid info.json for each one.
  • Validates every supported resource type (tenant metadata, clients, key chains, credential configs, issuance configs, presentation configs, status lists, trust lists, attribute providers, webhook endpoints, registrar config, and tenant-specific KMS config) against the same JSON Schemas the backend uses for config-import.
  • Detects unresolved ${VAR} placeholders without a default, without ever printing resolved secret values.
  • Collects errors across every file and tenant instead of stopping at the first problem, and reports the tenant, filename, and property path for each error.
  • Returns a non-zero exit code when validation fails, making it suitable for pull request checks.

apps/cli/src/commands/config/validate/registry.json is the single source of truth mapping each tenant config-import file/folder to its schema. It also drives the editor's json.schemas associations in .vscode/settings.json and the schemas bundled with the CLI, so they cannot drift from one another; both are regenerated by pnpm run gen:api.

Consumers can install the same bundled schemas and scoped VS Code associations in their own workspace:

eudiplo config editor setup . --config-directory ./config

This provides completion and inline validation while authoring files; eudiplo config validate tenants ./config remains the editor-independent and CI-friendly validation step.

Environment Variable Placeholders

Configuration files can contain dynamic placeholders that are replaced at import time before validation.

Syntax

"value": "${VAR_NAME}" – replaced by the environment variable VAR_NAME if it is set and non-empty.

"value": "${VAR_NAME:defaultValue}" – uses VAR_NAME from the environment if present; otherwise falls back to defaultValue.

Placeholders are resolved recursively in all string fields of imported JSON objects (arrays, nested objects included). Binary buffers are ignored.

Strict Mode Levels

CONFIG_VARIABLE_STRICT supports three levels to control behavior when a placeholder has no environment value and no default:

  • abort: Throw immediately and abort the entire import process.
  • skip: Throw for the current file (caught and logged as ImportError), skip that file and continue.
  • ignore: Keep the placeholder as-is, log a warning (ImportPlaceholder), and continue processing.

Default behavior is skip in production recommendations.

Examples

// Given process.env.CLIENT_ID_ROOT = "root-123"
{
    "clientId": "${CLIENT_ID_ROOT}", // => "root-123"
    "clientSecret": "${CLIENT_SECRET_ROOT:dev-secret}", // => "dev-secret" if CLIENT_SECRET_ROOT unset
}

Strict failure example (CONFIG_VARIABLE_STRICT=abort):

{ "apiKey": "${API_KEY}" } // throws if API_KEY not set

Non-strict warning example (CONFIG_VARIABLE_STRICT=ignore or absent / false):

{ "apiKey": "${API_KEY}" } // remains "${API_KEY}" and logs a warning

Logging Events

Event Condition
ImportPlaceholder Non-strict mode, missing env and no default
ImportError Strict mode, missing env and no default (file skipped)
ValidationError Placeholder substituted but subsequent schema validation fails

Recommendations

  • Prefer ${VAR:default} for optional values to avoid noisy warnings.
  • Use CONFIG_VARIABLE_STRICT=true in production to detect misconfiguration early.
  • Keep secrets in environment variables; use defaults only for non-sensitive fallbacks.

Note: Placeholder replacement occurs BEFORE class-validator schema validation so resolved values are validated, not the raw ${...} tokens.

Built-in Placeholders

In addition to environment variable placeholders, EUDIPLO provides built-in placeholders that are replaced at runtime when configurations are used.

<TENANT_URL>

The <TENANT_URL> placeholder allows configurations to be instance-independent. When a configuration is used, this placeholder is automatically replaced with the full URL including the host and tenant ID.

Use Case: This is particularly useful for portable configurations that need to reference URLs within the same tenant but should work across different deployments (e.g., development, staging, production) without modification.

Common Examples:

  • Verifiable Credential Type (vct): Use <TENANT_URL>/credentials/pid as the vct value to create instance-independent credential type identifiers
  • Trust List references: Point to trust lists hosted on the same instance, e.g., <TENANT_URL>/trust-lists/eu-trust-list

Example:

{
    "vct": "<TENANT_URL>/credentials/pid",
    "trustList": "<TENANT_URL>/trust-lists/my-trust-list"
}

When used in a tenant with ID company-xyz on host https://eudiplo.example.com, the placeholder is replaced with:

{
    "vct": "https://eudiplo.example.com/company-xyz/credentials/pid",
    "trustList": "https://eudiplo.example.com/company-xyz/trust-lists/my-trust-list"
}

This enables sharing configuration files between environments without hardcoding URLs.

Validation and Processing

For each configuration file:

  • JSON parsing - Validates file syntax
  • Schema validation - Uses the same validators as the API endpoints
  • Dependency checking - Verifies referenced configurations exist
  • Duplicate handling - Uses the selected CONFIG_IMPORT_MODE

Error Handling

Invalid configurations are handled gracefully:

  • Validation errors are logged with detailed information
  • Missing dependencies are reported
  • Existing configurations are preserved in create mode
  • Plan failures prevent resource changes from that tenant's bundle; tenant setup may already have created the tenant itself
  • Apply-time failures are reported with the tenant and stop the remainder of that tenant's reconciliation
  • Other tenants continue when one tenant folder fails

Logging

Import activities are logged with structured information:

{
    "event": "Import",
    "tenant": "company-xyz",
    "files": 5,
    "message": "5 credential configs imported for company-xyz"
}

Key import logging:

{
    "event": "Import",
    "tenant": "company-xyz",
    "message": "3 keys imported for company-xyz"
}

Error logging includes detailed validation information:

{
    "event": "ValidationError",
    "file": "invalid-config.json",
    "tenant": "company-xyz",
    "errors": [
        {
            "property": "credentialConfigs",
            "constraints": { "isArray": "credentialConfigs must be an array" },
            "value": "not-an-array"
        }
    ]
}

Best Practices

Configuration Management

  • Use descriptive filenames that reflect the configuration purpose
  • Test configurations in development before deploying
  • Document tenant-specific configurations

Production Deployment

  • Use CONFIG_IMPORT_MODE=create for additive initial provisioning
  • Use upsert or replace only when the folder should remain authoritative
  • Monitor logs for validation errors during startup
  • Detach file-managed resources before managing them through the API or UI

Multi-Tenant Setup

# Organize by tenant/organization
assets/config/
├── acme-corp/
   ├── info.json
   ├── registrar.json
   ├── kms.json
   ├── clients/
   ├── key-chains/
   ├── attribute-providers/
   ├── webhook-endpoints/
   ├── issuance/
   ├── presentation/
   ├── trust-lists/
   └── images/
├── university-x/
   ├── info.json
   ├── registrar.json
   ├── kms.json
   ├── clients/
   ├── key-chains/
   ├── attribute-providers/
   ├── webhook-endpoints/
   ├── issuance/
   ├── presentation/
   ├── trust-lists/
   └── images/
└── government-agency/
    ├── info.json
    ├── registrar.json
    ├── kms.json
    ├── clients/
    ├── key-chains/
    ├── attribute-providers/
    ├── webhook-endpoints/
    ├── issuance/
    ├── presentation/
    ├── trust-lists/
    └── images/

Even when you just have one tenant, use a folder structure to prepare for future multi-tenancy.

Troubleshooting

Common Issues

Import not running: Check CONFIG_IMPORT_MODE is not disabled

Configurations not updating: Select CONFIG_IMPORT_MODE=upsert and increase the resource generation when required

Validation errors: Check logs for specific validation failures and schema requirements

Missing dependencies: Ensure credential configs are imported before issuance configs that reference them

Key validation errors: Check that imported keys use supported algorithms (ES256) and have valid JWK format

Certificate import issues: Verify certificates are in PEM format and match the imported private key

Security Considerations

  • File permissions - Ensure config files have appropriate read permissions
  • Tenant isolation - Verify tenant boundaries are properly maintained

Warning

Avoid storing sensitive information (e.g., private keys, secrets) in configuration files. Use environment variables for sensitive data and reference them using placeholders.