Generalizes services/pstn-trunk.sh (renamed from voipms-trunk.sh in the
prior commit) away from VoIP.ms specifics - any IP-authenticated SIP
provider works, VoIP.ms is just the suggested default. Adds:
- Role-based outbound permission: a configurable allow-list of extensions
that may dial PSTN numbers (regex-gated on CHANNEL(peername)), separate
from internal extension-to-extension dialing which stays open to everyone
regardless. Blank list preserves the original "everyone can dial out"
behavior.
- Inbound ring-group: rings a configurable list of extensions instead of a
single hardcoded one.
- ntfy alerts: immediate on denied (unauthorized extension) or rejected
(concurrency cap hit) calls, plus an hourly cron-driven check that alerts
once per month when estimated spend crosses a threshold and every hour
call volume looks like a burst. Uses a self-contained pipe-delimited call
log rather than Asterisk's CDR, to avoid depending on CDR module
availability and CSV comma-quoting.
- Settings persisted to .pstn-trunk.env so "update in place" reapplies
everything from that file instead of fragile re-parsing out of generated
Asterisk config (which had a real bug: update mode was extracting the
wrong Dial(PJSIP/...) line).
Tested end-to-end against a sandboxed copy of the real vendor files:
permission-gate regex, ring-group dial-string construction, ntfy line
injection/removal, and the usage-alert script's threshold/burst/monthly-
dedup logic all verified with synthetic data. Caught and fixed a sed `&`
escaping bug in the ring-group substitution before it shipped (RING_DIAL
contains literal `&` join characters, which sed's replacement syntax
otherwise treats as "insert the match").
Adds a VoIP.ms SIP trunk on top of asterisk-digital-ocean: IP-authenticated
trunk (no password stored), NANP-only outbound dialplan, a global 3-call
concurrent cap via GROUP()/GROUP_COUNT(), and inbound routing to one
extension. Config lives in its own include files rather than being
appended directly to pjsip.conf/extensions.conf, since Easy Asterisk fully
regenerates both from its own internal state — the includes are patched
into the vendor's generator functions so they survive that regeneration.
Wires the new service into setup.sh's is_installed() and README's services
table, and updates docs/pstn-calling-voipms-plan.md to reflect what's now
implemented vs. still open (spend/volume alerting, live-account
verification).
Inbound (DID) is now decided as wanted, not outbound-only. Adds a cost
estimate table for 100 min/month each direction, and clarifies that
NANP-only restriction bounds cost-per-minute but not burn speed, so the
concurrent-call cap and spend alert are required before funding a live
trunk, not optional hardening.
Captures the toll-fraud / prepaid-cap research and decisions from a design
discussion (provider: VoIP.ms, US-only calling for now) so implementation
can be picked up in a future session without re-deriving the background.
Nothing implemented yet.