# SkillSpell environment configuration # Copy this file to packages/backend/.env and fill in the values: # cp .env.example packages/backend/.env # Only .env.example is tracked in git; your real .env is git-ignored. NODE_ENV=development # ─── LLM Provider ───────────────────────────────────────────────────── # All LLM calls run through the AWS Strands Agent framework against the # provider selected here. Switching providers is purely an env change — # no code edits at any call site. # # LLM_PROVIDER: anthropic | azure | bedrock | openai | google (default: anthropic) LLM_PROVIDER=anthropic # Shared credential/model vars (used by anthropic & azure): # AI_API_KEY — provider API key # AI_MODEL — main model / deployment name # AI_MODEL_LIGHT — optional faster model for suggestions/diagrams/grading # (falls back to AI_MODEL) # AI_API_BASE_URL — endpoint; required for azure, optional for anthropic AI_API_KEY=your-api-key AI_MODEL=your-model-name # AI_MODEL_LIGHT=your-light-model-name # ── Provider-specific configuration ────────────────────────────────── # anthropic (direct): set AI_API_KEY + AI_MODEL. AI_API_BASE_URL optional. # AI_API_BASE_URL=https://api.anthropic.com # ANTHROPIC_MODEL / ANTHROPIC_MODEL_LIGHT — optional per-provider overrides # # azure (Azure AI Foundry, Anthropic models): set AI_API_KEY + AI_MODEL + # AI_API_BASE_URL to your Foundry endpoint (auto-detected as azure if the URL # contains 'azure'). Uses Azure 'api-key' auth automatically. # AI_API_BASE_URL=https://your-resource.services.ai.azure.com/anthropic # # bedrock (AWS Bedrock, Anthropic models): uses the AWS credential chain below. # BEDROCK_MODEL=anthropic.claude-... (or set AI_MODEL) # # openai: requires OPENAI_API_KEY. OPENAI_MODEL / OPENAI_MODEL_LIGHT optional. # OPENAI_API_KEY= # OPENAI_MODEL=gpt-4o # # google (Gemini): requires GOOGLE_API_KEY. GOOGLE_MODEL / GOOGLE_MODEL_LIGHT optional. # GOOGLE_API_KEY= # GOOGLE_MODEL=gemini-2.0-flash # ─── AWS credentials (only for the Bedrock LLM provider) ────────────── # Needed when LLM_PROVIDER=bedrock. Resolved via the SDK default chain: # env vars (AWS_ACCESS_KEY_ID/AWS_SECRET_ACCESS_KEY) → IAM role → credential files. # AWS_REGION=us-east-1 # AWS_ACCESS_KEY_ID=your-access-key # AWS_SECRET_ACCESS_KEY=your-secret-key # ─── AI Timeouts & Retries ──────────────────────────────────────────── # Timeout for generation/refinement calls. Default: 600000ms (10min) # AI_GENERATION_TIMEOUT_MS=600000 # Timeout for lightweight calls (suggestions, diagrams, grading). Default: 30000ms (30s) # AI_LIGHT_TIMEOUT_MS=30000 # Max rate-limit retries with exponential backoff (0 = disabled). Default: 2 # AI_MAX_RETRIES=2 # ─── Storage (PostgreSQL — the single adapter, wired in app.module.ts) ─ # Local Postgres is started by `npm run db:postgres:up` (docker compose). # These defaults match docker/docker-compose.yml. Override with a strong # password in staging/production, and never expose port 5432 publicly. POSTGRES_HOST=localhost POSTGRES_PORT=5432 POSTGRES_DB=skillspell POSTGRES_USER=skillspell POSTGRES_PASSWORD=skillspell_dev POSTGRES_SSL=false # POSTGRES_CA_CERT= POSTGRES_POOL_SIZE=10 POSTGRES_SYNCHRONIZE=false # ─── Redis (required) ───────────────────────────────────────────────── # Used for the grading cache, rate-limiter/throttler storage, and draft state. # The Redis service in docker/docker-compose.yml starts automatically with # `npm run db:postgres:up`. These defaults match that container. REDIS_HOST=localhost REDIS_PORT=6379 # Leave empty for no-auth local dev; set a password for production. REDIS_PASSWORD= # ─── Authentication (required) ─────────────────────────────────────── # JWT secret — must be at least 32 characters. The app refuses to boot while # this is left at the placeholder below; generate a real one with: # node -e "console.log(require('crypto').randomBytes(64).toString('hex'))" JWT_SECRET=change-me-generate-a-64-char-hex-secret-before-running # JWT_ACCESS_TOKEN_EXPIRY=15m # JWT_REFRESH_TOKEN_EXPIRY=7d # ─── Password Policy ───────────────────────────────────────────────── # PASSWORD_MIN_LENGTH=8 # PASSWORD_BCRYPT_ROUNDS=12 # ─── Account Lockout ───────────────────────────────────────────────── # ACCOUNT_LOCKOUT_THRESHOLD=5 # ACCOUNT_LOCKOUT_DURATION_MINUTES=15 # ─── Encryption (required for SMTP password storage) ───────────────── # 64-char (256-bit) hex key for AES-256-GCM encryption of SMTP passwords # at rest. Read from ENCRYPTION_KEY. Generate with: # node -e "console.log(require('crypto').randomBytes(32).toString('hex'))" # (or: openssl rand -hex 32). SMTP password encryption is disabled if unset. # ENCRYPTION_KEY=your-64-char-hex-key # ─── Application ────────────────────────────────────────────────────── # Public URL for verification links, SAML/OIDC metadata, and email links. # APP_PUBLIC_URL=https://skillspell.example.com # Comma-separated allowed CORS origins (leave empty for same-origin only). # CORS_ALLOWED_ORIGINS=http://localhost:5173 # ─── Keycloak IdP (local dev SSO only) ─────────────────────────────── # Credentials for the local Keycloak Docker container (npm run saml:up). # DEV ONLY — do not use these values in production. # KEYCLOAK_PORT=8080 # KEYCLOAK_ADMIN=admin # KEYCLOAK_ADMIN_PASSWORD=admin # ─── Skills & Prompts Directories ──────────────────────────────────── # Root directory containing skills-workspace/skills/ (discovered by the Strands # AgentSkills plugin). Defaults to the monorepo root in development; set # explicitly for production deployments where cwd differs. # SKILLS_PROJECT_DIR=/app # Absolute path to the shared prompts folder. Falls back to ../shared/prompts. # PROMPTS_DIR=/app/packages/shared/prompts # ─── Session Persistence ───────────────────────────────────────────── # Max messages per skill session (rolling window). Default: 20 # SESSION_MAX_MESSAGES=20 # Max estimated tokens for compressed history in refinement prompts. Default: 2000 # SESSION_MAX_HISTORY_TOKENS=2000 # ─── OpenTelemetry (optional) ───────────────────────────────────────── # Master toggle — set to 'true' to enable OTEL (default: false). # OTEL_ENABLED=true # OTLP endpoint for traces/metrics/logs. Aspire (dev): http://localhost:18890 # OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:18890 # OTEL_SERVICE_NAME=skillspell-backend # ─── Langfuse v3 LLM Tracing (optional) ────────────────────────────── # Prereqs: OTEL_ENABLED=true and OTEL_EXPORTER_OTLP_ENDPOINT set. # Start the stack: docker compose -f docker/docker-compose.yml --profile langfuse up -d # Open http://localhost:3001, create a project, copy its keys: # LANGFUSE_BASE_URL=http://localhost:3001 # LANGFUSE_PUBLIC_KEY=pk-lf-... # LANGFUSE_SECRET_KEY=sk-lf-... # NOTE: prompt/completion content is ALWAYS captured when Langfuse is enabled. # LANGFUSE_OTEL_PAYLOADS=true # ─── Debug ──────────────────────────────────────────────────────────── # Dump full system + user prompts to debug-prompts/ before sending to the LLM. # Only works when NODE_ENV !== 'production'. # DEBUG_DUMP_PROMPTS=true # ─── Server ────────────────────────────────────────────────────────── # Port is auto-assigned by portless during development. # Set PORT manually only when running without portless. # PORT=3000