Migration from Wallet API v1

The waltid-wallet-migration tool migrates data from the old waltid-wallet-api (v1) database schema to the new waltid-wallet-api2 schema. This guide covers the migration process, what gets migrated, and how to run it.

Important: Always back up your v1 database before running the migration. The migration tool is designed to be idempotent (running it twice won't duplicate data), but having a backup is essential.


What Gets Migrated

The migration tool transfers the following data from v1 to v2:

v1 Tablev2 Table(s)Description
walletswallet2_wallets + store registration tablesWallet records with auto-created stores
wallet_keyswallet2_keysKeys (re-serialized with type inference)
credentialswallet2_credentialsCredentials (re-parsed into the v2 credential format)
wallet_didswallet2_didsDIDs with documents
account_wallet_mappingwallet2_account_walletsAccount-to-wallet ownership

Store Creation

For each migrated wallet, the tool automatically creates:

  • One key store: wallet-{id}-keys
  • One credential store: wallet-{id}-creds
  • One DID store: wallet-{id}-dids

These deterministic IDs ensure the migration is idempotent.


Prerequisites

  1. Source database: Your existing v1 wallet database (SQLite or PostgreSQL)
  2. Target database: A new or empty v2 database (SQLite or PostgreSQL)
  3. Java 17+: Required to run the migration tool

Running the Migration

Using Gradle

From the waltid-identity repository root:

SQLite to SQLite:

./gradlew :waltid-services:waltid-wallet-migration:run \
  --args="--source jdbc:sqlite:/path/to/old/wallet.db --target jdbc:sqlite:/path/to/new/wallet2.db"

PostgreSQL to PostgreSQL:

./gradlew :waltid-services:waltid-wallet-migration:run \
  --args="--source 'jdbc:postgresql://localhost:5432/walletv1?user=wallet&password=secret' --target 'jdbc:postgresql://localhost:5432/walletv2?user=wallet&password=secret'"

Cross-database (SQLite to PostgreSQL):

./gradlew :waltid-services:waltid-wallet-migration:run \
  --args="--source jdbc:sqlite:/path/to/wallet.db --target 'jdbc:postgresql://localhost:5432/walletv2?user=wallet&password=secret'"

Dry Run Mode

Add --dry-run to preview what would be migrated without writing anything:

./gradlew :waltid-services:waltid-wallet-migration:run \
  --args="--source jdbc:sqlite:/path/to/wallet.db --target jdbc:sqlite:/path/to/wallet2.db --dry-run"

Command Line Arguments

ArgumentRequiredDescription
--sourceYesJDBC URL for the v1 source database
--targetYesJDBC URL for the v2 target database
--dry-runNoPreview migration without writing data

Migration Output

The migration tool logs progress and provides a summary at the end:

INFO  - Source: jdbc:sqlite:/path/to/wallet.db
INFO  - Target: jdbc:sqlite:/path/to/wallet2.db
INFO  - Found 5 wallets to migrate
INFO  - 
INFO  - ═══════════════════════════════════════════
INFO  -   Migration complete
INFO  -   Wallets:          5
INFO  -   Keys:             12
INFO  -   Credentials:      28
INFO  -   DIDs:             5
INFO  -   Account mappings: 5
INFO  - ═══════════════════════════════════════════

If any items fail to migrate, warnings are logged with details:

WARN  - Failed to migrate credential abc123 for wallet xyz789: Invalid credential format

Post-Migration Steps

1. Verify the Migration

After migration, verify the data was transferred correctly:

Point wallet2-persistence.conf at the migrated database (e.g. jdbcUrl = "jdbc:sqlite:/data/wallet2.db"), then start wallet-api2 with your config directory and the database mounted:

docker run \
  -p 7005:7005 \
  -v /path/to/config:/waltid-wallet-api2/config \
  -v /path/to/data:/data \
  waltid/wallet-api2

# List wallets
curl http://localhost:7005/wallet

2. Update Your Configuration

Update your deployment to use the new wallet-api2 service and database:

wallet2-persistence.conf:

# wallet2-persistence.conf
jdbcUrl = "jdbc:sqlite:/data/wallet2.db"
driverClassName = "org.sqlite.JDBC"

Or for PostgreSQL:

# wallet2-persistence.conf
jdbcUrl = "jdbc:postgresql://localhost:5432/walletv2"
driverClassName = "org.postgresql.Driver"
username = "wallet"
password = "secret"
maximumPoolSize = 10

See the Persistence configuration reference for all options.

3. Update API Calls

The v2 API has some differences from v1. Key changes:

v1 Endpointv2 EndpointNotes
/api/wallet/{walletId}/.../wallet/{walletId}/...Simplified path
Presentation ExchangeDCQLDifferent query format for OID4VP
/api/wallet/{walletId}/credentials/usePresentationRequest/wallet/{walletId}/credentials/presentNew presentation endpoint

Troubleshooting

"Key store not found" errors

The migration creates stores with deterministic IDs. If you see this error, ensure the target database is empty or the migration completed successfully.

Credential parsing failures

Some v1 credentials may use formats not fully supported in v2. Check the warning logs for specific credential IDs and manually verify/re-import if needed.

Database connection issues

Ensure the JDBC URLs are correct and the databases are accessible:

  • SQLite: File path must exist and be writable
  • PostgreSQL: User must have CREATE TABLE permissions

Rollback

If you need to rollback:

  1. Stop the wallet-api2 service
  2. Restore your v1 database from backup
  3. Start the wallet-api (v1) service pointing to the restored database

The migration does not modify the source database, so your v1 data remains intact.

Last updated on July 13, 2026