Appearance
Securing your deployment
A hardening checklist for whoever runs a self-hosted FlowRunner: strong secrets, a locked-down database, HTTPS, an authenticated scheduler, and the protections the platform already runs for you.
FlowRunner keeps almost nothing outside MongoDB, so most of your security work is protecting two things well: the environment secrets the app boots with, and the database those secrets unlock.
Trust boundaries you control
1. Generate strong secrets and keep them out of git
Generate every secret with openssl — never reuse a value or paste one from an example.
| Variable | Generate | What it protects |
|---|---|---|
AUTH_SECRET | openssl rand -base64 32 | Signs Auth.js session JWTs. Required — the app refuses to start without it. |
SECRETS_KEY | openssl rand -base64 32 | Encrypts the per-workspace secrets vault (AES-256-GCM). Must decode to exactly 32 bytes. |
CRON_SECRET | openssl rand -hex 32 | Authenticates the scheduler tick. |
WARNING
Never commit these to git. Set them in your host's environment (Vercel project settings, container secrets, a secrets manager) and add .env* to .gitignore. A leaked AUTH_SECRET lets anyone forge sessions; a leaked SECRETS_KEY decrypts every stored secret.
assertEnv() hard-fails at boot only on AUTH_SECRET and MONGODB_URI. The optional keys log a warning and switch their feature off when absent — a missing SECRETS_KEY silently disables the vault rather than crashing. See Environment variables.
2. Lock down the database
The connection string is the keys to everything. Two operator-side controls that FlowRunner cannot enforce for you:
- Use a least-privilege database user. Create a Mongo user with
readWritescoped to only the app's database — notroot,dbAdmin, or a cluster-wide role. The app never needs admin rights, so a scopedreadWriteuser is best practice and limits the blast radius of a leakedMONGODB_URI. - Restrict network access. On Atlas, use Network Access → IP Access List to allow only your app hosts' egress IPs. On a self-hosted Mongo, bind it to a private network and firewall the port. Never leave the database open to
0.0.0.0/0.
3. Always serve over HTTPS
Terminate TLS at your proxy or platform edge and serve the app only over https://.
bash
AUTH_URL=https://app.example.com # your public base URL
AUTH_TRUST_HOST=true # required behind a proxy / hosted platformAUTH_TRUST_HOST=true tells Auth.js to trust the forwarded host header from your proxy — without it, sign-in redirects and cookies misbehave. Plain HTTP would expose session cookies in transit.
4. Authenticate the scheduler
The scheduler tick (POST /api/cron/tick) is fail-closed: with no CRON_SECRET set it returns 401 and no schedule ever fires. When set, callers must send Authorization: Bearer $CRON_SECRET, and the app compares it with a constant-time check.
bash
curl -sS -X POST -H "Authorization: Bearer $CRON_SECRET" https://app.example.com/api/cron/tick5. Back up the database and the key separately
A single mongodump (or Atlas continuous backup) captures everything — but the secrets collection holds only ciphertext, decryptable solely with the same SECRETS_KEY. Store that key in a separate secrets manager, versioned apart from the snapshot. Lose or rotate it and every stored secret is stranded. Full routine in Backup & restore.
6. Keep the components catalog on an origin you control
The editor loads node and component config UIs from a catalog origin (NEXT_PUBLIC_FORM_CATALOG_URL). Point it only at a host you operate.
NOTE
Today every built-in node and component is first-party, so production serves all of them from one trusted origin (components.flowrunner.io). That is safe because they are first-party. FlowRunner's design anticipates per-plugin origin isolation for third-party components, but that model assumes per-origin (wildcard-subdomain) hosting, which is not the current deploy — and executing untrusted third-party node logic server-side is not sandboxed yet (only first-party built-in nodes run in the engine). Until both land, do not point the catalog at an untrusted host. See The execution sandbox.
What the platform already enforces
You don't wire these up — they run automatically — but know they exist:
- Rate limiting. A fixed-window limiter backed by the
ratelimitscollection guards login, form submissions, uploads, invites, and flow runs. Its TTL index reaps old counters. - Audit log. Security and administrative events (secret changes, membership changes, ownership transfers) are appended per workspace to the
auditcollection, best-effort so they never block the operation they record.
Hardening checklist
- [ ]
AUTH_SECRET,SECRETS_KEY,CRON_SECRETgenerated withopenssl, never in git - [ ] Mongo user is
readWriteon the app database only — not admin - [ ] Database network access restricted to your app hosts
- [ ]
AUTH_URLset and served over HTTPS withAUTH_TRUST_HOST=true - [ ]
CRON_SECRETset so the scheduler is authenticated (fail-closed without it) - [ ]
SECRETS_KEYbacked up separately from the database - [ ] Components catalog points only at an origin you control