mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
* chore(release): open v3.8.45 development cycle
* chore(release): parallel-cycle flow — sync-next-cycle script + Hard Rule #21 semantics (#6203)
Integrated into release/v3.8.45
* perf(test): tsx/esm loader + tsx 4.23 + órfãos recuperados + CI via npm scripts (plano testes+CI, Pacote 1) (#6214)
* perf(test): tsx/esm loader, tsx 4.23 bump, orphan tests recovered, CI runs npm scripts
Pacote 1 (quick wins) do plano mestre testes+CI:
- Swap --import tsx -> --import tsx/esm on the 19 test scripts: the repo is pure
ESM and the CJS hook costs ~1.3s PER test process (2,462 processes/run).
Measured: bootstrap 2.3s -> 1.1s; real-suite A/B (tests/unit/db, 12 files)
22.2s -> 14.1s wall (-36%), 82/82 pass. Non-test scripts keep the full hook.
- Bump tsx ^4.22.3 -> ^4.23.0 (fix for privatenumber/tsx#809 startup regression;
helps module resolution on big graphs — hook cost unchanged, honest note).
- Recover 22 ORPHAN test files (tests/unit/feature-triage/*.test.mjs, 53 cases,
53/53 pass) that matched no glob and ran in NO CI job; drop the dead
'executors' dir from the braces glob.
- Single source of truth for the unit-suite invocation: new test:unit:ci:shard
(shard via TEST_SHARD env) called by ci.yml test-unit/node24/node26/coverage
and quality.yml fast-unit — closing two silent drifts: CI was NOT importing
setupPolyfill.ts, and the fast path glob OMITTED tests/unit/memory + usage.
quality.yml TIA step + ci.yml test-integration get the tsx/esm swap only.
Validation: full unit suite 21,153 tests / 21,135 pass / 13 skip (5 fails =
known load-flake family, 10/10 green rerun isolated); vitest 237/237; smoke
db+feature-triage 135/135. Record run 2 = ci.yml workflow_dispatch on the
stacked pacote-2 branch (clean runners).
* fix(test): dashboard UI tests keep full tsx hook; recover 15 more .mjs orphans; extend discovery gate to .mjs
Follow-up do dispatch de validacao (run 28720431562), que pegou 2 problemas reais:
1. tests/unit/dashboard/** (11 arquivos, 102 casos) importam componentes React cujo
grafo puxa @lobehub/icons — o build es/ dele faz require() interno de arquivos com
sintaxe ESM, que so funciona com o patch CJS do tsx (sem ele: SyntaxError
'Unexpected token export' no CI; local vira crawl de ~60s/arquivo). Esses 11
arquivos rodam agora numa 2a invocacao com --import tsx COMPLETO (mesmo shard),
e o resto da suite mantem tsx/esm (-50% bootstrap). Validado: 102/102.
2. check:test-discovery falhou porque ancorava textualmente os globs nos workflows —
COLLECTORS atualizado p/ o modelo fonte-unica (ancora = nome do script
test:unit:ci:shard nos workflows) + varredura ESTENDIDA a .test.mjs, que era o
ponto cego que deixou os orfaos apodrecerem. A extensao revelou +15 orfaos .mjs
(top-level + db/) alem dos 22 de feature-triage — TODOS religados via glob
tests/unit/**/*.test.mjs (171/171 pass). Um deles (encryption-error-handling)
codificava o contrato PRE-hardening (decrypt falho retornava ciphertext cru —
vazamento); alinhado ao contrato shipped (null + log) com comentario.
Gate: [test-discovery] OK — 2892 arquivos, 22 collectors, 60 orfaos congelados
(divida rastreada, shrink-only).
* ci: dedup heavy pipeline — compat to nightly, coverage folded into unit shards, i18n single job, draft-skip (#6215)
Pacote 2+3-ci do plano mestre testes+CI (aprovado 2026-07-04). O CI pesado rodava a
suite unit 4x por sync da release-PR (95 jobs, 208 min-maquina) e o ciclo v3.8.44
disparou 123 desses runs (88 cancelados, 0 uteis) porque a release-PR viva fica
aberta o ciclo inteiro.
- D2: matrizes Node 24/26 (build + 8 jobs de teste, ~28% do custo por run) saem do
per-sync e viram .github/workflows/nightly-compat.yml (diario, fail-fast off,
resolve a release ativa como o nightly-release-green, abre issue de tracking em
falha). ci.yml/ci-summary limpos das referencias.
- D3: a matrix Coverage Shard x8 (~18% do custo) e eliminada — o job test-unit roda
os MESMOS shards sob c8/NODE_V8_COVERAGE e sobe os artifacts coverage-shard-N; o
job de merge (test-coverage) so repontou needs (padrao do CI do nodejs/node).
timeout test-unit 15->25min pelo overhead de instrumentacao.
- D4: a matrix i18n de ~40 jobs de <1min (saturava sozinha os 20 slots de
concorrencia da conta Free) vira 1 job que itera os idiomas com ::group:: por
idioma e artifact unico com resultados nomeados por idioma (antes 40 result.txt
colidiam no merge-multiple do ci-summary).
- P3: jobs pesados pulam pull_requests DRAFT (predicado em 10 jobs-raiz; o resto
pula pela cadeia de needs; ci-summary segue rodando como sinal unico) — a skill
/generate-release ja abre a release-PR viva como draft e flipa ready no 0a.0a
(commit eb04fc5 no repo .agents/skills).
- C5 (CodeQL schedule) NAO incluido: bloqueado na acao do dono Settings -> CodeQL
Default->Advanced (documentado no proprio codeql.yml).
Validacao: js-yaml parse ok; check:workflows zizmor 156 < baseline 159 (ratchet
verde); validacao de execucao = workflow_dispatch deste ci.yml neste branch ate
package-artifact + electron-package-smoke verdes (registrada no PR).
* feat(quality): no-new-warnings por PR — ESLint bulk suppressions + lint-guard fork-condicional (Pacote 4) (#6218)
* feat(quality): no-new-warnings per PR via native ESLint bulk suppressions
Pacote 4 do plano mestre testes+CI (aprovado 2026-07-04). O ratchet de
eslintWarnings so rodava no CI pesado (release-PR) -> o drift acumulava invisivel
e explodia na release (+41/+37/+88 por ciclo, rebaselinado as cegas — historico
no proprio quality-baseline.json). Modelo novo (SonarSource Clean-as-You-Code +
ESLint bulk suppressions nativo >=9.24):
- config/quality/eslint-suppressions.json congela a divida existente por
arquivo+regra: 476 arquivos / 4.273 violacoes.
- npm run lint + lint-staged (pre-commit) + novo job lint-guard no quality.yml
rodam suppressions-aware: violacao NOVA fica vermelha NO PR que a introduz
(bulk suppressions ainda eleva estouros de baseline por arquivo a error).
- 3 regras warn promovidas a error em src/** (react-hooks/exhaustive-deps,
@next/next/no-img-element, import/no-anonymous-default-export) — divida
existente congelada, ocorrencia nova = erro imediato.
- collect-metrics mede sob o baseline congelado -> a metrica eslintWarnings
vira 'divida liquida nova' (~0 em regime); baseline apertado 4279->0 no mesmo
PR (exigencia do require-tighten). Aperto do ESTOQUE congelado: npx eslint .
--prune-suppressions na reconciliacao da release.
- Principio Zero: lint-guard usa continue-on-error para PR de FORK (report-only;
a campanha /green-prs aplica o fix via co-autoria) — bloqueante so para
branches internas, a origem real do drift.
Validacao: negativo (any novo em tests/) exit 1; negativo (img em src/, regra
promovida) exit 1; positivo escopado exit 0; baseline gerado por --suppress-all
no tip (tree inteiro passa por construcao); YAML js-yaml ok.
* fix(quality): clear the 6 residual warnings so lint-guard runs clean at --max-warnings 0
The committed baseline still let 6 warnings through the lint-guard gate:
5 now-unused inline eslint-disable directives (the file-level suppressions
made them redundant — removed via eslint --fix, suppressions regenerated to
absorb the re-exposed occurrences) and 1 anonymous default export in
tests/load/k6-soak.js (outside the src/** severity-override scope — named
the k6 scenario function instead).
Verified on the clean tree: lint-guard exit=0; any-canary (new 'const x: any'
in open-sse) exit=1 — the gate bites on NEW violations while the 4,273
frozen ones stay suppressed (476 files).
* fix(ci): lint-guard continue-on-error must be boolean on non-PR events
github.event.pull_request is undefined on workflow_dispatch — the bare property
expression made the job fail at plan time (run 28722888456: 4 jobs green, run red,
lint-guard never materialized). Guard with event_name check so the expression is
always boolean: PR de fork = report-only (Principio Zero), resto = blocking.
* docs(changelog): v3.8.45 bullets for the tests+quality+CI pipeline overhaul (#6214, #6215, #6218)
i18n CHANGELOG mirrors intentionally left to the release reconciliation
(release:sync-changelog-i18n), per cycle practice.
* fix(api): stabilize relay SSRF-guard binding for minified builds (#6149) (#6224)
* fix(mcp): forward extra context through static tool loops (#6178) (#6228)
* fix(services): 9Router embed route + pre-spawn port probe (#6205) (#6227)
* fix(backend): system-first memory injection for strict providers (#6135) (#6225)
* fix(auth): clear error for stale-key decryption failures (#6148) (#6226)
* fix(backend): record reasoning source for zero-metered reasoning models (#6187) (#6229)
* fix(providers): refresh stale NVIDIA NIM model registry (#6108) (#6223)
* fix(backend): distinct max_input_tokens for GPT-family models (#6191) (#6230)
* fix(oauth): extract keychain-import-only guard to restore file-size freeze (base-red) (#6158)
`src/app/api/oauth/[provider]/[action]/route.ts` grew to 959 lines, past its
frozen cap of 924 (`check:file-size` → Fast Quality Gates red on release/v3.8.44).
The growth came from #6054 (graceful 400 for keychain-import-only providers / zed):
a doc block, two Sets (KEYCHAIN_IMPORT_ONLY_PROVIDERS, OAUTH_FLOW_ACTIONS) and a
keychainImportOnlyResponse() helper, plus two duplicated guard blocks in GET/POST.
That is a cohesive, self-contained leaf, so extract it to a new
`keychainImportOnly.ts` exposing `keychainImportOnlyGuard(provider, action)`
(returns the 400 NextResponse or null). The two route callsites collapse to a
2-line guard each. route.ts: 959 -> 918 (< 924, freeze restored). No behavior
change.
Tests (Rule #8/#18):
- Existing tests/unit/oauth-keychain-import-only-6041.test.ts (route-level GET/POST
zed 400) still pass unchanged — behavior preserved.
- New tests/unit/oauth-keychain-import-only-guard.test.ts pins the extracted guard
in isolation (zed+flow -> 400, normal provider -> null, zed+non-flow -> null).
* fix(dashboard): stop model-test error freezing the page (React #31 object toast) (#6161)
Clicking 'test' on a provider model (e.g. a ClinePass flash model) could freeze
the entire dashboard. Root cause: POST /api/models/test returned an OBJECT in
`error` on the Zod-validation and invalid-JSON paths (`validation.error.format()`
/ a details object). The client does `notify.error(data.error)`, and
NotificationToast renders the message directly as a React child — an object throws
React #31 ('Objects are not valid as a React child'), crashing the tree = frozen
page instead of a toast.
Fixed in three layers (defense in depth):
1. Server (root cause): /api/models/test now returns a STRING `error` on every
path — flattens Zod issues to text, returns 'Invalid JSON body' for bad JSON.
2. Client: onTestModel funnels the response through extractApiErrorMessage() so any
object-shaped error is coerced to a string before notify.error.
3. Toast: NotificationToast coerces title/message via toToastText() — a resilient
catch-all so no future caller can freeze the page with a non-string.
Tests (Rule #18, both node:test / blocking suite):
- tests/unit/models-test-error-shape.test.ts — asserts STRING error on Zod-fail,
missing-field, and invalid-JSON (fails on the pre-fix route: 3/3 red -> green).
- tests/unit/notification-toast-coercion.test.ts — toToastText coercion matrix.
* fix(dashboard): remove the always-on Auto-Routing (combo) banner from the home page (#6164)
The blue "Auto-Routing Active — OmniRoute is automatically routing requests
using combo-based strategies" banner was rendered unconditionally on the home
page (`/home`, the default dashboard landing) — it did NOT reflect whether
auto-routing was actually active, and reappeared on every fresh browser / private
window / cleared localStorage (dismissal is stored per-browser). It added noise
to the landing page without conveying live state.
Remove it: drop the <AutoRoutingBanner /> usage + import from home/page.tsx and
delete the now-unused component and its test.
* fix(cline): force upstream streaming for Cline/ClinePass (streaming-only API) (#6165)
* fix(cline): force upstream streaming for Cline/ClinePass (streaming-only API)
Cline's API (api.cline.bot) only implements streaming (streamText). A
non-streaming request returns HTTP 500 "generateText is not implemented" (Claude
models) or HTTP 502 "empty response" (others). Live-verified on the VPS:
stream:true → works (STREAM_OK), stream:false → fails. This is why testing a Cline
model in the dashboard (the test button sends stream:false) failed.
Fix (reuses the existing isClaudeCodeCompatible mechanism, no new handler):
- Flag `cline` and `clinepass` registry entries with `forceStream: true`.
- In chatCore, OR `providerRequiresStreaming` into `upstreamStream` (line 1591)
so the upstream request always streams for these providers, while the client's
original `stream` intent still drives the response format. The existing
non-streaming branch (parseNonStreamingResponseBody) already accumulates the
upstream SSE and converts it back to JSON for stream:false clients — the same
path Claude-Code-compatible providers already use.
Tests (Rule #18): tests/unit/cline-force-stream.test.ts pins the registry flags +
resolveStreamFlag forcing behavior. Live VPS before/after recorded on the PR.
* fix(sse): cline forceStream must stream upstream only, keep client JSON
The #2081 wiring fed providerRequiresStreaming into resolveStreamFlag,
forcing the client-facing stream flag to true for forceStream providers.
That skips the if(!stream) branch that drains a forced upstream SSE and
converts it back to JSON, so a stream:false caller (model-test button,
plain JSON API) got STREAM_EARLY_EOF instead of a JSON body.
Keep providerRequiresStreaming only on upstreamStream (force upstream to
stream); leave the client-facing stream as the client sent it, so
readNonStreamingResponseBody accumulates the SSE into JSON. The promised
handleForcedSSEToJson (#2081 comment) was never implemented — this uses
the existing non-streaming SSE-buffering path (same as isClaudeCodeCompatible).
Live-verified on VPS: cline stream:true worked, stream:false failed.
* fix(providers): correct Kiro model catalog to real upstream ids (#6170)
* fix(providers): correct Kiro model catalog to real upstream ids
Kiro's API (generateAssistantResponse) returns 400 "Invalid model. Please
select a different model" for any id it does not recognize. The registry
exposed fabricated ids (copied from OmniRoute's own Anthropic catalog) that
Kiro never serves, so every call to them 400'd. Live-verified on the VPS:
Removed (400 Invalid model):
- auto-kiro (no "auto" model id — was sent verbatim upstream)
- claude-fable-5 (Kiro offers no Fable)
- claude-opus-4.8/4.7/4.6 (Kiro offers no Opus)
Corrected:
- claude-sonnet-4.6 -> claude-sonnet-4.5 (Kiro's Sonnet is 4.5; 4.5 -> 200)
Kept:
- claude-sonnet-5 (real Kiro model, plan-gated per account)
- claude-haiku-4.5, deepseek-3.2, glm-5, minimax-m2.5/m2.1,
qwen3-coder-next (all proven 200 on the VPS)
Aligns the free-model catalog and drops the orphaned auto-kiro price key.
Regression guard: tests/unit/kiro-catalog-real-models.test.ts (3/3).
Kiro cluster #6112/#6113/#6099.
* test(providers): align stale Kiro-catalog tests to the corrected upstream ids
The fabricated Kiro ids removed in the parent commit (claude-fable-5,
claude-opus-4.8/4.7/4.6, claude-sonnet-4.6) were still asserted as present by
three pre-existing tests, which encoded the bug:
- catalog-updates-v3x: now asserts Kiro does NOT expose Fable 5 / Opus (kept the
legit cc exposure) and guards the real claude-sonnet-4.5 pricing.
- model-family-fallback-notation: the dot-notation example moves from kiro/ to
anthropic/ (which genuinely serves Opus/Fable in dot notation) — coverage kept.
- provider-models-route: the Kiro local-catalog assertion now expects the real
Sonnet 5 / Sonnet 4.5 set and negatively guards the fabricated ids.
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
* fix(sse): surface ChatGPT-web image silent-drop as an accurate error (#6208)
When ChatGPT Web generates an image as an image_asset_pointer but the pointer
fails to resolve to a downloadable URL (unknown asset scheme, download 403/
expired, oversize), resolveImagePointers returned [] — indistinguishable from
'no image produced' — so the image-generation handler reported the misleading
502 'completed without returning image markdown'. The image genuinely existed
upstream; OmniRoute dropped it silently.
Fix: the executor flags x_image_resolution_failed when a pointer existed but
none resolved (and logs the unresolved asset scheme for follow-up), and the
handler surfaces a truthful 'generated but not retrievable' 502 instead of
'no image markdown'. Adds executorFactory DI for unit testing.
TDD: tests/unit/chatgpt-web-image-silentdrop.test.ts (red -> green), plus the
existing chatgpt-web / image-generation-handler suites stay green.
Reported via community triage (mesh escalated backlog).
* fix(dashboard): providers page data-timeout guard + live-ws standalone wiring (#6211)
* fix(dashboard): providers page data-timeout guard + live-ws standalone wiring
Captura de trabalho em progresso: timeout de dados na página de providers,
ajuste em ProviderLimits e instrumentation-node, com testes novos
(providers-page-data-timeout, live-ws-standalone-wiring).
* chore(quality): rebaseline ProviderLimits/index.tsx file-size (+6, #6211 data-timeout guard)
Cohesive fix growth from PR #6211's data-timeout guard on the quota page's two
first-paint fetches (1121->1127). The fast-path PR->release skips check:file-size,
so the bump lands with the PR. Justification recorded in file-size-baseline.json.
* fix(translator): strip reasoning param for nvidia z-ai/glm-5.2 (#6181)
* fix(translator): strip reasoning param for nvidia z-ai/glm-5.2
NVIDIA NIM OpenAI-compatible wrapper rejects the reasoning body field
and returns HTTP 400 "Unsupported parameter(s): `reasoning`".
Add a StripRule scoped to provider=nvidia + model /z-ai\/glm-5\.2/i.
Mirrors PR #6102 drop pattern (minimax-m2.7 thinking).
* docs(translator): tighten nvidia glm-5.2 strip-rule comment
* fix(translator): anchor glm-5.2 strip rule with word boundary
* fix: add nvidia to PROVIDER_TOOL_LIMITS (1536) to prevent tool truncation (#6177)
NVIDIA NIM API (nvidia/* models) silently truncates the tool list to 128
(the default MAX_TOOLS_LIMIT) because nvidia is not in PROVIDER_TOOL_LIMITS.
Tools beyond index 127 are dropped, causing agents to lose access to
critical tools like task, read, or high-index MCP tools.
Verified that NVIDIA NIM API supports up to 1536 tools by direct testing.
End-to-end confirmed: 198 tools sent, model successfully called tools at
indices 193, 195, and 197 (previously dropped by truncation to 128).
Follows the same pattern as #5563 (grok-cli: 200), integrated in v3.8.43.
* feat(provider): add Claude 5 Sonnet to Claude Web provider (#6200) (#6209)
* feat(provider): add Claude 5 Sonnet to Claude Web provider (#6200)
* test(providers): guard claude-web claude-sonnet-5 registry entry (#6209)
Adds the missing regression test the PR-test-policy gate requires: asserts the
claude-web registry exposes claude-sonnet-5 (Claude 5 Sonnet web) alongside the
existing 4.6 Sonnet / 4.5 Haiku entries. Fails on the release base (no entry).
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
---------
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
* fix(cli): detect POSIX auto-set HOSTNAME via os.hostname() to fix bind address (#6194) (#6195)
POSIX shells (bash/zsh) always set HOSTNAME to the machine name. The
.env loader uses first-wins semantics, so HOSTNAME=0.0.0.0 in .env is
silently ignored. This causes the server to bind to the LAN hostname
instead of 0.0.0.0, breaking localhost access and all internal
self-requests (ModelSync, HealthCheck, cloud sync).
The fix compares process.env.HOSTNAME against os.hostname(): when they
match, it's the POSIX auto-set signature and HOSTNAME is ignored.
OMNIROUTE_SERVER_HOST takes precedence as the dedicated escape hatch.
Backward compatibility is preserved: users who set HOSTNAME to a value
that doesn't match the machine name (e.g. Windows CMD/PowerShell users
with HOSTNAME in .env) will still have their value honoured.
Closes #6194
* feat(sse): surface Kiro adaptive-thinking reasoning as reasoning_content (#6213)
Kiro/CodeWhisperer streams Claude's reasoning as native `reasoningContentEvent`
frames when adaptive thinking is enabled, but the Kiro executor had no handler
for them, so `reasoning_effort` requests returned no reasoning. Wire it end to
end:
- translator (openai-to-kiro): enable Kiro thinking when the request carries
`reasoning_effort`, Anthropic `output_config.effort`, or a `thinking` block
(`{type:"enabled",budget_tokens}` mapped to a level; `{type:"adaptive"}`
defaults to `high`, matching Anthropic's documented default). Prepends the
Kiro `<thinking_mode>`/`<max_thinking_length>` prompt directive and sets
top-level `additionalModelRequestFields` ({output_config.effort,
thinking:{type:"adaptive"}, max_tokens}). Gated on `supportsReasoning`; drops
non-default temperature/top_p (rejected by adaptive-only Claude models).
- executor transformRequest: forward `additionalModelRequestFields` to AWS
(previously dropped by the strict top-level allowlist).
- executor stream loop: parse `reasoningContentEvent` (and reasoningText
variants) into the OpenAI reasoning_content channel.
Verified against the live CodeWhisperer stream: reasoningContentEvent frames are
returned, and larger effort/budget measurably deepens reasoning up to the model
cap. Unit tests cover the effort sources, forwarding, temp/top_p stripping, and
native reasoning-frame parsing.
* fix(chatcore): exempt opencode client from the default 128-tool truncation (#6193)
* fix(chatcore): exempt opencode client from the default 128-tool truncation
The default MAX_TOOLS_LIMIT (128) cap made truncateToolList blind-slice
tools.slice(0, 128), dropping opencode's built-in task tool and part of
its MCP tools when the inbound list exceeded 128 — so models routed
through OmniRoute could not launch subagents or reach all their tools.
Detect the opencode client (any x-opencode-* header, or 'opencode' in
the user-agent) and bypass ONLY the speculative 128 default. A known
provider ceiling (proactive PROVIDER_TOOL_LIMITS or a detected limit)
always wins and still truncates, even for opencode, so upstreams with
real hard limits (e.g. grok-cli 200) keep their 400-avoidance guard.
Non-opencode clients are unchanged.
- requestFormat.ts: add isOpencodeClient(headers, userAgent) + expose it
on resolveChatCoreRequestFormat.
- toolLimitDetector.ts: add getKnownToolLimit(); getEffectiveToolLimit
becomes getKnownToolLimit(provider) ?? DEFAULT_LIMIT (byte-identical
for existing callers).
- upstreamBody.ts: truncateToolList takes bypassDefaultToolLimit and
encodes the precedence; fix cosmetic debug-log count.
- chatCore.ts: thread the flag into prepareUpstreamBody.
- tests: extend tool-limit-detector unit tests.
* refactor(tools): accept nullable provider in tool-limit resolvers
Address PR review: widen getKnownToolLimit / getEffectiveToolLimit to
(provider: string | null | undefined) to match the call sites in
truncateToolList, and add unit assertions covering null/undefined
providers (getKnownToolLimit -> null, getEffectiveToolLimit -> 128).
---------
Co-authored-by: DKotsyuba <16292493+DKotsyuba@users.noreply.github.com>
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
* fix(providers): refresh GitHub Copilot catalog (#6154)
* fix(providers): refresh github copilot catalog
Limit GitHub Copilot discovery to the curated supported model set and keep the provider cooldown panel client-safe by moving countdown formatting out of localDb.
* chore(quality): rebaseline providerPageHelpers.ts file-size (+13, #6154 copilot catalog)
The GitHub Copilot catalog refresh grows the provider-page model-section helper
(1021->1034). Fast-path PR->release skips check:file-size, so the bump lands with
the PR. Justification recorded in file-size-baseline.json.
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
---------
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
* chore(quality): rebaseline kiro-translator file-size debt from #6213
The #6213 kiro adaptive-thinking feature grew openai-to-kiro.ts (853->890) and its
test (1093->1234); the fast-path PR->release does not gate check:file-size on merge,
so the growth accumulated on the release tip. Rebaselined to keep the tip green.
Justification recorded in file-size-baseline.json.
* fix(doctor): resolve two false-positive WARNs (#6162) (#6163)
* fix(doctor): resolve two false-positive WARNs (#6162)
The `omniroute doctor` command reported two warnings on healthy installs
even though the underlying checks actually passed. Both came from the
doctor probing state that already worked; they looked like bugs but users
couldn't tell without manual digging.
Issue 1 — Server liveness HTTP 401
/api/health and /api/health/degradation both require the management
token. Doctor called them without auth → 401 → WARN, even when the
Next.js server was clearly alive and listening.
Fix: probe the configured health endpoint first; on 401/403, fall
back to a publicly served static asset (/favicon.ico) to confirm the
server is alive. WARN now only fires when both probes fail.
Issue 2 — CLI Tools '@/shared' import
tool-detector.ts (and 3 other cli-helper files) import @/shared/...
aliases that resolve via tsconfig.json paths. The CLI ships raw TS
source (no compile step) and runs through tsx, but tsx does not honor
tsconfig paths at runtime, and tsconfig-paths only hooks CJS
Module._resolveFilename while doctor uses ESM `import()`.
Fix: replace @/shared/... with relative imports in the 4 cli-helper
files. This is the same pattern these files already use for ./config-
generator/* imports. No new dependency, no architectural change, and
the fix doesn't regress Next.js itself which keeps using @/shared.
Verified on v3.8.43 (Node v24.17, Windows 11):
Before: 7 ok, 2 warning(s), 0 failure(s)
After: 8 ok, N warning(s), 0 failure(s)
where N accurately reflects which CLI tools are installed and
configured for OmniRoute (e.g. Hermes Agent installed but not
pointed at 20128 → 2 real warnings, not 1 false-positive).
Refs #6162
* fix(doctor): derive fallback URL from primary URL via new URL()
Per Gemini code-assist review feedback: the previous fallback constructed
the /favicon.ico URL from defaults (127.0.0.1:PORT) which ignored custom
host/port/protocol configurations supplied via:
- OMNIROUTE_DOCTOR_LIVENESS_URL
- OMNIROUTE_DOCTOR_HOST
- --liveness-url / --host CLI flags
Parse the primary URL with new URL() to preserve protocol, host, port, and
subpaths. The previous default-based fallback remains as a catch-all for
invalid primary URLs.
* test(doctor): add regression tests for #6162 fixes
Two new test files lock the fix and satisfy the PR Test Policy gate
("production code change without tests"):
- tests/unit/cli-helper-tool-detector-paths-6162.test.ts
Locks the @/shared → relative imports fix across all 4 cli-helper
files. Asserts (a) no @/shared alias remains in the cli-helper
sources, and (b) each file is importable at runtime via tsx/ESM,
which would have thrown "Cannot find package '@/shared'" before
the fix.
- tests/unit/cli-doctor-liveness-fallback-6162.test.ts
Locks the /favicon.ico fallback in doctor.mjs. Asserts the
fallback probe exists, derives its URL from the primary URL via
new URL() (per Gemini review feedback), and that the buggy
'Server responded with HTTP 401' WARN path is gone.
Both tests use only node:test + node:assert/strict so they slot into
the existing 'test' and 'test:unit' scripts with no extra config.
* test(doctor): fix primary.ok regex in fallback test
The earlier regex /primary\.ok\s*\?/ required a '?' immediately after,
but the actual doctor.mjs code uses a multi-line if-block:
if (primary.ok) {
return ok(...);
}
Use /\bprimary\.ok\b/ instead so the assertion matches the existing
branching.
---------
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
* fix(doubao-web): switch provider to Dola global (#6235)
* fix(doubao-web): switch provider to Dola global
* fix(doubao-web): use .dola.com cookie domain for s_v_web_id + rebaseline test
The Dola switch left s_v_web_id with a host-only "www.dola.com" domain, which fails
the token-source contract (domain must start with "." or "http") — the sibling
sessionid/ttwid cookies and the canonical cookieDomain already use ".dola.com", which
also matches www.dola.com. Also rebaselines web-cookie-providers-new.test.ts (850->890)
for the provider-switch regression cases.
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
---------
Co-authored-by: Diego Rodrigues de Sa e Souza <diegosouza.pw@gmail.com>
* fix(providers): register zed in OAuth PROVIDERS to fix Unknown provider error (#6041) (#6078)
Registers a minimal import_token entry for the existing Zed IDE keychain-import
provider so getProvider("zed") no longer throws "Unknown provider: zed" when the
UI probes the OAuth capability endpoint; generateAuthData returns { supported: false }.
Test runner fix: the regression test imported from "vitest" but lives in tests/unit/
(node:test territory, outside the vitest include globs) — it ran in no runner. Converted
to node:test + node:assert so it actually executes (8/8 green).
Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
* fix(oauth): align zed in OAUTH_PROVIDER_IDS + config enum after #6078 merge
#6078 registered zed in the OAuth PROVIDERS registry but did not add it to the
constants PROVIDERS id map nor the oauth-providers-config enumeration test, leaving
that test red on the release tip (getProvider enumeration vs EXPECTED mismatch).
Adds ZED to the id constants + zed to EXPECTED_PROVIDER_KEYS/EXPECTED_CONFIG_BY_PROVIDER.
* fix(mitm): strip colons from macOS cert fingerprint before keychain match (#6134) (#6204)
fix(mitm): strip colons from macOS cert fingerprint before keychain match (#6134). Extracted testable macCertOutputHasFingerprint helper + regression guard. Thanks @rianonehub. Integrated into release/v3.8.45.
* docs(architecture): sync stale DB-layer counts (45+/55 → 95+/110+) in REPOSITORY_MAP, db-schema diagram and llm.txt (+42 i18n mirrors) (#6167)
docs(architecture): sync stale DB-layer counts (45+/55 → 95+/110+) across REPOSITORY_MAP, db-schema diagram, llm.txt + 42 i18n mirrors (#6167). Docs-only; check:docs-all passes locally on the reconstruction. Reds are pre-existing base-red drift on release/v3.8.45 (dast-smoke #6228; executor-kiro.test.ts eslint anys; changelog/package.json version drift) — none introduced by this PR. Integrated into release/v3.8.45.
* fix(api): count tool_use/tool_result/thinking blocks in count_tokens estimate (port from 9router#2337) (#6221)
fix(api): count tool_use/tool_result/thinking blocks in count_tokens estimate (#6221, port from 9router#2337). TDD-covered (6/6); typed the test casts to clear no-new-eslint. Reds are pre-existing base-red drift on release/v3.8.45 (dast-smoke #6228; executor-kiro.test.ts eslint anys; changelog/package.json version drift) — none introduced by this PR. Thanks @luweiCN. Integrated into release/v3.8.45.
* fix(antigravity): strip trailing assistant prefill turn for Vertex Claude models (#6114)
fix(antigravity): strip trailing assistant prefill for Vertex Claude models (#6114). TDD-covered (6/6), merged on TDD strength per owner. Reds are pre-existing base-red drift on release/v3.8.45. Thanks @anki1kr. Integrated into release/v3.8.45.
* fix(security): require management auth for mutable cloud routes (#6233) (#6233)
fix(security): require management auth for mutable cloud routes (#6233). Verified: 3 PR tests + full authz/route-guard suite 241/241 green. Thanks @vittoroliveira-dev. Integrated into release/v3.8.45.
* fix(dashboard): use connection.id (UUID) not connection.provider (category) in onboarding wizard href (issue #6144) (#6166)
refactor(dashboard): extract tested buildProviderDetailsHref helper for onboarding wizard (#6166). Behavioral #6144 fix already on tip via #6145; this lands the tested-helper hardening. Thanks @KooshaPari. Integrated into release/v3.8.45.
* feat(rankings): add 'Configured Only' filter to Free Provider Rankings page (#6245)
feat(rankings): add 'Configured Only' filter to Free Provider Rankings (#6245, closes #6150). 9/9 test green. Thanks @Iammilansoni. Integrated into release/v3.8.45.
* fix(i18n): add 118 missing Italian translations (#6212)
i18n(it): add 118 Italian translations (#6212). Audited net-additive (0 keys dropped, valid JSON). Thanks @serverless83. Integrated into release/v3.8.45.
* test(dashboard): realign #6145 onboarding-href guard to the #6166 helper refactor (#6270)
Realign the #6145 onboarding-href guard to the #6166 helper refactor (buildProviderDetailsHref). Test-only; unblocks the fast-path unit job across the open PR queue. Base-reds only (dast-smoke #6228, docs version-drift, executor-kiro anys). Integrated into release/v3.8.45.
* feat(providers): add Yuanbao (web) cookie-session provider (#6196) (#6256)
feat(providers): add Yuanbao (web) cookie-session provider (#6196). TDD-covered; base-reds only (dast-smoke #6228, docs version-drift, executor-kiro anys — #6145 guard fixed on tip via #6270). Integrated into release/v3.8.45.
* feat(providers): route built-in agentrouter through dynamic CC wire image (#6056) (#6255)
feat(providers): route built-in agentrouter through dynamic CC wire image (#6056). TDD-covered (agentrouter-cc-wire-image.test.ts). Base-reds only. Integrated into release/v3.8.45.
* feat(providers): bulk-add API keys for Cloudflare Workers AI (#6174) (#6254)
feat(providers): bulk-add API keys for Cloudflare Workers AI (#6174). Per-entry providerSpecificData (fixes shared-object reuse); TDD guard bulk-api-key-parser-cloudflare.test.ts. Base-reds only. Integrated into release/v3.8.45. (thanks @muflifadla38)
* feat(dashboard): routing/settings UX clarity — share %, Cloud Sync rename, base-URL override (#6147) (#6253)
feat(dashboard): routing/settings UX clarity (#6147) — effective share %, Cloud Sync→Remote Settings Sync rename, opt-in advanced base-URL override. TDD guard routing-settings-ux-6147.test.ts (6/6). Base-reds only. Integrated into release/v3.8.45.
* feat(combo): add option to disable session stickiness (#6168) (#6252)
feat(combo): add option to disable session stickiness (#6168) — per-combo/global, precedence config→settings→false (preserves #3825). TDD guard combo-disable-session-stickiness.test.ts (8/8). Base-reds only. Integrated into release/v3.8.45. (thanks @RCrushMe)
* feat(docker): OMNIROUTE_NO_SUDO env flag for root-less MITM cert trust (#6122) (#6249)
feat(docker): OMNIROUTE_NO_SUDO env flag for root-less MITM cert trust (#6122). resolveSudoSpawn strips sudo when set; argv-array spawn preserved (Hard Rule #13). TDD guard mitm-systemCommands-no-sudo.test.ts (5/5). Base-reds only. Integrated into release/v3.8.45. (thanks @powellnorma)
* feat(providers): add Requesty as an OpenAI-compatible gateway provider (#6120) (#6250)
feat(providers): add Requesty as an OpenAI-compatible gateway provider (#6120). Fixed the APIKEY_PROVIDERS count guard (167→168) the feature had missed. TDD guard requesty-provider.test.ts (4/4) + providers-constants-split (4/4). Base-reds only. Integrated into release/v3.8.45. (thanks @chirag127)
* fix(providers): remove deprecated MiMo v2 entries (#6248)
chore(providers): remove deprecated MiMo V2 catalog entries (superseded by V2.5); realign provider-catalog tests. Merged — thank you, @backryun! Base-reds only. Integrated into release/v3.8.45.
* fix(github-skills): add missing import, add unit tests, fix settings JSON parse (#6186)
feat(skills): GitHub skill-discovery subsystem — search/score/scan/import agent skills from GitHub, MCP tools (scope-gated) + /api/github-skills route (host-pinned api.github.com, sanitized errors). Merged — thank you, @Moseyuh333! Pre-merge fixes: passed toolDef.scopes so tools gate correctly under scope enforcement, routed the GET error path through sanitizeErrorMessage+500 (Hard Rule #12), and realigned the agent-skills count guards (catalog/routes/generator/mcp) for the new catalog entry. Base-reds only. Integrated into release/v3.8.45.
* Fix/5976 continued (#6216)
fix(combo): 5 streaming-path fixes (#5976) — locked-stream 500, error-frame-only-if-no-content, Gemini MALFORMED_RESPONSE→content_filter failover, correlationId substring, per-model-500 lockout skip + request-logger UI. Merged — thank you, @hartmark! Maintainer follow-up: releaseQualityClone cancels the abandoned quality-check tee branch (per-request memory) + regression test. All 41 combo/streaming quality tests green. Base-reds only. Integrated into release/v3.8.45.
* feat(dashboard): filter Free Provider Rankings by configured/available (#6150) (#6251)
feat(dashboard): configured-only / available-only filters on Free Provider Rankings (#6150) — server-side query params + tested lib helper; supersedes the client-side #6245 toggle with an available-only dimension. Lib logic 11/11 green; UI validated live on VPS. Base-reds only. Integrated into release/v3.8.45.
* ci: unblock test jobs from the Build gate (start at minute 0) (#6275)
test-unit x8, test-vitest, test-integration x2 and test-security all had
needs: build but never download the next-build artifact — the dependency
only serialized ~20min of Build wall-clock in front of every test run.
Switch them to needs: changes with the same skip condition Build uses
(docs-only PRs and drafts still skip), so the test chain
(test-unit -> test-coverage -> quality-gate -> sonarqube) now runs in
parallel with Build instead of after it.
Jobs that genuinely consume the artifact keep needs: build unchanged:
test-e2e x9, package-artifact, electron-package-smoke.
Expected effect on a full ci.yml run: critical path drops from
build + tests (~30min+) to max(build, tests) — roughly 15-20min saved
per run, no extra runner minutes beyond starting the same jobs earlier.
* ci(build): switch Next.js production build to Turbopack (1.9x faster) (#6273)
Next 16 ships Turbopack as the stable production bundler. Benchmarked on a
32-core box against the same tree: webpack 1035s -> Turbopack 539s (1.92x).
The build script already supports the switch via OMNIROUTE_USE_TURBOPACK=1
(scripts/build/build-next-isolated.mjs) and next.config.mjs already mirrors
the resolveAlias stubs in the turbopack block, so this only flips the CI env.
The webpack .build/next/cache actions/cache step is removed in the same
commit: Turbopack does not use the webpack cache dir (its persistent FS
cache is experimental and NOT enabled), so restoring ~0.5 GB per run would
be pure wasted download. Revert restores webpack + its cache together.
Validation: standalone output smoke-tested (server.js boots, health 200,
dashboard 307); the 428 build warnings are the known benign 'overly broad
file pattern' static-analysis notices for dynamic fs usage (covered at
runtime by outputFileTracingIncludes). Downstream e2e x9, package-artifact
and electron-package-smoke consume this artifact, so a green CI run here
validates the Turbopack artifact end-to-end. nightly-compat and npm-publish
stay on webpack until this PR proves out.
* feat(build): make Turbopack the default bundler for dev and build (#6283)
Turbopack (stable in Next 16) becomes the code default in the three entry
points that previously required an explicit OMNIROUTE_USE_TURBOPACK=1:
- scripts/build/build-next-isolated.mjs (production build)
- scripts/dev/run-next.mjs (dev server)
- scripts/dev/run-next-playwright.mjs (playwright dev runner)
OMNIROUTE_USE_TURBOPACK=0 remains the webpack escape hatch (Windows /
native-binding / bundler-compat issues), and only the documented '0'
opts out — junk values keep the default.
Benchmarked on this codebase (same tree, Next 16.2.9): webpack 1035s vs
Turbopack 539s on a 32-core box; ~20min vs 6min59s on ubuntu-latest.
Artifact validated end-to-end (standalone smoke + e2e/package-artifact/
electron-package-smoke CI jobs, Docker amd64+arm64 builds clean with the
v3.8.27 ImportTracer panic gone on 16.2.9).
TDD: tests/unit/build-bundler-default-turbopack.test.ts (new) +
run-next-playwright.test.ts extended with the unset-env default case;
both red before the flip, green after. ENVIRONMENT.md updated.
* feat(docker): build the image with Turbopack (v3.8.27 panic gone on Next 16.2.9) (#6285)
Flips the builder stage to OMNIROUTE_USE_TURBOPACK=1 and rewrites the stale
panic comment: the v3.8.27-era TurbopackInternalError ('entered unreachable
code: there must be a path to a root' in ImportTracer::get_traces) no longer
reproduces on Next 16.2.9.
Validation (2026-07-05, this exact Dockerfile, default
OMNIROUTE_BUILD_MEMORY_MB=4096, no overrides):
- amd64: docker build 659s, exit 0, zero panic/OOM strings in the full log,
container smoke-tested (/api/monitoring/health 200)
- arm64 (qemu): exit 0, zero panic strings
Webpack stays available as the escape hatch:
--build-arg / -e OMNIROUTE_USE_TURBOPACK=0. The V8 heap ceiling is kept:
Turbopack's compile is native Rust, but prerender/export still runs on V8.
* ci: opt-in self-hosted VPS runners for the release window (anti-queue) (#6284)
Adds the on-demand self-hosted runner plumbing for /generate-release:
- scripts/vps/release-runner-up.sh: starts the runner VM on Proxmox, waits
for >=1 'omni-release' runner to report online via the GitHub API, then
flips the USE_VPS_RUNNER repo variable to true. Any failure/timeout sets
it back to false and exits 1 so the caller falls back to hosted runners.
- scripts/vps/release-runner-down.sh: flips USE_VPS_RUNNER=false FIRST
(so no job gets scheduled onto a dying runner), then gracefully shuts
the VM down. Idempotent.
- ci.yml: build, test-unit x8 and test-vitest pick their runner
dynamically. Self-hosted is used ONLY when USE_VPS_RUNNER == 'true'
AND the event is own-origin (push/dispatch, or a PR whose head repo is
this repository). Fork PRs and the var's default/absent state always
fall back to ubuntu-latest.
Why: the Free plan caps hosted concurrency at 20 jobs; a release run
saturates it and queues. The benchmarked VPS (32-core, 4 runners) matches
hosted per-job times (unit ~8.5min/shard, build 9min turbo) but eliminates
the queue, which is the real release bottleneck (~30-50min on a busy day).
Scope is conservative: only the three job families benchmarked on the VPS;
e2e/electron/integration stay hosted (playwright/xvfb provisioning not
validated on the runner workspace yet).
* docs(changelog): restore v3.8.45/v3.8.44 sections eaten by the #6193 merge auto-resolve + CI-perf campaign bullets (#6273 #6275 #6283 #6284 #6285)
* fix(dashboard): null-guard connection in EditConnectionModal base-URL override (#6147) (#6287)
fix(dashboard): null-guard connection in EditConnectionModal (#6147) — fixes 'Cannot read properties of null (reading authType)' crash on every provider-detail page entry. TDD: connModals.test.tsx null-mount 9/9. Base-reds only. Integrated into release/v3.8.45.
* chore(release-green): clear test-masking + docs-all HARD reds for the v3.8.45 pre-flight
- test-masking: allowlist the 4 verified-legitimate assert reductions of the
cycle (#6248 MiMo V2 removal, #6170 Kiro catalog correction, #6154 Copilot
catalog refresh) and register the #6164 AutoRoutingBanner test deletion with
a real replacement guard (tests/unit/home-no-autorouting-banner.test.ts —
asserts the banner stays out of home/page.tsx and the component stays deleted)
- docs-sync: executors count 68 -> 73 in ARCHITECTURE.md + CODEBASE_DOCUMENTATION.md
- env-doc-sync: document OMNIROUTE_NO_SUDO (#6249/#6122) in .env.example +
docs/reference/ENVIRONMENT.md
* fix(quality): clear the cycle's 11 net-new ESLint errors + make validate-release-green suppressions-aware
- executor-kiro/save-call-log/call-logs-correlation tests: replace 15 'as any'
casts with typed shapes (net-new no-explicit-any errors from #6213/#6216);
prune the now-empty suppression entries so the frozen baseline stays exact
- github-skills + usage/call-logs routes: raw toLowerCase().includes() search
replaced by matchesSearch() (no-restricted-syntax — Turkish-safe search,
behavior covered by tests/unit/call-logs-correlation-substring.test.ts and
tests/unit/github-collector.test.ts)
- validate-release-green.mjs: run ESLint with --suppressions-location (match
the npm run lint contract — frozen debt is not a release red) and raise the
lint timeout 15->30min (a full pass takes ~14min alone; the 15min ceiling
expired under concurrent suite load and surfaced as 'could not parse eslint
json')
* fix(skills): generate the missing omni-github-skills registry entry + align catalog count tests
PR #6186 added omni-github-skills to the agent-skills catalog (API 22 -> 23)
but did not run the generator, so skills/omni-github-skills/SKILL.md never
existed and 6 integration assertions split between the old (42/43) and new
counts. Generated via scripts/skills/generate-agent-skills.mjs --apply and
aligned agent-skills-discovery to the real totals (43 = 23 API + 20 CLI;
handlers return 44 with config-codex-cli). 30/30 discovery+content tests green.
* fix(combo): restrict the #6216 empty-stream failover to truly empty bodies (restores #3399/#3685 contracts)
The 'streaming no recognized content' branch added by #6216 marked ANY
stream that ended without content deltas as invalid — sweeping in two
regression-guarded pass-through contracts: an empty stream terminated by an
explicit 'data: [DONE]' (#3399 context-cache protection) and an incomplete
Claude lifecycle (ping only, no message_start; #3685 — stream-readiness
timeout territory, not failover). Both unit guards were red on the branch
and green on main (86/86 vs 84/86).
The branch now fires only for a truly EMPTY body (zero bytes — the Gemini
HTTP-200-empty case that motivated #6216), tracked via sawAnyBytes. New
guard: '#5976 truly EMPTY streaming body (zero bytes) -> invalid for combo
failover'. 87/87 across both files.
Also in this pre-flight batch:
- agentSkillTools-mcp: api.have upper bound 22 -> 23 (the #6186 catalog
addition updated the totals but missed this bound)
- delete tests/unit/free-provider-rankings-configured-filter.test.ts:
#6251 (server-side configuredOnly/availableOnly) superseded the #6245
client-side toggle it pinned; replacement declared in the test-masking
allowlist (tests/unit/freeProviderRankings-filters.test.ts, 11/11)
* chore(quality): prune stale ESLint suppressions (4,273 -> 4,233)
Entries whose violations no longer exist (cleaned by cycle merges and the
pre-flight fixes) made 'npm run lint' exit 2 with 'suppressions left that do
not occur anymore'. Regenerated via --prune-suppressions; net-new policy
unchanged.
* fix(proxy): #6246 stop the v3.8.44 proxy IP-leak + over-deactivation regression (#6296)
Merged into release/v3.8.45. Reds pré-existentes classificados: dast-smoke (infra), check:file-size (drift de baseline de god-files congelados), e 3 testes de contagem agentSkills stale (43→44 já corrigidos no tip pelo #6186 — somem no squash sobre o tip). Núcleo do fix #6246 (proxy IP-leak + over-deactivation).
* fix(proxy): make "Test All" read-only + add bulk enable/disable (#6246) (#6299)
Merged into release/v3.8.45. Delta do par #6246 (Test-All read-only + bulk enable/disable), reconciliado com o núcleo #6296 já mergeado. CHANGELOG restaurado (ambos bullets do #6246 coexistem). Reds pré-existentes: dast-smoke (infra) + file-size drift.
* fix(resilience): evict sticky affinity on pinned-account failover (#6219) (#6231)
Merged into release/v3.8.45. Sticky affinity failover (#6219). Sincronizado com tip; CHANGELOG restaurado (base bullets preservados, net +1). Reds pré-existentes: dast-smoke + file-size drift.
* fix(sse): drop commentary-phase text in Responses passthrough (#6199) (#6232)
Merged into release/v3.8.45. Responses commentary-phase filter (#6199). Sincronizado; CHANGELOG restaurado net +1. Reds: dast-smoke + file-size drift.
* fix: bug-fix sweep — log path, AgentBridge DNS, opencode-go headers, GitLab Duo tools, M365 EDU (#6197 #6127 #6198 #5997 #6220 #6210) (#6234)
Merged into release/v3.8.45. Bug-fix sweep (#6197 log path, #6127/#6198 AgentBridge DNS, #6210 M365 EDU, #6220 GitLab Duo tools, #5997 opencode-go headers). 27 testes verdes. CHANGELOG restaurado net +5. Reds: dast-smoke + file-size drift.
* fix(docker): add id= to BuildKit cache mounts for strict builders (#6291)
Merged into release/v3.8.45. Dockerfile-only: explicit id= on BuildKit cache mounts (fixes strict-frontend parse error). Reds pré-existentes (dast-smoke/file-size drift) não relacionados a mudança de Dockerfile. Thanks @karimalsalah.
* fix(sse): strip zero-width markers from streamed tool-call arguments (follow-up to #5857) (#6292)
Merged into release/v3.8.45. Strip zero-width markers from streamed tool-call arguments (#5857 follow-up). Test 50/50. CHANGELOG reconciled net +1. Reds pré-existentes (dast-smoke/file-size drift). Thanks @DKotsyuba.
* ci(quality): merge-integrity fast-gates + pre-flight hermetic mode (#6300)
Merged into release/v3.8.45. CI merge-integrity fast-gates (changelog-integrity + agent-skills-sync) + pre-flight hermetic mode. O gate novo detectou 11 SKILL.md gerados fora de sync (drift pré-existente no tip: omni-api-keys endpoints, omni-github-skills do #6186) — regenerados neste PR, Merge-integrity GREEN no CI. Reds restantes base-red (dast-smoke/file-size drift/live-data flaky). Testes novos 17/17.
* fix(a2a): finish the #6186 catalog-count update — 3 hardcoded 22s left in production
#6186 added omni-github-skills (API 22 -> 23) and updated computeCoverage's
total, but left the old count hardcoded in the A2A layer: listCapabilities
metadata reported coverage.api.total 22 (type literal + value) and
SkillCoverageSchema pinned z.literal(22) — so the schema would REJECT the
correct runtime value. Aligned all three to 23 + the unit fixtures
(listCapabilities-a2a, agentSkills-schemas, 46/46 with agentSkillTools-mcp).
* fix(quality): type the 7 net-new 'as any' casts from #6292 (Lint red on the release tip)
#6292 rewrote/added zero-width-marker tests with 7 new 'as any' result casts,
pushing the file past its frozen suppression (84) — and when violations
exceed the suppressed count ESLint reports ALL of them, so the ci.yml Lint
job went red with 90 errors. The 7 new sites get typed shapes (delta /
arguments / choices / output accessors — same pattern as fecf888fd); the
pre-existing debt stays frozen at the exact new count (83). 50/50 tests
green, file lint-clean under the suppressions baseline.
* fix(api): Zod-validate POST /api/github-skills + document new gate envs + pin merge-integrity actions
Three latent heavy-CI reds surfaced by the VPS validation dispatch (the fast
path never runs these gates):
- t06 route-validation: POST /api/github-skills destructured request.json()
blind — a non-array 'targets' would .map-crash. Now validateBody(zod)
with defaults preserved (Hard Rule #7). Guard:
tests/unit/github-skills-route-validation.test.ts (4/4).
- env-doc-sync: document OMNIROUTE_SKIP_SYSTEM_TRUST (#6310) and the
changelog-integrity gate envs CHANGELOG_BASE_REF/ALLOW_CHANGELOG_REMOVALS
(#6300) in .env.example + ENVIRONMENT.md.
- zizmor ratchet: the new merge-integrity job's checkout/setup-node uses were
unpinned (+1 finding, 160 > baseline 159); pinned by SHA -> 158 (< baseline).
* fix(quality): clear the 2 remaining heavy-gate reds on the release tip
- check:error-helper: githubSkillTools.ts (#6186 wave) built MCP install
error results with raw err.message — routed through sanitizeErrorMessage()
(Hard Rule #12; 19/19 agentSkillTools tests green)
- check:mutation-test-coverage: tests/unit/combo-provider-cooldown-sibling.test.ts
(#6216) was missing from stryker.conf tap.testFiles — added so its mutant
kills count on nightly-mutation
* fix(security): 405 method-first for /api/keys/{id}/devices (dast-smoke QUERY check)
Schemathesis's newer unsupported-methods check (unpinned tool drift) sends
QUERY /api/keys/{id}/devices and demands 405 Method Not Allowed; the path had
no HIGH_RISK_METHOD_RULES entry, so the auth layer answered 401 first. Add
the devices rule (GET-only) so undocumented methods get a clean method-first
405 — same pattern as the v3.8.44 TRACE fix. TDD:
tests/unit/dast-method-not-allowed.test.ts gains the devices QUERY case (4/4).
* fix(mitm): test suite and CI must never mutate the OS trust store (OMNIROUTE_SKIP_SYSTEM_TRUST) (#6310)
Incident 2026-07-05 on the self-hosted release runner (VM 113): the
integration test 'POST /cert: installs trust when cert exists' exercised the
REAL install path, wrote a 105-byte fake PEM (FakeMITMCertForTestingOnly)
into /usr/local/share/ca-certificates and update-ca-certificates baked the
invalid entry into ca-certificates.crt — breaking ALL system TLS on the VM
(curl error 77, apt cert failures, and the intermittent gzip-corrupted
next-build artifacts that failed 6/9 e2e shards in run 28754447912). Hosted
runners are ephemeral, so the same mutation went unnoticed for months.
- installCert/uninstallCert: skip the OS dispatch under
OMNIROUTE_SKIP_SYSTEM_TRUST=1 — AFTER the input checks, so the #4546
environment-skip contract (missing file throws -> structured skip) and the
already-installed/not-installed early returns are preserved.
- installTproxyCa/uninstallTproxyCa: same guard, only when no run dep is
injected (DI'd tests keep exercising the full command sequence with mocks).
- tests/_setup/isolateDataDir.ts sets the env for every node:test process;
ci.yml/quality.yml/nightly-release-green.yml set it workflow-wide (e2e runs
the real app outside the test setup).
TDD: tests/unit/system-trust-test-guard.test.ts (guard exported to every test
process; guarded install resolves on a real file without touching the OS;
missing-file contract preserved). 82/82 across the affected cert/tproxy/
agent-bridge suites.
* ci(vps): hermetic nightly pre-flight on the release runner (descoped: e2e/integration/electron stay hosted) (#6305)
* ci(vps): extend the dynamic omni-release runner to e2e/integration/electron + nightly pre-flight
Completes the #6284 rollout to the jobs where the VPS pays the most:
- test-e2e (9 shards, 15-20min/shard hosted — dominated by setup, not tests),
test-integration (2 shards) and electron-package-smoke now pick the
self-hosted omni-release runner under the same gate: vars.USE_VPS_RUNNER
== 'true' AND own-origin (fork PRs never reach self-hosted).
- nightly-release-green (the release pre-flight) becomes runner-dynamic too:
on the VPS it runs in a clean env — no operator OMNIROUTE_API_KEY, no
local noauth CLIs — eliminating the machine-specific false positives that
dominated the 2026-07-05 pre-flight; passes --hermetic (no-op until the
#6300 validator lands, then belt-and-suspenders).
Validation plan (per operator request): release-runner-up.sh -> full ci.yml
workflow_dispatch on this branch exercising e2e/integration/electron on the
VPS (proves playwright --with-deps + xvfb on the runner) -> down.sh -> VM
off verified.
* fix(mitm): test suite and CI must never mutate the OS trust store (OMNIROUTE_SKIP_SYSTEM_TRUST)
Incident 2026-07-05 on the self-hosted release runner (VM 113): the
integration test 'POST /cert: installs trust when cert exists' exercised the
REAL install path, wrote a 105-byte fake PEM (FakeMITMCertForTestingOnly)
into /usr/local/share/ca-certificates and update-ca-certificates baked the
invalid entry into ca-certificates.crt — breaking ALL system TLS on the VM
(curl error 77, apt cert failures, and the intermittent gzip-corrupted
next-build artifacts that failed 6/9 e2e shards in run 28754447912). Hosted
runners are ephemeral, so the same mutation went unnoticed for months.
- installCert/uninstallCert: skip the OS dispatch under
OMNIROUTE_SKIP_SYSTEM_TRUST=1 — AFTER the input checks, so the #4546
environment-skip contract (missing file throws -> structured skip) and the
already-installed/not-installed early returns are preserved.
- installTproxyCa/uninstallTproxyCa: same guard, only when no run dep is
injected (DI'd tests keep exercising the full command sequence with mocks).
- tests/_setup/isolateDataDir.ts sets the env for every node:test process;
ci.yml/quality.yml/nightly-release-green.yml set it workflow-wide (e2e runs
the real app outside the test setup).
TDD: tests/unit/system-trust-test-guard.test.ts (guard exported to every test
process; guarded install resolves on a real file without touching the OS;
missing-file contract preserved). 82/82 across the affected cert/tproxy/
agent-bridge suites.
* ci(vps): descope — e2e/integration/electron stay on hosted runners; keep the hermetic nightly pre-flight dynamic
Validation verdict (runs 28754447912 + 28757670732, VM 113):
- e2e cannot run >1 per VM: both jobs bind port 20128 ('already used') — needs
a per-job port in the playwright runner before any VM rollout.
- concurrent ~1GB artifact downloads truncate on the VM uplink (gzip 'invalid
compressed data' — with 2 runners the e2e shard passed; corruption returned
at 4) — actions/download-artifact has no integrity retry here.
- integration shard 2 exceeded its 15-min timeout twice on the VM.
The VPS remains a win for whole-machine jobs: build/unit/vitest (already
dynamic via #6284) and nightly-release-green (single job, clean env, hermetic
pre-flight) — which this PR keeps.
* chore(quality): v3.8.45 cycle-close file-size rebaseline (Phase 0 drift absorption)
13 files grown by the cycle's merged PRs (#6216 streaming+request-logger UI;
#6251/#6253 dashboard UX) — legitimate merged-feature growth absorbed by the
release captain per the Phase 0 drift policy; all entries stay frozen (cannot
grow further). Justification key: _rebaseline_2026_07_06_v3845_release_close.
* chore(quality): v3.8.45 cycle-close cognitive/cyclomatic rebaseline (Phase 0 drift absorption)
cognitive 867->877 (+10), cyclomatic 2028->2035 (+7) — inherited cycle drift
measured by check:release-green (hermetic) on the release tip; the captain's
pre-flight fixes are gate/test/workflow changes (complexity-neutral).
Justification keys: _rebaseline_2026_07_06_v3845_release_close.
* docs(changelog): v3.8.45 reconciliation — fold Unreleased into the version section, 30 missing bullets, contributors hall
Phase 0a reconciliation (/generate-release): every commit since v3.8.44 now
has a bullet or a Maintenance rollup (82 commits; the only ref-less residues
are the bump/recording commits); #6193 bullet gains its PR ref; #6298
diagnosis credited (@subhansh-dev, landed via #6234); [3.8.44] header dated;
'### 🙌 Contributors' table injected (27 external + maintainer) + 42 i18n
mirrors resynced.
* chore(release): v3.8.45 — 2026-07-06
* fix(resilience): 502/503/504 keep the connection-unavailability path — only the exact 500 skips lockout (#5976 contract)
The #6216 branch used 'status >= 500', so a 503 on a per-model-quota /
openai-compatible provider returned cooldownMs 0 — no model lockout AND no
connection cooldown — hot-looping the failing upstream and breaking the
resilience-http-e2e 'priority combo falls back on 503' guard on the release
PR (the request after a 503 hit the same primary again). #6216's OWN unit
contract pins the narrow behavior ('Gemini 503 should NOT skip cooldown',
combo-provider-cooldown-sibling.test.ts) but computes the condition inline
instead of exercising auth.ts. Code aligned to the tested contract:
status === 500 skips (intermittent, not model-specific); 502/503/504 keep
the pre-#6216 model-lockout path. Validated: the failing integration test
flips red -> green locally (19s, deterministic before).
* fix(security): crypto-backed randomNumericId in doubao-web (CodeQL js/insecure-randomness)
The synthetic Dola device/web id was built from Math.random; CodeQL flags it
as insecure randomness in a security context. Not a secret, but
crypto.getRandomValues costs the same and closes alert #692 at the source.
Doubao executor tests 14/14 green. Alerts #693-695 (incomplete-url-substring
in unit-test asserts) dismissed as false positives per the v3.8.35 precedent
(Hard Rule #14).
* chore(quality): shave the #5976 fix comment back under the auth.ts file-size freeze (2447)
---------
Co-authored-by: Danny S <36470572+kanztu@users.noreply.github.com>
Co-authored-by: Luis Alejandro Vega <LuisAlejandroVega@redesprivadasvirtuales.com>
Co-authored-by: Milan Soni <123074437+Iammilansoni@users.noreply.github.com>
Co-authored-by: R. Beltran <rbeltran8000@gmail.com>
Co-authored-by: VXNCXNX <93332837+VXNCXNX@users.noreply.github.com>
Co-authored-by: Denis Kotsyuba <kocubads96@gmail.com>
Co-authored-by: DKotsyuba <16292493+DKotsyuba@users.noreply.github.com>
Co-authored-by: backryun <bakryun0718@proton.me>
Co-authored-by: Aris <arissunandar399@gmail.com>
Co-authored-by: Ankit <177378174+anki1kr@users.noreply.github.com>
Co-authored-by: Rian Priskanova <rian@evercore.technology>
Co-authored-by: Vittor Guilherme Borges de Oliveira <vittoroliveira.dev@gmail.com>
Co-authored-by: KooshaPari <42529354+KooshaPari@users.noreply.github.com>
Co-authored-by: serverless83 <35410475+serverless83@users.noreply.github.com>
Co-authored-by: Moseyuh333 <148680980+Moseyuh333@users.noreply.github.com>
Co-authored-by: Markus Hartung <mail@hartmark.se>
Co-authored-by: HenryHaniHannoush <karimmalsalah@gmail.com>
2071 lines
110 KiB
Plaintext
2071 lines
110 KiB
Plaintext
# ┌─────────────────────────────────────────────────────────────────────────────┐
|
|
# │ OmniRoute — .env Contract │
|
|
# │ This file documents EVERY environment variable read by the runtime. │
|
|
# │ Copy to .env and adjust values. Lines starting with # are commented out │
|
|
# │ (optional / off-by-default). Uncomment only what you need. │
|
|
# │ Reference: docs/ENVIRONMENT.md for full details and usage scenarios. │
|
|
# └─────────────────────────────────────────────────────────────────────────────┘
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 1. REQUIRED SECRETS — Must be set before first run!
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# These secrets are critical for security. Generate strong, unique values.
|
|
|
|
# JWT signing key for dashboard session tokens.
|
|
# Used by: src/lib/auth — signs/verifies all authenticated session cookies.
|
|
# Generate: openssl rand -base64 48
|
|
JWT_SECRET=
|
|
|
|
# Encryption key for API keys stored in the database.
|
|
# Used by: src/lib/db/apiKeys.ts — encrypts API key values at rest in SQLite.
|
|
# Generate: openssl rand -hex 32
|
|
API_KEY_SECRET=
|
|
|
|
# Initial admin login password — CHANGE THIS before first use!
|
|
# Used by: bootstrap only — sets the initial dashboard password on first boot.
|
|
# After first login you can change it from Dashboard → Settings → Security.
|
|
# Default: CHANGEME (insecure, for local dev only)
|
|
INITIAL_PASSWORD=CHANGEME
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 2. STORAGE & DATABASE
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# OmniRoute uses SQLite for all persistence. These variables control where
|
|
# data lives, encryption, and cleanup policies.
|
|
|
|
# Base directory for all persistent data (SQLite DB, logs, backups).
|
|
# Used by: src/lib/db/core.ts — resolves the SQLite database file path.
|
|
# Default: ~/.omniroute/ | Override for Docker or custom installations.
|
|
# Hint: When running in Docker, consider mounting a host directory here for data persistence across container restarts
|
|
# also if you want to share the same database as "npm run dev" use "./data"
|
|
# DATA_DIR=/var/lib/omniroute
|
|
|
|
# Encryption key for SQLite database encryption at rest.
|
|
# Used by: src/lib/db/encryption.ts — encrypts the entire SQLite database.
|
|
# Generate: openssl rand -hex 32 | Leave empty to disable DB encryption.
|
|
STORAGE_ENCRYPTION_KEY=
|
|
|
|
# Version tag for the encryption key — allows future key rotation.
|
|
# Used by: scripts/bootstrap-env.mjs, electron/main.js — persists key version.
|
|
# Default: v1 | Increment when rotating STORAGE_ENCRYPTION_KEY.
|
|
STORAGE_ENCRYPTION_KEY_VERSION=v1
|
|
|
|
# Automatic SQLite backup on startup.
|
|
# Used by: src/lib/db/backup.ts — creates a timestamped backup before migrations.
|
|
# Default: false (backups enabled) | Set true to skip backup on every restart.
|
|
DISABLE_SQLITE_AUTO_BACKUP=false
|
|
|
|
# ── Redis (Rate Limiting) ──
|
|
# Redis connection URL for the rate limiter backend. OPT-IN: leave this
|
|
# commented out to use the built-in in-memory rate limiter. Setting it to a
|
|
# non-running localhost (#4878) makes ioredis flood "[REDIS] Error:" logs.
|
|
# Used by: src/shared/utils/rateLimiter.ts
|
|
# Example: redis://localhost:6379 (or redis://redis:6379 in Docker)
|
|
# REDIS_URL=redis://localhost:6379
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 3. NETWORK & PORTS
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# OmniRoute can run on a single port (default) or split Dashboard/API ports.
|
|
|
|
# Canonical port for both Dashboard UI and API (single-port mode).
|
|
# Used by: src/lib/runtime/ports.ts — base port for the Next.js server.
|
|
# Default: 20128
|
|
PORT=20128
|
|
|
|
# Base path (URL subpath) when serving OmniRoute behind a reverse proxy under a subpath.
|
|
# Used by: next.config.mjs — sets Next.js `basePath`; auth redirects are basePath-aware.
|
|
# Default: "" (served at the domain root). Example: /omniroute to serve under https://host/omniroute
|
|
# OMNIROUTE_BASE_PATH=
|
|
|
|
# Split-port mode: serve Dashboard and API on separate ports for network isolation.
|
|
# Used by: src/lib/runtime/ports.ts — overrides PORT for each service.
|
|
# API_PORT=20129
|
|
# API_HOST=0.0.0.0
|
|
# DASHBOARD_PORT=20128
|
|
|
|
# Port for the real-time WebSocket live monitoring server.
|
|
# Used by: src/server/ws/liveServer.ts, src/app/api/v1/ws/route.ts
|
|
# Default: 20129
|
|
# LIVE_WS_PORT=20129
|
|
|
|
# Bind address for the live WebSocket server.
|
|
# Default: 127.0.0.1 (loopback only). Set to 0.0.0.0 to expose on LAN —
|
|
# remember to also configure LIVE_WS_ALLOWED_ORIGINS when doing so.
|
|
# LIVE_WS_HOST=127.0.0.1
|
|
|
|
# Comma-separated extra origins allowed to open a live WebSocket. The
|
|
# loopback dashboard origins are already permitted by default; use this
|
|
# var when fronting the server with a domain (e.g. https://omni.local).
|
|
# ⚠️ When using NEXT_PUBLIC_LIVE_WS_PUBLIC_URL or exposing the WS server
|
|
# beyond loopback, this MUST include the public origin(s) — otherwise
|
|
# the Origin allow-list check will reject all browser connections.
|
|
# Example: LIVE_WS_ALLOWED_ORIGINS=https://omni.local,https://dashboard.example.com,https://ws.my-ai.com
|
|
# LIVE_WS_ALLOWED_ORIGINS=https://omni.local,https://dashboard.example.com
|
|
|
|
# Comma-separated extra hostnames allowed to open a live WebSocket (LAN/Tailscale).
|
|
# Unlike LIVE_WS_ALLOWED_ORIGINS (which matches full origin URLs), this matches
|
|
# only the host portion — useful for wildcard-ish LAN/Tailscale setups.
|
|
# Used by: src/server/ws/liveServerAllowList.ts
|
|
# Example: LIVE_WS_ALLOWED_HOSTS=omni.local,tailscale-host,192.168.1.50
|
|
# LIVE_WS_ALLOWED_HOSTS=omni.local,tailscale-host,192.168.1.50
|
|
|
|
# Public URL for the live dashboard WebSocket (client-side, browser only).
|
|
# Set this when fronting the WS server with a reverse proxy or Cloudflare Tunnel.
|
|
# The browser will connect to this URL instead of ws://hostname:20129.
|
|
# The /live-ws path is already proxied from the main app (port 20128) to the
|
|
# live WS server (port 20129) by scripts/dev/standalone-server-ws.mjs.
|
|
# Used by: src/hooks/useLiveDashboard.ts
|
|
# Example: NEXT_PUBLIC_LIVE_WS_PUBLIC_URL=wss://ws.my-ai.com/live-ws
|
|
# NEXT_PUBLIC_LIVE_WS_PUBLIC_URL=
|
|
|
|
# Disable the standalone live WebSocket helper used by scripts/start-ws-server.mjs.
|
|
# Used by: scripts/start-ws-server.mjs (CI/embedded harness toggle).
|
|
# OMNIROUTE_DISABLE_LIVE_WS=0
|
|
|
|
# Enable the real-time dashboard WebSocket server.
|
|
# Used by: src/server/ws/liveServer.ts, scripts/start-ws-server.mjs
|
|
# Default: ON. Set to 0 or false to disable startup of the live WS server.
|
|
# Combine with LIVE_WS_HOST / LIVE_WS_ALLOWED_ORIGINS above when exposing
|
|
# beyond loopback.
|
|
# OMNIROUTE_ENABLE_LIVE_WS=1
|
|
|
|
# Per-(token,IP) relay rate limit, requests/minute. In-memory, per instance.
|
|
# 0 or negative disables the IP-dimension gate (per-token DB limit still applies).
|
|
# Default: 30
|
|
# Used by: src/app/api/v1/relay/chat/completions/route.ts
|
|
# RELAY_IP_PER_MINUTE=30
|
|
|
|
# Bundler selection for `npm run dev`. Set to 0 to fall back to webpack.
|
|
# Default is 1 (Turbopack). PR #4092 had forced webpack because earlier
|
|
# Turbopack 16.2.x panicked on the OmniRoute module graph with "internal error:
|
|
# entered unreachable code: there must be a path to a root"
|
|
# (turbopack-core/module_graph/mod.rs:662). That panic no longer reproduces on
|
|
# the pinned Next 16.2.9 — verified across a broad cold-compile sweep (36
|
|
# dashboard routes + open-sse-heavy API routes incl. /api/v1/chat/completions,
|
|
# /api/v1/models, /api/mcp) and repeated HMR rebuilds: zero panics. Turbopack
|
|
# also keeps dev memory far lower on the edit→rebuild loop (HMR rebuild RSS stays
|
|
# ~flat vs webpack's monotonic growth), which mitigates the dev-server OOM on
|
|
# this 60+ route app. The production build still uses webpack (build pipeline is
|
|
# unaffected by this dev-only flag).
|
|
OMNIROUTE_USE_TURBOPACK=1
|
|
|
|
# Skip the SQLite integrity health check on startup (faster boot on large DBs).
|
|
# Used by: src/lib/db/core.ts, src/lib/db/healthCheck.ts. Set to 1 to skip.
|
|
# OMNIROUTE_SKIP_DB_HEALTHCHECK=1
|
|
|
|
# Interval (ms) for the background credential health check scheduler.
|
|
# Default: 300000 (5 minutes). Minimum: 10000 (10 seconds).
|
|
# Used by: open-sse/config/constants.ts, src/lib/credentialHealth/scheduler.ts
|
|
# CREDENTIAL_HEALTH_CHECK_INTERVAL=300000
|
|
|
|
# TTL (ms) for cached credential health status.
|
|
# Default: 300000 (5 minutes).
|
|
# Used by: open-sse/config/constants.ts, src/lib/credentialHealth/cache.ts
|
|
# CREDENTIAL_HEALTH_CACHE_TTL=300000
|
|
|
|
# Set to 1 or true to disable background periodic testing of provider connections.
|
|
# Default: false
|
|
# Used by: src/lib/credentialHealth/scheduler.ts
|
|
# OMNIROUTE_DISABLE_CREDENTIAL_HEALTH_CHECK=false
|
|
|
|
# Set to "true" to emit `[ProxyFetch]` debug logs from the Vercel relay path
|
|
# in open-sse/utils/proxyFetch.ts. Off by default to avoid leaking routing
|
|
# hints in production logs.
|
|
# OMNIROUTE_PROXY_FETCH_DEBUG=true
|
|
|
|
# Docker production port mappings (docker-compose.prod.yml only).
|
|
# These set the HOST-side published ports. Container ports use PORT/API_PORT.
|
|
# PROD_DASHBOARD_PORT=20130
|
|
# PROD_API_PORT=20131
|
|
|
|
# Runtime override used by Electron and wrapped environments.
|
|
# OMNIROUTE_PORT takes precedence over PORT when running inside wrappers.
|
|
# Used by: src/lib/runtime/ports.ts — preserves canonical port in Electron.
|
|
# OMNIROUTE_PORT=20128
|
|
|
|
# Hostname/bind address for the Next.js server.
|
|
# Used by: scripts/dev/run-next.mjs (HOST), Playwright runner (HOSTNAME).
|
|
# Default: 0.0.0.0 (HOST) / 127.0.0.1 (HOSTNAME inside tests).
|
|
# NOTE: Do NOT use `HOSTNAME` — it is a POSIX shell variable automatically set to
|
|
# the machine name by bash/zsh. The .env loader cannot override it (first-wins
|
|
# semantics). Use OMNIROUTE_SERVER_HOST instead for `omniroute serve`.
|
|
# See: https://github.com/diegosouzapw/OmniRoute/issues/6194
|
|
#HOST=0.0.0.0
|
|
#HOSTNAME=127.0.0.1
|
|
#OMNIROUTE_SERVER_HOST=0.0.0.0
|
|
|
|
# Environment mode — affects Next.js behavior, logging verbosity, and caching.
|
|
# Values: production | development | Default: production
|
|
NODE_ENV=production
|
|
|
|
# Container runtime — controls startup script behavior (permissions, advice).
|
|
# Values: docker | podman | Default: docker
|
|
# Set to "podman" when running under rootless Podman so the entrypoint
|
|
# gives the correct fix instructions (podman unshare chown vs sudo chown).
|
|
CONTAINER_HOST=docker
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 4. SECURITY & AUTHENTICATION
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
# Salt for generating unique machine IDs (fingerprint diversification).
|
|
# Used by: src/lib/auth — combined with hardware identifiers for machine-id hash.
|
|
# Default: endpoint-proxy-salt | Change per-deployment for isolation.
|
|
MACHINE_ID_SALT=endpoint-proxy-salt
|
|
|
|
# Salt for deriving CLI machine-ID auth tokens (HMAC-SHA256).
|
|
# Used by: src/lib/machineToken.ts — rotates the local CLI auth token without
|
|
# touching code. Set to a new value to invalidate existing CLI tokens.
|
|
# Default: omniroute-cli-auth-v1
|
|
# OMNIROUTE_CLI_SALT=omniroute-cli-auth-v1
|
|
|
|
# Set true when running behind HTTPS (reverse proxy with TLS termination).
|
|
# Used by: src/lib/auth — sets the Secure flag on session cookies.
|
|
# Default: false | MUST be true in any non-localhost deployment.
|
|
AUTH_COOKIE_SECURE=false
|
|
|
|
# Require an API key for all /v1/* proxy endpoints.
|
|
# Used by: API middleware — rejects unauthenticated requests to the proxy API.
|
|
# Default: false | Set true for multi-user/public deployments.
|
|
REQUIRE_API_KEY=false
|
|
|
|
# Allow revealing full API key values in the Dashboard UI.
|
|
# Used by: src/shared/constants/featureFlagDefinitions.ts — controls show/hide of key values.
|
|
# Also configurable from Dashboard > Settings > Feature Flags.
|
|
# Default: false | Security risk if enabled on shared instances.
|
|
ALLOW_API_KEY_REVEAL=false
|
|
|
|
# Shared secret for the internal Codex Responses WebSocket bridge.
|
|
# Used by: src/app/api/internal/codex-responses-ws/route.ts — authenticates
|
|
# bridge requests between the Electron/browser WS relay and OmniRoute.
|
|
# ⚠️ REQUIRED for production — if unset, all WS bridge requests are rejected.
|
|
# Generate: openssl rand -base64 32
|
|
# OMNIROUTE_WS_BRIDGE_SECRET=
|
|
|
|
# Per-process secret that proves the trusted peer-IP stamp came from OmniRoute's
|
|
# own HTTP server (scripts/dev/peer-stamp.mjs). The custom server stamps the real
|
|
# TCP peer IP as `<token>|<ip>`; the authz middleware trusts the locality only
|
|
# when the token matches. Used by: src/server/authz/policies/management.ts.
|
|
# Auto-generated per boot — leave UNSET in normal use. Only set it to pin a fixed
|
|
# value across processes (e.g. a multi-process setup that must share the stamp).
|
|
# OMNIROUTE_PEER_STAMP_TOKEN=
|
|
|
|
# Comma-separated API key IDs that skip request logging (GDPR/compliance).
|
|
# Used by: src/lib/compliance/index.ts — suppresses logs for specific keys.
|
|
# NO_LOG_API_KEY_IDS=key_abc123,key_def456
|
|
|
|
# Fallback per-day request budget applied to API keys whose `rate_limits`
|
|
# column is null. Default (unset/empty/malformed) preserves the legacy
|
|
# 1000/day, 5000/week, 20000/month windows so existing deployments do not
|
|
# silently lose rate limiting on upgrade.
|
|
# Set explicitly to "0" to opt out entirely (unlimited fallback). Any
|
|
# positive integer N enables N/day, 5N/week, 20N/month.
|
|
# Used by: src/shared/utils/apiKeyPolicy.ts — checkRateLimit() fallback.
|
|
# DEFAULT_RATE_LIMIT_PER_DAY=1000
|
|
|
|
# Maximum request body size in bytes (rejects larger payloads).
|
|
# Used by: src/shared/middleware/bodySizeGuard.ts — prevents oversized uploads.
|
|
# Default: 10485760 (10 MB)
|
|
# MAX_BODY_SIZE_BYTES=10485760
|
|
|
|
# Heap-pressure-aware admission for POST /v1/chat/completions (#5152). A large
|
|
# coding-agent "compact" body amplifies into hundreds of MB of transient JS objects
|
|
# on the combo path; concurrent compacts can stack past the V8 heap ceiling and OOM
|
|
# the process. These shed a LARGE body with 503 (Retry-After) only while the heap is
|
|
# already under pressure — healthy heap admits every body untouched.
|
|
# Used by: src/shared/middleware/chatBodyAdmission.ts
|
|
# Bodies below this size skip the guard entirely (heap not even sampled). Default 262144 (256 KB).
|
|
# OMNIROUTE_CHAT_LARGE_BODY_BYTES=262144
|
|
# Hard cap — bodies above this are rejected with 413 before any clone/parse. Default 52428800 (50 MB).
|
|
# OMNIROUTE_CHAT_HARD_MAX_BODY_BYTES=52428800
|
|
# Shed large bodies once heapUsed/heap_size_limit reaches this ratio (0<r<1). Default 0.75.
|
|
# OMNIROUTE_CHAT_HEAP_SHED_RATIO=0.75
|
|
|
|
# Hard cap (bytes) for a non-streaming upstream response buffered fully into memory
|
|
# (#5152). Past this the upstream reader is cancelled and the request fails fast
|
|
# instead of growing an unbounded string until the V8 heap is exhausted.
|
|
# Used by: open-sse/handlers/chatCore/nonStreamingResponseBody.ts
|
|
# Default: 67108864 (64 MB)
|
|
# OMNIROUTE_MAX_NONSTREAMING_RESPONSE_BYTES=67108864
|
|
|
|
# CORS configuration — controls which cross-origin browser clients can call the API.
|
|
# Used by: src/server/cors/origins.ts — sets Access-Control-Allow-Origin.
|
|
# Same-origin dashboard requests behind a reverse proxy do not need CORS; they
|
|
# use session-bound CSRF protection. No wildcard is sent unless CORS_ALLOW_ALL=true.
|
|
# CORS_ALLOWED_ORIGINS=https://your-frontend.example.com
|
|
# CORS_ORIGIN=https://your-frontend.example.com # legacy single-origin alias
|
|
# CORS_ALLOW_ALL=false
|
|
|
|
# Allow provider URLs pointing to private/local networks (localhost, 192.168.x.x, etc.).
|
|
# REQUIRED for self-hosted providers: LM Studio, Ollama, vLLM, Llamafile, Triton, etc.
|
|
# Used by: src/shared/network/outboundUrlGuard.ts — disables SSRF guard for provider calls.
|
|
# Default: false (blocked) | Set true to enable local providers.
|
|
# OMNIROUTE_ALLOW_PRIVATE_PROVIDER_URLS=true
|
|
|
|
# Allow adding/validating providers on local/private addresses (127.0.0.1, localhost, LAN).
|
|
# Used by: src/shared/network/outboundUrlGuard.ts — scopes to the provider validation path and
|
|
# still blocks cloud-metadata (169.254.169.254, metadata.google.internal). Default: true
|
|
# (OmniRoute is local-first). Set false to enforce strict public-only blocking.
|
|
# OMNIROUTE_ALLOW_LOCAL_PROVIDER_URLS=false
|
|
|
|
# Legacy alias toggling the SSRF guard. Used by: src/shared/network/outboundUrlGuard.ts
|
|
# When unset, OmniRoute uses the per-feature defaults. Set to "false"/"0" to disable.
|
|
# OUTBOUND_SSRF_GUARD_ENABLED=true
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 5. INPUT SANITIZATION & PII PROTECTION (FASE-01)
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# Multi-layer defense: request-side injection guard + response-side PII sanitizer.
|
|
|
|
# ── Request-Side: Prompt Injection Guard ──
|
|
# Scans incoming messages for prompt injection patterns before routing.
|
|
# Used by: src/middleware/promptInjectionGuard.ts
|
|
# INPUT_SANITIZER_ENABLED=true
|
|
# INPUT_SANITIZER_MODE=warn # warn = log only | block = reject request | redact = strip patterns
|
|
|
|
# Legacy alias for INPUT_SANITIZER_MODE (same effect).
|
|
# INJECTION_GUARD_MODE=warn
|
|
|
|
# PII detection in incoming requests (emails, phone numbers, SSNs, etc.).
|
|
# Used by: src/middleware/promptInjectionGuard.ts — extends injection guard.
|
|
# PII_REDACTION_ENABLED=false
|
|
|
|
# Minimum streaming window size for PII detection (bytes). Default: 200.
|
|
# Used by: src/lib/streamingPiiTransform.ts.
|
|
# PII_WINDOW_SIZE=200
|
|
|
|
# Test bypass: allow setting PII_WINDOW_SIZE below minimum. Default: false.
|
|
# Used by: src/lib/streamingPiiTransform.ts.
|
|
# PII_TEST_BYPASS_MIN_WINDOW=false
|
|
|
|
# ── Response-Side: PII Sanitizer ──
|
|
# Scans LLM responses for leaked PII before returning to the client.
|
|
# Used by: src/lib/piiSanitizer.ts
|
|
# PII_RESPONSE_SANITIZATION=false
|
|
# PII_RESPONSE_SANITIZATION_MODE=redact # redact = mask PII | warn = log only | block = drop response
|
|
|
|
# ── VS Code Tokenized-Route Context Sanitizer ──
|
|
# Strips implicit active-editor context (editorContext/activeEditor/currentFile/
|
|
# selection/openTabs…) from requests on the /v1/vscode/[token]/* routes before
|
|
# forwarding upstream, and redacts the content of explicitly-attached sensitive
|
|
# files (.env, private keys, kubeconfig, credentials/secrets). Explicit
|
|
# attachments otherwise pass through. Secure-by-default: ON unless set to 0.
|
|
# Used by: src/app/api/v1/vscode/contextSanitizer.ts
|
|
# OMNIROUTE_VSCODE_SANITIZE_CONTEXT=1 # set to 0 to disable
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 6. TOOL & ROUTING POLICIES
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
# Tool policy mode — controls which tools LLMs can invoke via function calling.
|
|
# Used by: src/lib/toolPolicy.ts — enforces allowlist/denylist on tool_choice.
|
|
# Values: allowlist | denylist | disabled | Default: disabled
|
|
# TOOL_POLICY_MODE=disabled
|
|
|
|
# Payload manipulation rules JSON file.
|
|
# Used by: open-sse/services/payloadRules.ts — injects/removes upstream payload fields per model/protocol.
|
|
# Default: ./config/payloadRules.json
|
|
# OMNIROUTE_PAYLOAD_RULES_PATH=./config/payloadRules.json
|
|
|
|
# Reload interval for payloadRules.json mtime checks in milliseconds.
|
|
# Used by: open-sse/services/payloadRules.ts — keeps file-based rules hot-reloadable without restart.
|
|
# Default: 5000 | Minimum: 1000
|
|
# OMNIROUTE_PAYLOAD_RULES_RELOAD_MS=5000
|
|
|
|
# Prefer Claude Code OAuth for unprefixed Claude-family model IDs such as
|
|
# claude-sonnet-4-6 or newly released IDs like claude-fable-5.
|
|
# Used by: open-sse/services/model.ts. Explicit provider prefixes still win.
|
|
# Default: false
|
|
# OMNIROUTE_PREFER_CLAUDE_CODE_FOR_UNPREFIXED_CLAUDE_MODELS=false
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 7. URLS & CLOUD SYNC
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# URLs used for internal sync jobs, OAuth callbacks, and cloud relay.
|
|
|
|
# Internal base URL — used by server-side sync jobs to call /api/sync/cloud.
|
|
# Keep this as a loopback/container URL even when the app is publicly proxied.
|
|
# Used by: src/lib/cloudSync.ts, src/lib/initCloudSync.ts
|
|
# Default: http://localhost:20128
|
|
BASE_URL=http://localhost:20128
|
|
|
|
# Cloud relay URL — premium feature for remote config sync.
|
|
# Used by: src/lib/cloudSync.ts — pushes/pulls settings from OmniRoute Cloud.
|
|
CLOUD_URL=
|
|
|
|
# Timeout for cloud sync HTTP requests in milliseconds.
|
|
# Used by: src/lib/cloudSync.ts — fetchWithTimeout wrapper.
|
|
# Default: 12000 (12 seconds)
|
|
# CLOUD_SYNC_TIMEOUT_MS=12000
|
|
|
|
# Public-facing base URL — required for stable reverse proxy / OAuth callback setups.
|
|
# Used by: OAuth redirect_uri computation, Dashboard UI links, and generated public URLs.
|
|
# Set to your stable public URL when OAuth callbacks or generated browser links need a
|
|
# canonical host behind nginx/Caddy (e.g., https://omniroute.example.com).
|
|
#
|
|
# Dashboard display behavior: when this variable is unset, the dashboard
|
|
# auto-detects the base URL shown in curl examples and CLI tool snippets
|
|
# from window.location.origin (the host the user is browsing). Setting it
|
|
# explicitly is only required when running behind a reverse proxy with a
|
|
# different public hostname, or when OAuth callbacks / generated browser links must point
|
|
# to a canonical URL. Authenticated dashboard writes use same-origin requests plus
|
|
# session-bound CSRF protection and do not require a static public base URL.
|
|
#
|
|
# Default: http://localhost:20128
|
|
NEXT_PUBLIC_BASE_URL=http://localhost:20128
|
|
|
|
# Browser-facing OmniRoute origin for generated assets in API responses.
|
|
# Highest-priority public origin override; also used by non-dashboard public-origin validation.
|
|
# Used by: chatgpt-web image generation cache URLs (/v1/chatgpt-web/image/<id>).
|
|
# Set this when OpenWebUI or another relay reaches OmniRoute by an internal URL
|
|
# but the user's browser must fetch images from a LAN, tunnel, or public origin.
|
|
# Do not include /v1; if included accidentally it will be normalized away.
|
|
# OMNIROUTE_PUBLIC_BASE_URL=http://192.168.0.15:20128
|
|
|
|
# Absolute provider plugin manifest URL advertised to sidecar clients.
|
|
# Used by: open-sse/config/providerPluginManifestUrl.ts. When unset, OmniRoute
|
|
# derives the URL from request origin or HOST/PORT using OMNIROUTE_PUBLIC_PROTOCOL.
|
|
# OMNIROUTE_PROVIDER_MANIFEST_URL=https://omniroute.example.com/api/v1/provider-plugin-manifest
|
|
|
|
# Protocol used when deriving provider plugin manifest URLs without a request origin.
|
|
# Used by: open-sse/config/providerPluginManifestUrl.ts. Defaults to http.
|
|
# OMNIROUTE_PUBLIC_PROTOCOL=http
|
|
|
|
# Max wait time for an async chatgpt-web image to land via the celsius
|
|
# WebSocket, in milliseconds. Default 180000 (3 minutes). Increase during
|
|
# upstream queue-deep windows ("Lots of people are creating images right now").
|
|
# OMNIROUTE_CGPT_WEB_IMAGE_TIMEOUT_MS=180000
|
|
|
|
# Total in-memory byte budget for the chatgpt-web image cache (used to serve
|
|
# /v1/chatgpt-web/image/<id>), in megabytes. Default 256. Lower this if you
|
|
# run OmniRoute on a memory-constrained host; raise it if image generation
|
|
# is heavy and clients are racing the 30-minute TTL.
|
|
# OMNIROUTE_CGPT_WEB_IMAGE_CACHE_MAX_MB=256
|
|
|
|
# Overall wait budget for a chatgpt-web GPT-5.5 Pro background-poll handoff,
|
|
# in milliseconds. Default 1200000 (20 minutes). Pro reasoning runs are slow
|
|
# and complete out-of-band, so OmniRoute polls until the answer lands or this
|
|
# budget elapses. Raise it if Pro requests time out before finishing.
|
|
# OMNIROUTE_CGPT_WEB_PRO_TIMEOUT_MS=1200000
|
|
|
|
# Interval between chatgpt-web GPT-5.5 Pro background-poll attempts, in
|
|
# milliseconds. Default 4000 (4 seconds). Lower for snappier completion at the
|
|
# cost of more upstream polling; raise to reduce request volume.
|
|
# OMNIROUTE_CGPT_WEB_PRO_POLL_INTERVAL_MS=4000
|
|
|
|
# Public cloud URL — client-side mirror of CLOUD_URL.
|
|
NEXT_PUBLIC_CLOUD_URL=
|
|
|
|
# Legacy alias — fallback for NEXT_PUBLIC_BASE_URL in sync schedulers.
|
|
# NEXT_PUBLIC_APP_URL=http://localhost:20128
|
|
|
|
# Advanced reverse-proxy trust mode for deriving public origin from Forwarded /
|
|
# X-Forwarded-* headers when no explicit public base URL is set. Prefer setting
|
|
# NEXT_PUBLIC_BASE_URL. Only enable if direct client access to OmniRoute is blocked
|
|
# and your proxy strips/rebuilds incoming forwarded headers.
|
|
# Values: true/loopback (trust loopback proxy peers), private/lan (also trust LAN peers).
|
|
# OMNIROUTE_TRUST_PROXY=
|
|
|
|
# Public callback URL for asynchronous image/audio jobs (kie.ai, etc.).
|
|
# Used by: open-sse/utils/kieTask.ts — overrides callbackUrlFromBaseUrl().
|
|
# Honor order: KIE_CALLBACK_URL → OMNIROUTE_KIE_CALLBACK_URL → OMNIROUTE_PUBLIC_URL.
|
|
#KIE_CALLBACK_URL=
|
|
#OMNIROUTE_KIE_CALLBACK_URL=
|
|
#OMNIROUTE_PUBLIC_URL=
|
|
|
|
# Headroom token-saver proxy URL. The dashboard lifecycle (api/headroom/*) spawns
|
|
# a local headroom-ai CLI on loopback by default; override only to point at an
|
|
# external Docker sidecar proxy. Defaults to http://localhost:8787 when unset.
|
|
# Used by: src/lib/headroom/detect.ts.
|
|
#HEADROOM_URL=http://localhost:8787
|
|
|
|
# Upstream quota endpoints used by the Usage page. Override only for
|
|
# debugging or when routing through a corporate mirror. Used by:
|
|
# open-sse/services/usage.ts.
|
|
#OMNIROUTE_CROF_USAGE_URL=https://crof.ai/usage_api/
|
|
#OMNIROUTE_CODEWHISPERER_BASE_URL=https://codewhisperer.us-east-1.amazonaws.com
|
|
#OMNIROUTE_OPENCODE_QUOTA_URL=https://opencode.ai/zen/go/v1/quota
|
|
#OMNIROUTE_OPENCODE_GO_QUOTA_URL=https://api.z.ai/api/monitor/usage/quota/limit
|
|
#OMNIROUTE_OPENCODE_GO_DASHBOARD_URL=https://opencode.ai/workspace
|
|
#OMNIROUTE_OLLAMA_CLOUD_USAGE_URL=https://ollama.com/settings
|
|
|
|
# OpenCode Go dashboard quota scraping. Prefer configuring these per connection
|
|
# in Dashboard → Providers → OpenCode Go. Env vars are useful for headless
|
|
# deployments or shared server defaults. The cookie is sensitive.
|
|
#OPENCODE_GO_WORKSPACE_ID=wrk_...
|
|
#OMNIROUTE_OPENCODE_GO_WORKSPACE_ID=wrk_...
|
|
#OPENCODE_GO_AUTH_COOKIE=auth=...
|
|
#OMNIROUTE_OPENCODE_GO_AUTH_COOKIE=auth=...
|
|
|
|
# OpenCode Go/Zen VPS egress (#5997): on a datacenter VPS, Cloudflare in front of
|
|
# opencode.ai/zen/go 403s chat requests that lack OpenCode CLI identity headers.
|
|
# When your clients don't already send them, set this to synthesize the CLI headers
|
|
# (User-Agent, x-opencode-client, x-opencode-project, fresh request/session UUIDs) on
|
|
# absent keys. OFF by default — forward-only is safer when clients already send them.
|
|
# Values are overridable via OPENCODE_GO_USER_AGENT / OPENCODE_USER_AGENT / OPENCODE_CLIENT /
|
|
# OPENCODE_PROJECT (defaults: opencode-cli/1.0.0 / cli / default).
|
|
#OPENCODE_SYNTHESIZE_CLI_HEADERS=true
|
|
#OPENCODE_USER_AGENT=opencode-cli/1.0.0
|
|
#OPENCODE_CLIENT=cli
|
|
#OPENCODE_PROJECT=default
|
|
|
|
# Ollama Cloud quota scraping. Prefer configuring this per connection in
|
|
# Dashboard → Providers → Ollama Cloud. The cookie is sensitive.
|
|
#OLLAMA_USAGE_COOKIE=__Secure-session=...
|
|
#OLLAMA_CLOUD_USAGE_COOKIE=__Secure-session=...
|
|
#OMNIROUTE_OLLAMA_USAGE_COOKIE=__Secure-session=...
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 8. OUTBOUND PROXY (Upstream Provider Calls)
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# Route upstream LLM API calls through an HTTP/SOCKS5 proxy.
|
|
# Useful for corporate egress, geo-routing, or IP masking.
|
|
|
|
# Enable SOCKS5 proxy support in both server and client components.
|
|
# Used by: open-sse/executors — wraps fetch() calls through the proxy agent.
|
|
ENABLE_SOCKS5_PROXY=true
|
|
NEXT_PUBLIC_ENABLE_SOCKS5_PROXY=true
|
|
|
|
# Standard proxy variables (lowercase variants also supported).
|
|
# HTTP_PROXY=http://127.0.0.1:7890
|
|
# HTTPS_PROXY=http://127.0.0.1:7890
|
|
# ALL_PROXY=socks5://127.0.0.1:7890
|
|
# NO_PROXY=localhost,127.0.0.1
|
|
|
|
# Max concurrent sockets per cached HTTP/SOCKS proxy dispatcher.
|
|
# Long-lived SSE streams such as Codex /v1/responses need more than one
|
|
# connection when multiple requests share the same account-level proxy.
|
|
# Set to 1 only for legacy diagnostics. Values above 256 are capped.
|
|
# OMNIROUTE_PROXY_DISPATCHER_CONNECTIONS=32
|
|
|
|
# SOCKS5 handshake (connect) timeout in ms (default 10000, capped at 120000).
|
|
# Raise it when a single residential gateway host is hit by high concurrency
|
|
# (e.g. 100 simultaneous requests): the real SOCKS5 handshake can exceed 10s
|
|
# under a saturated pool even though the proxy is reachable, which otherwise
|
|
# surfaces as a false "[Proxy Fast-Fail] Proxy unreachable".
|
|
# SOCKS_HANDSHAKE_TIMEOUT_MS=10000
|
|
|
|
# Proxy fail-open mode (default: false = fail-closed).
|
|
# When false, a request whose assigned proxy fails to resolve is REFUSED rather than
|
|
# falling back to a direct connection — prevents real-IP leaks in egress-controlled
|
|
# deployments. Set true to restore the legacy DIRECT fallback (legacy behaviour).
|
|
# Used by: src/sse/handlers/chatHelpers.ts
|
|
# PROXY_FAIL_OPEN=false
|
|
|
|
# TLS fingerprint spoofing (opt-in) — mimics Chrome 124 TLS handshake via wreq-js.
|
|
# Reduces risk of JA3/JA4 fingerprint-based blocking by providers (e.g., Google).
|
|
# Used by: open-sse/executors — replaces Node.js default TLS fingerprint.
|
|
# ENABLE_TLS_FINGERPRINT=true
|
|
|
|
# Allow the Claude Turnstile Playwright browser context to ignore HTTPS certificate errors.
|
|
# Only enable for local debugging or trusted MITM/corporate proxy environments.
|
|
# Used by: open-sse/services/claudeTurnstileSolver.ts
|
|
# OMNIROUTE_TURNSTILE_IGNORE_TLS_ERRORS=false
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 9. CLI TOOL INTEGRATION
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# Control how OmniRoute discovers and launches CLI sidecars (Claude, Codex, etc.).
|
|
# Used by: src/shared/services/cliRuntime.ts
|
|
|
|
# CLI discovery mode: auto = search PATH | manual = use explicit paths below.
|
|
# CLI_MODE=auto
|
|
|
|
# Additional PATH entries for finding CLI binaries (colon-separated).
|
|
# CLI_EXTRA_PATHS=/host-cli/bin:/usr/local/bin
|
|
|
|
# Home directory override for reading CLI config files (~/.claude, etc.).
|
|
# CLI_CONFIG_HOME=/root
|
|
|
|
# Allow OmniRoute to write CLI config files (token refresh, etc.).
|
|
# CLI_ALLOW_CONFIG_WRITES=true
|
|
|
|
# Auto-sync CLI profile files after provider model discovery changes. OPT-IN, default OFF for
|
|
# both. When enabled, writes only the tool's profile files (~/.codex/*.config.toml or
|
|
# ~/.claude/profiles/<name>/settings.json); never changes the active/default config. Both also
|
|
# require CLI_ALLOW_CONFIG_WRITES (default on). Toggle from the CLI Code dashboard, or set here.
|
|
# Leave unset to disable. (Feature flags — a DB/dashboard override takes precedence over env.)
|
|
# OMNIROUTE_AUTO_SYNC_CODEX_PROFILES=true
|
|
# OMNIROUTE_AUTO_SYNC_CLAUDE_PROFILES=true
|
|
|
|
# Override binary paths for individual CLI tools.
|
|
# CLI_CLAUDE_BIN=claude
|
|
# CLI_CODEX_BIN=codex
|
|
# CLI_DROID_BIN=droid
|
|
# CLI_OPENCLAW_BIN=openclaw
|
|
# CLI_CURSOR_BIN=agent
|
|
# CLI_CLINE_BIN=cline
|
|
# CLI_CONTINUE_BIN=cn
|
|
# CLI_QODER_BIN=qoder
|
|
# CLI_QWEN_BIN=qwen
|
|
# CLI_AUGGIE_BIN=auggie
|
|
# AUGGIE_BIN=auggie
|
|
|
|
# Override the Hermes Agent home directory (where OmniRoute reads/writes the
|
|
# Hermes CLI config). Matches the env var the Hermes PowerShell installer sets
|
|
# on Windows (%LOCALAPPDATA%\hermes); defaults to ~/.hermes when unset.
|
|
# Used by: src/lib/cli-helper/config-generator/hermesHome.ts
|
|
# HERMES_HOME=~/.hermes
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 10. INTERNAL AGENT & MCP INTEGRATIONS
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# Used by MCP server, A2A skills, and CLI sidecars to call the running instance.
|
|
|
|
# Explicit base URL for MCP/A2A tools to reach OmniRoute (overrides localhost auto-detect).
|
|
# For browser-visible generated image URLs, prefer OMNIROUTE_PUBLIC_BASE_URL above.
|
|
# Used by: open-sse/mcp-server/server.ts, src/lib/a2a/
|
|
# OMNIROUTE_BASE_URL=http://localhost:20128
|
|
|
|
# API key for internal tool calls (MCP tools, A2A skills).
|
|
# OMNIROUTE_API_KEY=
|
|
|
|
# API key ID for MCP audit logging.
|
|
# Used by: open-sse/mcp-server/audit.ts — tags audit events with a key identity.
|
|
# OMNIROUTE_API_KEY_ID=
|
|
|
|
# Legacy alias for OMNIROUTE_API_KEY.
|
|
# ROUTER_API_KEY=
|
|
|
|
# CLI remote-mode context/profile for `omniroute` commands (overrides the active
|
|
# context in the local contexts store). Equivalent to the `--context <name>` flag.
|
|
# Used by: bin/cli/program.mjs, bin/cli/api.mjs (remote mode).
|
|
# OMNIROUTE_CONTEXT=
|
|
|
|
# Enforce scope-based access control on MCP tool calls.
|
|
# Used by: open-sse/mcp-server/server.ts — rejects calls outside allowed scopes.
|
|
# OMNIROUTE_MCP_ENFORCE_SCOPES=false
|
|
|
|
# Comma-separated scopes granted to this MCP connection.
|
|
# Full list: admin, combos, health, models, routing, budget, metrics, pricing, memory, skills
|
|
# OMNIROUTE_MCP_SCOPES=admin,combos,health
|
|
|
|
# Compress MCP tool descriptions before serializing the manifest.
|
|
# Used by: open-sse/mcp-server/descriptionCompressor.ts — reduces token spend
|
|
# for clients that read the full tool catalog.
|
|
# Accepted disabling values: 0, false, off. Default: enabled.
|
|
# OMNIROUTE_MCP_COMPRESS_DESCRIPTIONS=1
|
|
|
|
# Algorithm/profile used when description compression is enabled.
|
|
# Used by: open-sse/mcp-server/descriptionCompressor.ts
|
|
# Set to 0/false/off to skip compression entirely. Default: rtk
|
|
# OMNIROUTE_MCP_DESCRIPTION_COMPRESSION=rtk
|
|
|
|
# Model catalog sync interval in hours.
|
|
# Used by: src/shared/services/modelSyncScheduler.ts — periodic model refresh.
|
|
# Default: 24
|
|
# MODEL_SYNC_INTERVAL_HOURS=24
|
|
|
|
# Provider limits sync interval in minutes (rate limit windows, quotas).
|
|
# Used by: src/server-init.ts — polls provider health endpoints.
|
|
# Default: 70
|
|
PROVIDER_LIMITS_SYNC_INTERVAL_MINUTES=70
|
|
|
|
# Gap (ms) between consecutive OAuth quota fetches in a bulk provider-limits sync.
|
|
# OAuth providers are fetched one at a time with this spacing so a single host
|
|
# never bursts simultaneous usage/refresh requests to the same upstream. Set to 0
|
|
# to opt out (restores fully concurrent fetches). Default: 1500
|
|
PROVIDER_LIMITS_SYNC_SPACING_MS=1500
|
|
|
|
# Min interval (ms) between consecutive UPSTREAM quota fetches on the per-request
|
|
# preflight/monitor path (e.g. Codex /wham/usage), complementing the bulk-sync
|
|
# spacing above. Many accounts on one IP fetching quota in the same second can look
|
|
# like automation to the upstream and get an OAuth token revoked (#6009). This gate
|
|
# serializes genuine network calls (cache hits are unaffected). Set to 0 to disable.
|
|
# Default: 250 (clamped 0..5000).
|
|
# OMNIROUTE_QUOTA_FETCH_MIN_INTERVAL_MS=250
|
|
|
|
# Delay (ms) before refreshing provider limits after a real usage event (e.g. a
|
|
# completed request). Gives the upstream quota API time to register the consumption
|
|
# before the dashboard polls. Default: 5000
|
|
#PROVIDER_LIMITS_POST_USAGE_REFRESH_DELAY_MS=5000
|
|
|
|
# Disable all background services (sync, pricing, model refresh).
|
|
# Used by: src/instrumentation-node.ts, src/lib/initCloudSync.ts
|
|
# Useful for: CI builds, test environments, or resource-constrained containers.
|
|
# OMNIROUTE_DISABLE_BACKGROUND_SERVICES=false
|
|
|
|
# Force runtime background tasks (healthchecks/sync) even under automated test
|
|
# detection. Used by: src/lib/config/runtimeSettings.ts — overrides the test
|
|
# heuristic in instrumentation-node.ts. Default: unset (tests skip background).
|
|
#OMNIROUTE_ENABLE_RUNTIME_BACKGROUND_TASKS=1
|
|
|
|
# Proactive connection-cooldown recovery (#8): re-validates connections whose
|
|
# transient `rate_limited_until` window has elapsed OUTSIDE the request hot path,
|
|
# so the first request after a cooldown does not pay the probe latency. Lazy
|
|
# recovery in getProviderCredentials still applies regardless. Used by:
|
|
# src/lib/quota/connectionRecovery.ts.
|
|
# Tick cadence (ms). Default 60000, floor 5000.
|
|
# OMNIROUTE_CONNECTION_RECOVERY_INTERVAL_MS=60000
|
|
# Disable the proactive recovery scheduler entirely (default: false).
|
|
# OMNIROUTE_DISABLE_CONNECTION_RECOVERY=false
|
|
|
|
# Background job interval for budget reset checks (ms). Default: 600000 (10m).
|
|
# Used by: src/lib/jobs/budgetResetJob.ts. Floor: 10000.
|
|
#OMNIROUTE_BUDGET_RESET_JOB_INTERVAL_MS=600000
|
|
|
|
# Emergency budget-exhaustion fallback (set false or 0 to disable the reroute to
|
|
# nvidia/openai/gpt-oss-120b when a request fails with a 402 budget error).
|
|
# Used by: open-sse/services/emergencyFallback.ts. Default: enabled.
|
|
#OMNIROUTE_EMERGENCY_FALLBACK=true
|
|
|
|
# Reasoning cache cleanup cadence (ms). Default: 1800000 (30m). Floor: 60000.
|
|
# Used by: src/lib/jobs/reasoningCacheCleanupJob.ts.
|
|
#OMNIROUTE_REASONING_CACHE_CLEANUP_INTERVAL_MS=1800000
|
|
|
|
# Spend write batcher cadence (ms) and buffer size before forced flush.
|
|
# Used by: src/lib/spend/batchWriter.ts. Defaults: 60000 ms / 1000 entries.
|
|
#OMNIROUTE_SPEND_FLUSH_INTERVAL_MS=60000
|
|
#OMNIROUTE_SPEND_MAX_BUFFER_SIZE=1000
|
|
|
|
# Batch request processor retry, backoff, and concurrency settings.
|
|
# Used by: open-sse/services/batchProcessor.ts. Defaults shown.
|
|
#BATCH_RETRY_DURATION_MS=86400000
|
|
#BATCH_BACKOFF_BASE_MS=5000
|
|
#BATCH_BACKOFF_MAX_MS=3600000
|
|
#BATCH_MAX_CONCURRENT=1
|
|
|
|
# Config hot-reload polling interval (ms). Default: 5000.
|
|
# Used by: src/lib/config/hotReload.ts. Lower than 1000ms is rejected.
|
|
#OMNIROUTE_CONFIG_HOT_RELOAD_MS=5000
|
|
|
|
# Override the migrations directory used by src/lib/db/migrationRunner.ts.
|
|
# Default: <repo>/src/lib/db/migrations.
|
|
#OMNIROUTE_MIGRATIONS_DIR=
|
|
|
|
# Mass-pending-migrations safety threshold (#3416). If more than this many
|
|
# migrations are pending on an existing DB, startup aborts (a wiped tracking
|
|
# table could cause data loss). Raise it to restore an older backup; set to 0
|
|
# to disable the check. Used by: src/lib/db/migrationRunner.ts. Default: 50.
|
|
#OMNIROUTE_MAX_PENDING_MIGRATIONS=50
|
|
|
|
# Trust user-managed RTK project filter rules without strict signature checks.
|
|
# Used by: open-sse/services/compression/engines/rtk/filterLoader.ts. Default: 0.
|
|
#OMNIROUTE_RTK_TRUST_PROJECT_FILTERS=0
|
|
|
|
# T02 stacked-pipeline engine circuit-breaker (OPT-IN, default off). When enabled, a compression
|
|
# engine that throws repeatedly across requests is skipped (fail-open) for a cooldown.
|
|
# Used by: open-sse/services/compression/pipelineEngineBreaker.ts.
|
|
#COMPRESSION_PIPELINE_BREAKER_ENABLED=false # master switch (default false)
|
|
#COMPRESSION_PIPELINE_BREAKER_THRESHOLD=3 # consecutive failures before the engine opens
|
|
#COMPRESSION_PIPELINE_BREAKER_COOLDOWN_MS=30000 # ms the engine stays skipped before a probe
|
|
|
|
# T08/H8 — CCR retrieval-feedback ramp factor. Each prior retrieval of a stored block raises its
|
|
# effective minChars linearly, so frequently-retrieved content is compressed progressively less
|
|
# (>= 3 retrievals = never compressed). 1 disables the ramp (binary skip at the threshold only).
|
|
# Used by: open-sse/services/compression/engines/ccr/index.ts. Default: 2.
|
|
#COMPRESSION_CCR_RETRIEVAL_RAMP_FACTOR=2
|
|
# T08/H5 — usage-observed prefix freeze (OPT-IN, default off). When enabled, a system prompt seen
|
|
# >= THRESHOLD times is treated as a stable cacheable prefix and preserved from compression even
|
|
# for providers the static cache-aware heuristic does not recognize (freeze = preserve, never
|
|
# mutates). Used by: open-sse/services/compression/prefixFreeze.ts.
|
|
#COMPRESSION_PREFIX_FREEZE_ENABLED=false # master switch (default false)
|
|
#COMPRESSION_PREFIX_FREEZE_THRESHOLD=3 # observations before a prefix is frozen
|
|
|
|
# Skip the postinstall native-runtime warm-up (useful in CI / headless installs). Default: 0.
|
|
# Used by: scripts/postinstall.mjs.
|
|
#OMNIROUTE_SKIP_POSTINSTALL=0
|
|
|
|
# Operator-supplied JSON credentials for the offline compression-eval CLI
|
|
# (parsed with JSON.parse; leave unset for a dry run). Developer tooling only.
|
|
# Used by: scripts/compression-eval/index.ts. Default: {} (empty).
|
|
#OMNIROUTE_EVAL_CREDENTIALS={}
|
|
|
|
# Skip the DB healthcheck entirely on startup (useful for short-lived tasks / tests).
|
|
# Used by: src/lib/db/core.ts, src/lib/db/healthCheck.ts. Set to 1 to disable. Default: 0.
|
|
#OMNIROUTE_SKIP_DB_HEALTHCHECK=0
|
|
|
|
# Force a DB healthcheck regardless of cadence. Default: 0.
|
|
# Used by: src/lib/db/core.ts::shouldRunDbHealthCheck().
|
|
#OMNIROUTE_FORCE_DB_HEALTHCHECK=0
|
|
|
|
# DB healthcheck cadence override (ms). Default: 21600000 (6h).
|
|
# Used by: src/lib/db/core.ts::getDbHealthCheckIntervalMs().
|
|
#OMNIROUTE_DB_HEALTHCHECK_INTERVAL_MS=21600000
|
|
|
|
# Skip the Redis-backed auth cache used by API key lookups (forces DB reads).
|
|
# Used by: src/lib/db/apiKeys.ts. Set to 1 to disable. Default: enabled.
|
|
#OMNIROUTE_DISABLE_REDIS_AUTH_CACHE=0
|
|
|
|
# Flag set by bootstrap script after initial setup is complete.
|
|
# Used by: src/app/(dashboard)/dashboard/page.tsx — shows setup wizard vs. dashboard.
|
|
# OMNIROUTE_BOOTSTRAPPED=false
|
|
|
|
# Allow request body to override the Antigravity project field.
|
|
# Used by: open-sse/executors/antigravity.ts — escape hatch for multi-project setups.
|
|
# OMNIROUTE_ALLOW_BODY_PROJECT_OVERRIDE=0
|
|
|
|
# Adjust how Antigravity advertises remaining credits. Used by:
|
|
# open-sse/services/antigravityCredits.ts — accepts forced override strings.
|
|
# Default: empty (use upstream-reported credits).
|
|
#ANTIGRAVITY_CREDITS=
|
|
|
|
# Override the path to the Antigravity CLI (agy) token file read by the
|
|
# "auto-detect local login" import. Used by:
|
|
# src/app/api/providers/agy-auth/apply-local/route.ts — for non-standard installs.
|
|
# Default: ~/.gemini/antigravity-cli/antigravity-oauth-token
|
|
#AGY_TOKEN_FILE=
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 11. OAUTH PROVIDER CREDENTIALS
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# Built-in default credentials for localhost development.
|
|
# For remote/VPS deployments, register your own at each provider's developer console.
|
|
# The bootstrap-env script auto-populates these in .env if missing.
|
|
# Can also be overridden via data/provider-credentials.json where supported.
|
|
|
|
# ── Claude Code (Anthropic) ──
|
|
CLAUDE_OAUTH_CLIENT_ID=9d1c250a-e61b-44d9-88ed-5944d1962f5e
|
|
# Custom redirect URI override for Claude OAuth callback.
|
|
# CLAUDE_CODE_REDIRECT_URI=https://platform.claude.com/oauth/code/callback
|
|
|
|
# ── Codex / OpenAI ──
|
|
CODEX_OAUTH_CLIENT_ID=app_EMoamEEZ73f0CkXaXp7hrann
|
|
|
|
# Milliseconds to wait between consecutive Codex token refreshes.
|
|
# Used by: open-sse/services/refreshSerializer.ts. Default: 0 (no spacing).
|
|
# CODEX_REFRESH_SPACING_MS=0
|
|
|
|
# ── Trae (ByteDance) ──
|
|
# Trae stream idle timeout (ms). Default: 300000 (5 min).
|
|
# Used by: open-sse/executors/trae.ts.
|
|
# TRAE_STREAM_TIMEOUT_MS=300000
|
|
|
|
# Trae OAuth token override. Used by: open-sse/executors/trae.ts.
|
|
# TRAE_TOKEN=
|
|
|
|
# ── The Old LLM (theoldllm) ──
|
|
# Playwright navigation timeout (ms) for the browser-backed token capture.
|
|
# Used by: open-sse/executors/theoldllm.ts. Default: 30000 (30s).
|
|
# THEOLDLLM_NAV_TIMEOUT_MS=30000
|
|
|
|
# ── Gemini / Antigravity / Windsurf (all Google-based) ──
|
|
# These providers ship public OAuth client_id/secret values (or Firebase Web
|
|
# keys) embedded in their public CLIs/binaries. Defaults are baked into the
|
|
# code via open-sse/utils/publicCreds.ts — leave the env vars unset to use
|
|
# them. Only set these if you registered your own OAuth app and want to use
|
|
# your own credentials instead. See docs/security/PUBLIC_CREDS.md for context.
|
|
#
|
|
# GEMINI_OAUTH_CLIENT_ID=
|
|
# GEMINI_OAUTH_CLIENT_SECRET=
|
|
# ANTIGRAVITY_OAUTH_CLIENT_ID=
|
|
# ANTIGRAVITY_OAUTH_CLIENT_SECRET=
|
|
# WINDSURF_FIREBASE_API_KEY=
|
|
|
|
# ── Qwen (Alibaba) ──
|
|
QWEN_OAUTH_CLIENT_ID=f0304373b74a44d2b584a3fb70ca9e56
|
|
|
|
# ── Kimi Coding (Moonshot) ──
|
|
KIMI_CODING_OAUTH_CLIENT_ID=17e5f671-d194-4dfb-9706-5516cb48c098
|
|
|
|
# ── GitHub Copilot ──
|
|
GITHUB_OAUTH_CLIENT_ID=Iv1.b507a08c87ecfe98
|
|
|
|
# ── GitLab Duo ──
|
|
# Register an OAuth app at: https://gitlab.com/-/profile/applications
|
|
# Set redirect URI to: http://localhost:20128/callback (or your NEXT_PUBLIC_BASE_URL + /callback)
|
|
# Required scopes: ai_features, read_user (matches GITLAB_DUO_CONFIG.scope in src/lib/oauth/constants/oauth.ts)
|
|
# GITLAB_DUO_OAUTH_CLIENT_ID=***
|
|
# GITLAB_DUO_OAUTH_CLIENT_SECRET=*** # optional — PKCE flow does not require a secret
|
|
#
|
|
# Self-managed GitLab Duo instance overrides.
|
|
# Used by: src/lib/oauth/gitlab.ts and src/lib/oauth/constants/oauth.ts —
|
|
# fall back to these when the _DUO_ variants above are unset.
|
|
#GITLAB_DUO_BASE_URL=https://gitlab.com
|
|
#GITLAB_BASE_URL=https://gitlab.com
|
|
#GITLAB_OAUTH_CLIENT_ID=
|
|
#GITLAB_OAUTH_CLIENT_SECRET=
|
|
|
|
# ── Qoder ──
|
|
# Public OAuth client secret embedded in the Qoder CLI binary. Required only
|
|
# when QODER_OAUTH_AUTHORIZE_URL / TOKEN_URL / USERINFO_URL / CLIENT_ID are
|
|
# also set (see QODER_CONFIG.enabled in src/lib/oauth/constants/oauth.ts).
|
|
# Extract the value from the public Qoder CLI binary if you intend to use it.
|
|
# QODER_OAUTH_CLIENT_SECRET=
|
|
|
|
# ── Qoder Browser OAuth (experimental) ──
|
|
# OmniRoute only enables the browser OAuth flow when ALL 5 variables below are set:
|
|
# - QODER_OAUTH_AUTHORIZE_URL
|
|
# - QODER_OAUTH_TOKEN_URL
|
|
# - QODER_OAUTH_USERINFO_URL
|
|
# - QODER_OAUTH_CLIENT_ID
|
|
# - QODER_OAUTH_CLIENT_SECRET
|
|
#
|
|
# Redirect URI to register in the Qoder OAuth app:
|
|
# - Localhost dev with PORT=20128: http://localhost:20128/callback
|
|
# - LAN access (example): http://192.168.0.15:20128/callback
|
|
# - Public domain (recommended): https://omniroute.example.com/callback
|
|
#
|
|
# Behind reverse proxy / public domain, also set NEXT_PUBLIC_BASE_URL to the same public origin.
|
|
# If these values are not available, prefer QODER_PERSONAL_ACCESS_TOKEN below.
|
|
# QODER_OAUTH_AUTHORIZE_URL=
|
|
# QODER_OAUTH_TOKEN_URL=
|
|
# QODER_OAUTH_USERINFO_URL=
|
|
# QODER_OAUTH_CLIENT_ID=
|
|
# QODER_OAUTH_CLIENT_SECRET=
|
|
|
|
# ── Qoder Personal Access Token (direct API key fallback) ──
|
|
# Used by: open-sse/executors/qoder.ts — bypasses OAuth when set.
|
|
# QODER_PERSONAL_ACCESS_TOKEN=
|
|
# QODER_CLI_WORKSPACE=
|
|
# OMNIROUTE_QODER_WORKSPACE=
|
|
# Override the Qoder CLI config dir (isolated PAT session, avoids clobbering a browser login).
|
|
# QODER_CLI_CONFIG_DIR=
|
|
|
|
# ── Blackbox Web validated-token override (issue #2252) ──
|
|
# Used by: open-sse/executors/blackbox-web.ts. Blackbox `/api/chat` rejects
|
|
# requests whose `validated` field doesn't match the frontend `tk` token,
|
|
# returning HTTP 403 even with a valid session cookie + active subscription.
|
|
# Set this to the `tk` value exported from app.blackbox.ai's Next.js bundle
|
|
# to bypass the random-UUID fallback. Leave empty to keep the legacy behavior.
|
|
# BLACKBOX_WEB_VALIDATED_TOKEN=
|
|
|
|
# ── Vision Bridge OpenAI-compatible endpoint override (issue #2232) ──
|
|
# Used by: src/lib/guardrails/visionBridgeHelpers.ts. By default the
|
|
# vision-bridge guardrail sends non-Anthropic image-description calls to
|
|
# `https://api.openai.com/v1`, which fails with 401 if your operator doesn't
|
|
# have an OpenAI key or wants to use a different vision model
|
|
# (e.g., `google/gemini-2.0-flash` via the Gemini OpenAI-compat endpoint, or
|
|
# any model registered in OmniRoute via the self-loop endpoint).
|
|
#
|
|
# Set these two env vars to point the bridge at any OpenAI-compatible URL:
|
|
# - VISION_BRIDGE_BASE_URL=http://localhost:20128/v1 (OmniRoute self-loop)
|
|
# - VISION_BRIDGE_BASE_URL=https://generativelanguage.googleapis.com/v1beta/openai
|
|
# - VISION_BRIDGE_BASE_URL=https://openrouter.ai/api/v1
|
|
# Anthropic models (anthropic/*) keep their dedicated path and are unaffected.
|
|
# VISION_BRIDGE_BASE_URL=
|
|
# VISION_BRIDGE_API_KEY=
|
|
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# ⚠️ GOOGLE OAUTH (Antigravity) & OTHER PROVIDERS — REMOTE SERVERS
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
# The default Client IDs above ONLY work when OmniRoute runs on localhost.
|
|
# For remote/VPS hosting (including Docker containers on remote servers):
|
|
# 1. By default, the browser will attempt OAuth redirects back to localhost, which will fail.
|
|
# 2. Set NEXT_PUBLIC_BASE_URL=https://your-domain.com to fix the redirect URI.
|
|
# 3. You MUST create your own OAuth App in each provider's developer console (Google Cloud, etc.)
|
|
# and set the Authorized redirect URI to your domain (e.g., https://your-domain.com/callback).
|
|
# 4. Replace the _OAUTH_CLIENT_ID and _SECRET values above with your own credentials.
|
|
# ─────────────────────────────────────────────────────────────────────────────
|
|
|
|
# ── OAuth sidecar/CLI bridge (internal) ──
|
|
# Used by: src/lib/oauth/config/index.ts — internal CLI↔OmniRoute auth bridge.
|
|
# OMNIROUTE_SERVER=http://localhost:20128
|
|
# OMNIROUTE_TOKEN=
|
|
# OMNIROUTE_USER_ID=cli
|
|
# CLI_TOKEN= # legacy alias for OMNIROUTE_TOKEN
|
|
# CLI_USER_ID= # legacy alias for OMNIROUTE_USER_ID
|
|
# SERVER_URL= # legacy alias for OMNIROUTE_SERVER
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 12. PROVIDER USER-AGENT OVERRIDES
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# Customize the User-Agent header sent to each upstream provider.
|
|
# Format: {PROVIDER_ID}_USER_AGENT=custom-value
|
|
# Used by: open-sse/executors/base.ts — buildHeaders() dynamic lookup.
|
|
# Update these when providers release new CLI versions to avoid blocks.
|
|
|
|
CLAUDE_USER_AGENT="claude-cli/2.1.195 (external, cli)"
|
|
|
|
# Disable the deterministic tool-name cloak applied on both Anthropic-bound paths
|
|
# (executors/base.ts native OAuth + executors/cliproxyapi.ts CLIProxyAPI) —
|
|
# third-party-harness tool names are aliased to
|
|
# Claude Code canonical or PascalCase forms so Anthropic does not refuse the
|
|
# stream with a misleading 400 out-of-extra-usage placeholder. Set to true to
|
|
# forward the original names verbatim (debugging only).
|
|
# CLAUDE_DISABLE_TOOL_NAME_CLOAK=false
|
|
CODEX_USER_AGENT="codex-cli/0.142.0 (Windows 10.0.26200; x64)"
|
|
GITHUB_USER_AGENT="GitHubCopilotChat/0.54.0"
|
|
ANTIGRAVITY_USER_AGENT="antigravity/2.0.1 linux/arm64 google-api-nodejs-client/10.3.0"
|
|
KIRO_USER_AGENT="AWS-SDK-JS/3.0.0 kiro-ide/1.0.0"
|
|
# KIRO_VERIFY_FULL_CRC=false # opt-in: full per-frame message CRC validation on the Kiro event stream (debug corrupted streams; prelude CRC + TLS already protect framing)
|
|
# Optional override for the Kiro social device-code OAuth clientId. Kiro's
|
|
# device endpoint accepts any non-empty string and behaves like a User-Agent
|
|
# rather than a secret. Only override if AWS ever starts enforcing this field.
|
|
# Used by: src/lib/oauth/constants/oauth.ts (KIRO_CONFIG.socialClientId).
|
|
# KIRO_OAUTH_CLIENT_ID=kiro-cli
|
|
# Enable full per-frame message CRC validation for Kiro streams. Off by default
|
|
# because it is O(frame bytes) on the main thread; use only for debugging
|
|
# suspected corrupted-stream issues.
|
|
# Used by: open-sse/executors/kiro.ts
|
|
# KIRO_VERIFY_FULL_CRC=false
|
|
QODER_USER_AGENT="Qoder-Cli"
|
|
QWEN_USER_AGENT="QwenCode/0.19.3 (linux; x64)"
|
|
CURSOR_USER_AGENT="Cursor/3.4"
|
|
|
|
# Override Codex client version sent in headers independently of the
|
|
# CODEX_USER_AGENT string. Used by: open-sse/config/codexClient.ts.
|
|
# CODEX_CLIENT_VERSION=0.142.0
|
|
|
|
# Kill-switch to strip non-standard `codex.*` SSE events (e.g. codex.rate_limits)
|
|
# from the Codex Responses stream. These frames break the OpenAI SDK's
|
|
# responses.stream() with a 502 "Controller is already closed". Off by default;
|
|
# set to true/1/yes to enable. Used by: open-sse/executors/codex.ts.
|
|
# OMNIROUTE_CODEX_DROP_NONSTANDARD_EVENTS=true
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 13. CLI FINGERPRINT COMPATIBILITY (Anti-Detection)
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# When enabled, OmniRoute reorders HTTP headers and JSON body fields to match
|
|
# the exact signature of official CLI tools, reducing account flagging risk.
|
|
# Your proxy IP is preserved — you get both stealth AND IP masking.
|
|
# Used by: open-sse/config/cliFingerprints.ts, open-sse/executors/base.ts
|
|
|
|
# Enable per-provider:
|
|
# CLI_COMPAT_CODEX=1
|
|
# CLI_COMPAT_CLAUDE=1
|
|
# CLI_COMPAT_GITHUB=1
|
|
# CLI_COMPAT_ANTIGRAVITY=1
|
|
# CLI_COMPAT_CURSOR=1
|
|
# CLI_COMPAT_KIMI_CODING=1
|
|
# CLI_COMPAT_KILOCODE=1
|
|
# CLI_COMPAT_CLINE=1
|
|
# CLI_COMPAT_QWEN=1
|
|
|
|
# Or enable for all providers at once:
|
|
# CLI_COMPAT_ALL=1
|
|
|
|
# ── Kimi Coding CLI identity overrides ──
|
|
# Used by: src/lib/oauth/providers/kimi-coding.ts — sent in OAuth + API headers.
|
|
# Leave unset to use the captured defaults baked into the OmniRoute build.
|
|
#KIMI_CLI_VERSION=1.36.0
|
|
#KIMI_CODING_DEVICE_ID=
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 14. API KEY PROVIDERS
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# API keys for direct-authentication providers.
|
|
# Preferred setup: Dashboard → Providers → Add API Key.
|
|
# Setting here is an alternative for Docker/headless deployments.
|
|
|
|
# Static API keys for direct-authentication providers wired through the runtime.
|
|
# OmniRoute loads provider credentials from the encrypted database or
|
|
# data/provider-credentials.json. The variables below are documented escape
|
|
# hatches that are referenced in code today.
|
|
# DEEPSEEK_API_KEY=
|
|
# NVIDIA_API_KEY=
|
|
|
|
# Windsurf / Devin CLI direct API key.
|
|
# Used by: open-sse/executors/devin-cli.ts — bypasses OAuth when set.
|
|
# WINDSURF_API_KEY=
|
|
|
|
# Embedding Providers (optional — used by /v1/embeddings)
|
|
# OpenAI/Mistral/Together/Fireworks/NVIDIA configured via Dashboard → Providers
|
|
# also work for embeddings.
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 15. TIMEOUT SETTINGS
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# All timeout values are in milliseconds.
|
|
# Used by: src/shared/utils/runtimeTimeouts.ts — centralized timeout resolution.
|
|
#
|
|
# Hierarchy: REQUEST_TIMEOUT_MS acts as a global override.
|
|
# If set, it becomes the default for FETCH_TIMEOUT_MS, STREAM_IDLE_TIMEOUT_MS,
|
|
# and STREAM_READINESS_TIMEOUT_MS.
|
|
# The fine-grained variables below override their respective defaults only when set.
|
|
|
|
# ── Global shortcut ──
|
|
# REQUEST_TIMEOUT_MS=600000 # Overrides both fetch and stream idle defaults
|
|
|
|
# ── Upstream fetch (provider calls) ──
|
|
# FETCH_TIMEOUT_MS=600000 # Total request timeout (default: 600000 = 10 min)
|
|
# # Also drives anthropic-compatible-cc-* X-Stainless-Timeout.
|
|
# FETCH_HEADERS_TIMEOUT_MS=600000 # Time to receive response headers
|
|
# FETCH_BODY_TIMEOUT_MS=600000 # Time to receive full response body
|
|
# FETCH_CONNECT_TIMEOUT_MS=30000 # TCP connection establishment (default: 30s)
|
|
# FETCH_KEEPALIVE_TIMEOUT_MS=4000 # Keep-alive socket idle timeout (default: 4s)
|
|
|
|
# Default timeout (ms) for src/shared/utils/fetchTimeout.ts. Acts as the
|
|
# fallback when FETCH_TIMEOUT_MS is unset. Default: 120000 (2 min).
|
|
# OMNIROUTE_DEFAULT_FETCH_TIMEOUT_MS=120000
|
|
|
|
# ── Firecrawl web-fetch executor ──
|
|
# Point at a self-hosted Firecrawl instance (defaults to the public cloud API).
|
|
# When set to a non-cloud base URL, the API key becomes optional.
|
|
# FIRECRAWL_BASE_URL=https://api.firecrawl.dev
|
|
# FIRECRAWL_TIMEOUT_MS=30000 # Per-request timeout (default: 30000 = 30s)
|
|
|
|
# ── ChatGPT TLS sidecar (Firefox-fingerprinted client) ──
|
|
# Used by: open-sse/services/chatgptTlsClient.ts — wire-level timeout for
|
|
# the bogdanfinn/tls-client koffi binding and the JS-side grace window
|
|
# layered on top of it when the native library is wedged.
|
|
# OMNIROUTE_CHATGPT_TLS_TIMEOUT_MS=60000
|
|
# OMNIROUTE_CHATGPT_TLS_GRACE_MS=10000
|
|
# Max wait for the FIRST streamed byte from the ChatGPT TLS sidecar before the
|
|
# request is aborted as a dead stream, in milliseconds. Default 30000 (30s).
|
|
# Raise it if upstream cold-starts routinely exceed the window.
|
|
# OMNIROUTE_CHATGPT_STREAM_FIRST_BYTE_TIMEOUT_MS=30000
|
|
|
|
# ── Claude TLS sidecar (Chromium-fingerprinted client) ──
|
|
# Used by: open-sse/services/claudeTlsClient.ts — wire-level timeout for
|
|
# the bogdanfinn/tls-client koffi binding and the JS-side grace window
|
|
# layered on top of it when the native library is wedged.
|
|
# OMNIROUTE_CLAUDE_TLS_TIMEOUT_MS=60000
|
|
# OMNIROUTE_CLAUDE_TLS_GRACE_MS=10000
|
|
|
|
# ── Perplexity TLS sidecar (Firefox-fingerprinted client) ──
|
|
# Used by: open-sse/services/perplexityTlsClient.ts — wire-level timeout for
|
|
# the bogdanfinn/tls-client koffi binding and the JS-side grace window
|
|
# layered on top of it when the native library is wedged.
|
|
# OMNIROUTE_PPLX_TLS_TIMEOUT_MS=30000
|
|
# OMNIROUTE_PPLX_TLS_GRACE_MS=10000
|
|
|
|
# ── Grok web TLS sidecar (Chrome-fingerprinted client) ──
|
|
# Used by: open-sse/services/grokTlsClient.ts — wire-level timeout for the
|
|
# bogdanfinn/tls-client koffi binding and the JS-side grace window layered on
|
|
# top of it when the native library is wedged.
|
|
# OMNIROUTE_GROK_TLS_TIMEOUT_MS=60000
|
|
# OMNIROUTE_GROK_TLS_GRACE_MS=10000
|
|
|
|
# ── Browser-backed web-cookie chat (Playwright shared pool) ──
|
|
# Used by: open-sse/services/browserPool.ts + browserBackedChat.ts. The shared
|
|
# browser pool warms a headless context for web-cookie providers (e.g. claude-web)
|
|
# that need a real browser to satisfy anti-bot challenges. Set OMNIROUTE_BROWSER_POOL=off
|
|
# to fully disable the pool; set WEB_COOKIE_USE_BROWSER=1 to opt a web-cookie chat
|
|
# request into the browser-backed path.
|
|
# OMNIROUTE_BROWSER_POOL=on
|
|
# WEB_COOKIE_USE_BROWSER=0
|
|
|
|
# ── Circuit breaker thresholds and reset windows ──
|
|
# Used by: open-sse/config/constants.ts → src/lib/resilience/settings.ts.
|
|
# Defaults match historical PROVIDER_PROFILES values (post-scaling for
|
|
# 500+ connections). Lower the threshold to react faster, raise it to
|
|
# tolerate more transient failures before short-circuiting.
|
|
# OMNIROUTE_CIRCUIT_BREAKER_OAUTH_THRESHOLD=8
|
|
# OMNIROUTE_CIRCUIT_BREAKER_OAUTH_RESET_MS=60000
|
|
# OMNIROUTE_CIRCUIT_BREAKER_API_KEY_THRESHOLD=12
|
|
# OMNIROUTE_CIRCUIT_BREAKER_API_KEY_RESET_MS=30000
|
|
# OMNIROUTE_CIRCUIT_BREAKER_LOCAL_THRESHOLD=2
|
|
# OMNIROUTE_CIRCUIT_BREAKER_LOCAL_RESET_MS=15000
|
|
|
|
# ── Context-cache pin health gate ──
|
|
# Used by: open-sse/services/combo.ts. When a context-cache pin points at a
|
|
# provider that is durably unhealthy, the pin is dropped to allow failover.
|
|
# PIN_DROP_BACKOFF_LEVEL gates how deep a connection's backoff must be before the
|
|
# pin is considered durably unhealthy; PIN_DROP_GRACE_MS is the anti-flap window
|
|
# that tolerates brief transient cooldowns before dropping the pin.
|
|
# PIN_DROP_BACKOFF_LEVEL=2
|
|
# PIN_DROP_GRACE_MS=20000
|
|
|
|
# ── Stream idle detection ──
|
|
# STREAM_IDLE_TIMEOUT_MS=600000 # Max silence between SSE chunks (default: 600000)
|
|
# # Extended-thinking models rarely pause >90s.
|
|
# STREAM_READINESS_TIMEOUT_MS=80000 # Time to receive the first non-ping SSE event
|
|
# STREAM_READINESS_MAX_TIMEOUT_MS=180000 # Cap for adaptive first-event extensions
|
|
# # (large/tool-heavy/high-reasoning requests).
|
|
# OMNIROUTE_AGENT_GOAL_POLICY_ENABLED=true # Kill-switch for the /goal heuristic below.
|
|
# # Set to false to fully disable detection —
|
|
# # readiness timeouts and stream recovery are
|
|
# # never elevated by request body/headers when off.
|
|
# OMNIROUTE_AGENT_GOAL_READINESS_MAX_TIMEOUT_MS=600000 # Auto cap for detected /goal agent runs
|
|
# OMNIROUTE_AGENT_GOAL_STREAM_RECOVERY=true # Auto early stream recovery for /goal runs.
|
|
# # NOTE: this can only ADD recovery on top of the
|
|
# # operator default — it never overrides an explicit
|
|
# # STREAM_RECOVERY_ENABLED / DB settings opt-out.
|
|
|
|
# ── TLS client (wreq-js fingerprint proxy) ──
|
|
# TLS_CLIENT_TIMEOUT_MS=600000 # Inherits from FETCH_TIMEOUT_MS by default
|
|
|
|
# ── API Bridge (/v1 proxy server) ──
|
|
# API_BRIDGE_PROXY_TIMEOUT_MS=600000 # Proxy hop timeout (default: 10min)
|
|
# API_BRIDGE_SERVER_REQUEST_TIMEOUT_MS=600000 # Overall server request timeout (default: 10min)
|
|
# API_BRIDGE_SERVER_HEADERS_TIMEOUT_MS=60000 # Time to send response headers
|
|
# API_BRIDGE_SERVER_KEEPALIVE_TIMEOUT_MS=5000 # Keep-alive idle timeout
|
|
# API_BRIDGE_SERVER_SOCKET_TIMEOUT_MS=0 # Raw socket timeout (0 = disabled)
|
|
|
|
# ── Graceful shutdown ──
|
|
# Time to wait for in-flight requests before force-exiting on SIGTERM/SIGINT.
|
|
# Used by: src/lib/gracefulShutdown.ts
|
|
# Default: 30000 (30 seconds)
|
|
# SHUTDOWN_TIMEOUT_MS=30000
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 16. LOGGING
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# Used by: src/lib/logEnv.ts, src/lib/logRotation.ts, src/shared/utils/logger.ts
|
|
|
|
# Application log level — controls console and file log verbosity.
|
|
# Values: debug | info | warn | error | Default: info
|
|
# APP_LOG_LEVEL=info
|
|
|
|
# Log output format.
|
|
# Values: text | json | Default: text
|
|
# APP_LOG_FORMAT=text
|
|
|
|
# Write logs to file in addition to stdout.
|
|
# Default: true | Set false to disable file logging.
|
|
APP_LOG_TO_FILE=true
|
|
|
|
# Path to the application log file.
|
|
# Default: <DATA_DIR>/logs/application/app.log (DATA_DIR defaults to ~/.omniroute)
|
|
# APP_LOG_FILE_PATH=logs/application/app.log
|
|
|
|
# Maximum single log file size before rotation.
|
|
# Accepts: plain bytes or suffixed (50M, 1G, 512K). Default: 50M
|
|
# APP_LOG_MAX_FILE_SIZE=50M
|
|
|
|
# Days to keep rotated application log files before auto-deletion.
|
|
# Default: 7
|
|
# APP_LOG_RETENTION_DAYS=7
|
|
|
|
# Maximum number of rotated log file backups to keep.
|
|
# Default: 20
|
|
# APP_LOG_MAX_FILES=20
|
|
|
|
# How often OmniRoute checks whether the active log file has exceeded
|
|
# APP_LOG_MAX_FILE_SIZE and triggers a rotation. Set lower for very verbose
|
|
# services to prevent log files from growing large between checks.
|
|
# Accepts milliseconds. Default: 60000 (1 minute)
|
|
# APP_LOG_ROTATION_CHECK_INTERVAL_MS=60000
|
|
|
|
# Days to keep request/call log entries in the database before auto-cleanup.
|
|
# Default: 7
|
|
# CALL_LOG_RETENTION_DAYS=7
|
|
|
|
# Maximum call log entries stored in-memory buffer.
|
|
# Default: 10000
|
|
# CALL_LOG_MAX_ENTRIES=10000
|
|
|
|
# Maximum rows in the call_logs SQLite table before oldest entries are pruned.
|
|
# Default: 100000
|
|
# CALL_LOGS_TABLE_MAX_ROWS=100000
|
|
|
|
# Maximum age for orphaned active request log entries before the in-memory
|
|
# pending-request reaper removes them. Accepts milliseconds.
|
|
# Default: 3600000 (1 hour)
|
|
# MAX_PENDING_REQUEST_AGE_MS=3600000
|
|
|
|
# Whether call log pipeline capture stores stream chunks when enabled in settings.
|
|
# Only applies when call_log_pipeline_enabled=true.
|
|
# Default: true
|
|
# CALL_LOG_PIPELINE_CAPTURE_STREAM_CHUNKS=true
|
|
|
|
# Maximum call log artifact size for pipeline captures, in KB.
|
|
# Only applies when call_log_pipeline_enabled=true.
|
|
# Default: 512
|
|
# CALL_LOG_PIPELINE_MAX_SIZE_KB=512
|
|
|
|
# Call log payload truncation limits — controls how much of request/response
|
|
# bodies is retained in the database.
|
|
# Used by: open-sse/handlers/chatCore.ts — cloneBoundedChatLogPayload()
|
|
# CHAT_LOG_TEXT_LIMIT=65536 # Max string length before truncation (default: 64 KB)
|
|
# CHAT_LOG_ARRAY_TAIL_ITEMS=24 # Number of array items retained from tail (default: 24)
|
|
# CHAT_LOG_MAX_DEPTH=6 # Max nesting depth before truncation (default: 6)
|
|
# CHAT_LOG_MAX_OBJECT_KEYS=80 # Max object keys retained (default: 80, 0 = no limit)
|
|
|
|
# Maximum rows in the proxy_logs SQLite table.
|
|
# Default: 100000
|
|
# PROXY_LOGS_TABLE_MAX_ROWS=100000
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 17. MEMORY OPTIMIZATION (Low-RAM / Docker)
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
# Node.js V8 heap limit in MB, passed to the server via --max-old-space-size.
|
|
# Used by the standalone launcher (Docker CMD) and `omniroute serve`.
|
|
# Clamped to [64, 16384]. Default: 512 (safe for a 1 GB / 1 core VPS). Size it to
|
|
# roughly half the box's RAM, leaving the rest for native memory (better-sqlite3,
|
|
# buffers — ~300 MB) and the OS:
|
|
# 1 GB RAM → 512 (default)
|
|
# 2 GB RAM → 1024
|
|
# 4 GB RAM → 2048
|
|
# In a memory-capped container, set this EXPLICITLY: Node reads the HOST's RAM,
|
|
# not the cgroup limit, so leaving it to a RAM heuristic can oversize the heap and
|
|
# get the container OOM-killed. (#2939)
|
|
# OMNIROUTE_MEMORY_MB=512
|
|
|
|
# Heap-pressure shed threshold (MB) — chatCore returns 503 when V8 heapUsed exceeds
|
|
# it, to avoid hard OOM under concurrent large-context load.
|
|
# LEAVE UNSET: it now AUTO-CALIBRATES to 85% of the actual V8 heap ceiling, so it
|
|
# tracks OMNIROUTE_MEMORY_MB above and never sits below the ~260 MB runtime baseline
|
|
# (a fixed 200 here used to reject every request). Used by: open-sse/utils/heapPressure.ts.
|
|
# Override only to hand-tune for a known workload.
|
|
# HEAP_PRESSURE_THRESHOLD_MB=
|
|
|
|
# ── CLI helpers (bin/cli/) ──
|
|
# Override UI language for CLI output. Accepts BCP-47 locale (e.g. en, pt-BR).
|
|
# Falls back to LC_ALL / LC_MESSAGES / LANG / en if unset.
|
|
# OMNIROUTE_LANG=en
|
|
|
|
# Show server logs inline when running in supervised mode (omniroute serve).
|
|
# Set to "1" to forward server stdout/stderr to the terminal.
|
|
# Equivalent to the --log flag on `omniroute serve`.
|
|
# OMNIROUTE_SHOW_LOG=1
|
|
|
|
# Bearer token injected as x-omniroute-cli-token header for machine-auth (task 8.12).
|
|
# Auto-generated on first run if machine-id is available; set manually to override.
|
|
# OMNIROUTE_CLI_TOKEN=
|
|
|
|
# Per-attempt HTTP timeout for CLI → server calls (milliseconds). Default: 30000.
|
|
# OMNIROUTE_HTTP_TIMEOUT_MS=30000
|
|
|
|
# Set to 1 to print retry/backoff details to stderr during CLI commands.
|
|
# OMNIROUTE_VERBOSE=0
|
|
|
|
# Custom directory for CLI plugin discovery (omniroute-cmd-* packages).
|
|
# Default: ~/.omniroute/plugins/ Override in dev/CI to point at a local plugin tree.
|
|
# OMNIROUTE_PLUGIN_PATH=
|
|
|
|
# Allow plugins to request the 'exec' permission (spawn child processes from the
|
|
# plugin worker sandbox). Disabled by default; set to 1 to enable (local operator only).
|
|
# OMNIROUTE_PLUGINS_ALLOW_EXEC=0
|
|
|
|
# ── Prompt cache (system prompt deduplication) ──
|
|
# Used by: open-sse/services — caches identical system prompts across requests.
|
|
# PROMPT_CACHE_MAX_SIZE=50 # Max cached entries (default: 50)
|
|
# PROMPT_CACHE_MAX_BYTES=2097152 # Max total cache size in bytes (default: 2 MB)
|
|
# PROMPT_CACHE_TTL_MS=300000 # Cache entry TTL (default: 5 minutes)
|
|
|
|
# ── Semantic cache (deterministic response dedup, temperature=0) ──
|
|
# Used by: open-sse/services — caches identical temperature=0 responses.
|
|
# SEMANTIC_CACHE_MAX_SIZE=100 # Max cached entries (default: 100)
|
|
# SEMANTIC_CACHE_MAX_BYTES=4194304 # Max total cache size in bytes (default: 4 MB)
|
|
# SEMANTIC_CACHE_TTL_MS=1800000 # Cache entry TTL (default: 30 minutes)
|
|
|
|
# ── In-memory log buffers ──
|
|
# Maximum recent stream events kept in memory for the Dashboard live view.
|
|
# STREAM_HISTORY_MAX=50
|
|
|
|
# ── Context length default ──
|
|
# Global fallback max context length for models without explicit config.
|
|
# Used by: open-sse/services/contextManager.ts
|
|
# CONTEXT_LENGTH_DEFAULT=128000
|
|
|
|
# ── Usage token buffer ──
|
|
# Extra token headroom reserved when tracking usage quotas (prevents over-limit).
|
|
# Used by: open-sse/utils/usageTracking.ts
|
|
# USAGE_TOKEN_BUFFER=100
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 18. PRICING SYNC
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# Automatic model pricing synchronization from external sources.
|
|
# Used by: src/lib/pricingSync.ts
|
|
|
|
# Enable periodic pricing data sync. Default: false (opt-in only).
|
|
# PRICING_SYNC_ENABLED=false
|
|
|
|
# Sync interval in seconds. Default: 86400 (24 hours).
|
|
# PRICING_SYNC_INTERVAL=86400
|
|
|
|
# Comma-separated data sources. Default: litellm
|
|
# PRICING_SYNC_SOURCES=litellm
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 18b. ARENA ELO SYNC
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# Auto-update model intelligence from Arena AI leaderboard ELO scores (powers the
|
|
# Free Provider Rankings page). ON by default — fetches from api.wulong.dev on startup
|
|
# (non-blocking, never fatal). Set to false to opt out of the outbound sync.
|
|
# Also configurable from Dashboard > Settings > Feature Flags.
|
|
# Used by: src/shared/constants/featureFlagDefinitions.ts, src/lib/arenaEloSync.ts
|
|
# ARENA_ELO_SYNC_ENABLED=true
|
|
|
|
# Sync interval in seconds. Default: 86400 (24 hours).
|
|
# ARENA_ELO_SYNC_INTERVAL=86400
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 19. MODEL SYNC (Dev)
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# Development-time model catalog sync interval in seconds.
|
|
# Used by: src/lib/modelsDevSync.ts
|
|
# Default: 86400 (24 hours)
|
|
# MODELS_DEV_SYNC_INTERVAL=86400
|
|
|
|
# Self-correcting context-window reconciler interval in seconds (feature 5004).
|
|
# Pins provider-declared windows from /models discovery as auto:discovery overrides
|
|
# when they diverge from the catalog. Set to 0 to disable. Never overwrites manual overrides.
|
|
# Used by: src/lib/contextWindowResolver.ts
|
|
# Default: 86400 (24 hours)
|
|
# CONTEXT_WINDOW_RECONCILE_INTERVAL=86400
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 20. PROVIDER-SPECIFIC SETTINGS
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
|
|
# ── OpenRouter ──
|
|
# OpenRouter model catalog cache TTL in ms.
|
|
# Used by: src/lib/catalog/openrouterCatalog.ts
|
|
# Default: 86400000 (24 hours)
|
|
# OPENROUTER_CATALOG_TTL_MS=86400000
|
|
|
|
# ── Model catalog response shape ──
|
|
# Include display-friendly name fields in /v1/models responses.
|
|
# Disable for clients that expect model IDs only.
|
|
# Defined in: src/shared/constants/featureFlagDefinitions.ts
|
|
# Used by: src/app/api/v1/models/catalog.ts
|
|
# Default: true
|
|
# MODEL_CATALOG_INCLUDE_NAMES=true
|
|
|
|
# ── NanoBanana (Image Generation) ──
|
|
# Polling config for async image generation jobs.
|
|
# Used by: open-sse/handlers/imageGeneration.ts
|
|
# NANOBANANA_POLL_TIMEOUT_MS=120000 # Max wait for job completion (default: 120s)
|
|
# NANOBANANA_POLL_INTERVAL_MS=2500 # Poll frequency (default: 2.5s)
|
|
|
|
# ── AWS Bedrock (Kiro / Audio) ──
|
|
# Region used to construct AWS Bedrock endpoints. Used by:
|
|
# src/lib/providers/validation.ts and open-sse/handlers/audioSpeech.ts.
|
|
# AWS_REGION takes precedence over AWS_DEFAULT_REGION when both are set.
|
|
# AWS_REGION=us-east-1
|
|
# AWS_DEFAULT_REGION=us-east-1
|
|
|
|
# ── Cloudflare Workers AI ──
|
|
# Account ID override for Cloudflare Workers AI executor.
|
|
# Used by: open-sse/executors/cloudflare-ai.ts
|
|
# CLOUDFLARE_ACCOUNT_ID=
|
|
|
|
# ── Deno Deploy proxy relay (#4643 / 9router#1437) ──
|
|
# Override the Deno Deploy REST API base used by the proxy-pool relay deployer.
|
|
# Default: https://api.deno.com/v2 (omit unless mocking).
|
|
# Used by: src/app/api/settings/proxy/deno-deploy/route.ts
|
|
# DENO_DEPLOY_API_BASE=https://api.deno.com/v2
|
|
|
|
# Default Deno Deploy app name suggested in the "Deploy Relay" modal.
|
|
# Used by: src/app/(dashboard)/dashboard/settings/components/proxy/DenoRelayModal.tsx
|
|
# NEXT_PUBLIC_DENO_RELAY_DEFAULT_PROJECT=omniroute-deno-relay
|
|
|
|
# Set to "false" to hide the Deno Deploy relay option from the Proxy Pool tab.
|
|
# Used by: src/app/(dashboard)/dashboard/settings/components/proxy/ProxyPoolTab.tsx
|
|
# NEXT_PUBLIC_DENO_RELAY_ENABLED=true
|
|
|
|
# ── Cloudflare Workers proxy relay (#4640 / 9router#1360) ──
|
|
# Override the Cloudflare REST API base used by the proxy-pool relay deployer.
|
|
# Default: https://api.cloudflare.com/client/v4 (omit unless mocking).
|
|
# Used by: src/app/api/settings/proxy/cloudflare-deploy/route.ts
|
|
# CLOUDFLARE_API_BASE=https://api.cloudflare.com/client/v4
|
|
|
|
# Default worker project name suggested in the "Deploy Relay" modal.
|
|
# Used by: src/app/(dashboard)/dashboard/settings/components/proxy/CloudflareRelayModal.tsx
|
|
# NEXT_PUBLIC_CLOUDFLARE_RELAY_DEFAULT_PROJECT=omniroute-relay
|
|
|
|
# Set to "false" to hide the Cloudflare Workers relay option from the Proxy Pool tab.
|
|
# Used by: src/app/(dashboard)/dashboard/settings/components/proxy/ProxyPoolTab.tsx
|
|
# NEXT_PUBLIC_CLOUDFLARE_RELAY_ENABLED=true
|
|
|
|
# ── Cloudflare Tunnel (cloudflared) ──
|
|
# Custom path to cloudflared binary for tunnel management.
|
|
# Used by: src/lib/cloudflaredTunnel.ts
|
|
# CLOUDFLARED_BIN=/usr/local/bin/cloudflared
|
|
|
|
# ── Search cache ──
|
|
# TTL for search API response caching (Perplexity, Brave, etc.).
|
|
# Used by: open-sse/services/searchCache.ts
|
|
# Default: 300000 (5 minutes)
|
|
# SEARCH_CACHE_TTL_MS=300000
|
|
|
|
# ── OpenAI-compatible multi-connection ──
|
|
# Allow multiple simultaneous connections per OpenAI-compatible provider node.
|
|
# Used by: src/app/api/providers/route.ts
|
|
# ALLOW_MULTI_CONNECTIONS_PER_COMPAT_NODE=false
|
|
|
|
# ── CC-compatible provider (experimental) ──
|
|
# Enable the Claude Code compatible provider endpoint.
|
|
# This is only for third-party relays that accept Claude Code clients exclusively.
|
|
# OmniRoute rewrites requests to pass those relays' Claude Code client validation.
|
|
# If you only want to use Claude Code CLI, or you are not sure what these relays are,
|
|
# keep this disabled and add a regular Anthropic-compatible provider instead.
|
|
# Used by: src/shared/utils/featureFlags.ts
|
|
# ENABLE_CC_COMPATIBLE_PROVIDER=false
|
|
|
|
# ── 9router embedded service ──
|
|
# Override the host/port where the embedded 9router instance listens.
|
|
# Rarely needed — defaults match the bootstrap config (127.0.0.1:20130).
|
|
# Used by: open-sse/executors/ninerouter.ts
|
|
# NINEROUTER_HOST=127.0.0.1
|
|
# NINEROUTER_PORT=20130
|
|
|
|
# ── Embedded service WebSocket proxy ──
|
|
# Standalone WebSocket proxy that tunnels WS connections to embedded services.
|
|
# Binds to loopback by default. Only change EMBED_WS_PROXY_HOST if you know
|
|
# what you are doing — exposing this to non-loopback bypasses local-only policy.
|
|
# Used by: src/lib/services/embedWsProxy.ts
|
|
# EMBED_WS_PROXY_HOST=127.0.0.1
|
|
# EMBED_WS_PROXY_PORT=20131
|
|
|
|
# ── CLIProxyAPI bridge (legacy) ──
|
|
# Connection settings for external CLIProxyAPI instances.
|
|
# Used by: open-sse/executors/cliproxyapi.ts
|
|
# CLIPROXYAPI_HOST=127.0.0.1
|
|
# CLIPROXYAPI_PORT=5544
|
|
# CLIPROXYAPI_CONFIG_DIR=~/.cli-proxy-api
|
|
|
|
# ── Mux embedded service ──
|
|
# Override the port where the embedded Mux (coder/mux) agent-orchestration
|
|
# daemon listens. Always bound to 127.0.0.1 — never configurable to 0.0.0.0.
|
|
# Rarely needed — defaults to 8322.
|
|
# Used by: src/lib/services/bootstrap.ts, src/app/api/services/mux/_lib.ts
|
|
# MUX_SERVICE_PORT=8322
|
|
|
|
# ── Local hostnames (Docker networking) ──
|
|
# Comma-separated additional hostnames treated as "local" for provider routing.
|
|
# Used by: open-sse/config/providerRegistry.ts — allows Docker service names.
|
|
# LOCAL_HOSTNAMES=omlx,mlx-audio
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 21. PROXY HEALTH
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# Fine-tune proxy health checking behavior.
|
|
# Used by: src/lib/proxyHealth.ts
|
|
|
|
# Timeout for fast-fail health checks (ms). Default: 2000
|
|
# PROXY_FAST_FAIL_TIMEOUT_MS=2000
|
|
|
|
# Health check result cache TTL (ms). Default: 30000 (30s)
|
|
# PROXY_HEALTH_CACHE_TTL_MS=30000
|
|
|
|
# Unhealthy health check result cache TTL (ms). Default: 2000 (2s)
|
|
# Keeps transient fast-fail timeouts from poisoning a proxy for the full
|
|
# healthy-result cache window under high concurrency.
|
|
# PROXY_HEALTH_UNHEALTHY_CACHE_TTL_MS=2000
|
|
|
|
# Background proxy health scheduler (src/lib/proxyHealth/scheduler.ts).
|
|
# Periodically probes every registered proxy and (optionally) removes dead ones.
|
|
# Set "false" to disable the scheduler entirely. Default: enabled.
|
|
# PROXY_HEALTH_ENABLED=true
|
|
# Sweep interval in ms (minimum 60000). Default: 600000 (10min).
|
|
# PROXY_HEALTH_INTERVAL_MS=600000
|
|
# Reachability probe target for the scheduler and the auto-test endpoint.
|
|
# Point it at an internal/self-hosted URL to avoid the public default.
|
|
# PROXY_HEALTH_TEST_URL=https://httpbin.org/ip
|
|
# Set "true" to let the scheduler auto-remove proxies after repeated failures.
|
|
# PROXY_AUTO_REMOVE=false
|
|
# Consecutive failures before an auto-remove fires. Default: 3.
|
|
# PROXY_AUTO_REMOVE_AFTER=3
|
|
# Let automated reachability probes (the scheduler + the "Test All" button) WRITE
|
|
# a proxy's status. Default "false": probes are read-only and never deactivate a
|
|
# proxy — only the operator sets active/inactive (a flaky probe must not strand an
|
|
# assigned proxy; #6246). Set "true" to restore the legacy test-and-set behaviour.
|
|
# PROXY_HEALTH_AUTO_DEACTIVATE=false
|
|
|
|
# Allow OAuth and provider validation flows to bypass a pinned proxy and connect
|
|
# directly when proxy reachability pre-checks fail. Default: false.
|
|
# Also configurable from Dashboard > Settings > Feature Flags.
|
|
# OMNIROUTE_CONTROL_PLANE_PROXY_DIRECT_FALLBACK=false
|
|
|
|
# Rate limit maximum wait time before failing a request (ms). Default: 120000 (2 min)
|
|
# Used by: open-sse/services/rateLimitManager.ts
|
|
# RATE_LIMIT_MAX_WAIT_MS=120000
|
|
|
|
# Force the auto-enable rate limit safety net on/off regardless of the persisted
|
|
# Dashboard setting. Used by: open-sse/services/rateLimitManager.ts.
|
|
# Accepted values: true|1|on (force on), false|0|off (force off), unset (use Dashboard).
|
|
# RATE_LIMIT_AUTO_ENABLE=
|
|
|
|
# Provider cooldown tracking: minimum time (ms) before a failed provider/connection
|
|
# can be retried. Prevents subsequent requests from re-walking failing providers.
|
|
# Scaled exponentially: minCooldown * 2^(failures-1), capped at maxRetryCooldownMs.
|
|
# Used by: open-sse/services/providerCooldownTracker.ts
|
|
# PROVIDER_COOLDOWN_MIN_MS=5000
|
|
|
|
# Provider cooldown tracking: maximum time (ms) before a failed provider/connection
|
|
# is retried regardless. Hard cap to prevent providers from being skipped indefinitely.
|
|
# Used by: open-sse/services/providerCooldownTracker.ts
|
|
# PROVIDER_COOLDOWN_MAX_MS=300000
|
|
|
|
# Enable/disable global provider cooldown tracking. Opt-in: this global
|
|
# cross-request cooldown overlaps the existing Connection Cooldown / Provider
|
|
# Circuit Breaker layers, so it is OFF by default. When disabled, only the
|
|
# existing per-request/per-connection cooldown state is used (previous behavior).
|
|
# Used by: open-sse/services/providerCooldownTracker.ts
|
|
# Accepted values: true|1|on (enable). Unset or anything else = disabled (default).
|
|
# PROVIDER_COOLDOWN_ENABLED=true
|
|
|
|
# Transparent stream recovery (free-claude-code port). When enabled, the opening SSE
|
|
# window is briefly held (up to STREAM_RECOVERY.HOLDBACK_MS) so an upstream truncation
|
|
# before any byte reaches the client can be retried invisibly. Opt-in: holding the
|
|
# window adds up to that much time-to-first-token latency on every stream, so it is
|
|
# OFF by default. Seeds ResilienceSettings.streamRecovery.enabled.
|
|
# Used by: open-sse/services/streamRecovery.ts, open-sse/handlers/chatCore.ts
|
|
# Accepted values: true|1|on (enable). Unset or anything else = disabled (default).
|
|
# STREAM_RECOVERY_ENABLED=true
|
|
|
|
# Mid-stream continuation (Fase 4.4): when an upstream stream truncates AFTER bytes
|
|
# already reached the client, re-request with the partial text as an assistant prefill
|
|
# and stitch the missing suffix (plain-text OpenAI-compatible streams only; never with a
|
|
# tool call in flight). OFF by default — the recovered tail arrives as one burst, not
|
|
# token-by-token. Independent of STREAM_RECOVERY_ENABLED (different risk profile).
|
|
# Seeds ResilienceSettings.streamRecovery.continueMidStream.
|
|
# Used by: open-sse/services/streamRecovery.ts, open-sse/handlers/chatCore.ts
|
|
# Accepted values: true|1|on (enable). Unset or anything else = disabled (default).
|
|
# STREAM_RECOVERY_MIDSTREAM_ENABLED=true
|
|
|
|
# Stagger interval (ms) between provider token healthchecks at startup.
|
|
# Used by: src/lib/tokenHealthCheck.ts. Default: 3000.
|
|
# HEALTHCHECK_STAGGER_MS=3000
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 22. DEBUGGING
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# These variables enable verbose debugging output. NEVER enable in production.
|
|
|
|
# Cursor executor verbose debug (decoded SSE chunks, etc.).
|
|
# CURSOR_STREAM_DEBUG is kept as a backward-compatible alias.
|
|
# Used by: open-sse/executors/cursor.ts
|
|
# CURSOR_DEBUG=1
|
|
|
|
# Enable verbose trace logging for OmniRoute internals.
|
|
# Used by: open-sse/handlers/chatCore.ts.
|
|
# OMNIROUTE_TRACE=true
|
|
|
|
# Standard DEBUG flag (same effect as OMNIROUTE_TRACE).
|
|
# DEBUG=true
|
|
# CURSOR_STREAM_DEBUG=1
|
|
|
|
# When CURSOR_DEBUG=1, also append raw decoded chunks to this file path.
|
|
# CURSOR_DUMP_FILE=/tmp/cursor-stream.log
|
|
|
|
# Cursor stream idle timeout (ms). Default: 300000 (5 min).
|
|
# Used by: open-sse/executors/cursor.ts.
|
|
# CURSOR_STREAM_TIMEOUT_MS=300000
|
|
|
|
# Cursor tool-commit directive toggle. Default-on: when a request declares
|
|
# tools, a directive is prepended so composer-2.5 reliably issues tool calls
|
|
# instead of narrating intent. Set to 0 to disable.
|
|
# Used by: open-sse/executors/cursor.ts.
|
|
# CURSOR_TOOL_DIRECTIVE=1
|
|
|
|
# Per-image fetch timeout (ms) for remote image_url vision input. Default: 15000.
|
|
# Used by: open-sse/utils/cursorImages.ts.
|
|
# CURSOR_IMAGE_FETCH_TIMEOUT_MS=15000
|
|
|
|
# Cursor state DB path override (for cursor version detection).
|
|
# Used by: open-sse/utils/cursorVersionDetector.ts. Default: probed automatically.
|
|
# CURSOR_STATE_DB_PATH=
|
|
|
|
# Direct Cursor bearer token used by scripts/ad-hoc/cursor-tap.cjs (developer tooling).
|
|
# CURSOR_TOKEN=
|
|
|
|
# Log Responses API SSE-to-JSON translation details.
|
|
# DEBUG_RESPONSES_SSE_TO_JSON=true
|
|
|
|
# Log request shape (content-type + content-length) for large chat payloads.
|
|
# Used by: src/app/api/v1/chat/completions/route.ts. Set to "0" to silence.
|
|
# Default: enabled.
|
|
# OMNIROUTE_LOG_REQUEST_SHAPE=1
|
|
|
|
# Write raw (untruncated) request/response JSON in call log artifacts.
|
|
# When enabled, serializeArtifactForStorage skips size-based truncation.
|
|
# Also enabled automatically when APP_LOG_LEVEL=debug.
|
|
# WARNING: produces large files — use only for temporary debugging.
|
|
# CHAT_DEBUG_FILE=true
|
|
|
|
# Enable E2E test mode — relaxes auth and enables test harness hooks.
|
|
# NEXT_PUBLIC_OMNIROUTE_E2E_MODE=true
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 23. GITHUB INTEGRATION (Issue Reporting)
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# Allow users to report issues directly from the Dashboard to GitHub.
|
|
# Used by: src/app/api/v1/issues/report/route.ts
|
|
|
|
# GitHub repository in owner/repo format.
|
|
# GITHUB_ISSUES_REPO=owner/repo
|
|
|
|
# GitHub Personal Access Token with issues:write scope.
|
|
# GITHUB_ISSUES_TOKEN=ghp_xxxx
|
|
|
|
# Generic GitHub access token consumed by issue triage / agent helpers.
|
|
# Used by: src/app/api/v1/issues/* and src/lib/cloudAgent/* — falls back to
|
|
# GITHUB_ISSUES_TOKEN when unset.
|
|
# GITHUB_TOKEN=
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 24. PROVIDER QUOTAS, TUNNELS & SANDBOXED SKILLS
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# Provider quota endpoints, network tunnels (Tailscale, Ngrok, MITM debug
|
|
# proxy), 1Proxy egress pool, skills sandbox runtime, and miscellaneous CLI
|
|
# binaries referenced by the executor layer or the dashboard runtime.
|
|
|
|
# ── Alibaba (Bailian) coding plan quota ──
|
|
# Host/full URL override used by: open-sse/services/bailianQuotaFetcher.ts.
|
|
# When unset the fetcher uses the production Alibaba endpoints.
|
|
# ALIBABA_CODING_PLAN_HOST=
|
|
# ALIBABA_CODING_PLAN_QUOTA_URL=
|
|
|
|
# ── Context window tuning ──
|
|
# Tokens reserved for completion output when computing prompt budgets.
|
|
# Used by: open-sse/services/contextManager.ts. Default: 1024.
|
|
# CONTEXT_RESERVE_TOKENS=1024
|
|
|
|
# ── Model alias rewriting (legacy compatibility) ──
|
|
# Toggle the legacy model-alias compatibility layer used by older clients.
|
|
# Used by: open-sse/services/model.ts. Default: enabled.
|
|
# MODEL_ALIAS_COMPAT_ENABLED=true
|
|
|
|
# ── Devin CLI binary path ──
|
|
# Used by: open-sse/executors/devin-cli.ts. Default: looked up via PATH.
|
|
# CLI_DEVIN_BIN=devin
|
|
|
|
# ── Command Code (custom CLI) callback ──
|
|
# Local port used for OAuth-style callbacks from the Command Code CLI helper.
|
|
# Used by: src/app/api/providers/command-code/auth/shared.ts.
|
|
# COMMAND_CODE_CALLBACK_PORT=
|
|
|
|
# ── Command Code CLI version header ──
|
|
# Value sent as the x-command-code-version header to the Command Code upstream.
|
|
# Overrides the built-in default; bump if the upstream requires a newer CLI version.
|
|
# Used by: open-sse/executors/commandCode.ts
|
|
# Default: 0.33.2
|
|
# COMMAND_CODE_VERSION=0.33.2
|
|
|
|
# ── MITM debug proxy (development only) ──
|
|
# Used by: src/mitm/server.cjs — captures upstream traffic for inspection.
|
|
# MITM_LOCAL_PORT=443
|
|
# MITM_DISABLE_TLS_VERIFY=0
|
|
# Idle socket timeout (ms) for proxied connections; sockets idle past this are torn
|
|
# down to avoid leaking half-open tunnels (src/mitm/socketTimeouts.ts, server.cjs).
|
|
# MITM_IDLE_TIMEOUT_MS=60000
|
|
# Routing-decision log verbosity: 0 silences, higher values log more bypass/route
|
|
# decisions (src/mitm/server.cjs, _internal/bypass.cjs).
|
|
# MITM_VERBOSE=1
|
|
# Strip the leading `sudo` from MITM cert-trust commands (src/mitm/systemCommands.ts) —
|
|
# for root-less / user-namespaced deployments (e.g. rootless Docker/Podman)
|
|
# where the operator trusts the CA manually (e.g. via Node's extra-CA-certs mechanism).
|
|
# OMNIROUTE_NO_SUDO=0
|
|
|
|
# ── Test/CI-only guards (never needed in production) ──
|
|
# Set automatically by tests/_setup/isolateDataDir.ts and the CI workflows: the
|
|
# test suite must NEVER mutate the OS trust store (a fake test PEM installed via
|
|
# update-ca-certificates broke all system TLS on a persistent runner, 2026-07-05).
|
|
# OMNIROUTE_SKIP_SYSTEM_TRUST=1
|
|
# check-changelog-integrity.mjs (anti CHANGELOG-eat gate): explicit base ref
|
|
# override, and the justified-removal escape hatch for intentional bullet removals.
|
|
# CHANGELOG_BASE_REF=origin/release/v0.0.0
|
|
# ALLOW_CHANGELOG_REMOVALS=1
|
|
|
|
# ── 1Proxy egress pool ──
|
|
# Used by: src/lib/oneproxySync.ts — fetches proxy nodes from the OmniRoute
|
|
# CrofAI 1Proxy service. Disable, override URL, or tune the import quality.
|
|
# ONEPROXY_ENABLED=true
|
|
# ONEPROXY_API_URL=https://1proxy-api.aitradepulse.com
|
|
# ONEPROXY_MAX_PROXIES=500
|
|
# ONEPROXY_MIN_QUALITY_THRESHOLD=50
|
|
|
|
# ── Free Proxy Pool (1proxy source) ──
|
|
# Used by: src/lib/freeProxyProviders/oneproxy.ts
|
|
# Set FREE_PROXY_1PROXY_ENABLED=false to disable this source.
|
|
# FREE_PROXY_1PROXY_ENABLED=true
|
|
# FREE_PROXY_1PROXY_API_URL=https://1proxy-api.aitradepulse.com/api/v1/proxies/advanced
|
|
# FREE_PROXY_1PROXY_MAX=500
|
|
# FREE_PROXY_1PROXY_MIN_QUALITY=50
|
|
|
|
# ── Free Proxy Pool (Proxifly source) ──
|
|
# Used by: src/lib/freeProxyProviders/proxifly.ts
|
|
# Enabled by default; set to false to disable.
|
|
# FREE_PROXY_PROXIFLY_ENABLED=true
|
|
# FREE_PROXY_PROXIFLY_QUANTITY=100
|
|
# FREE_PROXY_PROXIFLY_ANONYMITY=elite
|
|
|
|
# ── Free Proxy Pool (IPLocate source) ──
|
|
# Used by: src/lib/freeProxyProviders/iplocate.ts
|
|
# Opt-in only; must set FREE_PROXY_IPLOCATE_ENABLED=true to activate.
|
|
# FREE_PROXY_IPLOCATE_ENABLED=false
|
|
# FREE_PROXY_IPLOCATE_BASE_URL=https://raw.githubusercontent.com/iplocate/free-proxy-list/main/protocols
|
|
|
|
# ── Free Proxy Pool (Webshare source) ──
|
|
# Used by: src/lib/freeProxyProviders/webshare.ts
|
|
# Paid, per-account proxy list — requires FREE_PROXY_WEBSHARE_API_KEY to activate,
|
|
# regardless of FREE_PROXY_WEBSHARE_ENABLED.
|
|
# FREE_PROXY_WEBSHARE_ENABLED=true
|
|
# FREE_PROXY_WEBSHARE_API_KEY=
|
|
# FREE_PROXY_WEBSHARE_API_URL=https://proxy.webshare.io/api/v2/proxy/list/
|
|
# FREE_PROXY_WEBSHARE_MAX=500
|
|
|
|
# ── Vercel Relay ──
|
|
# Used by: src/app/api/settings/proxy/vercel-deploy/route.ts
|
|
# Hides the "Deploy Relay" button when set to false.
|
|
# NEXT_PUBLIC_VERCEL_RELAY_ENABLED=true
|
|
# VERCEL_API_BASE=https://api.vercel.com
|
|
# Default project name pre-filled in the Vercel Relay deploy modal.
|
|
# NEXT_PUBLIC_VERCEL_RELAY_DEFAULT_PROJECT=omniroute-relay
|
|
|
|
# ── Tailscale tunnel binaries ──
|
|
# Optional explicit paths to tailscale/tailscaled binaries used by the
|
|
# dashboard's tunnel manager. Used by: src/lib/tailscaleTunnel.ts.
|
|
# TAILSCALE_BIN=/usr/local/bin/tailscale
|
|
# TAILSCALED_BIN=/usr/local/bin/tailscaled
|
|
# Pre-shared Tailscale auth key for non-interactive / headless `tailscale up`
|
|
# (passed via --auth-key=). When unset, login falls back to the interactive
|
|
# browser auth URL. Used by: src/lib/tailscaleTunnel.ts.
|
|
# TAILSCALE_AUTHKEY=
|
|
|
|
# ── Ngrok tunnel ──
|
|
# Used by: src/lib/ngrokTunnel.ts — authenticates outbound tunnels.
|
|
# NGROK_AUTHTOKEN=
|
|
|
|
# ── Database backups ──
|
|
# Used by: src/lib/db/backup.ts.
|
|
# DB_BACKUP_MAX_FILES=20
|
|
# DB_BACKUP_RETENTION_DAYS=0
|
|
|
|
# ── TLS sidecar override ──
|
|
# Used by: open-sse/services/chatgptTlsClient.ts tests. Production deployments
|
|
# should leave this unset; the sidecar is auto-managed.
|
|
# OMNIROUTE_TLS_PROXY_URL=
|
|
|
|
# ── Skills sandbox (experimental) ──
|
|
# Used by: src/lib/skills/builtins.ts. All values support comma lists where
|
|
# noted in the source.
|
|
# SKILLS_MAX_FILE_BYTES=1048576
|
|
# SKILLS_MAX_HTTP_RESPONSE_BYTES=256000
|
|
# SKILLS_MAX_SANDBOX_OUTPUT_CHARS=100000
|
|
# SKILLS_SANDBOX_TIMEOUT_MS=10000
|
|
# SKILLS_SANDBOX_NETWORK_ENABLED=0
|
|
# SKILLS_ALLOWED_SANDBOX_IMAGES=
|
|
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# 25. TEST & E2E
|
|
# ═══════════════════════════════════════════════════════════════════════════════
|
|
# Used by scripts/dev/run-next-playwright.mjs, scripts/dev/smoke-electron-packaged.mjs,
|
|
# scripts/dev/run-ecosystem-tests.mjs and scripts/build/uninstall.mjs.
|
|
# Production deployments should leave every value below unset.
|
|
|
|
# E2E bootstrap mode for the Playwright runner. Accepted: auth | fresh | reuse.
|
|
# Default (when unset): auth.
|
|
# OMNIROUTE_E2E_BOOTSTRAP_MODE=auth
|
|
|
|
# Admin password injected into the Playwright test environment.
|
|
# Falls back to INITIAL_PASSWORD when unset.
|
|
# OMNIROUTE_E2E_PASSWORD=
|
|
|
|
# Disable the local healthcheck poll during Playwright runs (default: true).
|
|
# OMNIROUTE_DISABLE_LOCAL_HEALTHCHECK=true
|
|
|
|
# Disable the OAuth token healthcheck loop during tests (default: true).
|
|
# OMNIROUTE_DISABLE_TOKEN_HEALTHCHECK=true
|
|
|
|
# Exclude specific providers from the PROACTIVE token-refresh sweep (comma-separated,
|
|
# case-insensitive). Targeted alternative to OMNIROUTE_DISABLE_TOKEN_HEALTHCHECK: keeps
|
|
# rotating-cascade providers (Codex/OpenAI share one Auth0 family) on the reactive 401
|
|
# path only, while short-TTL providers like Kimi-coding keep being refreshed proactively.
|
|
# OMNIROUTE_HEALTHCHECK_SKIP_PROVIDERS=codex,openai
|
|
|
|
# Silence healthcheck noise in Playwright stdout (default: true).
|
|
# OMNIROUTE_HIDE_HEALTHCHECK_LOGS=true
|
|
|
|
# Skip the Next.js production build before Playwright starts (CI optimization).
|
|
# OMNIROUTE_PLAYWRIGHT_SKIP_BUILD=0
|
|
|
|
# Skip the OmniRoute uninstall hook (used by CI to keep node_modules intact).
|
|
# OMNIROUTE_SKIP_UNINSTALL_HOOK=0
|
|
|
|
# Ecosystem/protocol test orchestrators wait this long (ms) for the server to
|
|
# become healthy. Default: 180000.
|
|
# ECOSYSTEM_SERVER_WAIT_MS=180000
|
|
|
|
# Docs translation pipeline (used by scripts/i18n/run-translation.mjs).
|
|
# OpenAI-compatible base URL, e.g. https://cloud.omniroute.online/v1
|
|
# OMNIROUTE_TRANSLATION_API_URL=
|
|
# Bearer token for the translation backend (NEVER commit a real key here).
|
|
# OMNIROUTE_TRANSLATION_API_KEY=
|
|
# Model id, e.g. gpt-4o-mini or cx/gpt-5.4-mini.
|
|
# OMNIROUTE_TRANSLATION_MODEL=gpt-4o-mini
|
|
# Per-request timeout in milliseconds (default 60000).
|
|
# OMNIROUTE_TRANSLATION_TIMEOUT_MS=60000
|
|
# Number of parallel translation requests (default 4).
|
|
# OMNIROUTE_TRANSLATION_CONCURRENCY=4
|
|
|
|
# ─── Cloud Sync hardening (v3.8.6) ──────────────────────────────────────────
|
|
# Shared secret used to verify the HMAC-SHA256 of the Cloud sync response body
|
|
# (the Cloud endpoint must sign each response with the same secret and place
|
|
# the hex digest in the X-Cloud-Sig header). When unset, v3.8.6 logs a warning
|
|
# but accepts unsigned responses for back-compat. v3.9 will make this required.
|
|
# OMNIROUTE_CLOUD_SYNC_SECRET=
|
|
#
|
|
# Set to "true" to allow the Cloud Sync endpoint to overwrite local OAuth
|
|
# tokens (accessToken / refreshToken / providerSpecificData). Default OFF —
|
|
# only non-credential metadata is synced. See docs/security/SOCKET_DEV_FINDINGS.md §5.
|
|
# OMNIROUTE_CLOUD_SYNC_SECRETS=false
|
|
|
|
# ─── Zed import legacy compat (v3.8.6) ──────────────────────────────────────
|
|
# Set to "true" to fall back to the v3.8.5 one-step "import everything from
|
|
# the keychain" behaviour. Default OFF — the new 2-step confirmation flow
|
|
# requires `confirmedAccounts` in the request body. See SOCKET_DEV_FINDINGS.md §2.
|
|
# OMNIROUTE_ZED_IMPORT_LEGACY_ONE_STEP=false
|
|
|
|
# ─── Build profile (build-time only) ────────────────────────────────────────
|
|
# Set to "minimal" before `npm run build` to physically remove four optional
|
|
# privileged modules (MITM cert install, Zed keychain import, Cloud Sync,
|
|
# 9router installer) from the standalone bundle. The resulting artifact is
|
|
# intended to be published as `omniroute-secure`. See SECURITY.md.
|
|
# OMNIROUTE_BUILD_PROFILE=full
|
|
|
|
# Electron smoke harness (used by scripts/dev/smoke-electron-packaged.mjs).
|
|
# ELECTRON_SMOKE_URL=http://127.0.0.1:20128/login
|
|
# ELECTRON_SMOKE_TIMEOUT_MS=45000
|
|
# ELECTRON_SMOKE_SETTLE_MS=2000
|
|
# ELECTRON_SMOKE_APP_EXECUTABLE=
|
|
# ELECTRON_SMOKE_DATA_DIR=
|
|
# ELECTRON_SMOKE_KEEP_DATA=0
|
|
# ELECTRON_SMOKE_STREAM_LOGS=0
|
|
|
|
# Playground Studio
|
|
# Default model used by the improve-prompt route (optional; falls back to model in request body).
|
|
PLAYGROUND_IMPROVE_PROMPT_DEFAULT_MODEL=
|
|
# Maximum number of parallel compare columns in the Compare tab.
|
|
PLAYGROUND_COMPARE_MAX_COLUMNS=4
|
|
# Memory engine (plan 21)
|
|
# MEMORY_EMBEDDING_CACHE_TTL_MS=300000 # default 5 min
|
|
# MEMORY_EMBEDDING_CACHE_MAX=1000 # default 1000 entries
|
|
# MEMORY_TRANSFORMERS_MODEL=Xenova/all-MiniLM-L6-v2
|
|
# MEMORY_STATIC_MODEL=minishlab/potion-base-8M # HF repo id (download once)
|
|
# MEMORY_STATIC_CACHE_DIR= # default <DATA_DIR>/embeddings
|
|
# MEMORY_VEC_TOP_K=20 # default top-K for vector search
|
|
# MEMORY_RRF_K=60 # RRF k constant (sqlite-vec hybrid recipe)
|
|
# HF_HUB_ENDPOINT=https://huggingface.co # override Hugging Face Hub base URL for static potion downloads
|
|
# TV6 typed memory decay (OPT-IN, default off — the sweep DELETES decayed memories)
|
|
# MEMORY_TYPED_DECAY_ENABLED=false # master switch for the destructive sweep (default off)
|
|
# MEMORY_TYPED_DECAY_EPISODIC_DAYS=30 # episodic TTL in days; 0 = episodic immune too
|
|
# MEMORY_TYPED_DECAY_ACCESS_IMMUNITY=3 # access_count >= N → immune; 0 disables access immunity
|
|
# MEMORY_TYPED_DECAY_SWEEP_INTERVAL=0 # periodic sweep interval (seconds); 0 = no periodic sweep
|
|
# AgentBridge + Traffic Inspector (Group A)
|
|
|
|
# AgentBridge
|
|
AGENTBRIDGE_UPSTREAM_CA_CERT=
|
|
|
|
# Inspector
|
|
INSPECTOR_BUFFER_SIZE=1000
|
|
INSPECTOR_HTTP_PROXY_PORT=8080
|
|
INSPECTOR_HTTP_PROXY_AUTOSTART=false
|
|
INSPECTOR_TLS_INTERCEPT=false
|
|
INSPECTOR_SYSTEM_PROXY_GUARD_MINUTES=30
|
|
INSPECTOR_MAX_BODY_KB=1024
|
|
INSPECTOR_MASK_SECRETS=true
|
|
INSPECTOR_LLM_HOSTS_EXTRA=
|
|
INSPECTOR_INTERNAL_INGEST_TOKEN=
|
|
# Quota Sharing (Group B — planos 16+22)
|
|
QUOTA_STORE_DRIVER=sqlite # sqlite | redis
|
|
# QUOTA_STORE_REDIS_URL= # ex.: redis://localhost:6379 (apenas quando driver=redis)
|
|
# QUOTA_SATURATION_THRESHOLD=0.5 # 0..1; >= threshold ativa modo strict (sem empréstimo)
|
|
# QUOTA_SOFT_DEPRIORITIZE_FACTOR=0.7 # 0..1; multiplicador do score quando soft policy ativa
|
|
# STATUS_SOFT_DEPRIORITIZE_FACTOR=0.5 # 0..1; multiplicador do score p/ provider esgotado (credits_exhausted/rate_limited) quando preflight cutoff OFF (#4540)
|
|
# QUOTA_CONSUMPTION_RETENTION_DAYS=14 # GC de buckets quota_consumption.updated_at antigos
|
|
# QUOTA_PREFLIGHT_CUTOFF_ENABLED=false # opt-in (default OFF): hard quota cutoff drops low-quota candidates before auto-routing scoring
|
|
|
|
# ─── Auto-Combo tier filter (#4517) ───────────────────────────────────────
|
|
# When an `auto/<category>:free` (or any `:<tier>`) request matches NO connected
|
|
# candidates, OmniRoute returns an EMPTY pool by default — so `:free` really means
|
|
# "free tier only" and a paid model is never picked just because no free provider is
|
|
# connected. Set this to `true`/`1` to restore the legacy behavior of falling back to
|
|
# the full (unfiltered) pool with a warning. Source: open-sse/services/autoCombo/virtualFactory.ts
|
|
# OMNIROUTE_AUTO_FREE_FALLBACK_TO_FULL_POOL=false
|
|
|
|
# ─── OpenCode config regeneration (scripts/ad-hoc/regen-opencode-config.ts) ───
|
|
# Base URL of the OmniRoute instance to query for /v1/models when regenerating
|
|
# an opencode.json with accurate limit.context values. Used by:
|
|
# scripts/ad-hoc/regen-opencode-config.ts. Default: http://localhost:20128
|
|
# OMNIROUTE_URL=
|
|
# API key to authenticate against the OmniRoute /v1/models endpoint. Falls back
|
|
# to OPENCODE_API_KEY when unset. Used by: scripts/ad-hoc/regen-opencode-config.ts.
|
|
# OMNIROUTE_KEY=
|
|
# OpenCode-style API key (sk-...) for the regenerated opencode.json. Used by:
|
|
# scripts/ad-hoc/regen-opencode-config.ts. Falls back to OMNIROUTE_KEY.
|
|
# OPENCODE_API_KEY=
|
|
|
|
# ─── Bifrost Go sidecar (PR-4 in #3932) ──────────────────────────────────────
|
|
# Master kill switch for the bifrost sidecar proxy. When set to 0, the
|
|
# /api/v1/relay/chat/completions/bifrost route returns 503 with the
|
|
# X-Bifrost-Killswitch header and the operator is bounced to the TS path.
|
|
# Use this to disable the sidecar without redeploying (e.g. during a
|
|
# tier-1 router incident or a key rotation). Default: 1 (sidecar active).
|
|
# BIFROST_ENABLED=1
|
|
# When BIFROST_BASE_URL is set, /api/v1/relay/chat/completions/bifrost routes
|
|
# traffic to the Go gateway instead of the TS relay handler, removing TS from
|
|
# the hot path. Auth/rate-limit/injection-guard stay in the route (security not
|
|
# duplicated). Falls back to TS path via X-Bifrost-Fallback header on
|
|
# timeout/failure. See bin/omniroute for the local-redis companion.
|
|
# BIFROST_BASE_URL=
|
|
# Port the supervised Bifrost embedded service binds to (127.0.0.1:<port>), read by
|
|
# src/lib/services/bootstrap.ts when OmniRoute manages the Bifrost sidecar lifecycle.
|
|
# Default: 8080.
|
|
# BIFROST_PORT=8080
|
|
# API key for the Bifrost gateway (sent as Authorization: Bearer ...). If
|
|
# unset, the route expects the request to carry a valid OmniRoute API key;
|
|
# this key is for gateway-side auth only.
|
|
# BIFROST_API_KEY=
|
|
# When true, the Bifrost sidecar route streams responses back via SSE through
|
|
# the gateway rather than the TS streaming executor. Default: true (when
|
|
# BIFROST_BASE_URL is set).
|
|
# BIFROST_STREAMING_ENABLED=
|
|
# Per-request timeout when proxying to the Bifrost gateway. Default: 30000 (30s).
|
|
# BIFROST_TIMEOUT_MS=
|
|
# Alias for BIFROST_API_KEY (used by scripts that read the env via
|
|
# OMNIROUTE_*). BIFROST_API_KEY takes precedence when both are set.
|
|
# OMNIROUTE_BIFROST_KEY=
|
|
# Relay backend selection for the OpenAI-compatible relay endpoint:
|
|
# ts | bifrost | auto. "ts" (default when Bifrost is not configured) uses the
|
|
# TypeScript relay; "auto" selects Bifrost when BIFROST_BASE_URL is set (and
|
|
# BIFROST_ENABLED != 0) and falls back to TS if the sidecar is unreachable;
|
|
# "bifrost" forces Bifrost (strict — no TS fallback). Auth, rate limits,
|
|
# injection guard and model allowlists always run in the Next route first.
|
|
# RELAY_ROUTING_BACKEND is an accepted alias. Responses carry X-Routing-Backend
|
|
# and X-Routing-Fallback.
|
|
# OMNIROUTE_RELAY_BACKEND=
|
|
# RELAY_ROUTING_BACKEND=
|
|
# Cooldown (ms) after a Bifrost sidecar hop fails in "auto" mode before the relay
|
|
# re-attempts the sidecar; it goes straight to the TS path while the cooldown lasts.
|
|
# 0 disables. Default 5000. Only applies when OMNIROUTE_RELAY_BACKEND=auto.
|
|
# OMNIROUTE_BIFROST_FAILURE_COOLDOWN_MS=
|
|
# Opt-in native HTTPS/TLS for `omniroute serve` (equivalent to --tls-cert /
|
|
# --tls-key). Provide BOTH a PEM certificate and its private key and the
|
|
# standalone server terminates TLS on the same listener (wss:// works
|
|
# unchanged). With neither set the server stays plain HTTP; providing only one
|
|
# (or an unreadable path) logs a warning and stays HTTP (never half-enables).
|
|
# OMNIROUTE_TLS_CERT=
|
|
# OMNIROUTE_TLS_KEY=
|
|
|
|
# ─── 1-click local service launchers (PR-3 in #3932) ────────────────────────
|
|
# Master switch for /api/local/* routes. When unset or "0", all /api/local/*
|
|
# routes return 503 in production. Default: 0. Must be "1" in non-loopback
|
|
# deploys to enable the Redis launcher and similar 1-click local service
|
|
# starters. Belt-and-suspenders with the isLocalOnlyPath() route-guard
|
|
# classification (LOCAL_ONLY_API_PREFIXES in src/server/authz/routeGuard.ts).
|
|
# OMNIROUTE_LOCAL_ENDPOINTS_ENABLED=
|
|
# Bearer token for /api/local/* callers that aren't on loopback (e.g. the
|
|
# desktop app). When set, requests from non-loopback IPs must carry
|
|
# Authorization: Bearer <token>. Required when
|
|
# OMNIROUTE_LOCAL_ENDPOINTS_ENABLED=1 in non-loopback deployments. Default:
|
|
# unset (loopback-only).
|
|
# OMNIROUTE_LOCAL_ENDPOINTS_TOKEN=
|
|
# Container name for the 1-click Redis launcher (`omniroute redis up`).
|
|
# Default: omniroute-redis. Used by bin/cli/commands/redis.mjs and the
|
|
# RedisLauncherPanel.
|
|
# OMNIROUTE_REDIS_CONTAINER_NAME=
|
|
# Host port for the 1-click Redis launcher. Default: 6379. Bump if the host
|
|
# already binds 6379. The container's internal port stays 6379.
|
|
# OMNIROUTE_REDIS_HOST_PORT=
|
|
# Redis image used by the 1-click Redis launcher. Default: redis:7-alpine.
|
|
# Override to redis:8-alpine or a private registry mirror as needed.
|
|
# OMNIROUTE_REDIS_IMAGE=
|
|
|
|
# ── Cluster Profile: Qdrant Vector Memory (opt-in via `docker compose --profile memory up`) ──
|
|
# Qdrant is an OPTIONAL sidecar for deployments that need cosine-distance vector
|
|
# search at >1M embeddings. The default vector store is sqlite-vec
|
|
# (src/lib/memory/vectorStore.ts:108); flip this profile on only if you hit the
|
|
# sqlite-vec ceiling or want persistent cross-replica vector state. See
|
|
# docs/architecture/cluster-decisions.md § "Qdrant (memory profile)".
|
|
# QDRANT_HOST=qdrant
|
|
# QDRANT_PORT=6333
|
|
# QDRANT_GRPC_PORT=6334
|
|
# QDRANT_API_KEY=
|
|
# QDRANT_COLLECTION=omniroute-memory
|
|
# QDRANT_EMBEDDING_MODEL=text-embedding-3-small
|
|
# QDRANT_VECTOR_SIZE=1536
|
|
# QDRANT_HNSW_EF_CONSTRUCT=128
|
|
|
|
# ── Cluster Profile: Bifrost Tier-1 Router (opt-in via `docker compose --profile bifrost up`) ──
|
|
# Bifrost is an OPTIONAL Go-based Tier-1 router that handles the upstream-provider
|
|
# multiplexing layer. Default: OmniRoute's open-sse/executors/bifrost.ts in-process
|
|
# executor handles routing directly. Flip this profile on only if you want the
|
|
# gateway as a separate sidecar (helps in 3+ replica deployments where you want
|
|
# provider rotation centralised). See docs/architecture/cluster-decisions.md §
|
|
# "Bifrost (bifrost profile)".
|
|
# Set OMNIROUTE_RELAY_BACKEND=auto to use this sidecar when healthy, or
|
|
# OMNIROUTE_RELAY_BACKEND=bifrost to require it without TS fallback.
|
|
# BIFROST_BASE_URL=http://bifrost:8080
|
|
# BIFROST_API_KEY=
|
|
# BIFROST_STREAMING_ENABLED=true
|
|
# BIFROST_TIMEOUT_MS=30000
|