Appearance
Alarm notification
Nexus can call, text and email people when an alarm trips, work down an escalation ladder until somebody acknowledges, and respect an on-call schedule so the right person is woken.
This page covers the parts an administrator owns: the licence, the delivery accounts, and what to check when a notification does not arrive. Who is on call and what escalates to whom is authored in Studio, in its Twilio workspace — an engineering task, not an operations one.
The failure to watch for is notifying nobody
A roster that reaches no one looks exactly like a roster that is armed. It stays quiet, and quiet is what you expect between alarms. Nexus refuses or flags the configurations that can never reach anybody — see When a save is refused — but nothing can detect a phone number that was simply typed wrong. Test the ladder after you build it.
Licensing
Notification is three separate add-ons. They are not included in a default licence — a Nexus that has never been sold them has the feature switched off entirely.
| Entitlement | Covers |
|---|---|
alarmNotification | The feature itself: contacts, rosters, escalation, and email delivery. |
notifySms | Sending SMS. |
notifyVoice | Placing voice calls. |
Without alarmNotification, the configuration endpoints answer HTTP 402 with {"error":"LICENSE_NOT_ENTITLED","module":"alarmNotification"}, and Studio hides the Twilio workspace from its rail rather than showing an editor that cannot save. If an engineer reports the workspace is missing, check the licence before checking anything else.
notifySms and notifyVoice are separate because a site that only wants text messages should not pay for voice. They gate delivery, not configuration — an unlicensed channel can be authored and will simply never send.
Setting up delivery
Both accounts are read once, at start-up. Saving either one takes effect on the next restart.
Email
SMTP has no configuration screen. It lives in the smtp section of raylux_project.json in the data directory, and is edited with the service stopped.
json
{
"smtp": {
"host": "smtp.example.com",
"port": 587,
"username": "nexus@example.com",
"password": "…",
"fromAddress": "nexus@example.com",
"useTls": true
}
}| Field | Notes |
|---|---|
host | Required. An empty host means email is not configured, and no email is attempted. |
port | Defaults to 587. Also selects how TLS is done — see below. |
useTls | Defaults to true, and required if the relay wants credentials. Turning it off sends the username and password in clear. |
fromAddress | What recipients see. Some relays reject a from they do not own. |
The port decides which kind of TLS is used, because that is what the port means everywhere else:
| Port | What happens | Who uses it |
|---|---|---|
587 | Connect, then STARTTLS. Required — Nexus will not fall back to cleartext. | Gmail, SendGrid, Office 365, Mailgun, Brevo. The usual choice. |
465 | TLS from the first byte (SMTPS). | Also offered by most of the above. |
| anything else | STARTTLS, as with 587. | An in-plant relay on 25 or 2525. |
You do not configure the mode separately. Set useTls to say whether TLS is required at all, and the port says which form it takes.
Pointing 465 at a STARTTLS port, or the reverse, does not degrade — it fails
The two are different protocols, not different strengths of the same one. If a relay's documentation says 587, use 587.
The password is redacted in a configuration backup
A backup downloaded from the web interface omits the SMTP password, so restoring one onto a fresh machine leaves email unconfigured until you set it again. A full data-directory backup keeps it.
SMS and voice
Twilio credentials do have a screen: sign in as an administrator and go to Config ▸ SMS (/config/sms).
| Field | Where to find it |
|---|---|
| Account SID | Twilio console. Also used as the API username. |
| Auth token | Twilio console. Write-only — Nexus tells you whether a token is stored, never what it is. |
| From number | A number on your Twilio account, in E.164 form (+15125550142). |
A saved token is never shown again
Reading the configuration back reports whether a token is stored, not the token. This is deliberate: an administrator's browser session should not be able to exfiltrate the account credential. To change it, enter a new one.
A trial Twilio account can only reach verified numbers
On a Twilio trial, every destination number must be verified in the Twilio console first. An unverified number fails at Twilio, not at Nexus, so the Nexus log records a send that was rejected rather than a configuration problem. This catches most first-time setups.
How someone is chosen
When an alarm trips, Nexus resolves the escalation policy's current stage to a list of people, then sends.
On-call windows. A roster entry may carry windows like Mon–Fri 08:00–17:00. Two behaviours are worth knowing:
- A window whose end is not after its start wraps past midnight.
22:00–06:00is the night shift, not an empty window. dayslists the days a window starts on. A window starting Friday night is still active at 02:00 Saturday, even though Saturday is not in its days — which is what "Friday nights" means to the person carrying the phone.- An entry with no windows at all is on call always. That is the common case for a small plant with one maintenance number.
Fallback contacts. A roster can name people to use only when the schedule leaves a hole — a rota gap at 3am on a public holiday. A fallback is never a second rota; it is consulted only when nobody is scheduled.
Channels are intersected. A stage that asks for SMS reaches only the roster members who offer SMS. A stage that names no channels uses whatever each entry declares, which is the simplest thing to author and cannot miss.
Escalation
A policy is an ordered list of stages. Each names a roster, optionally some channels, and an acknowledgement timeout.
| Setting | Meaning |
|---|---|
| Ack timeout | Seconds to wait for an acknowledgement before moving to the next stage. Default 300. |
Ack timeout 0 | Do not wait — fire this stage and move on immediately. This is how a fan-out to several groups at once is authored. |
Two behaviours protect you from a ladder that stalls:
- An acknowledgement stops the ladder, including a timer already running.
- A stage that can reach nobody is skipped, not waited out. Waiting out a five-minute timeout on a stage with no reachable recipient is the failure that looks healthy — the alarm ages while nothing happens.
When a ladder runs out of stages it reports as exhausted rather than looping.
When a save is refused
Nexus checks the notification configuration when it is saved, and separates problems it will not store from ones it will.
Refused (HTTP 400). The configuration is not saved. Each problem names a path such as notification.rosters[0].entries[1].contact:
| Problem | Why it cannot be stored |
|---|---|
| A roster entry, stage or fallback naming a contact or roster that does not exist | Nothing can make a missing id resolve except fixing the id. |
| An on-call window with no days selected | It can never match any day, so that entry is never on call through it. Remove the window to be on call at all times. |
| An escalation policy with no stages | It can never notify anyone. |
Saved, with a warning. The configuration is stored, and the warning is reported on every save until it is resolved:
| Warning | Why it is allowed |
|---|---|
| An entry naming a channel the contact has no address for | Becomes correct the moment the contact is given that address. Rosters are routinely written before contact details are. |
| A stage naming channels no roster entry offers | Becomes correct when some entry offers that channel. |
The line is whether the problem can be fixed by editing something else. A missing id or an empty window can only be fixed where it is wrong, so storing it would persist something with no path to becoming right. Studio shows these warnings on its status line after a successful save, and Nexus records each one in the log:
[warning] [NotificationController] Saved with a warning at
notification.rosters[0].entries[0].channels[0]: contact 'bob' has no email
address, so the 'email' channel can never reach themNotification configuration reloads without a restart
Unlike the SMTP and Twilio credentials above, a change to contacts, rosters or escalation policies takes effect immediately. Studio says so when it saves.
When nobody was notified
Work down this list in order.
Is the licence entitled? No
alarmNotificationmeans the feature is off. Studio hides the workspace; the API answers 402.Is delivery configured? An empty SMTP
host, or missing Twilio credentials, means nothing is attempted. Both are read at start-up — if you set them and did not restart, they are not in effect.Did you restart after changing credentials? This is the most common cause of "it worked in the config screen and nothing arrived."
Was anybody actually on call? Check the log around the alarm time. Nexus records why a resolution was empty, and the remedy differs:
What the log says What to fix Nobody was on call and there is no fallback A schedule gap. Add a window, or give the roster fallback contacts. People are on call but none can be reached on the requested channels Missing contact details, or a stage asking for a channel the roster does not offer. Is the relay rejecting the message? The log records the SMTP status alongside curl's own description, and the status is the useful half — curl reports a perfectly clear rejection as "Weird server reply" when it cannot parse the reply text.
Status Meaning 550Permanent refusal. The relay will never accept this message — wrong sender domain, failed SPF/DKIM, or a recipient it will not serve. 535Authentication failed. Check the username and password. 421Temporary. The relay is busy or throttling; a retry may work. A forwarding-only address is not a relay
An address that merely forwards elsewhere — Cloudflare Email Routing, and most "alias" services — typically refuses to forward mail that is not authenticated, because forwarding spoofed mail would put its own reputation behind it. Sending straight to such a domain's mail exchanger gets
550 5.7.26even though every earlier step succeeded. Send through a relay that authenticates you instead, and let the forwarder do its job.Is the address right? Nexus does not validate or normalise a phone number — that belongs to the delivery provider, and rejecting a number locally would silently drop someone from the roster over a formatting opinion. A malformed number fails at Twilio and is recorded in the log.
Read the log. Every send and every failure is recorded with a
[NotificationController]or delivery-driver prefix. See Running Nexus for where the log lives.
See also
- Backup and restore — the SMTP password is not in a configuration-only backup
- Security hardening — third-party account credentials on a plant network
- Where files live — the data directory that holds
raylux_project.json