SIP REGISTER gateway¶
One gossipper server process can register one or more AORs on a PBX, originate
lab UAC calls as a chosen AOR, and answer inbound INVITEs on the registered
Contact with a per-profile armed UAS scenario. There is no WebRTC path, no kefir
Testing mode, and no separate gossipper-agent binary.
gossipper ui mounts the same Gateway API (/api/v2/gateways, Autodiscover
/api/v2/gateway/ips) and persists {data-dir}/gateway.json. Inbound arm still
needs a management UAS listen (gossipper server); UI-only REGISTER uses a
standalone UDP socket.
Example config: examples/gossipper-gateway.json.
Lab UAC/UAS ids: lab scenarios. Control UI: UI mode.
How it works¶
- Each enabled profile with
register=trueruns a Go Digest REGISTER loop (not SIPp XML) on the UAS listen UDP (same port as Contact). - Contact is
sip:{aor_user}@{advertised_ip}:{contact_port}wherecontact_portis the management UAS listen port (default 5060). - FreeSWITCH / Sofia NAT-pins inbound INVITE to the REGISTER source
(
received=/Route). REGISTER and UAS must share that port so INVITE lands on the armed scenario instead of a dead ephemeral socket. - Originate starts a supervisor UAC job toward the PBX with
sip_from=sip:{user}@{domain}and that profile's Digest credentials. - Arm stores a UAS scenario on the profile (
armed_scenario_idingateway.json). After restart the same id is loaded and re-armed. New INVITEs whose Request-URI user matches the profile AOR use that scenario. Unmatched INVITEs fall back to the last-armed live UAS. In-flight calls keep the previous XML. Save and Originate also persist the last UAC scenario, destination, and call count (originate_scenario_id/originate_to/originate_calls) so the Gateway dialog restores them after restart.
Disable a profile to stop REGISTER and refuse originate. Inbound matching also ignores disabled profiles.
Config¶
Top-level "gateway" (object) remains a seed for a single profile. Prefer
"gateways" (array) for more than one. Both may appear: the object is first,
then the array. {ui_data_dir}/gateway.json overrides the JSON seed after the
first save (legacy single-object files migrate to { "profiles": [ ... ] }).
"gateways": [
{
"id": "desk",
"name": "Desk 1001",
"enabled": true,
"domain": "pbx.local",
"addr": "192.168.1.10:5060",
"username": "1001",
"password": "secret",
"register": true,
"advertised_ip": "192.168.1.20",
"contact_port": 5060
}
]
Omitted "enabled" defaults to true (CLI seed / legacy files).
| Field | Notes |
|---|---|
id |
Path id ([A-Za-z0-9][A-Za-z0-9._-]{0,63}); generated if empty |
name |
UI label (defaults to AOR user or Gateway) |
enabled |
Off stops REGISTER, originate, and inbound AOR match |
domain |
SIP domain in AOR / From |
addr |
Registrar host:port (default port 5060) |
transport |
udp only in v1 |
username / password |
Digest credentials |
register_user |
Optional AOR user when it differs from username |
register |
When true (and enabled), start the refresh loop |
advertised_ip |
Host in REGISTER Contact and UAS 180/200 Contact/SDP (must be reachable from the PBX) |
register_expires |
Requested TTL seconds (default 300) |
keepalive_seconds |
NAT keep-alive OPTIONS interval toward the registrar (default 20; -1 disables) |
contact_port |
Always the UAS listen port (server listeners[0]); REGISTER Contact uses that socket, not a stale saved value |
The loop honours Expires from 200 (header or Contact expires=), refreshes at
90% (minimum 5s), retries every 15s on failure, and sends Expires: 0 on
shutdown from the same UDP socket and Call-ID. While registered, gossipper
sends fire-and-forget OPTIONS to the registrar on that socket every
keepalive_seconds (default 20) so consumer NAT mappings (~30–60s) stay open;
inbound INVITE to Contact would otherwise black-hole. Saving a profile does not
re-REGISTER unless SIP identity, credentials, advertised IP, keep-alive, or
enable/register changed. Passwords are never logged.
UI persistence: {ui_data_dir}/gateway.json (mode 0600), including
armed_scenario_id and originate defaults (originate_scenario_id,
originate_to, originate_calls) per profile. Control UI Save writes the
selected Armed UAS and UAC originate fields with the SIP fields. A PUT that
omits armed_scenario_id keeps the previous arm. Empty originate fields keep
the previous UAC defaults (so enable/disable does not wipe them). Originate
also remembers the last used UAC id, destination, and call count. The server
JSON is only a seed when that file is missing.
NAT¶
Set advertised_ip to the address the PBX must use in Request-URI / Contact
routing. If gossipper is behind NAT, that is typically a public or SBC-facing
IP with UDP forwarded to the UAS listen port. REGISTER is sent from that same
listen port so the registrar's NAT mapping matches Contact. Contact in REGISTER
and keep-alive OPTIONS is that same listen port — a saved contact_port that
differs (for example 15069 vs listen 5060) is ignored, otherwise the PBX INVITEs
a hole that does not exist. REGISTER refresh alone (~270s at default Expires
300) is too slow for typical UDP NAT timeouts; keep-alive OPTIONS on the same
socket (default 20s) holds the mapping so inbound INVITE to
sip:{aor}@{advertised_ip}:{uas_port} reaches gossipper.
UAS answers (180/200 Contact and SDP c=) also use that advertised_ip for
[local_ip], not the LAN bind address. RFC 3261 ACK to 200 is sent to Contact;
a private Contact (192.168.x.x) is unreachable from the PBX, so Timer G
retransmits 200 forever. RTP sockets bind the real listen/bind IP (or 0.0.0.0
when that address is not on a local interface) — advertised_ip is SIP/SDP
only. Builtin uas answers INVITE with negotiated SDP and plays synthetic RTP
until BYE. Cadence labs stay on fake_ringing_uas / one_way_uas.
Originate vs arm¶
| Action | Role | API | Typical ids |
|---|---|---|---|
| Originate | UAC toward the trunk | POST /api/v2/gateways/{id}/originate |
one_way, short_call, custom UAC XML |
| Arm inbound | UAS on Contact | PUT /api/v2/gateways/{id}/arm |
uas, one_way_uas, early_183, late_180, custom UAS XML |
Compat aliases: GET/PUT /api/v2/gateway, PUT /gateway/arm,
POST /gateway/originate operate on the first profile.
Arming a UAC id (for example one_way) returns 400 — use originate.
Originating a UAS id returns 400 — use arm.
Empty arm id resets to builtin uas. Each UAC lab has an inbound twin
(one_way_uas, no_rtp_uas, …) that answers INVITE with the same SDP/RTP.
When any gateway profile is enabled, unmatched out-of-dialog OPTIONS get an immediate 200 (PBX keep-alive) without consuming the armed INVITE scenario.
REST (/api/v2)¶
| Method | Path | Body / result |
|---|---|---|
GET |
/gateways |
{ "gateways": [ snapshot, ... ] } |
POST |
/gateways |
Create profile; password masked in response |
GET |
/gateways/{id} |
One snapshot |
PUT |
/gateways/{id} |
Update; empty/*** password keeps the previous secret |
DELETE |
/gateways/{id} |
204 |
PUT |
/gateways/{id}/arm |
{ "scenario_id": "early_183" } |
POST |
/gateways/{id}/originate |
{ "scenario_id": "one_way", "to": "100", "total_calls": 1 } → { "job_id" } |
GET |
/gateway |
First profile (compat) |
GET |
/gateway/ips |
Local IPv4s + public IPv4 for Advertised IP Autodiscover |
PUT |
/gateway |
Create-or-update first profile |
PUT |
/gateway/arm |
Arm first profile |
POST |
/gateway/originate |
Originate as first profile |
GET |
/sip/trace?since=&limit= |
REST snapshot of the process-wide live-trace ring ({ messages, next, capture }; kind is sip or app) |
GET |
/sip/trace/ws?since=&token= |
WebSocket: first frame is backlog, then push on each new record / Clear / capture change |
GET/PUT |
/sip/trace/capture |
{ sip, app, level } independent capture flags plus Debug floor (debug/info/warn/error, default debug; empty PUT level keeps the previous value). SIP default on, app/debug default off |
GET |
/sip/trace/export?format=text or pcap&scenario= |
Full ring as a .txt dump or reconstructed Ethernet/IPv4/UDP .pcap (pcap is SIP only; respects capture flags) |
POST |
/sip/trace/clear |
Drop the in-memory ring ({ cleared: true }) |
Originate jobs use profile_kind=gateway and profile_id = gateway id, with
engine overlay remote_host / remote_port = registrar, sip_from,
auth_username / auth_password, service = to.
Challenged trunks need Digest-ready UAC XML (or builtin uac / invite_media).
IP-auth PBXs work with the current lab UAC scenarios.
Control UI¶
Nav Gateway: table of profiles (name, AOR, REGISTER status, enable switch)
and a modal to create/edit, Autodiscover Advertised IP, arm UAS, and originate.
The edit dialog footer shows Save progress (Saving…, Saved · time · register
state, or Save failed). Live Trace is its own nav page (#/sip): a
process-wide circular buffer of SIP (UAS listen UDP plus in-process engines)
and scenario/app debug (call.started, command steps, <log>, timeouts).
SIP and Debug buttons independently enable capture. Filter by scenario.
Click a row for the raw message. Pause / Clear (POST /api/v2/sip/trace/clear).
Text / PCAP download the full ring (GET /api/v2/sip/trace/export); pcap
is SIP only. The page is a WebSocket (/sip/trace/ws), not HTTP polling. Gateway
has a shortcut button to the same page. Originate UAC jobs still run as a child
process and do not appear here. No Graph or WebRTC on Gateway.