Overview
When network isolation is enabled (--isolate-network), ToolHive currently starts three auxiliary containers per workload: a Squid egress forward proxy, a Squid ingress reverse proxy, and a dnsmasq DNS container. The two Squid containers are logically a single gateway — splitting them into two processes is an implementation artifact, not a deliberate design.
This epic introduces a swappable network-proxy backend and an Envoy implementation that consolidates egress + ingress into a single container, reducing the auxiliary count from 3 → 2 while preserving every current control.
Why
- Fewer moving parts — one gateway container instead of two (3 aux → 2). One image pull, one startup sequence, one config surface, one thing to supervise per workload.
- L3 + L4 enforcement — Envoy's RBAC filter matches on
destination_ip (CIDR) in addition to L7 host/authority, catching direct-IP connections that L7-only rules miss.
- Unified, structured access logs — a single stdout log stream for both directions instead of two separate Squid log formats.
- Config as code — a typed, diffable, unit-testable protobuf-JSON bootstrap instead of template-rendered Squid text.
- Future extensibility — Envoy's xDS opens the door to runtime policy updates and, later, transparent L3/L4 interception.
Scope
- Backend is opt-in and experimental, selected with
TOOLHIVE_NETWORK_PROXY=envoy. Squid remains the default. Nothing changes for existing users until we deliberately flip it.
- Local-Docker network isolation only. The Kubernetes operator has a separate egress path and is out of scope.
- dnsmasq stays for now (see the final task for the path to eliminating it).
Design and Squid-vs-Envoy comparison: docs/arch/14-envoy-network-proxy.md.
Work breakdown
Tracked as sub-issues below, intended to land as a stacked sequence of PRs:
- Extract a swappable
networkProxy seam (pure refactor, Squid re-homed).
- Add the Envoy backend behind
TOOLHIVE_NETWORK_PROXY=envoy.
- Harden the Envoy backend (digest-pin image, lock down admin, quiet health-check log noise).
- Graduate backend selection from an env var to a first-class
RunConfig field / CLI flag.
- Make Envoy the default and/or add transparent L3/L4 interception (also the path to dropping the DNS container).
Overview
When network isolation is enabled (
--isolate-network), ToolHive currently starts three auxiliary containers per workload: a Squid egress forward proxy, a Squid ingress reverse proxy, and a dnsmasq DNS container. The two Squid containers are logically a single gateway — splitting them into two processes is an implementation artifact, not a deliberate design.This epic introduces a swappable network-proxy backend and an Envoy implementation that consolidates egress + ingress into a single container, reducing the auxiliary count from 3 → 2 while preserving every current control.
Why
destination_ip(CIDR) in addition to L7 host/authority, catching direct-IP connections that L7-only rules miss.Scope
TOOLHIVE_NETWORK_PROXY=envoy. Squid remains the default. Nothing changes for existing users until we deliberately flip it.Design and Squid-vs-Envoy comparison:
docs/arch/14-envoy-network-proxy.md.Work breakdown
Tracked as sub-issues below, intended to land as a stacked sequence of PRs:
networkProxyseam (pure refactor, Squid re-homed).TOOLHIVE_NETWORK_PROXY=envoy.RunConfigfield / CLI flag.