Skip to main content

Overview

Openinary uses Better Auth for authentication with SQLite as the database backend. The system supports both web-based login (email/password) and API key authentication for programmatic access.

Authentication Architecture

Better Auth Configuration

Shared authentication between API and Web:
  • Single SQLite database at /data/auth.db
  • Better Auth v1.1.9 with API Key plugin
  • Automatic database initialization on first startup
  • Session-based auth for web, API key for API requests
The authentication system uses a shared package that both the API and Web applications access, ensuring consistent authentication across all interfaces.

Auth Database Security

SQLite Configuration

Location: /data/auth.db (configurable via DB_PATH) File Permissions:
  • Automatically set to 600 (owner read/write only)
  • Enforced by scripts/secure-db.sh on startup
  • Validated by health check endpoint
Tables:
  • user - User accounts (passwords bcrypt-hashed)
  • session - Web sessions
  • account - Auth providers
  • verification - Email/phone verification
  • apiKey - API keys (hashed)

What’s Protected

Hashed/Encrypted:
  • User passwords (bcrypt)
  • API keys (hashed)
  • Session tokens (secure cookies)

API Key Management

Initial Setup

Mode: MODE=fullstack (default)
1

Create admin account

Visit /setup to create your first admin account.
2

Create API keys

Go to /api-keys to create API keys via the web UI.

Using API Keys

Authorization header:
curl -H "Authorization: Bearer sk_your_key_here" \
  http://localhost:3000/t/image.jpg

API Routes

Complete API Routes Table

MethodRouteDescriptionAuthentication
PUBLIC ROUTES
GET/API health checkPublic
GET/healthAPI health statusPublic
GET/health/databaseDatabase statusProtected
GET/t/*Image/video transformationPublic
GET/video-status/*Video processing statusPublic
GET/video-status/*/sizeOptimized video sizePublic
GET/video-status/statsVideo queue statisticsPublic
GET/queue/eventsSSE stream for queue eventsPublic
PROTECTED ROUTES
POST/uploadUpload one or multiple filesProtected
GET/storageList file tree structureProtected
GET/storage/*/metadataFile metadataProtected
DELETE/storage/*Delete a file and its cacheProtected
POST/api-keys/createCreate a new API keyProtected
GET/api-keys/listList user’s API keysProtected
DELETE/api-keys/:keyIdDelete an API keyProtected
PATCH/api-keys/:keyIdUpdate an API keyProtected
GET/queue/statsQueue statisticsProtected
GET/queue/jobsList jobs (pagination)Protected
POST/queue/jobs/:id/retryRetry a failed jobProtected
POST/queue/jobs/:id/cancelCancel a pending jobProtected
DELETE/queue/jobs/:idDelete a jobProtected
GET/queue/worker/statsWorker statisticsProtected

Legend

  • Public: No authentication required
  • Protected: API key authentication required (header Authorization)

Important Notes

Public routes:
  • /queue/events is public (real-time SSE stream)
  • /t/* is public for direct access to transformations
  • Health endpoints are mostly public except /health/database
Protected routes:
  • All other /queue/* routes are protected
  • /health/database is protected (sensitive information)
  • Protected routes require Authorization: Bearer <API_KEY> header
Example - Create key:
curl -X POST http://localhost:3000/api-keys/create \
  -H "Authorization: Bearer YOUR_KEY" \
  -H "Content-Type: application/json" \
  -d '{"name": "My Key", "expiresIn": 31536000}'

Docker Security

Non-Root User

All containers run as user node:
  • Prevents privilege escalation
  • Limits damage if compromised
  • Automatic ownership: chown -R node:node /app

Volume Permissions

# docker-compose.yml
volumes:
  - ./data:/app/data          # Database

Startup Security

scripts/secure-db.sh runs automatically:
  • Creates /data directory
  • Sets database permissions to 600
  • Verifies security
  • Displays status

Security Best Practices

API Keys

Do:
  • Store in environment variables
  • Use different keys per service
  • Set appropriate expiration
  • Rotate regularly
  • Disable unused keys
Don’t:
  • Commit to version control
  • Share between environments
  • Use same key everywhere
  • Keep expired keys enabled

Passwords

Requirements:
  • Minimum 8 characters
  • Mixed case, numbers, symbols

Rate Limiting

  • Built-in: 100 requests/minute per API key
  • Configurable in packages/shared/src/auth.ts

Incident Response

1

Disable immediately

Disable the key via web UI at /api-keys.
2

Review audit logs

docker logs openinary_api | grep "api_key.success"
3

Generate new key

Create a replacement API key with appropriate permissions.
4

Update applications

Update all applications using the compromised key.
1

Check integrity

docker exec openinary_api sqlite3 /app/data/auth.db "PRAGMA integrity_check;"
2

Restore from backup

docker exec openinary_api /app/scripts/restore-db.sh /backup/latest.db.gz
docker compose restart

Security checkup

CriterionStatus
AuthenticationBetter Auth with API keys
Password Hashingbcrypt automatic
API Key HashingHashed in database
File PermissionsEnforced 600
BackupsDaily automated
Audit LoggingStructured JSON
HTTPS CookiesProduction enforced
Secret ValidationStartup checks
Non-Root ContainersUser node
Health Monitoring/health/database

Additional Resources