Rebased onto the current release/v3.8.51 tip as part of a combined provider-retirement/provenance merge batch (Designer Web, Felo Web, Runtime, GPL-derived removal, Qwen Web already landed). Large conflict set (this is the biggest PR in the batch — the common ChatGPT Web provider touches chat, images, count-tokens, session leases, and combos). Conflicts resolved: - `open-sse/config/providers/registry/chatgpt-web/*`, `open-sse/executors/chatgpt-web*`, `open-sse/handlers/imageGeneration/providers/chatgptWeb.ts`, and their tests: kept deleted, matching the PR's stated scope. - `open-sse/config/providers/registry/minimax/web/index.ts`, `open-sse/handlers/imageGeneration/providers/geminiWeb.ts`, `open-sse/executors/gemini-web.ts`'s stale image-mode branch: base-drift collisions against already-merged sibling retirements (#11691, #11708) — kept deleted / dropped the dead code, since this PR's own branch forked before those merged. - `src/shared/constants/reservedProviderPrefixes.ts`, `open-sse/executors/index.ts`, `executorProxy.ts`, `virtualFactory.ts`, `autoStrategy.ts`, `src/lib/db/providers.ts`, `src/sse/handlers/chat.ts`: combined the Designer + Runtime (Felo/Qwen) + common-ChatGPT-Web retirement guard calls at each shared chokepoint — compute-once-then-OR pattern, consistent with prior combinations in this batch. - `src/sse/services/model.ts` / `src/sse/handlers/chatHelpers.ts`: adopted this PR's new `getModelInfoOrRetirementResponse()` central wrapper (a real improvement over ad-hoc try/catch), and extended it to also catch the Designer + Runtime retirement errors it didn't originally cover, so the consolidation doesn't regress the other two mechanisms. - `src/app/api/v1/images/edits/route.ts`: this PR moved the retirement check earlier (before `enforceApiKeyPolicy`) but left the old later call+catch block in place from base drift — removed the now-redundant duplicate `resolveImageRouteModel()` call and merged the Designer catch into the earlier one. - `open-sse/config/imageRegistry.ts`, `tests/snapshots/executors/executor-map.json` (`keyCount` recomputed to 133), `tests/snapshots/provider/translate-path.json`: same "both sides inserted a different retired provider at the same slot" pattern — resolved by dropping both. - `tests/unit/chatcore-executor-proxy.test.ts`, `provider-node-reserved-prefix.test.ts`, `combo-auto-candidate-expansion.test.ts`, `messages-count-tokens-route.test.ts`, `virtual-auto-combo.test.ts`: split into independent per-mechanism test blocks (established pattern); `virtual-auto-combo.test.ts`'s old "includes cookie web-session providers" positive-inclusion test (which used chatgpt-web as its example) was retired along with the provider and replaced by this PR's negative-exclusion test for the same slot. - `docs/architecture/ARCHITECTURE.md`, `CODEBASE_DOCUMENTATION.md` (+ 4 i18n mirrors), `README.md`, `FREE-TIERS-GUIDE.md`, `docs/diagrams/free-tier-budget.svg`, `docs/screenshots/free-tier-budget-card.svg`, `docs/reference/PROVIDER_REFERENCE.md`: recomputed every stale count from the real merged state — 104 executors (`countFiles` gate logic), 351 providers (regenerated via `gen:provider-reference`), 152/351 `hasFree` entries, 445/438/7 free-tier catalog rows, 13 ToS-avoid providers, budget-card regenerated via its real generator script. One doc conflict (`oauth/` module list) needed picking HEAD's side specifically — theirs still listed the already-removed `raycast` module instead of the real `openference`. - `config/quality/test-masking-allowlist.json`: additive merge of the PR's 17 `_deletedWithReplacement` entries alongside the batch's existing ones (one real duplicate-key mistake in my first pass, caught and fixed via a `object_pairs_hook` duplicate-key check before finalizing). Also fixed two real, unrelated-to-my-merge issues surfaced by the focused suite: - `tests/unit/resolve-web-provider-host.test.ts`: the PR's own test had a typo — it asserted `perplexity-web`'s resolved host as `"perplexity.ai"`, but the provider's registered `website` is `"https://www.perplexity.ai"` and the resolver returns the URL's `host` verbatim (no www-stripping), so the correct value is `"www.perplexity.ai"` (consistent with the same test's own `url` assertion). - `tests/unit/hard-session-lease-bypass-inventory.test.ts`: this golden call-site inventory was already stale on the pristine post-#11713 tip (confirmed via a throwaway probe worktree) — `src/lib/db/providers.ts`'s 3 connection-fallback sites and a third `src/app/api/providers/route.ts` site were never added to the golden list by the earlier-merged #11698/#11720 PRs. Updated it to the real current inventory (dated inline comments explain each delta and which PR introduced it), plus this PR's own legitimate deltas (image-edits duplicate-call removal, `ChatGptWebExecutor.execute()` site removed). Focused suite green (433/433 across executor-proxy, reserved-prefix, hard-session-lease-bypass-inventory, resolve-web-provider-host, retirement/runtime-block/source-retirement/management-retirement/image-handler-retirement, migration-168, combo-auto-candidate-expansion, virtual-auto-combo, executor-map-golden and siblings), plus `typecheck:core`, `check-file-size`, and `check-changelog-integrity` clean. Thanks for the thorough provenance-hold retirement work — appreciated.
15 KiB
title, version, lastUpdated
| title | version | lastUpdated |
|---|---|---|
| Przewodnik po stealth | 3.8.40 | 2026-06-28 |
Przewodnik po stealth
Source of truth:
open-sse/utils/tlsClient.ts,open-sse/services/{claudeCodeCCH,claudeCodeFingerprint,claudeCodeObfuscation,claudeCodeCompatible}.ts,open-sse/config/cliFingerprints.ts,src/mitm/Last updated: 2026-06-28 — v3.8.40 Audience: Engineers maintaining provider-specific stealth integrations.
OmniRoute integruje się z providerami, których edge aktywnie fingerprintuje nieoficjalne klienty (TLS JA3/JA4, kolejność nagłówków, kształt body JSON, tokeny integralności). Ta strona dokumentuje powierzchnie stealth, które OmniRoute udostępnia, oraz miejsca ich implementacji.
Uwaga prawna i etyczna
Funkcje stealth istnieją po to, by OmniRoute mógł działać jako warstwa kompatybilności między oficjalnymi kontami użytkownika (Claude Code CLI, Codex, Antigravity, Cursor itd.) a ujednoliconym API OmniRoute. Nie służą do omijania fraud detection, współdzielenia poświadczeń ani naruszania Terms of Service providera. Maintainerzy oczekują, że operatorzy będą przestrzegać upstream ToS, które zaakceptowali przy tworzeniu kont.
Warstwa fingerprintingu TLS
open-sse/utils/tlsClient.ts — wreq-js (Chrome 124)
Lazy-loaded sesja wreq-js, która impersonuje Chrome 124 na macOS. Używana jako generyczny wrapper JA3/JA4 dla upstreamów za Cloudflare. Przy braku zainstalowanego wreq-js spada na natywny fetch (available = false).
- Singleton session:
browser: "chrome_124", os: "macos" - Proxy resolution (priority):
HTTPS_PROXY→HTTP_PROXY→ALL_PROXY(także lower-case) - Timeout:
TLS_CLIENT_TIMEOUT_MS(dziedziczy zFETCH_TIMEOUT_MS, domyślnie 600000) - Response z
wreq-jsjest zgodny z fetch (headers,text(),json(),clone(),body).
Pakiet stealth Claude Code
Gdy cliCompatMode jest włączony, OmniRoute przekształca wychodzące żądania Claude tak, by były nieodróżnialne od ruchu claude-cli. Współpracują trzy moduły:
claudeCodeFingerprint.ts
Oblicza 3-znakowy fingerprint cc_version osadzony w nagłówku billing:
SHA256(SALT + msg[4] + msg[7] + msg[20] + version)[:3]
FINGERPRINT_SALT = "59cf53e54c78"(hardcoded; zgodny z oficjalnym klientem)- Inputs: znaki na indeksach 4, 7, 20 tekstu pierwszej wiadomości user + string wersji
- Output: 3-znakowy prefiks hex
claudeCodeCCH.ts (Client Content Hash)
Server-side check integralności, który oficjalne Claude Code CLI liczy przez Bun/Zig. OmniRoute reimplementuje to z xxhash-wasm:
- Serialize body z placeholdrem
cch=00000; xxhash64(bytes, seed) & 0xFFFFF- Zero-padded 5-znakowy lowercase hex
- Zamień
cch=00000;na wyliczony token
Constants:
- Seed:
0x6e52736ac806831e - Pattern:
/\bcch=([0-9a-f]{5});/
claudeCodeObfuscation.ts
Wstawia Unicode zero-width joiner (U+200D) po pierwszym znaku „wrażliwych” nazw klientów, by filtry upstream nie mogły ich grepnąć. Domyślna lista słów:
opencode, open-code, cline, roo-cline, roo_cline, cursor, windsurf,
aider, continue.dev, copilot, avante, codecompanion
Stosowane do: bloków system, całego messages[].content oraz tools[].description / tools[].function.description. Nadpisywalne przez operatora przez setSensitiveWords().
claudeCodeCompatible.ts — providery anthropic-compatible-cc-*
Dla zewnętrznych relay Anthropic, które akceptują tylko ruch „prawdziwego Claude Code”:
CLAUDE_CODE_COMPATIBLE_USER_AGENT = "claude-cli/2.1.219 (external, sdk-cli)"CLAUDE_CODE_COMPATIBLE_STAINLESS_PACKAGE_VERSION = "0.94.0"CLAUDE_CODE_COMPATIBLE_STAINLESS_RUNTIME_VERSION = "v26.3.0"anthropic-beta = "claude-code-20250219,interleaved-thinking-2025-05-14,effort-2025-11-24"domyślnie- Per-connection toggle „Enable redact-thinking beta” dodaje
redact-thinking-2026-02-12, gdy upstream CC Compatible wymaga redacted thinking streams - Per-connection toggle „Enable summarized thinking display” zapisuje
providerSpecificData.requestDefaults.summarizeThinkingi dodajedisplay: "summarized"do żądań thinking CC Compatible, które nie miały jeszcze ustawionego display mode CONTEXT_1M_BETA_HEADER = "context-1m-2025-08-07"(rodzina Opus/Sonnet 4.x)- Domyślna ścieżka:
/v1/messages?beta=true
Moduły siostrzane w tym samym bundle:
claudeCodeConstraints.ts— reguły temperature + cache-controlclaudeCodeToolRemapper.ts— remap nazw tooliclaudeCodeExtraRemap.ts— dodatkowa normalizacja payloadu
Stealth Antigravity
Żądania Antigravity zachowują tekst wywołującego bajt po bajcie. OmniRoute nie wstawia zero-width characters do promptów ani nie zmienia nazw/nie wstrzykuje tooli, by udawać klienta IDE.
antigravityHeaderScrub.ts
Usuwa markery Stainless SDK (x-stainless-lang, x-stainless-package-version, x-stainless-os, x-stainless-arch, x-stainless-runtime, x-stainless-runtime-version, x-stainless-timeout, x-stainless-retry-count, x-stainless-helper-method) przed forwardem.
⚠️ Ryzyko: ANTIGRAVITY_CREDITS=always (hot spot banów kont)
ANTIGRAVITY_CREDITS=always (konsumowane przez open-sse/executors/antigravity.ts) kieruje każde żądanie przez Antigravity AI Credit Overages (płatne kredyty Google) zamiast pozwolić free-tier quota Google bramkować ruch. Jest to udokumentowane jako feature, ale to najczęstszy raport naruszenia ToS, jaki widzimy — wiele kont Google Ultra zostało zbanowanych z 403 / "service disabled for ToS violation" / insufficient_quota po kilku godzinach z =always.
Egzekucja upstream jest po stronie Google, nie ma nic, co OmniRoute mógłby temu zapobiec. Nazwa zmiennej env i istniejąca dokumentacja sprawiają wrażenie bezpiecznego przełącznika; nim nie jest.
Dlaczego to silniej przyciąga abuse detection niż użycie wyłącznie free-tier:
- Utrzymany zautomatyzowany spend na jednym koncie Google flaguje się inaczej niż free-tier hits-quota-and-stops.
- Credit overages nie mają rate ceiling, więc źle skonfigurowany klient może spalić kilkaset USD w minutach i wyglądać jak odsprzedaż API key lub bot traffic.
- Wielu użytkowników OmniRoute uderzających w overage credits równolegle z tego samego zewnętrznego IP wzmacnia sygnał.
Zalecana postawa:
- Zostaw domyślne
ANTIGRAVITY_CREDITS=off, chyba że operator świadomie akceptuje ryzyko płatnych kredytów i egzekucji konta.retrynajpierw wysyła normalne żądanie i wstrzykuje credits co najwyżej raz po kwalifikującym się quota 429;alwayswstrzykuje credits już przy pierwszym żądaniu. - Rozkładaj obciążenie między providery przez Auto-Combo (
model: "auto"lub combokr/glm/etc) zamiast nasycać pojedyncze konto Antigravity. - Ustaw per-connection limity RPM na stronie edycji providera Antigravity (Dashboard → Providers → Antigravity → connection → rate limit). 30–60 RPM to obronny górny limit przy sustained use.
- Używaj stabilnej, kontrolowanej przez operatora sieci upstream i unikaj współdzielenia jednego konta między niepowiązanych użytkowników lub workloady.
- Przy banie: odwołaj się przez
support.google.com→ „Restore Workspace/Account access” z dokładnym body odpowiedziquota_exceeded/service disabledod Google. Przywrócenie nie jest gwarantowane.
Referencja środowiska dokumentuje implikacje konta i spendu dla każdego trybu credits.
Punkty styku:
open-sse/executors/antigravity.ts— czytaprocess.env.ANTIGRAVITY_CREDITSsrc/lib/oauth/providers/antigravity.ts— plumbing poświadczeń- Oryginalny raport incydentu: Discussion #1183
Rejestr fingerprintów CLI — open-sse/config/cliFingerprints.ts
Tabela per-provider, która pinuje dokładną kolejność nagłówków i kolejność pól body JSON zrzutowaną z mitmproxy traces oficjalnych CLI. Obecnie zarejestrowane: codex, claude, plus profile wyprowadzane w runtime w providerHeaderProfiles.ts dla antigravity i github.
interface CliFingerprint {
headerOrder: string[]; // case-sensitive
bodyFieldOrder: string[]; // top-level JSON keys
userAgent?: string | (() => string);
extraHeaders?: Record<string, string>;
}
Przełączanie per provider przez env (patrz poniżej). Gdy wyłączone, nagłówki/klucze body pojawiają się w kolejności, jaką dał Node/JSON — łatwe do fingerprintu.
Proxy MITM (Antigravity, Linux/macOS/Windows)
Dla CLI, których binarów nie da się przekierować przez OPENAI_BASE_URL, OmniRoute uruchamia lokalny proxy z terminacją TLS. Endpointy są pod src/app/api/cli-tools/antigravity-mitm/.
| Method | Endpoint | Purpose |
|---|---|---|
| GET | /api/cli-tools/antigravity-mitm |
Status — running, pid, dnsConfigured, certExists |
| POST | /api/cli-tools/antigravity-mitm |
Start MITM (requires apiKey + sudoPassword) |
| DELETE | /api/cli-tools/antigravity-mitm |
Stop MITM |
| GET | /api/cli-tools/antigravity-mitm/alias |
List model aliases |
| PUT | /api/cli-tools/antigravity-mitm/alias |
Save model aliases for a tool |
Cel interceptowanego hosta: daily-cloudcode-pa.googleapis.com (upstream Antigravity).
Sekwencja startu (src/mitm/manager.ts::startMitm)
- Wygeneruj self-signed cert przez
selfsigned(RSA-2048, SHA-256, 1y) —cert/generate.ts - Zainstaluj cert w systemowym trust store —
cert/install.ts - Dodaj wpis hosts
127.0.0.1 daily-cloudcode-pa.googleapis.com—dns/dnsConfig.ts - Spawn
src/mitm/server.cjszROUTER_API_KEY+MITM_LOCAL_PORT(domyślnie443) - Zapisz PID do
<DATA_DIR>/mitm/.mitm.pid
Dynamiczna detekcja trust-store na Linux — cert/install.ts
getLinuxCertConfig() przechodzi listę priorytetów i wybiera pierwszy istniejący katalog:
| Distro family | Directory | Update command |
|---|---|---|
| Debian / Ubuntu | /usr/local/share/ca-certificates |
update-ca-certificates |
| Arch / CachyOS / Manjaro | /etc/ca-certificates/trust-source/anchors |
update-ca-trust |
| Fedora / RHEL / CentOS | /etc/pki/ca-trust/source/anchors |
update-ca-trust |
| openSUSE | /etc/pki/trust/anchors |
update-ca-certificates |
Nazwa pliku cert: omniroute-mitm.crt. Dopasowanie fingerprintu przez getCertFingerprint() (SHA-1 z DER).
Dodatkowo updateNssDatabases() instaluje do per-user NSS DB, gdy dostępne jest certutil: ~/.pki/nssdb, ~/snap/chromium/.../nssdb, wszystkie profile Firefox (w tym snap), pod nickiem OmniRoute MITM Root CA.
macOS / Windows
- macOS:
security add-trusted-cert -d -r trustRoot -k /Library/Keychains/System.keychain - Windows: elevated PowerShell →
certutil -addstore Root
Auth
Wszystkie endpointy MITM wymagają management auth (requireCliToolsAuth). Hasło sudo jest cache'owane w module scope (nigdy globalThis) i czyszczone przy stopMitm().
Nadpisania User-Agent — zmienne env (.env.example sekcja 12)
| Variable | Default |
|---|---|
CLAUDE_USER_AGENT |
claude-cli/2.1.219 (external, cli) |
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 |
QODER_USER_AGENT |
Qoder-Cli |
CURSOR_USER_AGENT |
Cursor/3.4 |
Konsumowane przez open-sse/executors/base.ts::buildHeaders() przez dynamic lookup. Podbijaj je, gdy providery wypuszczają nowe wersje CLI — stare stringi UA zaczynają być odrzucane jako outdated clients.
Przełączniki trybu kompatybilności CLI (.env.example sekcja 13)
| Variable | Effect |
|---|---|
CLI_COMPAT_CODEX=1 |
Codex fingerprint |
CLI_COMPAT_CLAUDE=1 |
claude-cli fingerprint |
CLI_COMPAT_GITHUB=1 |
GitHub Copilot Chat fingerprint |
CLI_COMPAT_ANTIGRAVITY=1 |
Antigravity fingerprint |
CLI_COMPAT_KIRO=1 |
Kiro |
CLI_COMPAT_CURSOR=1 |
Cursor |
CLI_COMPAT_KIMI_CODING=1 |
Kimi Coding |
CLI_COMPAT_KILOCODE=1 |
KiloCode |
CLI_COMPAT_CLINE=1 |
Cline |
CLI_COMPAT_ALL=1 |
Enable all of the above |
IP providera jest zawsze zachowane — toggle tylko przekształca wire image żądania, nie przełącza egress IP.
Sanityzacja nagłówków inbound
OmniRoute czyści inbound nagłówki klienta przed forwardem, by żądanie przychodzące z Cursor nie wyciekało User-Agent: Cursor/X.Y.Z do upstream Claude. Zobacz src/shared/constants/upstreamHeaders.ts — denylist utrzymywany w lockstep ze schematami Zod i testami jednostkowymi.
Aktualizacja fingerprintów przy rotacji u providera
- Złap ruch oficjalnego CLI przez
mitmproxy(TLS interception + dump) - Wyodrębnij JA3/JA4 i literową kolejność nagłówków
- Zaktualizuj odpowiedni wpis
CLI_FINGERPRINTS[...] - Podbij pasujący domyślny
*_USER_AGENTw.env.example - Jeśli zmienił się sam TLS handshake, zaktualizuj odpowiedni wrapper providera lub opcję wreq-js
browser: - Odpal testy TLS danego providera i ręcznego canary przeciwko żywemu providerowi
- Wypuść w patch release; udokumentuj w
CHANGELOG.md
Testy
open-sse/services/__tests__/claudeTlsClient.test.ts— zachowanie współdzielonego wrappera TLStests/unit/anthropic-cache-fingerprint.test.ts— determinizm fingerprintutests/unit/chatgpt-web-source-retirement.test.ts— wspólne źródło ChatGPT Web pozostaje nieobecne, a Codex Web pozostaje zachowany
Zobacz też
- RESILIENCE_GUIDE.md — co się dzieje, gdy ścieżka stealth dostanie
403 - TROUBLESHOOTING.md
- ENVIRONMENT.md — pełna referencja env
- CLI-TOOLS.md — widok operatora na workflow MITM