# --------------------------------------------------------------------------- # AI Software Factory — environment configuration # Copy to .env and fill in real values. NEVER commit secrets to source control. # --------------------------------------------------------------------------- # App NODE_ENV=************ (redacted) PORT=************ (redacted) WEB_URL=************ (redacted) # Server-to-server base (realtime → API internal endpoints, web /health/ready # probe). Loopback by design: these are local service-to-service calls, not # browser-facing — the public origin for browsers is NEXT_PUBLIC_API_URL above. API_URL=************ (redacted) REALTIME_URL=************ (redacted) PUBLIC_BASE_URL=************ (redacted) # --- Reverse proxy (Caddy) — keep in sync with the /etc/caddy/Caddyfile --- # Topology: # notion.filps.com = THE app origin. Caddy serves the web UI and reverse- # proxies /api/v1/*, /saml/*, /sso/*, /realtime* and the S3 bucket # prefixes (/factory-files/*, /factory-thumbs/* → the S3 shim :9000) # to the local service ports. The BROWSER talks only to this # origin, so the host-scoped httpOnly `factory_refresh` cookie (no # Domain= attr — see apps/api/src/common/cookies.ts) satisfies the web # middleware's session gate (apps/web/src/middleware.ts) without any # cross-host cookie sharing. # aiops.filps.com = direct access to the backend API (webhook deliveries, # API-key clients, /docs, programmatic use). Not browser-facing. CORS_ORIGINS=************ (redacted) WEB_ORIGIN=************ (redacted) SSO_ORIGIN=************ (redacted) WEBHOOK_BASE_URL=************ (redacted) # Browser-facing bases (NEXT_PUBLIC_* are inlined into the web bundle at # `next dev` startup). NEXT_PUBLIC_API_URL=************ (redacted) NEXT_PUBLIC_WS_URL=************ (redacted) NEXT_PUBLIC_APP_URL=************ (redacted) # Database (PostgreSQL) # Redis (caching, queues, presence) # Auth / Sessions JWT_EXPIRES_IN=************ (redacted) REFRESH_TOKEN_TTL_DAYS=************ (redacted) BCRYPT_ROUNDS=************ (redacted) # S3-compatible object storage (local: the S3 shim at :9000, reached THROUGH # the frontend origin — see S3_ENDPOINT below) — TASK-0101 (two private # buckets; `S3_BUCKET` is the legacy alias for S3_BUCKET_FILES). S3_BUCKET_FILES=************ (redacted) S3_BUCKET_THUMBS=************ (redacted) S3_BUCKET=************ (redacted) S3_REGION=************ (redacted) S3_FORCE_PATH_STYLE=************ (redacted) S3_SIGN_EXPIRY_SECONDS=************ (redacted) # ClamAV (dev mode = empty; Docker profile sets CLAMAV_HOST=clamav:3310) CLAMD_SOCKET=************ (redacted) CLAMAV_HOST=************ (redacted) CLAMAV_PORT=************ (redacted) # Search (OpenSearch) SEARCH_USERNAME=************ (redacted) SEARCH_PASSWORD=************ (redacted) SEARCH_INDEX_PREFIX=************ (redacted) # Content-index dense_vector tuning (TASK-0099 — dimensions are immutable per # index; a change rolls a new index via `pnpm search:reindex`). HNSW: m / # ef_construction, dimension = the embedding model's output (1536 = # text-embedding-3-small). SEARCH_VECTOR_DIMENSION=************ (redacted) SEARCH_KNN_M=************ (redacted) SEARCH_KNN_EF_CONSTRUCTION=************ (redacted) # AI gateway (provider-agnostic; any OpenAI-compatible endpoint works) # Wired to the OpenAI-compatible self-hosted gateway (key from # ai-api-key.txt in the repo root). The key only grants `qwen3.8-27B` (a vLLM # reasoning model); embeddings 403 for this key, so AI_EMBEDDING_PROVIDER # stays EMPTY and the embed:chunks job skips (vectors stay null). AI_PROVIDER=************ (redacted) AI_BASE_URL=************ (redacted) AI_API_KEY=************ (redacted) AI_MODEL=************ (redacted) AI_EMBEDDING_MODEL=************ (redacted) # Embedding provider for the `embed:chunks` job (TASK-0099). EMPTY (default) # = no real provider wired: the job logs "embedding disabled (Phase 12)" and # skips (the chunk docs' vector stays null). Set to a provider name to record # placeholder AiEmbedding cache rows until Phase 12 wires the real call. AI_EMBEDDING_PROVIDER=************ (redacted) # --- OSS-reasoning-model tuning (qwen3.8-27B via vLLM; root-cause fixes) --- # Page-generation total cap (default 120 s): the forced generatePlan tool # call still needs ~130 s for a detailed plan (long silent reasoning phase # first). 600 s leaves generous margin; the client can cancel earlier. AI_GENERATE_TIMEOUT_MS=************ (redacted) # Max gap between streamed BYTES (default 30 s): the model's silent thinking # phase produced a 39 s gap in probes → mid-stream 504 the gateway cannot # retry. 90 s covers it. AI_STREAM_IDLE_MS=************ (redacted) # Output-token clamp (default 4096): the model emits ~9 k reasoning tokens # BEFORE the plan JSON; a 4096 clamp truncated the plan mid-stream. AI_MAX_OUTPUT_TOKENS=************ (redacted) # Email (SMTP) SMTP_HOST=************ (redacted) SMTP_PORT=************ (redacted) SMTP_USER=************ (redacted) SMTP_PASSWORD=************ (redacted) SMTP_FROM=************ (redacted) # Realtime REALTIME_PORT=************ (redacted) REALTIME_HEARTBEAT_MS=************ (redacted) YJS_STORAGE_PREFIX=************ (redacted) # Shared service token for the internal endpoints (`/internal/*`, TASK-0092 # slice 2): the realtime gateway authenticates to the api's internal block # endpoints (hydration read + the flush's CAS persist) with this bearer # token. Empty (default) = the internal endpoints are CLOSED (fail-closed). # Webhooks WEBHOOK_RETRY_MAX=************ (redacted) WEBHOOK_TIMEOUT_MS=************ (redacted) # DEV-ONLY (E2E exit scenario, TASK-0130): allow loopback delivery URLs and a # short retry backoff so the visible-retries window is observable in ~15s. # The canonical production defaults are 30s/15min (proven by the integration # suite on a mocked clock). Remove these lines to restore. WEBHOOK_ALLOW_PRIVATE=************ (redacted) WEBHOOK_BACKOFF_BASE_MS=************ (redacted) WEBHOOK_BACKOFF_MAX_MS=************ (redacted) # Rate limiting # NOTE: the realtime app reads RATE_LIMIT_WINDOW_MS / RATE_LIMIT_MAX. The API # guard (apps/api/src/common/guards/rate-limit.guard.ts) reads a DIFFERENT pair # — RATE_LIMIT_API / RATE_LIMIT_AUTH (per user/min) — so the two lines above # do NOT affect the API. The API guard's defaults (100/min, 10/min) throttle # the local full-stack E2E (which legitimately makes many calls), so they are # raised high here for local dev/E2E only (this .env is gitignored; CI sets its # own). Recorded in the Phase 11 gap report as a pre-existing env-naming split. RATE_LIMIT_WINDOW_MS=************ (redacted) RATE_LIMIT_MAX=************ (redacted) RATE_LIMIT_API=************ (redacted) RATE_LIMIT_AUTH=************ (redacted) # Observability (optional; enable in Phase 15) OTEL_ENABLED=************ (redacted) OTEL_EXPORTER_OTLP_ENDPOINT=************ (redacted) # Encryption (application-level secret encryption, at-rest secrets) DATABASE_URL=************ (redacted) REDIS_URL=************ (redacted) SEARCH_URL=************ (redacted) # Public S3 base: the shim at :9000 behind the app origin. Caddy routes # the two bucket prefixes (/factory-files/*, /factory-thumbs/*) to # 127.0.0.1:9000 preserving Host (no path rewrite). MUST be a bare # origin (no path): both S3 clients (packages/storage, worker) derive # the signed `host` header from the endpoint authority, and the worker # client builds URLs as `origin + path`. Presigned URLs + the browser's # direct upload XHR are then SAME-ORIGIN with the web app (CSP `img-src`/ # `connect-src` `'self'` covers them — no CSP change needed). SigV4 # survives the proxy because the signed host = endpoint authority and # Caddy forwards the original Host unchanged. S3_ENDPOINT=************ (redacted) S3_ACCESS_KEY=************ (redacted) S3_SECRET_KEY=************ (redacted) JWT_SECRET=************ (redacted) REFRESH_TOKEN_SECRET=************ (redacted) SESSION_SECRET=************ (redacted) MASTER_ENCRYPTION_KEY=************ (redacted) INTERNAL_SERVICE_TOKEN=************ (redacted) INTERNAL_TEST_SEAM=************ (redacted)