Files
B2BCall-dialer/docs/NETWORK_ARCHITECTURE.md
Matheus 97ef8a6ba8 feat: edição de rotas de entrada, fix de 2 bugs reais no ESL, diagnóstico de NAT/áudio e softphone WebRTC (PHASE 65/66)
Três achados reportados pelo usuário numa mensagem só: (1) Rotas de Entrada
não tinha edição depois de criada — implementada no mesmo padrão de Filas;
(2) telas de Platform > Infraestrutura sempre davam "Timeout no ESL" nesta
VM — não era limitação permanente como o comentário antigo dizia, e sim
ESL_HOST=freeswitch (nome DNS que só existe dentro da rede do Docker) mais
um segundo bug independente (`show gateways as json` não é comando válido
nesta versão do FreeSWITCH); (3) ramal externo registrava mas sem áudio —
diagnosticado com contadores de pacote do iptables: a VM está atrás de um
roteador sem port-forward pra faixa de RTP, achado de infraestrutura de
rede, não bug de código.

Também integra o softphone WebRTC (handphone.js/OpenSIPS, já em produção):
código-fonte encontrado em git.falehandix.com.br/Handix/handphone-2.0,
patch mínimo pra aceitar o endereço do proxy em runtime (era build-time),
nova config global (Platform > Infraestrutura > Softphone WebRTC) e widget
na topbar do tenant que pega usuário/domínio/senha do ramal vinculado ao
agente logado.

Adiciona docs/QA_SETUP.md — runbook completo pra subir o ambiente do zero
numa máquina nova (Docker, migrations, seed, systemd), e completa o
.env.example que estava faltando a maioria das variáveis reais.

Testado ponta a ponta com Playwright: edição de rota (criar/editar/F5),
as 3 telas de Infraestrutura com dado real, e um tenant/ramal/agente de
teste criados na hora confirmando que o script do softphone recebe as
credenciais certas.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BFaBaBSQGhyXGEgtTYZGV8
2026-08-30 22:14:39 -03:00

3.4 KiB

Arquitetura de Rede

Estado atual (fase FreeSWITCH inicial)

freeswitch roda na rede padrão do Docker Compose (bridge, b2bcall_default), igual a postgres e redis. Nenhuma porta é publicada no host — nem 8021 (ESL), nem SIP (5060/5080), nem RTP. Isso é intencional: ainda não existe nenhum tronco SIP real nem ramal externo, então não há motivo pra expor nada.

apps/api roda hoje direto no host (fora do Docker), então usa APP_DATABASE_URL/REDIS_URL apontando pra localhost nas portas publicadas pelo Postgres/Redis. Ela não consegue (nem precisa, ainda) alcançar o FreeSWITCH.

Decisão pendente: network_mode do FreeSWITCH (agente.md secao 19)

Quando existir um tronco SIP real (fase Trunks), será preciso decidir entre:

  • network_mode: host: mais simples pra SIP/RTP (sem NAT entre o container e a rede), mas perde isolamento de rede do Docker.
  • macvlan/ipvlan: dá ao FreeSWITCH um IP próprio na rede física, sem expor outros serviços do host: mais trabalho de configurar, melhor isolamento.

Não decidido ainda — só vira relevante quando houver um carrier/SBC real pra conectar (secao 17: "FreeSWITCH não deverá depender de IP SIP público" — a topologia esperada é Internet → OpenSIPS → rede SIP privada → FreeSWITCH, então o FreeSWITCH em si tende a ficar em rede privada mesmo, o que favorece manter bridge/macvlan em vez de host).

Achado real (PHASE 65): VM atrás de roteador, ramal externo sem áudio

Esta VM de laboratório está atrás de um roteador (NAT) — o IP da interface de rede (ens18) é privado (10.10.32.x), o roteador é quem faz NAT pro IP público real. Reportado pelo usuário: um softphone externo registrava com sucesso, mas sem áudio.

Diagnóstico (contadores de pacotes do iptables, não suposição): 10 pacotes chegaram em 5060/udp (SIP), zero pacotes chegaram em qualquer porta da faixa de RTP (16384-16584/udp). O REGISTER funciona porque a resposta trafega de volta pelo mesmo "buraco" NAT que o próprio pacote do cliente abriu — mas RTP usa portas completamente diferentes, como um fluxo NOVO. Sem uma regra de port forward no roteador pra essa faixa (apontando pro IP privado desta VM), esses pacotes nunca chegam até o Docker/FreeSWITCH.

Do lado desta VM estava tudo certo: docker-compose.yml publica a faixa de RTP pra rede (não só loopback), Ext-RTP-IP/Ext-SIP-IP resolvem certo via STUN (sofia status profile internal mostra o IP público real), e a detecção de NAT do próprio FreeSWITCH (apply-nat-acl value="nat.auto", nat.auto é uma ACL auto-gerada pelo core do FreeSWITCH — RFC1918 exceto a própria rede local do container, não precisa existir em acl.conf.xml) funciona corretamente (testado direto via fs_cli -x "acl <ip> nat.auto").

Não tem fix de código pra isso — é infraestrutura de rede fora do controle desta aplicação. Checklist pra quem for expor telefonia real atrás de um roteador: encaminhar UDP 5060 e toda a faixa RTP (16384-16584, Dockerfile do FreeSWITCH) pro IP privado da VM, sem tradução de porta.

Quando apps/api virar container

Hoje ela roda no host por conveniência de desenvolvimento. Quando virar o serviço Docker b2bcall-api (agente.md secao 14), as connection strings precisam trocar de localhost pros hostnames internos do compose (postgres, redis, freeswitch) — ver nota em docs/AUTHENTICATION.md sobre essa pegadinha.