-
-
Notifications
You must be signed in to change notification settings - Fork 8.5k
Expand file tree
/
Copy path.env.example
More file actions
3114 lines (2692 loc) · 171 KB
/
Copy path.env.example
File metadata and controls
3114 lines (2692 loc) · 171 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# ┌─────────────────────────────────────────────────────────────────────────────┐
# │ 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
# Fallback alias for DATA_DIR, checked only when DATA_DIR is unset.
# Used by: open-sse/executors/promptql/threadSticky.ts — locates the PromptQL
# executor's on-disk thread-sticky session cache. Leave unset to rely on DATA_DIR.
# OMNIROUTE_DATA_DIR=/var/lib/omniroute
# Directory the runtime plugin scanner reads, overriding the home-derived default (#11827).
# Used by: src/lib/plugins/scanner.ts — getDefaultPluginDir(); it is also the root the
# plugin manager installs into. Set it in Docker/K8s to point straight at the bind-mounted
# plugin tree, instead of moving HOME (which changes every other HOME-relative behaviour)
# just to relocate the scan path. Unset = <HOME>/.omniroute/plugins, and
# /tmp/.omniroute/plugins when the process exports no home at all.
# Distinct from the CLI-only variable in section 9 that points the omniroute-cmd-* command
# loader (bin/cli/plugins.mjs) at a package tree — this one drives the server-side scanner.
# OMNIROUTE_PLUGINS_DIR=/opt/omniroute/plugins
# Escape hatch for the test/eval DATA_DIR guard (#10428). A test or node eval/print
# probe (-e/--eval/-p/--print, including --eval=/--print=) that never chose a DATA_DIR
# is redirected to a throwaway temp dir so it cannot open the operator's real database.
# Set to 1 only for a deliberate run against the real DATA_DIR — never for CI.
# Used by: src/lib/dataPaths.ts
# OMNIROUTE_ALLOW_DEFAULT_DATA_DIR=1
# Build provenance (#10427). OMNIROUTE_BUILD_SHA lets a container inject the artifact's git
# SHA when the dist/BUILD_SHA sentinel is absent; it is also what `npm run build:release`
# stamps. OMNIROUTE_RELEASE_REF is the ref the pack gate checks ancestry against, and
# OMNIROUTE_ALLOW_CANARY_BUILD=1
# API key the canary-deploy smoke uses when the target gateway requires auth (#10429).
# Used by: scripts/ops/deploy-canary.mjs — sent as `Authorization: Bearer` on the
# /v1/chat/completions probe. Never needed by the server itself.
# OMNIROUTE_SMOKE_API_KEY=sk-... records a deliberate off-release-line build instead of
# failing it. Used by: scripts/build/buildProvenance.ts, src/lib/monitoring/buildSha.ts
# OMNIROUTE_BUILD_SHA=abc1234
# OMNIROUTE_RELEASE_REF=origin/main
# OMNIROUTE_ALLOW_CANARY_BUILD=1
# Build-phase signal (#10060). Set to 1 by scripts/build/build-next-isolated.mjs and
# inherited by every spawned build worker so the DB layer returns a no-op stub instead
# of loading the native better-sqlite3 addon (which aborts the worker on exit).
# Never set this for the running server. Used by: src/lib/buildPhase.ts, src/lib/db/core.ts
# OMNIROUTE_BUILDING=1
# Skip the optional native-dependency prebuild check for exotic vendored trees.
# This does not make a missing dependency buildable. Used by: scripts/check/check-native-deps.mjs
# Default: 0 | Set to 1 only when native dependencies are supplied out of band.
# OMNIROUTE_SKIP_NATIVE_DEP_CHECK=0
# 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
# Routine/pre-write SQLite backups.
# Used by: src/lib/db/backup.ts. Set true only when those backups are managed externally.
# This never disables the migration runner's mandatory, content-addressed safety snapshot
# or its mass-migration guard for an existing persistent database.
# Default: false (routine backups enabled).
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
# Namespace prefix for ALL OmniRoute Redis keys (rate limiter + auth cache +
# quota store). Prevents key collisions when OmniRoute shares a Redis instance
# with other apps (e.g. on 127.0.0.1:6379). Default when unset: omniroute:
# REDIS_KEY_PREFIX=omniroute:
# Host interface docker-compose publishes the Redis sidecar on.
# Default: 127.0.0.1 (loopback only). The compose Redis runs WITHOUT
# `requirepass`, and app containers reach it over the compose network
# (redis:6379) — the published port is only for host-side tooling. Setting this
# to 0.0.0.0 exposes an unauthenticated Redis to your whole LAN.
# REDIS_BIND_HOST=127.0.0.1
# Host port for the compose Redis sidecar. Default: 6379.
# REDIS_PORT=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.
# Also mirrored to NEXT_PUBLIC_OMNIROUTE_BASE_PATH at build time so the dashboard
# endpoint display (useDisplayBaseUrl) shows https://host/omniroute/v1 instead of
# https://host/v1. Rebuild after changing this value (Next basePath is build-time).
# Default: "" (served at the domain root). Example: /omniroute to serve under https://host/omniroute
# Docker: baked at image build time via build-arg; root-path images can also apply this at
# container start (see docs/guides/DOCKER_GUIDE.md).
# OMNIROUTE_BASE_PATH=
# Client fetch/EventSource under this path are rewritten via installBasePathFetch
# (src/shared/utils/basePathFetch.ts) so absolute `/api/*` and `/v1/*` hits work
# without a reverse-proxy rewrite. Rebuild after changing (Next basePath is build-time).
#
# Browser-visible mirror of OMNIROUTE_BASE_PATH, inlined at build time so the
# dashboard endpoint display can read it client-side. Set it to the same value
# as OMNIROUTE_BASE_PATH; when unset the hook falls back to OMNIROUTE_BASE_PATH.
# Used by: src/shared/hooks/useDisplayBaseUrl.ts
# NEXT_PUBLIC_OMNIROUTE_BASE_PATH=
#
# Optional: set the public origin *with* the same path so OAuth and display URLs
# stay consistent without relying on window.location.origin alone:
# NEXT_PUBLIC_BASE_URL=https://host/omniroute
#
# Explicit path probed by the container health check. Unset, the probe derives it
# from OMNIROUTE_BASE_PATH; setting it opts back into the deep monitoring endpoint.
# Used by: scripts/dev/healthcheck.mjs
# OMNIROUTE_HEALTHCHECK_PATH=/api/monitoring/health
# Opt-in iframe embedding of the OmniRoute HTML pages (issue #10273). Off by default:
# every route ships `frame-ancestors 'none'` + `X-Frame-Options: DENY`, which is why the
# VS Code Simple Browser (used by the OmniCopilot extension's "Open Dashboard → editor"
# mode) renders a blank tab. Set this to `vscode` to switch the HTML pages — dashboard,
# login, docs, landing — to `frame-ancestors 'self' vscode-webview:` and drop
# X-Frame-Options for them (XFO cannot express a custom scheme). The API surface
# (/api, /v1, /v1beta, /a2a, /healthz and the root-level aliases) keeps the strict
# headers regardless. Only `vscode` is recognised; `1`/`true` do NOT enable it.
# Used by: next.config.mjs via scripts/build/dashboardEmbed.mjs — build-time, rebuild after changing.
# Docker: pass it as a build arg (`docker build --build-arg DASHBOARD_ALLOW_EMBED=vscode`);
# setting it on an already-built server or image does nothing.
# DASHBOARD_ALLOW_EMBED=vscode
# 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
# Connection backpressure: cap concurrent in-flight chat connections (503 + Retry-After when full).
# Used by: src/sse/utils/backpressure.ts — disabled when unset/0.
# OMNI_MAX_CONCURRENT_CONNECTIONS=0
# Optional OmniRoute-to-OmniRoute peer chaining guard. Give every instance a
# unique ID and allowlist only the other OmniRoute base URLs it may call.
# Requests to allowlisted peers carry X-OmniRoute-Peer-Trace; repeated instances
# and exhausted hop budgets are rejected with HTTP 508 before provider routing.
# OMNIROUTE_INSTANCE_ID=gateway-a
# OMNIROUTE_PEER_URLS=http://gateway-b:20128/v1
# OMNIROUTE_PEER_MAX_HOPS=4
# Port for the real-time WebSocket live monitoring server.
# Used by: src/server/ws/liveServer.ts, src/app/api/v1/ws/route.ts
# Default: 20132
# LIVE_WS_PORT=20132
# 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:20132.
# The path portion of this URL (e.g. ws://localhost:20132/live-ws -> /live-ws) is also used by the dev proxy
# (scripts/dev/standalone-server-ws.mjs) and the handshake response to route
# WebSocket upgrades. Default path: /live-ws.
# Used by: src/hooks/useLiveDashboard.ts, src/app/api/v1/ws/route.ts,
# scripts/dev/standalone-server-ws.mjs, and scripts/start-ws-server.mjs.
# Example: NEXT_PUBLIC_LIVE_WS_PUBLIC_URL=wss://ws.my-ai.com/live-ws
# NEXT_PUBLIC_LIVE_WS_PUBLIC_URL=ws://localhost:20132/live-ws
# 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` and `npm run build`. 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 (scripts/build/build-next-isolated.mjs)
# reads the same flag: Turbopack by default, 0 builds with webpack (`npm run
# build:contributor` sets it for you).
OMNIROUTE_USE_TURBOPACK=1
# Disable systemd sd_notify (Type=notify / WatchdogSec=) even when running
# under a systemd unit with NOTIFY_SOCKET set.
# Used by: scripts/dev/systemd-notify.mjs. Set to 1 to disable.
# OMNIROUTE_DISABLE_SD_NOTIFY=1
# Injected by systemd when running under a service unit (sd_notify protocol).
# Read by scripts/dev/systemd-notify.mjs — never set this yourself.
# NOTIFY_SOCKET=/run/systemd/notify
# 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
# Set to "true" or "1" to include client/egress IPs and the account prefix in
# the verbose `[ProxyEgress]` process-log line (src/lib/proxyLogger.ts). Kept
# OFF by default so the process log does not leak IPs or the account prefix.
# PROXY_LOG_INCLUDE_IPS=true
# Set to any non-empty value to emit `[omniroute completion]` diagnostics from
# the CLI shell-completion cache paths (read/refresh/write) in
# bin/cli/commands/completion.mjs. Off by default — these caches fail silently
# so a missing/corrupt cache never breaks tab-completion.
# OMNIROUTE_DEBUG_COMPLETION=1
# Set to 1 to print per-request timing diagnostics from the CLI quota commands
# to stderr (`[omniroute] GET <path> completed in Nms`).
# Used by: bin/cli/commands/quota.mjs
# OMNIROUTE_DEBUG=1
# 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" for any Podman topology. The entrypoint cannot determine
# whether the engine is local or reached through Podman Machine, so it prints
# topology-neutral guidance and links contrib/podman/README.md.
CONTAINER_HOST=docker
# Container runtime override for skill sandboxing.
# Used by: src/lib/skills/sandbox.ts + src/lib/skills/containerProvider.ts
# Values: auto | docker | apple | wsl | orbstack | podman
# - auto: OS-aware auto-detect (apple/orbstack on macOS, wsl on Windows, podman on Linux)
# - apple: Apple Container (native OCI on macOS 26+)
# - wsl: WSL Container CLI (wslc.exe on Windows)
# - orbstack: OrbStack (high-perf Linux VM + docker shim on macOS)
# - podman: Podman (rootless, daemonless)
# - docker: Docker (default fallback)
# (defined under SKILLS & SANDBOXING section below)
# ═══════════════════════════════════════════════════════════════════════════════
# 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) is unlimited (no implicit caps).
# Malformed values preserve the legacy 1000/day, 5000/week, 20000/month windows.
# 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
# Atomic admission for POST /v1/chat/completions (#7846). Large request bodies
# amplify into multiple transient representations during parsing, compression, and
# provider dispatch. Heavyweight capacity is reserved before parsing; excess work
# receives 503 + Retry-After instead of overlapping until the process OOMs.
# Used by: src/shared/middleware/chatBodyAdmission.ts
# Actual bodies at or above this size take the heavyweight lease (BYTE path,
# including POST /v1/responses) and use the same #10437 healthy-headroom escape
# as structure-heavy. Default 262144 (256 KB).
# OMNIROUTE_CHAT_LARGE_BODY_BYTES=262144
# Actual-byte hard cap enforced during bounded ingestion. Default 52428800 (50 MB).
# OMNIROUTE_CHAT_HARD_MAX_BODY_BYTES=52428800
# Legacy request-COUNT cap (#503-fanout). Now binds only when explicitly set here —
# left unset, heavyweight admission is gated by OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES below
# instead (an auto-derived byte budget), fixing coding-agent fan-out (multiple
# subagents/CLIs) collapsing to an effective concurrency of ~1 and 503ing.
# Two overlapping ~750k-token /v1/responses abort ~12 Gi heaps (#7849) — a
# memory-budget warning, not a hard product max of 2. A healthy heap may admit
# more via HEALTHY_HEADROOM. Tens of long SSE clients (40-50) is heap +
# OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES / #10110. Multiply heaps with N independent
# DATA_DIRs (#11024); never replicas>1 on one SQLite.
# OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT=1
# Override for the auto-derived ingest byte budget (#503-fanout). Default: 25% of the
# process's effective memory ceiling (V8 heap limit, or the tighter cgroup/container
# limit) divided by an 8x transient-amplification factor, clamped between 8 MiB and
# 2 GiB; explicit overrides are clamped to the same safe range. Read
# chatAdmission.maxInflightBytes/budgetSource at /api/monitoring/health before overriding.
# OMNIROUTE_CHAT_MAX_INFLIGHT_BYTES=134217728
# Heap-pressure shed ratio (heapUsed/heap_size_limit) for BYTE and STRUCTURE
# heavyweight admission (#10183, #10268, #10437): a concurrent heavyweight request
# past OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT is only shed with a retryable 503 when the
# heap is ALSO under this much pressure — on a healthy heap it is admitted via
# healthy-headroom instead. Range (0, 1]. Default 0.75.
# OMNIROUTE_CHAT_ADMISSION_HEAP_SHED_RATIO=0.75
# Bounded extra capacity for the healthy-heap fast path above OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT
# (#10437, BYTE + STRUCTURE, including bodies >= OMNIROUTE_CHAT_LARGE_BODY_BYTES):
# once this many concurrent leases are active through the healthy-heap bypass,
# further busy requests fall through to the same bounded-wait/shed path used under real heap
# pressure. 0 disables the bypass entirely. Default 1.
# OMNIROUTE_CHAT_ADMISSION_HEALTHY_HEADROOM=1
# Message count that classifies an otherwise small body as heavyweight. Default 200.
# OMNIROUTE_CHAT_HEAVY_MESSAGE_COUNT=200
# Tool count that classifies an otherwise small body as heavyweight. Default 64.
# OMNIROUTE_CHAT_HEAVY_TOOL_COUNT=64
# Conservative string-size token estimate that classifies a request as heavyweight. Default 32000.
# OMNIROUTE_CHAT_HEAVY_ESTIMATED_TOKENS=32000
# Optional opt-in hard message-count cap; excess receives compact-required 413 before
# compression can run. Unset/0 (the default) means no history cap: heap growth is bounded
# by OMNIROUTE_CHAT_MAX_HEAVY_IN_FLIGHT and the heap-pressure shed instead. Set a positive
# value only on memory-constrained deployments that need a hard ceiling.
# OMNIROUTE_CHAT_HARD_MAX_MESSAGES=0
# Skip OmniRoute's local context-window and max-input-token check for direct
# single-model requests. Default: false (dangerous opt-in).
# The upstream provider still enforces its real limits, so enabling this can
# replace an early OmniRoute 400 with an upstream context-length error.
# Prompt compression and the model's own output-token cap remain active.
# Also configurable from Dashboard > Settings > Feature Flags; no restart is
# required. Used by: src/shared/utils/featureFlags.ts and open-sse/handlers/chatCore.ts.
# DISABLE_CONTEXT_WINDOW_CHECKS=false
# How long a heavy request waits for heavyweight capacity before a retryable 503.
# A short bounded wait serializes agent bursts instead of an instant 503; 0 = instant.
# Default 2000 (2s).
# OMNIROUTE_CHAT_ADMISSION_QUEUE_MS=2000
# Queued-bytes budget for the admission wait: bounds total buffered body bytes parked
# per lane so the wait cannot amplify the heap (#4380). Over-budget waits 503 immediately.
# Default 4194304 (4 MB).
# OMNIROUTE_CHAT_ADMISSION_MAX_QUEUED_BYTES=4194304
# Per-connection virtual admission lanes (#9654): idle-lane eviction TTL. Default 60000 (60s).
# OMNIROUTE_CHAT_VIRTUAL_TTL_MS=60000
# Per-connection virtual admission lanes (#9654): max concurrent sessions (lanes). Default 64.
# OMNIROUTE_CHAT_VIRTUAL_MAX_SESSIONS=64
# Adaptive runtime virtual admission lanes (#9654): master switch for the per-tenant
# adaptive gate (system 2, open-sse/services/admission). NOTE: the TTL/MAX_SESSIONS
# vars above tune the byte-level per-connection lanes (system 1); this switch enables
# the adaptive runtime lanes. Dashboard feature flag of the same name; env wins over
# the dashboard override; restart required. Default: off.
# OMNIROUTE_CHAT_VIRTUAL_LANES=1
# 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_FORWARDING_HEADER_BUDGET_BYTES=768
# 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
# Default ON when unset. Set to false/0/no/off to disable. Truthy: true/1/yes/on.
# INPUT_SANITIZER_ENABLED=false
# INPUT_SANITIZER_MODE=warn # warn = log only | block = reject request | redact = legacy (does NOT strip injection; use PII_REDACTION_ENABLED for request PII)
# INPUT_SANITIZER_BLOCK_THRESHOLD=high # high (default) | medium | low — severities at/above this are blocked in block mode
# Legacy aliases for INPUT_SANITIZER_MODE / INPUT_SANITIZER_BLOCK_THRESHOLD (same effect).
# INJECTION_GUARD_MODE=warn
# INJECTION_GUARD_BLOCK_THRESHOLD=high
# PII detection in incoming requests (emails, phone numbers, SSNs, etc.).
# Used by: src/middleware/promptInjectionGuard.ts — extends injection guard.
# PII_REDACTION_ENABLED=false
# Redacts well-known API-key / secret-token patterns (OpenAI, Anthropic, GitHub,
# Slack, etc.) from request/response payloads before they reach providers/clients.
# Opt-in; mirrors PII_REDACTION_ENABLED. Used by: src/lib/guardrails/credentialMasker.ts.
# CREDENTIAL_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
# Per-model concurrency cap for round-robin combos (#9100).
# Used by: open-sse/services/comboConfig.ts — the round-robin combo semaphore
# was hard-capped at 3 concurrent requests per model with no override, which
# serialized higher-concurrency traffic behind that cap.
# Validated to >= 1, clamped to <= 32. | Default: 3
# COMBO_CONCURRENCY_PER_MODEL=3
# ═══════════════════════════════════════════════════════════════════════════════
# 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
# Highest-priority OmniRoute public origin override, also used by non-dashboard
# public-origin validation. Set it when external clients reach OmniRoute through
# a stable LAN, tunnel, or public origin that differs from its internal URL.
# 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
# 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
# Official OpenCode Go usage endpoint, authenticated with the connection API key.
# Override only for relays or test fixtures.
#OMNIROUTE_OPENCODE_QUOTA_URL=https://opencode.ai/zen/go/v1/usage
#OMNIROUTE_OLLAMA_CLOUD_USAGE_URL=https://ollama.com/settings
# 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
# Pin the echo-IP target used by proxy egress probes. Unset, the probe tries
# api64.ipify.org then api4.ipify.org so IPv4-only tunnels are not reported dead.
# Used by: src/lib/proxyEchoTarget.ts.
# OMNIROUTE_PROXY_ECHO_URL=https://api4.ipify.org?format=json
# 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
# New proxied TLS routing requires an explicit, comma-separated provider allowlist.
# Direct TLS keeps its legacy behavior when this is unset.
# TLS_FINGERPRINT_PROVIDERS=codex,openai
# 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
# Force container detection on (1/true) or off (0/false). Leave unset for auto-detect
# via /.dockerenv, /run/.containerenv, cgroup markers, or KUBERNETES_SERVICE_HOST.
# Used by: src/shared/utils/containerEnv.ts — gates ephemeral-home CLI config writes.
# OMNIROUTE_CONTAINER=1
# Allow CLI-tool config writes into an unmounted container path anyway (default off).
# Prefer host-side `omniroute configure` / Remote Mode, or a bind-mounted CLI_CONFIG_HOME.
# CLI equivalent: --allow-container-write. Used by: src/shared/utils/containerEnv.ts
# OMNIROUTE_ALLOW_CONTAINER_CONFIG_WRITE=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=qodercli
# CLI_QWEN_BIN=qwen
# CLI_AIDER_BIN=aider
# CLI_GOOSE_BIN=goose
# CLI_GEMINI_BIN=gemini
# CLI_KILO_BIN=kilocode
# CLI_OPENCODE_BIN=opencode
# CLI_HERMES_BIN=hermes
# CLI_FORGE_BIN=forge
# CLI_JCODE_BIN=jcode
# CLI_DEEPSEEK_TUI_BIN=deepseek-tui
# CLI_CODEWHALE_BIN=codewhale
# CLI_SMELT_BIN=smelt
# CLI_PI_BIN=pi
# CLI_CRUSH_BIN=crush
# CLI_OMP_BIN=omp
# CLI_LETTA_BIN=letta
# CLI_PRIME_AGENT_BIN=prime-agent
# Windsurf has no default binary — set this to enable binary detection for it.
# CLI_WINDSURF_BIN=windsurf
# CLI_AUGGIE_BIN=auggie
# CLI_5DIVE_BIN=5dive
# 5dive keeps root-owned auth profiles under a system state dir (its own STATE_DIR,
# default /var/lib/5dive); override here when it lives elsewhere.
# CLI_5DIVE_STATE_DIR=/var/lib/5dive
# AUGGIE_BIN=auggie
# ── ZCode (Z.ai GLM coding-plan CLI) local provider ──
# The local "zcode" provider talks to the authenticated ZCode app-server over a
# custom framed stdio protocol. Overrides below tune that stdio lifecycle.
# ZCODE_BIN=zcode
# ZCODE_ARGS=["--some-flag"]
# ZCODE_CWD=
# ZCODE_PROVIDER_ID=builtin:zai-coding-plan
# ZCODE_SERVER_RUNTIME_ROOT=~/.zcode/server
# ZCODE_SERVER_NODE=~/.zcode/server/node
# ZCODE_SERVER_ENTRY=~/.zcode/server/zcode-server.cjs
# ZCODE_STARTUP_TIMEOUT_MS=10000
# ZCODE_RPC_TIMEOUT_MS=30000
# ZCODE_TURN_TIMEOUT_MS=120000
# ZCODE_POLL_INTERVAL_MS=250
# 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=
# Days of A2A task history to keep before the daily purge deletes a row.
# Used by: src/lib/a2a/taskManager.ts (historyRetentionDays). Unset, non-numeric,
# or <= 0 falls back to the default.
# OMNIROUTE_A2A_HISTORY_RETENTION_DAYS=30
# Kill-switch for the A2A memory-hits observability feature (Orchestration Canvas
# Fase 2). Set to "0" to skip the memory recall lookup entirely; any other value
# (including unset) keeps it enabled.
# Used by: src/lib/a2a/taskExecution.ts (collectMemoryHits).
# OMNIROUTE_A2A_MEMORY_HITS=1
# Enable the offline/local Issue Agent recorded-triage endpoint.
# Used by: src/app/api/issue-agent/runs/route.ts. Default: disabled.
# OMNIROUTE_ISSUE_AGENT_ENABLED=false
# Timeout (ms) for a single Issue Agent recorded-triage run. Clamped to an internal
# maximum; falls back to the built-in default when unset or invalid.
# Used by: src/lib/issueAgent/execution.ts.
# OMNIROUTE_ISSUE_AGENT_TIMEOUT_MS=
# 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=
# Disable the optional OS keychain backend for CLI remote-context credentials.
# When enabled, context tokens stay in config.json with mode 0600 and the CLI
# prints a one-time fallback warning. Useful for deliberate headless/container
# operation; leave unset to use keytar when the native backend is available.
# Used by: bin/cli/contexts.mjs.
# OMNIROUTE_CONTEXT_KEYCHAIN_DISABLED=0
# 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
# Abort budget (ms) for MCP-server internal management reads (health, resilience,
# combos, quota, usage). Default: 10000. Used by: open-sse/mcp-server/fetchTimeout.ts
# OMNIROUTE_MCP_FETCH_TIMEOUT_MS=10000
# Abort budget (ms) for MCP hops that wait on a provider (route_request, web_search,
# web_fetch). Default: 60000. Used by: open-sse/mcp-server/fetchTimeout.ts
# OMNIROUTE_MCP_UPSTREAM_TIMEOUT_MS=60000
# Maximum number of local-corpus index instances cached in memory.
# Used by: src/lib/localCorpus/configured.ts — bounds the LRU cache of
# LocalCorpusIndex objects (one per indexed root directory). Default: 5.
# OMNIROUTE_CORPUS_CACHE_SIZE=5
# 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/lib/usage/providerLimits.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
# Proactive Claude warmup scheduler (#8848): fires a trivial request to opted-in
# OAuth connections on a cron schedule (America/Los_Angeles) so accounts do not
# hit the 5-hour sliding window cold. Off by default — set ENABLED=1 and flip
# per-connection flags in settings.claudeWarmup.connections to activate.
# Used by: src/lib/warmupScheduler.ts.
# OMNIROUTE_WARMUP_ENABLED=false
# OMNIROUTE_WARMUP_CRON="0 7 * * *"
# OMNIROUTE_WARMUP_CONCURRENCY=3
# OMNIROUTE_WARMUP_MODEL=