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
268 lines
12 KiB
Markdown
268 lines
12 KiB
Markdown
# 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:
|
|
|
|
```bash
|
|
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):
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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.
|
|
|
|
```bash
|
|
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
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
docker exec -it $CONTAINER asterisk -rvvv
|
|
```
|
|
|
|
Expected: the call connects, and a line appears in the call log:
|
|
|
|
```bash
|
|
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`/`internal` tier 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.conf` or the dashboard's
|
|
displayed caps.
|
|
- Kill-switch tripped (see §7 below) — every PSTN call, in or out, blocked
|
|
regardless of tier.
|
|
- `dialplan show intercom` and 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).
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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)
|
|
|
|
```bash
|
|
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:
|
|
|
|
```bash
|
|
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.
|
|
|
|
```bash
|
|
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.conf` actually 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
|
|
|
|
```bash
|
|
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
|
|
```
|