Skip to content

Database

Both the issuer and the verifier keep their state in PostgreSQL. Each service takes the same database block, and the two use separate databases on what can be the same server.

What is stored

Session state, and nothing else that matters to a holder:

  • The issuer records each issuance session — which credential configuration it is for, the pre-authorized code, the session's status — for as long as the session is live.
  • The verifier records each verification session, the verification query it was started from, and the outcome.
  • Reusable configuration also lives here: the issuer's credential configurations and the verifier's verification queries persist until you remove them.

Verified claims are pushed to your application and are not retained as a record for you to query later. If you need the claims, configure the callback — see Receive verified data and Receive the issued credential.

Configuration

"database": {
  "persistenceUnitName": "model", // (1)!
  "driver": "org.postgresql.Driver", // (2)!
  "url": "jdbc:postgresql://postgres:5432/verifier", // (3)!
  "user": "postgres", // (4)!
  "password": "postgres" // (5)!
}
  1. Persistence unit name. Leave this as model.
  2. JDBC driver class. Leave this as the PostgreSQL driver.
  3. JDBC connection URL. The host is resolved from inside the container, so in a Compose setup it is the service name — postgres — not localhost.
  4. Database user.
  5. Database password. Do not keep the sample value — see Operating it.

The issuer's URL ends in /issuer and the verifier's in /verifier. Point them at one database and their migrations will collide.

Creating the databases

Both databases must exist before the services start; the services create their own tables but not their own database. In the quickstart Compose setup this is a script in /docker-entrypoint-initdb.d:

CREATE DATABASE verifier ENCODING 'UTF8';
CREATE DATABASE issuer ENCODING 'UTF8';

Init scripts run only once

PostgreSQL executes /docker-entrypoint-initdb.d scripts only when the data directory is empty — that is, on the very first start against a fresh volume. Editing the script later has no effect. To re-run it, drop the volume: docker compose down -v then docker compose up. That deletes all data.

Schema migrations

Each service manages its own schema with Flyway and applies any outstanding migrations at startup. Upgrading a service image can therefore change the schema, which has two consequences worth planning for:

  • Take a backup before upgrading. Migrations are applied automatically and are not reversible by restarting the old image.
  • Upgrade one service at a time and confirm it starts cleanly. A failed migration stops the service from starting; the logs name the migration that failed.

Operating it

The services depend on the database being reachable. In a Compose setup, gate them on a health check rather than start order:

depends_on:
    postgres:
        condition: service_healthy

For anything beyond local testing:

  • Do not use the sample credentials. postgres / postgres is a placeholder for local work. See Going to production.
  • Back it up. Losing the database loses your credential configurations and verification queries along with in-flight sessions, and both have to be recreated by hand.
  • Restrict network access. Only the two backends need to reach the database. The quickstart publishes port 5433 on the host for convenience; a real deployment should not.

What's Next