Database
All auth data is stored in a single SQLite file at/app/data/auth.db (configurable via DB_PATH).
On startup, scripts/secure-db.js automatically sets the file permissions to 600 (owner read/write only). It runs in both Docker images, before the server starts.
API keys
Getting your first key
- Fullstack mode (default)
- API-only mode
- Visit
/setupto create your admin account. - Go to
/api-keysin the dashboard to create your first key.
Using a key
Pass it in theAuthorization header:
Best practices
- Store keys in environment variables, never in source code.
- Create a separate key per service or environment.
- Set an expiration when creating keys (
expiresInin seconds). - Rotate keys regularly; disable unused ones promptly.
API routes and rate limiting
For the full list of routes and their auth requirements, see the API Reference. Rate limiting defaults and configuration options are also documented there.Upload and delivery
Uploads are validated by content, not by extension or declared MIME type: the leading bytes must match the format the file claims to be, so a.jpg carrying an HTML document never reaches storage. Delivery then serves those bytes as they are, with X-Content-Type-Options: nosniff and a content type taken from the shared media table rather than sniffed from the body.
That pairing is what closes the stored-XSS path on the public /t/* route, which returns uploaded bytes untouched. Both halves are load-bearing: strict at the door, no sniffing on the way out.
Docker security
The two images differ here, and the difference matters when you pick one.
The
full image bundles nginx and supervisord alongside the app and sets no USER, so every process in it runs as root. Application directories are owned by node, but that does not change which user the processes run under. If running non-root is a requirement for you, deploy the api image and put your own reverse proxy in front of it.
- Mount the
/app/datavolume with appropriate host permissions. - Neither image needs a privileged container or extra capabilities.
Incident response
Compromised API key
Compromised API key
1
Disable the key
Go to
/api-keys in the dashboard and disable or delete the key immediately.2
Review logs
bash docker compose logs api | grep "api_key.success" 3
Issue a replacement
Create a new key and update all services that used the compromised one.
Database integrity check
Database integrity check
ok. If it reports errors, restore from your most recent backup and restart.Additional resources
Better Auth
Official Better Auth documentation.
API Key Plugin
Better Auth API Key plugin reference.