Repository navigation
Hashed, revocable API keys per app - #220
Merged
Merged
Conversation
Hashed, revocable, multi-key API keys per app, modelled on SendLit and FrontLit, plus the operator-supplied bootstrap key for bundled installs.
Ports the SendLit/FrontLit API key model (docs/prds/apikey.md). - Migration 0008 renames api_keys to apps (the tenant) and adds a new api_keys table that stores only SHA-256 hashes. Existing plaintext keys become hashed legacy keys, so integrations keep working. Media, settings, signatures and TUS uploads now reference apps.id instead of the secret. - Keys are ml_live_ + 32 random bytes, shown once. Apps can have up to 10 active keys, with optional expiry and last-used tracking. - Revoking a key or deleting an app stops its keys and the signatures they minted. Deleted apps' keys no longer authenticate, including over TUS. - API keys are read only from the x-medialit-apikey header; the body field is gone. Uploads authenticate and check their declared size before the body is read. - Deactivated accounts get 403 account_inactive on every API credential. - MEDIALIT_BOOTSTRAP_API_KEY seeds a key for bundled installs at startup; the admin key is no longer logged. - Dashboard API: list, create and revoke keys per app; routes use appId.
The local integration stack sets MEDIALIT_BOOTSTRAP_API_KEY instead of scraping the admin key from the API log, which no longer contains it. docker-compose.yml and the env examples document the setting. The SDK's Media type drops apikey, which the API never returned.
The settings page lists an app's keys by name and prefix with created and last-used dates, creates keys in a dialog that shows the secret once, and revokes them after confirmation. The configured bootstrap key shows as managed by configuration. Creating an app shows its first key once. The app route segment is now [appId] (same URL values), and the dashboard no longer receives any key secret after creation.
Adds an API keys page (create, use, rotate, revoke, legacy keys), the MEDIALIT_BOOTSTRAP_API_KEY setup for bundled Compose installs, and an upgrade section covering migrations, backups and the hashed-key release.
Creating or renaming an app to a name the user already has now returns 409 with a plain message. It used to return the database error, which included the failed SQL and internal IDs. API key dates render with a fixed locale and time zone so server and browser output match, and the new-app dialog has a description.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Ports the SendLit/FrontLit API key model to MediaLit. Plan and decisions:
docs/prds/apikey.md.ml_live_+ 32 random bytes) is shown once, at creation.api_keysbecomesapps(the tenant), with a newapi_keystable. An app can have up to 10 active keys, each with a name, prefix, last-used time, optional expiry and soft revocation. Media, settings, signatures and TUS uploads now referenceapps.idinstead of the secret.legacykey. A media or settings row that matches no app stops the migration with nothing changed.x-medialit-apikey; the bodyapikeyfield is gone. Uploads now authenticate and check their declared size before the body is read, so an unauthenticated request can't write a large file to disk.403 account_inactiveon every API credential.MEDIALIT_BOOTSTRAP_API_KEYcreates a key for the admin's default app at startup, before the API reports healthy, so SendLit, FrontLit and CourseLit can share oneMEDIALIT_APIKEYin their compose files. The admin key is no longer logged.[keyid]route segment is now[appId](same URL values).Before merging
self-hosting.mdx): back up the database first. The migration drops the plaintext key column and can't be rolled back. Docker Compose installs migrate automatically; others must runbun apps/api/src/db/migrate.ts.docs/prds/apikey.md§6.4.Test plan
bun run test:integration:stack): REST, TUS, MCP and CLI against real Postgres and MinIO, using a bootstrap-seeded keydrizzle-kit generatereports no schema drift🤖 Generated with Claude Code