Skip to content

fix(dev): run the dev stack on the host network so inbound SIP arrives - #374

Merged
BechsteinDigital merged 1 commit into
mainfrom
fix/dev-stack-host-network
Sep 1, 2026
Merged

fix(dev): run the dev stack on the host network so inbound SIP arrives#374
BechsteinDigital merged 1 commit into
mainfrom
fix/dev-stack-host-network

Conversation

@BechsteinDigital

Copy link
Copy Markdown
Owner

A registering SIP account gets out through NAT: the REGISTER leaves, the reply returns over the same binding, the account shows Up. An inbound INVITE is a new transaction, and for that the far end has to reach the address we gave it as Contact — on the bridge network a container IP that does not exist outside Docker. So it never rings, and nothing appears in the log, because nothing ever arrives.

Mapping ports does not help: RTP picks its ports dynamically, so even a forwarded INVITE would produce a silent call. Coturn already sits on the host network for the same reason.

What hangs off that

No port mapping The host binds directly on DEV_CALLORA_PORT (ASPNETCORE_URLS)
No compose DNS postgres no longer resolves — the database is reached over localhost
No host.docker.internal Both ICE agents now use the same address Coturn hands out as its relay IP
Linux only macOS and Windows have no real host network

Two things this PR fixes beyond the switch itself

The frontdoor would have broken — and only when used. It proxies to the compose service name callora-backend:5000, which does not resolve from a bridge-network container once the backend is on the host network. It stays on the bridge (so its 8080 port mapping keeps doing its job) and reaches the host through the gateway instead.

DEV_MEDIA_IP defaults to 0.0.0.0, not to a LAN address. Binding to all interfaces works everywhere and costs time: the agent then gathers one host candidate per interface — four Docker bridges plus LAN plus loopback here — the browser brings its own, Chrome paces its checks ~50 ms apart, and several dozen pairs turn into seconds before nomination. Measured: 2 s to ICE, 2 s to DTLS, against 265 ms for all the signalling before it. So the fast path is a .env entry, documented in .env.example; a LAN address checked into the repository would be one machine's address and, on every other, a media socket that binds to nothing.

Verification

docker compose config passes with and without the frontdoor overlay, and the nested ${BACKEND_UPSTREAM:-host.docker.internal:${DEV_CALLORA_PORT:-5000}} resolves as intended.

Ein registrierendes SIP-Konto kommt durch NAT nach draußen: der REGISTER geht raus,
die Antwort kommt über dieselbe Bindung zurück, das Konto steht auf Up. Ein
eingehender INVITE ist aber eine neue Transaktion, und dafür müsste die Gegenstelle
die Adresse erreichen, die wir ihr als Contact genannt haben — im Bridge-Netz eine
Container-IP, die es außerhalb von Docker nicht gibt. Es klingelt nie, und im Log
steht nichts, weil nie etwas ankommt.

Ports zu mappen reicht nicht: RTP wählt seine Ports dynamisch, ein durchgereichter
INVITE ergäbe ein stummes Gespräch. Coturn steht aus demselben Grund im Host-Netz.

Was daran hängt:

Ohne Portmapping bindet der Host direkt auf DEV_CALLORA_PORT, und der Compose-Name
"postgres" löst nicht mehr auf — die Datenbank wird über localhost erreicht.
host.docker.internal gibt es im Host-Netz auch nicht mehr, also nutzen beide
ICE-Agenten dieselbe Adresse, die Coturn als Relay-IP ausgibt.

Die Frontdoor bleibt im Bridge-Netz, damit ihr Portmapping auf 8080 weiter das tut,
wofür es da ist — sie erreicht den Host jetzt über das Gateway statt über den
Compose-Namen, der von dort nicht mehr auflöst. Ohne das wäre die optionale
Frontdoor ab dieser Änderung kaputt gewesen, und zwar erst beim Benutzen.

DEV_MEDIA_IP ist neu und hat bewusst 0.0.0.0 als Default: das funktioniert überall,
kostet aber Zeit. Der Agent sammelt dann einen Host-Kandidaten je Interface — hier
vier Docker-Bridges plus LAN plus Loopback —, der Browser bringt seine eigenen mit,
Chrome staffelt die Prüfungen mit ~50 ms Abstand, und aus mehreren Dutzend Paaren
werden Sekunden bis zur Nominierung (gemessen: 2 s bis ICE, 2 s bis DTLS, bei 265 ms
für die gesamte Signalisierung davor). Die LAN-Adresse gehört deshalb in die .env
und nicht als Default ins Repository: dort wäre sie die Adresse genau eines Rechners
und bei jedem anderen ein Mediensocket, der an nichts bindet.

Linux only — auf macOS und Windows gibt es kein echtes Host-Netz.

Beide Zusammenstellungen validiert: docker compose config, mit und ohne Frontdoor.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_011iSX3geDv7PNRmiusRr1xd
@BechsteinDigital
BechsteinDigital merged commit 057530a into main Sep 1, 2026
5 checks passed
@BechsteinDigital
BechsteinDigital deleted the fix/dev-stack-host-network branch September 1, 2026 19:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant