Add sms-inbound: verification codes from a VoIP DID to ntfy push
New service for one narrow job — getting SMS verification codes sent to a
VoIP number onto a phone with no SIM. Deliberately not a texting app: no
outbound path (Anveo Direct has none; that needs an Anveo Retail account, and
a free texting app covers sending), and messages arrive as push notifications
rather than being routed into Asterisk as SIP MESSAGE, since a code you read
and type is better served by a notification than a softphone chat thread.
Two modes, both driven entirely from the provider's "forward SMS to URL" box:
- direct — the provider calls ntfy itself; nothing installed here. ntfy
accepts GET publishing at /{topic}/(publish|send|trigger) with message and
title as query params, and auth via ?auth= holding base64url (unpadded) of
the literal "Bearer <token>" — confirmed against ntfy's server.go and
server_auth.go rather than its docs.
- relay — a stdlib systemd service, Caddy-fronted on its own domain with no
Authelia (the provider can't log in; a random 32-char token in the path is
the secret). Buys two things direct mode can't have: an unescaped "&" in a
message body survives intact, because the relay takes everything after the
last message= verbatim instead of parse_qs — which is why the generated URL
always puts the message placeholder last — and no ntfy credentials sit in a
third party's web portal.
Verification codes are bearer credentials, so: a 24-char random topic name
(the repo's ntfy defaults to auth-default-access: read-write, making the topic
name the read credential), constant-time token compare, a 60/min rate limit,
and the relay logs sender/recipient/length but never the message body.
The Anveo guide gains a section covering the two things that actually decide
whether codes arrive: short-code support (Anveo has it, unusually — VoIP.ms
does not except for Google) and Anveo's carrier-sourced *mobile* DIDs, which
are classified as mobile in the lookups that reject VoIP numbers at signup.
Also documents MMS and group texts being out of reach, and why the native
Messages app never sees any of this.
Verified against a stub ntfy: plain OTP, encoded "&", unencoded "&", "+" as
space, wrong token (404), missing message (400) and the rate limit (57x204
then 429) all behave; both installer modes were run end to end in a sandbox
and their generated URLs, settings files and READMEs checked.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NAddJGE1G6eGaPzmScG5Vh
This commit is contained in:
@@ -199,6 +199,93 @@ In the Security Dashboard's PSTN Trunk tab:
|
||||
# container is named easy-asterisk-do instead
|
||||
```
|
||||
|
||||
## 8. SMS — receiving verification codes
|
||||
|
||||
Voice and SMS are separate features on an Anveo DID and are configured in
|
||||
different places. This section covers **receiving** only; see "What about
|
||||
sending?" below for why.
|
||||
|
||||
### Pick the right kind of number first
|
||||
|
||||
Anveo sells two classes of US DID, and for verification codes the difference
|
||||
matters more than anything else in this section:
|
||||
|
||||
- **Geographic (default)** — the cheap ones this guide orders in step 2
|
||||
($0.25 setup, $0.15/month). Industry lookups classify these as VoIP.
|
||||
- **Mobile** — a separate pool sourced from wireless carriers, available
|
||||
across roughly 20 major US city area codes (released on Anveo Retail first,
|
||||
then Direct). These are classified as *mobile* in the same databases that
|
||||
services query when they decide whether to accept your number. Priced above
|
||||
the geographic ones — check the DID ordering tool for the current rate.
|
||||
|
||||
Plenty of services (Google, WhatsApp, Microsoft, many banks) reject a number
|
||||
that looks like VoIP at signup, before any message is ever sent. **If codes
|
||||
are the reason you're buying the number, order a mobile one** — no amount of
|
||||
correct SMS routing fixes a signup form that refuses the number outright.
|
||||
|
||||
### Short codes
|
||||
|
||||
Most verification codes come from short codes (262966, 32665, ...), and most
|
||||
VoIP providers don't deliver them at all — VoIP.ms, for instance, doesn't
|
||||
except for Google, and users there report a large fraction of 2FA codes never
|
||||
arriving. Anveo is unusual in supporting short-code SMS to its DIDs, which is
|
||||
the main reason it's worth using for this.
|
||||
|
||||
Not every number in the pool has it enabled, so confirm on your specific DID
|
||||
(or ask support to turn it on) rather than assuming.
|
||||
|
||||
### Wire it up
|
||||
|
||||
Run the installer and follow what it prints:
|
||||
|
||||
```bash
|
||||
sudo ./setup.sh sms-inbound
|
||||
```
|
||||
|
||||
It generates a long random ntfy topic, then offers two ways for Anveo to
|
||||
reach it:
|
||||
|
||||
- **Relay (recommended)** — a small systemd service on the droplet receives
|
||||
Anveo's request and republishes to ntfy properly. Two concrete wins: a
|
||||
message body containing `&` survives intact (Anveo interpolates the text
|
||||
into the query string unescaped, so an unencoded `&` otherwise truncates
|
||||
the message), and your ntfy credentials never get stored in Anveo's portal.
|
||||
- **Direct** — Anveo calls ntfy itself; nothing runs on the droplet. Simpler,
|
||||
but the URL you paste into Anveo carries your ntfy token, and the `&` case
|
||||
loses the tail of the message.
|
||||
|
||||
Then in the Anveo portal: **Phone Numbers → the DID → SMS tab**, set the
|
||||
destination to **URL**, and paste the string the installer printed. Keep the
|
||||
`$[message]$` placeholder **last** — that's what makes the unescaped-`&` case
|
||||
recoverable.
|
||||
|
||||
Send a text to the number from another phone; the notification should arrive
|
||||
within seconds. `journalctl -u sms-inbound -f` shows sender, recipient and
|
||||
message length (never the body — these are one-time passcodes and the journal
|
||||
has a wider audience than the notification does).
|
||||
|
||||
### What about sending?
|
||||
|
||||
Not covered, on purpose. Outbound SMS isn't available on Anveo Direct — Anveo
|
||||
support directs users to an Anveo **Retail** account for it, which is a
|
||||
second account to fund and manage. Any of the free texting apps covers
|
||||
sending without involving this box.
|
||||
|
||||
### MMS and group texts
|
||||
|
||||
Don't plan on either. No VoIP provider delivers MMS over SIP, and MMS to a
|
||||
VoIP DID generally drops or arrives as a media link through a separate API.
|
||||
US group texts are MMS, so a SIM-less phone on this number will silently miss
|
||||
them.
|
||||
|
||||
### The native Messages app never sees these
|
||||
|
||||
Android's Messages app reads the telephony SMS provider, which only the
|
||||
cellular radio (or whichever app holds the default-SMS-app role) writes to;
|
||||
iOS lets nothing write to Messages at all. Codes arrive as ntfy push
|
||||
notifications instead — which, for a passcode you're about to read and type,
|
||||
is the more useful place anyway.
|
||||
|
||||
## Bugs hit and fixed along the way (informational — already fixed)
|
||||
|
||||
These were all real, confirmed-live bugs in earlier versions of this
|
||||
|
||||
Reference in New Issue
Block a user