Production Checklist
Required
- [ ] Set
NODE_ENV=production - [ ] Set
JWT_SECRETto a random 32+ char string (openssl rand -base64 48) - [ ] Set
ARTIFACT_SECRETfor signed artifact URLs (node -e "console.log(require('crypto').randomBytes(48).toString('hex'))") - [ ] Configure at least one AI provider key
- [ ] Set
VITE_API_URLat frontend build time (for cross-origin deploys) - [ ] Set
CORS_ORIGINto your frontend domain(s)
Database
- [x]
SQLite database— Done (better-sqlite3 with WAL mode) - [x]
PostgreSQL support— Done (INF-001). SetDATABASE_URL=postgres://…to use PostgreSQL instead of SQLite. Installpg+pg-native(ordeasyncas fallback). Both backends use the same adapter interface — no code changes needed.
Infrastructure
- [x]
Redis for rate limiting, token revocation, and SSE pub/sub— Done (INF-002). SetREDIS_URL=redis://…to enable. Installioredis+rate-limit-redis. Falls back to in-memory stores when not configured. - [x]
Cron-based auto-runs— Done (ENH-006). Configure per-project schedules via the Automation page. - [x]
CI/CD webhook trigger— Done (ENH-011).POST /api/v1/projects/:id/triggerwith per-project Bearer tokens. - [x]
Email verification on registration— Done (SEC-001). ConfigureRESEND_API_KEYorSMTP_HOSTfor transactional email. - [x]
Graceful shutdown— Done (MAINT-013). Drains in-flight runs on SIGTERM/SIGINT. - [x]
BullMQ durable background run execution— Done (INF-003). WhenREDIS_URLis set andbullmqis installed, runs execute via a durable job queue with crash recovery, retry, and global concurrency control (MAX_WORKERS). Falls back to in-process execution without Redis. - [x]
Failure notifications (Teams/email/webhook)— Done (FEA-001). Configure per-project viaPATCH /api/projects/:id/notifications. Dispatches on run completion with failures. - [ ] Store videos and screenshots to S3/R2 instead of local disk (MNT-006)
- [x]
Workspace/organisation scoping for multi-tenancy— Done (ACL-001). Every entity is scoped to a workspace viaworkspaceId. Workspaces auto-created on first login. Existing data backfilled on startup. - [x]
Role-based access control— Done (ACL-002). Three roles: Admin / QA Lead / Viewer.requireRole()middleware guards all mutating routes. Frontend gates Settings to admins and hides actions for insufficient roles.
Security
- [x] OAuth state parameter validated
- [x] JWT secret throws in production if missing
- [x] Rate limiting — three-tier: general (300/15 min), auth (5–10/15 min), expensive ops (20/hr), AI generation (30/hr)
- [x] Passwords hashed with scrypt
- [x] JWT in HttpOnly; Secure; SameSite=Strict cookie (never in localStorage)
- [x] CSRF double-submit cookie on all mutating endpoints
- [x] No sensitive data in API responses (JWT never in response body)
- [x] Email verification required before login (SEC-001)
- [x] Artifact serving protected by HMAC-SHA256 signed URLs (ENH-007)
- [x] Gitleaks secrets scanning in CI (ENH-030)
- [x] DB-backed password reset tokens with atomic one-time claim (ENH-013)
- [x]
Nonce-based Content Security Policy— Done (SEC-002). Per-request nonce replaces'unsafe-inline'inscript-src. Vite plugin injectsnonce="__CSP_NONCE__"on<script>tags;serveIndexWithNonce()replaces at serve-time. - [x]
GDPR/CCPA account data export and deletion— Done (SEC-003).GET /api/auth/export(JSON archive) andDELETE /api/auth/account(cascade hard-delete). Both require password confirmation.