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
- Player calls
POST /keys/leases with the content ID and auth token
- Key server extracts the viewer ID from the JWT
sub claim, creates a lease, and returns the lease ID + TTL
- Player sends
X-Lease-Id header on every GET /keys/:contentId request
- Key server validates the lease on each request — if expired or revoked, returns 403
- 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
- The player’s next key request (or lease renewal) gets a
403 response
- The player emits a
KEY_LEASE_EXPIRED error
- Playback stops — the player cannot fetch new keys
See Player Leases for how to handle this in your UI.
Configuration
| Variable | Default | Description |
|---|
LEASE_TTL_MS | — | Lease TTL in milliseconds. If unset, leases are disabled. |
DATABASE_URL | sqlite:///data/blindcast.db | Where 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';