DocumentsPipelock
transport-modes
transport-modes
Type
External
Status
Published
Created
Mar 25, 2026
Updated
Jul 18, 2026
Updated by
Dosu Bot

Transport Mode Comparison#

Pipelock supports multiple proxy modes, each with different scanning capabilities. Choosing the right mode determines what security checks apply to your agent's traffic.

Mode Summary#

ModeEndpointProtocolContent InspectionResponse ScanningBest For
Fetch/fetch?url=...HTTPFull bodyInjection detectionAI agents that need extracted text
CONNECTHTTPS_PROXYHTTPS tunnelHostname onlyNoneStandard HTTPS clients (no interception)
CONNECT + TLS interceptionHTTPS_PROXYHTTPS tunnel (MITM)Full body + headersInjection detectionFull DLP on HTTPS traffic
Absolute-URIHTTP_PROXYHTTPFull URLInjection detection (when enabled)Plaintext HTTP clients
WebSocket/ws?url=...WS/WSSBidirectional framesDLP + injectionReal-time agent communication
MCP stdiopipelock mcp proxy -- CMDstdioFull messagesFull (6 layers)Local MCP servers
MCP HTTPpipelock mcp proxy --upstream URLHTTPFull messagesFull (6 layers)Remote MCP servers (HTTP)
MCP WebSocketpipelock mcp proxy --upstream ws://...WS/WSSFull messagesFull (6 layers)Remote MCP servers (WS)

Detailed Breakdown#

Fetch Proxy (/fetch?url=...)#

The highest-protection mode. Designed for AI agents that need web content.

Scanning:

  • Ordered URL scan (length/parsing, scheme, CRLF injection, path traversal, destination policy, immutable SSRF/DLP floors, configured DLP, entropy, DNS SSRF/rebinding, rate limit, data budget, and context checks)
  • request_policy route and operation checks, including followed redirect hops
  • Raw HTML scan for injection in hidden elements (script, style, comments, hidden divs)
  • Readability text extraction (strips HTML, returns clean text)
  • Response injection detection on extracted content
  • Redirect chain: each hop traverses the ordered URL scanner pipeline

What the agent receives: Extracted text content, not raw HTML. Hidden injection is detected even though the agent never sees it.

Use when: Your agent fetches web pages and you want both URL scanning and content inspection.

curl "http://localhost:8888/fetch?url=https://example.com"

CONNECT Tunnel (via HTTPS_PROXY)#

Standard HTTP CONNECT proxy. Without TLS interception, pipelock cannot see the encrypted traffic after the tunnel is established.

Scanning (without TLS interception):

  • Ordered URL scan on the target hostname (before tunnel)
  • No content inspection during the tunnel (encrypted bytes)
  • No response scanning

Scanning (with tls_interception.enabled: true):

  • Ordered URL scan on the target hostname (before tunnel)
  • Full request body DLP (JSON, form, multipart extraction)
  • Request header DLP scanning
  • Authority enforcement (Host must match CONNECT target)
  • request_policy route and operation checks on the inner HTTP request
  • Response injection detection (buffered scan-then-send)
  • Compressed response blocking (fail-closed)

What the agent receives: Without interception: raw HTTPS response from the origin server. With interception: response re-encrypted by pipelock after scanning.

Use when: Your agent or SDK uses HTTPS_PROXY natively. Enable TLS interception for full DLP and injection scanning. Without interception, only hostname-level protection applies.

# Without TLS interception (hostname scanning only)
HTTPS_PROXY=http://localhost:8888 curl https://example.com

# With TLS interception (full body/header DLP + response scanning)
# Requires: tls_interception.enabled: true in config
# Requires: pipelock CA trusted by the agent (pipelock tls install-ca)
HTTPS_PROXY=http://localhost:8888 curl --cacert ~/.pipelock/ca.pem https://example.com

Absolute-URI Forward Proxy (via HTTP_PROXY)#

Handles plaintext HTTP requests where the client sends the full URL as the request target.

Scanning:

  • Ordered URL scan on the full URL
  • request_policy route and operation checks
  • Response injection scanning (buffer-then-scan-then-send, fail-closed on compressed responses)
  • Response body buffered (up to MaxResponseMB), scanned for injection, then forwarded; oversized buffered responses are blocked fail-closed
  • Data budget tracking on response size

What the agent receives: Raw HTTP response from the origin server.

Use when: Your application makes plaintext HTTP requests through HTTP_PROXY. Note that most modern APIs use HTTPS, making this mode less common.

HTTP_PROXY=http://localhost:8888 curl http://example.com

WebSocket Proxy (/ws?url=...)#

Bidirectional WebSocket proxy with frame-level scanning.

Scanning:

  • Ordered URL scan on the target URL
  • request_policy route-only checks on the upgrade and per-frame operation checks on reassembled text frames
  • DLP scanning on WebSocket upgrade request headers
  • Bidirectional frame scanning (both client-to-server and server-to-client)
  • Fragment reassembly for multi-frame messages
  • Compression rejection (RSV1 bit check prevents deflate-based DLP bypass)
  • Data budget tracking per domain

Learn-and-lock contracts: WebSocket handshakes use HTTP GET semantics. A signed GET rule for https://api.example.com/stream also authorizes a /ws?url=wss://api.example.com/stream handshake; frame-level DLP and injection scanning still run after the connection is established.

What the agent receives: WebSocket frames, scanned in both directions.

Use when: Your agent uses WebSocket connections for real-time communication and you need DLP scanning on the message content.

# Agent connects to pipelock, which proxies to the target
ws://localhost:8888/ws?url=wss://api.example.com/stream

MCP stdio proxy (pipelock mcp proxy -- COMMAND)#

Wraps a local MCP server process with full bidirectional message scanning.

Scanning:

  • Response scanning: injection detection in tool results
  • Input scanning: DLP + injection in tool arguments (when mcp_input_scanning enabled)
  • Tool scanning: poisoned description detection + rug-pull drift detection (when mcp_tool_scanning enabled)
  • Tool policy: pre-execution allow/deny rules with shell obfuscation detection (when mcp_tool_policy enabled)
  • Chain detection: suspicious tool call sequence patterns (when tool_chain_detection enabled)
  • Session binding: tool inventory pinning per session (when mcp_session_binding enabled)

What the agent receives: MCP responses with injection warnings injected, or blocked entirely depending on config action.

Use when: Running local MCP servers (filesystem, database, custom tools) and you want to scan all tool interactions.

{
  "mcpServers": {
    "filesystem": {
      "command": "pipelock",
      "args": ["mcp", "proxy", "--config", "pipelock.yaml", "--", "npx", "@modelcontextprotocol/server-filesystem", "/tmp"]
    }
  }
}

MCP HTTP Proxy (pipelock mcp proxy --upstream URL)#

Proxies a remote MCP server over HTTP with the same scanning as stdio mode.

Scanning: Same 6 layers as MCP stdio (response, input, tool, policy, chain, session binding).

Transport sub-modes:

  • Stdio-to-HTTP bridge (pipelock mcp proxy --upstream URL): Translates stdio JSON-RPC to HTTP requests against a streamable HTTP MCP server
  • HTTP reverse proxy (pipelock mcp proxy --listen ADDR --upstream URL or pipelock run --mcp-listen ADDR --mcp-upstream URL): Listens on an HTTP port and reverse-proxies to the upstream MCP server

Non-loopback reverse-proxy listeners fail closed unless a bearer token file is
configured. Standalone mode uses --listener-auth-token-file; combined mode
uses --mcp-auth-token-file. Clients authenticate to Pipelock with
Proxy-Authorization: Bearer ...; the independent Authorization header is
preserved for the upstream. Browser clients instead use Authorization for
the listener credential because browsers do not expose Proxy-Authorization
to JavaScript; that consumed header is not forwarded, so configure any browser
upstream credential separately. Browser requests carrying Origin are rejected by
default and can be admitted one exact origin at a time with
--listener-allowed-origin or --mcp-allowed-origin. The explicit
--listener-allow-unauthenticated / --mcp-allow-unauthenticated escape hatch
is for deployments where a verified network policy is the access boundary.
Standalone reverse-listener mode also honors --header / --header-file for
operator-configured upstream credentials; those values take precedence over
client-supplied Authorization, MCP protocol-version, and A2A service headers.
Duplicate or malformed session/protocol service headers are rejected before
forwarding. A tokenless loopback listener additionally requires the request
Host to be localhost or a literal loopback address on the listener's actual
port, closing the browser DNS-rebinding path that a loopback bind alone leaves
open.

Use when: Connecting to remote MCP servers over HTTP and you want the same scanning coverage as local stdio servers.

Authenticated upstreams (--header): When the upstream MCP server requires a static auth header (Bearer token, API key), pass it via --header:

pipelock mcp proxy --upstream https://mcp.example.com/v1 \
  --header "Authorization: Bearer $UPSTREAM_TOKEN"

The flag is repeatable. Pipelock validates header names as RFC 7230 tokens and rejects ASCII control bytes, DEL, CRLF, and Unicode whitespace in values. The transport-managed and connection-critical headers Mcp-Session-Id, Content-Type, Accept, Content-Length, Transfer-Encoding, and Host are blocked case-insensitively at both the CLI flag-parser and the transport layer so an attacker-controlled extra header cannot shadow Pipelock's session correlation or smuggle a request via header injection.

MCP WebSocket Proxy (pipelock mcp proxy --upstream ws://...)#

Proxies a remote MCP server over WebSocket with the same scanning as stdio mode.

Scanning: Same 6 layers as MCP stdio (response, input, tool, policy, chain, session binding).

How it works: When --upstream receives a ws:// or wss:// URL, pipelock connects to the upstream over WebSocket and translates between stdin/stdout JSON-RPC and WebSocket text frames. Each JSON-RPC message maps to one WebSocket text frame. Fragment reassembly is handled automatically.

Use when: Connecting to MCP servers that expose a WebSocket endpoint (common with OpenClaw gateways and other real-time MCP hosts).

{
  "mcpServers": {
    "remote": {
      "command": "pipelock",
      "args": ["mcp", "proxy", "--config", "pipelock.yaml", "--upstream", "ws://localhost:3000/mcp"]
    }
  }
}

Security Implications#

CONNECT Tunnels: With and Without TLS Interception#

Without TLS interception (tls_interception.enabled: false, the default), CONNECT tunnels are opaque encrypted bytes after the hostname scan. DLP cannot detect secrets in bodies or headers, and response injection scanning does not apply.

With TLS interception enabled, pipelock performs a TLS MITM: it terminates TLS with the client (forged certificate), scans the decrypted traffic, then forwards to the upstream server over a separate TLS connection. This closes the body-blindness gap.

Without interception:

  • DLP cannot detect secrets in HTTPS request/response bodies
  • Response injection scanning does not apply
  • Only destination-visible URL scanning applies; encrypted request and response content remains opaque

With interception:

  • Full request body DLP (JSON, form, multipart)
  • Request header DLP (Authorization, Cookie, etc.)
  • Response injection scanning (buffered, scan-then-send)
  • Authority enforcement (Host must match CONNECT target)

If your agent handles secrets and you need content-level DLP on HTTPS traffic, either enable TLS interception or use the fetch proxy or MCP proxy modes.

Fetch Proxy vs CONNECT: Trade-offs#

ConcernFetch ProxyCONNECT (no interception)CONNECT (TLS interception)
URL scanningOrdered pipelineOrdered pipelineOrdered pipeline
DLP on request bodiesN/ANo (encrypted)Yes
DLP on responsesYesNo (encrypted)Yes
Injection detectionYesNo (encrypted)Yes
Agent receivesExtracted textRaw HTTPS responseRaw HTTPS response (re-encrypted)
TLS terminationPipelock terminatesEnd-to-endPipelock MITM (forged cert)
SDK compatibilityRequires /fetch APINative HTTPS_PROXYNative HTTPS_PROXY + CA trust
PerformanceSlower (extraction)Fastest (pass-through)Moderate (decrypt + scan + re-encrypt)

Signed Action Receipt Coverage#

Every configured enforcement event produces a signed action receipt: every block, and — under flight_recorder.require_receipts: true — every allow on the per-request proxy and MCP decision paths (including A2A method allows). Clean frames of a long-lived stream are summarized rather than individually receipted; the deliberate exceptions are listed in Intentional no-receipt and summarized cases below. The table below enumerates which deny paths are covered on each transport. Every row has been exercised by a test in the signed-receipt-coverage suite.

TransportPre-forward blocksPost-forward blocksTransport-specific blocksReceipt path
Fetch (/fetch)URL scan, DLP, SSRFRedirect block, response scan, audit-mode escalation, session profiling, header DLP, budget exhaustion, cross-request exfiltrationDirect emit to flight recorder
CONNECT (no TLS intercept)URL scan, DLP, SSRF, blocklistRedirect inside tunnel (not visible)Hostname-only receipts
CONNECT + TLS interceptionURL scan + full hostname DLPBody DLP, header DLP, response injectionAuthority mismatchFull content receipts; required inner-request allows are durable before upstream
Absolute-URI (forward proxy)URL scan, DLP, SSRFRedirect block, response scan, audit-mode escalation, session profiling, header DLP, budget exhaustion, CEEA2A header scan, A2A stream scan, A2A response body scanFull content receipts
WebSocket (/ws)Handshake-time URL scan, DLPFrame-level DLP, injection, address poisoning, CEESession close reasonPer-frame block receipts + session close (clean frames summarized, not individually receipted)
MCP stdioInput scan, tool scan, policyResponse injection, chain detection, session binding driftTool call, tool response, policy decisionFull content receipts
MCP HTTP / SSEInput scan, tool scan, policyResponse injection, chain detection, session binding driftTool call, tool response, policy decisionFull content receipts, stream-aware
MCP HTTP reverse proxyInput scan, tool scan, policyResponse injection, chain detection, session binding driftTool call, tool response, policy decisionFull content receipts

Receipt emission is best-effort by default on the async flight-recorder channel and survives config reload across all transports. Set flight_recorder.require_receipts: true to fail closed before allow-path proxy/MCP traffic is forwarded when the required receipt cannot be emitted; for TLS-intercepted CONNECT, the inner HTTP request's durable intent receipt is emitted before the upstream request. Block-path receipts stay best-effort because the action is already denied. Receipts chain via chain_prev_hash / chain_seq for tamper-evidence. See docs/guides/receipt-verification.md for the verify CLI and the cross-implementation conformance suite.

Intentional no-receipt and summarized cases#

The guarantee is "every configured enforcement event is provable," not "every frame produces a receipt." The matrix below is the canonical, single-source-of-truth list of when a signed action receipt is and is not emitted, including the deliberate no-receipt cases (clean streaming frames are summarized to avoid an O(n)-in-stream-length receipt flood that a chatty peer could weaponize as a denial-of-service vector). It is generated from and drift-checked against TestReceiptCoverage_MatrixMatchesDocs in internal/proxy/receipt_coverage_matrix_test.go; edit the matrix there and run UPDATE_GOLDEN=1 go test ./internal/proxy/ -run TestReceiptCoverage_MatrixMatchesDocs to regenerate this block.

ScenarioReceiptNotes
tools/call blockyesBlock receipt (best-effort: the action is already denied).
tools/call allow, require_receipts: trueyesAllow receipt; fails closed if emission fails.
tools/call allow, defaultnoAllow receipts are opt-in via require_receipts.
A2A method blockyesBlock receipt with the A2A method name as target.
A2A method allow, require_receipts: trueyesAllow receipt; fails closed if emission fails.
A2A method allow, defaultnoAllow receipts are opt-in via require_receipts.
Proxy block (fetch / CONNECT / forward / WS handshake)yesPre- or post-forward block receipt.
Proxy allow, require_receipts: trueyesAllow receipt; fails closed if emission fails.
Clean WebSocket frameno (intentional)Per-frame allow receipts are O(n) in stream length; summarized, not emitted, to avoid a receipt-flood denial-of-service vector.
Clean SSE / streamed response chunkno (intentional)Streamed response chunks are summarized, not receipted per chunk.
WebSocket session closeyesSession-close receipt records the close reason.
Required receipt emission failsblockRequest fails closed with receipt_emission_failed instead of forwarding.

See Also#