Files
Myron Blair 4fad312f87 Full rewrite for current infra state — StreamHoard consolidation, VoIP split out, dead references purged
- Replace all Jellyfin/MediaStack/WireGuard-CT110 references (destroyed 2026-07-23) with
  StreamHoard/CT104 throughout; add streamhoard.md covering the full consolidated stack,
  MSP360 backup setup, and migration incidents
- Split FusionPBX/SignalWire/landline content into its own voip.md (was scattered across
  jarvis.md and sites.md, and referenced the old destroyed DO droplet IP)
- Fix stale facts: JARVIS webhook port (:1972 -> 80/https), rotated registration key/GitHub
  PAT, FortiGate admin port (9443 not 443), current WAN-IP-drift/VIP gotchas
- Add reference.md for repo inventory notes, assistant hard limits, ZeroTraceGPT IQ, payments
  migration status
- Update gotchas.md with unprivileged-LXC mount limitations and MSP360 dpkg-repack gotcha
2026-07-25 22:47:31 -05:00

7.9 KiB

VoIP Reference Card — FusionPBX / SignalWire / Landline

FusionPBX server

  • Host: VM130 on PVE1, 10.48.200.130 — moved on-prem from an old DO droplet (134.209.72.226, now destroyed — do not reference that IP anymore, any doc saying FusionPBX is at 134.209.72.226 is stale).
  • Web UI: https://fusion.orbishosting.com (NPM proxies to 10.48.200.130:80)
  • SSH: root@10.48.200.130 via PVE1 hop, password Joker1974!!! (no longer needs the old DO-relay hop)
  • DB: PostgreSQL (not MySQL), fusionpbx / fusionpbx / RSfAjzefyYmufVqtnrYpz90yHaQ, psql -U fusionpbx -h 127.0.0.1 -d fusionpbx
  • Timezone: America/Chicago

SIP registration settings (for a device/softphone registering against this PBX)

Setting Value
SIP Server/Domain fusion.orbishosting.com (or 10.48.200.130) — must be the domain name for some devices (see FXO gotcha below), not always interchangeable with the IP
Port (LAN) 5060 UDP/TCP
Port (WAN, per FortiGate forwards) 5080 UDP or 5081 TCP/TLS
Auth username extension number, e.g. 1000
Auth password per-extension, from v_extensions.password in the FusionPBX DB

Example working extensions (query select extension, password, effective_caller_id_name from v_extensions; for current full list):

Ext Who
1000 Myron Blair (Desk)
1001 Myron Blair
1002 Tommy Ivy
1003 Kitchen
1004 Master Bedroom
1050 Landline FXO gateway (Grandstream ATA, see below)
1091 "Landline" ring group (rings all 8 phones)

Extension key/BLF provisioning: category differs by phone model — T57W/T48S use category=line in v_device_keys, the AX86R WiFi phone only has a programmable keys section (no line section). Guessing wrong silently produces no key at all — check the actual Smarty templates at /var/www/fusionpbx/resources/templates/provision/yealink/<model>/*.cfg before assuming a key type.

SignalWire outbound trunk (carrier side — separate from the extension/phone side above)

FusionPBX registers to SignalWire as a SIP gateway (this is the outbound carrier trunk, not something a phone registers against):

  • Gateway UUID: c0e12aab-2406-44ee-ac38-6602116eaf2f, username orbis
  • Realm: orbis-hosting-0364f5f67488.sip.signalwire.com
  • Space: orbis-hosting.signalwire.com, Project ID 16fbbcfd-1c94-4827-869c-0364f5f67488
  • Check status: fs_cli -x "sofia status" on VM130 — should show REGED for external::c0e12aab-...

SignalWire API gotchas (cost real debugging time — read before touching SignalWire)

  1. Credential types are NOT interchangeable. The dashboard shows a PSK_-prefixed Signing Key at the top of the API Credentials page that looks like "the" token but gives a real 401 on classic REST calls. A pat_-prefixed Personal Access Token also 401s. Only a named Project Token (PT-prefix, created in a separate table further down the same page) works for REST Basic Auth (ProjectID:PT...) and the Relay SDK.
  2. Setting the SIP registration password requires the relay API, not the Fabric API. PUT /api/fabric/resources/sip_endpoints/{fabric_id} returns HTTP 200 but does NOT affect registration auth. The correct call is PUT https://{space}/api/relay/rest/endpoints/sip/{inner_sip_endpoint_id} with {"password":"..."} — the inner sip_endpoint id is a different id than the Fabric resource id that wraps it. Symptom of using the wrong one: gateway stuck FAIL_WAIT with [904] Operation has no matching challenge even though the wire-level 401 challenge is valid.
  3. Outbound INVITE From header user-part must equal the digest auth username (orbis), not the caller's DID/Caller ID. In FusionPBX this is v_gateways.caller_id_in_from — must be false. If wrong: INVITE gets 407, retries with Proxy-Authorization, gets ANOTHER 407 with the same nonce (looks like a FreeSWITCH bug, isn't).
  4. Destination must be full 11-digit E.164 (+1 + 10 digits) — a dialplan bridge producing +8172662022 (missing the 1) gets 404/NO_ROUTE_DESTINATION, easily misdiagnosed as "SignalWire doesn't recognize this number."
  5. Trial-tier gotcha: even with the right token, sending SMS to an unverified number returns 422 integration_test_verified_caller_required.
  6. FreeSWITCH fs_cli chat to a registered phone needs the destination prefixed with the Sofia profile name: chat sip|<from>@<domain>|internal/<ext>@<domain>|<body> — just user@domain silently fails ("Invalid chat interface").
  7. Diagnostic method that actually works for SIP auth issues: independently reconstruct the digest (MD5 HA1/HA2/response) in a standalone script using the exact nonce/realm/uri from a live packet capture, compare byte-for-byte to what FreeSWITCH sent. Proves credentials/computation bug (mismatch) vs. carrier-side policy rejection (match, still rejected) before chasing further theories.

FortiGate SIP/RTP port forwards (VIPs, verify live via show firewall vip — the doc's static tables drift)

fusion-sip-5080 (udp), fusion-sip-5080-tcp, fusion-sip-5081-tcp, fusion-sip-5081-udp, fusion-rtp (udp 16384-32768)
  → all forward to 10.48.200.130 (FusionPBX)

WAN IP drifts silently and semi-regularly — when it does, all VIP extip fields need updating, or calls will connect at the signaling level but have one-way/no audio (RTP arriving at the real current WAN IP with no matching forward rule). Always curl ifconfig.me from any on-prem host to check the actual live WAN IP before trusting a documented value.

FortiGate SIP ALG (default-voip-alg-mode) must stay proxy-based (the default) — switching to kernel-helper-based breaks inbound call routing entirely (the inbound dialplan's ${sip_to_user} handling is tuned around ALG being active).

FusionPBX's sofia.conf is cached independently of FreeSWITCH's process at /var/cache/fusionpbx/<hostname>.configuration.sofia.conf (Lua file cache) — any sofia profile/gateway DB or vars.xml change needs this file cleared too, not just a service/process restart.

Landline/FXO integration (Grandstream ATA, working as of 2026-07-18)

Old Flyingvoice FTA5111 (10.48.200.119) is retired — do not reuse without a firmware fix from the vendor. It has a 100%-reproducible crash-on-save bug (firmware V3.20) confirmed via extensive testing; not a config mistake.

Current device: Grandstream HT8xx-family ATA at 10.48.200.45 (admin/Joker1974!!!), registered as extension 1050.

FXO config (/cgi-bin/config_a2, Advanced Settings → FXO Port):

  • Primary SIP Server = fusion.orbishosting.commust be the domain, not the IP (extension 1050 is only provisioned under that domain name in FreeSWITCH's directory, even though 10.48.200.130 also technically exists as a domain).
  • SIP User ID/Auth ID = 1050, password = ext 1050's FusionPBX password.
  • PSTN Ring Thru FXS = No (device defaults to Yes — rings a nonexistent local FXS phone instead of routing to SIP).
  • Stage Method = default 2 (2-stage dialing) — gives the "landline button" real dial tone (SIP call seizes the line, ATA plays back real dial tone, DTMF passthrough).

Inbound PSTN→VoIP routing is on a DIFFERENT page — Basic Settings (/cgi-bin/config2), NOT the FXO Port page:

  • Unconditional Call Forward to VOIP: User ID = 1091 (the "Landline" ring group, rings all 8 phones), Sip Server = fusion.orbishosting.com, port 5060.
  • Without this field, incoming landline calls have nowhere to go once FXS-thru is disabled — easy to miss since the FXO page itself has no inbound-destination field.

Grandstream ATA web quirk: login is POST /cgi-bin/dologin, but every subsequent settings-changing POST /cgi-bin/update needs a session_token hidden field (separate from login's csrf_token) scraped fresh from whatever config page you just loaded — omitting it silently bounces to a re-login page with the submission discarded, no error shown. Partial POSTs (only the fields you want changed) work fine.