Is your feature request related to a problem?
The proxy currently accepts requests over TCP as well as a Unix socket, which breaks its own security model.
Access control in this project is built on socket ownership: a caller is authorised because it can connect(2) to a Unix socket whose filesystem permissions (user/group) the operator controls. A TCP listener has no equivalent — there is no peer uid/gid, so anything that can reach the port is implicitly trusted. --listen-tcp defaults to 127.0.0.1:2375, and deploy/docker-compose.yml binds 0.0.0.0:2375, so the weaker path is the one exercised by default.
This is already acknowledged as a one-sided rule: all three implementations reject tcp:// for the outbound connection to the Docker daemon (ts/src/flags.ts:42, Makefile:111) on exactly these grounds. The inbound listener never got the same treatment.
The three implementations are also not equivalent today, which contradicts the "equal peers" rule in AGENTS.md:
|
Unix listener |
TCP listener |
| Go |
yes |
yes |
| Rust |
yes |
yes |
| TypeScript |
no |
yes |
TypeScript is TCP-only (ts/src/index.ts:43) and has no Unix socket support at all.
Describe the solution
Make Unix socket the only transport the proxy listens on, in all three implementations.
- Remove the
--listen-tcp flag and its listener from Go, Rust, and TypeScript.
- Add Unix socket listening to TypeScript via
--listen-socket, reaching parity with Go and Rust, including fd://3 systemd socket activation and stale socket file cleanup.
- Make a listen-socket bind failure fatal. With a single listener, a running-but-unbound process is never useful; today the bind error is logged and the process stays up.
- Migrate the integration suite (
deploy/docker-compose.yml, deploy/docker-compose.sock.yml, deploy/test.sh, deploy/test-sock.sh) from tcp://proxy:2375 to a shared-volume Unix socket.
- Update
README.md (flag table and security boundary section) and the systemd example, which currently documents ListenStream=127.0.0.1:2375.
Describe alternatives
- Keep
--listen-tcp but default it to disabled. Rejected: the insecure path stays reachable via config, and the security boundary stays advisory rather than structural.
- Keep TCP and add peer authentication (mTLS). Rejected: substantially more surface and key management, to re-derive an identity the kernel already gives us for free over a Unix socket.
- Leave TypeScript TCP-only. Rejected: directly contradicts the equal-peers rule.
Which implementation(s) would this affect?
Additional context
Breaking change. Any deployment passing --listen-tcp or connecting via DOCKER_HOST=tcp://... must switch to a mounted Unix socket. The flag is removed outright rather than deprecated, so Go and Rust will exit on the unknown argument instead of silently listening on a socket the caller is not expecting.
The Quint spec models policy and invariants, not the transport layer, so no spec change is expected.
Is your feature request related to a problem?
The proxy currently accepts requests over TCP as well as a Unix socket, which breaks its own security model.
Access control in this project is built on socket ownership: a caller is authorised because it can
connect(2)to a Unix socket whose filesystem permissions (user/group) the operator controls. A TCP listener has no equivalent — there is no peer uid/gid, so anything that can reach the port is implicitly trusted.--listen-tcpdefaults to127.0.0.1:2375, anddeploy/docker-compose.ymlbinds0.0.0.0:2375, so the weaker path is the one exercised by default.This is already acknowledged as a one-sided rule: all three implementations reject
tcp://for the outbound connection to the Docker daemon (ts/src/flags.ts:42,Makefile:111) on exactly these grounds. The inbound listener never got the same treatment.The three implementations are also not equivalent today, which contradicts the "equal peers" rule in
AGENTS.md:TypeScript is TCP-only (
ts/src/index.ts:43) and has no Unix socket support at all.Describe the solution
Make Unix socket the only transport the proxy listens on, in all three implementations.
--listen-tcpflag and its listener from Go, Rust, and TypeScript.--listen-socket, reaching parity with Go and Rust, includingfd://3systemd socket activation and stale socket file cleanup.deploy/docker-compose.yml,deploy/docker-compose.sock.yml,deploy/test.sh,deploy/test-sock.sh) fromtcp://proxy:2375to a shared-volume Unix socket.README.md(flag table and security boundary section) and the systemd example, which currently documentsListenStream=127.0.0.1:2375.Describe alternatives
--listen-tcpbut default it to disabled. Rejected: the insecure path stays reachable via config, and the security boundary stays advisory rather than structural.Which implementation(s) would this affect?
Additional context
Breaking change. Any deployment passing
--listen-tcpor connecting viaDOCKER_HOST=tcp://...must switch to a mounted Unix socket. The flag is removed outright rather than deprecated, so Go and Rust will exit on the unknown argument instead of silently listening on a socket the caller is not expecting.The Quint spec models policy and invariants, not the transport layer, so no spec change is expected.