Release v3.8.40

v3.8.40 cycle integration → main. All test gates green (Unit/Integration/Coverage/Node-compat/Quality-Ratchet). The only red check, 'PR Test Policy', is the test-masking heuristic firing on the cumulative ~57-commit release diff (legitimate assert consolidations already reviewed per-PR — Gemini CLI removal #5246, retired GPT models #5280, provider catalog refreshes); overridden with --admin per the documented release-PR convention. CodeQL/SonarQube advisory scans non-blocking; #5278's code already passed CodeQL on main. Homologated on VPS 192.168.0.15 (v3.8.40 healthy).
This commit is contained in:
Diego Rodrigues de Sa e Souza
2026-06-29 08:40:06 -03:00
committed by GitHub
parent 1c18be4f8f
commit 7c23dab64d
1007 changed files with 16451 additions and 21343 deletions

View File

@@ -53,6 +53,24 @@ omniroute status
Default salt: `omniroute-cli-auth-v1`
## Legacy format (SHA-256, 32-char) — still accepted
Before the HMAC format above, the CLI derived its token as
`SHA-256(machineId + salt).hex[0..32]` (a 32-char prefix) in
`bin/cli/utils/cliToken.mjs` (`getLegacyCliTokenSync` in `src/lib/machineToken.ts`).
For backwards compatibility the server accepts **both** formats: the verifier builds
`expectedTokens = [getMachineTokenSync(), getLegacyCliTokenSync()]` and compares the
incoming header against each with `timingSafeEqual`
(`src/server/authz/policies/management.ts` and `src/lib/middleware/cliTokenAuth.ts`).
So a token is valid if it matches **either** the 64-char HMAC digest or the 32-char
legacy SHA-256 prefix.
**Opt-out:** set `OMNIROUTE_DISABLE_CLI_TOKEN=true` (env or `.env`) to disable the CLI
token mechanism entirely; all access then requires an explicit API key. On multi-user
hosts this is recommended, since `machine-id` is per-device (not per-user) and another
user on the same host could compute the same token.
## Files
| File | Purpose |

View File

@@ -1,47 +0,0 @@
---
title: "CLI Machine-ID Token Authentication"
---
# CLI Machine-ID Token Authentication
OmniRoute's CLI uses a **machine-derived token** to authenticate to the local server without requiring an explicit API key. This enables zero-config local use while preserving security for remote access.
## How it works
1. **CLI side** (`bin/cli/utils/cliToken.mjs`): computes `SHA-256(machineId + salt).hex[0..32]` using [`node-machine-id`](https://github.com/automation-stack/node-machine-id) and injects the result as the `x-omniroute-cli-token` header on every `apiFetch` call.
2. **Server side** (`src/lib/middleware/cliTokenAuth.ts`): `isCliTokenAuthValid(request)` accepts the token only if:
- `OMNIROUTE_DISABLE_CLI_TOKEN` is not `"true"`
- The header is present and exactly 32 hex characters
- The originating IP is loopback (`127.0.0.1`, `::1`, `::ffff:127.0.0.1`)
- The token matches the server's own machine-derived hash (timing-safe compare)
3. `requireManagementAuth` and other route guards call `isCliTokenAuthValid` before checking API keys — so the CLI gets transparent localhost access without storing any credential.
## Threat model
| Scenario | Risk | Mitigation |
| ------------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| Another user on same host | Could compute the same token | `machine-id` is per-device; on single-user desktops this is acceptable. Use `OMNIROUTE_DISABLE_CLI_TOKEN=true` in multi-user setups. |
| Token leak via logs | Logs may reveal the token | The header value is masked in audit logs (`x-omniroute-cli-token: ***`). |
| Replay attack | Token is static | Only accepted from `127.0.0.1`/`::1`. Rejected for any other `x-forwarded-for` IP. |
| Reuse on another machine | Machine-bound by design | `node-machine-id` reads `/etc/machine-id` (Linux), `IOPlatformUUID` (macOS), `MachineGuid` (Windows). Different per host. |
## Opt-out
Set `OMNIROUTE_DISABLE_CLI_TOKEN=true` in `.env` or the server environment to disable this mechanism entirely. All access then requires an explicit API key.
## Audit logging
Every request authenticated via CLI token is logged with `event: "cli_token_auth"`, the source IP, user-agent, path, and the first 8 characters of the machine-id hash (non-reversible).
## API key precedence
An explicit `Authorization: Bearer <key>` header (from `--api-key` or `OMNIROUTE_API_KEY`) always takes precedence over the CLI token and is evaluated first.
## Related files
- `bin/cli/utils/cliToken.mjs` — CLI token generation
- `src/lib/middleware/cliTokenAuth.ts` — server validation
- `src/lib/api/requireManagementAuth.ts` — integration into auth pipeline
- `tests/unit/cli-machine-token.test.ts` — unit tests

View File

@@ -1,13 +1,13 @@
---
title: "Compliance & Audit"
version: 3.8.2
lastUpdated: 2026-05-13
version: 3.8.40
lastUpdated: 2026-06-28
---
# Compliance & Audit
> **Source of truth:** `src/lib/compliance/`, `src/app/api/compliance/`
> **Last updated:** 2026-05-13 — v3.8.0
> **Last updated:** 2026-06-28 — v3.8.40
OmniRoute records administrative actions, authentication events, provider
credential lifecycle changes, and MCP tool invocations to SQLite-backed audit

View File

@@ -1,7 +1,7 @@
---
title: "Egress IP Family Policy (IPv4/IPv6)"
version: 3.8.24
lastUpdated: 2026-06-13
version: 3.8.40
lastUpdated: 2026-06-28
---
# Egress IP Family Policy (IPv4/IPv6)

View File

@@ -1,14 +1,14 @@
---
title: "Error Message Sanitization"
version: 3.8.2
lastUpdated: 2026-05-14
version: 3.8.40
lastUpdated: 2026-06-28
---
# Error Message Sanitization
> **Source of truth:** `open-sse/utils/error.ts` — `sanitizeErrorMessage`, `buildErrorBody`, `createErrorResult`
> **Tests:** `tests/unit/error-message-sanitization.test.ts`
> **Last updated:** 2026-05-14 — v3.8.0
> **Last updated:** 2026-06-28 — v3.8.40
> **Audience:** Any engineer touching error responses (HTTP routes, SSE streams, executors, MCP handlers).
> **Status:** **MANDATORY** for every code path that returns an error message to a client.
@@ -124,7 +124,7 @@ const safe = String(err).split("\n")[0];
- `sanitizeErrorMessage` handles `null`/`undefined`/`Error` instance inputs safely.
- `buildErrorBody` never exposes stack traces in its `message` field.
When adding a new route or executor, copy the assertion pattern from this file. The coverage gate (`npm run test:coverage`) enforces ≥75% statements/lines/functions and ≥70% branches — error paths must be covered.
When adding a new route or executor, copy the assertion pattern from this file. The coverage gate (`npm run test:coverage`) enforces ≥60% statements/lines/functions/branches — error paths must be covered.
## Related controls

View File

@@ -1,13 +1,13 @@
---
title: "Guardrails"
version: 3.8.31
lastUpdated: 2026-06-20
version: 3.8.40
lastUpdated: 2026-06-28
---
# Guardrails
> **Source of truth:** `src/lib/guardrails/`
> **Last updated:** 2026-06-20 — v3.8.31 (injection-guard coverage + 16 KB scan bound + red-team)
> **Last updated:** 2026-06-28 — v3.8.40 (injection-guard coverage + 16 KB scan bound + red-team)
Guardrails enforce safety, policy, and content transformations at the boundary
between OmniRoute and upstream providers. Each guardrail can inspect (and
@@ -220,13 +220,13 @@ Guardrails that throw are recorded with `error: <message>` and logged via
Environment variables read by the built-in guardrails:
| Variable | Used by | Effect |
| ------------------------------------- | -------------------------------- | ----------------------------------------------------- |
| `INPUT_SANITIZER_ENABLED` | `prompt-injection` | Set `false` to disable detection entirely. |
| `INPUT_SANITIZER_MODE` | `prompt-injection`, `pii-masker` | Shared mode: `warn`, `block`, `log`, or `redact`. |
| Variable | Used by | Effect |
| ------------------------------------- | -------------------------------- | ------------------------------------------------------------------------------------------------ |
| `INPUT_SANITIZER_ENABLED` | `prompt-injection` | Set `false` to disable detection entirely. |
| `INPUT_SANITIZER_MODE` | `prompt-injection`, `pii-masker` | Shared mode: `warn`, `block`, `log`, or `redact`. |
| `INJECTION_GUARD_MODE` | `prompt-injection` | Mode for the injection guard; also a DB feature flag that **overrides** the env vars (DB > ENV). |
| `PII_REDACTION_ENABLED` | `pii-masker` | When `true` + mode `redact`, request PII is stripped. |
| `PII_RESPONSE_SANITIZATION` / `_MODE` | `pii-masker` (downstream) | Controls response-side masker behavior. |
| `PII_REDACTION_ENABLED` | `pii-masker` | When `true` + mode `redact`, request PII is stripped. |
| `PII_RESPONSE_SANITIZATION` / `_MODE` | `pii-masker` (downstream) | Controls response-side masker behavior. |
The Vision Bridge reads runtime config from the DB-backed settings store
(`getSettings()`), not env vars: `visionBridgeEnabled`, `visionBridgeModel`,
@@ -290,11 +290,11 @@ O injection-guard (`createInjectionGuard` / `withInjectionGuard`) cobre todas as
que aceitam prompt do usuário. Respeita `INJECTION_GUARD_MODE` (default `warn` = só loga;
`block` = retorna HTTP 400 `SECURITY_001`).
| Tipo | Rotas | Modo default |
|---|---|---|
| Texto (já existente) | `/v1/chat/completions`, `/v1/completions`, `/v1/relay/chat/completions` | warn |
| Generativas | `/v1/messages`, `/v1/responses`, `/v1/images/generations`, `/v1/images/edits`, `/v1/videos/generations`, `/v1/music/generations`, `/v1/audio/speech` | warn |
| Dados | `/v1/embeddings`, `/v1/rerank`, `/v1/search`, `/v1/moderations` | warn |
| Tipo | Rotas | Modo default |
| -------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | ------------ |
| Texto (já existente) | `/v1/chat/completions`, `/v1/completions`, `/v1/relay/chat/completions` | warn |
| Generativas | `/v1/messages`, `/v1/responses`, `/v1/images/generations`, `/v1/images/edits`, `/v1/videos/generations`, `/v1/music/generations`, `/v1/audio/speech` | warn |
| Dados | `/v1/embeddings`, `/v1/rerank`, `/v1/search`, `/v1/moderations` | warn |
A extração de texto (`extractMessageContents`) cobre `messages`/`input`/`prompt`/`query`+`documents`/`instructions`/`system`.

View File

@@ -1,7 +1,7 @@
---
title: "MITM TPROXY Transparent Decrypt"
version: 3.8.31
lastUpdated: 2026-06-20
version: 3.8.40
lastUpdated: 2026-06-28
---
# MITM TPROXY Transparent Decrypt
@@ -35,12 +35,12 @@ exchange, and re-encrypts the request to the original destination.
The other four capture modes each have a limitation:
| Mode | How traffic is steered | Limitation |
|------|------------------------|------------|
| AgentBridge | `/etc/hosts` DNS spoof of a fixed host set | only the registered IDE-agent hosts |
| Custom Hosts | `/etc/hosts` DNS spoof per host | one entry per host; sudo to edit hosts |
| HTTP_PROXY | `HTTP_PROXY`/`HTTPS_PROXY` env | only apps that honor the env var |
| System-wide proxy | OS proxy settings | mutates global state; needs revert |
| Mode | How traffic is steered | Limitation |
| ----------------- | ------------------------------------------ | -------------------------------------- |
| AgentBridge | `/etc/hosts` DNS spoof of a fixed host set | only the registered IDE-agent hosts |
| Custom Hosts | `/etc/hosts` DNS spoof per host | one entry per host; sudo to edit hosts |
| HTTP_PROXY | `HTTP_PROXY`/`HTTPS_PROXY` env | only apps that honor the env var |
| System-wide proxy | OS proxy settings | mutates global state; needs revert |
TPROXY transparent decrypt steers traffic at the **kernel** layer instead. It
marks new local outbound TCP connections to a target port (default `443`) in the
@@ -62,12 +62,12 @@ installs (see [§4](#4-the-per-sni-dynamic-ca-and-trust-store-installer)).
## §2 Requirements
| Requirement | Detail |
|-------------|--------|
| **OS** | Linux only — **IP_TRANSPARENT** is a Linux-only socket option. The loader returns "unavailable" on every other platform. |
| **Privilege** | The **CAP_NET_ADMIN** capability to create the transparent socket and apply `iptables`/`ip` rules — in practice, run as root. |
| **Native addon** | A tiny N-API addon (`src/mitm/tproxy/native/transparent.c`) must be built or shipped as a prebuild. See [§3](#3-the-native-ip_transparent-addon). |
| **Kernel modules** | `iptables` with the `TPROXY`, `mangle`, and `mark` match support (validated against kernel 6.8.0). |
| Requirement | Detail |
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------- |
| **OS** | Linux only — **IP_TRANSPARENT** is a Linux-only socket option. The loader returns "unavailable" on every other platform. |
| **Privilege** | The **CAP_NET_ADMIN** capability to create the transparent socket and apply `iptables`/`ip` rules — in practice, run as root. |
| **Native addon** | A tiny N-API addon (`src/mitm/tproxy/native/transparent.c`) must be built or shipped as a prebuild. See [§3](#3-the-native-ip_transparent-addon). |
| **Kernel modules** | `iptables` with the `TPROXY`, `mangle`, and `mark` match support (validated against kernel 6.8.0). |
**Graceful degradation:** if any requirement is missing (non-Linux, no toolchain,
addon not built), the addon loader (`src/mitm/tproxy/transparentSocket.ts::loadTransparentAddon`)
@@ -80,16 +80,16 @@ OmniRoute keeps working.
## §3 The native IP_TRANSPARENT addon
Node's `net` module cannot `setsockopt(IP_TRANSPARENT)` *before* `bind()`, which
Node's `net` module cannot `setsockopt(IP_TRANSPARENT)` _before_ `bind()`, which
TPROXY requires (otherwise the kernel drops the redirected packets). The addon
(`src/mitm/tproxy/native/transparent.c`, built via `binding.gyp`) is a small N-API
module exposing three functions, consumed through `transparentSocket.ts`:
| Addon function | Socket work | Used for |
|----------------|-------------|----------|
| Addon function | Socket work | Used for |
| ------------------------------------- | ---------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------- |
| `createTransparentListener(ip, port)` | `socket()` + **SO_REUSEADDR** + **IP_TRANSPARENT** + `bind()` + `listen()`, returns the raw fd | the transparent capture listener (Node adopts the fd via `server.listen({ fd })`) |
| `setSocketMark(fd, mark)` | `setsockopt` **SO_MARK** on an existing fd | anti-loop (mark the proxy's own sockets) |
| `connectMarked(ip, port, mark)` | `socket()` + **SO_MARK** **before** a non-blocking `connect()`, returns fd | the re-encrypted upstream forward (the SYN carries the mark) |
| `setSocketMark(fd, mark)` | `setsockopt` **SO_MARK** on an existing fd | anti-loop (mark the proxy's own sockets) |
| `connectMarked(ip, port, mark)` | `socket()` + **SO_MARK** **before** a non-blocking `connect()`, returns fd | the re-encrypted upstream forward (the SYN carries the mark) |
The original destination is read from `socket.localAddress`/`localPort` — TPROXY
preserves it, so there is no **SO_ORIGINAL_DST**/NAT lookup.
@@ -102,7 +102,7 @@ npm run build:native:tproxy # cd src/mitm/tproxy/native && node-gyp rebuild
```
- During `npm run build`, `scripts/build/build-tproxy-native.mjs` runs `node-gyp
rebuild`. It is **Linux-only and non-fatal** — a missing toolchain just leaves
rebuild`. It is **Linux-only and non-fatal** — a missing toolchain just leaves
the capture mode unavailable.
- `assembleStandalone.mjs` copies `build/Release/transparent.node` into the
standalone bundle; `transparentSocket.ts` resolves it both module-relative and
@@ -149,12 +149,12 @@ other.
`installTproxyCa(caPem, sudoPassword?)` detects the distro's anchor directory
(in order: Debian-style first) and runs the matching refresh command:
| Anchor directory | Refresh command |
|------------------|-----------------|
| `/usr/local/share/ca-certificates` | `update-ca-certificates` |
| `/etc/ca-certificates/trust-source/anchors` | `update-ca-trust` |
| `/etc/pki/ca-trust/source/anchors` | `update-ca-trust` |
| `/etc/pki/trust/anchors` | `update-ca-certificates` |
| Anchor directory | Refresh command |
| ------------------------------------------- | ------------------------ |
| `/usr/local/share/ca-certificates` | `update-ca-certificates` |
| `/etc/ca-certificates/trust-source/anchors` | `update-ca-trust` |
| `/etc/pki/ca-trust/source/anchors` | `update-ca-trust` |
| `/etc/pki/trust/anchors` | `update-ca-certificates` |
Install stages the PEM to a temp file, then (privileged) `mkdir -p` the anchor
dir, `cp` the staged file into it, and runs the refresh command. `uninstallTproxyCa()`
@@ -229,15 +229,15 @@ forward path defends against this with a bypass socket mark (**SO_MARK**):
## §6 Security
| Control | Detail |
|---------|--------|
| **Loopback-only API** | `/api/tools/agent-bridge/tproxy` is covered by the `/api/tools/agent-bridge/` prefix in `LOCAL_ONLY_API_PREFIXES` (`src/server/authz/routeGuard.ts`). Loopback enforcement runs **before** auth (Hard Rules #15 + #17) — a leaked JWT over a tunnel cannot start TPROXY capture, which applies `iptables` rules and installs a trust-store CA via child processes. |
| **Dedicated CA slot** | The dynamic CA installs to `omniroute-tproxy-ca.crt`, never clobbering the static MITM cert. |
| **CA key never leaves the host** | `DynamicCertStore` holds the CA key in memory; it is not exported. |
| **Secret masking** | `maskSecret()` on request/response bodies and `sanitizeHeaders()` on headers run **before** `globalTrafficBuffer.push()`. |
| **No shell interpolation** | All `iptables`/`ip`/trust-store commands run via `execFile`/`execFileWithPassword` with arg arrays (Hard Rule #13). |
| **Upstream cert verification** | The re-encrypted forward verifies the upstream cert by default (`rejectUnauthorized: true`). |
| **Error sanitization** | The route's error responses go through `sanitizeErrorMessage()` (Hard Rule #12). |
| Control | Detail |
| -------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| **Loopback-only API** | `/api/tools/agent-bridge/tproxy` is covered by the `/api/tools/agent-bridge/` prefix in `LOCAL_ONLY_API_PREFIXES` (`src/server/authz/routeGuard.ts`). Loopback enforcement runs **before** auth (Hard Rules #15 + #17) — a leaked JWT over a tunnel cannot start TPROXY capture, which applies `iptables` rules and installs a trust-store CA via child processes. |
| **Dedicated CA slot** | The dynamic CA installs to `omniroute-tproxy-ca.crt`, never clobbering the static MITM cert. |
| **CA key never leaves the host** | `DynamicCertStore` holds the CA key in memory; it is not exported. |
| **Secret masking** | `maskSecret()` on request/response bodies and `sanitizeHeaders()` on headers run **before** `globalTrafficBuffer.push()`. |
| **No shell interpolation** | All `iptables`/`ip`/trust-store commands run via `execFile`/`execFileWithPassword` with arg arrays (Hard Rule #13). |
| **Upstream cert verification** | The re-encrypted forward verifies the upstream cert by default (`rejectUnauthorized: true`). |
| **Error sanitization** | The route's error responses go through `sanitizeErrorMessage()` (Hard Rule #12). |
**The MITM CA is a powerful capability.** A CA trusted by the OS that can sign any
host means anything OmniRoute intercepts can be decrypted. It is gated behind the
@@ -273,7 +273,7 @@ iptables -t mangle -A PREROUTING -p tcp --dport <dport> -m mark --mark <mark> -j
Revert deletes them in reverse: `PREROUTING -D`, `OUTPUT -D`, `ip route del`, `ip rule del`.
> The recipe is **OUTPUT-based** because the MITM use case is *local* outbound
> The recipe is **OUTPUT-based** because the MITM use case is _local_ outbound
> traffic (apps on the same host), which TPROXY in `PREROUTING` alone does not
> see — `PREROUTING` only sees forwarded traffic. The `OUTPUT` chain marks new
> local connections, the `ip rule` reroutes them to local delivery (`lo`), and
@@ -287,14 +287,14 @@ The start request (`POST /api/tools/agent-bridge/tproxy`) accepts the following
fields, validated by `StartTproxyBodySchema` (`tproxy/route.ts`). All are optional
and fall back to their defaults:
| Field | Type | Default | Notes |
|-------|------|---------|-------|
| **dport** | int (165535) | `443` | Destination TCP port to transparently intercept |
| **mark** | int (≥1) | `0x2333` | Firewall mark set on `OUTPUT`, matched by the `ip rule` + `PREROUTING` |
| **onPort** | int (165535) | `8443` | Port the transparent (**IP_TRANSPARENT**) listener binds |
| **routeTable** | int (≥1) | `233` | Policy-routing table id holding the `local 0.0.0.0/0` route |
| **bypassMark** | int (≥1, ≠ `mark`) | `0x539` | The bypass socket mark (**SO_MARK**) the proxy sets on its own upstream conns; excluded in `OUTPUT` (anti-loop) |
| **sudoPassword** | string | — | Non-root desktops only: authorizes the trust-store install; ignored when root |
| Field | Type | Default | Notes |
| ---------------- | ------------------ | -------- | --------------------------------------------------------------------------------------------------------------- |
| **dport** | int (165535) | `443` | Destination TCP port to transparently intercept |
| **mark** | int (≥1) | `0x2333` | Firewall mark set on `OUTPUT`, matched by the `ip rule` + `PREROUTING` |
| **onPort** | int (165535) | `8443` | Port the transparent (**IP_TRANSPARENT**) listener binds |
| **routeTable** | int (≥1) | `233` | Policy-routing table id holding the `local 0.0.0.0/0` route |
| **bypassMark** | int (≥1, ≠ `mark`) | `0x539` | The bypass socket mark (**SO_MARK**) the proxy sets on its own upstream conns; excluded in `OUTPUT` (anti-loop) |
| **sudoPassword** | string | — | Non-root desktops only: authorizes the trust-store install; ignored when root |
There are **no environment variables** for TPROXY — all configuration is via the
POST body or the defaults above.
@@ -365,17 +365,17 @@ See [§5 Anti-loop](#anti-loop-so_mark).
## §11 Source map
| File | Responsibility |
|------|----------------|
| `src/mitm/tproxy/commands.ts` | Pure `iptables`/`ip` apply + revert command builder; `validateTproxyConfig` |
| `src/mitm/tproxy/setup.ts` | Transactional `applyTproxy` / `revertTproxy` runner (rollback on failure) |
| `src/mitm/tproxy/transparentSocket.ts` | Native-addon loader (`loadTransparentAddon`), `createTransparentListenerFd`, `connectMarked`, `setSocketMark`, `isTransparentSocketAvailable` |
| `src/mitm/tproxy/native/transparent.c` | N-API addon: `createTransparentListener` (IP_TRANSPARENT), `setSocketMark`, `connectMarked` |
| `src/mitm/tproxy/native/binding.gyp` | node-gyp build manifest |
| `src/mitm/tproxy/dynamicCert.ts` | `DynamicCertStore` — per-SNI dynamic CA + leaf cache |
| `src/mitm/tproxy/caTrust.ts` | OS trust-store install/uninstall (`installTproxyCa` / `uninstallTproxyCa`, dedicated slot) |
| `src/mitm/tproxy/tlsCapture.ts` | TLS-terminating decrypt engine + re-encrypted anti-loop forward |
| `src/mitm/tproxy/captureMode.ts` | Transparent-listener orchestration; reads orig dest from `socket.localAddress` |
| `src/mitm/tproxy/captureManager.ts` | Singleton lifecycle: `startCaptureMode` / `stopCaptureMode` / `getCaptureStatus` |
| `src/app/api/tools/agent-bridge/tproxy/route.ts` | `GET` / `POST` / `DELETE` route (LOCAL_ONLY) |
| `src/lib/inspector/tproxyCaptureApi.ts` | Client fetch helpers (`fetchTproxyStatus` / `startTproxyCaptureMode` / `stopTproxyCaptureMode`) |
| File | Responsibility |
| ------------------------------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
| `src/mitm/tproxy/commands.ts` | Pure `iptables`/`ip` apply + revert command builder; `validateTproxyConfig` |
| `src/mitm/tproxy/setup.ts` | Transactional `applyTproxy` / `revertTproxy` runner (rollback on failure) |
| `src/mitm/tproxy/transparentSocket.ts` | Native-addon loader (`loadTransparentAddon`), `createTransparentListenerFd`, `connectMarked`, `setSocketMark`, `isTransparentSocketAvailable` |
| `src/mitm/tproxy/native/transparent.c` | N-API addon: `createTransparentListener` (IP_TRANSPARENT), `setSocketMark`, `connectMarked` |
| `src/mitm/tproxy/native/binding.gyp` | node-gyp build manifest |
| `src/mitm/tproxy/dynamicCert.ts` | `DynamicCertStore` — per-SNI dynamic CA + leaf cache |
| `src/mitm/tproxy/caTrust.ts` | OS trust-store install/uninstall (`installTproxyCa` / `uninstallTproxyCa`, dedicated slot) |
| `src/mitm/tproxy/tlsCapture.ts` | TLS-terminating decrypt engine + re-encrypted anti-loop forward |
| `src/mitm/tproxy/captureMode.ts` | Transparent-listener orchestration; reads orig dest from `socket.localAddress` |
| `src/mitm/tproxy/captureManager.ts` | Singleton lifecycle: `startCaptureMode` / `stopCaptureMode` / `getCaptureStatus` |
| `src/app/api/tools/agent-bridge/tproxy/route.ts` | `GET` / `POST` / `DELETE` route (LOCAL_ONLY) |
| `src/lib/inspector/tproxyCaptureApi.ts` | Client fetch helpers (`fetchTproxyStatus` / `startTproxyCaptureMode` / `stopTproxyCaptureMode`) |

View File

@@ -1,20 +1,19 @@
---
title: "Public Credentials Handling"
version: 3.8.2
lastUpdated: 2026-05-14
version: 3.8.40
lastUpdated: 2026-06-28
---
# Public Credentials Handling
> **Source of truth:** `open-sse/utils/publicCreds.ts`
> **Tests:** `tests/unit/publicCreds.test.ts`
> **Last updated:** 2026-05-14 — v3.8.0
> **Last updated:** 2026-06-28 — v3.8.40
> **Audience:** Engineers integrating providers that ship public OAuth client_id / client_secret / Firebase Web API keys in their public CLIs.
> **Status:** **MANDATORY** for all new code that embeds upstream identifiers.
## Why this exists
Some upstream providers (Gemini CLI, Antigravity CLI, Windsurf / Devin CLI, GitHub Copilot, and similar OAuth-native clients) ship credentials extracted from their **public binaries or web apps**. Google explicitly documents that these are not secrets:
- [OAuth 2.0 for native apps (PKCE)](https://developers.google.com/identity/protocols/oauth2/native-app) — OAuth client_id / client_secret for installed apps are public; PKCE provides the actual security.
- [Firebase API keys](https://firebase.google.com/docs/projects/api-keys) — Web client identifiers are public by design.

View File

@@ -1,13 +1,13 @@
---
title: "Stealth Guide"
version: 3.8.2
lastUpdated: 2026-05-13
version: 3.8.40
lastUpdated: 2026-06-28
---
# Stealth Guide
> **Source of truth:** `open-sse/utils/tlsClient.ts`, `open-sse/services/{chatgptTlsClient,claudeCodeCCH,claudeCodeFingerprint,claudeCodeObfuscation,claudeCodeCompatible,antigravityObfuscation}.ts`, `open-sse/config/cliFingerprints.ts`, `src/mitm/`
> **Last updated:** 2026-05-13 — v3.8.0
> **Last updated:** 2026-06-28 — v3.8.40
> **Audience:** Engineers maintaining provider-specific stealth integrations.
OmniRoute integrates with providers whose edges actively fingerprint non-official clients (TLS JA3/JA4, header ordering, JSON body shape, integrity tokens). This page documents the stealth surfaces OmniRoute exposes and where they are implemented.
@@ -88,7 +88,7 @@ Applied to: `system` blocks, all `messages[].content`, and `tools[].description`
For third-party Anthropic relays that only accept "real Claude Code" traffic:
- `CLAUDE_CODE_COMPATIBLE_USER_AGENT = "claude-cli/2.1.187 (external, sdk-cli)"`
- `CLAUDE_CODE_COMPATIBLE_USER_AGENT = "claude-cli/2.1.195 (external, sdk-cli)"`
- `CLAUDE_CODE_COMPATIBLE_STAINLESS_PACKAGE_VERSION = "0.94.0"`
- `CLAUDE_CODE_COMPATIBLE_STAINLESS_RUNTIME_VERSION = "v24.3.0"`
- `anthropic-beta = "claude-code-20250219,interleaved-thinking-2025-05-14,effort-2025-11-24"` by default
@@ -212,17 +212,16 @@ All MITM endpoints require management auth (`requireCliToolsAuth`). The sudo pas
## User-Agent Overrides — env vars (`.env.example` section 12)
| Variable | Default |
| ------------------------ | --------------------------------------------- |
| `CLAUDE_USER_AGENT` | `claude-cli/2.1.187 (external, cli)` |
| `CODEX_USER_AGENT` | `codex-cli/0.142.0 (Windows 10.0.26200; x64)` |
| `GITHUB_USER_AGENT` | `GitHubCopilotChat/0.45.1` |
| `ANTIGRAVITY_USER_AGENT` | `antigravity/2.0.1 darwin/arm64` |
| `KIRO_USER_AGENT` | `AWS-SDK-JS/3.0.0 kiro-ide/1.0.0` |
| `QODER_USER_AGENT` | `Qoder-Cli` |
| `QWEN_USER_AGENT` | `QwenCode/0.15.9 (linux; x64)` |
| `CURSOR_USER_AGENT` | `Cursor/3.3` |
| `GEMINI_CLI_USER_AGENT` | `google-api-nodejs-client/10.3.0` |
| Variable | Default |
| ------------------------ | --------------------------------------------------------------- |
| `CLAUDE_USER_AGENT` | `claude-cli/2.1.195 (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` |
| `QWEN_USER_AGENT` | `QwenCode/0.19.3 (linux; x64)` |
| `CURSOR_USER_AGENT` | `Cursor/3.4` |
Consumed by `open-sse/executors/base.ts::buildHeaders()` via dynamic lookup. **Bump these when providers release new CLI versions** — stale UA strings start getting rejected as outdated clients.

View File

@@ -9,7 +9,6 @@
"EGRESS_POLICY",
"COMPLIANCE",
"SOCKET_DEV_FINDINGS",
"CLI_TOKEN",
"CLI_TOKEN_AUTH"
"CLI_TOKEN"
]
}