# --------------------------------------------------------------------------- # AI Software Factory — environment configuration # Copy to .env and fill in real values. NEVER commit secrets to source control. # --------------------------------------------------------------------------- # App NODE_ENV=development PORT=4000 WEB_URL=http://localhost:3000 API_URL=http://localhost:4000 REALTIME_URL=ws://localhost:4001 PUBLIC_BASE_URL=http://localhost:3000 # TASK-0131: the public origin that SERVES the SSO protocol surface # (/saml/*, /sso/*, later /scim/*) — these are browser bindings mounted by # the API outside /api/v1. In two-origin dev (web :3000 + api :4000) this # must be the API origin, or the IdP's POST to the ACS and the browser # start/authorize GETs will 404 on the web origin. # Leave UNSET/empty for single-origin deployments (the reference nginx # fronts web + api under one host — WEB_URL serves both). SSO_ORIGIN=http://localhost:4000 # --- Reverse proxy (two domains) — see infrastructure/caddy/Caddyfile ------- # Deployment: = the app origin (UI + /api/v1/* + /realtime + # S3 presigned URLs); = direct backend API surface (webhook # deliveries, API-key clients, /api/v1/docs, IdP SCIM/ACS). The BROWSER talks # only to , so the host-scoped httpOnly `factory_refresh` cookie # (no Domain= attr) satisfies the web middleware's session gate without # cross-host cookie sharing. Leave the localhost values for local dev. # Two-domain deploy: CORS_ORIGINS/WEB_ORIGIN/NEXT_PUBLIC_API_URL → # https:// (…/api/v1); NEXT_PUBLIC_WS_URL → wss://; # WEBHOOK_BASE_URL → https:///api/v1. CORS_ORIGINS=http://localhost:3000 WEB_ORIGIN=http://localhost:3000 WEBHOOK_BASE_URL=http://localhost:4000/api/v1 # NEXT_PUBLIC_*: inlined into the web bundle at `next dev`/`build` startup. NEXT_PUBLIC_API_URL=http://localhost:4000/api/v1 NEXT_PUBLIC_WS_URL=ws://localhost:4001 NEXT_PUBLIC_APP_URL=http://localhost:3000 # Database (PostgreSQL) DATABASE_URL=postgresql://factory:factory@localhost:5432/factory?schema=public # Read replica (Phase 15 — OPTIONAL). When set, db.read() (tree loads, record # listing/table views, search) routes to this URL and db.write() stays on # DATABASE_URL. Permission decisions + If-Match/baseVersion version checks are # NEVER replica reads (write-then-read-your-write always hits the primary). # In staging this is the PgBouncer `reader` pool on the read replica # (infrastructure/kubernetes/postgres/replica.yaml). Leave EMPTY in dev. DATABASE_REPLICA_URL= # Per-app PgBouncer pool_size share (Phase 15). PgBouncer (transaction mode) # owns connection pooling — Prisma 5.22 has no client-side pool — so this is # the app's share of the pooler, NOT a Prisma option (see # docs/development/deployment.md §3.1 pooling + perf-report §6). DATABASE_POOL_SIZE=20 # Perf-cache gate (Phase 15): auto = on only in production (off in dev/tests), # on = force on, off = force off. See docs/development/perf-report.md §5. PERF_CACHE=auto # Redis (caching, queues, presence) REDIS_URL=redis://localhost:6379 # Auth / Sessions JWT_SECRET=change-me-to-a-long-random-string JWT_EXPIRES_IN=15m REFRESH_TOKEN_SECRET=change-me-to-another-long-random-string REFRESH_TOKEN_TTL_DAYS=30 SESSION_SECRET=change-me-session-secret BCRYPT_ROUNDS=12 # S3-compatible object storage (MinIO in local dev) — TASK-0101. # Two PRIVATE buckets (no public access; served only via signed URLs): # S3_BUCKET_FILES — uploaded objects, `files/{ws}/{fileId}/{v}/{name}` layout # S3_BUCKET_THUMBS — thumbnail/preview artifacts, `thumbs/{ws}/{fileId}/{size}/{name}` S3_ENDPOINT=http://localhost:9000 S3_ACCESS_KEY=minioadmin S3_SECRET_KEY=minioadmin S3_BUCKET_FILES=factory-files S3_BUCKET_THUMBS=factory-thumbs # Legacy alias for S3_BUCKET_FILES (older .env files). S3_BUCKET=factory-files S3_REGION=us-east-1 S3_FORCE_PATH_STYLE=true # Signed-URL lifetime in seconds (15 min default). S3_SIGN_EXPIRY_SECONDS=900 # ClamAV (TASK-0102). Empty CLAMD_SOCKET = dev mode (scan skipped + WARN). # Docker profile: CLAMAV_HOST=clamav (or localhost from the host) + CLAMAV_PORT. CLAMD_SOCKET= CLAMAV_HOST= CLAMAV_PORT=3310 # Search (OpenSearch) SEARCH_URL=http://localhost:9200 SEARCH_USERNAME= SEARCH_PASSWORD= SEARCH_INDEX_PREFIX=factory # 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=1536 SEARCH_KNN_M=16 SEARCH_KNN_EF_CONSTRUCTION=100 # AI gateway (Phase 12 — provider-agnostic; ONE env block, zero app-code # change to swap providers). Provider matrix (AI_PROVIDER): # openai OpenAI API (chat: AI_MODEL; embeddings: AI_EMBEDDING_MODEL) # anthropic Anthropic API (embeddings fall through to the fallback provider) # google Google Gemini (chat: gemini-1.5-pro) # oss OpenAI-compatible self-hosted endpoint (AI_BASE_URL required) # custom customer endpoint (AI_BASE_URL + optional extra headers) # mock CI/dev deterministic mock — no network; the capability suite and # local dev run the full gateway surface offline (never production) AI_PROVIDER=openai AI_BASE_URL=https://api.openai.com/v1 AI_API_KEY= AI_MODEL=gpt-4o AI_EMBEDDING_MODEL=text-embedding-3-small # Embedding provider for the `embed:chunks` worker job. With the Phase-12 # gateway wired, a configured provider runs the real embed path; EMPTY still # skips (no AiEmbedding rows, chunk docs' vector stays null) for dev without # an API key. AI_EMBEDDING_PROVIDER= # --- Optional stream/plan tuning (all OPTIONAL — empty = the built-in # --- defaults; raise them for SLOW REASONING MODELS, e.g. OSS vLLM: a long # --- silent thinking phase before the plan + a plan JSON larger than 4096 # --- tokens) --- # AI page-generation total cap in ms (default 120000). A complex plan from a # reasoning model can take ~2-3 min (silent thinking first); raise to 600000. AI_GENERATE_TIMEOUT_MS= # Max gap between streamed BYTES in ms (default 30000). A reasoning model's # silent thinking phase can exceed 30 s of no bytes (observed 39 s) → mid- # stream 504. Raise to 90000 for such models. AI_STREAM_IDLE_MS= # Platform default for the per-call maxOutputTokens clamp (default 4096). # Reasoning models spend ~9k tokens THINKING before the answer; a complex # generation plan needs ~15k+ total → raise to 32000 so the plan JSON is not # truncated mid-stream. A workspace's settings.ai.budgets.maxOutputTokens # still wins over this platform default. AI_MAX_OUTPUT_TOKENS= # (Advanced) fetch-level response cap in ms (default 60000). AI_STREAM_TIMEOUT_MS= # Email (SMTP) SMTP_HOST=localhost SMTP_PORT=587 SMTP_USER= SMTP_PASSWORD= SMTP_FROM="AI Software Factory " # Realtime REALTIME_PORT=4001 REALTIME_HEARTBEAT_MS=30000 YJS_STORAGE_PREFIX=yjs # 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). INTERNAL_SERVICE_TOKEN= # Webhooks WEBHOOK_RETRY_MAX=8 WEBHOOK_TIMEOUT_MS=10000 # Rate limiting RATE_LIMIT_WINDOW_MS=60000 RATE_LIMIT_MAX=300 # Observability (optional; enable in Phase 15) OTEL_ENABLED=false OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 # Encryption (application-level secret encryption, at-rest secrets) MASTER_ENCRYPTION_KEY=