Three follow-ups from live testing on this session's actual VPS: - tools/pstn-test-check.sh's SMS section printed the Forward-to-URL value to configure but never said what to do next — add the "text this DID, then watch journalctl -u sms-inbound -f" step right after it. - docs/pstn-sms-test-checklist.md: the "which box actually handled this" question has a simple answer (a DID's inbound routing targets exactly one IP:port, so there's no ambiguity to resolve, only a portal setting to confirm) — written up so it doesn't need re-deriving. Also fixed the --list example to cd into the repo first; ./setup.sh is a relative path and silently fails with "command not found" from any other directory, confirmed live in this session. - New tools/coturn-test-check.sh: health-checks the shared coturn instance (services/coturn.sh) and every consumer registered against it (Asterisk, any number of Mattermost instances) — container/identity, each cached consumer credential cross-checked against coturn's own live user database (catches the container/volume-recreated-without-db drift case), UFW rules for both the TURN port and the relay range, a capacity explanation reasoned from the actual port-range math instead of a guess, and a real TURN allocation test per consumer via turnutils_uclient — the only way to prove credentials + port range + firewall all actually work together, not just that each looks right in isolation. Deliberately does not attempt a concurrent load test, since that would consume real relay ports other services may be actively using. Verified the turnadmin -l output parsing, UFW rule matching, and the empty-array-under-set–u loop pattern against mock data before shipping. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01H4k6J1qXXyYxhGEgnJaMvn
12 KiB
PSTN + DID + SMS — end-to-end test checklist
For verifying an already-installed Asterisk + PSTN trunk + SMS setup
(droplet or home/LAN) actually works, top to bottom. Not a setup guide —
see docs/anveo-direct-setup-guide.md for account/droplet setup from
scratch, and docs/pstn-calling-voipms-plan.md for the design/architecture
background. This doc assumes both are already done and just walks through
proving it all still works, in order, with what to check when a step fails.
Run through this after any of: first go-live, a provider/DID change, an Asterisk or PSTN trunk reinstall, or just periodically to catch drift (a provider-side change, an expired international allow-list, a forgotten kill-switch trip).
How do I know a test call/text actually used THIS box?
Short answer: there's no ambiguity to resolve — a DID can only point at one
place. In the Anveo (or any IP-auth) portal, the DID's inbound routing
targets one specific IP:port ($[E164]$@<this box's public IP>:5060), and
the Outbound Trunk's Authorized IP Addresses list is what lets this box's
outbound calls out. If you have multiple boxes, only the one whose IP is
actually configured in the portal can send or receive on that DID at all —
there's nothing to "make sure" beyond confirming the portal points at this
box's current IP (§ Provider portal checklist output from
tools/pstn-test-check.sh prints it directly).
The practical way to watch it happen on this box specifically, live,
while you place the test: run docker exec -it $CONTAINER asterisk -rvvv
or journalctl -u sms-inbound -f in one terminal, then place the call/text
from another phone in real time. If it shows up here as it happens, this
box handled it — no separate confirmation needed.
0. Before you start
$CONTAINER/$EA_DIR only live in the shell session where you set them —
a new terminal, a reboot, or just enough time passing between test steps
leaves them empty again, and an empty $CONTAINER silently turns
docker exec -it $CONTAINER asterisk -rx ... into docker exec -it asterisk -rx ..., which fails with "No such container: asterisk" (there's
no container literally named that — it's easy-asterisk or
easy-asterisk-do). Auto-detect instead of typing it by hand, so a stale
or forgotten variable can't silently break every command below:
CONTAINER="$(docker ps --format '{{.Names}}' | grep -m1 -E '^easy-asterisk(-do)?$')"
EA_DIR=~/docker/asterisk; [ -d ~/docker/asterisk-digital-ocean ] && EA_DIR=~/docker/asterisk-digital-ocean
echo "Container: $CONTAINER Dir: $EA_DIR"
If $CONTAINER prints empty, the container isn't running at all — check
docker compose -f $EA_DIR/docker-compose.yml ps before anything else.
Re-run this block at the start of every new terminal session, not just
once — it's cheap and removes the whole class of failure above.
Check the three services this checklist covers are actually installed —
run this from the repo directory itself (~/ubuntu-post-install, not
~/docker/asterisk or wherever you happen to be — ./setup.sh is a
relative path and fails with "command not found" from anywhere else):
cd ~/ubuntu-post-install && sudo ./setup.sh --list | grep -E "asterisk|pstn-trunk|sms-inbound|security-dashboard"
Read your box's own current settings before testing — this file has your
real DID, tiers reference, ring-group, and rate, regenerated on every
pstn-trunk install/update:
cat $EA_DIR/README-pstn-trunk.md
1. Registration sanity check
Every extension you're about to test with needs to actually be registered before anything else will work.
docker exec -it $CONTAINER asterisk -rx "pjsip show endpoints"
Each extension you plan to test should show Avail (registered), not
Unavail. If a softphone shows registered in its own UI but Asterisk
disagrees, the extension's transport (UDP vs TLS) is the usual culprit —
check it via the Security Dashboard's Extensions tab ⓘ button on that
row before going further, not by re-entering credentials.
2. Trunk itself
docker exec -it $CONTAINER asterisk -rx "pjsip show endpoint pstn-trunk"
Look for Contact state, not just that the endpoint exists. An
IP-authenticated trunk has no registration to show — what matters here is
that Asterisk considers the endpoint reachable, and that the provider's own
portal doesn't report the account balance at $0 or the DID unassigned.
3. Permission tiers — set before testing calls
Every extension starts at internal (no PSTN at all) until granted a
tier. Before testing outbound/inbound, set at least one extension to
full via the Security Dashboard's Extensions tab (or restricted with an
approved number, if that's what you want to test). Press "Commit Changes
(Restart Asterisk)" after — tier/permission edits are written to disk
immediately but the dialplan has been observed not picking them up until a
full container restart, not just a reload (see "Dashboard changes may need
Commit Changes" in docs/anveo-direct-setup-guide.md). Skipping this step
is the single most common reason a "correctly configured" test call still
fails.
4. Outbound call test
From a full-tier extension's softphone, dial a real US number as
1 + area code + number (11 digits — the dialplan matches the full NANP
pattern, not a bare 10-digit number).
Watch the live console while dialing:
docker exec -it $CONTAINER asterisk -rvvv
Expected: the call connects, and a line appears in the call log:
tail -f $EA_DIR/logs/pstn-trunk-calls.log
If it fails, check in this order (matches the actual bugs this system
has hit — see the "Bugs hit and fixed" section of
docs/anveo-direct-setup-guide.md):
restricted/internaltier dialing a number not on its approved list — expected to fail with a busy signal, not a bug.- Concurrency cap already hit — check
pstn-limits.confor the dashboard's displayed caps. - Kill-switch tripped (see §7 below) — every PSTN call, in or out, blocked regardless of tier.
dialplan show intercomand count digits by hand against what you dialed — a pattern-length mismatch has bitten this exact system before.
5. Inbound call test — shared DID / ring-group
From any outside phone, dial the trunk's main DID (shown in
README-pstn-trunk.md). Every full/restricted-tier member of the
configured ring-group should ring simultaneously for ~20 seconds unless a
personal DID routes it elsewhere (see §6).
docker exec -it $CONTAINER asterisk -rx "dialplan show from-pstn-trunk"
If nothing rings: confirm the calling number's actual Caller-ID is
reaching Asterisk correctly — a bare Hangup()/no ring with no ntfy denial
alert either usually means the inbound context isn't loading at all (check
asterisk -rx "dialplan show from-pstn-trunk" returns content, not
"No such context"). A Busy(15) + ntfy denial alert means it loaded fine
and the caller's number just isn't on a restricted-tier member's approved
list — expected behavior, not a bug.
6. Inbound call test — personal DID
If you've assigned a personal DID to a specific extension (Security Dashboard → PSTN Trunk tab → Personal numbers), dial that number instead of the shared trunk DID. Only the owning extension (or every current member, if assigned to a Group) should ring — no ring-group fallback. Confirm the owner's outbound Caller-ID also shows this DID instead of the shared trunk DID when they place a call out.
7. Spend-cap kill-switch (only if enabled)
Don't trigger this for real — it force-hangs-up active calls and blocks all PSTN traffic until manually cleared. To confirm it's wired up without tripping it:
cat $EA_DIR/config/asterisk/pstn-trunk-killswitch.conf
systemctl status pstn-trunk-usage.timer 2>/dev/null || crontab -l | grep pstn-trunk-usage
The timer/cron entry should be active and recent (journalctl -u pstn-trunk-usage.service -n 20 shows it actually ran within the last
minute). If you do want to confirm the block itself works, lower
MAX_MONTHLY_SPEND temporarily via sudo ./setup.sh pstn-trunk (update
mode) to something you've already exceeded, confirm calling is blocked
with the priority=urgent ntfy alert, then re-run and clear it (this is
CLI-only by design — not on the dashboard).
8. International calling (only if you've allowed any countries)
cat $EA_DIR/config/asterisk/pstn-intl-allowed.conf
Only full-tier extensions can dial these regardless of the allow-list.
Dial 011 + country code + number. If an expiry was set, confirm the
allow-list actually clears itself after that date (the periodic
usage-alert script does this, not a separate cron job) — worth checking
once near the expiry date rather than assuming.
9. Internal SIP messaging (no PSTN/SMS involved)
From one extension enabled for messaging=yes (Security Dashboard →
Extensions tab → Messaging column) to another, send a native SIP message
in Sipnetic (or whichever softphone). Should land instantly, no carrier
involved. Check the log if it doesn't arrive:
tail -f $EA_DIR/logs/sip-messages.log
An extension with messaging=no should have its message silently denied
(fails closed) — confirm this too, not just the success path.
10. SMS inbound test
Prerequisite: sudo ./setup.sh sms-inbound already run, and the Anveo
portal's DID → SMS tab → "Forward to URL" configured with the exact string
the installer printed (see docs/anveo-direct-setup-guide.md §8 if this
hasn't been done yet).
Text the DID's number from any outside phone. Expected: it lands in Sipnetic's message thread for whichever extension/group owns that DID within a few seconds — not a push notification, a real message in the softphone's own thread.
journalctl -u sms-inbound -f
Shows sender, recipient, message length, and the AMI exchange — never the message body itself. If nothing arrives:
- Confirm the DID is a mobile-class number if you're testing a 2FA/verification code specifically — geographic DIDs get silently rejected by many services before a message is ever sent (see the setup guide's "Pick the right kind of number" section).
- Confirm short-code SMS is actually enabled on this specific DID (not every number in the pool has it by default).
- Check
pstn-personal-dids.conf/pstn-groups.confactually has an entry for the DID you texted — an unowned DID has nowhere to route to.
Outbound SMS from this box isn't a working path yet (see the setup guide's "What about sending?" section) — don't test for it.
11. Dashboard cross-check
Open the Security Dashboard's Calls & Texts tab and confirm the calls
and messages from the tests above actually show up there — this is reading
the same log files tested above, so an empty tab after a successful test
call usually means the dashboard's ASTERISK_LOG_DIR/log paths have
drifted from where Asterisk is actually writing, not that logging is
broken. Also check the Extensions tab shows correct live status
(online/offline) for whichever device you tested with.
Quick reference — verification commands
docker exec -it $CONTAINER asterisk -rx "pjsip show endpoints"
docker exec -it $CONTAINER asterisk -rx "pjsip show endpoint pstn-trunk"
docker exec -it $CONTAINER asterisk -rx "dialplan show intercom"
docker exec -it $CONTAINER asterisk -rx "dialplan show from-pstn-trunk"
tail -f $EA_DIR/logs/pstn-trunk-calls.log
tail -f $EA_DIR/logs/sip-messages.log
journalctl -u sms-inbound -f # SMS via Anveo's HTTP webhook — the path §10 actually tests
tail -f $EA_DIR/logs/pstn-sms.log # SMS-over-SIP dialplan path — confirmed NOT offered by Anveo;
# only relevant if a future provider delivers texts over SIP
journalctl -u pstn-trunk-usage.service -n 20