Four customers, four trunk topologies
Every customer SIP integration is a variation on the same three knobs: where the carrier sits (public internet, on-net via firewall, MPLS, or single-host public IP), how authentication works (IP allowlist or registered credentials), and which transport actually carries the signaling (UDP vs TCP — the most common silent failure). The table below is the cheat sheet. Click any row to jump to that customer's deep dive.
| Customer | Trunk Provider | Provider IP(s) | Network Path | Transport | RTP | Codec | Status |
|---|---|---|---|---|---|---|---|
| Allianz | Connecta | 31.155.2.249 | Public internet | UDP/TCP (TBD) | 10000–10499 | ulaw / alaw | Pre-contract |
| Garanti BBVA | KASP + Cisco VVB | 10.231.16.153 · 10.231.16.148 | IP-routed (firewall) | UDP + TCP | 10000–10499 | ulaw / alaw | POC done |
| Anadolu Sigorta | Genesys SBC | 10.1.137.60/61 + 4× /32 | MPLS circuit | TCP (silent) | 10000–10499 | ulaw / alaw | POC running |
| KKB / Kloudeks | Verimor | 194.49.126.26 | Public internet | UDP/TCP | 10000–10499 | ulaw / alaw | POC running |
Allianz — Connecta SIP Trunk (cloud-hosted)
The simplest topology in the lineup. Allianz's CRM owns campaign creation; Freya is the voice runtime; Connecta is the Turkish telecom partner that hands us a SIP trunk over the public internet. There is no VPN, no SBC, no MPLS — just an IP-allowlisted trunk to a Connecta-side gateway at 31.155.2.249.
| Trunk provider | Connecta (TR telecom partner) |
| Provider IP | 31.155.2.249 (public internet) |
| Auth | IP-based, no credentials |
| Transport | UDP/TCP — confirmed in onboarding mail thread |
| Inbound DID | 0216 / 0850-399-99-99 (444) / contact-center numbers — pending Allianz selection |
| Outbound prefix | Connecta-issued routing prefix (e.g. 9015) |
| Routing | Allianz CRM → Connecta → public internet → Freya Asterisk → voice-agent |
| Encryption | none currently |
| Status | Pre-contract · 2026-04-24 |
Topology
identify ACL. Lock the firewall down to Connecta's /32 and nothing else.Things you must remember
- Connecta is a trunk, not a PBX. Allianz's CRM still owns campaign creation, destination lists, and outcome write-back. Freya is just the voice runtime sitting between the trunk and the AI agent.
- Public-internet trunk = strict ACL. Only Connecta's
31.155.2.249may reach our 5060. Lock the firewall by source IP and use the matchingidentifyblock inpjsip.confso unsolicited INVITEs are dropped. - The 9015 prefix. Outbound dialed numbers go out as e.g.
9015-0532-1234567. The dialplan needs a rewrite rule to insert/strip the prefix or Connecta will route the call to the wrong destination. - DID choice has product implications. If Allianz picks the 444 number, callbacks land in their existing call center. If they pick a fresh 0216 number, callbacks come back to Freya. This is a business decision being made on their side.
$ # The KKB pjsip.conf has the same shape Allianz will use. Inspect: $ ssh freya@192.168.35.197 "docker exec freya-asterisk asterisk -rx 'pjsip show endpoints'" $ # Find the [providers] endpoint - that's the trunk. Note the identify match line. $ # When Allianz signs, we'll add a near-identical block pinned to 31.155.2.249.
Reference: notes/customers/allianz/2026-03-31-1017-allianz-connecta-sip-trunk-notes.md
Garanti BBVA — Cisco UCCE + KASP Light Proxy
Garanti is the most enterprise-flavoured topology in the set. The customer telephony stack is Cisco UCCE with a VVB (Virtual Voice Browser) sitting between the call center and the SIP world, and a KASP (Kamailio-based) SIP proxy in light/stateless mode in front of it. Freya's Asterisk pod is on the Garanti OpenShift cluster, on the same routed network. Splunk gives the customer full packet visibility.
| Customer telephony stack | Cisco UCCE call center + VVB voice browser |
| SIP proxy | KASP at 10.231.16.153/32 — light / stateless mode |
| Voice browser | Cisco VVB at 10.231.16.148/32 |
| Asterisk node IP | 10.231.x.120 (node-label pinned for stable IP across restarts) |
| Network path | IP-routed via Garanti firewall · full packet visibility through Splunk |
| Transport | SIP 5060 UDP and TCP — both opened |
| RTP range | Asterisk 10000–10499 · Cisco VVB 13000–13499 |
| Codec | ulaw / alaw |
| Deployment | OpenShift 4.20, namespace freya-poc |
| Status | POC complete · 2026-04-17 |
Topology — and the M08 lesson visualized
Record-Route, so once the dialog is established, ACK and BYE bypass the proxy and go UA-to-UA between Asterisk and VVB. The customer firewall opened 5060 only on the proxy path; the direct UA path was blocked. ACK never arrived. rtptimeout=60 closed the call at exactly 60 s. Fix: open UDP 5060 directly between VVB and Asterisk.The Garanti M08 story (read it once, remember it forever)
- Symptom. Every call dropped at exactly 60 seconds.
- Six hypotheses tested. Firewall ACL gap, NAT/address rewrite missing, stateless proxy bypassing ACK, codec/transport mismatch,
identifymatch miss, header parse failure. - Root cause. KASP light mode does not insert
Record-Route. ACK and BYE bypass the proxy and go direct VVB↔Asterisk. Customer firewall opened 5060 only on the proxy path. ACK never arrived.rtptimeout=60did its job. - Fix. Open UDP 5060 directly between VVB and Asterisk in addition to the proxy path.
Garanti-tuned PJSIP knobs
| Knob | Default | Garanti | Why |
|---|---|---|---|
force_rport | yes | no | SIP proxy is in the middle. Don't let Asterisk override the source-port hint from the proxy. |
rewrite_contact | yes | no | If we rewrite Contact to the packet source, downstream hops get confused. Trust what the proxy puts in headers. |
$ # See the same knobs on KKB endpoints (default values, since KKB has no proxy): $ ssh freya@192.168.35.197 "docker exec freya-asterisk asterisk -rx 'pjsip show endpoint providers'" \ | grep -E 'force_rport|rewrite_contact|rtp_timeout'
Reference: freya-onprem/openshift/garanti/, freya-onprem/playbooks/sip-asterisk-debug.md
Anadolu Sigorta — Genesys SBC + MPLS
The customer telephony stack here is Genesys (call center + SBC, on Cisco UCCE Appliance OS variant). Freya's Asterisk sits at 10.31.39.14; the Genesys SBC is at 10.1.137.60/61 (two interfaces on one device) plus four Genesys generic /32s. The path between them is an MPLS circuit, which means firewall-transparent at the port level — the customer firewall does not enforce TCP/UDP rules on this path. ACLs only live on the network gear.
| Customer telephony stack | Genesys call center + Genesys SBC (Cisco UCCE Appliance variant) |
| SBC IPs | 10.1.137.60/61 + 4× Genesys generic /32s |
| Asterisk node IP | 10.31.39.14 (node-label pinned) |
| Network path | MPLS circuit · firewall-transparent · ACLs only on network gear |
| Transport | SIP 5060 TCP (initially labeled UDP, silently enforced as TCP — see A20) |
| RTP range | Asterisk 10000–10499 · SBC 16384–48000 |
| Inbound DID | 0850-724-0850 (test only, reverted) — never attach the live customer-service DID |
| Trunk codes | 5001 outbound · 5002 inbound · 5003 outbound-transfer · 5004 inbound-transfer |
| Direction header | X-Freya-Direction (whitelisted; per-trunk policy) |
| Codec | ulaw / alaw |
| Deployment | OpenShift 4.18+, namespace freya-poc |
| Status | POC running · concurrent-call testing |
Topology
tcpdump on the wire before debugging anywhere else.Two traps you must remember
A20 — UDP/TCP confusion
Customer ticket said "open 5060 UDP". İşnet (outsourced firewall) actually enforced TCP. Symptom: silent failure, no SIP arrives, no error. Burcu (İşnet) fixed it live during the 2026-04-03 debug call. Lesson: always confirm transport actively with tcpdump before anything else.
A4 — non-ASCII header names
Genesys's X-Genesis-… headers contained Turkish characters in field names (not values — names). PJSIP silently dropped the entire INVITE. Fix: ASCII-only header names. Lesson: when an INVITE silently disappears, capture it on the wire and look at the bytes — your dialplan never saw it.
$ # The "always confirm transport" check: $ ssh freya@192.168.35.197 "sudo tcpdump -i any -nn -v 'tcp port 5060 or udp port 5060'" 2>&1 | head -30
Reference: notes/customers/anadolu-sigorta/on-prem-blueprint.md, 2026-04-03-1404-anadolu-sigorta-sip-debug-notes.md
KKB / Kloudeks — Verimor Trunk
This is the box you have an SSH key to: kkbfcfreyasrv01 (192.168.35.197 internal, 185.199.89.19 public). Single-host Docker Compose deployment, 4× H100 NVL, Gemma LLM. The trunk is Verimor, a Turkish wholesale PSTN carrier, reached over the public internet at 194.49.126.26. Outbound caller-ID is 908502427127.
| Trunk provider | Verimor (TR PSTN) |
| Provider IP | 194.49.126.26/32 |
| Caller-ID | 908502427127 |
| Asterisk public IP (egress) | 185.199.89.19 |
| Transport | SIP 5060 UDP/TCP · RTP 10000–10499 UDP |
| Codec | ulaw / alaw |
| Deployment | Single-host Docker Compose at kkbfcfreyasrv01 |
| Status (2026-04-24) | POC running · concurrency tests show carrier capping |
Topology
The 2026-04-24 outbound concurrency tests
Three burst campaigns at 16:31, 18:26, and 20:38 UTC. The Freya stack itself was healthy throughout — voice agents, dashboard, campaign worker, Asterisk all green. The failures originate at the carrier (Verimor), plus a number-format bug that broke the first burst entirely.
2464e8d2
d8cb2cf4
879c7bb0
94350 prefix bug visualized: every dialed string was malformed before it reached the trunk, so the 4 unique numbers got a 404 (cause 3 — unallocated) and the 5 always-blocked numbers still got a 603 because the tail digits matched.What 2026-04-24 told us
- Not a Freya-side network problem. Voice agents (30 containers), dashboard, campaign worker, Asterisk — all healthy. Pre-dial path completed successfully on every attempt, including the failed ones.
- Same five destinations rejected across all three campaigns.
+905374705251,+905510154221,+905532890209,+905053864612,+905535918355all returned SIP 603 / Q.850 cause 41 in every test. Stable carrier-side block, not transient congestion. - Number-format bug in the 16:31 campaign. Every destination dialed with a stray
94350prefix instead of+90. Result: zero connections. - One transient 408 on
+905537721287at 20:38. Same number connected fine at 18:26, so it was a handset/PSTN issue, not a trunk issue.
Open work items at KKB
- Open a Verimor ticket with the five blocked numbers and ask about per-caller-ID concurrency caps.
- Find and fix the source of the
94350prefix (campaign upload path or dialplan rewrite). - Throttle outbound dialing on the campaign worker (e.g. 2 calls/sec/caller-ID).
- Surface SIP response codes + Q.850 cause in the dashboard so future triage doesn't need shell access.
Full report: notes/customers/kloudeks-kkb/reports/kkb-outbound-concurrency-2026-04-24/
Final — trace a live call through every layer
The capstone exercise. Place one outbound test call on KKB and account for every hop. Four SSH terminals, one dashboard tab, one synchronized timeline. If you can read all four streams in parallel and tell the story of the call, you are a SIP debugger.
Setup — four parallel SSH sessions
Open four terminals on your laptop. Each one tails a different layer of the stack on kkbfcfreyasrv01:
$ ssh freya@192.168.35.197 \
"docker exec freya-asterisk \
asterisk -rx 'pjsip set logger on'"
$ ssh freya@192.168.35.197 \
"docker logs -f freya-asterisk" \
| grep --line-buffered \
-E 'INVITE|ACK|BYE|200 OK|180|183|603|404|408|486'
→ INVITE sip:+905...@194.49.126.26
← SIP/2.0 100 Trying
← SIP/2.0 183 Session Progress
← SIP/2.0 200 OK
→ ACK sip:+905...@...
... 32s of media ...
→ BYE sip:+905...@...
← SIP/2.0 200 OK
$ ssh freya@192.168.35.197 \
"sudo tcpdump -i any -w /tmp/capstone.pcap \
udp portrange 10000-10499 or port 5060"
tcpdump: listening on any, link-type
LINUX_SLL2 (Linux cooked v2)
0 packets dropped by kernel
... capturing ...
^C
3214 packets captured
3221 packets received by filter
0 packets dropped by kernel
$ ssh freya@192.168.35.197 \
"docker logs -f freya-campaign-worker"
[INFO] Processing initiate_call job
7e2f4a8c-... outbound dial=+9053...
[INFO] initiate_call: in_progress
[INFO] Stored call_state in Valkey
[INFO] Awaiting ARI StasisStart for
PJSIP/providers-00000017
[INFO] Bridged call to voice-agent
kkb-freya-voice-agent-3
[INFO] initiate_call: connected
... 32s ...
[INFO] initiate_call: completed
duration=32.4s code=200
$ ssh freya@192.168.35.197 \
"docker logs -f kkb-freya-voice-agent-1"
[boot] resolve_config starting
[boot] register_call ok
[boot] generate_first_message ok
[boot] Stored boot manifest in Valkey
[pipe] WebSocket /ai_media accepted
[pipe] STT engaged · partial = 'merhaba'
[pipe] LLM step 1 · 482ms
[pipe] TTS engaged · sent 18 frames
[pipe] STT engaged · partial = 'evet'
[pipe] LLM step 2 · 419ms
... 12 turns ...
[hangup] BYE received from peer
[hangup] MixMonitor stopped
What you should see, in order
- Campaign worker (T3) ·
Processing initiate_call job <uuid>→initiate_call: in_progress. The dashboard's "Place call" button has reached the worker. - Voice-agent (T4) ·
Boot step 'resolve_config' starting→register_call→generate_first_message→Stored boot manifest in Valkey. Per-call agent process is ready before the SIP leg even starts ringing. - Asterisk SIP log (T1) · outbound
INVITEto194.49.126.26(Verimor) →100 Trying→183 Session Progress→200 OK→ACK. The full happy-path SDP exchange. - Asterisk channel log ·
Channel PJSIP/providers-… joined 'simple_bridge'→Channel WebSocket/ai_media-… joined 'simple_bridge'. The trunk leg and the agent leg are now in the same bridge. - Voice-agent (T4) · STT + LLM + TTS activity logs. The pipeline turns are now real-time. Each STT partial / LLM call / TTS frame should land within hundreds of milliseconds.
- pcap (T2) · UDP 10000–10499 packets in both directions, ~50/sec each way. RTP is flowing. If this layer is silent while T1 says "200 OK", you have a media-path problem (firewall, NAT, codec).
- Hangup · SIP
BYE→200 OK→End MixMonitor Recording. Clean teardown. If T1 shows the BYE but T3/T4 never see "completed", you have a state-machine problem in the campaign worker or the agent.
If any of those is missing, you have a specific layer to investigate. The whole thing is a state machine, and now you can read every transition.
$ ssh freya@192.168.35.197 "docker exec freya-asterisk asterisk -rx 'pjsip set logger off'" $ scp freya@192.168.35.197:/tmp/capstone.pcap . $ open capstone.pcap # opens in Wireshark on macOS
In Wireshark: Telephony → VoIP Calls → your call → Flow Sequence. Compare what you see there to the order above. They should agree exactly. The pcap is the ground truth — your terminal logs are interpretations of the same wire data, and Wireshark is the unfiltered version.
You place an outbound test call. T1 shows INVITE → 100 Trying → 200 OK → ACK. T2 shows packets only on port 5060, none in the 10000–10499 range. T3 says "in_progress" but never "connected". T4 shows the voice-agent booting but never engaging STT. Where is the bug?
Show answer
Media-path failure. Signaling is healthy (T1 is clean), but RTP isn't flowing (T2 has no port 10000–10499 packets), so the bridge never carries audio and the agent never hears the user. Check: (1) firewall ACL covers UDP 10000–10499 in both directions, (2) the SDP offer/answer in the INVITE/200 OK negotiated a codec both sides accept (ulaw/alaw), (3) Asterisk's rtp.conf port range matches the firewall rule. The campaign worker reports "in_progress" because the SIP leg is technically up; it never moves to "connected" because no media bridge events arrive.
You are now a SIP debugger.