Configuration
Orimora is configured entirely through environment variables (and team-level preferences in the database). The source of truth is .env.example at the repository root; expand each section below for the full table.
Core application
Section titled “Core application”Core application variables
| Variable | Required | Description |
|---|---|---|
APP_URL | Yes (prod) | Public base URL, e.g. https://wiki.example.com. Drives magic links and OAuth redirects. Dev: http://localhost:5173. |
NODE_ENV | — | development or production. |
PORT | — | HTTP listen port (default 3000 in Docker; Vite dev uses its own port). |
Database and cache
Section titled “Database and cache”Database and Redis
| Variable | Required | Description |
|---|---|---|
DATABASE_URL | Yes | PostgreSQL 16+ connection string. |
REDIS_URL | Yes | Used for sessions, rate limiting, queues, and BullMQ workers. |
Authentication secrets
Section titled “Authentication secrets”Session and magic-link secrets
| Variable | Required | Description |
|---|---|---|
SESSION_SECRET | Yes | 64 hex chars (32 bytes). Signs session cookies. |
MAGIC_LINK_SECRET | Yes | 64 hex chars. Signs magic-link JWTs. |
Generate both with:
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"Optional dedicated HMAC secrets (recommended in production for independent rotation):
| Variable | Default when unset | Purpose |
|---|---|---|
API_KEY_SECRET | MAGIC_LINK_SECRET | Hashes API-key tokens at rest |
EMAIL_CHANGE_SECRET | MAGIC_LINK_SECRET | Signs email-change confirmation tokens |
ACCOUNT_DELETION_SECRET | SESSION_SECRET | Signs account-deletion confirmation tokens |
| Variable | Default | Description |
|---|---|---|
SESSION_IDLE_TIMEOUT_DAYS | 7 | Invalidate sessions after this many idle days. Set 0 to disable idle expiry. |
Registration and public URLs
Section titled “Registration and public URLs”Registration, docs redirect, CORS
| Variable | Default | Description |
|---|---|---|
ALLOW_REGISTRATION | false | When false, only invited users can join (open sign-up disabled). |
DOCS_URL | https://docs.orimora.com | Where the app redirects /docs requests (Starlight site). |
EXTRA_ALLOWED_ORIGINS | — | Comma-separated extra origins for WebSocket /collab and Origin checks. |
Email (magic link)
Section titled “Email (magic link)”SMTP settings
If SMTP is unset, magic-link email is not sent (use OAuth or another strategy in dev).
| Variable | Description |
|---|---|
SMTP_HOST | Server hostname |
SMTP_PORT | Usually 587 (STARTTLS) or 465 |
SMTP_USER | Username |
SMTP_PASSWORD | Password (name in .env.example) |
SMTP_FROM | From address |
Google, Microsoft, OIDC
| Variable | Description |
|---|---|
GOOGLE_CLIENT_ID | OAuth client ID |
GOOGLE_CLIENT_SECRET | OAuth client secret |
Redirect: {APP_URL}/auth/google/callback
Microsoft Entra ID
Section titled “Microsoft Entra ID”| Variable | Description |
|---|---|
MICROSOFT_TENANT_ID | Tenant ID or common for multi-tenant |
MICROSOFT_CLIENT_ID | Application (client) ID |
MICROSOFT_CLIENT_SECRET | Client secret |
Redirect: {APP_URL}/auth/microsoft/callback
Generic OIDC
Section titled “Generic OIDC”| Variable | Description |
|---|---|
OIDC_ISSUER | Issuer URL |
OIDC_CLIENT_ID | Client ID |
OIDC_CLIENT_SECRET | Client secret |
OIDC_SCOPE | Space-separated scopes (default openid email profile) |
Redirect: {APP_URL}/auth/oidc/callback
Collaboration (Yjs / Hocuspocus)
Section titled “Collaboration (Yjs / Hocuspocus)”WebSocket /collab settings
| Variable | Description |
|---|---|
COLLAB_SECRET | Optional shared secret for the collab endpoint |
COLLAB_MAX_CONNECTIONS | Max concurrent WebSocket connections |
COLLAB_MAX_YJS_STATE_BYTES | Max persisted Yjs state size per document (default 8 MB). Set 0 to disable the service-layer check. |
MCP OAuth (Claude mobile / Custom Connectors)
Section titled “MCP OAuth (Claude mobile / Custom Connectors)”OAuth 2.1 + Dynamic Client Registration for the MCP endpoint
Lets the Claude mobile app and claude.ai Custom Connectors authorize against /api/mcp via a browser OAuth flow, in addition to the existing kb_… API keys (which stay unchanged). See the MCP guide for the user-facing setup.
The OAuth issuer is APP_URL — set it to your public https origin before enabling in production, or discovery hands out unusable localhost URLs (the server logs a warning at startup).
| Variable | Default | Description |
|---|---|---|
MCP_OAUTH_ENABLED | true | Master switch. When false, the .well-known/oauth-* routes 404 and the server presents as Bearer-only. |
MCP_OAUTH_ALLOWED_APP_SCHEMES | claude:// | Comma-separated custom redirect schemes accepted at registration (besides https and http://localhost). |
MCP_OAUTH_CORS_ORIGINS | https://claude.ai,https://www.claude.ai | Browser origins allowed to call the OAuth + MCP surface cross-origin. |
MCP_OAUTH_ACCESS_TOKEN_TTL_SECONDS | 3600 | Access-token lifetime. |
MCP_OAUTH_REFRESH_TOKEN_TTL_SECONDS | 2592000 | Refresh-token lifetime (30 days). |
MCP_OAUTH_AUTH_CODE_TTL_SECONDS | 60 | Authorization-code lifetime. |
OAUTH_REGISTER_RATE_LIMIT_PER_HOUR | 10 | Per-IP cap on Dynamic Client Registration (/oauth/register). |
MCP_OAUTH_TOKEN_SECRET | API_KEY_SECRET | HMAC secret for hashing OAuth codes/tokens at rest. Set an independent 64-char value in production. |
Object storage, AI, publishing, push, quota, cron
Section titled “Object storage, AI, publishing, push, quota, cron”Optional services and encryption keys
| Area | Variables |
|---|---|
Upload storage (opt-in: STORAGE_DRIVER=s3; default local) | STORAGE_DRIVER, S3_BUCKET, S3_REGION, S3_ACCESS_KEY, S3_SECRET_KEY, S3_ENDPOINT, S3_FORCE_PATH_STYLE, S3_PRESIGN_TTL_SECONDS — see below |
| AI | LLM_ENCRYPTION_KEY (required) — AI assistant |
| Publishing | PUBLISHING_ENCRYPTION_KEY (required), GIT_MIRROR_ALLOWED_HOSTS |
| Web Push | VAPID_PUBLIC_KEY, VAPID_PRIVATE_KEY, VAPID_SUBJECT — enables the push channel. Step-by-step setup + key generation: Notifications → web push setup |
| Storage quota | DEFAULT_TEAM_STORAGE_QUOTA_BYTES, STORAGE_QUOTA_WARN_PERCENT |
| Backups | BACKUP_ENABLED, BACKUP_PATH, BACKUP_SCHEDULE, BACKUP_RETENTION_DAILY/WEEKLY, BACKUP_S3_BUCKET, BACKUP_PG_DUMP_TIMEOUT_MS |
| Off-site DR | BACKUP_RCLONE_REMOTE, BACKUP_POST_HOOK — encrypted off-site copies, see Backups + Disaster Recovery |
| Cron | CRON_SECRET for POST /api/admin/cron.cleanup — required in production (drives scheduled cleanup + invite reminders) |
| Observability | METRICS_TOKEN (Prometheus /api/metrics); SENTRY_DSN, SENTRY_ENVIRONMENT, SENTRY_RELEASE (error tracking — see below) |
| MCP CLI | ORIMORA_API_KEY — see MCP guide |
Webhook/publishing auto-pause thresholds (WEBHOOK_*, PUBLISHING_*) and SESSION_IDLE_TIMEOUT_DAYS are documented inline in .env.example.
Error tracking. Set SENTRY_DSN to forward unexpected 5xx server errors and SSO authentication failures to any Sentry-compatible backend — sentry.io, a self-hosted Sentry, or GlitchTip all work with only the DSN changing (no vendor lock-in). Routine 4xx is never sent, and email addresses and token-shaped strings are scrubbed from payloads. Leave it empty to disable. SENTRY_ENVIRONMENT defaults to NODE_ENV; SENTRY_RELEASE (e.g. a git SHA) groups issues by version. The metrics endpoint, health probes, error tracking, and correlation IDs are covered together in Observability.
Team preferences (HTTP API)
Section titled “Team preferences (HTTP API)”Some defaults are stored per team in the database (document width, locale, revision retention). See the admin settings UI and REST API overview.
S3 object storage
Section titled “S3 object storage”By default, uploads (document attachments, avatars, team logos) are stored on disk under ./uploads and served by the app. Set STORAGE_DRIVER=s3 to store them in an S3-compatible bucket instead — AWS S3, MinIO, Cloudflare R2 or Backblaze B2.
| Variable | Purpose |
|---|---|
STORAGE_DRIVER | local (default) or s3 |
S3_BUCKET | Bucket name (required when s3) |
S3_ACCESS_KEY / S3_SECRET_KEY | Credentials (required when s3) |
S3_REGION | Region (default us-east-1) |
S3_ENDPOINT | Custom endpoint for non-AWS S3 (MinIO/R2/B2); empty = real AWS S3 |
S3_FORCE_PATH_STYLE | true for MinIO and some self-hosted gateways |
S3_PRESIGN_TTL_SECONDS | Lifetime of presigned GET URLs (default 300) |
The bucket stays private — files are served via short-lived presigned URLs that the upload routes redirect to, after the same permission check as the local path (so a restricted document’s attachments stay protected). In production, STORAGE_DRIVER=s3 with a missing bucket or credentials is a hard start-up failure rather than a silent fallback.
Orphaned objects (left behind by a rare failed delete) are swept by the scheduled cleanup cron, conservatively: only objects that no row references and that are older than 24 h are removed, so a just-uploaded file is never deleted.
Set the driver at deploy time, not on a running instance. Flipping local → s3 leaves existing on-disk files unreachable (every existing image 404s) until they are backfilled into the bucket; the driver is never auto-detected from S3_*.
Migrating an existing instance. With S3_* set (the driver can stay local), copy the existing uploads into the bucket first:
yarn storage:migrate-s3 # dry run — reports what would copyyarn storage:migrate-s3 --apply # uploadIt is idempotent (re-run to catch files added since) and non-destructive (local files are only read). Once it reports success, set STORAGE_DRIVER=s3 and redeploy. To roll back, set it to local again — the local files are untouched.
Security notes
Section titled “Security notes”See also
Section titled “See also”- Installation — local dev checklist
- Deployment — Docker Compose and migrations
- Coolify Setup — production env checklist
- REST API overview — Bearer keys and rate limits