Skip to content

Databases

This service uses TypeORM for data persistence. By default, a local SQLite database is used, but the system also supports PostgreSQL and can be extended to work with other engines such as MySQL thanks to TypeORM’s flexible architecture.

Configuration

Key Type Notes
DB_TYPE string Database type (default: sqlite)
DB_HOST string Database host [when DB_TYPE is {"override":true} | "sqlite" → otherwise required]
DB_PORT number Database port [when DB_TYPE is {"override":true} | "sqlite" → otherwise required]
DB_USERNAME string Database username [when DB_TYPE is {"override":true} | "sqlite" → otherwise required]
DB_PASSWORD string Database password [when DB_TYPE is {"override":true} | "sqlite" → otherwise required]
DB_DATABASE string Database name [when DB_TYPE is {"override":true} | "sqlite" → otherwise required]
DB_SYNCHRONIZE boolean Enable TypeORM schema synchronization. Set to false in production after initial setup and rely on migrations instead. (default: true)
DB_MIGRATIONS_RUN boolean Run pending database migrations automatically on startup (default: true)

SQLite (Default)

When using DB_TYPE=sqlite, the service will store its data in a local file-based SQLite database located at the path defined by the FOLDER variable (./config by default). This setup is lightweight and ideal for:

  • Development
  • Testing
  • Prototyping

No additional database server is required.


PostgreSQL

To connect to a PostgreSQL instance, set the following environment variables:

DB_TYPE=postgres
DB_HOST=your-hostname
DB_PORT=5432
DB_USERNAME=your-username
DB_PASSWORD=your-password
DB_DATABASE=your-database

This mode is suitable for:

  • Production deployments
  • Multi-instance setups
  • Scenarios requiring scalability and better concurrency handling

Make sure your database is reachable and properly initialized before starting the service.


Database Migrations

Starting from v2.0.0, EUDIPLO uses TypeORM migrations for database schema management instead of automatic synchronization. This provides:

  • Safe upgrades: Schema changes are applied in a controlled manner
  • Rollback capability: Migrations can be reverted if needed
  • Production safety: No accidental schema changes in production

How Migrations Work

By default, migrations run automatically when the service starts (DB_MIGRATIONS_RUN=true). The migration system detects whether you're running a fresh installation or upgrading:

  • Fresh installation: The baseline migration creates all tables from scratch
  • Upgrading: If tables already exist (from previous synchronize: true mode), the baseline migration is skipped, and only new migrations are applied

Configuration

Variable Default Description
DB_MIGRATIONS_RUN true Run pending migrations on startup
DB_SYNCHRONIZE false Auto-sync schema (⚠️ dev only)

Production Safety

Never set DB_SYNCHRONIZE=true in production. Use migrations instead to ensure controlled schema updates.

Manual Migration Commands

From the apps/backend directory:

# Generate a new migration based on entity changes
pnpm migration:generate --name=AddNewFeature

# Run pending migrations
pnpm migration:run

# Revert the last migration
pnpm migration:revert

# Show migration status
pnpm migration:show

Upgrading from Pre-2.0.0

If you're upgrading from a version that used synchronize: true:

  1. Your existing database schema is already correct
  2. The baseline migration will detect existing tables and skip creation
  3. Your data is preserved, and the migration history starts fresh

No action is required — just start the new version, and migrations will handle the rest.


Encryption at Rest

EUDIPLO encrypts sensitive data at rest using AES-256-GCM authenticated encryption. This protects cryptographic keys and personal information stored in the database from unauthorized access, even if the database itself is compromised.

Encrypted Fields

The following data is automatically encrypted before being written to the database:

Entity Field Data Type
KeyEntity key Private JWK material
Session credentials Verified presentation claims (PII)
Session credentialPayload Issuance offer data
Session auth_queries Authorization query data
Session offer Credential offer object
InteractiveAuthSession presentationData OpenID4VP presentation data
InteractiveAuthSession completedStepsData Step completion results
InteractiveAuthSession authorizationDetails Authorization details

Encryption Key Sources

The encryption key can be sourced from different providers, configured via ENCRYPTION_KEY_SOURCE:

Source Description Security Level Use Case
env Derived from MASTER_SECRET via HKDF Development Local dev, testing
vault Fetched from HashiCorp Vault at startup Production Self-hosted, on-prem
aws Fetched from AWS Secrets Manager Production AWS-native deployments
azure Fetched from Azure Key Vault Production Azure-native deployments

Security Hardening

When using vault, aws, or azure, the encryption key is only in RAM — it's never stored in environment variables. This means a compromised container cannot retrieve the key via env or /proc/*/environ commands.

Environment-based Key (Development)

ENCRYPTION_KEY_SOURCE=env  # default
MASTER_SECRET=your-jwt-secret  # key derived via HKDF

HashiCorp Vault

ENCRYPTION_KEY_SOURCE=vault
VAULT_ADDR=https://vault.example.com:8200
VAULT_TOKEN=hvs.your-token
VAULT_ENCRYPTION_KEY_PATH=secret/data/eudiplo/encryption-key  # optional, defaults shown

# Create the key in Vault:
vault kv put secret/eudiplo/encryption-key key=$(openssl rand -base64 32)

AWS Secrets Manager

ENCRYPTION_KEY_SOURCE=aws
AWS_REGION=us-east-1
AWS_ENCRYPTION_SECRET_NAME=eudiplo/encryption-key

# Create the key in AWS:
aws secretsmanager create-secret \
  --name eudiplo/encryption-key \
  --secret-string $(openssl rand -base64 32)

Uses IAM roles automatically when running in EKS/ECS (no credentials needed).

Azure Key Vault

ENCRYPTION_KEY_SOURCE=azure
AZURE_KEYVAULT_URL=https://myvault.vault.azure.net
AZURE_ENCRYPTION_SECRET_NAME=eudiplo-encryption-key

# Create the key in Azure:
az keyvault secret set \
  --vault-name myvault \
  --name eudiplo-encryption-key \
  --value $(openssl rand -base64 32)

Uses Managed Identity automatically when running in Azure (no credentials needed).

Key Requirements

The encryption key must be:

  • 256 bits (32 bytes)
  • Encoded as: Base64 (44 characters) or Hex (64 characters)

Generate a secure key:

# Base64 (recommended)
openssl rand -base64 32

# Hex
openssl rand -hex 32

Migration from Unencrypted Data

If upgrading from a version without encryption, EUDIPLO automatically handles migration:

  1. Reading: Unencrypted data is detected and read normally
  2. Writing: All writes use encryption

To fully encrypt existing data, update each record (e.g., via a migration script or by re-creating keys/sessions).

Important: Key Recovery

If you lose your encryption key, all encrypted data becomes unrecoverable. Ensure you:

  • Use a secrets manager with backup/versioning
  • Document the key's location for disaster recovery
  • Test key rotation procedures

Database Column Type Change

Encrypted columns use text type instead of json. If you have custom migrations or database constraints on these columns, update them accordingly.


Extensibility

Because this service uses TypeORM, it is easy to integrate additional database engines such as:

  • MySQL / MariaDB
  • Microsoft SQL Server
  • Oracle

To add support for a new engine:

  • Install the appropriate TypeORM driver (e.g., mysql2)
  • Set DB_TYPE to the corresponding engine name
  • Configure the necessary connection options via environment variables

Let us know if you need help extending support for additional databases.


Multi-Tenant Database Schema

EUDIPLO supports multi-tenancy, allowing multiple tenants to share the same database while keeping their data isolated. This is achieved by adding a tenantId column to relevant entities and filtering all queries by this identifier.

Tenant Isolation in Database

In multi-tenant mode, data isolation is achieved through a tenantId column in all relevant entities:

Core Entities with Tenant Support

Session Entity:

@Entity()
export class Session {
    @PrimaryColumn('uuid')
    id: string;

    @Column('varchar')
    tenantId: string; // Tenant ID for multi-tenancy support

    // ... other fields
}

Issuance Configuration Entity:

@Entity()
export class IssuanceConfig {
    @PrimaryColumn('varchar')
    id: string;

    @Column('varchar')
    tenantId: string;

    // ... other fields
}

Database Queries

All database operations automatically filter by tenantId when in multi-tenant mode:

// Example: Get sessions for specific tenant
return this.sessionRepository.findBy({ tenantId });

// Example: Update session with tenant scope
return this.sessionRepository.update({ id: sessionId, tenantId }, updateData);