Telecom Knowledge Base
// knowledge_base v1.0

Современная
Телефонная Система

Полный справочник по построению VoIP-системы с нуля — от базовых протоколов до продакшен-архитектуры. Все функции, компоненты, связи и порядок реализации.

134 Функции
11 Компонентов
16 Категорий
8 Фаз разработки
01

Основы телефонии

Фундаментальные протоколы и концепции, на которых строится любая современная телефонная система. Понимание этих основ — ключ ко всему остальному.

Что такое VoIP

Voice over IP — голос поверх интернет-протокола

VoIP (Voice over Internet Protocol) — это технология передачи голоса через IP-сети (интернет, локальная сеть) вместо традиционных телефонных линий. Голос оцифровывается, сжимается кодеком и отправляется в виде IP-пакетов — точно так же, как веб-страницы или электронная почта.

Традиционная телефония (PSTN)

Выделенный канал на всё время разговора (circuit-switching). Один провод — один звонок. Дорогая инфраструктура, ограниченные возможности, фиксированная маршрутизация.

VoIP-телефония

Голос передаётся пакетами через общую IP-сеть (packet-switching). Один канал — тысячи звонков. Дешёвая инфраструктура, гибкая маршрутизация, интеграция с ПО.

Почему VoIP победил?

Стоимость: междугородние и международные звонки почти бесплатны. Гибкость: можно звонить с телефона, компьютера, браузера. Интеграция: CRM, аналитика, запись, IVR — всё через API. Масштабирование: добавление линий не требует нового оборудования.

Ключевой принцип VoIP

В VoIP есть чёткое разделение сигнализации и медиа. Сигнализация (SIP) — это «кто кому звонит, принять/отклонить». Медиа (RTP) — это сам голос. Они передаются по разным каналам и могут идти разными путями.

Протокол SIP

SIP — Session Initiation Protocol

SIP — протокол сигнализации. Он отвечает только за установку, изменение и завершение сеансов связи. SIP не передаёт голос — для этого используется RTP. SIP похож на HTTP: текстовый, читаемый, с заголовками и методами. Стандартные порты: 5060 (UDP/TCP) и 5061 (TLS).

Полный диалог SIP-вызова между двумя телефонами через АТС (PBX):

Телефон A                    PBX (АТС)                   Телефон B
    │                           │                           │
    │  ──── INVITE ────>       │                           │
    │                           │  ──── INVITE ────>       │
    │                           │                           │
    │                           │  <── 100 Trying ───       │
    │  <── 100 Trying ───       │                           │
    │                           │                           │
    │                           │  <── 180 Ringing ──       │
    │  <── 180 Ringing ──       │                           │
    │                           │                           │
    │                           │  <──── 200 OK ─────       │
    │  <──── 200 OK ─────       │                           │
    │                           │                           │
    │  ────── ACK ──────>────── ACK ──────>       │
    │                           │                           │
    │  ═══════════════════ RTP (голос) ═══════════════════   │
    │                           │                           │
    │  ────── BYE ──────>────── BYE ──────>       │
    │                           │                           │
    │                           │  <──── 200 OK ─────       │
    │  <──── 200 OK ─────       │                           │
    │                           │                           │
Рис. 1 — Полный SIP-диалог: установка, медиа-сессия и завершение вызова
1
INVITE — инициация звонка. Содержит SDP с параметрами медиа (кодеки, IP, порт).
2
100 Trying — сервер принял запрос и обрабатывает (временный ответ).
3
180 Ringing — телефон получателя звонит (вызывающий слышит гудки).
4
200 OK — получатель поднял трубку. Содержит ответный SDP.
5
ACK — подтверждение. После него начинается RTP-поток (голос).
6
BYE — завершение вызова. Ответ 200 OK подтверждает разрыв.

Анатомия SIP INVITE-сообщения — ключевые заголовки:

SIP INVITE — пример сообщения
INVITE sip:1002@192.168.1.10 SIP/2.0 Via: SIP/2.0/UDP 192.168.1.20:5060;branch=z9hG4bK776 From: "Иван Иванов" <sip:1001@192.168.1.10>;tag=49583 To: <sip:1002@192.168.1.10> Call-ID: a84b4c76e66710@192.168.1.20 CSeq: 314159 INVITE Contact: <sip:1001@192.168.1.20:5060> Content-Type: application/sdp Max-Forwards: 70 Content-Length: 142 ; ─── SDP-тело (Session Description Protocol) ─── v=0 o=- 2890844526 2890844526 IN IP4 192.168.1.20 s=SIP Call c=IN IP4 192.168.1.20 t=0 0 m=audio 49170 RTP/AVP 0 8 101 a=rtpmap:0 PCMU/8000 a=rtpmap:8 PCMA/8000 a=rtpmap:101 telephone-event/8000
Ключевые заголовки

From / To — кто звонит и кому
Via — маршрут прохождения запроса
Contact — прямой адрес для ответа
Call-ID — уникальный идентификатор звонка
CSeq — порядковый номер транзакции

SDP-тело

SDP (Session Description Protocol) передаётся внутри SIP-сообщений и описывает медиа-параметры:
— IP-адрес и порт для RTP
— Список поддерживаемых кодеков
— Тип медиа (аудио, видео)
Обе стороны обмениваются SDP для согласования параметров (offer/answer).

Протокол RTP

RTP — Real-time Transport Protocol

RTP — протокол, который передаёт сам голос (и видео). В отличие от SIP, RTP работает поверх UDP и отправляет пакеты с аудио-данными 50 раз в секунду (каждые 20 мс). RTP-поток идёт отдельно от SIP — часто напрямую между устройствами, минуя сервер.

SIP-сигнализация                          RTP-медиа

  Телефон A ──SIP──> PBX ──SIP──> Телефон B     Телефон A ═══RTP═══ Телефон B
  (порт 5060)        (порт 5060)               (порт 10000-20000)

Сигнализация проходит через PBX,         Медиа может идти напрямую
а медиа — напрямую между телефонами       (если PBX не нужен для записи/мониторинга)
Рис. 2 — Разделение сигнализации (SIP) и медиа (RTP)

Структура RTP-пакета:

Структура RTP-заголовка (12 байт)
; Каждый RTP-пакет содержит: ┌──────────────────────────────────────────────────┐ │ V=2 │ P │ X │ CC │ M │ Payload Type (7 бит) │ ; Тип кодека (0=PCMU, 8=PCMA...) ├──────────────────────────────────────────────────┤ │ Sequence Number (16 бит) │ ; Порядковый номер пакета ├──────────────────────────────────────────────────┤ │ Timestamp (32 бит) │ ; Временная метка аудио-семпла ├──────────────────────────────────────────────────┤ │ SSRC (32 бит) │ ; Идентификатор источника ├──────────────────────────────────────────────────┤ │ Payload (аудио-данные) │ ; 160 байт для G.711 @ 20мс └──────────────────────────────────────────────────┘
Ключевые поля

Payload Type — какой кодек используется (определён в SDP при установке).
Sequence Number — порядковый номер для обнаружения потерь пакетов.
Timestamp — когда был записан аудио-семпл (для синхронизации).
SSRC — уникальный идентификатор потока (один на каждого участника).

Джиттер-буфер (Jitter Buffer)

Пакеты приходят с разной задержкой (jitter). Джиттер-буфер накапливает несколько пакетов и выдаёт их с равномерным интервалом. Слишком маленький буфер — потери и щелчки. Слишком большой — заметная задержка голоса. Типичный размер: 20–60 мс. Адаптивный буфер подстраивается автоматически.

RTP и RTCP

Вместе с RTP работает протокол RTCP (RTP Control Protocol) — он передаёт статистику качества: потери пакетов, джиттер, задержку. RTCP используется для мониторинга качества связи, но не для самого голоса.

Кодеки

Кодек (Codec) — алгоритм кодирования и декодирования голоса. Определяет качество звука, требуемую полосу пропускания и вычислительную нагрузку. Выбор кодека — компромисс между качеством и трафиком.

Кодек Битрейт Качество Сжатие Особенности
G.711 (a-law / u-law) 64 kbps Отличное Нет Стандарт для LAN. a-law — Европа, u-law — Северная Америка. Минимальная задержка.
G.722 64 kbps HD Voice Нет Широкополосный (wideband, 16 kHz). Звучит заметно лучше G.711. Поддерживается большинством IP-телефонов.
G.729 8 kbps Хорошее 8:1 Сильное сжатие — идеален для каналов с ограниченной полосой. Лицензируемый (есть бесплатный G.729a).
Opus 6–510 kbps Лучшее Переменное Современный, адаптивный. Используется в WebRTC. Open-source, без лицензий. Лучшее качество на любом битрейте.
iLBC 13.3 / 15.2 kbps Хорошее ~4:1 Устойчив к потерям пакетов (каждый фрейм независим). Хорош для нестабильных сетей.
GSM 13 kbps Среднее ~5:1 Унаследованный кодек мобильных сетей. Используется редко, но иногда встречается в legacy-системах.
Рекомендация

Для внутренней сети (LAN) используйте G.711 или G.722 — максимальное качество, полоса не проблема. Для WebRTC — Opus (он обязателен). Для каналов с ограниченной полосой — G.729. Opus является лучшим универсальным выбором, если оборудование его поддерживает.

DTMF

DTMF — Dual-Tone Multi-Frequency

DTMF — тоновые сигналы, генерируемые при нажатии кнопок на телефоне. Каждая кнопка — это комбинация двух частот (отсюда «dual-tone»). DTMF используется для навигации по голосовым меню (IVR), ввода PIN-кодов, управления конференциями и других интерактивных сценариев.

Три способа передачи DTMF в VoIP:

In-band (в аудиопотоке)

Тоны передаются прямо в RTP-потоке как обычный звук. Проблема: сжимающие кодеки (G.729) искажают тоны, и они не распознаются. Работает только с G.711.

Ненадёжный

RFC 2833 (RTP Events)

Цифры передаются как специальные RTP-пакеты с payload type 101. Не зависит от кодека — работает всегда. Самый распространённый метод.

Рекомендуемый

SIP INFO

Цифра отправляется в SIP-сообщении типа INFO. Используется в некоторых специфичных сценариях (например, при взаимодействии с определёнными шлюзами).

Специальный

Важно для IVR

Если ваши голосовые меню не реагируют на нажатия — почти всегда проблема в режиме DTMF. Убедитесь, что и сервер, и телефон используют один и тот же метод (обычно RFC 2833).

NAT и телефония

Почему NAT — главная головная боль VoIP

NAT (Network Address Translation) заменяет приватные IP-адреса на публичный при выходе в интернет. Проблема: SIP-сообщения содержат приватный IP телефона («отправляй голос на 192.168.1.100:10000»), который недоступен извне. Результат — одностороннее аудио или полное его отсутствие.

Проблема NAT в VoIP:

Телефон A                 NAT/Роутер              SIP-сервер
192.168.1.100             203.0.113.50             198.51.100.10
     │                        │                        │
     │  SIP INVITE            │                        │
     │  Contact: 192.168.1.100 │                        │
     │  SDP: audio 192.168.1.100:10000                  │
     │ ──────────────────────>│                        │
     │                        │  SIP INVITE            │
     │                        │  Contact: 192.168.1.100 <── Приватный IP!
     │                        │ ──────────────────────>│
     │                        │                        │
     │                        │    RTP → 192.168.1.100 │
     │                        │        НЕДОСТУПЕН!      │
     │                        │    ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗ ✗  │
Рис. 3 — Приватный IP-адрес в SDP недоступен из интернета — голос не проходит

Решения проблемы NAT:

STUN Основной

Session Traversal Utilities for NAT. Телефон спрашивает STUN-сервер: «какой у меня публичный IP и порт?» — и подставляет их в SDP. Работает с большинством NAT. Не работает с symmetric NAT.

TURN Запасной

Traversal Using Relays around NAT. Весь медиа-трафик проходит через TURN-сервер (relay). Работает всегда, но увеличивает задержку и нагрузку на сервер. Используется когда STUN не помогает.

ICE Комплексный

Interactive Connectivity Establishment. Собирает все возможные «кандидаты» (прямой IP, STUN, TURN) и проверяет каждый путь. Выбирает лучший рабочий вариант. Стандарт для WebRTC.

SIP ALG Избегайте

Application Layer Gateway. Функция роутера, которая «умно» переписывает SIP-пакеты. На практике почти всегда ломает SIP. Рекомендация: отключить SIP ALG на роутере.

Практический совет

Если звонок устанавливается (вы слышите гудки), но после ответа нет голоса — в 90% случаев это проблема NAT. Проверьте: STUN настроен? SIP ALG отключён? Порты RTP (10000–20000) проброшены?

WebRTC

WebRTC — звонки прямо из браузера

WebRTC (Web Real-Time Communication) — стандарт, встроенный во все современные браузеры (Chrome, Firefox, Safari, Edge), позволяющий передавать голос, видео и данные без плагинов. Для VoIP это означает: пользователь открывает веб-страницу и сразу может звонить.

Технологический стек WebRTC:

Медиа и безопасность

Opus — обязательный аудио-кодек (лучшее качество)
DTLS-SRTP — шифрование медиа (обязательно, нельзя отключить)
ICE + STUN + TURN — решение NAT-проблем
SDP — описание медиа-сессии (offer/answer)

Подключение к SIP-миру

Способ 1: SIP over WebSocket
Библиотека SIP.js или JsSIP в браузере отправляет SIP-сообщения через WebSocket (WSS). Сервер (FreeSWITCH) принимает их как обычные SIP.

Способ 2: Verto Protocol
Собственный протокол FreeSWITCH поверх WebSocket (JSON-RPC). Проще, чем SIP, но привязан к FreeSWITCH.

Браузер (WebRTC)          FreeSWITCH            SIP-телефон

┌─────────────┐       ┌──────────────┐       ┌───────────┐
│  SIP.js     │       │ mod_verto    │       │           │
│  (JavaScript│──WSS──│ mod_sofia    │──SIP──│ IP Phone  │
│  WebRTC API)│       │              │       │           │
└─────────────┘       └──────────────┘       └───────────┘
       │                     │                     │
       └─────── DTLS-SRTP (шифрованный RTP) ───────┘
                      или через сервер
Рис. 4 — Подключение WebRTC-браузера к SIP-инфраструктуре через FreeSWITCH
WebRTC = будущее телефонии

WebRTC делает телефонию доступной любому пользователю с браузером — без установки ПО, без настроек сети, с обязательным шифрованием. Идеально для контакт-центров, CRM-интеграций и клиентских порталов.

Минимальный пример подключения SIP.js к FreeSWITCH
JavaScript — SIP.js UserAgent
const ua = new SIP.UserAgent({ uri: SIP.UserAgent.makeURI("sip:1001@pbx.example.com"), transportOptions: { server: "wss://pbx.example.com:7443" }, authorizationUsername: "1001", authorizationPassword: "secret" }); // Подключение и регистрация await ua.start(); const registerer = new SIP.Registerer(ua); await registerer.register(); // Исходящий звонок const inviter = new SIP.Inviter(ua, SIP.UserAgent.makeURI("sip:1002@pbx.example.com") ); await inviter.invite();

SIP-транки и SBC

Что такое SIP-транк — простыми словами

Представьте: вы построили собственную телефонную систему (FreeSWITCH, Kamailio, веб-телефоны). Внутри вашей сети все сотрудники могут звонить друг другу. Но как позвонить на обычный мобильный или городской номер? Ваша система не подключена к телефонной сети (PSTN).

SIP-транк — это виртуальный «провод» между вашей телефонной системой и оператором связи, который имеет выход в обычную телефонную сеть (PSTN). Через этот «провод» передаётся SIP-сигнализация и голосовой трафик (RTP).

Аналогия

Ваша PBX — это офисная АТС.
SIP-транк — это телефонная линия от оператора, которая приходит в вашу АТС.
Без неё АТС работает только внутри офиса. С ней — звонки уходят наружу на любые номера мира.

Раньше это был физический кабель (ISDN PRI — 30 каналов в одном проводе). Сегодня SIP-транк — это IP-подключение через интернет. Никаких физических проводов — только настройка в конфигурации.

Что даёт SIP-транк

DID-номера — вам выделяют телефонные номера (например +49 30 1234567 для Берлина), на которые люди могут звонить и попадать в вашу систему.

Исходящие звонки — ваши сотрудники могут звонить на любые номера мира, оператор маршрутизирует вызов в PSTN.

Caller ID — при исходящем звонке у абонента отображается ваш номер (DID), а не какой-то случайный.

Ёмкость — количество одновременных звонков. В отличие от ISDN (30 каналов), SIP-транк может пропускать сотни звонков одновременно.

Как получить SIP-транк — пошаговый процесс
1
Выбираете SIP-провайдера — компанию, у которой есть лицензия оператора связи и подключение к PSTN. Примеры: Telnyx Twilio Vonage Flowroute — глобальные. sipgate (DE), OVH Telecom (FR), Ростелеком (RU) — локальные.
2
Регистрируетесь и верифицируетесь — подтверждаете компанию (KYC), адрес, иногда лицензии. Для некоторых стран (ОАЭ, Саудовская Аравия) требуется локальное юрлицо и регуляторное одобрение.
3
Покупаете DID-номера — выбираете номера в нужных странах/городах. Например: +49 30 XXXXXXX (Berlin), +1 212 XXXXXXX (New York), +971 4 XXXXXXX (Dubai). Цена: от $1 до $15/мес за номер в зависимости от страны.
4
Получаете учётные данные для подключения — провайдер даёт вам один из двух вариантов:
IP-аутентификация — вы сообщаете IP-адрес вашего сервера, провайдер разрешает трафик только с этого IP. Без паролей.
Credentials — логин + пароль, ваш сервер регистрируется на SIP-сервере провайдера (как обычный SIP-телефон).
5
Настраиваете транк на вашей PBX — прописываете адрес SIP-сервера провайдера, учётные данные, кодеки, правила маршрутизации. Входящие звонки на ваши DID приходят по SIP INVITE от провайдера.
6
Тестируете — звоните с мобильного на ваш DID-номер → звонок должен прийти в вашу систему. Звоните из вашей системы на мобильный → у абонента отображается ваш DID как Caller ID.
Два способа аутентификации транка
IP-аутентификация Registration (credentials)
Как работает Провайдер добавляет ваш IP в белый список. Все SIP-пакеты с этого IP принимаются без пароля Ваш сервер отправляет REGISTER на SIP-сервер провайдера с логином/паролем, как обычный SIP-телефон
Для кого Enterprise с выделенным IP. Наш вариант Небольшие компании, динамический IP, домашние PBX
Безопасность Высокая — привязка к IP, нельзя подключиться с чужого адреса Средняя — пароль может быть скомпрометирован
NAT Не проблема — IP фиксированный Может быть проблемой — REGISTER через NAT требует keep-alive
Failover Можно указать несколько IP (primary + backup) Ограничен — регистрация с одного хоста
🔍 Как SIP-транк выглядит технически (SIP-сообщения)

Когда кто-то звонит на ваш DID +49 30 1234567, провайдер отправляет на ваш сервер SIP INVITE:

INVITE sip:+493012345670@your-server-ip:5060 SIP/2.0 Via: SIP/2.0/UDP provider-sip-server:5060 From: <sip:+4917612345678@provider.com>;tag=abc123 To: <sip:+493012345670@your-server-ip> Call-ID: 9876xyz@provider.com CSeq: 1 INVITE Contact: <sip:+4917612345678@provider-sip-server:5060> Content-Type: application/sdp ; SDP — описание медиа-потока (какой кодек, куда слать RTP) v=0 o=- 123456 1 IN IP4 provider-media-server c=IN IP4 provider-media-server m=audio 20000 RTP/AVP 0 8 18 a=rtpmap:0 PCMU/8000 ; G.711 μ-law a=rtpmap:8 PCMA/8000 ; G.711 A-law a=rtpmap:18 G729/8000 ; G.729

Ваш сервер отвечает 200 OK со своим SDP (свой IP для RTP, свои кодеки), и голосовой канал устанавливается.

Для исходящего звонка — наоборот: ваш сервер отправляет INVITE на SIP-сервер провайдера:

INVITE sip:+4917612345678@provider-sip-server:5060 SIP/2.0 From: <sip:+493012345670@your-server-ip>;tag=xyz789 To: <sip:+4917612345678@provider-sip-server> ; Провайдер проверяет: пришло ли с разрешённого IP? (IP-auth) ; Или: есть ли Authorization-заголовок? (credentials) ; Если ОК — маршрутизирует вызов в PSTN
💰 Сколько стоит SIP-транк (ценообразование)
Компонент Модель оплаты Примерная цена
DID-номер Ежемесячная аренда $1–15/мес за номер (зависит от страны, US дешевле, ОАЭ дороже)
Входящие звонки По минутам или бесплатно $0–0.01/мин (многие провайдеры включают во входящие бесплатно)
Исходящие звонки По минутам $0.005–0.03/мин (локальные), $0.02–0.15/мин (международные)
Каналы (ёмкость) Зависит от провайдера Некоторые берут за concurrent channels ($2–5/канал), другие — без ограничений
Экстренные номера (E911) Обязательный сбор (US) $1–2/мес за номер
Enterprise-совет

При масштабе 43 страны — договаривайтесь с провайдерами напрямую о volume pricing. Крупные провайдеры (Telnyx, Bandwidth, BICS) дают скидки при объёме от 10,000+ минут/мес. Для некоторых стран выгоднее использовать локальных операторов (дешевле, лучше качество, регуляторные требования).

Как SBC помогает при работе с SIP-транками

Когда у вас один провайдер — можно обойтись без Trunk SBC. FreeSWITCH напрямую общается с провайдером.

Когда у вас 43 страны и десятки провайдеров — начинается хаос, и Trunk SBC становится спасением:

Проблема 1: Каждый провайдер «говорит» на своём SIP

Стандарт SIP (RFC 3261) — это 269 страниц. Каждый провайдер интерпретирует его по-своему:

• Один отправляет Caller ID в заголовке From
• Другой — в P-Asserted-Identity
• Третий — в Remote-Party-ID
• Кто-то требует + перед номером, кто-то без
• Форматы номеров: E.164, локальный, с кодом страны, без

Trunk SBC решает: нормализует все входящие SIP-сообщения к единому формату. FreeSWITCH всегда видит одинаковый SIP независимо от провайдера.

Проблема 2: LCR — куда дешевле маршрутизировать

Звонок из Берлина в Мюнхен:

• Через Telnyx: $0.008/мин
• Через sipgate (локальный DE): $0.003/мин
• Через Twilio: $0.014/мин

Звонок из Берлина в Дубай:

• Через Telnyx: $0.09/мин
• Через du (локальный UAE): $0.04/мин
• Через Twilio: $0.12/мин

Trunk SBC решает: LCR (Least Cost Routing) — автоматически выбирает самый дешёвый маршрут для каждого звонка на основе префикса номера. Экономия — 30-60% на исходящих звонках.

Проблема 3: Провайдер упал — звонки не проходят

У любого провайдера бывают аварии. Если FreeSWITCH напрямую подключён к одному транку — все исходящие звонки прекращаются.

Trunk SBC решает: автоматический failover. Если primary-транк не отвечает за 3 секунды → переключение на backup-транк. Пользователь даже не заметит.

# Пример failover-логики на Trunk SBC (Kamailio) # Звонок в Германию: if (prefix == "+49") { route(1): sipgate.de ← primary (локальный, дешёвый) route(2): telnyx.com ← backup route(3): twilio.com ← last resort }
Проблема 4: Toll Fraud — мошеннические звонки

Самая опасная угроза в телефонии. Злоумышленник получает доступ к вашей системе и начинает звонить на premium-номера (Куба, Сомали, спутниковые) по $3-5/минуту. За выходные — счёт на $50,000+.

Trunk SBC решает:

Rate limiting — максимум N одновременных исходящих звонков
Geo-blocking — блокировка звонков в «опасные» направления
Spending alerts — уведомление если расходы за час превысили порог
Anomaly detection — необычный паттерн (100 звонков на Кубу в 3 AM) → блокировка

Проблема 5: Topology hiding

Без Trunk SBC провайдер видит внутреннюю структуру вашей сети — IP-адреса FreeSWITCH, номера портов, имена серверов в заголовках Via и Contact.

Trunk SBC решает: переписывает все SIP-заголовки. Провайдер видит только IP/имя Trunk SBC. Ваша внутренняя архитектура скрыта.

Проблема 6: Кодеки и медиа

Внутри вашей сети используется Opus (высокое качество, низкий трафик). Но многие PSTN-провайдеры принимают только G.711 (PCMU/PCMA) или G.729.

Trunk SBC + RTPEngine решают: транскодирование на границе. Внутри сети — Opus/G.722. На выходе к провайдеру — G.711. Прозрачно для пользователей.

Итого: путь звонка через оба SBC
  ВХОДЯЩИЙ ЗВОНОК (клиент звонит на ваш DID)

  Мобильный      SIP-провайдер     Trunk      Kamailio    FreeSWITCH    Access      Оператор
  телефон   ───▶  (Telnyx)   ───▶  SBC   ───▶  Core   ───▶  (IVR,   ───▶  SBC    ───▶  (веб-
              PSTN→SIP           нормализ.    маршрут.    очередь)    SRTP↔RTP     телефон)
                                 E.164        LB→FS       bridge      WebRTC


  ИСХОДЯЩИЙ ЗВОНОК (оператор звонит клиенту)

  Оператор      Access       Kamailio    FreeSWITCH    Trunk      SIP-провайдер    Мобильный
  (веб-    ───▶  SBC    ───▶  Core   ───▶  (bridge) ───▶  SBC   ───▶  (sipgate)  ───▶  телефон
  телефон)     WSS→SIP     маршрут.    dial out     LCR          SIP→PSTN
               SRTP→RTP    LB→FS                   failover
                                                    anti-fraud
Обратите внимание: оба SBC участвуют в каждом звонке «наружу». Access SBC защищает вход от клиентов, Trunk SBC защищает выход к провайдерам. Между ними — доверенное ядро (Core + FreeSWITCH), которое никогда не контактирует с внешним миром напрямую.

Протоколы АТС — полная карта

Зачем знать протоколы

В телефонии один звонок задействует 5-8 протоколов одновременно. Каждый отвечает за свою задачу: один устанавливает звонок, другой передаёт голос, третий шифрует, четвёртый пробивает NAT. Без понимания этих протоколов невозможно диагностировать проблемы.

Карта протоколов: кто с кем и через что общается
  Браузер           SIP-телефон       SBC            Kamailio       FreeSWITCH       Go API
  (WebRTC)         (аппаратный)      (граница)       (Core)         (медиа)          (логика)
     │                  │               │               │               │               │
     │◄──── WSS ────────┼──────────────►│               │               │               │
     │  SIP over        │               │               │               │               │
     │  WebSocket       │               │               │               │               │
     │  (порт 443)      │               │               │               │               │
     │                  │◄── SIP/TLS ──►│               │               │               │
     │                  │  (порт 5061)   │               │               │               │
     │                  │               │◄── SIP/UDP ──►│               │               │
     │                  │               │  (порт 5060)   │               │               │
     │                  │               │               │◄── SIP/UDP ──►│               │
     │                  │               │               │  (порт 5060)   │               │
     │                  │               │               │               │               │
     │◄──DTLS-SRTP──────┼──────────────►│               │               │               │
     │  голос            │               │RTPEngine      │               │               │
     │  шифрованный      │◄── RTP ──────►│ SRTP↔RTP      │               │               │
     │  (порты           │  голос         │ транскод.     │               │               │
     │   10000-20000)    │  (порты        │               │◄── RTP ──────►│               │
     │                  │   10000-20000) │               │  (внутренний)  │               │
     │                  │               │               │               │               │
     │◄──ICE/STUN───────┼───────────────┼──►STUN-сервер  │               │               │
     │  NAT traversal    │               │               │               │               │
     │                  │               │               │               │◄── ESL ──────►│
     │                  │               │               │               │  (порт 8021)   │
     │                  │               │               │               │  TCP, текст    │
     │                  │               │               │               │               │
     │                  │               │               │               │── HTTP ──────►│
     │                  │               │               │               │  JSON CDR      │
     │                  │               │               │               │  (порт 8080)   │
     │                  │               │               │               │               │
     │◄──────────── HTTPS / WSS ────────────────────────────────────────────────────────►│
     │  REST API, WebSocket (дашборд, управление)                       │  (порт 443)    │
Три уровня протоколов телефонии
  ┌───────────────────────────────────────────────────────────────┐
  │  УРОВЕНЬ 1: СИГНАЛИЗАЦИЯ (кто кому звонит, куда направить)   │
  │                                                               │
  │  SIP ─── установка/завершение звонка                          │
  │  SDP ─── «мой IP для голоса: X, мои кодеки: Y»               │
  │  OСОБЕННОСТЬ: сигнализация и голос идут РАЗНЫМИ путями!       │
  └───────────────────────────────────────────────────────────────┘

  ┌───────────────────────────────────────────────────────────────┐
  │  УРОВЕНЬ 2: МЕДИА (сам голос / видео)                        │
  │                                                               │
  │  RTP ──── голосовые пакеты (каждые 20мс = 50 пакетов/сек)    │
  │  RTCP ─── «качество связи: потери 2%, jitter 15мс»           │
  │  SRTP ─── зашифрованный RTP (TLS для голоса)                  │
  │  DTLS ─── обмен ключами для SRTP (WebRTC)                    │
  └───────────────────────────────────────────────────────────────┘

  ┌───────────────────────────────────────────────────────────────┐
  │  УРОВЕНЬ 3: ТРАНСПОРТ И ВСПОМОГАТЕЛЬНЫЕ                      │
  │                                                               │
  │  TLS ──── шифрование SIP (SIP/TLS = SIPS, порт 5061)         │
  │  WSS ──── SIP поверх WebSocket (для браузеров)                │
  │  STUN ─── «мой публичный IP = X» (NAT discovery)             │
  │  TURN ─── relay через сервер (когда STUN не помог)            │
  │  ICE ──── комбинация STUN+TURN для надёжного соединения       │
  │  DNS ──── SRV-записи для нахождения SIP-серверов              │
  └───────────────────────────────────────────────────────────────┘
SIP Session Initiation Protocol — главный протокол телефонии

Что делает: устанавливает, изменяет и завершает звонки. Это «управляющий» протокол — он НЕ передаёт голос, а только договаривается о звонке.

Аналогия: SIP — это когда вы набираете номер на телефоне и слышите гудки. Сам разговор — это уже RTP.

  Телефон A                 FreeSWITCH                Телефон B

      │── INVITE ──────────►│                           │
      │   «Хочу позвонить    │── INVITE ────────────────►│
      │    на 1002»           │   «Звонок для тебя»       │
      │                       │                           │
      │◄── 100 Trying ──────│                           │
      │   «Обрабатываю»       │◄── 180 Ringing ──────────│
      │◄── 180 Ringing ─────│   «Звоню»                 │
      │   «Звонит»            │                           │
      │                       │◄── 200 OK ────────────────│
      │◄── 200 OK ───────────│   «Поднял трубку»         │
      │   «Ответил!»          │                           │
      │── ACK ───────────────►│── ACK ──────────────────►│
      │   «Подтверждаю»       │                           │
      │                       │                           │
      │◄═══════════ RTP (голос) ═══════════════════════════►│
      │           разговор идёт...                        │
      │                       │                           │
      │── BYE ───────────────►│── BYE ──────────────────►│
      │   «Кладу трубку»      │                           │
      │◄── 200 OK ───────────│◄── 200 OK ────────────────│
SIP-методЧто делаетКогда
INVITEНачинает звонок или изменяет параметры (re-INVITE)Набор номера, hold, перевод
ACKПодтверждает 200 OKЗавершение handshake
BYEЗавершает звонокКто-то повесил трубку
REGISTER«Я телефон 1001, я по адресу 192.168.1.50»При включении телефона, каждые 60 сек
OPTIONS«Ты живой?» — проверка доступностиKeep-alive, мониторинг
CANCELОтменяет INVITE до ответаЗвонящий повесил трубку пока звонит
REFERПеревод звонка на другой номерAttended/blind transfer
SUBSCRIBE/NOTIFYПодписка на события (BLF, presence)Лампочки занятости на телефоне
INFOПередача данных в диалоге (DTMF)SIP INFO метод для DTMF

Транспорт SIP:

ТранспортПортКогда
UDP5060Внутренняя сеть (быстро, без overhead). Между Kamailio и FreeSWITCH
TCP5060Большие SIP-сообщения (>MTU), надёжная доставка
TLS5061Шифрование. SIP-телефоны через интернет к SBC
WSS443WebSocket Secure. Браузеры (WebRTC) к SBC
SDP Session Description Protocol — «визитная карточка» медиа

Что делает: передаётся внутри SIP-сообщений (INVITE и 200 OK). Говорит: «Мой IP для голоса — такой-то, мои кодеки — такие-то, слушаю на порту таком-то».

Аналогия: SIP — это «Давай созвонимся». SDP — это «Вот мой номер и я говорю по-русски и по-английски».

;; SDP внутри SIP INVITE — «вот что я умею» v=0 o=- 123456 1 IN IP4 10.0.1.50 ← мой IP s=call c=IN IP4 10.0.1.50 ← куда слать голос t=0 0 m=audio 20000 RTP/AVP 111 0 8 ← порт 20000, кодеки 111, 0, 8 a=rtpmap:111 opus/48000/2 ← кодек 111 = Opus a=rtpmap:0 PCMU/8000 ← кодек 0 = G.711 μ-law a=rtpmap:8 PCMA/8000 ← кодек 8 = G.711 A-law a=sendrecv ← и отправляю, и принимаю

Второй участник отвечает своим SDP в 200 OK — выбирает кодек из предложенных. После этого оба знают куда и как слать голос.

RTP / SRTP / RTCP — передача голоса

RTP (Real-time Transport Protocol)

Собственно голос. Аудио нарезается на пакеты по 20мс (50 пакетов/сек) и отправляется по UDP. Каждый пакет — ~160 байт (G.711).

Аналогия: если SIP — это «Давай поговорим», то RTP — это сам разговор по телефону.

Порты: 10000–20000 (UDP). Каждый звонок занимает 2 порта (чётный для RTP, нечётный для RTCP).

Ключевой момент:

SIP и RTP идут разными путями! SIP — через Kamailio (сигнализация). RTP — через RTPEngine или напрямую (медиа). Это создаёт проблемы с NAT и файрволами.

  A ──SIP──► Kamailio ──SIP──► B
  │                              │
  A ──RTP──► RTPEngine ──RTP──► B
     (разные IP и порты!)
ПротоколЧтоШифрованиеКогда
RTPГолос / видеоНетВнутренняя сеть (доверенная)
SRTPЗашифрованный RTPAES-128Между SBC и клиентами, между SBC и провайдерами
DTLS-SRTPSRTP с обменом ключами по DTLSAES-128 + DTLS handshakeWebRTC (обязательно) — браузер отказывается без шифрования
RTCPСтатистика качества (потери, jitter, RTT)Параллельно с RTP, для мониторинга
DTMF — передача нажатий клавиш

Когда в IVR нажимаете «1» — как эта цифра попадает на сервер? Три способа:

МетодКакПлюсыМинусы
RFC 2833 Рекомендуем Специальные RTP-пакеты с типом 101 (telephone-event). Цифра передаётся внутри RTP-потока, но отдельно от голоса Надёжный, не зависит от кодека, не искажается транскодированием
SIP INFO SIP-сообщение INFO с телом Signal=1, Duration=250. Идёт через сигнализацию (SIP), не через медиа (RTP) Работает когда RTP недоступен Задержки (SIP медленнее RTP), не все устройства поддерживают
In-band Звук тонального сигнала (бип) кодируется прямо в голосовой поток Работает с любым устройством Искажается при сжатии (G.729). Ненадёжный
WebRTC — стек протоколов для браузера

WebRTC — это не один протокол, а стек из 5-6 протоколов:

  ┌──────────────────────────────────────────┐
  │              БРАУЗЕР                      │
  │                                          │
  │  ┌──────────────────────────────┐        │
  │  │ SIP.js / JsSIP                │        │
  │  │ SIP-клиент в JavaScript      │        │
  │  └──────────┬───────────────────┘        │
  │             │                             │
  │  ┌──────────▼───────────────────┐        │
  │  │ WebSocket (WSS)               │        │
  │  │ SIP-сообщения через WS       │        │
  │  │ порт 443, TLS обязателен     │        │
  │  └──────────┬───────────────────┘        │
  │             │                             │
  │  ┌──────────▼───────────────────┐        │
  │  │ ICE (Interactive Connectivity │        │
  │  │      Establishment)          │        │
  │  │ Пробует все варианты связи:  │        │
  │  │ 1. Напрямую (host candidate) │        │
  │  │ 2. Через NAT (STUN)         │        │
  │  │ 3. Через relay (TURN)       │        │
  │  └──────────┬───────────────────┘        │
  │             │                             │
  │  ┌──────────▼───────────────────┐        │
  │  │ DTLS (Datagram TLS)           │        │
  │  │ Обмен ключами шифрования    │        │
  │  │ (как TLS, но для UDP)        │        │
  │  └──────────┬───────────────────┘        │
  │             │                             │
  │  ┌──────────▼───────────────────┐        │
  │  │ SRTP                          │        │
  │  │ Зашифрованный голос / видео  │        │
  │  │ AES-128                      │        │
  │  └──────────────────────────────┘        │
  └──────────────────────────────────────────┘
STUN / TURN / ICE — пробиваем NAT

Большинство устройств находятся за NAT (роутером). Их внутренний IP (192.168.x.x) не виден из интернета. Как доставить голос?

ПротоколЧто делаетАналогия
STUN Браузер спрашивает STUN-сервер: «Какой у меня публичный IP?» Сервер отвечает: «203.0.113.55:12345». Теперь браузер знает свой внешний адрес и сообщает его в SDP Спросить друга: «Какой у меня номер на определителе?»
TURN Если STUN не помог (симметричный NAT, корпоративный файрвол) — TURN-сервер становится relay: весь голос идёт через него. Работает всегда, но дороже (трафик через сервер) Звонить через посредника, который передаёт слова
ICE Комбинирует STUN и TURN. Пробует все варианты связи и выбирает лучший: напрямую → через STUN → через TURN Попробовать позвонить напрямую, если не получилось — через оператора
ESL / NATS / HEP — внутренние протоколы стека

Кроме стандартных телефонных протоколов, в нашем стеке есть протоколы для связи между компонентами:

ПротоколМежду кемЗачемПорт
ESL (Event Socket) Go API ↔ FreeSWITCH Управление звонками из Go: originate, transfer, hold, события 8021 / TCP
NATS Go API ↔ другие сервисы Шина событий: «звонок завершён», «агент освободился», «новый CDR» 4222 / TCP
HEP (Homer Encapsulation) Kamailio / FS → Homer Копирование SIP-пакетов для мониторинга и troubleshooting. Homer показывает call-flow диаграммы 9060 / UDP
DMQ (Kamailio Dialog) Kamailio ↔ Kamailio Синхронизация состояния между нодами кластера (регистрации, диалоги) 5062 / UDP
📚 Полная сводная таблица: все протоколы в одном месте
ПротоколУровеньТранспортПорт(ы)ЗачемГде у нас
SIPСигнализацияUDP/TCP5060Установка/завершение звонковВезде
SIP/TLSСигнализацияTLS5061Шифрованный SIPSBC ↔ телефоны
SIP/WSSСигнализацияWebSocket+TLS443SIP из браузераSBC ↔ браузер
SDPСигнализациявнутри SIPОписание медиа-параметровВ каждом INVITE/200 OK
RTPМедиаUDP10000-20000Голос (незашифрованный)Внутренняя сеть
SRTPМедиаUDP10000-20000Голос (зашифрованный)SBC ↔ внешний мир
DTLS-SRTPМедиаUDP10000-20000Голос WebRTC (обязательно шифрованный)RTPEngine ↔ браузер
RTCPМедиаUDPRTP+1Статистика качестваПараллельно RTP
DTMF (RFC 2833)Медиавнутри RTPНажатия клавиш в IVRВ RTP-потоке
STUNNATUDP3478Определение публичного IPБраузер → STUN-сервер
TURNNATUDP/TCP3478Relay медиа через серверБраузер → TURN-сервер
ICENATФреймворк: STUN + TURN + выбор лучшего путиWebRTC
ESLВнутреннийTCP8021Go управляет FreeSWITCHGo API ↔ FS
NATSВнутреннийTCP4222Шина событий между сервисамиGo API ↔ сервисы
HEPМониторингUDP9060Копирование SIP для HomerKamailio/FS → Homer
DMQКластерUDP5062Синхронизация Kamailio-нодKamailio ↔ Kamailio
HTTP/RESTAPITCP+TLS443/8080REST API, CDR, конфигурацияReact ↔ Go API ↔ FS
WebSocketAPITCP+TLS443Реалтайм дашборды, уведомленияReact ↔ Go API
Один звонок = 8 протоколов одновременно

Когда оператор в браузере звонит клиенту на мобильный через PSTN:

  Браузер оператора                     Наш стек                    Мобильный клиента

  1. WSS          SIP INVITE ──────►  Access SBC (Kamailio)
  2. SIP/UDP                        ──►  Kamailio Core
  3. SIP/UDP                        ──►  FreeSWITCH
  4. ESL           Go получает event ◄──  FreeSWITCH
  5. SIP/UDP      FS dial out       ──►  Kamailio Core
  6. SIP/UDP                        ──►  Trunk SBC
  7. SIP/TLS                        ──►  SIP-провайдер  ──► PSTN ──► Мобильный

  Медиа (голос):
  8. DTLS-SRTP    браузер          ──►  RTPEngine (Access)
  9. RTP          RTPEngine        ──►  FreeSWITCH (транскод Opus→G.711)
 10. RTP          FreeSWITCH       ──►  RTPEngine (Trunk)
 11. RTP          RTPEngine        ──►  SIP-провайдер  ──► PSTN ──► Мобильный

  Параллельно:
 12. HEP          SIP-пакеты       ──►  Homer (мониторинг)
 13. NATS         событие          ──►  Dashboard (React)
 14. RTCP         статистика       ──►  Prometheus → Grafana

14 протоколов задействованы в одном звонке. Вот почему телефония сложнее веб-разработки.

02

Каталог функций

Полный каталог всех функций современной телефонной системы — 134 функции в 16 категориях. Каждая функция с описанием и техническим объяснением работы.

📞 Управление вызовами (Call Control) 9
Функция Описание Как это работает
Входящий вызов Приём звонка извне SIP INVITE → обработка диалпланом
Исходящий вызов Звонок наружу Выбор транка по LCR, SIP INVITE наружу
Внутренний вызов Звонок между сотрудниками Маршрутизация через location table
Удержание (Hold) Пауза, абонент слышит музыку re-INVITE с a=sendonly, MOH stream
Возобновление (Resume) Снять с удержания re-INVITE с a=sendrecv
Завершение (Hangup) Положить трубку SIP BYE → разрыв RTP
Повторный набор (Redial) На последний номер Клиентская функция, хранит историю
Быстрый набор (Speed Dial) Звонок по короткому коду Диалплан: *1 → translate → INVITE
Горячая линия (Hotline) Снял трубку = автозвонок Автоматический INVITE при off-hook
🔁 Переводы и переадресации (Transfer & Forwarding) 9
Функция Описание Как это работает
Слепой перевод (Blind Transfer) Перевёл — отключился SIP REFER → новый INVITE
Перевод с консультацией (Attended Transfer) Поговорил → перевёл Два вызова + REFER с Replaces
Переадресация всегда (Forward Always) Все звонки на другой номер Диалплан проверяет настройку до звонка
По занятости (Forward Busy) Если занят — переведи Проверка статуса 486 Busy → forward
По неответу (Forward No Answer) Не ответил за N сек Таймер → при timeout → forward
По недоступности (Forward Unreachable) Телефон выключен Нет регистрации → forward
Follow Me Звонит по очереди на разные устройства Последовательный набор с таймаутами
Simultaneous Ring Звонит везде сразу Параллельный fork INVITE на все контакты
DISA Звонок снаружи → внутренний гудок IVR с аутентификацией → internal dialplan
👥 Многосторонние вызовы (Multi-party) 7
Функция Описание Как это работает
Трёхсторонний вызов Добавить третьего Conference bridge с 3 участниками
Конференц-комната Постоянная комната с PIN mod_conference, номер + PIN вход
Динамическая конференция Создаётся через API ESL conference create
Модератор конференции mute/unmute/kick Управление через ESL или DTMF-коды
Видеоконференция Видео + аудио + экран mod_conference с видео-кодеками
Intercom / Пейджинг Сообщение на группу Multicast RTP или sequential paging
Двусторонний Intercom Автоответ на громкую Alert-Info: Auto Answer header
🎧 IVR — голосовое меню 8
Функция Описание Как это работает
Одноуровневое IVR Простое меню Lua/JS скрипт + playAndGetDigits
Многоуровневое IVR Вложенные меню Дерево скриптов с переходами
DTMF-ввод Распознавание нажатий RFC 2833 events → digit collection
ASR (распознавание речи) Голосовой ввод mod_unimrcp → внешний ASR (Vosk/Whisper)
TTS (синтез речи) Текст → голос mod_tts → внешний TTS (Piper/Google)
Набор по имени (Directory) Поиск по фамилии DTMF → T9 mapping → search → transfer
Динамический IVR Меню из БД/CRM Lua/JS HTTP-запрос → построение меню
Callback из IVR «Нажмите 1, перезвоним» Сохранение номера → originate позже
📋 Очереди (ACD) 13
Функция Описание Как это работает
Ring All Звонят все операторы Параллельный bridge ко всем агентам
Round Robin По очереди Циклический выбор следующего агента
Longest Idle Кто дольше свободен Трекинг времени последнего вызова
Fewest Calls У кого меньше звонков Счётчик вызовов за период
Skill-based Routing По навыкам Сопоставление skills вызова и агента
Priority Queue VIP в начало Приоритет из CRM/CallerID → сортировка
Позиция в очереди «Вы 3-й» Счётчик + TTS/запись
Время ожидания «Ожидайте 2 мин» Среднее время = total_wait / answered
Периодические объявления Каждые 30 сек Таймер → playback в очереди
MOH (музыка ожидания) Музыка/реклама mod_local_stream → аудиопоток
Overflow Ждёт > 5 мин → другая группа Таймер/порог → transfer
Callback Перезвоним не теряя место Сохранить позицию → originate при освобождении
Wrap-up Time Пауза после звонка Таймер Not Ready → авто Ready
📩 Голосовая почта (Voicemail) 9
Функция Описание Как это работает
Личный ящик У каждого свой mod_voicemail, box per extension
Приветствие Личное голосовое Запись через *97, файл в storage
Разные приветствия По ситуации busy/unavail/temp greetings
Voicemail-to-Email Сообщение на почту Email с MP3-вложением
Визуальная голосовая почта Список в UI API → список сообщений → плеер
Общий ящик Групповой для отдела Shared mailbox, несколько подписчиков
PIN-доступ Прослушивание по телефону DTMF PIN → menu → playback
Транскрипция Голос → текст ASR → текст в email/UI
Автоудаление Удаление через N дней Cron job / scheduled cleanup
🔌 Маршрутизация (Routing & Dialplan) 12
Функция Описание Как это работает
Внутренние номера (Extensions) 100-199 Dialplan pattern matching
DID (прямые номера) Внешний → внутренний DID → extension mapping в БД
DNIS По какому номеру позвонили SIP To header → routing decision
CallerID / АОН Определение номера SIP From header → display
CallerID подмена Исходящий показывает номер компании effective_caller_id_number
Маршрутизация по времени День/ночь Time conditions в dialplan
По CallerID Регион/VIP Prefix matching → route
По географии Международный → дешёвый транк GeoIP / prefix table
LCR (Least Cost Routing) Самый дешёвый маршрут Rate table → sort by cost → try
Failover Основной упал → запасной Sequential try → next on failure
Чёрный список (Blacklist) Блокировка номеров Lookup в БД/Redis → reject
Белый список (Whitelist) Только разрешённые Lookup → allow or reject
👁 Супервизор и контроль (Supervisor) 7
Функция Описание Как это работает
Прослушивание (Spy) Слышит разговор eavesdrop UUID, mute flag
Подсказка (Whisper) Говорит оператору eavesdrop с whisper flag
Вмешательство (Barge-In) Входит в разговор three_way_bridge
Перехват (Call Pickup) Ответить за коллегу intercept UUID
Групповой перехват Внутри отдела Pickup group → intercept first ringing
Принудительная запись Включить запись uuid_record start
Оценка звонка (Call Scoring) Оценка оператора Метаданные в CDR → отчёты
📊 Запись и аналитика (Recording & Analytics) 10
Функция Описание Как это работает
Запись всех вызовов Автоматическая uuid_record on CHANNEL_ANSWER
Выборочная запись Определённые направления Условия в dialplan → record
Запись по требованию Кнопка API → uuid_record start/stop
CDR (журнал вызовов) Полный лог mod_cdr_csv / pg_cdr → PostgreSQL
Живые дашборды Реалтайм статистика ESL events → NATS → WebSocket → React
Исторические отчёты За период SQL-агрегация CDR → графики
SLA-мониторинг % отвеченных за N сек CDR analysis: answer_time < threshold
Речевая аналитика AI-анализ Whisper STT → LLM → тональность, темы
Скрининг (Screen Recording) Запись экрана Отдельный агент на рабочей станции
Heatmap нагрузки Пиковые часы CDR group by hour → visualization
🔴 Статусы и Presence 5
Функция Описание Как это работает
BLF (Busy Lamp Field) Лампочки на телефоне SIP SUBSCRIBE/NOTIFY dialog
Presence Статус пользователя SIP PUBLISH → presence server
DND (Do Not Disturb) Не беспокоить Флаг в user settings → reject calls
Автостатус Календарь → DND Calendar API polling → update status
Статус оператора Ready/Not Ready Agent state machine в mod_callcenter
📱 Автообзвон (Dialer) 8
Функция Описание Как это работает
Preview Dialer Оператор видит карточку Показать данные → ждать клик → originate
Progressive Dialer Автоматический набор Agent free → originate next number
Predictive Dialer Предиктивный Алгоритм: abandon rate + avg talk time → pace
Power Dialer N номеров сразу Multi-originate → first answer → bridge
Автоинформатор Робот с сообщением Originate → playback → hangup
IVR-обзвон Робот с меню Originate → IVR script → collect response
SMS-рассылка Массовые SMS HTTP API к SMS-шлюзу
Кампании Управление списками CRUD кампаний, schedule, retry logic
🔗 Интеграции (Integrations) 9
Функция Описание Как это работает
CRM-интеграция Карточка клиента Webhook on call → CRM API → screen pop
Click-to-Call Клик по номеру POST /calls → ESL originate
Webhook / API Событие → HTTP Event → NATS → webhook dispatcher
LDAP/AD Импорт сотрудников LDAP sync → create extensions
Календарь Google/Outlook Calendar API → time conditions, auto-DND
Helpdesk Автосоздание тикета Webhook → Zendesk/Jira API
Мессенджеры Omnichannel Telegram/WhatsApp bot → unified queue
AI-ассистент Бот на типовые вопросы STT → LLM → TTS → response or transfer
Скрипты разговоров Сценарий оператору Web UI с динамическим скриптом по этапам
🌐 WebRTC и мобильность 6
Функция Описание Как это работает
Web-телефон Звонки из браузера SIP.js + WebSocket + WebRTC
Мобильный клиент iOS/Android SIP-клиент + push notifications
Softphone Программный телефон на ПК SIP UA (общие открытые варианты)
Один номер везде Единый на всех устройствах Multiple registrations per AOR
Переключение устройства Без прерывания REFER + Replaces → move call
Push-уведомления Входящий → push SIP → push service → wake app → REGISTER → INVITE
🔒 Безопасность (Security) 9
Функция Описание Как это работает
TLS Шифрование сигнализации mod_tls / Kamailio tls module
SRTP Шифрование голоса Crypto suite negotiation в SDP
Fail2ban / Антифрод Блокировка подбора Kamailio pike + fail2ban integration
ACL Разрешённые IP Kamailio permissions module
Fraud Detection Подозрительные звонки Rules: international burst, off-hours, new dest
Лимиты Макс. одновременных / расход dialog module counter + budget check
Аудит-лог Кто что менял API middleware → log to DB
2FA Двухфакторная в панель TOTP / email OTP для web login
SRTP обязательный Запрет незашифрованных crypto: mandatory в SIP profile
🛡 Отказоустойчивость (High Availability) 6
Функция Описание Как это работает
HA (Active/Standby) Основной + запасной Keepalived + shared IP (VIP)
Кластеризация Несколько серверов как один FreeSWITCH mod_sofia profiles per node
Балансировка нагрузки Распределение Kamailio dispatcher module
Geo-redundancy Разные дата-центры DMQ sync + DNS SRV records
Автомасштабирование Облачное Kubernetes + metrics-based HPA
Резервное копирование Бэкапы pg_dump + rsync recordings + cron
Администрирование (Administration) 7
Функция Описание Как это работает
Web-панель GUI настройки React SPA + REST API
Мультитенантность Несколько компаний tenant_id в каждой таблице, domain routing
Роли и права admin/supervisor/user RBAC в API middleware
Провижининг Автонастройка телефонов HTTP provisioning server + MAC-based config
Шаблоны Типовые профили Template → apply to user → create extension+settings
Bulk-операции Массовые действия CSV import → batch create
REST API Полное управление OpenAPI spec, JWT auth, rate limiting
03

Архитектура системы

Общая архитектура, слои системы, потоки данных и разделение сигнализации и медиа.

Общая схема

Enterprise-архитектура: 43 страны, carrier-grade, миграция на FreeSWITCH

Архитектура для крупной компании с присутствием в 43 странах. Гео-распределённая система с полной отказоустойчивостью, SBC на границе каждого региона, локальный breakout трафика (вызовы остаются в регионе), централизованное управление и мониторинг. Каждый регион автономен — при потере связи между регионами локальные вызовы продолжают работать.

═══════════════════════════════════════════════════════════════════════════════════════
                     ГЛОБАЛЬНАЯ АРХИТЕКТУРА — 43 СТРАНЫ, 4 РЕГИОНА
═══════════════════════════════════════════════════════════════════════════════════════

                              ┌─────────────────────────────┐
                              │      КЛИЕНТЫ (43 страны)    │
                              │  WebPhone   IP-телефоны     │
                              │  Мобильные  Софтфоны        │
                              │  Web UI     Contact Center  │
                              └──────────────┬──────────────┘GeoDNS / Anycast
                               (маршрутизация к ближайшему региону)
                                             │
              ┌──────────────────────────────┬┴┬──────────────────────────────┐
              ▼                              ▼                                ▼
┌──────────────────────┐  ┌──────────────────────┐  ┌──────────────────────┐
│  РЕГИОН: EUROPE      │  │  РЕГИОН: AMERICAS    │  │  РЕГИОН: ASIA-PAC    │  + MIDDLE EAST
│  Frankfurt (Primary) │  │  US-East (Primary)   │  │  Singapore (Primary) │    Dubai
│  Amsterdam (Standby) │  │  US-West (Standby)   │  │  Tokyo (Standby)     │
│  ~20 стран           │  │  ~10 стран           │  │  ~8 стран            │    ~5 стран
└──────────┬───────────┘  └──────────┬───────────┘  └──────────┬───────────┘
           │                           │                           │
           └───────────────────────────┼───────────────────────────┘
                                       │
                          Межрегиональная связь
                     (WireGuard VPN mesh / выделенные каналы)
                                       │
                              ┌────────┴────────┐
                              ▼                  ▼
                   ┌──────────────────┐  ┌──────────────────┐
                   │  CENTRAL MGMT    │  │  GLOBAL MONITOR  │
                   │  Go API Master   │  │  Homer + Grafana │
                   │  PostgreSQL (HA)  │  │  Prometheus      │
                   │  Admin Panel     │  │  Loki (logs)     │
                   └──────────────────┘  └──────────────────┘
Рис. 5a — Глобальная архитектура: 4 региона, GeoDNS-маршрутизация, централизованное управление
Детальная архитектура одного региона

Каждый регион — это полностью автономный кластер со всеми компонентами. Ниже — детальная схема одного региона (Europe / Frankfurt):

═══════════════════════════════════════════════════════════════════════════════════════
              РЕГИОН EUROPE (Frankfurt) — детальная архитектура
═══════════════════════════════════════════════════════════════════════════════════════

                 КЛИЕНТЫ РЕГИОНА
   ┌───────────┬────────────┬──────────────┬───────────────┐
   │ Web UIWebPhoneIP-телефоныМобильные     │
   │ (React)   │ (SIP.js)   │ Yealink,Snom │ iOS/Android   │
   └─────┬─────┴─────┬──────┴───────┬──────┴──────┬────────┘
         │HTTPSWSSSIP/TLSSIP/TLS
         ▼            ▼              ▼              ▼
   ┌─────────────────────────────────────────────────────────┐
   │  СЛОЙ 1: EDGE / LOAD BALANCER                          │
   │  ┌─────────────────┐  ┌─────────────────┐              │
   │  │ HAProxy / Nginx │  │ HAProxy / Nginx │  (Active/Active)│
   │  │  HTTPS, WSS     │  │  HTTPS, WSS     │              │
   │  │  TLS termination│  │  TLS termination│              │
   │  └────────┬────────┘  └────────┬────────┘              │
   │           └──────────┬─────────┘                        │
   └──────────────────────┼──────────────────────────────────┘
                          │
   ┌──────────────────────┼──────────────────────────────────┐
   │  СЛОЙ 2: SBC (Session Border Controller)                 │
   │                      │                                   │
   │  ┌───────────────────▼───────────────────┐              │
   │  │  Kamailio SBC Cluster (Active/Active)  │              │
   │  │                                        │              │
   │  │  ┌────────────┐    ┌────────────┐      │              │
   │  │  │ KAM-SBC-1  │    │ KAM-SBC-2  │      │   Keepalived  │
   │  │  │ Внешний    │    │ Внешний    │      │   Floating   │
   │  │  │ интерфейс  │    │ интерфейс  │      │   VIP        │
   │  │  └──────┬─────┘    └──────┬─────┘      │              │
   │  │         └────────┬────────┘             │              │
   │  │                  │                      │              │
   │  │  Функции SBC:                           │              │
   │  │  • Topology hiding (скрытие топологии) │              │
   │  │  • SIP normalization (нормализация)    │              │
   │  │  • Anti-fraud / rate limiting           │              │
   │  │  • TLS termination для SIP              │              │
   │  │  • Транк-аутентификация (IP ACL)        │              │
   │  │  • DDoS protection (pike + htable)      │              │
   │  │  • Протокол-конвертация (WSS↔UDP)       │              │
   │  └────────────────────┬──────────────────-┘              │
   │  ┌────────────────────▼───────────────────┐              │
   │  │  RTPEngine Cluster                      │              │
   │  │  ┌────────────┐    ┌────────────┐      │              │
   │  │  │ RTPE-1     │    │ RTPE-2     │      │              │
   │  │  │ NAT trav.  │    │ NAT trav.  │      │              │
   │  │  │ SRTP↔RTP   │    │ SRTP↔RTP   │      │              │
   │  │  │ WebRTC↔SIP │    │ WebRTC↔SIP │      │              │
   │  │  └────────────┘    └────────────┘      │              │
   │  └────────────────────────────────────────┘              │
   └──────────────────────┬──────────────────────────────────┘
                          │ SIP (внутренний, доверенный)
   ┌──────────────────────┼──────────────────────────────────┐
   │  СЛОЙ 3: CORE SIP PROXY                                   │
   │                      │                                   │
   │  ┌───────────────────▼───────────────────┐              │
   │  │  Kamailio Core Cluster (Active/Active) │              │
   │  │  ┌────────────┐    ┌────────────┐      │              │
   │  │  │ KAM-CORE-1 │    │ KAM-CORE-2 │      │  DMQ sync    │
   │  │  │ Регистрация│    │ Регистрация│      │  между       │
   │  │  │ Маршрутиз. │    │ Маршрутиз. │      │  нодами      │
   │  │  │ LB → FS    │    │ LB → FS    │      │              │
   │  │  └──────┬─────┘    └──────┬─────┘      │              │
   │  │         └────────┬────────┘             │              │
   │  └──────────────────┼─────────────────────┘              │
   │                     │                                     │
   │  Функции Core:      │                                     │
   │  • User registration (usrloc)                            │
   │  • Internal routing (диалплан, DID)                      │
   │  • Load balancing → FreeSWITCH (dispatcher)              │
   │  • Failover между FS-нодами                              │
   │  • Dialog tracking                                       │
   └─────────────────────┼────────────────────────────────────┘
                         │
   ┌─────────────────────┼────────────────────────────────────┐
   │  СЛОЙ 4: MEDIA SERVERS (FreeSWITCH Cluster)              │
   │                     │                                     │
   │  ┌──────────────────▼──────────────────────────────┐     │
   │  │                                                  │     │
   │  │  ┌──────────┐  ┌──────────┐  ┌──────────┐      │     │
   │  │  │ FS-1     │  │ FS-2     │  │ FS-N     │      │     │
   │  │  │ IVR      │  │ IVR      │  │ IVR      │      │     │
   │  │  │ Queues   │  │ Queues   │  │ Queues   │      │     │
   │  │  │ Conf     │  │ Conf     │  │ Conf     │      │     │
   │  │  │ Record   │  │ Record   │  │ Record   │      │     │
   │  │  │ Bridge   │  │ Bridge   │  │ Bridge   │      │     │
   │  │  └──────────┘  └──────────┘  └──────────┘      │     │
   │  │  (горизонтальное масштабирование по нагрузке)   │     │
   │  └─────────────────────────────────────────────────┘     │
   └──────────┬──────────────┬──────────────┬─────────────────┘
              │ESLCDRrecordings
              ▼              ▼              ▼
   ┌──────────────────────────────────────────────────────────┐
   │  СЛОЙ 5: APPLICATION & API                                │
   │                                                           │
   │  ┌─────────────┐  ┌─────────────┐  ┌──────────────┐     │
   │  │ Go API-1    │  │ Go API-2    │  │ Go API-N     │     │
   │  │ REST/WS     │  │ REST/WS     │  │ REST/WS      │     │
   │  │ ESL client  │  │ ESL client  │  │ ESL client   │     │
   │  │ CDR writer  │  │ Dialer      │  │ Webhooks     │     │
   │  └─────────────┘  └─────────────┘  └──────────────┘     │
   └──────────┬──────────────┬──────────────┬─────────────────┘
              │              │              │
   ┌──────────┼──────────────┼──────────────┼─────────────────┐
   │  СЛОЙ 6: DATA & EVENTS  │              │                  │
   │          │              │              │                  │
   │  ┌───────▼─────┐ ┌─────▼─────┐ ┌─────▼──────┐          │
   │  │PostgreSQL   │ │  Redis    │ │  NATS     │          │
   │  │Primary +    │ │ Sentinel  │ │ Cluster    │          │
   │  │Replica (HA) │ │ Cluster   │ │ (JetStream)│          │
   │  └─────────────┘ └───────────┘ └────────────┘          │
   │                                                          │
   │  ┌─────────────┐ ┌───────────────┐                      │
   │  │ MinIO (S3)  │ │ Homer + Grafana│                      │
   │  │ Recordings  │ │ SIP capture   │                      │
   │  │ Voicemail   │ │ Prometheus    │                      │
   │  │ IVR audio   │ │ Loki (logs)   │                      │
   │  └─────────────┘ └───────────────┘                      │
   └──────────────────────────────────────────────────────────┘
              │
              │ SIP (от Core к транкам)
              │
   ┌──────────┼───────────────────────────────────────────────┐
   │  СЛОЙ 7: TRUNK SBC (Session Border Controller — PSTN)    │
   │          │                                                │
   │  ┌───────▼──────────────────────────────────────┐        │
   │  │  Kamailio Trunk SBC (Active/Active)          │        │
   │  │                                               │        │
   │  │  ┌─────────────┐    ┌─────────────┐          │        │
   │  │  │ KAM-TRUNK-1 │    │ KAM-TRUNK-2 │          │Keepalived│
   │  │  │ SIP-facing  │    │ SIP-facing  │          │Floating │
   │  │  │ к провайдерам│    │ к провайдерам│          │VIP      │
   │  │  └──────┬──────┘    └──────┬──────┘          │        │
   │  │         └────────┬────────┘                   │        │
   │  │                  │                            │        │
   │  │  Функции Trunk SBC:                           │        │
   │  │  • SIP-нормализация (разные провайдеры = разный SIP) │
   │  │  • Транк-аутентификация (IP ACL + credentials)      │
   │  │  • E.164 нормализация номеров                       │
   │  │  • LCR (Least Cost Routing) между транками          │
   │  │  • Failover: primary → backup провайдер             │
   │  │  • Topology hiding от провайдеров                   │
   │  │  • Anti-fraud / toll fraud protection               │
   │  │  • Кодек-переговоры (Offer/Answer) с провайдерами   │
   │  │  • Caller ID manipulation / CNAM                    │
   │  └────────────────────┬─────────────────────────┘        │
   │                       │                                   │
   │  ┌────────────────────▼───────────────────┐              │
   │  │  RTPEngine Trunk (опционально)         │              │
   │  │  ┌────────────┐    ┌────────────┐      │              │
   │  │  │ RTPE-T1    │    │ RTPE-T2    │      │              │
   │  │  │ Кодек      │    │ Кодек      │      │              │
   │  │  │ транскод.  │    │ транскод.  │      │              │
   │  │  │ G.711↔G.729│    │ G.711↔G.729│      │              │
   │  │  └────────────┘    └────────────┘      │              │
   │  │  (для провайдеров с жёсткими кодек-    │              │
   │  │   требованиями или RTP-пиннингом)      │              │
   │  └────────────────────────────────────────┘              │
   └──────────────────────┬────────────────────────────────────┘
                          │
   ┌──────────────────────┼───────────────────────────────────┐
   │  СЛОЙ 8: PSTN / ВНЕШНИЙ МИР                                │
   │                      │                                    │
   │  ┌───────────────────▼───────────────────────────────┐   │
   │  │  SIP-транки (локальные для региона)                  │   │
   │  │                                                    │   │
   │  │  ┌──────────┐ ┌──────────┐ ┌──────────────┐      │   │
   │  │  │ Telnyx   │ │ Twilio   │ │ Локальный    │      │   │
   │  │  │ (backup) │ │ (primary)│ │ оператор     │      │   │
   │  │  └──────────┘ └──────────┘ │ (DE, FR, UK) │      │   │
   │  │                             └──────────────┘      │   │
   │  │  Маршрутизация: LCR + failover + geo-local       │   │
   │  └───────────────────────────────────────────────────┘   │
   └──────────────────────────────────────────────────────────┘
Рис. 5b — Детальная архитектура одного региона (Europe / Frankfurt): 8 слоёв, полная отказоустойчивость
Ключевые архитектурные решения

1. Тройной Kamailio — Access SBC (внешний, клиенты/интернет), Core (внутренний, маршрутизация), Trunk SBC (внешний, PSTN-провайдеры). Три зоны доверия: Access SBC принимает удар из интернета, Trunk SBC нормализует трафик от провайдеров, Core работает только с доверенным трафиком от обоих SBC.
2. Двусторонняя граница — SBC и на северной стороне (клиенты), и на южной (PSTN). Внутренняя сеть полностью изолирована от внешнего мира с обоих направлений.
3. Гео-распределение — каждый регион автономен. Звонки внутри региона не зависят от других регионов.
4. Локальный breakout — звонок из Германии на немецкий номер идёт через локальный транк в Frankfurt, не через US.
5. Все компоненты в HA — нет единой точки отказа. Каждый компонент задублирован минимум 2x.

Зачем SBC в enterprise-архитектуре

При работе в 43 странах с десятками SIP-провайдеров SBC обязателен. Это не дублирование Kamailio Core — это принципиально другая роль: граница доверия между внешним миром и вашей внутренней сетью.

Компонент Зона Задачи Почему отдельный
Kamailio Access SBC DMZ North / клиенты Topology hiding, anti-DDoS, SIP normalization, TLS termination, протокол-конвертация (WSS↔UDP), NAT traversal Принимает весь недоверенный трафик от клиентов из интернета. Северная граница
RTPEngine Access DMZ North / клиенты NAT traversal, SRTP↔RTP, WebRTC bridge, медиа-прокси для клиентского трафика Медиа-трафик из интернета не попадает напрямую к FreeSWITCH
Kamailio Core Внутренняя сеть Регистрация пользователей, внутренняя маршрутизация, LB между FreeSWITCH, dialog tracking Работает только с доверенным трафиком от обоих SBC. Оптимизирован под скорость, не под защиту
FreeSWITCH Внутренняя сеть IVR, очереди, конференции, запись, bridge, диалплан Никогда не виден из интернета. Занимается только медиа-логикой
Kamailio Trunk SBC DMZ South / PSTN SIP-нормализация для разных провайдеров, транк-аутентификация (IP ACL + credentials), E.164 нормализация, LCR, Caller ID, anti-fraud Изолирует внутреннюю сеть от PSTN-провайдеров. Южная граница. Каждый провайдер «видит» только Trunk SBC
RTPEngine Trunk DMZ South / PSTN Транскодирование кодеков (G.711↔G.729), RTP-пиннинг, медиа-прокси для PSTN-трафика Опциональный. Нужен если провайдеры требуют специфические кодеки или RTP-параметры
Двусторонний SBC = обязательный при масштабе 43 страны

На нашем масштабе оба SBC (Access + Trunk) необходимы:

Access SBC (северная граница — клиенты):
Защита от атак — публичные SIP-адреса постоянно сканируются ботами, без SBC внутренняя сеть уязвима
WebRTC-мост — конвертация WSS/DTLS-SRTP от браузеров в стандартный SIP/RTP
NAT traversal — клиенты за NAT, SBC решает проблемы доставки медиа

Trunk SBC (южная граница — PSTN-провайдеры):
Десятки SIP-транков в разных странах с разными особенностями SIP-стека (каждый оператор кодирует SIP по-своему)
Регуляторные требования — в ряде стран (ОАЭ, Сингапур, Германия) оператор требует сертифицированную точку подключения
Нормализация SIP — разные провайдеры отправляют CallerID, кодеки и заголовки в разных форматах, Trunk SBC приводит всё к единому виду
LCR + Failover — маршрутизация по стоимости между провайдерами, автоматическое переключение при отказе транка
Anti-fraud — защита от toll fraud (мошеннические звонки на premium-номера) на уровне транков

Compliance (оба SBC):
GDPR (Europe), CCPA (US), локальные законы о записи звонков требуют контроля на обеих границах

🌐 Межрегиональная маршрутизация и GeoDNS
                      ┌──────────────────┐
                      │     GeoDNS        │
                      │  sip.company.com  │
                      └────────┬─────────┘
                               │
            ┌──────────────────┼──────────────────┐
            ▼                  ▼                  ▼
  ┌────────────────┐  ┌────────────────┐  ┌────────────────┐
  │ EU SBC Cluster │  │ US SBC Cluster │  │ APAC SBC       │
  │ Frankfurt      │  │ Virginia       │  │ Singapore      │
  │ 2x Kamailio    │  │ 2x Kamailio    │  │ 2x Kamailio    │
  │ 2x RTPEngine   │  │ 2x RTPEngine   │  │ 2x RTPEngine   │
  └───────┬────────┘  └───────┬────────┘  └───────┬────────┘
          │                   │                   │
          └─────────┬─────────┴─────────┬─────────┘
                    │                   │
                    ▼                   ▼
          WireGuard VPN Mesh       SIP trunk routing:
          (между регионами)        EU user → EU trunk
                                        US user → US trunk
                                        Cross-region → nearest

  Правила межрегиональной маршрутизации:
  ┌─────────────────────────────────────────────────────────┐
  │ 1. Звонок внутри региона → локальный FreeSWITCH + транк│
  │ 2. Звонок между регионами → VPN → SBC другого региона  │
  │ 3. PSTN → ближайший к номеру регион (local breakout)   │
  │ 4. Failover: EU primary down → EU standby              │
  │    EU standby down → US cluster (geo-failover)         │
  └─────────────────────────────────────────────────────────┘
Рис. 5c — GeoDNS + межрегиональная маршрутизация с failover
Регион Primary ДЦ Standby ДЦ Страны Локальные транки
Europe Frankfurt, DE Amsterdam, NL ~20 (DE, FR, UK, ES, IT, PL, ...) Deutsche Telekom, BT, Orange, Telnyx EU
Americas Virginia, US Oregon, US ~10 (US, CA, MX, BR, AR, ...) Twilio, Bandwidth, Telnyx US
Asia-Pacific Singapore Tokyo, JP ~8 (SG, JP, AU, IN, KR, ...) SingTel, NTT, Telstra, Tata
Middle East Dubai, UAE Frankfurt (EU failover) ~5 (UAE, SA, QA, KW, BH) du, Etisalat, STC
🔒 Сетевые зоны безопасности (Network Zones)
  ИНТЕРНЕТ — КЛИЕНТЫ (недоверенная зона North)
  ─────────────────────────────────────────────────
  │ WebRTC-браузеры, SIP-телефоны за NAT, мобильные
  ─────────────────────────────────────────────────
                      │
                 FIREWALL L1 (North)DMZ NORTH — Access (полу-доверенная зона)
  ─────────────────────────────────────────────────
  │ Kamailio Access SBC — внешний SIP от клиентов
  │ RTPEngine Access   — медиа-прокси, SRTP↔RTP
  │ HAProxy/Nginx      — HTTPS/WSS, TLS termination
  │ TURN-сервер        — relay для WebRTC
  ─────────────────────────────────────────────────
                      │
                 FIREWALL L2CORE (доверенная зона)
  ─────────────────────────────────────────────────
  │ Kamailio Core  — внутренняя маршрутизация
  │ FreeSWITCH     — медиа-обработка (IVR, очереди)
  │ Go API         — бизнес-логика, ESL, WebSocket
  ─────────────────────────────────────────────────
                      │
                 FIREWALL L3DATA (защищённая зона)
  ─────────────────────────────────────────────────
  │ PostgreSQL     — данные (шифрование at rest)
  │ Redis          — кеш и сессии
  │ MinIO          — записи разговоров
  │ NATS           — шина событий
  ─────────────────────────────────────────────────
                      │
                 FIREWALL L4DMZ SOUTH — Trunk (полу-доверенная зона)
  ─────────────────────────────────────────────────
  │ Kamailio Trunk SBC — SIP к PSTN-провайдерам
  │ RTPEngine Trunk   — транскодирование кодеков
  │ Нормализация SIP, LCR, anti-fraud, Caller ID
  ─────────────────────────────────────────────────
                      │
                 FIREWALL L5 (South)PSTN / ВНЕШНИЕ ПРОВАЙДЕРЫ (недоверенная зона South)
  ─────────────────────────────────────────────────
  │ SIP-транки (Telnyx, Twilio, локальные операторы)
  │ SMS-шлюзы, внешние API
  ─────────────────────────────────────────────────
Рис. 5d — Пять сетевых зон с двусторонним DMZ: Internet(N) → DMZ North → Core → Data → DMZ South → PSTN
Принцип: внутренняя сеть изолирована с ОБОИХ направлений

Северная граница (клиенты): Любой SIP/RTP от клиентов из интернета сначала проходит через Access SBC (DMZ North), где нормализуется, фильтруется и проксируется. Только доверенный трафик от Access SBC попадает к Kamailio Core и FreeSWITCH.

Южная граница (PSTN): Исходящие вызовы к PSTN проходят через Trunk SBC (DMZ South), который нормализует SIP под требования каждого провайдера, применяет LCR-маршрутизацию и защищает от toll fraud. Провайдеры видят только Trunk SBC, а не внутреннюю архитектуру.

Данные (PostgreSQL, Redis) находятся в самой защищённой зоне и недоступны извне — ни с северной, ни с южной стороны.

Слои системы

Система разделена на шесть функциональных слоёв. Каждый слой изолирован и взаимодействует с соседними по чётко определённым протоколам.

Слой 1 Клиенты (Clients)

Точки входа пользователей в систему. Четыре типа клиентов:

React — веб-интерфейс администратора и оператора (управление, статистика, настройки).
SIP.js — веб-телефон в браузере через WebRTC (звонки без установки ПО).
IP-телефоны — аппаратные SIP-телефоны (Yealink, Grandstream, Snom).
Мобильные — SIP-клиенты на смартфонах (Ooh, Ooh, Ooh Ooh Ooh, Ooh Ooh Ooh Ooh, Ooh Ooh, Ooh Ooh, Ooh Ooh Ooh Ooh, и т.д.).

Слой 2 Балансировка и проксирование (Proxy Layer)

Входная точка для всего трафика. Два компонента:

Nginx — reverse proxy для HTTP/HTTPS и WebSocket. TLS-терминация, маршрутизация запросов к Go API и WebSocket-соединений к Kamailio.
Kamailio — SIP proxy/registrar. Регистрация устройств, аутентификация, маршрутизация SIP, балансировка нагрузки между FreeSWITCH-серверами, обработка NAT.

Слой 3 Бизнес-логика (Business Logic)

Центральный узел управления системой:

Go Backend API — REST API и WebSocket-сервер. Управление пользователями, правами, настройками. Логика автообзвона, аналитика, CDR-записи, интеграции с CRM. Взаимодействует с FreeSWITCH через ESL (Event Socket Library).

Слой 4 Медиа-обработка (Media Layer)

Обработка голоса и медиа-потоков:

FreeSWITCH — медиа-сервер (B2BUA). IVR, очереди, конференции, запись, диалплан. Кластер из N серверов для горизонтального масштабирования.
RTPEngine — медиа-прокси. Транскодирование (WebRTC ↔ RTP), управление NAT для медиа, проксирование голосового трафика.

Слой 5 Хранение данных (Data Layer)

Персистентное и оперативное хранение:

PostgreSQL — основная БД: пользователи, настройки, CDR, диалпланы, маршрутизация.
Redis — кеш и оперативные данные: сессии, статусы операторов, блокировки, очереди задач.
MinIO — S3-совместимое хранилище: записи разговоров, голосовые сообщения, файлы IVR.

Слой 7 Trunk SBC (южная граница)

Второй SBC — пограничный контроллер между внутренней сетью и PSTN-провайдерами:

Kamailio Trunk SBC — SIP-нормализация для разных провайдеров, транк-аутентификация, E.164 нормализация номеров, LCR-маршрутизация, Caller ID manipulation, anti-fraud.
RTPEngine Trunk — опциональный, для транскодирования кодеков (G.711↔G.729) если провайдеры требуют специфические форматы.
Topology hiding — провайдеры видят только Trunk SBC, а не внутреннюю архитектуру.

Слой 8 Внешний мир (External World)

Связь с внешними сетями и сервисами:

SIP-транки — подключение к PSTN через провайдеров (Telnyx, Twilio, локальные операторы). Входящие и исходящие вызовы на обычные телефоны.
SMS-шлюзы — отправка SMS-уведомлений (пропущенные, callback).
Внешние API — CRM, Helpdesk, системы аналитики.

Потоки данных

Три основных сценария звонков, показывающие как данные проходят через все слои системы.

Поток A Входящий звонок из PSTN к оператору

Клиент звонит на номер компании, проходит IVR-меню и попадает к свободному оператору.

1
SIP INVITE из PSTN — звонок приходит через SIP-транк на Kamailio (порт 5060).
2
Аутентификация транка — Kamailio проверяет IP-адрес транка (IP ACL) и анализирует DID (набранный номер).
3
Маршрутизация к FreeSWITCH — Kamailio определяет целевой FreeSWITCH-сервер и перенаправляет INVITE.
4
Обработка диалплана — FreeSWITCH проверяет диалплан по DID и направляет вызов на IVR.
5
IVR-меню — клиент слышит приветствие и нажимает 2 (DTMF) для перехода в очередь «Поддержка».
6
Выбор оператораmod_callcenter определяет свободного оператора с наибольшим временем ожидания (longest idle).
7
Bridge к оператору — FreeSWITCH отправляет INVITE на телефон оператора (через Kamailio) и соединяет звонок.
8
Проксирование медиа — RTPEngine проксирует голосовой трафик между SIP-транком и телефоном оператора.
9
Запись CDR — Go Backend получает событие от FreeSWITCH (ESL) и записывает CDR в PostgreSQL.
10
Обновление дашборда — NATS публикует событие о новом звонке, React-дашборд обновляется в реальном времени через WebSocket.
Поток B Исходящий звонок из веб-телефона

Оператор звонит клиенту через веб-телефон (SIP.js в браузере) на мобильный номер.

1
INVITE через WebSocket — SIP.js отправляет SIP INVITE через WSS-соединение на Kamailio.
2
Аутентификация и проверка прав — Kamailio аутентифицирует оператора и проверяет его право на исходящие вызовы.
3
Выбор транка по LCR — Kamailio определяет оптимальный SIP-транк по правилам наименьшей стоимости (Least Cost Routing) и направляет вызов к FreeSWITCH.
4
Установка Caller ID — FreeSWITCH подставляет Caller ID компании (номер, имя) согласно настройкам исходящей линии.
5
INVITE в PSTN — FreeSWITCH отправляет SIP INVITE через выбранный SIP-транк в телефонную сеть (PSTN).
6
Конвертация медиа — RTPEngine конвертирует WebRTC-поток (DTLS-SRTP, Opus) в обычный RTP (G.711) для PSTN.
7
Абонент отвечает — 200 OK приходит от PSTN, голосовой канал устанавливается в обоих направлениях.
8
Запись разговора — FreeSWITCH начинает запись, файл сохраняется в MinIO (S3-совместимое хранилище).
Поток C Внутренний звонок: WebRTC к IP-телефону

Сотрудник звонит коллеге из браузера на настольный IP-телефон внутри компании.

1
INVITE через WSS — браузер (SIP.js) отправляет SIP INVITE через WebSocket (WSS) на Kamailio.
2
Поиск регистрации — Kamailio находит регистрацию IP-телефона в таблице location (usrloc) и определяет его адрес.
3
INVITE к IP-телефону — Kamailio направляет SIP INVITE на IP-телефон по UDP/SIP (порт 5060).
4
Транскодирование медиа — RTPEngine конвертирует: WebRTC (DTLS-SRTP, Opus) на стороне браузера и RTP (G.711) на стороне IP-телефона.
5
Соединение установлено — IP-телефон звонит, сотрудник поднимает трубку, голос идёт в обе стороны через RTPEngine.

Сигнализация vs Медиа

Два полностью раздельных пути (Signaling vs Media)

Фундаментальный принцип VoIP-архитектуры: сигнализация и медиа идут по совершенно разным путям, через разные компоненты, по разным протоколам. Сигнализация (SIP) — это управляющий канал: «кто звонит, кому, принять или отклонить». Медиа (RTP) — это сам голос, реальные аудио-данные. Понимание этого разделения критически важно для диагностики и архитектуры.

═══ СИГНАЛИЗАЦИЯ (Control Plane) ═══                ═══ МЕДИА (Data Plane) ═══

  Протокол: SIP                                     Протокол: RTP / SRTP
  Транспорт: TCP / UDP / WSS                        Транспорт: UDP
  Нагрузка: Лёгкая (текстовые сообщения)            Нагрузка: Тяжёлая (50 пакетов/сек)
  Задача: Управление вызовами                        Задача: Передача голоса

  ┌──────────┐      ┌──────────┐      ┌──────────┐
  │ Телефон  │─SIP─▶│ Kamailio │─SIP─▶│FreeSWITCH│
  └──────────┘      └──────────┘      └──────────┘

  ┌──────────┐      ┌──────────┐      ┌──────────┐
  │ Телефон A│═RTP═▶│RTPEngine │═RTP═▶│ Телефон B│
  └──────────┘      └──────────┘      └──────────┘

  Сигнализация проходит:                             Медиа проходит:
  Телефон → Kamailio → FreeSWITCH                    Телефон A → RTPEngine → Телефон B
  (настройка, изменение, завершение)                  (голос, реальное время)
Рис. 6 — Сигнализация (SIP) и медиа (RTP) идут разными путями через разные компоненты
Сигнализация (SIP)

Путь: Телефон → Kamailio → FreeSWITCH
Протокол: SIP (TCP/UDP/WSS, порт 5060/5061)
Характер: Лёгкий трафик, текстовые сообщения
Задачи: Установка вызова, перевод, удержание, завершение, регистрация устройств
Компоненты: Kamailio (маршрутизация), FreeSWITCH (логика)

Медиа (RTP)

Путь: Телефон A → RTPEngine → Телефон B
Протокол: RTP/SRTP (UDP, порты 10000–20000)
Характер: Тяжёлый трафик, 50 пакетов/сек на поток
Задачи: Передача голоса, транскодирование, шифрование
Компоненты: RTPEngine (прокси, транскодирование)

Аналогия для понимания

Сигнализация — это SMS-сообщения, которыми вы договариваетесь о встрече: «Давай встретимся в кафе в 15:00». Медиа — это сама встреча: реальный разговор лицом к лицу. SMS идут через сотового оператора, а встреча происходит в кафе — совершенно разные каналы для совершенно разных задач.

Практическое значение

Если звонок устанавливается (телефон звонит), но нет голоса — проблема в медиа-пути (RTPEngine, NAT, файрволл для UDP-портов). Если звонок вообще не проходит — проблема в сигнализации (Kamailio, SIP-маршрутизация, аутентификация). Диагностика всегда начинается с определения: «Это проблема сигнализации или медиа?»

04

Компоненты стека

Детальное описание каждого компонента: что делает, какие функции покрывает, ключевые модули и примеры конфигурации.

K

Kamailio — SIP-прокси

C SIP Proxy Signaling Only

Kamailio — высокопроизводительный SIP-прокси-сервер, написанный на C. Способен обрабатывать тысячи вызовов в секунду на одном ядре. Работает исключительно с сигнализацией (SIP) — голосовые потоки (RTP) через него не проходят.

Модуль Назначение
usrloc Хранение регистраций (кто где онлайн)
auth_db Аутентификация SIP из PostgreSQL
dispatcher Балансировка между FreeSWITCH нодами
pike Защита от brute-force (rate limiting)
permissions ACL — белые/чёрные списки IP
tls Шифрование SIP-сигнализации
nathelper Исправление NAT в SIP-заголовках
websocket SIP поверх WebSocket для WebRTC
rtpengine Управление RTPEngine для медиа-проксирования
dmq Синхронизация данных между нодами кластера
dialog Отслеживание активных диалогов
htable In-memory хеш-таблицы для кеширования
kamailio.cfg — маршрутизация входящего запроса
request_route { # Проверяем максимальное количество пересылок if (!maxfwd_process(10)) { sl_send_reply("483", "Too Many Hops"); exit; } # Защита от flood-атак if (!pike_check_req()) { xlog("L_WARN", "PIKE: блокировка $si\n"); exit; } # Аутентификация REGISTER-запросов if (is_method("REGISTER")) { if (!auth_check("$fd", "subscriber", 1)) { auth_challenge("$fd", 0); exit; } save("location"); exit; } # INVITE — отправляем на FreeSWITCH через dispatcher if (is_method("INVITE")) { ds_select_dst(1, 4); # группа 1, round-robin t_relay(); exit; } }
Ключевой инсайт:

Kamailio не работает с голосом. Его задача — быстро решить КУДА отправить вызов и сделать это надёжно. Думайте о нём как о «маршрутизаторе для SIP-пакетов».

F

FreeSWITCH — Медиа-сервер

C Media Server PBX

FreeSWITCH — медиа-сервер и «мозг» обработки вызовов. Написан на C, обрабатывает голос, IVR-меню, очереди, конференции и запись разговоров. Именно здесь вызов «оживает» — FreeSWITCH отвечает, проигрывает приветствие, соединяет абонентов и записывает разговор.

Модуль Назначение
mod_sofia SIP-стек (регистрация, вызовы)
mod_callcenter Очереди вызовов (ACD)
mod_conference Конференц-связь
mod_voicemail Голосовая почта
mod_dptools Инструменты диалплана (bridge, transfer, record)
mod_ivr Интерактивное голосовое меню
mod_lua Lua-скрипты для логики вызовов
mod_event_socket ESL — внешнее управление (Go, Python)
mod_verto WebRTC-протокол (альтернатива SIP.js)
mod_local_stream Музыка ожидания (MOH)
mod_spandsp Факс T.38, DTMF detection
mod_unimrcp ASR/TTS через MRCP-протокол
Lua — IVR-скрипт: приветствие и выбор очереди
-- IVR: отвечаем, проигрываем меню, ждём выбор session:answer() session:sleep(1000) session:streamFile("ivr/welcome.wav") session:streamFile("ivr/press_1_sales_2_support.wav") local digit = session:getDigits(1, "#", 5000) if digit == "1" then -- Перевод в очередь продаж session:transfer("sales@queue") elseif digit == "2" then -- Перевод в очередь поддержки session:transfer("support@queue") else -- Нет ввода — повторяем или переводим на оператора session:transfer("default@queue") end
Ключевой инсайт:

FreeSWITCH управляется извне через ESL (Event Socket). Ваш Go-сервис подключается к ESL и командует: «ответь», «переведи», «запиши». Это позволяет вынести всю бизнес-логику из конфигов FreeSWITCH в нормальный код.

R

RTPEngine — Медиа-прокси

C Media Proxy Sipwise

RTPEngine — медиа-прокси, разработанный компанией Sipwise. Проксирует RTP/SRTP-потоки между клиентами, обеспечивая NAT traversal, конвертацию шифрования и транскодирование кодеков. Не обрабатывает логику вызовов — только пропускает и трансформирует медиа-потоки.

Функция Назначение
NAT traversal Проксирование медиа для клиентов за NAT
SRTP ↔ RTP Конвертация шифрованного в нешифрованный поток
WebRTC bridge DTLS-SRTP (браузер) ↔ plain RTP (IP-телефон)
Codec transcode Перекодирование (Opus ↔ G.711) при необходимости
Media recording Запись RTP-потока в файл
ICE handling Обработка ICE candidates для WebRTC
kamailio.cfg — вызов RTPEngine для медиа-проксирования
# Обработка INVITE — подключаем RTPEngine route[RELAY] { # Для WebRTC-клиентов: конвертируем DTLS-SRTP → plain RTP if ($proto == "ws" || $proto == "wss") { rtpengine_offer("ICE=remove RTP/AVP"); } else { rtpengine_offer("replace-origin replace-session-connection"); } t_on_reply("MANAGE_REPLY"); t_relay(); } onreply_route[MANAGE_REPLY] { if (has_body("application/sdp")) { rtpengine_answer("replace-origin replace-session-connection"); } }
Ключевой инсайт:

Без RTPEngine WebRTC-клиенты не смогут разговаривать с обычными SIP-телефонами — разные протоколы шифрования и кодеки. RTPEngine выступает «переводчиком» между мирами.

Go

Go Backend — API и бизнес-логика

Go REST API ESL Client

Go Backend — ваш кастомный сервис, «клей» между всеми компонентами системы. Предоставляет REST API для фронтенда, управляет вызовами через ESL, обрабатывает CDR-события, управляет кампаниями автообзвона и рассылает вебхуки во внешние системы.

Функция Назначение
REST API CRUD пользователей, очередей, IVR, маршрутов
ESL Client Управление вызовами через FreeSWITCH
WebSocket Hub Реалтайм-события для React-дашборда
CDR Processor Приём событий → запись в PostgreSQL
Dialer Engine Управление кампаниями автообзвона
Webhook Dispatcher Отправка событий во внешние системы (CRM)
Provisioning Server Автонастройка IP-телефонов по MAC
Auth & RBAC JWT-аутентификация, роли и права
Go — подключение к FreeSWITCH ESL и originate
package main import ( "fmt" "github.com/cgrates/fsock" ) func main() { // Подключаемся к FreeSWITCH Event Socket conn, err := fsock.NewFSock( "127.0.0.1:8021", "ClueCon", // пароль ESL 10, // reconnects eventHandlers, eventFilters, logger, ) if err != nil { panic(err) } // Инициируем исходящий вызов cmd := "originate {origination_caller_id_number=1001}" + "sofia/gateway/trunk1/+74951234567 &bridge(user/1001)" reply, err := conn.SendApiCmd(cmd) fmt.Println("Originate:", reply) }

Ключевые Go-библиотеки:

gin fsock pgx go-redis nats.go jwt gorilla/websocket
Ключевой инсайт:

Go идеален для телефонии: горутины для тысяч одновременных ESL-событий, низкая задержка, простой деплой одним бинарником. Вся бизнес-логика живёт здесь, а не в конфигах Kamailio или FreeSWITCH.

P

PostgreSQL — Основная БД

SQL RDBMS Persistent Storage

PostgreSQL — основное постоянное хранилище всех данных системы. Содержит информацию о пользователях, маршрутизации, CDR-записях, конфигурации IVR, очередей и кампаний. Используется как Go-сервисом (бизнес-данные), так и Kamailio (аутентификация SIP).

Группа Таблицы / Назначение
Мультитенантность tenants — компании, домены, лимиты
Пользователи users, extensions, sip_credentials, devices
Маршрутизация dialplans, did_numbers, trunks, outbound_routes
IVR ivr_menus — дерево меню в JSON
Очереди queues, queue_members — настройки и агенты
CDR cdr — журнал всех вызовов
Записи recordings — метаданные файлов записей
Voicemail voicemail_boxes, voicemail_msgs
Конференции conference_rooms
Автообзвон campaigns, campaign_numbers
Аудит audit_log — история изменений
SQL — таблица CDR (журнал вызовов)
CREATE TABLE cdr ( id BIGSERIAL PRIMARY KEY, uuid UUID NOT NULL UNIQUE, tenant_id INT NOT NULL REFERENCES tenants(id), caller_number VARCHAR(32) NOT NULL, callee_number VARCHAR(32) NOT NULL, direction VARCHAR(10) NOT NULL, -- inbound / outbound / internal start_time TIMESTAMPTZ NOT NULL, answer_time TIMESTAMPTZ, end_time TIMESTAMPTZ, duration INT DEFAULT 0, -- секунды bill_sec INT DEFAULT 0, -- оплачиваемые секунды disposition VARCHAR(20), -- ANSWERED, NO_ANSWER, BUSY, FAILED queue_id INT, agent_id INT, recording_url TEXT, hangup_cause VARCHAR(64), created_at TIMESTAMPTZ DEFAULT NOW() ); CREATE INDEX idx_cdr_tenant_time ON cdr(tenant_id, start_time DESC); CREATE INDEX idx_cdr_caller ON cdr(caller_number); CREATE INDEX idx_cdr_callee ON cdr(callee_number);
Ключевой инсайт:

PostgreSQL используется и Kamailio (для аутентификации), и Go (для бизнес-данных). Это единый источник правды (single source of truth) для всей системы.

R

Redis — Кеш и реалтайм

In-Memory Cache Pub/Sub

Redis — in-memory хранилище для эфемерного состояния реального времени. Здесь живут данные, которые нужны «прямо сейчас»: активные вызовы, статусы операторов, метрики очередей, API-сессии и счётчики rate limiting.

Ключ (key pattern) Хранимые данные
call:{uuid} Активный вызов: caller, callee, state, start_time
agent:{id}:status Статус оператора: ready/busy/break/offline
presence:{ext} Presence: available/dnd/away
queue:{id}:stats Очередь: waiting, talking, avg_wait, sla%
session:{token} API-сессия: user_id, tenant_id, role, expires
rate:{ip} Rate limiting: counter + TTL
location:{aor} Кеш регистраций из Kamailio
Go — работа со статусом оператора через go-redis
import ( "context" "fmt" "time" "github.com/redis/go-redis/v9" ) var rdb = redis.NewClient(&redis.Options{ Addr: "localhost:6379", }) func SetAgentStatus(ctx context.Context, agentID int, status string) error { key := fmt.Sprintf("agent:%d:status", agentID) return rdb.Set(ctx, key, status, 24*time.Hour).Err() } func GetAgentStatus(ctx context.Context, agentID int) (string, error) { key := fmt.Sprintf("agent:%d:status", agentID) return rdb.Get(ctx, key).Result() } // Использование: // SetAgentStatus(ctx, 42, "ready") -- оператор свободен // SetAgentStatus(ctx, 42, "busy") -- в разговоре // SetAgentStatus(ctx, 42, "break") -- на перерыве
Ключевой инсайт:

Redis — это «оперативная память» системы. Всё что нужно знать ПРЯМО СЕЙЧАС (кто онлайн, кто свободен, сколько в очереди) — здесь. Данные эфемерны: перезапуск Redis не сломает систему, только временно обнулит метрики.

N

NATS — Шина событий

Go Message Broker Pub/Sub

NATS — легковесный брокер сообщений, написанный на Go. Быстрее RabbitMQ, проще Kafka. Обеспечивает асинхронную связь между компонентами: FreeSWITCH генерирует события, Go обрабатывает их и рассылает подписчикам — дашборду, CDR-процессору, вебхук-диспетчеру.

Subject (тема) Данные события
call.started uuid, caller, callee, direction, queue
call.answered uuid, agent_id, wait_time
call.ended uuid, duration, disposition, recording_path
agent.status.changed agent_id, old_status, new_status
queue.stats.updated queue_id, waiting, talking, sla
voicemail.new box_id, caller, duration, file_path
cdr.ready cdr_id, complete record
webhook.dispatch url, payload, event_type
Go — публикация и подписка на NATS-события
import ( "encoding/json" "log" "github.com/nats-io/nats.go" ) // Подключение к NATS nc, _ := nats.Connect("nats://localhost:4222") // Публикация события "вызов начат" event := map[string]interface{}{ "uuid": "a1b2c3d4", "caller": "+74951234567", "callee": "1001", "direction": "inbound", } data, _ := json.Marshal(event) nc.Publish("call.started", data) // Подписка — получаем все события вызовов nc.Subscribe("call.>", func(msg *nats.Msg) { log.Printf("[%s] %s", msg.Subject, string(msg.Data)) })
Ключевой инсайт:

NATS развязывает компоненты. FreeSWITCH не знает о React-дашборде, Go не знает о CRM — они просто публикуют события. Каждый подписчик получает то, что ему нужно, не создавая связности.

UI

React Frontend — Панель управления

TypeScript SPA React 18

React Frontend — одностраничное приложение (SPA) для администрирования системы, панели оператора и реалтайм-дашбордов. Включает встроенный WebRTC-телефон на базе SIP.js и визуальный конструктор IVR на React Flow.

Страница Назначение
Dashboard Реалтайм: активные вызовы, очереди, агенты
Users CRUD пользователей и расширений
Queues Настройка очередей, стратегий, агентов
IVR Builder Визуальный drag-n-drop конструктор меню
CDR Журнал вызовов с фильтрами и экспортом
Recordings Прослушивание записей, скачивание
Reports Графики, SLA, heatmap нагрузки
WebPhone Встроенный SIP.js-телефон
Campaigns Управление автообзвоном
Settings Транки, маршруты, безопасность
TypeScript — подключение WebSocket для реалтайм-событий
import { useEffect } from "react"; import { useQueueStore } from "./stores/queue"; export function useRealtimeEvents() { const updateQueue = useQueueStore(s => s.update); useEffect(() => { const ws = new WebSocket("wss://api.example.com/ws"); ws.onmessage = (e) => { const event = JSON.parse(e.data); switch (event.type) { case "call.started": // Добавляем вызов на дашборд addActiveCall(event.data); break; case "queue.stats.updated": // Обновляем метрики очереди updateQueue(event.data); break; } }; return () => ws.close(); }, []); }

Ключевые библиотеки:

React 18 TypeScript SIP.js React Flow Recharts TanStack Query Tailwind Zustand
Ключевой инсайт:

React Flow позволяет строить IVR визуально — перетаскивая блоки. Это намного удобнее чем XML/Lua конфиги. Пользователь рисует меню мышкой, а система генерирует конфигурацию автоматически.

H

Homer — SIP-мониторинг

HEP SIP Capture Troubleshooting

Homer — специализированный инструмент захвата и анализа SIP-трафика. Перехватывает все SIP-пакеты через HEP-протокол, строит визуальные диаграммы вызовов и помогает диагностировать проблемы с сигнализацией. Незаменим при отладке «звонок не проходит».

Функция Назначение
SIP Capture Захват всех SIP-пакетов через HEP-протокол
Call Flow Визуальная диаграмма: INVITE → 180 → 200 → BYE
Search Поиск по номеру, Call-ID, времени, статусу
Alerts Уведомления при SIP-ошибках, высоком % отказов
Statistics Графики: calls/sec, response codes, latency
kamailio.cfg — отправка SIP-пакетов в Homer через HEP
# Загружаем модуль siptrace для отправки в Homer loadmodule "siptrace.so" modparam("siptrace", "duplicate_uri", "sip:homer.local:9060") modparam("siptrace", "hep_mode_on", 1) modparam("siptrace", "hep_version", 3) modparam("siptrace", "trace_flag", 22) # В request_route — включаем трассировку request_route { setflag(22); # trace_flag — все запросы отправляются в Homer sip_trace(); ... }
Ключевой инсайт:

Homer — ваш «Wireshark для SIP». Когда звонок не проходит, Homer покажет ТОЧНО где он застрял: на Kamailio? На FreeSWITCH? Получил 403? Таймаут? Визуальная диаграмма call flow экономит часы отладки.

AI

AI/ML-сервисы — Распознавание и аналитика

Python ASR / TTS NLP

AI/ML-сервисы — набор внешних сервисов для обработки речи и интеллектуальной аналитики. Распознавание речи (ASR), синтез речи (TTS), анализ транскрипций через LLM и определение тональности. Могут быть как self-hosted, так и облачными.

Сервис Назначение
Vosk Self-hosted ASR (Speech-to-Text), поддержка русского
Whisper OpenAI ASR, высокое качество, можно self-host
Piper Self-hosted TTS (Text-to-Speech), нейросетевой
LLM (Claude/GPT) Анализ транскрипций, суммаризация, классификация
Sentiment Определение тональности: позитивная/негативная

Поток обработки (integration flow):

Вызов → FreeSWITCH записывает → WAV-файл → Whisper STT → текст транскрипции → LLM-анализ (суммаризация, классификация, тональность) → результаты в БД

Python — распознавание записи через Whisper
import whisper # Загружаем модель (можно: tiny, base, small, medium, large) model = whisper.load_model("medium") # Распознаём запись разговора result = model.transcribe( "/recordings/2026-02-18/call_a1b2c3d4.wav", language="ru", task="transcribe" ) print(result["text"]) # "Здравствуйте, я хотел бы узнать о статусе моего заказа..." # Сегменты с временными метками for seg in result["segments"]: print(f"[{seg['start']:.1f}s] {seg['text']}")
Ключевой инсайт:

Vosk работает оффлайн и бесплатно. Для реалтайм-распознавания в IVR — идеальный выбор. Whisper лучше для post-call анализа, где важно качество, а не скорость.

Инфраструктура — DevOps-компоненты

Docker Monitoring CI/CD

Инфраструктура — набор DevOps-инструментов, обеспечивающих контейнеризацию, мониторинг, логирование, хранение файлов и автоматизацию развёртывания. Без этого слоя невозможно надёжно эксплуатировать систему в продакшене.

Инструмент Назначение
Docker / Compose Контейнеризация всех компонентов
Nginx Reverse proxy, TLS, балансировка HTTP
Prometheus Сбор метрик (CPU, RAM, calls, queue depth)
Grafana Визуализация метрик, алерты
Loki Агрегация логов из всех контейнеров
MinIO S3-совместимое хранилище для записей разговоров
Certbot Автоматическое обновление SSL-сертификатов
Ansible Автоматизация развёртывания и конфигурации
docker-compose.yml — основные сервисы (фрагмент)
version: "3.8" services: kamailio: image: kamailio/kamailio:latest ports: - "5060:5060/udp" - "5061:5061/tcp" depends_on: [postgres, redis] freeswitch: image: freeswitch/freeswitch:latest ports: - "8021:8021" # ESL volumes: - recordings:/var/lib/freeswitch/recordings rtpengine: image: drachtio/rtpengine:latest network_mode: host # нужен прямой доступ к UDP-портам go-backend: build: ./backend ports: - "8080:8080" depends_on: [postgres, redis, nats, freeswitch] postgres: image: postgres:16 volumes: - pgdata:/var/lib/postgresql/data redis: image: redis:7-alpine nats: image: nats:latest ports: - "4222:4222"
Ключевой инсайт:

Docker Compose — ваш лучший друг на старте. Один файл docker-compose.yml поднимает ВСЮ систему. Для продакшена — переходите на Docker Swarm или Kubernetes, но начинайте с Compose.

05

Связи компонентов

Матрица «функция → компонент», диаграмма взаимодействий и полная последовательность обработки вызова.

Матрица «Функция → Компонент»

Матрица показывает, какой компонент за какую функцию отвечает. — основная ответственность (Primary): компонент является главным исполнителем функции. — участие (Secondary): компонент вспомогательно участвует в реализации. Пустая ячейка — компонент не задействован.

Категория функций Kamailio FreeSWITCH RTPEngine Go API PostgreSQL Redis NATS React
Управление вызовами
Переводы
Многосторонние
IVR
Очереди (ACD)
Голосовая почта
Маршрутизация
Супервизор
Запись / Аналитика
Статусы / Presence
Автообзвон
Интеграции
WebRTC
Безопасность
Отказоустойчивость
Администрирование
Как читать матрицу

По строкам видно, какие компоненты нужны для каждой функции. По столбцам — зона ответственности каждого компонента. Например, FreeSWITCH — основной исполнитель большинства телефонных функций, а Go API отвечает за бизнес-логику, аналитику и интеграции.

Кто с кем говорит

Диаграмма показывает все межкомпонентные соединения с указанием используемых протоколов. Каждая стрелка — это реальный сетевой канал, который нужно настроить, защитить и мониторить.

                    ┌───────────┐           ┌────────────┐
                    │  Браузер  │           │ IP-телефон │
                    │(SIP.js)   │           │  (SIP)     │
                    └─────┬─────┘           └──────┬─────┘
                          │                        │
                     WSS/SIP                  UDP/SIP
                          │                        │
                    ┌─────▼────────────────────────▼─────┐
                    │            Kamailio               │
                    │     SIP Proxy / Registrar           │
                    └──┬─────────┬──────────┬──────────┬──┘
                       │         │          │          │
                  SIPng ctrlSQLHEP │
                       │         │          │          │
                       ▼         ▼          ▼          ▼
               ┌────────────┐ ┌──────────┐ ┌──────────┐ ┌───────┐
               │ FreeSWITCH │ │ RTPEngine│ │PostgreSQL│ │ Homer │
               │  (B2BUA)   │ │ (Media)  │ │  (Data)  │ │(SIP   │
               │            │ │          │ │          │ │ мон.) │
               └──┬───┬──┬──┘ └──────────┘ └─────▲────┘ └───▲───┘
                  │   │  │                          │        │
             RTP │   │  │HEP                     │        │
                  │   │  └─────────────────────────────────►─┘
                  ▼   │
          ┌──────────┐S3 API              ┌──────────────────┐
          │ RTPEngine││     Go API       │
          │ (Media   ││ REST + WebSocket │
          │  proxy)  │└──┬───┬───┬───┬───┘
          └──────────┘ ┌──────┐               │   │   │   │
                      │MinIO │          ESL/│   │SQL│   │NATS
                      │(S3)  │          TCP │   │   │   │
                      └──────┘               │   │   │   │
                                               ▼   ▼   ▼   ▼
               ┌────────────┐ ┌──────────┐ ┌─────┐ ┌──────────┐
               │ FreeSWITCH │ │PostgreSQL│ │NATS │ │  Redis   │
               └────────────┘ └──────────┘ └──┬──┘ └──────────┘события
                                                │
                                                ▼
                                         ┌───────────┐
                                         │   React   │
                                         │ Dashboard │
                                         └───────────┘
Рис. 9 — Карта всех межкомпонентных соединений с указанием протоколов

Сводная таблица всех соединений:

Источник Назначение Протокол Назначение соединения
Браузер (SIP.js) Kamailio WSS/SIP SIP-сигнализация через WebSocket (регистрация, звонки)
IP-телефон Kamailio UDP/SIP SIP-сигнализация (регистрация, звонки)
Kamailio FreeSWITCH SIP Маршрутизация вызовов к медиа-серверу
Kamailio RTPEngine ng control Управление медиа-проксированием (offer/answer SDP)
FreeSWITCH RTPEngine RTP Голосовые потоки к медиа-прокси
Go API FreeSWITCH ESL/TCP Управление вызовами, получение событий (Event Socket)
Go API PostgreSQL SQL Чтение/запись данных (пользователи, CDR, настройки)
Go API Redis Redis protocol Кеш, сессии, статусы, блокировки
Go API NATS NATS Публикация и подписка на события (real-time)
Go API React HTTP/WS REST API и WebSocket для реалтайм-обновлений
Kamailio PostgreSQL SQL Таблицы маршрутизации, регистрации, ACL
Kamailio Homer HEP Зеркалирование SIP-трафика для мониторинга
FreeSWITCH Homer HEP Зеркалирование SIP-трафика для мониторинга
FreeSWITCH MinIO S3 API Сохранение записей разговоров и голосовой почты
Важно для файрволла и сети

Каждое соединение из таблицы выше требует открытых портов и сетевых правил. При развёртывании убедитесь, что все указанные протоколы и порты разрешены между соответствующими хостами. Особое внимание — UDP-портам RTP (10000–20000), которые часто блокируются по умолчанию.

Полная последовательность входящего вызова

Входящий вызов из PSTN — все 15 шагов через все компоненты

Детальная последовательность обработки входящего звонка, проходящего через каждый компонент системы: от поступления SIP INVITE из телефонной сети до обновления дашборда в реальном времени. Каждый шаг указывает задействованный компонент и выполняемое действие.

1
SIP INVITE из PSTN — SIP-транк отправляет INVITE с номером вызывающего (CID) и набранным номером (DID). SIP-транк
2
Kamailio принимает на порту 5060 — SIP proxy получает INVITE и начинает обработку в route[MAIN]. Kamailio
3
Аутентификация транка (IP ACL) — Kamailio проверяет IP-адрес отправителя по списку разрешённых IP-адресов транков. Kamailio
4
Проверка DID в PostgreSQL — Kamailio запрашивает базу данных: какому тенанту/маршруту принадлежит набранный номер. Kamailio PostgreSQL
5
Offer SDP → RTPEngine — Kamailio отправляет SDP-часть INVITE в RTPEngine для подготовки медиа-проксирования. Kamailio RTPEngine
6
RTPEngine выделяет медиа-порты — RTPEngine резервирует пару UDP-портов для приёма и отправки голосового трафика. RTPEngine
7
Маршрутизация INVITE к FreeSWITCH — Kamailio выбирает целевой FreeSWITCH-сервер (по нагрузке) и перенаправляет INVITE. Kamailio FreeSWITCH
8
FreeSWITCH обрабатывает диалплан — по DID определяется контекст и extension; вызов направляется на IVR-приложение. FreeSWITCH
9
IVR отвечает, проигрывает приветствие — FreeSWITCH отвечает 200 OK, проигрывает аудиофайл: «Нажмите 1 для продаж, 2 для поддержки...» FreeSWITCH
10
Клиент нажимает DTMF 2 → очередь «Поддержка» — FreeSWITCH распознаёт DTMF и переводит вызов в очередь mod_callcenter. FreeSWITCH
11
mod_callcenter находит оператора — алгоритм «longest idle» выбирает оператора, который дольше всех свободен. Статус берётся из Redis. FreeSWITCH Redis
12
INVITE к оператору через Kamailio — FreeSWITCH отправляет новый INVITE на SIP-адрес оператора; Kamailio находит его регистрацию и доставляет вызов. FreeSWITCH Kamailio
13
Оператор отвечает → 200 OK — телефон оператора отправляет 200 OK; цепочка подтверждений (ACK) проходит обратно через Kamailio и FreeSWITCH. Оператор
14
RTPEngine соединяет медиа-потоки — голос теперь идёт: SIP-транк (PSTN) ↔ RTPEngine ↔ телефон оператора. Транскодирование при необходимости. RTPEngine
15
CDR + событие + дашборд — Go Backend получает событие через ESL, записывает CDR в PostgreSQL. NATS публикует событие. React-дашборд обновляется через WebSocket. Go API PostgreSQL NATS React
Весь путь за миллисекунды

Несмотря на 15 шагов и участие 8 компонентов, весь процесс от поступления INVITE до звонка телефона оператора занимает менее 500 мс при нормальной нагрузке. SIP-сигнализация передаётся в виде текстовых сообщений и обрабатывается каждым компонентом за единицы миллисекунд.

Диаграмма последовательности (sequence diagram) в текстовом формате
PSTN      Kamailio    RTPEngine   FreeSWITCH    Redis     Оператор   Go API    NATS    React
  │          │            │           │           │          │          │         │        │
  │─INVITE─▶│            │           │           │          │          │         │        │
  │          │──offer──▶│           │           │          │          │         │        │
  │          │◀─answer──│           │           │          │          │         │        │
  │          │──INVITE─────────────▶│           │          │          │         │        │
  │          │           │           │──IVR─────▶│          │          │         │        │
  │          │           │           │◀─DTMF 2──│          │          │         │        │
  │          │           │           │──query───▶│          │          │         │        │
  │          │           │           │◀─agent───│          │          │         │        │
  │          │◀─INVITE──────────────│           │          │          │         │        │
  │          │──────────────────────────────────▶│          │          │         │        │
  │          │◀──────────────────────────────────│ 200 OK   │          │         │        │
  │          │──answer──▶│           │           │          │          │         │        │
  │          │           │═══RTP═══▶│═══════════│══════════│          │         │        │
  │═══RTP══▶│═══════════│◀══RTP═══│           │          │          │         │        │
  │          │           │           │──event──────────────────────▶│         │        │
  │          │           │           │           │          │          │──pub──▶│        │
  │          │           │           │           │          │          │         │──WS──▶│
Рис. 10 — Последовательность сообщений входящего вызова через все компоненты системы
06

План реализации

Пошаговый план от первого звонка между двумя телефонами до полноценной продакшен-системы.

ФАЗА 1
Первый звонок

Установка FreeSWITCH, регистрация двух софтфонов и первый звонок между ними. Минимальная рабочая система.

Что делаем:
— Устанавливаем FreeSWITCH из пакетов или исходников
— Создаём два SIP-аккаунта (extensions) в конфигурации
— Регистрируем софтфоны (Oнлинфон, Oбщие, Oлинфон или Oбщие — любой SIP-клиент)
— Совершаем звонок между двумя внутренними номерами

Чему учимся: SIP-регистрация, базовый call flow (INVITE → 200 OK → ACK → BYE), dialplan, профили пользователей.

Разблокируем: внутренние звонки

ФАЗА 2
Программное управление

Пишем Go-клиент для ESL (Event Socket Library) — программный контроль над звонками.

Что делаем:
— Подключаемся к FreeSWITCH через ESL (inbound-режим)
— Слушаем события: новый звонок, ответ, завершение
— Управляем звонками из кода: ответить, положить трубку, перевести
— Originate — инициация звонка из Go-кода

Чему учимся: протокол ESL, событийная архитектура (event-driven), команды FreeSWITCH API, управление каналами.

Разблокируем: API управления звонками

ФАЗА 3
WebRTC

Добавляем веб-клиент на SIP.js — звонки прямо из браузера без установки ПО.

Что делаем:
— Настраиваем WebSocket-транспорт в FreeSWITCH (mod_verto или WSS для SIP)
— Подключаем библиотеку SIP.js на веб-странице
— Реализуем входящие и исходящие звонки из браузера
— Настраиваем TLS-сертификаты (WebRTC требует HTTPS)

Чему учимся: SIP over WebSocket, SRTP-шифрование, ICE/STUN/TURN, кодек Opus, взаимодействие WebRTC и SIP.

Разблокируем: звонки из браузера

ФАЗА 4
REST API

Создаём полноценный Go REST API для управления пользователями, расширениями и правилами маршрутизации.

Что делаем:
— Проектируем схему базы данных: пользователи, расширения (extensions), правила маршрутизации
— REST API: CRUD для пользователей и настроек
— Синхронизация конфигурации FreeSWITCH через mod_xml_curl или ESL
— Аутентификация и авторизация API (JWT)

Чему учимся: проектирование API, схема БД для телефонии, провижининг (provisioning), динамическая конфигурация.

Разблокируем: админ-панель, автоматический провижининг

ФАЗА 5
SIP-транк

Подключаем SIP-провайдера для звонков на реальные телефонные номера (PSTN).

Что делаем:
— Выбираем SIP-провайдера (trunk provider) и регистрируемся
— Настраиваем SIP-транк (gateway) в FreeSWITCH
— Маршрутизация исходящих звонков через транк
— Приём входящих звонков на DID-номера (Direct Inward Dialing)
— Настройка Caller ID (отображаемый номер)

Чему учимся: конфигурация транков, DID-номера, Caller ID, кодеки для WAN, аутентификация у провайдера (IP-based или credentials).

Разблокируем: связь с внешним миром (PSTN)

ФАЗА 6
IVR и очереди

Голосовые меню (IVR) на Lua-скриптах и очереди вызовов через mod_callcenter.

Что делаем:
— Создаём голосовое меню (IVR): «Нажмите 1 для продаж, 2 для поддержки»
— Пишем Lua-скрипты для логики IVR (условия, переходы, TTS)
— Настраиваем mod_callcenter: очереди, агенты, стратегии распределения
— Стратегии: round-robin, longest-idle, ring-all, skill-based routing

Чему учимся: dialplan-скриптинг (Lua), логика IVR, стратегии очередей, статусы агентов, music on hold.

Разблокируем: функции контакт-центра

ФАЗА 7
Запись и CDR

Запись звонков, сбор CDR (Call Detail Records) и построение дашбордов аналитики.

Что делаем:
— Включаем запись звонков (mod_recording) с хранением в файловой системе или S3
— Собираем CDR: время, длительность, номера, статус (mod_cdr_csv, mod_cdr_pg_csv)
— Сохраняем CDR в PostgreSQL для анализа
— Строим дашборд: количество звонков, среднее время ожидания, SLA

Чему учимся: медиа-хранилище, форматы записи, CDR-схемы, аналитика звонков, отчёты по качеству.

Разблокируем: мониторинг качества, аналитика

ФАЗА 8
Масштабирование

Добавляем Kamailio как SIP-прокси, кластеризуем FreeSWITCH и подключаем Homer для мониторинга.

Что делаем:
— Устанавливаем Kamailio перед FreeSWITCH: балансировка, маршрутизация, защита
— Кластер FreeSWITCH: несколько инстансов для отказоустойчивости и нагрузки
— Homer SIP Capture — сбор и визуализация SIP-трафика для отладки
— Health checks, автоматический failover, мониторинг (Prometheus + Grafana)

Чему учимся: SIP-проксирование, балансировка нагрузки, высокая доступность (HA), SIP capture, продакшен-мониторинг.

Разблокируем: продакшен-система

Каждая фаза самодостаточна

Вы можете остановиться после любой фазы и иметь полностью рабочую систему. Каждая следующая фаза строится на предыдущей, добавляя новые возможности. Начните с Фазы 1 — первый звонок можно совершить за один вечер.

07

Реализация FreeSWITCH

Детальный план реализации FreeSWITCH (Слой 4: Media) — 8 фаз от базового звонка до WebRTC. Миграция с MirtaPBX (Asterisk). ~400 concurrent calls в EU, полный call-center.

Что такое FreeSWITCH

FreeSWITCH — программная телефонная станция

FreeSWITCH — это soft-switch (программный коммутатор). Если раньше телефонные станции были огромными шкафами с оборудованием, то FreeSWITCH — это то же самое, но в виде программы на Linux-сервере.

Представьте коммутатор из кинофильма — оператор втыкает провода, соединяя абонентов. FreeSWITCH делает то же самое, только программно и автоматически.

Open Source Написан на C Создан в 2006 Автор: Anthony Minessale (ex-Asterisk)

Ключевая концепция: B2BUA (Back-to-Back User Agent)

FreeSWITCH — это B2BUA. Он не просто пропускает звонок насквозь (как прокси). Он полностью разрывает звонок на две половинки:

  Телефон A              FreeSWITCH              Телефон B
      │                       │                       │
      │──── SIP Leg A ──────│                       │
      │   (отдельный звонок)  │                       │
      │                       │──── SIP Leg B ──────│
      │                       │   (отдельный звонок)  │
      │                       │                       │
      │◄═══ Голос (RTP) ════►│◄═══ Голос (RTP) ════►│

Leg A — звонок между телефоном A и FreeSWITCH.
Leg B — звонок между FreeSWITCH и телефоном B.

Это даёт полный контроль: между двумя ногами FreeSWITCH может делать что угодно — записывать, транскодировать, подмешивать аудио, ставить на удержание, переводить, подключать третьего участника.

Аналогия: Прокси vs B2BUA

Прокси (Kamailio) — это почтальон, который передаёт конверт не открывая. Быстро, массово, но не может изменить содержимое.
B2BUA (FreeSWITCH) — это секретарь, который читает письмо, может переписать, перенаправить, скопировать, добавить приложение.

Из чего состоит FreeSWITCH
  ┌──────────────────────────────────────────────────────────────┐
  │                      FreeSWITCH                              │
  │                                                              │
  │  ┌────────────────────────────────────────────────────────┐  │
  │  │                     ЯДРО (Core)                        │  │
  │  │                                                         │  │
  │  │  ┌──────────────┐ ┌──────────────┐ ┌────────────────┐  │  │
  │  │  │ State Machine│ │ Event System │ │Channel Engine │  │  │
  │  │  │ жизненный    │ │ все события  │ │ управление     │  │  │
  │  │  │ цикл канала  │ │ pub/sub      │ │ звонками       │  │  │
  │  │  └──────────────┘ └──────────────┘ │ Leg A ↔ Leg B  │  │  │
  │  │                                     └────────────────┘  │  │
  │  │  ┌──────────────┐ ┌──────────────┐ ┌────────────────┐  │  │
  │  │  │ Codec Engine │ │ Timer System │ │ Memory Pool   │  │  │
  │  │  │ перекодиров. │ │              │ │ управление     │  │  │
  │  │  │ Opus↔G.711   │ │              │ │ памятью        │  │  │
  │  │  └──────────────┘ └──────────────┘ └────────────────┘  │  │
  │  └───────────────────────┬────────────────────────────────┘  │
  │                          │ Module API                        │
  │  ┌───────────────────────┼────────────────────────────────┐  │
  │  │                МОДУЛИ (~200)                            │  │
  │  │                                                         │  │
  │  │  Endpoints       Applications      Codecs             │  │
  │  │  mod_sofia(SIP)  mod_dptools       mod_opus            │  │
  │  │  mod_verto(WS)   mod_callcenter    mod_g711            │  │
  │  │  mod_skinny       mod_conference    mod_g729            │  │
  │  │                   mod_voicemail     mod_g722            │  │
  │  │                                                         │  │
  │  │  Events          Languages         Formats            │  │
  │  │  mod_event_socket mod_lua           mod_shout(MP3)     │  │
  │  │  mod_json_cdr     mod_python        mod_sndfile(WAV)   │  │
  │  │                   mod_v8(JS)                            │  │
  │  │                                                         │  │
  │  │  Config          TTS / ASR                              │  │
  │  │  mod_xml_curl     mod_flite                             │  │
  │  │  mod_dialplan_xml mod_tts_commandline                   │  │
  │  │  mod_dialplan_lua                                       │  │
  │  └─────────────────────────────────────────────────────────┘  │
  │                                                              │
  │  ┌─────────────────────────────────────────────────────────┐  │
  │  │              КОНФИГУРАЦИЯ                               │  │
  │  │  Directory (кто)  Dialplan (куда)  SIP Profiles (как)  │  │
  │  └─────────────────────────────────────────────────────────┘  │
  └──────────────────────────────────────────────────────────────┘
Ядро (Core) — мозг FreeSWITCH

Написано на C. Это фундамент, который нельзя менять — на него нанизываются модули.

КомпонентЧто делает
Channel Engine Управляет звонками. Каждый звонок = «канал» (channel) с уникальным UUID. Канал проходит состояния: NEW → INIT → ROUTING → EXECUTE → EXCHANGE_MEDIA → HANGUP
Event System Внутренняя шина событий. Всё что происходит — звонок создан, DTMF нажата, запись начата — генерирует событие. ESL подписывается на эти события извне
State Machine Жизненный цикл каждого канала. Чёткие переходы между состояниями. Гарантирует что ресурсы освобождаются при завершении звонка
Codec Engine Перекодирование аудио на лету: Opus ↔ G.711 ↔ G.729. Если два телефона говорят на разных кодеках — ядро транскодирует прозрачно
Memory Pool Управление памятью. Каждый звонок получает свой пул — когда звонок завершается, вся память освобождается одним блоком (нет утечек)
🔌 Модули FreeSWITCH — подробная классификация

Endpoint-модули — как FreeSWITCH общается с внешним миром:

МодульПротоколЗачем
mod_sofiaSIP (UDP/TCP/TLS/WSS)Основной. 95% звонков идут через него
mod_vertoWebSocket JSON-RPCWebRTC из браузера (собственный протокол FS)
mod_skinnyCisco SCCPАппаратные Cisco-телефоны
mod_freetdmTDM / ISDNПодключение к аналоговым линиям (legacy)

Application-модули — что делать со звонком:

МодульЗачем
mod_dptoolsБазовые инструменты: answer, bridge, playback, record, transfer, hold, park, intercept
mod_callcenterОчереди (ACD): стратегии распределения, агенты, тиры, музыка ожидания
mod_conferenceКонференц-звонки: комнаты, PIN, mute/unmute, запись
mod_voicemailГолосовая почта: greeting, хранение, уведомления, MWI
mod_avmdОпределение автоответчика (Answering Machine Detection)
mod_fifoПростая парковка звонков (First In, First Out)

Codec-модули — как кодировать голос:

МодульКодекБитрейтКогда
mod_opusOpus6-510 kbps (адаптивный)WebRTC, внутренние звонки (лучшее качество)
встроенG.711 (PCMU/PCMA)64 kbpsPSTN-транки, базовая совместимость
mod_g729G.7298 kbpsЭкономия трафика (WAN, мобильные)
mod_g722G.72264 kbpsHD Voice (wideband)

Language-модули — скриптовые языки для dialplan:

МодульЯзыкДля чего
mod_luaLuaРекомендуем Быстрый, встроенный, IVR-логика, HTTP-запросы из dialplan
mod_pythonPythonСложная логика, ML/AI интеграции
mod_v8JavaScript (V8)Для тех кто знает JS

Event/CDR-модули — связь с внешним миром:

МодульЗачем
mod_event_socketESL — Go подключается и управляет FS программно (порт 8021)
mod_json_cdrОтправляет CDR (детализацию) по HTTP в Go API после каждого звонка
mod_xml_curlFS запрашивает конфигурацию по HTTP у Go API (динамические пользователи, dialplan, очереди)
Три раздела конфигурации
РазделЧто определяетАналогияИсточник
Directory Кто может звонить. Список SIP-аккаунтов с паролями и настройками Телефонная книга компании XML или mod_xml_curl (из БД)
Dialplan Куда маршрутизировать звонок. Правила: «если набрали 1XXX → bridge к user/1XXX» Правила коммутатора: «этот провод соединить с тем» XML, Lua или mod_xml_curl
SIP Profiles На каких IP/портах слушать SIP, какие кодеки, TLS, NAT Какие «двери» открыты для звонков XML (статический, меняется редко)
Жизненный цикл звонка в FreeSWITCH
  Телефон A набирает 1002
         │
         ▼
  [1] CHANNEL_CREATE   ← FS создаёт канал (Leg A), UUID: abc-123
         │
         ▼
  [2] ROUTING          ← Ищет в Dialplan: что делать с номером 1002?
         │                 Нашёл: bridge user/1002[3] CHANNEL_CREATE  ← Создаёт второй канал (Leg B), UUID: def-456
         │                 Звонит на телефон 1002
         ▼
  [4] RINGING         ← Телефон 1002 звонит (180 Ringing)
         │
         ▼
  [5] CHANNEL_ANSWER  ← Телефон 1002 поднят (200 OK)
         │
         ▼
  [6] EXCHANGE_MEDIA  ← Голос идёт: A → FS → B и B → FS → A
         │                 Здесь: запись, транскодирование, DTMF
         │
         │  ... разговор ...
         │
         ▼
  [7] CHANNEL_HANGUP  ← Кто-то повесил трубку (BYE)
         │
         ▼
  [8] CDR              ← Запись о звонке → Go API → PostgreSQL
         │
         ▼
  [9] DESTROY          ← Каналы уничтожены, память освобождена
FreeSWITCH ДЕЛАЕТ vs НЕ ДЕЛАЕТ
FreeSWITCH ДЕЛАЕТFreeSWITCH НЕ ДЕЛАЕТ (это другие компоненты)
Принимает/делает SIP-звонкиМаршрутизация тысяч SIP-запросов → Kamailio
IVR, очереди, конференцииВеб-интерфейс → React
Запись звонковREST API для пользователей → Go API
Транскодирование кодековХранение данных → PostgreSQL / ClickHouse
DTMF-обработкаБалансировка нагрузки между серверами → Kamailio
Голосовая почта, TTS, ASRNAT traversal для медиа → RTPEngine
Мост между двумя абонентамиAnti-DDoS, topology hiding → SBC (Kamailio)
Автообзвон (по команде из Go)Бизнес-логика, кампании → Go API
Разделение ответственности

FreeSWITCH = медиа-сервер. Он обрабатывает звонки: принимает, играет аудио, записывает, соединяет, ставит в очередь.
Kamailio = SIP-прокси. Он маршрутизирует SIP быстро и массово, но не трогает медиа (голос).

Вместе: Kamailio решает куда направить звонок, FreeSWITCH решает что с ним делать.

Способы конфигурации FreeSWITCH (XML, HTTP API, Lua, ESL)
СпособКак работаетДля чего
Статический XML Файлы на диске. Изменения → reloadxml в CLI SIP-профили, глобальные параметры — то, что меняется редко
mod_xml_curl Рекомендуем FS запрашивает конфигурацию по HTTP у Go API. Go отвечает XML из PostgreSQL Пользователи, очереди, dialplan — всё динамическое, из БД
mod_lua Lua-скрипт в dialplan вместо XML. Может ходить в Redis, HTTP API Сложная IVR-логика, маршрутизация по данным из БД
ESL (Event Socket) Go подключается к FS (порт 8021) и управляет звонками программно originate, transfer, hold, record — всё в реальном времени
mod_callcenter API ESL-команды для управления очередями и агентами без перезагрузки Добавить/удалить агента, сменить статус, создать очередь

Для enterprise-системы: XML остаётся только для статики (SIP-профили, модули, ESL). Всё остальное — динамически через mod_xml_curl + Go API + PostgreSQL.

  ┌──────────────────────────────────────────────┐
  │                 Go API                        │
  │                    │                          │
  │          PostgreSQL (users, queues,           │
  │          dialplan rules, IVR trees)           │
  └─────────────┬──────────────┬─────────────────┘
                │              │
    ┌───────────┼──────────────┼──────────────┐
    │           │              │              │
    ▼           ▼              ▼              │
  mod_xml_curl  mod_lua       ESL inbound    │
  «кто user?    dialplan     originate,      │
   какие        логика на    transfer,       │
   очереди?»    Lua + HTTP   uuid_kill...    │
    │           │              │              │
    └───────────┼──────────────┘              │
                │                             │
          FreeSWITCH                          │
                │                             │
          Статический XML:                    │
          • sip_profiles/internal.xml         │
          • modules.conf.xml                  │
          • event_socket.conf.xml             │
  ┌───────────────────────────────────────────┘
Контекст миграции

Откуда: MirtaPBX (на базе Asterisk) — полный call-center: IVR, очереди, запись, автообзвон, супервизор, WebRTC.
Куда: FreeSWITCH — в составе enterprise-архитектуры (Kamailio SBC → Kamailio Core → FreeSWITCH → Go API).
Почему: масштабирование (43 страны), полный контроль, стоимость лицензий.
Подход: «снизу вверх» — на каждой фазе работающая система, постепенное наращивание функционала.

Обзор: 8 фаз реализации
  ФАЗА 1          ФАЗА 2         ФАЗА 3         ФАЗА 4
  Базовый         ESL              IVR             Очереди
  звонок          управление       меню            ACD
  ┌─────────┐     ┌─────────┐     ┌─────────┐     ┌─────────┐
  │ mod_sofia│────▶│mod_event│────▶│ mod_lua │────▶│mod_call │
  │ dialplan │     │ _socket │     │ DTMF    │     │ center  │
  │ bridge   │     │ Go ← FS │     │ TTS     │     │ агенты  │
  └─────────┘     └─────────┘     └─────────┘     └─────────┘
       │               │               │               │
       ▼               ▼               ▼               ▼
  2 телефона      Go управляет    Входящие       Распределение
  звонят друг     звонками        попадают       по операторам
  другу           программно      в меню         автоматически

  ФАЗА 5          ФАЗА 6         ФАЗА 7         ФАЗА 8
  Запись           Конференции     Автообзвон      WebRTC
  + CDR            + Voicemail     (Dialer)        + Видео
  ┌─────────┐     ┌─────────┐     ┌─────────┐     ┌─────────┐
  │record_  │────▶│mod_conf │────▶│ Go ESL  │────▶│mod_verto│
  │ session │     │mod_voice│     │originate│     │ SIP.js  │
  │json_cdr │     │ mail    │     │ AMD     │     │ SRTP    │
  └─────────┘     └─────────┘     └─────────┘     └─────────┘
       │               │               │               │
       ▼               ▼               ▼               ▼
  Все звонки      Комнаты с       Обзвон          Браузер =
  записываются    PIN, голосовая  списков          телефон
  CDR → PostgreSQL почта          автоматически    оператора

Фаза 1: Установка + базовый звонок

Цель Два SIP-телефона звонят друг другу через FreeSWITCH

Самый минимум: FreeSWITCH запущен, два устройства зарегистрированы, можно позвонить. Это фундамент, на котором строится всё остальное.

Что делаем

1. Docker-образ FreeSWITCH

Собираем из исходников (Debian-based) — чтобы включить только нужные модули. Ванильный пакет содержит ~200 модулей, нам нужно ~30.

2. SIP-профили (mod_sofia)

internal (порт 5060) — для внутренних устройств (телефоны, веб-клиенты).
external (порт 5080) — для SIP-транков (пока пустой, пригодится в будущем).

3. User Directory (XML)

Регистрация SIP-аккаунтов: 1001, 1002 — с паролями, доменом, настройками кодеков.

4. Dialplan (XML)

Минимальный: набираем 1001 → bridge к устройству 1001. Набираем 1002 → bridge к 1002.

5. Кодеки

G.711 (PCMU/PCMA) — базовый, работает везде. Opus — для будущего WebRTC.

Ключевые модули
МодульЗачем
mod_sofiaSIP-стек FreeSWITCH. Регистрация, приём/отправка SIP
mod_dptoolsИнструменты dialplan: bridge, answer, hangup, playback
mod_dialplan_xmlПарсинг XML-диалплана
mod_commandsCLI-команды (sofia status, show channels)
mod_consoleКонсоль для отладки (fs_cli)
mod_logfileЛогирование в файл
💻 Пример конфигурации: SIP-профиль + User + Dialplan

SIP-профиль internal (sip_profiles/internal.xml):

<!-- Внутренний SIP-профиль для устройств --> <profile name="internal"> <settings> <param name="sip-ip" value="$${local_ip_v4}"/> <param name="sip-port" value="5060"/> <param name="rtp-ip" value="$${local_ip_v4}"/> <param name="codec-prefs" value="OPUS,PCMU,PCMA"/> <param name="inbound-codec-negotiation" value="generous"/> <param name="auth-calls" value="true"/> </settings> </profile>

User Directory (directory/default/1001.xml):

<user id="1001"> <params> <param name="password" value="$${default_password}"/> <param name="vm-password" value="1001"/> </params> <variables> <variable name="toll_allow" value="domestic,international,local"/> <variable name="accountcode" value="1001"/> <variable name="effective_caller_id_name" value="Extension 1001"/> <variable name="effective_caller_id_number" value="1001"/> </variables> </user>

Dialplan (dialplan/default.xml):

<extension name="internal_calls"> <!-- Любой 4-значный номер → bridge к этому устройству --> <condition field="destination_number" expression="^(10\d{2})$"> <action application="bridge" data="user/$1@$${domain}"/> </condition> </extension>
Проверка

Установите MicroSIP (Windows) или Orologio (Mac). Зарегистрируйте 1001 и 1002. С 1001 наберите 1002 — должен пойти звонок. Если слышите голос — Фаза 1 готова.

Фаза 2: ESL — программное управление

Цель Go-сервис подключается к FreeSWITCH и управляет звонками программно

ESL (Event Socket Library) — протокол FreeSWITCH для внешнего управления. Через него Go-backend может создавать звонки, слушать события, переводить, вешать трубку — всё программно, без изменения XML-конфигурации.

Два режима ESL

Inbound (основной) — Go подключается к FreeSWITCH на порт 8021. Может отправлять команды и подписываться на события. Один Go-сервис управляет всем FS.

Outbound — FreeSWITCH сам подключается к Go при входящем звонке. Каждый звонок = отдельное TCP-соединение. Используется для сложной логики dialplan на стороне Go.

Рекомендация Начинаем с Inbound — проще, Go всегда online, одно соединение.

Ключевые ESL-команды
КомандаЧто делает
originateИнициировать звонок программно (позвонить на номер)
uuid_bridgeСоединить два активных канала
uuid_transferПеревести звонок на другой номер/extension
uuid_killПовесить трубку на конкретном канале
uuid_holdПоставить на удержание
uuid_recordНачать/остановить запись
uuid_setvarУстановить переменную на канале
Ключевые события (Events)

Go подписывается на события FreeSWITCH и реагирует в реальном времени:

СобытиеКогдаЧто делаем в Go
CHANNEL_CREATEНовый звонок появилсяСоздаём запись в Redis (active call)
CHANNEL_ANSWERАбонент поднял трубкуОбновляем статус, начинаем таймер
CHANNEL_HANGUPЗвонок завершёнCDR → PostgreSQL, очищаем Redis
DTMFНажата клавишаОбработка IVR, перевод по DTMF
RECORD_START/STOPЗапись началась/кончиласьМетаданные записи → БД
CUSTOM callcenter::infoСобытие очередиОбновляем dashboard супервизора
💻 Пример: Go ESL-клиент (inbound)
// Go: подключение к FreeSWITCH через ESL (inbound) // Библиотека: github.com/perorin/goeslern или cgrates/fsock package main import ( "fmt" "log" esl "github.com/cgrates/fsock" ) func main() { // Подключаемся к FreeSWITCH ESL conn, err := esl.NewFSock( "127.0.0.1:8021", // FS ESL адрес "ClueCon", // пароль (дефолтный) 10, // reconnect attempts eventHandlers, // map[string][]func(string, int) eventFilters, // какие события слушать logger, ) // Инициируем звонок программно // Звоним на 1001, при ответе — bridge к 1002 reply, _ := conn.SendApiCmd( "originate user/1001 &bridge(user/1002)", ) fmt.Println("Originate result:", reply) // Слушаем события conn.ReadEvents() } // Обработчик событий var eventHandlers = map[string][]func(string, int){ "CHANNEL_ANSWER": {func(body string, _ int) { log.Printf("Звонок отвечен: %s", body) }}, "CHANNEL_HANGUP": {func(body string, _ int) { log.Printf("Звонок завершён: %s", body) }}, }
💻 Конфигурация ESL + Lua в FreeSWITCH

Event Socket (autoload_configs/event_socket.conf.xml):

<configuration name="event_socket.conf"> <settings> <param name="listen-ip" value="0.0.0.0"/> <param name="listen-port" value="8021"/> <param name="password" value="your_secure_password"/> <!-- ВАЖНО: в production — только внутренний IP, не 0.0.0.0 --> </settings> </configuration>

Lua-скрипт (scripts/incoming_call.lua) — пример сложной логики:

-- Lua-скрипт для обработки входящего звонка local caller = session:getVariable("caller_id_number") local destination = session:getVariable("destination_number") freeswitch.consoleLog("INFO", "Звонок от " .. caller .. " на " .. destination .. "\n") -- Проверяем в Redis — VIP-клиент? -- Если да → приоритетная очередь -- Если нет → обычная очередь session:answer() session:execute("playback", "ivr/welcome.wav") session:execute("transfer", "5000 XML default") -- → IVR
Проверка

Go-сервис запускается, подключается к FS по ESL, вызывает originate — телефон 1001 звонит. Go получает события CHANNEL_CREATE → CHANNEL_ANSWER → CHANNEL_HANGUP. Если это работает — Фаза 2 готова.

Фаза 3: IVR (Interactive Voice Response)

Цель Входящий звонок попадает в голосовое меню, клиент выбирает опцию по DTMF

IVR — это «голосовой робот», который отвечает: «Нажмите 1 для продаж, 2 для поддержки, 3 для бухгалтерии». В FreeSWITCH реализуется через Lua-скрипты + аудиофайлы + play_and_get_digits.

Что делаем

1. Аудиофайлы

Приветствие, пункты меню, ожидание, "Пожалуйста подождите", "Все операторы заняты". Формат: WAV 16kHz mono (для качества) или 8kHz (для совместимости).

2. IVR-дерево на Lua

Многоуровневое меню: Главное → Подменю → Действие. Вся логика в Lua-скриптах.

3. DTMF-обработка

RFC 2833 (основной), SIP INFO (fallback). play_and_get_digits — играет аудио и ждёт нажатия.

4. Таймауты и fallback

Не нажал ничего за 5 сек → повторяем. 3 неудачных попытки → перевод на оператора.

5. TTS (Text-to-Speech)

Для динамических фраз: "Ваш номер в очереди — пять". mod_flite (английский) или mod_tts_commandline + Piper (русский).

Типичное IVR-дерево
        Входящий звонок
              │
        «Здравствуйте!»
        «Нажмите 1, 2 или 3»
              │
     ┌────────┼────────┐
     │        │        │
   [1]      [2]      [3]
  Продажи  Поддержка Бухгалтерия
     │        │        │
     │     ┌──┼──┐     │
     │    [1]  [2]    │
     │   Тех. Возврат  │
     │   отдел         │
     ▼        ▼        ▼
  Очередь  Очередь  Extension
  sales    support   3001
💻 Пример: IVR на Lua
-- scripts/ivr_main.lua — Главное IVR-меню session:answer() session:sleep(500) local max_retries = 3 local retry = 0 while retry < max_retries do -- play_and_get_digits(min, max, tries, timeout, terminators, -- audio_file, bad_input_file, var_name, regex, digit_timeout) local digit = session:playAndGetDigits( 1, 1, 1, 5000, "#", "ivr/main_menu.wav", -- «Нажмите 1, 2 или 3» "ivr/invalid_choice.wav", -- «Неверный выбор» "ivr_choice", "[123]", -- допустимые цифры 3000 ) if digit == "1" then -- Продажи → очередь sales session:execute("transfer", "5001 XML default") return elseif digit == "2" then -- Поддержка → подменю session:execute("lua", "ivr_support.lua") return elseif digit == "3" then -- Бухгалтерия → extension напрямую session:execute("transfer", "3001 XML default") return else retry = retry + 1 end end -- 3 неудачных попытки → оператор session:execute("playback", "ivr/connecting_operator.wav") session:execute("transfer", "5000 XML default")
Модули Фазы 3
mod_dptools play_and_get_digits, playback, transfer   mod_lua Lua-скрипты для IVR-логики   mod_flite TTS (английский)   mod_tts_commandline TTS через внешнюю команду (Piper для русского)   mod_say_ru Озвучивание чисел и дат на русском
Проверка

Звоним на номер 5000 (IVR) → слышим приветствие → нажимаем 1 → попадаем в очередь продаж. Нажимаем 2 → подменю поддержки. Ничего не нажимаем → через 3 попытки переводят на оператора. Если всё работает — Фаза 3 готова.

Фаза 4: Очереди (ACD — Automatic Call Distribution)

Цель Звонки распределяются по операторам с учётом стратегии, навыков, загрузки

ACD — ядро call-центра. Клиент ждёт в очереди, система выбирает лучшего свободного оператора и соединяет. В FreeSWITCH — mod_callcenter.

Стратегии распределения
СтратегияКак работаетКогда использовать
longest-idle-agentЗвонок идёт оператору, который дольше всех свободенСамая справедливая. Рекомендуем
round-robinПо кругу: 1→2→3→1→2→3Равная загрузка
ring-allЗвонят всем одновременно, кто первый поднялМаленькие команды
top-downСначала первому, если не ответил → второмуПриоритетные агенты
agent-with-least-talk-timeКто меньше всего наговорил за сменуРавная нагрузка по минутам
Что делаем

Очереди: sales, support, billing — каждая со своей стратегией, MOH, таймаутами.

Агенты: статусы — Available, On Break, Logged Out. Тиры (уровни приоритета): tier 1 звонят первыми, если все в tier 1 заняты → tier 2.

Музыка ожидания (MOH): mod_local_stream — разные плейлисты для разных очередей.

Announcements: «Вы 3-й в очереди», «Примерное время ожидания — 2 минуты».

Callback: клиент нажимает * → оставляет номер → система перезвонит когда оператор освободится.

Overflow: очередь переполнена (>20 ждущих) → перевод в другую очередь / voicemail / IVR.

💻 Конфигурация mod_callcenter

Очередь (autoload_configs/callcenter.conf.xml):

<queue name="sales@default"> <param name="strategy" value="longest-idle-agent"/> <param name="moh-sound" value="local_stream://moh_sales"/> <param name="max-wait-time" value="300"/> <!-- 5 мин макс --> <param name="max-wait-time-with-no-agent" value="30"/> <!-- если агентов нет --> <param name="tier-rules-apply" value="true"/> <param name="tier-rule-wait-second" value="30"/> <!-- 30с → следующий tier --> <param name="announce-sound" value="ivr/queue_position.wav"/> <param name="announce-frequency" value="30"/> </queue>

Агенты:

<agent name="agent-1001" type="callback" contact="user/1001@default" status="Available" max-no-answer="3" wrap-up-time="10" reject-delay-time="15"/>

Тиры:

<tier agent="agent-1001" queue="sales@default" level="1" position="1"/> <tier agent="agent-1002" queue="sales@default" level="1" position="2"/> <tier agent="agent-2001" queue="sales@default" level="2" position="1"/> <!-- level 2 подключается если все level 1 заняты 30+ сек -->

ESL-события от очереди (Go получает в реальном времени):

// Go получает события mod_callcenter через ESL // Event: CUSTOM callcenter::info // CC-Action: member-queue-start ← клиент вошёл в очередь // CC-Action: agent-offering ← звоним оператору // CC-Action: bridge-agent-start ← разговор начался // CC-Action: bridge-agent-end ← разговор окончен // CC-Action: member-queue-end ← клиент покинул очередь
Проверка

Звоним на 5001 (очередь sales) → слышим музыку ожидания → «Вы 1-й в очереди» → телефон оператора 1001 звонит → оператор поднимает → разговор. Go получает все события через ESL. Если работает — Фаза 4 готова.

Фаза 5: Запись + CDR

Цель Все звонки записываются, CDR (детализация) сохраняются в PostgreSQL

Запись нужна для контроля качества, обучения, compliance (GDPR требует уведомлять). CDR — для аналитики: сколько звонков, средняя длительность, пропущенные, загрузка агентов.

Запись звонков

record_session — записывает весь звонок от начала до конца, включая переводы.

Двуканальная запись (stereo): оператор в левом канале, клиент в правом. Это критично для speech analytics — позволяет анализировать каждого отдельно.

Формат: WAV (для качества) → конвертация в MP3 (mod_shout) для хранения. Или сразу MP3.

Хранение: локальный диск → Go API загружает в MinIO (S3). Путь в PostgreSQL.

Условная запись: только очереди, только внешние, по расписанию, по согласию клиента.

GDPR: уведомление «Разговор записывается» (playback перед записью), автоудаление через N дней.

CDR (Call Detail Records)

mod_json_cdr — после каждого звонка FreeSWITCH отправляет JSON по HTTP POST в Go API.

Что содержит CDR:

caller_id кто звонил
destination куда звонил
start_time начало звонка
answer_time когда ответили
end_time конец звонка
duration общая длительность
billsec оплачиваемое время (от answer)
hangup_cause причина завершения
recording_path путь к записи
queue_name очередь
agent оператор

Go API → PostgreSQL → таблица cdr.

Модули Фазы 5
mod_dptools record_session   mod_json_cdr CDR в JSON по HTTP   mod_shout запись в MP3   mod_cdr_pg_csv альтернатива: CDR напрямую в PostgreSQL
Архитектура хранения CDR: PostgreSQL + ClickHouse + Elasticsearch

При 400+ concurrent calls CDR быстро растут до миллионов записей. PostgreSQL одна не справится с аналитическими запросами на таких объёмах. Решение — тройной стек: каждая БД делает то, в чём она лучшая.

Поток данных CDR
  FreeSWITCH
       │
       │ JSON CDR (HTTP POST после каждого звонка)
       ▼
  Go API ──────────────────────────────────────────────
       │                    │                        │
       │ sync (мгновенно)async (батчами)async (батчами)
       ▼                    ▼                        ▼
  ┌─────────────┐   ┌──────────────┐   ┌──────────────────┐
  │ PostgreSQL   │   │ ClickHouse    │   │ Elasticsearch     │
  │             │   │              │   │                  │
  │ «Горячие»   │   │ ВСЯ история  │   │ Полнотекстовый   │
  │ последние   │   │ за годы      │   │ поиск            │
  │ 7-30 дней   │   │              │   │                  │
  │             │   │ Аналитика:   │   │ «Найди звонок    │
  │ JOIN с      │   │ агрегации,   │   │  с +4917...»     │
  │ users,      │   │ отчёты,      │   │ «Все звонки      │
  │ queues,     │   │ дашборды     │   │  агента Иванов»  │
  │ agents      │   │              │   │ Fuzzy match      │
  │             │   │ Retention:   │   │                  │
  │ TTL: 30 дней│   │ 3-5 лет      │   │ TTL: 90 дней     │
  │ затем →     │   │ сжатие 5-10x │   │ (горячий индекс) │
  │ удаляется   │   │              │   │                  │
  └─────────────┘   └──────────────┘   └──────────────────┘
       │                    │                        │
       ▼                    ▼                        ▼
  Оперативная          Grafana /               Kibana /
  работа               BI-отчёты               поиск в UI
PostgreSQL — оперативные данные

Роль: основная БД для «горячих» данных последних 7-30 дней. Здесь хранятся текущие смены, активные агенты, незакрытые звонки.

Почему PG: нужны JOIN-ы с таблицами users, queues, agents, tenants. Транзакции, целостность данных. ACID.

Что хранит:

cdr — CDR за последние 30 дней
active_calls — текущие звонки (Redis лучше, но PG как backup)
agent_sessions — рабочие смены агентов
queue_stats — текущая статистика очередей

TTL: cron-задача или pg_partman — автоудаление записей старше 30 дней (они уже в ClickHouse).

Оптимизации:

• Партиционирование по дате (1 партиция = 1 день)
• Индексы: caller_id, callee, start_time, agent_id, queue_name
• BRIN-индексы на start_time (эффективно для диапазонов дат)

ClickHouse — аналитика и история

Роль: вся история CDR за годы. Аналитические запросы, отчёты, дашборды.

Почему ClickHouse: колоночная СУБД — запросы вида «средняя длительность по очередям за Q4 2025» выполняются за 0.1-0.5 сек на 100M+ записей (PostgreSQL: 15-30 сек).

Что хранит: полную копию всех CDR за 3-5 лет. Сжатие 5-10x (100M CDR ≈ 8-15 GB).

Типичные запросы:

• «Все звонки за март в Германии» — 0.2 сек
• «Средняя длительность по очередям за квартал» — 0.1 сек
• «Top-10 агентов по количеству звонков» — 0.05 сек
• «Почасовая нагрузка за последний год» — 0.3 сек
• «Стоимость звонков по странам за месяц» — 0.1 сек

Запись: Go отправляет CDR батчами (каждые 5 сек или 1000 записей) через HTTP-интерфейс ClickHouse.

Elasticsearch — поиск

Роль: мгновенный поиск CDR по любому полю. Супервизор вводит номер телефона — за 50мс видит все звонки.

Почему Elastic:

Full-text search — поиск по фрагменту номера: «+4917» → все звонки с номерами начинающимися на +4917
Fuzzy matching — ошибся на цифру? Всё равно найдёт
Агрегации в реальном времени — «сколько звонков за последний час по каждой очереди»
Kibana — готовые дашборды без написания кода

Что индексируем:

caller_id callee agent_name queue_name hangup_cause direction country tenant_id

TTL: ILM-политика — горячий индекс (7 дней, SSD) → тёплый (30 дней) → удаление (90 дней). Для долгосрочного хранения — ClickHouse.

Кто что делает — сравнение
ЗадачаКтоСкорость
Текущие звонки агентаPostgreSQL<10 мс
CDR с JOIN users/queuesPostgreSQL<50 мс (30 дней)
«Все звонки за Q4 по Германии»ClickHouse0.1-0.5 сек
«Среднее время ожидания за год»ClickHouse0.05-0.2 сек
Ежемесячный отчёт (PDF)ClickHouse1-3 сек
«Найди звонок с +4917612...»Elasticsearch<50 мс
«Все звонки агента Иванов»Elasticsearch<50 мс
Kibana дашборд (live)Elasticsearchreal-time
💻 Go API: запись CDR в три хранилища
// Go API — обработка CDR от FreeSWITCH // FreeSWITCH отправляет JSON POST на /api/v1/cdr func (h *CDRHandler) HandleCDR(w http.ResponseWriter, r *http.Request) { var cdr CDRRecord json.NewDecoder(r.Body).Decode(&cdr) // 1. PostgreSQL — синхронно (основная запись) h.pgRepo.InsertCDR(ctx, &cdr) // 2. ClickHouse — асинхронно (батчами) h.chBuffer.Add(cdr) // буфер, сбрасывается каждые 5 сек или 1000 записей // 3. Elasticsearch — асинхронно (батчами) h.esBuffer.Add(cdr) // bulk index API w.WriteHeader(http.StatusOK) } // ClickHouse buffer — flush каждые 5 сек func (b *CHBuffer) flushLoop() { ticker := time.NewTicker(5 * time.Second) for range ticker.C { if batch := b.Drain(); len(batch) > 0 { // INSERT INTO cdr VALUES (...), (...), (...) b.ch.ExecBatch(ctx, "INSERT INTO cdr", batch) } } }

ClickHouse — схема таблицы CDR:

CREATE TABLE cdr ( call_id String, start_time DateTime, answer_time Nullable(DateTime), end_time DateTime, duration UInt32, billsec UInt32, caller_id String, callee String, direction Enum8('inbound'=1, 'outbound'=2, 'internal'=3), hangup_cause String, queue_name String, agent_id String, tenant_id String, country LowCardinality(String), recording_url String, cost Decimal64(4) ) ENGINE = MergeTree() PARTITION BY toYYYYMM(start_time) -- партиция по месяцу ORDER BY (tenant_id, start_time) -- кластерный индекс TTL start_time + INTERVAL 5 YEAR -- автоудаление через 5 лет SETTINGS index_granularity = 8192;

Elasticsearch — индекс CDR:

// Elasticsearch index mapping { "mappings": { "properties": { "call_id": { "type": "keyword" }, "caller_id": { "type": "keyword", "fields": { "search": { "type": "search_as_you_type" }}}, "callee": { "type": "keyword", "fields": { "search": { "type": "search_as_you_type" }}}, "agent_name": { "type": "text" }, "queue_name": { "type": "keyword" }, "start_time": { "type": "date" }, "duration": { "type": "integer" }, "country": { "type": "keyword" }, "hangup_cause": { "type": "keyword" }, "tenant_id": { "type": "keyword" } } } }
📈 Примеры запросов: PostgreSQL vs ClickHouse vs Elasticsearch

«Текущие звонки агента 1001» → PostgreSQL (JOIN с users)

SELECT c.*, u.name AS agent_name FROM cdr c JOIN users u ON c.agent_id = u.id WHERE c.agent_id = '1001' AND c.start_time > now() - INTERVAL '1 day' ORDER BY c.start_time DESC; -- 5-10 мс на 30 дней данных

«Среднее время ожидания по очередям за Q4 2025» → ClickHouse

SELECT queue_name, avg(answer_time - start_time) AS avg_wait_sec, count() AS total_calls, countIf(hangup_cause = 'ORIGINATOR_CANCEL') AS abandoned FROM cdr WHERE start_time BETWEEN '2025-10-01' AND '2025-12-31' AND direction = 'inbound' GROUP BY queue_name ORDER BY avg_wait_sec DESC; -- 0.1 сек на 50M записей

«Стоимость звонков по странам за январь» → ClickHouse

SELECT country, count() AS calls, sum(billsec) / 60 AS total_minutes, sum(cost) AS total_cost, avg(cost) AS avg_cost_per_call FROM cdr WHERE toYYYYMM(start_time) = 202601 AND direction = 'outbound' GROUP BY country ORDER BY total_cost DESC; -- 0.2 сек на 100M+ записей

«Найди все звонки с номера +4917612...» → Elasticsearch

// Elasticsearch query — мгновенный поиск по фрагменту номера { "query": { "multi_match": { "query": "+4917612", "fields": ["caller_id.search", "callee.search"], "type": "bool_prefix" } }, "sort": [{ "start_time": "desc" }], "size": 50 } // 30-50 мс, результаты по мере ввода (search-as-you-type)
Порядок внедрения CDR-стека

Шаг 1: Начинаем только с PostgreSQL (Фаза 5 базовая). Это работает сразу.
Шаг 2: Когда CDR перевалят за 1-5M записей и запросы начнут тормозить — добавляем ClickHouse. Go пишет в оба хранилища.
Шаг 3: Когда супервизорам понадобится мгновенный поиск и Kibana-дашборды — добавляем Elasticsearch.

Не нужно внедрять всё сразу. Каждый компонент добавляется когда появляется реальная потребность.

Фаза 6: Конференции + Voicemail

Цель Конференц-звонки с PIN-кодом и голосовая почта

Конференции — для совещаний (несколько участников в одном звонке). Voicemail — когда оператор не ответил, клиент оставляет голосовое сообщение.

mod_conference

Создание комнат: набираем 8XXX → попадаем в конференцию 8XXX. Динамическое создание.

PIN-код: при входе вводится PIN для авторизации.

Управление: mute/unmute по DTMF (0 — mute себя), kick через ESL, запись конференции.

ESL: conference 8001 list — список участников. conference 8001 mute {member_id} — замутить.

Лимиты: максимум участников, автоокончание если остался 1.

mod_voicemail

Голосовая почта: если оператор не ответил за 20 секунд → «Оставьте сообщение после сигнала».

Greeting: персональное приветствие (запись своего) или стандартное.

Прослушивание: набираем *97 → вводим PIN → слушаем сообщения, удаляем, сохраняем.

Уведомление: email (SMTP) или webhook в Go API при новом сообщении.

MWI: индикатор на SIP-телефоне — мигающая лампочка «новое сообщение».

Модули Фазы 6
mod_conference конференц-звонки   mod_voicemail голосовая почта   mod_smtp email-уведомления

Фаза 7: Автообзвон (Dialer)

Цель Система автоматически обзванивает список номеров и соединяет с операторами

Вся логика дайлера — в Go, не в FreeSWITCH. FS только делает звонки по команде через ESL. Go управляет кампаниями, списками, стратегиями, пейсингом.

Три режима автообзвона
РежимКак работаетКогдаRatio
Preview Оператор видит карточку клиента на экране → сам нажимает «Позвонить» → Go отправляет ESL originate Сложные продажи, VIP-клиенты. Оператор готовится к звонку 1:1
Progressive Go ждёт свободного агента → originate на следующий номер из списка → при ответе bridge к агенту Стандартный исходящий обзвон. Агент не простаивает, но и не перегружен 1:1
Predictive Go оценивает average handle time + answer rate → originate заранее на N номеров одновременно → когда агент освободится, звонок уже ждёт Массовый обзвон. Максимальная эффективность, но есть риск «брошенных звонков» (клиент поднял, а агент ещё не освободился) 1.2–2:1
Логика Go-дайлера

Campaign: название, список номеров, режим, расписание, Caller ID, trunk.

Pacing: сколько одновременных originate. Predictive подстраивает автоматически.

Retry: не ответил → повторить через 30 мин, максимум 3 попытки.

Blacklist / DNC: проверка номера перед звонком (Do Not Call).

Расписание: звоним только 9:00–18:00 по часовому поясу клиента.

Статистика: answer rate, average handle time, abandonment rate → dashboard.

AMD (Answering Machine Detection)

mod_avmd — определяет, ответил человек или автоответчик.

Если автоответчик → оставляем голосовое сообщение (или вешаем трубку).

Если человек → bridge к оператору.

Точность: ~85-90%. Ложное срабатывание = потерянный клиент, поэтому лучше ошибиться в сторону «человек».

Альтернатива: не использовать AMD, а сразу bridge к агенту — агент сам разберётся. Проще, но агент тратит время на автоответчики.

Модули Фазы 7
mod_avmd Answering Machine Detection   ESL originate Go инициирует звонки   Вся логика в Go кампании, пейсинг, retry, DNC, расписание

Фаза 8: WebRTC + Видео

Цель Операторы работают через веб-телефон в браузере, видеозвонки

Финальная фаза — браузер становится полноценным телефоном. Оператору не нужен софтфон или аппаратный телефон — всё в веб-интерфейсе.

Два варианта WebRTC
Вариант A: mod_vertoВариант B: SIP over WSS (через Kamailio)
Как Собственный протокол FreeSWITCH — JSON-RPC поверх WebSocket. Клиент: verto.js Стандартный SIP через WebSocket (RFC 7118). Kamailio SBC терминирует WSS → UDP к FS. Клиент: SIP.js или JsSIP
Плюсы Проще настроить. Прямое подключение к FS. Больше контроля Стандартный протокол. Не привязан к FS. Kamailio SBC уже в архитектуре
Минусы Привязка к FreeSWITCH. Нестандартный протокол Сложнее: Kamailio WSS + RTPEngine + SRTP. Больше компонентов
Для нас Быстрый старт, proof of concept Production — SBC уже есть, стандартный SIP, масштабируемость

Рекомендация Начать с mod_verto (быстрый proof of concept), потом перейти на SIP over WSS через Kamailio для production.

Что делаем

1. mod_verto — включаем, настраиваем WSS (порт 8082, TLS обязателен для браузеров).

2. Кодеки — Opus (голос, обязателен для WebRTC), VP8/VP9 (видео).

3. SRTP — обязательно для WebRTC (DTLS-SRTP). Браузеры не поддерживают обычный RTP.

4. React-интеграция — вебтелефон встроен в панель оператора: кнопки звонок/hold/transfer/mute, номеронабиратель, статус.

5. Видео — mod_conference с видео (VP8), screen sharing через extended SDP.

Модули Фазы 8
mod_verto WebSocket JSON-RPC   mod_opus Opus-кодек (голос)   mod_vp8 VP8-кодек (видео)   mod_sofia SIP over WSS (Вариант B)   SIP.js клиентская SIP-библиотека (Вариант B)
Проверка

Открываем React-панель → нажимаем «Позвонить» на 1002 → браузер использует микрофон → звонок проходит → голос слышен в обе стороны. Если работает — все 8 фаз FreeSWITCH реализованы.

Сводка: все модули FreeSWITCH по фазам
ФазаМодулиРезультат
1mod_sofia, mod_dptools, mod_dialplan_xml, mod_commandsДва телефона звонят друг другу
2mod_event_socket, mod_lua, mod_json_cdrGo управляет звонками через ESL
3mod_lua, mod_flite, mod_tts_commandline, mod_say_ruВходящие попадают в голосовое меню
4mod_callcenter, mod_local_streamЗвонки распределяются по операторам
5mod_dptools (record_session), mod_json_cdr, mod_shoutЗапись + CDR в PostgreSQL
6mod_conference, mod_voicemailКонференции и голосовая почта
7mod_avmd + Go ESLАвтообзвон списков
8mod_verto / mod_sofia (WSS), mod_opus, mod_vp8Браузер = телефон
Каждая фаза = работающая система

После Фазы 1 — уже можно звонить. После Фазы 4 — уже полноценный call-center. После Фазы 8 — полный паритет с MirtaPBX (и больше). Миграцию пользователей можно начинать уже после Фазы 4-5.