D'un serveur Debian vierge à votre premier appel traité.
Installez le binaire, éditez un fichier de configuration, activez une licence de démonstration gratuite et passez un appel — le tout en une matinée.
Installation
Sur Debian/Ubuntu avec systemd, la commande unique détecte votre architecture, télécharge et vérifie la somme de contrôle du binaire signé, configure l'unité systemd et génère un squelette de configuration à compléter :
L'installateur vérifie la somme de contrôle SHA-256 de l'archive avant toute installation (ainsi que la signature minisign si minisign est présent et qu'une clé publique est configurée) — il refuse l'installation en cas de non-correspondance. Il délègue ensuite à deploy/install.sh, qui est idempotent et peut être relancé en toute sécurité pour mettre à jour le binaire en place.
La compilation et l'installation à partir des sources fonctionnent de la même manière, une fois que vous disposez d'un binaire de version :
deploy/install.sh crée un utilisateur/groupe système strowger, installe le binaire dans /opt/strowger/bin/strowgerd (ainsi que strowger-cli, avec un lien symbolique dans /usr/local/bin s'il est présent à côté du binaire du démon), écrit un squelette de configuration dans /etc/strowger/config.toml à partir de config.example.toml (sans écraser l'existant), crée /etc/strowger/strowger.env, installe l'unité systemd et réserve la plage de ports RTP configurée dans net.ipv4.ip_local_reserved_ports afin que les sockets clients éphémères de l'hôte ne s'approprient jamais un port nécessaire à strowger. Relancez-le chaque fois que rtp_port_range est modifié.
Configuration
Tout est centralisé dans un seul fichier TOML, /etc/strowger/config.toml, dont le chemin est lu à partir de la variable d'environnement STROWGER_CONFIG. Les secrets ne sont jamais écrits en clair ici — ils sont interpolés depuis l'environnement avec la syntaxe ${VAR_NAME} afin que le fichier lui-même puisse être versionné ou partagé en toute sécurité.
[daemon]
Les paramètres principaux du processus : l'adresse de liaison de l'API HTTP, l'IP publique avec laquelle la pile SIP s'identifie, et l'emplacement des données sur le disque.
[daemon]
http_host = "127.0.0.1"
http_port = 7060
public_ip = "203.0.113.10" # REQUIS — Identité SIP ; pas de STUN en v1
data_dir = "/var/lib/strowger" # strowger.db (SQLite) + recordings/ + cdr/
rtp_port_range = "20002-40001" # plage de ports média RTP par défaut
public_ip est requis — le démon en a besoin pour construire des messages SIP/SDP corrects pour votre trunk. http_port est par défaut à 7060. Avec l'unité systemd fournie, ProtectSystem=strict rend tout le système en lecture seule sauf data_dir ; data_dir doit donc rester /var/lib/strowger en production ; ne le modifiez que pour un test local hors systemd.
[ai.gemini] / [ai.openai]
Le backend de synthèse et reconnaissance vocale en temps réel. Gemini Live est le backend par défaut ; OpenAI Realtime est un second backend structuré de la même manière. Chaque appel peut outrepasser le modèle, et la clé API peut également être fournie par appel (en utilisant la clé de configuration par défaut si elle est omise).
[ai.gemini]
api_key = "${STROWGER_GEMINI_API_KEY}"
model = "gemini-3.1-flash-live-preview"
playout_ms = 200 # tampon de diffusion (playout), en ms
# [ai.openai] # identique à [ai.gemini]
# api_key = "${STROWGER_OPENAI_API_KEY}"
Sans clé API configurée, le backend IA est désactivé et les appels fonctionnent en mode écho — utile pour tester la chaîne SIP/RTP de manière isolée.
[[trunk]]
Un bloc par trunk SIP. Deux modes d'authentification : REGISTER (identifiants, register = true) ou IP-auth (register = false — pas de REGISTER, l'opérateur authentifie par l'IP source et le keepalive OPTIONS sert de signal de disponibilité).
[[trunk]]
name = "ovh-prod"
registrar = "sip-domain.io"
outbound_proxy = "oa285772-ovh-1.sip-proxy.io"
username = "0033184163651"
auth_username = "0033184163651"
password = "${STROWGER_OVH_PASSWORD}"
number = "+33184163651"
transport = "udp"
register = true # false = trunk IP-auth, sans REGISTER
[inbound] et [[inbound_route]]
Paramètres entrants transversaux, puis une route par numéro (DID). Le gestionnaire d'une route est soit un webhook (le client autorise et configure chaque appel en direct), soit static (objectifs et premier message fixés dans le TOML).
[inbound]
answer_delay_ms = 3000 # délai entre 180 Ringing -> 200 OK à la réponse
initial_silence_ms = 1000 # silence avant le premier mot de l'agent
webhook_timeout_secs = 5 # timeout du GET webhook ; si expiré -> SIP 486
[[inbound_route]]
trunk = "ovh-prod"
did = "+33184163651" # correspondance exacte +E164
[inbound_route.handler]
type = "webhook"
url = "http://127.0.0.1:9100/api/voice/inbound?token=secret"
[[inbound_route]]
trunk = "ovh-prod"
did = "*184163652" # correspondance par suffixe (>= 6 chiffres)
[inbound_route.handler]
type = "static"
objectives = "Vous êtes le réceptionniste de l'entreprise…"
first_message = "Bonjour, comment puis-je vous aider ?"
language = "fr"
Le champ did d'une route accepte trois formes, comparées après normalisation (uniquement les chiffres ; +, espaces, points, tirets et paramètres URI sont supprimés) :
Modèle
Signification
+33184163651
correspondance exacte — ne correspond pas à une présentation 0033…/0… du même numéro
*184163651
correspondance par suffixe (≥ 6 chiffres) ; 9 chiffres = un numéro national français, invariant selon le format de présentation — le choix par défaut recommandé
*
catch-all (tout intercepter) pour le trunk
La résolution est déterministe : exacte > suffixe le plus long > catch-all. Deux modèles normalisés vers la même valeur sont rejetés au chargement.
D'autres réglages optionnels existent pour un ajustement avancé — watchdogs média et politique de latching RTP sous [media], gestion du VAD/barge-in/silence sous [ai.gemini]/[ai.openai], et délais d'expiration des appels/extinction au niveau du démon — consultez la référence commentée dans config.example.toml dans le dépôt pour la liste complète et les valeurs par défaut.
Transfert d'appel par l'agent
Strowger prend en charge le transfert d'appel autonome par l'agent (déflexion d'appel / escalade humaine) via l'outil intégré transfer_call dans Gemini Live et OpenAI Realtime.
Configurez la politique de transfert et les listes blanches de sécurité dans config.toml :
# /etc/strowger/config.toml
[transfer]
enabled = true # Activer la déclaration de l'outil transfer_call (défaut : false)
mode = "refer" # "refer" (délégation opérateur) ou "hairpin" (2ème jambe pontée)
max_per_call = 3 # Protection anti-boucle : transferts max par appel
allow = [ # Liste blanche : E.164 exact (+331...), préfixe (+33184*), ou URI SIP
"+33144556677",
"+33184*",
"sip:*@company.com",
]
deny = [ # La liste noire prime sur la liste blanche
"+33184163999",
]
drain_timeout_ms = 4000 # Vidage du flux audio avant d'initier le transfert
refer_timeout_ms = 15000 # Timeout d'attente de progression REFER/NOTIFY de l'opérateur
hairpin_timeout_ms = 30000 # Timeout d'attente de réponse de la 2ème jambe en mode hairpin
Modes de transfert :
refer (Par défaut) : Effectue un SIP REFER (RFC 3515) dans le dialogue pour déléguer le routage de l'appel à l'opérateur/PBX amont, libérant l'audio et les canaux après le transfert.
hairpin : Initie une seconde jambe sortante directement vers la destination cible, interconnecte l'audio PCM 8 kHz bidirectionnel entre l'appelant et la cible (A↔C), libère proprement la session vocale IA, et supervise les deux jambes avec un BYE en cascade.
Backends IA & clés
L'intelligence vocale repose sur un modèle speech-to-speech en temps réel, accessible via WebSocket. Vous fournissez votre propre clé fournisseur — l'audio est transmis exclusivement à un tiers, le fournisseur de modèle que vous choisissez, sous votre propre contrat et DPA, facturé au coût réel sans marge de notre part. Le modèle est choisi par backend et peut être outrepassé par appel.
GEMINIGemini Live — par défaut
Backend par défaut. Modèle de référence gemini-3.1-flash-live-preview. Obtenez une clé sur Google AI Studio → aistudio.google.com/apikey (ou un compte de service Google Cloud). Configurez-la comme STROWGER_GEMINI_API_KEY.
OPENAIOpenAI Realtime
Même principe comme second backend. Obtenez une clé sur la plateforme OpenAI → platform.openai.com/api-keys. Configurez-la comme STROWGER_OPENAI_API_KEY.
Placez les clés dans le fichier des secrets (jamais dans config.toml ni dans le dépôt) ; la configuration y fait référence via ${VAR} :
Choisissez le backend par appel avec le champ backend du webhook entrant / corps du POST /calls (par défaut : le backend configuré). Un appel peut également outrepasser le model et porter sa propre clé, sinon la clé de configuration est utilisée. Sans aucune clé configurée, les appels fonctionnent en mode écho (flux SIP/RTP uniquement, pas d'IA).
Secrets
Les valeurs réelles des secrets ne figurent jamais dans config.toml — et jamais dans le dépôt. Elles résident dans /etc/strowger/strowger.env, créé avec les droits 0640 root:strowger par l'installateur, et sont interpolées dans le fichier de configuration via ${VAR} au démarrage :
# /etc/strowger/strowger.env — 0640 root:strowger, jamais versionné
STROWGER_OVH_PASSWORD=...
STROWGER_GEMINI_API_KEY=...
STROWGER_OPENAI_API_KEY=...
L'unité systemd charge ce fichier comme son EnvironmentFile, de sorte que les variables ne sont visibles que par l'utilisateur du service strowger, jamais dans le fichier d'unité, jamais journalisées, et supprimées de tout contenu HTTP qui les transporterait.
L'arrêt (systemctl stop, SIGTERM) est gracieux : l'API HTTP se ferme d'abord, puis le gestionnaire d'appels, enfin le démon se désenregistre proprement de ses trunks, le tout dans la limite de TimeoutStopSec=30. Si vous mettez à jour le binaire et relancez install.sh, notez que daemon-reload ne redémarre pas le processus en cours — le nouveau binaire ne prend effet qu'après un systemctl restart strowger.
Licence & activation
Le binaire fourni applique sa licence — ceci est compilé dans le binaire, pas une option de configuration. Sans licence activée, les nouveaux appels sont refusés. Activez-la une fois, après le lancement du démon :
La démo est limitée à 1 trunk, 1 canal, des appels limités à 60 secondes, avec une validité glissante de 30 jours qui se renouvelle tant que le démon communique avec le serveur de licence. La licence est par canal simultané : les capacités telles que max_concurrent_calls, max_trunks et max_call_seconds sont des valeurs lues directement depuis la licence vérifiée, et non des paramètres modifiables localement.
En cas d'expiration ou de révocation, seuls les nouveaux appels sont refusés — un appel déjà en cours se termine toujours normalement, et /health indique degraded avec un motif plutôt que l'arrêt complet du démon. Pour lever les limites de la démo, contactez [email protected] : le même license_id est mis à jour côté serveur, et le démon récupère les nouvelles capacités lors de son prochain contact avec le serveur de licence — pas de réinstallation, pas de nouvelle clé à saisir.
CDRs & enregistrements
Tout ce que strowger écrit sur le disque se trouve dans data_dir (/var/lib/strowger sous systemd) : le fichier d'état SQLite (strowger.db), un répertoire recordings/ (activable par appel) et un répertoire cdr/ contenant le journal quotidien en ajout seul.
Les CDR (tickets d'appel) par appel sont également envoyés directement à votre callback_url à la fin de chaque appel — c'est la méthode recommandée pour les exploiter ; consultez la référence API pour le contenu complet du callback et la liste des champs CDR.
Votre premier appel
Avec un trunk enregistré et une licence activée, vous êtes prêt à passer ou recevoir un appel. Le contrat complet POST /calls, le webhook d'autorisation entrant et le callback de fin d'appel sont documentés dans la référence API.