Reach consumers by network, not by a second host bind
CI / test (push) Successful in 16s

The compose file published a second host port on the docker bridge IP so
sibling containers could reach the relay. That is a host-reachable address for
a service whose entire safety story is that it has exactly one, on loopback.

It joins the consumers' existing docker network instead, and is reached there
by name on the container port. Nothing about a consumer's deployment has to
change to use it, and the relay gains no address outside that network.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
This commit is contained in:
Nirav Patel
2026-08-18 19:25:11 -04:00
co-authored by Claude Opus 5
parent e4d6230dd1
commit 399487862c
3 changed files with 48 additions and 17 deletions
+11 -5
View File
@@ -32,10 +32,11 @@ The shape is fixed by its first consumer, `reminders-service`
## Two rules
**It listens on loopback and the docker bridge, and nowhere else.** An
**It publishes one host port, on `127.0.0.1`, and nothing else.** An
authenticated relay reachable from the internet is an open spam relay the
moment the token leaks — and that token is copied into every consumer's
environment. It gets no `0.0.0.0` binding and no cloudflared route, ever.
environment. It gets no `0.0.0.0` binding, no bridge-IP binding, and no
cloudflared route, ever.
**It does not log what it was asked to send.** The log records channel, a
redacted address, the provider's status and message id. Never a subject, never
@@ -45,9 +46,14 @@ a body, never a credential.
From the host: `http://127.0.0.1:<CHANNEL_EXIT_PORT>/send`
From another container: `http://172.17.0.1:<CHANNEL_EXIT_PORT>/send` — a
consumer's own `127.0.0.1` is that consumer, so a loopback gateway URL fails
every delivery while looking correctly configured.
From another container: `http://channel-exit:8080/send`. The relay joins the
docker network its consumers already run on (`CHANNEL_EXIT_NETWORK`) and is
reached there by name, on the **container** port — `CHANNEL_EXIT_PORT` is a
host-side publish and does not exist inside that network.
A consumer's own `127.0.0.1` is that consumer, so a loopback gateway URL fails
every delivery while looking correctly configured. That is the one mistake to
watch for here.
## Running it