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:
Claude
2026-07-25 02:13:39 +00:00
parent 51d090ee2c
commit e1c3203d88
5 changed files with 719 additions and 1 deletions
+87
View File
@@ -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