Skip to main content
Leases add time-limited access control to content keys. When enabled, the key server issues a lease on first key request and validates it on subsequent requests. Revoking a lease immediately stops playback — without re-encrypting the video.

Enable leases

Set LEASE_TTL_MS to enable:
docker run -d \
  -e MASTER_KEY_HEX=... \
  -e SALT_HEX=... \
  -e CORS_ORIGINS=https://app.example.com \
  -e AUTH_JWKS_URL=https://auth.example.com/.well-known/jwks.json \
  -e LEASE_TTL_MS=300000 \
  -e DATABASE_URL=postgres://user:pass@db:5432/blindcast \
  -p 4100:4100 \
  blindcast/keyserver
Leases require authentication. If neither AUTH_JWT_SECRET nor AUTH_JWKS_URL is set, leases are disabled regardless of LEASE_TTL_MS.

How leases work

  1. Player calls POST /keys/leases with the content ID and auth token
  2. Key server extracts the viewer ID from the JWT sub claim, creates a lease, and returns the lease ID + TTL
  3. Player sends X-Lease-Id header on every GET /keys/:contentId request
  4. Key server validates the lease on each request — if expired or revoked, returns 403
  5. Player renews the lease at 75% of TTL via POST /keys/leases/renew

Revoking access

Revoke by viewer

Revoke all active leases for a viewer (e.g., when they cancel their subscription):
curl -X POST https://keys.example.com/keys/leases/revoke \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <admin-token>" \
  -d '{"viewerId": "user-123"}'

Revoke by lease ID

Revoke a specific lease:
curl -X POST https://keys.example.com/keys/leases/revoke \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer <admin-token>" \
  -d '{"leaseId": "abc-123-def"}'

Revoke via database

For bulk operations, update the database directly:
-- Revoke all leases for a viewer
UPDATE leases SET revoked = TRUE WHERE viewer_id = 'user-123';

-- Revoke all leases for a piece of content
UPDATE leases SET revoked = TRUE WHERE content_id = 'video-456';

What happens when a lease is revoked

  1. The player’s next key request (or lease renewal) gets a 403 response
  2. The player emits a KEY_LEASE_EXPIRED error
  3. Playback stops — the player cannot fetch new keys
See Player Leases for how to handle this in your UI.

Configuration

VariableDefaultDescription
LEASE_TTL_MSLease TTL in milliseconds. If unset, leases are disabled.
DATABASE_URLsqlite:///data/blindcast.dbWhere leases are stored

Cleanup

Expired leases accumulate over time. The key server runs a background cleanup task every hour, deleting leases that expired more than 24 hours ago. For Postgres deployments, you can also run cleanup manually:
DELETE FROM leases WHERE expires_at < NOW() - INTERVAL '24 hours';