# your domain, e.g https://example.com
APP_URL=http://localhost:3000
PORT=3000

# --- Security / reverse proxy ---
# The app derives the client IP (req.ip) from the `X-Forwarded-For` header via
# Fastify `trustProxy`. That header is client-forgeable, so XFF is trusted only
# from proxies on the configured trusted networks. Deploy this app behind a
# trusted reverse proxy that SETS/OVERWRITES (not appends) `X-Forwarded-For`
# with the real client IP. If XFF is trusted from an untrusted source, any
# per-IP throttling — including the /mcp Basic brute-force limiter — can be
# bypassed by an attacker who simply spoofs `X-Forwarded-For` to rotate IPs.
# (The /mcp limiter keeps a global per-email key as an IP-independent backstop,
# but the per-IP and per-IP+email keys rely on a trustworthy X-Forwarded-For.)
#
# TRUST_PROXY controls which proxies are trusted to set X-Forwarded-For.
# Default (unset/empty): `loopback, linklocal, uniquelocal` — XFF is trusted
# ONLY from private/loopback proxies, so a public-IP client cannot spoof req.ip.
# This is the safe default for the common case where the reverse proxy runs on
# loopback or a private network; req.ip still resolves to the real client.
# WARNING: this changed the previous default of trust-all. If your reverse proxy
# sits on a PUBLIC IP, the default will NOT trust its XFF and req.ip will be the
# proxy's IP — set TRUST_PROXY accordingly. Accepted values:
#   - true   restore trust-all (ONLY safe if a trusted proxy ALWAYS overwrites
#            X-Forwarded-For; otherwise clients can spoof their IP)
#   - false  never trust X-Forwarded-For (req.ip is the socket peer)
#   - <int>  number of trusted proxy hops in front of the app
#   - <list> comma-separated CIDR/IP list of trusted proxies, e.g.
#            `127.0.0.1, 10.0.0.0/8`
# TRUST_PROXY=

# APP_SECRET has a DUAL role: it signs JWTs AND derives the AES-256-GCM key that
# encrypts stored AI-provider credentials (API keys) at rest. CONSEQUENCE: if you
# change APP_SECRET after setup, every stored AI API key becomes undecryptable —
# you must re-enter them in AI settings — and all existing sessions/JWTs are
# invalidated. Choose it ONCE, keep it stable, and back it up alongside your DB.
# minimum of 32 characters. Generate one with: openssl rand -hex 32
APP_SECRET=REPLACE_WITH_LONG_SECRET

JWT_TOKEN_EXPIRES_IN=30d

DATABASE_URL="postgresql://postgres:password@localhost:5432/docmost?schema=public"
REDIS_URL=redis://127.0.0.1:6379

# options: local | s3 | azure
STORAGE_DRIVER=local

# S3 driver config
AWS_S3_ACCESS_KEY_ID=
AWS_S3_SECRET_ACCESS_KEY=
AWS_S3_REGION=
AWS_S3_BUCKET=
AWS_S3_ENDPOINT=
AWS_S3_FORCE_PATH_STYLE=

# Azure Blob Storage driver config
AZURE_STORAGE_ACCOUNT_NAME=
AZURE_STORAGE_ACCOUNT_KEY=
AZURE_STORAGE_CONTAINER=

# default: 50mb
FILE_UPLOAD_SIZE_LIMIT=

# options: smtp | postmark
MAIL_DRIVER=smtp
MAIL_FROM_ADDRESS=hello@example.com
MAIL_FROM_NAME=Docmost

# SMTP driver config
SMTP_HOST=127.0.0.1
SMTP_PORT=587
SMTP_USERNAME=
SMTP_PASSWORD=
SMTP_SECURE=false
SMTP_IGNORETLS=false

# Postmark driver config
POSTMARK_TOKEN=

# for custom drawio server
DRAWIO_URL=

# Gotenberg URL for server-side PDF export
GOTENBERG_URL=

DISABLE_TELEMETRY=false

# Allow other sites to embed Docmost in an iframe.
IFRAME_EMBED_ALLOWED=false

# Only used when IFRAME_EMBED_ALLOWED=true. When empty, any origin is allowed.
# Example: https://intranet.example.com,https://portal.example.com
IFRAME_ALLOWED_ORIGINS=

# Enable debug logging in production (default: false)
DEBUG_MODE=false

# Log database queries
DEBUG_DB=false

# Log http requests
LOG_HTTP=false

# MCP server (community): the embedded /mcp endpoint authenticates PER USER.
# An MCP client authenticates with one of:
#   - HTTP Basic: `Authorization: Basic base64(email:password)` — the user's own
#     Docmost login/password. The server validates the credentials and the MCP
#     session then acts under that user's permissions (edits attributed to them).
#   - Bearer access JWT: `Authorization: Bearer <access-jwt>` (the user's
#     `authToken` cookie value). Validated as an ACCESS token.
#
# OPTIONAL service-account fallback. When a request carries NEITHER Basic NOR
# Bearer credentials and these are set, the MCP session falls back to this
# shared service account (back-compat; useful for CI/scripts). Leave BLANK to
# require per-user credentials.
MCP_DOCMOST_EMAIL=
MCP_DOCMOST_PASSWORD=
# MCP_DOCMOST_API_URL=http://127.0.0.1:3000/api
# Optional shared guard for the /mcp endpoint. When set, every /mcp request must
# carry a matching `X-MCP-Token` header (separate from `Authorization`, which now
# carries the per-user credentials). When unset, /mcp relies on the per-user
# credentials above plus the workspace MCP toggle and network isolation (do not
# expose the port publicly).
# MCP_TOKEN=
# MCP_SESSION_IDLE_MS=1800000

# Per-embedding-call timeout in milliseconds for the RAG indexer.
# A slow/hung embeddings endpoint fails after this and the batch continues.
# AI_EMBEDDING_TIMEOUT_MS=120000

# --- Anonymous public-share AI assistant ---
# Opt-in per workspace (AI settings -> "public share assistant"; off by default).
# When enabled, anonymous visitors of a published share can ask an AI about that
# share at POST /api/shares/ai/stream. The assistant is read-only and hard-scoped
# to the single share tree, but every call spends real tokens on the workspace
# owner's configured AI provider.
#
# DEPLOYMENT REQUIREMENT: the per-IP rate limit on this endpoint is only
# effective behind a trusted reverse proxy that OVERWRITES (not appends)
# X-Forwarded-For with the real client IP. The app runs with trustProxy, so
# without such a proxy an attacker can rotate X-Forwarded-For to evade the
# per-IP limit. Put this endpoint (and the app) behind a proxy you control that
# sets X-Forwarded-For to the real client IP.
#
# Backstop: a cluster-wide, sliding-window cap per workspace (IP-independent,
# keyed by the server-resolved workspace id) bounds the owner's bill even if the
# per-IP limit is fully evaded. It is a COST backstop, not an access control, and
# FAILS CLOSED if Redis is unavailable (an optional assistant briefly going
# offline is safer than an unbounded bill). Override the hourly cap below
# (default: 100 calls per workspace per rolling hour).
# SHARE_AI_WORKSPACE_MAX_PER_HOUR=100
#
# Per-request output-token ceiling for the anonymous assistant (default: 512).
# Worst-case output per accepted call = agent steps (5) × this value.
# SHARE_AI_MAX_OUTPUT_TOKENS=512
