Documentation

Un seul démon pour toute la chaîne.

Strowger remplace la pile de téléphonie hétéroclite — serveur SIP, pont média, scripts de liaison — par un processus Rust unique gérant la signalisation SIP, le flux média RTP (G.711) et la session IA en temps réel. Un binaire, un fichier de config, une API HTTP. Propulsé par le moteur sipvox.

Principes
  • Autonome. Utilisable par n'importe quel projet via son API HTTP. Zéro couplage avec le reste de votre infrastructure.
  • Maîtrise du média. Strowger gère l'intégralité du chemin RTP de bout en bout — rien à câbler entre les machines, aucune couche intermédiaire à surveiller. Toute une classe de bugs de plomberie disparaît par conception.
  • Souverain. Les CDR, enregistrements, transcriptions et la signalisation ne quittent jamais votre infrastructure. L'audio de la conversation est transmis à un seul tiers : le fournisseur de modèle que vous choisissez, avec vos propres clés et votre DPA.
  • Expertise audio intégrée (tampon de lecture, latence, barge-in, watchdog) — des mois de débogage en production sont ici des contraintes de conception, et non des problèmes à redécouvrir.
Architecture
Architecture Strowger : les clients HTTP pilotent le démon sipvoxd (couches httpd, contrôleur d'appel, sip, média, ia) jusqu'au trunk opérateur via SIP et RTP. Architecture Strowger : les clients HTTP pilotent le démon sipvoxd (couches httpd, contrôleur d'appel, sip, média, ia) jusqu'au trunk opérateur via SIP et RTP.

Un espace de travail Cargo composé de crates réutilisables — sip, media, ai, call, httpd — assemblées par le démon sipvoxd. Chaque crate est utilisable individuellement comme bibliothèque.

Démarrage rapide
# Compilation
cargo build --release

# Lancement (dev : une clé API vide désactive l'auth)
SIPVOX_CONFIG=/etc/sipvox/config.toml ./target/release/sipvoxd

# Test de santé
curl -s http://127.0.0.1:7060/health
Configuration

Un seul fichier TOML (chemin via SIPVOX_CONFIG), les secrets sont interpolés depuis l'environnement pour que le fichier reste versionnable :

[daemon]
http_host = "127.0.0.1"
http_port = 7060
public_ip = "203.0.113.10"        # IP publique du serveur (pas de STUN en v1)
data_dir  = "/var/lib/sipvox"     # SQLite, enregistrements
rtp_port_range = "22000-22999"

[ai.gemini]
api_key = "${SIPVOX_GEMINI_API_KEY}"
model   = "gemini-3.1-flash-live-preview"
playout_ms = 200

[[trunk]]
name           = "ovh-prod"
registrar      = "sip-domain.example"
outbound_proxy = "oaXXXXXX-ovh-1.sip-proxy.example"
username       = "0033XXXXXXXXX"
auth_username  = "0033XXXXXXXXX"
password       = "${SIPVOX_OVH_PASSWORD}"
number         = "+33XXXXXXXXX"
transport      = "udp"
register       = true             # false = trunk avec auth par IP (pas de REGISTER)

[[inbound_route]]
trunk   = "ovh-prod"
did     = "*184163651"            # +E164 exact | *suffixe | * fourre-tout
handler = { type = "webhook", url = "http://127.0.0.1:9100/api/voice/inbound?token=…" }
Appels entrants

Chaque INVITE entrant est autorisé et configuré par un unique webhook vers votre service (voir la référence API pour le mapping HTTP→SIP et le corps de la réponse 200). La réponse est séquencée pour que l'appelant n'entende jamais de blanc, et un backend IA hors ligne ne répond jamais — zéro facturation. Il n'y a pas de logique de rappel locale : votre service identifie l' appelant dans le webhook et reconnaît un rappel de son côté.

Média & qualité audio

Ce sont des exigences non négociables, apprises en production :

  • Tampon de lecture à l'émission ≥ 200 ms par défaut (voix saccadée = sous-alimentation du TTS).
  • Full-duplex avec barge-in ; seuil par défaut conservateur (500 ms est trop sensible).
  • Watchdog de silence : une légère "relance" après N secondes sans audio de l'IA.
  • TTFT mesuré et journalisé par appel ; alerte au-delà de 1,5 s (référence : 0,83 s).
  • Taille du prompt journalisée — la latence augmente avec la taille du prompt.
  • Plage RTP dédiée, jamais partagée — une collision ressemble à un "non-réponse" massif.
  • Codecs : PCMA + PCMU, trames de 20 ms ; le verrouillage RTP symétrique maintient le flux média entrant sans contournements fragiles. Enregistrement bidirectionnel natif → WAV mixé + par canal.
Backends IA

Un trait VoiceBackend isole le modèle. Le backend v1 est Gemini Live avec ses réglages optimisés (VAD/barge-in configurable, watchdog de silence, full-duplex, mesure du TTFT). OpenAI Realtime est le backend prévu pour la v2. Les modèles sont utilisés avec vos propres clés, facturés au coût réel — sans marge cachée. Les outils invoqués par l'IA pendant un appel sont relayés via HTTP à votre service (proxy_url) et leurs résultats agrégés dans le callback final.

Sécurité
  • API HTTP : liaison loopback + X-API-Key. Jamais exposée publiquement sans votre reverse proxy.
  • SIP : les INVITE/OPTIONS entrants hors liste blanche (IP du registrar/proxy résolues + CIDR de config) sont rejetés silencieusement et comptabilisés. Le REGISTER entrant est toujours rejeté (Strowger n'est pas un registrar).
  • Secrets : droits 0600 env/config uniquement. Jamais dans les logs, jamais dans l'API, jamais dans les callbacks (l'auth SIP est masquée).
  • Enregistrements : opt-in par appel, répertoire 0700, rétention automatique.
Observabilité

tracing avec call_id comme champ structuré partout ; métriques Prometheus (enregistrements par état, appels par statut/direction, TTFT de l'IA, taille du prompt, sous-alimentations du tampon, pertes RTP, rejets anti-scan, latence des outils proxy) ; un fichier de log par appel avec la chronologie SIP complète + événements IA + appels d'outils.

Déploiement
cargo build --release
sudo ./deploy/install.sh target/release/sipvoxd
# puis éditez /etc/sipvox/config.toml et /etc/sipvox/sipvox.env (secrets)
sudo systemctl enable --now sipvox
journalctl -u sipvox -f

L'arrêt (SIGTERM) est progressif : l'API se ferme, puis le worker d'appel, suivi d'un désenregistrement propre des trunks. Les secrets résident dans /etc/sipvox/sipvox.env (0640 root:sipvox), interpolés dans config.toml via ${VAR} — jamais dans le dépôt ni dans l'unité systemd.

Modèle de licence

Strowger est sous licence par canal simultané — un canal = un appel simultané. Le binaire s'active en ligne auprès d'un serveur de licence et renouvelle un bail signé (Ed25519) ; la durée du bail est la tolérance hors ligne (7 jours pour les licences datées, 30 pour les perpétuelles). Les capacités (max_concurrent_calls, trunks, enregistrement, IA) sont des valeurs lues depuis le bail vérifié — pas des options à activer. À l'expiration du bail, seuls les nouveaux appels sont refusés ; les appels en cours se terminent toujours normalement, et /health passe en état degraded avec un motif clair.

Sans aucune licence, Strowger fonctionne en mode libre-service gratuit — limité à 1 trunk, 1 canal et une durée d'appel maximale d'une minute, avec documentation communautaire et support IA par email. Suffisant pour le relier à votre trunk et l'entendre répondre ; activez une licence pour lever les limites. Voir les tarifs et le téléchargement.

Un pilote se déploie en une matinée : un binaire, un fichier de configuration, votre trunk.

STROWGER — AGENTS VOCAUX POUR OPÉRATEURS · PROPULSÉ PAR LE MOTEUR SIPVOX · DOCUMENT DE TRAVAIL, TARIFS INDICATIFS · NOM EN COURS DE VÉRIFICATION DE MARQUE (EUIPO/USPTO)