- apps/freeswitch-config (b2bcall-fs-config): Fastify service implementing
the mod_xml_curl HTTP protocol (form-encoded POST -> XML response),
containerized, no host port published
- reactivated mod_xml_curl in FreeSWITCH, binding restricted to
directory|dialplan only (configuration was removed after testing showed
it firing several unnecessary HTTP round-trips at boot for module
configs we don't need dynamic — matches agente.md's own 'don't put every
critical config through XML Curl' guidance)
- no extensions/dialplan tables exist yet (next phases), so the service
always answers 'not found' for now — this phase only proves the wire
protocol works without breaking the static vanilla config fallback
- verified end-to-end: user/8888 (nowhere) -> SUBSCRIBER_ABSENT via
fs-config; user/1000 (static vanilla extension) -> USER_NOT_REGISTERED,
proving FreeSWITCH correctly falls through to static XML when xml_curl
says not found
- docs/XML_CURL.md, including the not-yet-authenticated endpoint note (fine
while it only returns not-found; needs gateway-credentials before serving
real directory/dialplan data)
- packages/telephony: TelephonyProvider interface (agente.md secao 25) and
FreeSwitchTelephonyProvider implementation over the 'esl' library
(actively maintained, TypeScript-native, built-in reconnect-with-backoff
satisfying secao 195); normalizeEslEvent() translates raw ESL events into
the internal vocabulary (secao 24)
- apps/freeswitch-events (b2bcall-fs-events): permanent ESL connection,
resubscribes on every reconnect, publishes normalized events to the
'b2bcall:events' Redis pub/sub channel; containerized (Dockerfile +
docker-compose service) since its whole job is reaching the freeswitch
container by internal hostname
- packages/shared: reusable createLogger() (structured JSON per secao 189),
fixed a BigInt serialization crash surfaced by the esl library's error
stats
- found and fixed a real FreeSWITCH 1.11 default: without an explicit
apply-inbound-acl, mod_event_socket silently rejects any non-loopback
connection ('Access Denied, go away.') even with the correct password —
added a dedicated ACL (loopback + the Docker Compose network range, never
0.0.0.0/0) in infrastructure/freeswitch/overrides/autoload_configs/
- verified end-to-end with a local loopback test call: CALL_CREATED ->
CALL_ANSWERED -> CALL_ENDED observed on the Redis channel with the
correct callUuid and hangup cause
- docs/EVENT_SOCKET.md
- infrastructure/freeswitch/Dockerfile: debian:trixie-slim + SignalWire
packaged freeswitch-meta-vanilla, avoiding a C/C++ build on a 1.9GB RAM VM
- FREESWITCH_PAT used only via Docker BuildKit secret, apt credentials file
created and deleted within the same RUN — verified absent from the final
image with docker history
- minimal module set (agente.md secao 15): sofia, event_socket, commands,
dptools, callcenter, avmd, curl, local_stream, etc. mod_xml_curl installed
but disabled — it refuses to load without a configured gateway-url, which
will exist once b2bcall-fs-config is built
- entrypoint.sh rotates the Event Socket password away from the 'ClueCon'
default at container runtime (never baked into the image); fails loudly if
ESL_PASSWORD is unset
- port 8021 not published to the host; only reachable from other containers
on the compose network
- found and fixed: freeswitch-conf-vanilla is a Recommends (not a Depends)
of freeswitch-meta-vanilla, so --no-install-recommends silently produced
an empty /etc/freeswitch and a crash loop
- verified end-to-end: fs_cli status via ESL with the custom password,
default password rejected, expected modules loaded, healthcheck green,
~44MB RAM usage
- docs/FREESWITCH.md, docs/NETWORK_ARCHITECTURE.md (network_mode decision
deferred until a real SIP trunk exists)