# ─── OmniSight Engine Configuration ─── # Copy this file to .env and fill in your API keys. # All backend env vars use the OMNISIGHT_ prefix. # Active LLM provider: anthropic | google | openai | xai | groq | deepseek | together | ollama OMNISIGHT_LLM_PROVIDER=anthropic # Override model name (leave empty for provider default) # OMNISIGHT_LLM_MODEL= # ─── API Keys (set the one matching your chosen provider) ─── OMNISIGHT_ANTHROPIC_API_KEY= OMNISIGHT_GOOGLE_API_KEY= OMNISIGHT_OPENAI_API_KEY= OMNISIGHT_XAI_API_KEY= OMNISIGHT_GROQ_API_KEY= OMNISIGHT_DEEPSEEK_API_KEY= OMNISIGHT_TOGETHER_API_KEY= # Ollama (local, no key needed) # OMNISIGHT_OLLAMA_BASE_URL=http://localhost:11434 # ─── RTK Output Compression ─── # Enable/disable output compression for token savings # OMNISIGHT_RTK_ENABLED=true # Only compress outputs larger than this (bytes) # OMNISIGHT_RTK_COMPRESSION_THRESHOLD=1000 # ─── B15 #350 — Skill Loading Mode (Progressive Disclosure) ─── # Controls how role/task skills are attached to agent system prompts. # eager (default) — full skill body inlined at agent start # (~50K chars / ~12.5K tokens, legacy behaviour) # lazy — only a metadata catalog is injected up-front; # the agent emits `[LOAD_SKILL: ]` markers # and nodes.py injects the full body on demand # (~70% token reduction on short tasks) # Stays `eager` by default for back-compat; flip to `lazy` once you've # A/B'd your workload. The resolved value is logged once at INFO on # first prompt build. # OMNISIGHT_SKILL_LOADING=eager # ─── Git Authentication ─── # SSH key path for private repos (default: ~/.ssh/id_ed25519) # OMNISIGHT_GIT_SSH_KEY_PATH=~/.ssh/id_ed25519 # GitHub Personal Access Token (for HTTPS auth + PR creation) # OMNISIGHT_GITHUB_TOKEN=ghp_... # GitLab Personal Access Token (for HTTPS auth + MR creation) # OMNISIGHT_GITLAB_TOKEN=glpat-... # Self-hosted GitLab URL (leave empty for gitlab.com) # OMNISIGHT_GITLAB_URL=https://gitlab.company.com # ─── Gerrit Code Review ─── # Enable Gerrit integration (set to true to use Gerrit as review gateway) # OMNISIGHT_GERRIT_ENABLED=true # Gerrit web UI URL # OMNISIGHT_GERRIT_URL=https://gerrit.example.com # Gerrit SSH host and port (for git push + CLI commands) # OMNISIGHT_GERRIT_SSH_HOST=gerrit.example.com # OMNISIGHT_GERRIT_SSH_PORT=29418 # Gerrit project path # OMNISIGHT_GERRIT_PROJECT=project/omnisight-core # Post-merge replication targets (comma-separated remote names) # OMNISIGHT_GERRIT_REPLICATION_TARGETS=github,gitlab # Webhook authentication secret — shared between Gerrit's webhooks # plugin (in refs/meta/config) and backend's _verify_jira_signature # check. The endpoint /api/v1/orchestrator/merge-conflict accepts this # value via the `X-Jira-Webhook-Secret` header (constant-time compare, # NOT HMAC). Pair with an operator API key in the `Authorization: # Bearer ...` header to satisfy the dual-gate auth (require_operator + # signature). See docs/sop/lessons/L-OP-708-webhook-endpoints-with-require-operator-signature-are-dual-g.md for the full design. # Generate with: python -c 'import secrets; print("ji_"+secrets.token_urlsafe(30))' # OMNISIGHT_JIRA_WEBHOOK_SECRET= # ─── OAuth Login (Google / GitHub / Microsoft / Apple / Discord / GitLab / Bitbucket) ─── # Google Cloud Console → OAuth client ID (Web application). # Authorized redirect URI: # https:///api/v1/auth/oauth/google/callback # GitHub Developer Settings → OAuth Apps → New OAuth App. # Authorization callback URL: # https:///api/v1/auth/oauth/github/callback # Microsoft Entra admin center → App registrations → Web redirect URI: # https:///api/v1/auth/oauth/microsoft/callback # Apple Developer → Certificates, Identifiers & Profiles → Services ID. # Return URL: # https:///api/v1/auth/oauth/apple/callback # Apple client secret is the signed Services ID JWT for your Team ID / Key ID. # Discord Developer Portal → Applications → OAuth2 redirect URI: # https:///api/v1/auth/oauth/discord/callback # GitLab Applications → Redirect URI: # https:///api/v1/auth/oauth/gitlab/callback # Bitbucket OAuth consumer → Callback URL: # https:///api/v1/auth/oauth/bitbucket/callback # Slack App → OAuth & Permissions → Redirect URLs: # https:///api/v1/auth/oauth/slack/callback # Notion integration OAuth redirect URI: # https:///api/v1/auth/oauth/notion/callback # Salesforce Connected App callback URL: # https:///api/v1/auth/oauth/salesforce/callback # Salesforce login base URL: empty = production login.salesforce.com; # set https://test.salesforce.com for sandbox or a community/My Domain origin. # The flow signing key signs the short-lived state/PKCE cookie. Leave # it empty only if OMNISIGHT_DECISION_BEARER is already configured. # OMNISIGHT_OAUTH_GOOGLE_CLIENT_ID= # OMNISIGHT_OAUTH_GOOGLE_CLIENT_SECRET= # OMNISIGHT_OAUTH_GITHUB_CLIENT_ID= # OMNISIGHT_OAUTH_GITHUB_CLIENT_SECRET= # OMNISIGHT_OAUTH_MICROSOFT_CLIENT_ID= # OMNISIGHT_OAUTH_MICROSOFT_CLIENT_SECRET= # OMNISIGHT_OAUTH_APPLE_CLIENT_ID= # OMNISIGHT_OAUTH_APPLE_CLIENT_SECRET= # OMNISIGHT_OAUTH_DISCORD_CLIENT_ID= # OMNISIGHT_OAUTH_DISCORD_CLIENT_SECRET= # OMNISIGHT_OAUTH_GITLAB_CLIENT_ID= # OMNISIGHT_OAUTH_GITLAB_CLIENT_SECRET= # OMNISIGHT_OAUTH_BITBUCKET_CLIENT_ID= # OMNISIGHT_OAUTH_BITBUCKET_CLIENT_SECRET= # OMNISIGHT_OAUTH_SLACK_CLIENT_ID= # OMNISIGHT_OAUTH_SLACK_CLIENT_SECRET= # OMNISIGHT_OAUTH_NOTION_CLIENT_ID= # OMNISIGHT_OAUTH_NOTION_CLIENT_SECRET= # OMNISIGHT_OAUTH_SALESFORCE_CLIENT_ID= # OMNISIGHT_OAUTH_SALESFORCE_CLIENT_SECRET= # OMNISIGHT_OAUTH_SALESFORCE_LOGIN_BASE_URL= # OMNISIGHT_OAUTH_HUBSPOT_CLIENT_ID= # OMNISIGHT_OAUTH_HUBSPOT_CLIENT_SECRET= # OMNISIGHT_OAUTH_REDIRECT_BASE_URL=https:// # OMNISIGHT_OAUTH_FLOW_SIGNING_KEY= # ─── Frontend (Next.js) API Keys ─── # These are used when calling LLMs directly from the frontend via /api/chat ANTHROPIC_API_KEY= # GOOGLE_GENERATIVE_AI_API_KEY= # OPENAI_API_KEY= # XAI_API_KEY= # GROQ_API_KEY= # DEEPSEEK_API_KEY= # TOGETHER_AI_API_KEY= # Backend URL (for frontend API client and Next.js rewrite proxy) # NEXT_PUBLIC_API_URL=http://localhost:8000 # BACKEND_URL=http://localhost:8000 # Used by next.config.mjs rewrite (Docker: http://backend:8000) # ─── Production / Docker Settings ─── # L10 #337 / OP-1487 — image-first deployment via digest-pinned images. # `docker-compose.prod.yml` pulls # `${OMNISIGHT_REGISTRY}/omnisight-{backend,frontend}@${OMNISIGHT_*_DIGEST}` # on `up`. Until the G5 registry cutover, the default remains # `ghcr.io/${OMNISIGHT_GHCR_NAMESPACE:-your-org}` for backwards compatibility. # Set OMNISIGHT_REGISTRY to the full registry/namespace prefix when testing # the GitLab Container Registry mirror, e.g. `sora.services:49154/omnisight`. # OMNISIGHT_REGISTRY= # Optional full registry/namespace prefix; default uses GHCR namespace # OMNISIGHT_GHCR_NAMESPACE=your-org # Lowercase GitHub org/user; default `your-org` (placeholder) # BP.W3.14 frontend freshness metadata. scripts/deploy-prod.sh maintains # these so /metrics exposes omnisight_frontend_build_lag_commits and the # Bootstrap wizard can show prod frontend build vs master HEAD. # OMNISIGHT_FRONTEND_BUILD_COMMIT= # OMNISIGHT_MASTER_HEAD_COMMIT= # OMNISIGHT_FRONTEND_BUILD_LAG_COMMITS=0 # OMNISIGHT_DEBUG=false # Set to false for production (default: false) # OMNISIGHT_FRONTEND_ORIGIN=https://omnisight.company.com # Public frontend URL for CORS # OMNISIGHT_EXTRA_CORS_ORIGINS= # Additional CORS origins, comma-separated # OMNISIGHT_DATABASE_PATH= # SQLite path (empty = default data/omnisight.db) # OMNISIGHT_DOCKER_IMAGE=omnisight-agent:latest # Agent sandbox image name # Phase 64-A S1: Tier-1 sandbox runtime. "runsc" (gVisor) blocks most # container-escape CVEs via a user-space kernel. ENV=production hard-fails # if runsc is missing; dev / WSL2 / mac may set runc for compatibility. # OMNISIGHT_DOCKER_RUNTIME=runsc # Phase 64-A S2: Tier-1 egress whitelist. DOUBLE-GATED. Both must be # set to open the air-gap; otherwise sandbox stays --network none. # After flipping these on, run scripts/setup_t1_egress_iptables.sh as # root on each host to install the iptables ACCEPT/DROP rules. # OMNISIGHT_T1_ALLOW_EGRESS=false # OMNISIGHT_T1_EGRESS_ALLOW_HOSTS=github.com,gerrit.internal:29418 # Phase 64-A S3: agent docker image digest allow-list. Empty = no # check (today's behaviour). Set in prod to refuse a tampered image: # `docker image inspect --format '{{.Id}}' omnisight-agent:` # OMNISIGHT_DOCKER_IMAGE_ALLOWED_DIGESTS=sha256:abc...,sha256:def... # Phase 64-A S4: sandbox wall-clock lifetime cap. After this many # seconds the watchdog SIGKILLs the container even if its commands # look in-progress. 0 = disabled (NOT recommended in prod). Default # 2700 = 45 min, matches the design spec. # OMNISIGHT_SANDBOX_LIFETIME_S=2700 # Phase 64-D D3: per-exec output cap. Truncates docker exec output # above this byte count + appends a marker. Defends Tier-0 LLM # context. 0 = disabled (default 10 KB). # OMNISIGHT_SANDBOX_MAX_OUTPUT_BYTES=10000 # Phase 67-A: prompt cache marker injection. Default ON (recommended # in prod for both cost and TTFT). Set to false only for debug. # OMNISIGHT_PROMPT_CACHE_ENABLED=true # Phase 67-D: RAG pre-fetch on step error. v1 confidence floor 0.5; # raise to 0.7 after Phase 63-E memory decay lands. # OMNISIGHT_RAG_MIN_CONFIDENCE=0.5 # OMNISIGHT_RAG_TOP_K=3 # Phase 67-B: write_file existing-file-overwrite cap. # OMNISIGHT_PATCH_MAX_INLINE_LINES=50 # Phase 67-C: speculative pre-warm depth (how many in-degree-0 tasks # to pre-launch a container for). # OMNISIGHT_PREWARM_DEPTH=2 # Phase 65 S2: fine-tune hold-out regression threshold. Candidate # weighted score must be >= baseline - this many percentage points # OR the promotion is rejected. Default 5pp. # OMNISIGHT_FINETUNE_REGRESSION_PP=5 # Phase 65 S3: fine-tune backend. noop|openai|unsloth. # noop — synthetic handle; default; safe for dev / opt-out prod. # openai — uses openai SDK; requires OPENAI_API_KEY. # unsloth — local unsloth-cli; production callers MUST wrap the # subprocess in a Tier-2 sandbox (Phase 64-B) for hub egress. # OMNISIGHT_FINETUNE_BACKEND=noop # Phase 63-E: episodic memory quality decay (L3 opt-in). # TTL — rows untouched for this many seconds get their decayed_score # multiplied by FACTOR each pass. Never deletes — only down-weights. # OMNISIGHT_MEMORY_DECAY_TTL_S=7776000 # 90 days # OMNISIGHT_MEMORY_DECAY_FACTOR=0.9 # 10% decay per pass # OMNISIGHT_MEMORY_DECAY_INTERVAL_S=86400 # nightly # L1-06: hourly LLM burn-rate kill-switch. A daily cap catches drift; # the hourly cap catches spikes that would blow a month's budget in # a few hours but stay under daily. 0 = disabled. Rule of thumb: # token_budget_daily / 12. # OMNISIGHT_TOKEN_BUDGET_HOURLY=0.0 # ─── Internet-exposure auth (Phase 54 + S1-S3) ─── # IF YOU ARE EXPOSING THIS URL TO THE INTERNET, READ THIS BLOCK. # # Auth mode. open=anyone-is-admin (dev only). session=cookie required # for mutators. strict=cookie + CSRF for everything except /healthz + # /auth/login. Production deploys MUST set this to `strict`. # OMNISIGHT_AUTH_MODE=strict # Bootstrap admin. Created on first boot if the users table is empty. # DO NOT keep the default password 'omnisight-admin' on an exposed # URL — config validation will refuse to start the app in prod if you # do. Use a passphrase ≥ 12 chars. # OMNISIGHT_ADMIN_EMAIL=you@example.com # OMNISIGHT_ADMIN_PASSWORD=correct-horse-battery-staple-please-replace # ─── FX.7.8 — Grafana observability admin (required when --profile observability is on) ─── # Both vars are *required* by docker-compose.prod.yml when the # `observability` profile is enabled — compose interpolation # `${VAR:?msg}` refuses to render the grafana service if either is unset # or empty, and the grafana-env-validator init container additionally # rejects: # - GRAFANA_ADMIN_PASSWORD shorter than 12 characters # - GRAFANA_ADMIN_PASSWORD equal to GRAFANA_ADMIN_USER # - GRAFANA_ADMIN_PASSWORD case-insensitively in {admin, password, # changeme, grafana, omnisight, default, root, letmein, 123456, # 12345678, 123456789, 1234567890, qwerty, qwerty123, admin123, # password123, grafana123} # This is to stop the well-known `admin / admin` default from shipping # to a host whose port 3001 is internet-reachable. # GRAFANA_ADMIN_USER=omnisight-grafana-admin # GRAFANA_ADMIN_PASSWORD=correct-horse-battery-staple-please-replace # ─── FX.7.9 — production deploy ref verifier escape hatch ─── # scripts/deploy-prod.sh delegates ref policy to # scripts/check_deploy_ref.sh. The verifier enforces TWO layers: # # (1) ref allowlist — deploy/prod-deploy-allowlist.txt lists the # only branches and tag patterns permitted to # deploy. Adding entries is a PR review. # (2) GPG signature — deploy/prod-deploy-signers.txt lists the # fingerprints whose keys may sign a # deploy-eligible tag (annotated) or branch tip. # The matching public keys must also be imported # into the prod host's gpg keyring. # # Failure mode is "fail-closed loud" — the deploy aborts with the # specific reason (missing rule / unsigned tip / untrusted signer). # Until the first real release-signing key exists, prod deploys must # either use: # # ./scripts/deploy-prod.sh --insecure-skip-verify # # or set this env var to 1. Both paths print a multi-line stderr # warning at every invocation; both should be flagged in the post- # deploy review and removed once a real signer is provisioned. # OMNISIGHT_DEPLOY_INSECURE_SKIP_VERIFY= # Cookie Secure flag. Cloudflare Tunnel terminates HTTPS, so behind it # the answer is always 'true' — set this so session cookies refuse to # travel over a future plain-HTTP path (custom dev proxies, etc.). # OMNISIGHT_COOKIE_SECURE=true # /auth/login brute-force defence. 5 fails in 15 min → 429 with # Retry-After. Tighten under attack; loosen if your team shares a # NAT and trips it innocently. # OMNISIGHT_LOGIN_MAX_ATTEMPTS=5 # OMNISIGHT_LOGIN_WINDOW_S=900 # Phase 67-E: Tier-1 sandbox RAG pre-fetch (stricter guards). # Similarity (proxy) floor — borderline matches rejected, cosine-style. # OMNISIGHT_RAG_MIN_COSINE=0.85 # Total token budget for the injected block. # OMNISIGHT_RAG_MAX_BLOCK_TOKENS=1000 # ─── Phase 47 Decision Engine tuning ─── # [DEPRECATED by K6] Legacy single bearer token. If set, K6 auto-migrates it # to an api_keys row on startup. Use Admin UI > API Keys to create per-service # keys instead, then remove this env var. # OMNISIGHT_DECISION_BEARER= # Max pending decisions before proposers receive DecisionQueueFull (default 256). # OMNISIGHT_DECISION_PENDING_MAX=256 # Timeout sweep cadence in seconds (default 10). # OMNISIGHT_DECISION_SWEEP_INTERVAL_S=10 # Sliding-window rate limit on decision mutators, per client IP. # OMNISIGHT_DECISION_RL_WINDOW_S=10 # OMNISIGHT_DECISION_RL_MAX=30 # ─── Runtime limits ─── # Max bytes a single `bash` tool call may return (default 128 KB). # OMNISIGHT_BASH_TIMEOUT=120 # Hard cap on SDK clone size before cleanup (default 8192 MB). # OMNISIGHT_SDK_CLONE_MAX_MB=8192 # Git credentials YAML path (default: configs/git_credentials.yaml). # OMNISIGHT_GIT_CREDENTIALS_FILE= # ─── Y6 #282 — Workspace hierarchy + per-tenant quota ─── # Parent dir for the 5-layer agent workspace tree # ``{root}/{tenant_id}/{product_line}/{project_id}/{agent_id}/{repo_url_hash}/``. # Defaults to ./data/workspaces so a single bind-mount in compose covers # every stateful directory (DB + audit chain + workspaces). Resolved # relative to the process CWD when not absolute. # OMNISIGHT_WORKSPACE_ROOT=./data/workspaces # Default per-tenant workspace quota in MB. 0 = unlimited (current # behaviour, preserved as default until row 5 ships enforcement). # Per-tenant overrides in `tenant_quota_overrides` take precedence. # OMNISIGHT_WORKSPACE_QUOTA_MB_DEFAULT=0 # ─── OP-813 (A5) — MCP-JIRA integration ─── # Read-only JIRA MCP server giving the runner a tool surface for asking the # ticket graph: ``getTicket(key)``, ``searchTickets(jql)``, ``getComments(key)``. # Mutations (transitions, comments) NEVER go through MCP — they stay in # ``backend.agents.jira_dispatch`` so the audit / governance trail is single- # sourced (per OP-813 AC#4). Set the token to enable; leave blank or unset # to disable. # OMNISIGHT_MCP_JIRA_TOKEN= # Override the default server URL when running ``mcp-atlassian`` self-hosted # or behind an alternate gateway. Defaults to the community reference. # OMNISIGHT_MCP_JIRA_URL= # ─── OP-853 (C4) — MCP-Graphiti temporal ticket queries ─── # Read-only Graphiti MCP server for temporal runner context: # ``getTicketTimeline(key)``, ``findSimilarPriorTicketsByTimeline(features)``, # ``getBotSuccessRateByPattern(bot, feature)``. Ingestion is event-driven from # JIRA changelog and Gerrit stream-events; runner calls are query-only. # OMNISIGHT_MCP_GRAPHITI_TOKEN= # Override the default Graphiti MCP URL. Defaults to https://mcp-graphiti.local. # OMNISIGHT_MCP_GRAPHITI_URL=