Edge cases
S011 — Edge Cases
S011-EC-001 [P2] Upstream pins certificates (HSTS / HPKP / app pinning)
Many SaaS providers pin certificates at the application layer. When the proxy presents its CA-signed leaf, the agent's TLS library accepts it (the CA is in the trust store), but the application or the OS HSTS preload may reject the chain anyway.
Behaviour: the proxy has no signal that this happened — the failure is in the agent's process, not at the proxy. The proxy treats the connection as a normal TLS session that the client closed early.
Operator UX: document the symptom (intermittent connection resets to
specific hosts) and recommend mode: proxy (no interception) for pinned
hosts. A future feature MAY add a known-pinned-hosts allowlist that
forces those hosts back to S006 even if a matching mode: intercept rule
exists.
S011-EC-002 [P2] ALPN mismatch
The agent advertises h2 in its ClientHello; the upstream offers only
http/1.1, or vice versa.
Behaviour: fail closed. The proxy returns 502 to the client with
X-Outcall-Failure-Reason: alpn_mismatch. A retry handshake on the
client's side may succeed at HTTP/1.1 if the client falls back.
S011-EC-003 [P2] Agent does not trust the CA
Container started without the CA installed in its trust store.
Behaviour: the agent's TLS library rejects the leaf cert during the
client handshake. The proxy sees the handshake fail and emits
client_handshake_failed reason=untrusted_ca in logs. The agent's process
sees an SSL verification error appropriate to its language.
S011-EC-004 [P2] CA key on disk is unreadable
--ca-key is set but the file does not exist or is not readable by the
daemon's user.
Behaviour: the daemon exits non-zero on startup with a clear stderr
message (cannot read CA key: <path>: <errno>). Interception is never
enabled. Existing rules with mode: intercept would have already failed
validation in rules reload, so this case shouldn't bring down a running
daemon — but it does prevent startup.
S011-EC-005 [P2] Body exceeds buffer cap
egress.match_body: true is set but the request body exceeds
--intercept-body-cap-bytes.
Behaviour: the proxy stops buffering at the cap, evaluates the rule
with http.body == null, and continues to stream the request to upstream
on ALLOW. A structured warning log identifies the rule and the body size.
The agent receives the upstream response unchanged.
S011-EC-006 [P3] Streaming response (chunked / SSE)
Upstream responds with Transfer-Encoding: chunked or
text/event-stream.
Behaviour: the proxy streams chunks to the client without buffering. Response body is not matched against rules in v1 (rules see only request body). Connection lifetime is controlled by the slower of the two sides.
S011-EC-007 [P3] WebSocket upgrade over intercepted TLS
The agent issues GET wss://... with Upgrade: websocket.
Behaviour: the proxy evaluates the upgrade request normally (method,
path, headers including Sec-WebSocket-Protocol). On ALLOW, the proxy
opens an upstream WebSocket and bridges frames in both directions. Frame
contents are not inspected. On BLOCK, the upgrade returns 403, the
WebSocket never opens.
S011-EC-008 [P2] Cache eviction during in-flight handshake
Two requests arrive simultaneously; the leaf cache is full; the existing entry is evicted to make room while the second handshake is using it.
Behaviour: generated leaves are reference-counted; eviction from the cache only removes the lookup entry. Active handshakes hold their cert until the handshake completes. New requests after eviction generate a fresh leaf.
S011-EC-009 [P2] Client uses TLS 1.2 only
Some agents (older Python builds, Java 8) negotiate TLS 1.2 only.
Behaviour: the proxy supports both TLS 1.2 and 1.3 on the client side. ALPN works identically. No special handling required.
S011-EC-010 [P3] mTLS (client certificate) request
Upstream requires a client certificate. The agent has it; the proxy does not.
Behaviour: in v1, the proxy fails the upstream handshake and returns
502 with X-Outcall-Failure-Reason: upstream_handshake_failed. mTLS
bridging requires forwarding the client's certificate decision through
the proxy, which is out of scope for this spec.
Operator UX: document that mTLS-protected hosts must use mode: proxy
(no interception), where the original tunnel is preserved end-to-end.
S011-EC-011 [P2] Daemon shutdown with active intercepted tunnels
A SIGTERM arrives while N intercepted tunnels are open.
Behaviour: the proxy stops accepting new connections immediately. In- flight tunnels enter a drain phase with a configurable grace (default 30s). After grace, remaining tunnels are forcibly closed (RST to client and upstream). All cached leaves are dropped from memory; the CA private key is zeroed before unmap.
S011-EC-012 [P3] HTTP/3 / QUIC
QUIC runs over UDP. The proxy never sees it.
Behaviour: out of scope. The DNS filter (S007) blocks resolution for
non-allow-listed hosts; the bridge nftables ruleset blocks UDP egress
that doesn't match a direct_ip rule. So a misbehaving HTTP/3 client
either falls back to TCP/TLS (intercepted) or fails at L3.