mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
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:
committed by
GitHub
parent
1c18be4f8f
commit
7c23dab64d
@@ -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 |
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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`.
|
||||
|
||||
|
||||
@@ -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 (1–65535) | `443` | Destination TCP port to transparently intercept |
|
||||
| **mark** | int (≥1) | `0x2333` | Firewall mark set on `OUTPUT`, matched by the `ip rule` + `PREROUTING` |
|
||||
| **onPort** | int (1–65535) | `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 (1–65535) | `443` | Destination TCP port to transparently intercept |
|
||||
| **mark** | int (≥1) | `0x2333` | Firewall mark set on `OUTPUT`, matched by the `ip rule` + `PREROUTING` |
|
||||
| **onPort** | int (1–65535) | `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`) |
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@
|
||||
"EGRESS_POLICY",
|
||||
"COMPLIANCE",
|
||||
"SOCKET_DEV_FINDINGS",
|
||||
"CLI_TOKEN",
|
||||
"CLI_TOKEN_AUTH"
|
||||
"CLI_TOKEN"
|
||||
]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user