mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-26 09:52:11 +03:00
Release v3.8.31 (#4377)
Release v3.8.31 — see CHANGELOG.md [3.8.31] for full notes and contributors. Merged over known non-blocking reds (all correctness gates green): Integration Tests (2/2) is env/flaky (polls a real upstream batch that did not complete in the poll window); SonarQube/SonarCloud is the advisory server-side new-code quality gate. Unit (8 shards), Coverage, Node 22/24/26, Lint, PR Test Policy, Quality Ratchet, Docs-Strict, Quality-Extended and all 4 CodeQL analyses are green.
This commit is contained in:
committed by
GitHub
parent
3b2a2f02a9
commit
d0396c200d
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: "AgentBridge"
|
||||
version: 3.8.6
|
||||
lastUpdated: 2026-05-28
|
||||
version: 3.8.31
|
||||
lastUpdated: 2026-06-20
|
||||
---
|
||||
|
||||
# AgentBridge
|
||||
@@ -10,7 +10,7 @@ AgentBridge is OmniRoute's MITM (Man-in-the-Middle) proxy that intercepts HTTPS
|
||||
|
||||
**Dashboard location:** `/dashboard/tools/agent-bridge`
|
||||
**Sidebar group:** Tools (after Cloud Agents)
|
||||
**See also:** [`TRAFFIC_INSPECTOR.md`](./TRAFFIC_INSPECTOR.md) — monitor all intercepted traffic in real-time.
|
||||
**See also:** [`TRAFFIC_INSPECTOR.md`](./TRAFFIC_INSPECTOR.md) — monitor all intercepted traffic in real-time; [`docs/security/MITM-TPROXY-DECRYPT.md`](../security/MITM-TPROXY-DECRYPT.md) — the Linux TPROXY transparent-decrypt capture mode driven by the `/api/tools/agent-bridge/tproxy` route.
|
||||
|
||||
---
|
||||
|
||||
@@ -225,6 +225,64 @@ Wildcard `*` maps any unrecognized model to the specified target. Persisted in `
|
||||
|
||||
AgentBridge intercepts credentials (OAuth tokens, API keys) that the IDE uses to authenticate with upstream providers. These are **masked before logging** (see §2.7) but are visible to OmniRoute's MITM layer. First activation of each agent shows a dismissible risk notice modal.
|
||||
|
||||
### 3.6 Maintenance & Diagnostics
|
||||
|
||||
The dashboard exposes a **Maintenance & Diagnostics** card (`AgentBridgeMaintenanceCard`, in `src/app/(dashboard)/dashboard/tools/agent-bridge/components/`) that surfaces operational MITM routes which previously had no UI. Its subtitle: *"Self-test the capture pipeline, undo leftover system state, and move your setup between machines."* The card client helpers live in `src/lib/inspector/agentBridgeMaintenanceApi.ts`.
|
||||
|
||||
| Button | Route | What it does |
|
||||
|--------|-------|--------------|
|
||||
| **Diagnose** | `GET /api/tools/agent-bridge/diagnose` | Runs the capture-pipeline self-test and shows a per-check report (✓/✗ + remediation hint). |
|
||||
| **Repair** | `POST /api/tools/agent-bridge/repair` | Undoes orphaned MITM system state (DNS spoof entries, root CA, system proxy) left behind by a crash or SIGKILL. Idempotent — reports "Nothing to repair" when state is clean. |
|
||||
| **Remove CA** | `DELETE /api/tools/agent-bridge/cert` | Untrusts and removes the MITM root CA from the OS trust store (explicit, idempotent). Shown only when the CA is currently trusted; requires an inline "Remove CA?" confirmation. |
|
||||
| **Export config** | `GET /api/tools/agent-bridge/config` | Downloads the portable config JSON (see §3.7). |
|
||||
| **Import config** | `POST /api/tools/agent-bridge/config` | Uploads a previously-exported config JSON (see §3.7). |
|
||||
|
||||
**Diagnostics checks** (`summarizeDiagnostics()` in `src/mitm/inspector/diagnostics.ts`). The route runs the effectful probe for each and feeds the booleans into the pure summarizer; a single `healthy` verdict plus a per-failure hint is returned:
|
||||
|
||||
| Check name | What it verifies | Hint on failure |
|
||||
|------------|------------------|-----------------|
|
||||
| `server-running` | The MITM server process is active | "The MITM server is not running. Start it from the AgentBridge tab." |
|
||||
| `server-reachable` | The MITM server accepts connections on its port (TCP probe) | "The MITM server is not accepting connections on its port. Check that the port is free and that you have privileges to bind it." |
|
||||
| `cert-exists` | The MITM certificate has been generated on disk | "No MITM certificate has been generated yet. Generate one from the AgentBridge tab." |
|
||||
| `cert-trusted` | The MITM root CA is in the OS trust store | "The MITM root CA is not trusted by the OS store, so TLS interception will fail. Trust the certificate from the AgentBridge tab." |
|
||||
| `dns-configured` | Target hostnames are spoofed in `/etc/hosts` | "Target hostnames are not spoofed in /etc/hosts, so traffic never reaches the proxy. Enable DNS for the agent(s) you want to capture." |
|
||||
|
||||
**Orphaned-state banner:** when the page detects state left behind by a crash (DNS spoof / CA / system proxy), the card shows an amber banner — *"A previous session left system state behind (DNS spoof, CA, or system proxy). Run Repair to clean it up."* — and highlights the **Repair** button. `Repair` is the application-layer analogue of ProxyBridge's `--cleanup` flag (it delegates to `repairMitm()` in `src/mitm/manager.ts`).
|
||||
|
||||
> The MITM root CA is kept installed across stop/start to avoid repeated sudo
|
||||
> prompts (the same behavior as mitmproxy/Charles), so removing it is an explicit
|
||||
> **Remove CA** action rather than something that happens automatically on stop.
|
||||
|
||||
### 3.7 Portable config import/export
|
||||
|
||||
AgentBridge can serialize the **operator-tunable** state into a versioned JSON blob so a setup can be replicated across machines. The serializer is `src/lib/inspector/configPortability.ts` (`exportConfig()` / `importConfig()`), validated by `AgentBridgeConfigSchema`.
|
||||
|
||||
The export includes exactly three pieces (built-in defaults are intentionally **NOT** exported, so importing never duplicates or fights them):
|
||||
|
||||
| Field | Source | Notes |
|
||||
|-------|--------|-------|
|
||||
| `bypassPatterns` | user-defined bypass patterns (`agent_bridge_bypass`) | default bank/gov/okta patterns are excluded |
|
||||
| `customHosts` | Traffic Inspector custom hosts (`inspector_custom_hosts`) | each: `{ host, kind: "llm"\|"app"\|"custom", label? }` |
|
||||
| `agentMappings` | per-agent model mappings (`agent_bridge_mappings`) | `{ [agentId]: [{ source, target }] }` for every agent that has mappings |
|
||||
|
||||
```jsonc
|
||||
// GET /api/tools/agent-bridge/config
|
||||
{
|
||||
"version": 1,
|
||||
"bypassPatterns": ["*.internal.example.com"],
|
||||
"customHosts": [{ "host": "api.example.com", "kind": "llm", "label": null }],
|
||||
"agentMappings": { "copilot": [{ "source": "gpt-4o", "target": "claude-sonnet-4.7" }] }
|
||||
}
|
||||
```
|
||||
|
||||
**Import behavior** (`POST /api/tools/agent-bridge/config`): bypass patterns and per-agent mappings **replace wholesale**; custom hosts are added **idempotently** (`INSERT OR IGNORE`). The response reports how many of each were applied:
|
||||
|
||||
```jsonc
|
||||
{ "ok": true, "bypassPatterns": 1, "customHosts": 1, "agents": 1 }
|
||||
```
|
||||
|
||||
What is **NOT** in the config: server running state, cert paths, per-agent DNS state, upstream CA path, and TPROXY settings — those are host/runtime state, not portable preferences.
|
||||
|
||||
---
|
||||
|
||||
## §4 Per-agent reference
|
||||
@@ -364,18 +422,31 @@ Base path: `/api/tools/agent-bridge/`
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| GET | `/api/tools/agent-bridge/agents` | List all 9 agents with current state |
|
||||
| GET | `/api/tools/agent-bridge/state` | Global server state (running, port, cert info) |
|
||||
| POST | `/api/tools/agent-bridge/server` | Start/stop/restart server (`action: "start"\|"stop"\|"restart"\|"trust-cert"\|"regenerate-cert"`) |
|
||||
| GET | `/api/tools/agent-bridge/agents/{id}` | State of one agent (dns_enabled, cert_trusted, etc.) |
|
||||
| GET | `/api/tools/agent-bridge/state` | Global server state + per-agent detection/status |
|
||||
| GET | `/api/tools/agent-bridge/agents` | List registered agents (id, name, hosts, viability, state) |
|
||||
| GET | `/api/tools/agent-bridge/agents/{id}` | State of one agent (target config + detection + stored state) |
|
||||
| PATCH | `/api/tools/agent-bridge/agents/{id}` | Update `setup_completed` for agent |
|
||||
| GET | `/api/tools/agent-bridge/agents/{id}/detect` | Run detection probe for agent (`installed`, `version?`, `path?`) |
|
||||
| POST | `/api/tools/agent-bridge/agents/{id}/dns` | Enable/disable DNS for agent (`{enabled: boolean}`) |
|
||||
| GET | `/api/tools/agent-bridge/agents/{id}/mappings` | Model mappings for agent |
|
||||
| PUT | `/api/tools/agent-bridge/agents/{id}/mappings` | Update model mappings |
|
||||
| GET | `/api/tools/agent-bridge/bypass` | List bypass patterns |
|
||||
| PUT | `/api/tools/agent-bridge/bypass` | Update bypass patterns |
|
||||
| POST | `/api/tools/agent-bridge/cert` | Download or regenerate CA cert |
|
||||
| PUT | `/api/tools/agent-bridge/agents/{id}/mappings` | Replace model mappings |
|
||||
| POST | `/api/tools/agent-bridge/server` | Start/stop/restart server (`action: "start"\|"stop"\|"restart"\|"trust-cert"\|"regenerate-cert"`) |
|
||||
| GET | `/api/tools/agent-bridge/cert` | Cert status (`exists`, `trusted`, `path`) |
|
||||
| POST | `/api/tools/agent-bridge/cert` | Trust (install) the MITM root CA |
|
||||
| DELETE | `/api/tools/agent-bridge/cert` | Untrust (remove) the MITM root CA — idempotent (see §3.6) |
|
||||
| POST | `/api/tools/agent-bridge/cert/regenerate` | Regenerate the self-signed MITM cert |
|
||||
| GET | `/api/tools/agent-bridge/cert/download` | Stream the PEM cert for download |
|
||||
| GET | `/api/tools/agent-bridge/bypass` | List bypass patterns (`default` + `user`) |
|
||||
| POST | `/api/tools/agent-bridge/bypass` | Replace user-defined bypass patterns wholesale |
|
||||
| DELETE | `/api/tools/agent-bridge/bypass?pattern=...` | Remove a single user-defined bypass pattern |
|
||||
| GET | `/api/tools/agent-bridge/diagnose` | Capture-pipeline self-test (see §3.6) |
|
||||
| POST | `/api/tools/agent-bridge/repair` | Undo orphaned MITM system state (see §3.6) |
|
||||
| GET | `/api/tools/agent-bridge/config` | Export portable config JSON (see §3.7) |
|
||||
| POST | `/api/tools/agent-bridge/config` | Import portable config JSON (see §3.7) |
|
||||
| GET | `/api/tools/agent-bridge/upstream-ca` | Get configured upstream CA path |
|
||||
| POST | `/api/tools/agent-bridge/upstream-ca` | Set upstream CA cert path |
|
||||
| POST | `/api/tools/agent-bridge/upstream-ca` | Validate + persist upstream CA path |
|
||||
| POST | `/api/tools/agent-bridge/upstream-ca/test` | Validate-only (dry-run) an upstream CA path — does not persist |
|
||||
| GET / POST / DELETE | `/api/tools/agent-bridge/tproxy` | TPROXY transparent-decrypt capture mode — see [`docs/security/MITM-TPROXY-DECRYPT.md`](../security/MITM-TPROXY-DECRYPT.md) |
|
||||
|
||||
Full OpenAPI schemas: `docs/reference/openapi.yaml` → tag `AgentBridge`.
|
||||
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
title: "Cloud Agents"
|
||||
version: 3.8.2
|
||||
lastUpdated: 2026-05-13
|
||||
version: 3.8.31
|
||||
lastUpdated: 2026-06-20
|
||||
---
|
||||
|
||||
# Cloud Agents
|
||||
|
||||
> **Source of truth:** `src/lib/cloudAgent/` and `src/app/api/v1/agents/tasks/`
|
||||
> **Last updated:** 2026-05-13 — v3.8.0
|
||||
> **Last updated:** 2026-06-20 — v3.8.31 (frontmatter refresh; 4 agents incl. cursor-cloud)
|
||||
|
||||
OmniRoute orchestrates third-party cloud-hosted coding agents (Codex Cloud, Cursor,
|
||||
Devin, Jules) as long-running tasks. Each agent is wrapped behind a uniform interface so
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
---
|
||||
title: "OmniRoute MCP Server Documentation"
|
||||
version: 3.8.8
|
||||
lastUpdated: 2026-05-30
|
||||
version: 3.8.31
|
||||
lastUpdated: 2026-06-20
|
||||
---
|
||||
|
||||
# OmniRoute MCP Server Documentation
|
||||
@@ -310,6 +310,8 @@ Wildcard scopes are supported: `read:*` grants all read-scopes, `*` grants full
|
||||
| `OMNIROUTE_MCP_SCOPES` | (empty) | Comma-separated allowlist of scopes considered "available" by default (used when caller does not provide its own scopes) |
|
||||
| `OMNIROUTE_MCP_COMPRESS_DESCRIPTIONS` | (unset = on) | When set to `0/false/off/no`, disables MCP description compression at registration time |
|
||||
| `OMNIROUTE_MCP_DESCRIPTION_COMPRESSION` | (unset = on) | Alternate alias for the same toggle as above |
|
||||
| `MCP_TOOL_DENY` | (unset = no filter) | Comma-separated tool names to drop from `tools/list` (tool-cardinality reduction — see below) |
|
||||
| `MCP_TOOL_ALLOW` | (unset = no filter) | Comma-separated tool names to keep exclusively (allow-list mode — see below) |
|
||||
| `DATA_DIR` | `~/.omniroute` | Heartbeat file is written to `${DATA_DIR}/runtime/mcp-heartbeat.json` |
|
||||
|
||||
---
|
||||
@@ -325,6 +327,33 @@ MCP tool, prompt, and resource registries can compress descriptions at registrat
|
||||
|
||||
---
|
||||
|
||||
## Tool Cardinality Reduction (F4.3)
|
||||
|
||||
Description compression shrinks each tool's metadata; **tool-cardinality reduction** goes one step further by reducing *how many* tools are announced at all. Advertising fewer tools in the `tools/list` manifest cuts the per-request token cost the client's model pays for the tool catalog ("layer 5" compression). The implementation is a pure, stateless filter in `open-sse/mcp-server/toolCardinality.ts` (`reduceToolManifest`), wired into the registration loop in `createMcpServer()` (`open-sse/mcp-server/server.ts`).
|
||||
|
||||
**Opt-in, off by default.** The filter only runs when at least one of two environment variables is set; with neither set, all 87 tools are announced unchanged.
|
||||
|
||||
| Variable | Mode |
|
||||
| :--------------- | :-------------------------------------------------------------------------------------- |
|
||||
| `MCP_TOOL_DENY` | Blacklist — comma-separated tool names that are always dropped from `tools/list` |
|
||||
| `MCP_TOOL_ALLOW` | Allow-list — comma-separated tool names; only these survive, everything else is dropped |
|
||||
|
||||
`deny` takes priority over `allow`. Names are comma-separated, trimmed, and empty entries are ignored. Examples:
|
||||
|
||||
```bash
|
||||
# Drop two tools from the catalog
|
||||
MCP_TOOL_DENY="omniroute_get_health,omniroute_list_combos" omniroute --mcp
|
||||
|
||||
# Announce only the routing + quota tools (allow-list mode)
|
||||
MCP_TOOL_ALLOW="omniroute_route_request,omniroute_check_quota" omniroute --mcp
|
||||
```
|
||||
|
||||
**How filtered tools are removed:** registration always succeeds; a tool the profile rejects is then `.disable()`d on the MCP SDK handle, so it never appears in `tools/list` but the wiring stays intact (clean enable/disable, no re-registration). The profile parser is `readMcpToolProfileFromEnv(process.env)`, which returns `null` (no filtering) when both vars are empty.
|
||||
|
||||
The richer `ToolProfile` shape behind `reduceToolManifest` also supports scope-intersection filtering (`allowScopes`, with `read:*`-style wildcard matching) and a deterministic `maxTools` cap, but those two knobs need the full manifest at registration time and are **not** exposed through the environment variables today (a `tools/list`-level hook is a tracked follow-up). `estimateManifestTokens()` is available to compare the manifest token cost before and after reduction.
|
||||
|
||||
---
|
||||
|
||||
## Runtime Heartbeat
|
||||
|
||||
The stdio transport persists liveness to `${DATA_DIR}/runtime/mcp-heartbeat.json` every 5 seconds. The dashboard (`/api/mcp/status`) reads this file plus PID liveness to derive `online`. HTTP transports report state from in-process `getMcpHttpStatus()` instead (no file write).
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
---
|
||||
title: "Memory System"
|
||||
version: 3.8.6
|
||||
lastUpdated: 2026-05-28
|
||||
version: 3.8.31
|
||||
lastUpdated: 2026-06-20
|
||||
---
|
||||
|
||||
# Memory System
|
||||
|
||||
> **Source of truth:** `src/lib/memory/` and `src/app/api/memory/`
|
||||
> **Last updated:** 2026-05-28 — v3.8.6 (plan 21 — Memory Engine Redesign)
|
||||
> **Last updated:** 2026-06-20 — v3.8.31 (off-by-default + int8 quantization catch-up)
|
||||
|
||||
OmniRoute provides persistent conversational memory keyed by API key (and
|
||||
optionally session id). Memories are extracted automatically from LLM responses
|
||||
@@ -15,6 +15,18 @@ via lightweight regex pattern matching and injected back into subsequent
|
||||
requests as a leading system message (or first user message for providers that
|
||||
reject the system role).
|
||||
|
||||
> **Memory is OFF by default (v3.8.30+).** `DEFAULT_MEMORY_SETTINGS.enabled` is
|
||||
> now `false` (`src/lib/memory/settings.ts`). Enabling memory injects up to
|
||||
> `maxTokens` (~2k) of retrieved context into **every** chat request, which is
|
||||
> billed — a surprising cost for new installs and for clients that manage their
|
||||
> own context. Opt in explicitly under **Settings → Memory** (the
|
||||
> `MemorySkillsTab` shows a token-cost warning callout when memory is enabled).
|
||||
> A client can opt a single request out with the `x-omniroute-no-memory`
|
||||
> request header (`true`/`1`/`yes`) — see the request-header table in
|
||||
> [API_REFERENCE.md](../reference/API_REFERENCE.md). A no-memory request sets
|
||||
> `memoryOwnerId = null`, which disables **both** memory and skill injection for
|
||||
> that request (`open-sse/handlers/chatCore/headers.ts::isNoMemoryRequested`).
|
||||
|
||||
Memory is **scoped per API key**, not per user — every request authenticated
|
||||
with the same API key shares the same memory pool, with optional further
|
||||
scoping by `sessionId`.
|
||||
@@ -235,6 +247,30 @@ routes under `src/app/api/settings/qdrant/` are all wired as of v3.8.6:
|
||||
| `/api/settings/qdrant/cleanup` | `POST` | Remove expired / old points |
|
||||
| `/api/settings/qdrant/embedding-models` | `GET` | List available embedding models |
|
||||
|
||||
### Vector quantization (int8 — opt-in, both backends)
|
||||
|
||||
Both vector backends support **opt-in int8 quantization** to cut the memory
|
||||
footprint of stored vectors (~4× smaller than Float32) at a small recall cost.
|
||||
Default is **off** on both — vectors stay full-precision unless explicitly
|
||||
enabled.
|
||||
|
||||
| Backend | Setting | Type | Default | Where read |
|
||||
| ------------ | -------------------------------- | ----------------------------- | -------- | --------------------------------------------------- |
|
||||
| Qdrant | `qdrantQuantization` (DB key) | `"none" \| "int8" \| "binary"` | `"none"` | `src/lib/memory/qdrant.ts::normalizeQdrantConfig()` |
|
||||
| sqlite-vec | `MEMORY_VEC_QUANTIZATION` (env) | `"none" \| "int8"` | `"none"` | `src/lib/memory/vectorStore.ts::requestedVecQuantization()` |
|
||||
|
||||
- **Qdrant** is configured per-instance via the `qdrantQuantization` setting
|
||||
key (exposed as the `quantization` field on `PUT /api/settings/qdrant`). When
|
||||
`"int8"`, `buildQuantizationConfig()` requests scalar quantization
|
||||
(`always_ram`, quantile `0.99`) and searches enable `rescore: true` so the
|
||||
full-precision vectors refine the int8 candidate set.
|
||||
- **sqlite-vec** quantization is **environment-only** (not a DB setting): set
|
||||
`MEMORY_VEC_QUANTIZATION=int8` to store the local vectors as an `int8[dim]`
|
||||
column via `vec_quantize_int8(?, 'unit')`. The chosen mode is folded into the
|
||||
`embedding_signature` (an `:int8` suffix), so switching modes triggers a full
|
||||
reindex of the `vec_memories` table — the same lazy-backfill path used when
|
||||
the embedding model changes.
|
||||
|
||||
## Memory Types
|
||||
|
||||
`MemoryType` (`src/lib/memory/types.ts`):
|
||||
@@ -332,7 +368,7 @@ route after writes.
|
||||
|
||||
| DB key | Type | Default | UI control |
|
||||
| --------------------- | ------- | -------------------------------------------------- | ----------------------------------------------- |
|
||||
| `memoryEnabled` | boolean | `true` | Memory on/off |
|
||||
| `memoryEnabled` | boolean | `false` (off by default since v3.8.30) | Memory on/off |
|
||||
| `memoryMaxTokens` | integer | `2000` (range `0–16000`) | Token budget for injection |
|
||||
| `memoryRetentionDays` | integer | `30` (range `1–365`) | Retention window |
|
||||
| `memoryStrategy` | enum | `"hybrid"` (one of `recent`, `semantic`, `hybrid`) | Retrieval strategy |
|
||||
@@ -373,6 +409,7 @@ Six optional env vars tune the engine's runtime behaviour (documented in `.env.e
|
||||
| `MEMORY_STATIC_CACHE_DIR` | `<DATA_DIR>/embeddings` | Where to store downloaded models |
|
||||
| `MEMORY_VEC_TOP_K` | `20` | Default top-K for vector search |
|
||||
| `MEMORY_RRF_K` | `60` | RRF k constant for hybrid search |
|
||||
| `MEMORY_VEC_QUANTIZATION` | `none` | Set to `int8` to store local sqlite-vec vectors quantized (~4× smaller; opt-in). Mode change forces a reindex. |
|
||||
|
||||
## Summarisation (`summarization.ts`)
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
---
|
||||
title: "Traffic Inspector"
|
||||
version: 3.8.6
|
||||
lastUpdated: 2026-05-28
|
||||
version: 3.8.31
|
||||
lastUpdated: 2026-06-20
|
||||
---
|
||||
|
||||
# Traffic Inspector
|
||||
|
||||
Traffic Inspector is OmniRoute's built-in HTTPS traffic debugger — a Charles Proxy / mitmweb / HTTP Toolkit-like tool that is **LLM-aware** and **agent-aware**. It lives at `/dashboard/tools/traffic-inspector` and receives live traffic from up to 4 simultaneous capture sources.
|
||||
Traffic Inspector is OmniRoute's built-in HTTPS traffic debugger — a Charles Proxy / mitmweb / HTTP Toolkit-like tool that is **LLM-aware** and **agent-aware**. It lives at `/dashboard/tools/traffic-inspector` and receives live traffic from up to 5 simultaneous capture sources.
|
||||
|
||||
**Dashboard location:** `/dashboard/tools/traffic-inspector`
|
||||
**Sidebar group:** Tools (after AgentBridge)
|
||||
@@ -42,7 +42,7 @@ The `TrafficBuffer` (`src/mitm/inspector/buffer.ts`) is a shared in-memory ring
|
||||
|
||||
## §2 Capture modes
|
||||
|
||||
Traffic Inspector supports **4 simultaneous capture sources**. Each is independently toggleable.
|
||||
Traffic Inspector supports **5 simultaneous capture sources**. Each is independently toggleable. The `source` field on every `InterceptedRequest` (`src/mitm/inspector/types.ts`) is one of `"agent-bridge"`, `"custom-host"`, `"http-proxy"`, `"system-proxy"`, or `"tproxy"`.
|
||||
|
||||
### Mode 1 — AgentBridge (default, always on)
|
||||
|
||||
@@ -99,6 +99,17 @@ export HTTPS_PROXY=http://127.0.0.1:8080
|
||||
- Dashboard shows "Reverting system proxy" prompt if user navigates away while active
|
||||
- UI shows `⚠ Advanced` badge + explicit confirmation checkbox
|
||||
|
||||
### Mode 5 — TPROXY transparent decrypt (Linux, root, opt-in)
|
||||
|
||||
**Source:** Kernel TPROXY + policy routing (`src/mitm/tproxy/`)
|
||||
**Mechanism:** Marks new local outbound TCP connections to a target port (default `443`) in `mangle OUTPUT`, an `ip rule` reroutes the marked packets to local delivery, and `mangle PREROUTING`'s `TPROXY` target hands them to a transparent (**IP_TRANSPARENT**) listener (default port `8443`). The listener terminates TLS with a leaf certificate issued **per SNI hostname on demand** by a dynamic CA, captures the decrypted exchange, and forwards the request re-encrypted to the original destination.
|
||||
**Reach:** **Arbitrary** destination hosts on the target port — no `/etc/hosts` spoof, no `HTTP_PROXY` env, no system-wide proxy mutation. The intercepted process needs no config change, but must trust the dynamic CA.
|
||||
**Note:** `source` = `"tproxy"`
|
||||
|
||||
**Requirements:** Linux only (**IP_TRANSPARENT** is Linux-only), the **CAP_NET_ADMIN** capability (root), and a native N-API addon that must be built with a C toolchain (`npm run build:native:tproxy`). When unavailable, the dashboard toggle is disabled with the tooltip "TPROXY decrypt requires Linux + root + the native addon". The firewall rules apply/revert transactionally (a crash never leaves a `mangle` rule behind) and flush on reboot. An SO_MARK-based anti-loop keeps the proxy's own re-encrypted forward from being re-intercepted.
|
||||
|
||||
This is a substantial subsystem with its own dedicated operator guide — see **[`docs/security/MITM-TPROXY-DECRYPT.md`](../security/MITM-TPROXY-DECRYPT.md)** for the full firewall recipe, the per-SNI dynamic CA + trust-store installer, the local-only route, anti-loop details, and the configuration schema. The toggle is driven by `GET / POST / DELETE /api/tools/agent-bridge/tproxy` (note: the route lives under the AgentBridge prefix, not the Traffic Inspector prefix).
|
||||
|
||||
### Capture mode comparison
|
||||
|
||||
| Mode | Setup | Sudo? | Reach | Notes |
|
||||
@@ -107,6 +118,7 @@ export HTTPS_PROXY=http://127.0.0.1:8080
|
||||
| 2. Custom Hosts | Per-host input | Yes (hosts file) | Any app using that host | Persisted in DB |
|
||||
| 3. HTTP_PROXY | `export HTTPS_PROXY=...` | No | Apps respecting env | Port 8080, no TLS decrypt by default |
|
||||
| 4. System-wide | Toggle + confirm | Yes | All apps on machine | Auto-disable in 30 min |
|
||||
| 5. TPROXY decrypt | Toggle (Linux + native addon) | Yes (root + CA install) | Any host on the target port | Decrypts arbitrary hosts; off by default — see [MITM-TPROXY-DECRYPT.md](../security/MITM-TPROXY-DECRYPT.md) |
|
||||
|
||||
---
|
||||
|
||||
@@ -166,7 +178,8 @@ export HTTPS_PROXY=http://127.0.0.1:8080
|
||||
| Host filter | Substring match on `host` field |
|
||||
| Agent filter | Dropdown: All / per-agent |
|
||||
| Status filter | All / 2xx / 3xx / 4xx / 5xx / error |
|
||||
| Source filter | All / agent-bridge / custom-host / http-proxy / system-proxy |
|
||||
| Source filter | All / agent-bridge / custom-host / http-proxy / system-proxy / tproxy |
|
||||
| **Live** filter | Show only in-flight (open) requests — `liveOnly` toggle (see §4.6) |
|
||||
|
||||
### 3.5 Resizable panels
|
||||
|
||||
@@ -245,6 +258,47 @@ interface LlmMetadata {
|
||||
}
|
||||
```
|
||||
|
||||
### 4.6 Live in-flight request filter
|
||||
|
||||
The request `status` field is `number | "in-flight" | "error"` — an entry is
|
||||
pushed as `"in-flight"` the moment the request starts and **updated in place**
|
||||
when the response (or error) arrives. The toolbar's **"Live"** toggle
|
||||
(`liveOnly`, i18n key `trafficInspector.liveOnly`) restricts the list to entries
|
||||
whose `status === "in-flight"`, letting you watch open connections in real time.
|
||||
|
||||
The filter is a pure, client-side predicate in
|
||||
`src/lib/inspector/matchesTrafficFilter.ts`:
|
||||
|
||||
```ts
|
||||
if (f.liveOnly && req.status !== "in-flight") return false;
|
||||
```
|
||||
|
||||
The toggle state lives in `useTrafficFilters` (the inspector dashboard hooks) and
|
||||
combines with the other filters (profile, host, agent, source, status, context).
|
||||
|
||||
### 4.7 Process attribution (Linux)
|
||||
|
||||
On Linux, each intercepted request can be attributed to the **originating local
|
||||
process**. Two optional fields are added to `InterceptedRequest`:
|
||||
|
||||
```ts
|
||||
pid?: number; // originating process id (Linux only)
|
||||
processName?: string; // originating process name (Linux only)
|
||||
```
|
||||
|
||||
`src/mitm/inspector/processAttribution.ts` maps the connection's *client*
|
||||
ephemeral port to a PID + name by:
|
||||
|
||||
1. Reading `/proc/net/tcp` and `/proc/net/tcp6` to find the socket inode for the
|
||||
port (`parseProcNetTcpForInode`, a pure fixture-testable parser).
|
||||
2. Scanning `/proc/<pid>/fd/` for a symlink to `socket:[<inode>]`.
|
||||
3. Reading the process name from `/proc/<pid>/comm`.
|
||||
|
||||
A 1-second TTL cache bounds the procfs scan cost under load. Attribution is
|
||||
**best-effort** — any failure resolves to `null` and never blocks capture. On
|
||||
macOS/Windows the function returns `null` (stub; `lsof`/`GetExtendedTcpTable`
|
||||
support is a follow-up).
|
||||
|
||||
---
|
||||
|
||||
## §5 Sessions
|
||||
@@ -396,10 +450,15 @@ Base path: `/api/tools/traffic-inspector/`
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| GET | `/capture-modes` | State of all 4 capture modes |
|
||||
| GET | `/capture-modes` | State of the AgentBridge / custom-hosts / HTTP_PROXY / system-proxy modes + the `tls-intercept` toggle |
|
||||
| POST | `/capture-modes/http-proxy` | Start/stop HTTP_PROXY listener (`{action: "start"\|"stop"}`) |
|
||||
| POST | `/capture-modes/system-proxy` | Apply/revert system-wide proxy (`{action: "apply"\|"revert"}`) |
|
||||
| POST | `/capture-modes/tls-intercept` | Toggle HTTPS body decryption in proxy mode |
|
||||
| POST | `/capture-modes/tls-intercept` | Toggle HTTPS body decryption in proxy mode (`{enabled: boolean}`) |
|
||||
|
||||
> **TPROXY decrypt** (capture mode 5) is driven by a **separate** route under the
|
||||
> AgentBridge prefix — `GET / POST / DELETE /api/tools/agent-bridge/tproxy` — not
|
||||
> under `/api/tools/traffic-inspector/`. See
|
||||
> [`docs/security/MITM-TPROXY-DECRYPT.md`](../security/MITM-TPROXY-DECRYPT.md).
|
||||
|
||||
### Sessions
|
||||
|
||||
|
||||
Reference in New Issue
Block a user