Skip to main content
The key server uses a database only for lease state (active leases, revocations). Content keys are never stored — they are derived on every request from the master key using HKDF.

SQLite (default)

SQLite is the default. The database file is stored at /data/blindcast.db inside the container.
docker run -d \
  -v blindcast-data:/data \
  -e MASTER_KEY_HEX=... \
  -e SALT_HEX=... \
  -e CORS_ORIGINS=https://app.example.com \
  -p 4100:4100 \
  blindcast/keyserver
Mount a Docker volume at /data to persist leases across container restarts. Without a volume, lease state is lost when the container stops.

When SQLite is fine

  • Development and testing
  • Single-instance deployments
  • Low-throughput key servers (< 100 concurrent viewers)

When to switch to Postgres

  • Multiple key server instances (SQLite doesn’t support concurrent writers from different processes)
  • High-throughput deployments (> 100 concurrent viewers)
  • You need lease data in your existing Postgres for querying or auditing

Postgres

Set DATABASE_URL to switch to Postgres:
docker run -d \
  -e MASTER_KEY_HEX=... \
  -e SALT_HEX=... \
  -e CORS_ORIGINS=https://app.example.com \
  -e DATABASE_URL=postgres://user:pass@db.example.com:5432/blindcast \
  -p 4100:4100 \
  blindcast/keyserver

Auto-migration

The key server automatically creates the required tables on startup. No manual migration step needed.

Lease table schema

CREATE TABLE leases (
  id TEXT PRIMARY KEY,           -- lease ID (UUID)
  viewer_id TEXT NOT NULL,       -- viewer identifier (from JWT sub claim)
  content_id TEXT NOT NULL,      -- content being accessed
  expires_at TIMESTAMP NOT NULL, -- when the lease expires
  revoked BOOLEAN DEFAULT FALSE, -- true if explicitly revoked
  created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

CREATE INDEX idx_leases_viewer ON leases(viewer_id);
CREATE INDEX idx_leases_content ON leases(content_id);
CREATE INDEX idx_leases_expires ON leases(expires_at);

If leases are not used

If you don’t use leases (no LEASE_TTL_MS set, no player lease option), the database is not accessed at runtime. Key derivation is purely in-memory with no I/O.