Security & production checklist
Indelible fails closed in production: it refuses to boot with development secrets and defaults egress protections on. This page covers the settings that matter for a safe self-hosted deployment.
Secrets
Section titled “Secrets”Generate strong, unique secrets and keep them out of version control:
openssl rand -base64 32 # JWT_SECRET: signs short-lived access tokensopenssl rand -base64 32 # CSRF_SECRET: signs CSRF tokensopenssl rand -base64 32 # AUTH_CREDENTIAL_KEY: encrypts stored integration tokensJWT_SECRET/CSRF_SECRET: required in production; the API will not start with the development defaults. To rotateJWT_SECRET, deploy the new value in a low-traffic window; outstanding access tokens become invalid and clients transparently re-authenticate via the refresh cookie.AUTH_CREDENTIAL_KEY: required in production whenever an integration (e.g. Notion) is configured. It encrypts the OAuth tokens Indelible stores on a user’s behalf. Set the same value onind-apiandind-worker.
Accounts
Section titled “Accounts”After creating your account(s), close registration:
AUTH_ALLOW_SIGNUPS: 'false'The first account on an empty instance can always be created, so you can lock signups immediately after onboarding yourself.
Network exposure
Section titled “Network exposure”- TLS: terminate HTTPS at a reverse proxy (Caddy, nginx, Traefik) in front of
the API. Set
IND_BASE_URL,FRONTEND_URL, andCORS_ORIGINSto your realhttps://origins. The mobile apps default to HTTPS and show an explicit unencrypted-connection warning before they’ll talk to a plain-http://server; see Use the mobile apps. TRUSTED_PROXIES: when running behind a proxy or CDN, set this to the proxy’s IPs/CIDRs so rate limiting and audit logs see the real client IP. Leave it empty when the API is exposed directly, otherwise a client can spoofX-Forwarded-Forto evade auth rate limits.CORS_ORIGINS: lock this to the exact origin(s) serving your web app. Never use a wildcard.
Server-side fetch protections
Section titled “Server-side fetch protections”Indelible fetches user-supplied URLs (article capture, feeds, the renderer, AI providers, webhook delivery). Two guards keep those fetches from reaching your internal network:
EGRESS_ALLOW_PRIVATE_TARGETS: defaults off in production. It blocks fetches to private, loopback, link-local, and cloud-metadata addresses. Only set ittrueif you must reach a local renderer/feed/AI endpoint, and network-isolate those services if you do. Set it consistently onind-api,ind-worker, andind-renderer.WEBHOOKS_ALLOW_PRIVATE_TARGETS: defaults off; only honored in addition to the egress guard. Leave off unless you deliberately deliver webhooks to a private endpoint.
Email ingestion
Section titled “Email ingestion”If you enable inbound email (EMAIL_INGEST_PROVIDER=resend), set
EMAIL_INGEST_WEBHOOK_SECRET so Indelible can verify that inbound webhooks really
came from your provider, and keep RESEND_API_KEY secret.
Object storage
Section titled “Object storage”- Keep the bucket private. By default (
ASSET_SERVING_MODE=passthrough) assets are streamed through the API, so the object store never needs to be reachable from a browser at all. SetASSET_COOKIE_SECRET(64-character hex); the API refuses to start in production without it in that mode. ASSET_SERVING_MODE=presignedhands browsers short-lived URLs pointing directly atS3_ENDPOINT. Only use it when that endpoint is reachable from your users’ browsers and terminates TLS. Pointing it at an internal address such ashttp://minio:9000yields links no browser can open.- Restrict the S3 credentials to the single Indelible bucket.
Backups
Section titled “Backups”Back up two things together:
- PostgreSQL: all metadata, accounts, highlights, and reading state.
- Object storage: the archived content itself (the bucket).
A database restore without its matching bucket (or vice versa) leaves dangling references. Snapshot both on the same schedule.
Production checklist
Section titled “Production checklist”-
IND_ENV=production - Strong
JWT_SECRETandCSRF_SECRET(not the dev defaults) -
AUTH_CREDENTIAL_KEYset on api + worker if any integration is configured -
IND_BASE_URL,FRONTEND_URL,CORS_ORIGINSset to your real HTTPS origins - TLS terminated at the proxy;
TRUSTED_PROXIESset to the proxy ranges -
AUTH_ALLOW_SIGNUPS=falseafter creating your account(s) -
EGRESS_ALLOW_PRIVATE_TARGETSleft off (or services network-isolated if on) - Object storage bucket private; S3 credentials scoped to that bucket
-
EMAIL_INGEST_WEBHOOK_SECRETset if inbound email is enabled - PostgreSQL + object-storage backups scheduled together