Hardening
A checklist for taking a working setup to a production one. Most items take minutes; the ones that do not are marked.
Transport
- Serve enrolment and the socket over TLS only. The node refuses
plaintext in release builds, but check that no reverse proxy is
terminating TLS and forwarding
ws://over an untrusted hop. - Consider certificate pinning. Deliver a SHA-256 SPKI pin in the QR
payload’s
pfield at pairing time. - Always configure a backup pin before enabling pinning.
Pinning without a backup pin is how you brick a fleet. When the certificate rotates and no deployed node accepts the new one, recovery requires physical access to every device.
Credentials and secrets
-
LUNO_SECRETcomes from a secret manager, never from source control. - Rotate device credentials on a schedule, and immediately when an operator with backend access leaves.
- Pairing sessions use short expiries and
maxEnrollments: 1. -
enrollmentIdis generated from a CSPRNG with at least 128 bits of entropy. Verify this if you wrote your own store or backend — it is a bearer secret.
Authorization
- Every route that reaches
luno.sms.sendis authenticated. This is the single most consequential item on the list. - Scope every device lookup yourself.
luno.devices.list()takes no filter and returns the whole fleet — the engine does not know who your users are. Filter by the caller’s identity before responding, and never by an id the caller supplied. - Admin operations — creating pairing sessions, revoking devices — are restricted separately from send access.
Rate limits and allowlists
- Set
rateLimitPerMinuteto a value your SIM’s plan tolerates, not to the maximum you might ever want. - Set an
allowlistif your traffic goes to a predictable set of numbers or country prefixes. - Add your own server-side limit as well. The device-side limit protects the SIM from your backend; a server-side limit protects your backend from its own callers.
Carriers apply anti-spam heuristics to consumer plans and will block a SIM that behaves like a bulk sender. There is rarely a warning, and reinstating a blocked SIM is a slow manual process. Start conservative.
The device itself
- Screen lock enabled with a non-trivial PIN.
- Full-disk encryption on (default on modern Android).
- Developer options and USB debugging off after setup.
- The handset is physically secured — a gateway phone in an unlocked drawer is an unlocked gateway.
- The SIM has a PIN, so removing it and putting it in another handset is not trivially useful.
- Consider a dedicated SIM with a spend cap, so a runaway loop has a financial ceiling.
Monitoring
- Alert on heartbeat gaps — a node that stops reporting.
- Alert on rising queue depth with a healthy link, which means sends are failing.
- Alert on a rising
undeliveredrate, which often means the SIM has been flagged or has lost service. - Alert on repeated
AUTHerrors, which usually means a silently revoked permission or an expired credential. - Log
errorevents with theirref, so failures can be traced back to the request that caused them.
Data handling
- Decide how long you retain message bodies on your server, and enforce it. Luno encrypts them on the device; what happens after they reach you is entirely your responsibility.
- Confirm your own logs do not capture message bodies or phone numbers. The node redacts at source; your handlers can undo that in one careless line.
- Know your obligations. Operating an SMS gateway means processing personal communications, with the legal consequences that follow in your jurisdiction.
Before you call it production
- A device has survived 48 hours untouched and still sends. See OEM reliability.
- A reboot brings the node back with no interaction.
- A revoked device stops working immediately and does not reconnect.
- Your handlers are idempotent — verified by replaying the same event twice.
- You have tested what happens when the node is offline for an hour.