An adapter turns a notification decision into a side effect: an SMTP session, an HTTP POST, a process, a Telegram message. jaque has four, one per contact type, and none of them is trusted to remember anything. Every decision is written to the event log before an attempt is made and its outcome is written after, so whoever delivers can be killed, restarted or replaced by another process and resume from the log with no duplicate that already landed and no notification still owed. Delivery is at least once, by design, and idempotent by notification ID.
Status: the path is tested end to end against a fake receiver, including
a flaky one, but no deployment has paged a human yet; see the
status page. JAQUE_TELEGRAM_TOKEN is the
environment form of -telegram-token by the general rule that every flag
has a JAQUE_ prefixed variable, documented in CLI flags.
1. The four adapters
| Adapter | Contact type | Mechanism |
|---|---|---|
email |
net/smtp with STARTTLS and optional PLAIN auth, or the system mail binary |
|
| webhook | webhook |
HTTP POST to the contact's url |
| command | command |
a top-level command template run as a process, the Nagios model |
| telegram | telegram |
the Bot API's sendMessage to the contact's chat_id |
The command adapter is the primitive: it is how Nagios has always
delivered, and it is what the email adapter falls back to when no relay
is configured. Anything not listed is reached through webhook or
command; there is no plugin interface for adapters. The contact side
of this table is specified in
Contacts and policies.
2. Durability through the log
A notification has three events and a stable identity.
Text version
engine decides deliverer attempts log
-------------- ------------------ ---
NotifyDecision -------> NotificationRequested -----> appended BEFORE the attempt
adapter.Send(...)
NotificationSent -----> appended on success
or NotificationFailed -----> appended on permanent failure
NotificationRequested is appended before any adapter is called. The
deliverer, whichever process that is, folds the log on startup to find
every NotificationRequested without a terminal NotificationSent or
NotificationFailed and resumes exactly those. The notification ID is
derived from the object, the kind, the level and the instant the problem
began, never from the current time, so a re-notification of the same
problem and a replayed request after a crash produce the same ID and are
deduplicated against it.
The guarantee is at least once. If ownership of delivery moves between processes while an attempt is in flight, the new owner may repeat a send that the old one had started; it will never skip one. Retries use backoff and stay inside one delivery shard, so two attempts for one ID never run concurrently.
3. Where delivery runs: -notify-delivery
The engine that owns an object always decides whether to notify and writes that decision to the log. Who performs the side effect is a separate switch.
-notify-delivery |
Who delivers |
|---|---|
embedded (default) |
the same process that decided, under -target all or -target engine |
external |
one or more -target notifier processes, each pulling its share of open notifications from the log |
Under external, several notifiers split the work by object ownership
over their own membership bucket; Cluster and
coordination describes the
split. A -target notifier process requires -notify-delivery external
on the engine upstream, or it finds nothing to deliver.
In either mode delivery is sharded by object across -notify-workers
goroutines (default 4), each with a bounded queue of -notify-queue
notifications (default 64). A slow receiver fills its shard's queue and
then blocks the notifier's log follower; nothing is dropped for being
slow. The block is visible as jaque_notifier_blocked_total counting up
and jaque_eventlog_consumer_lag{follower="notifier"} growing, and
jaque_notifier_pending shows how many notifications sit in the shards.
4. SMTP
-smtp-host, -smtp-port (default 25), -smtp-user, -smtp-pass, -smtp-from
With -smtp-host set, email contacts are delivered over SMTP with
STARTTLS; -smtp-user empty disables authentication. With -smtp-host
empty, email contacts are delivered by running the system mail binary
through the command adapter, which suits a host that already has local
mail delivery configured and nothing else.
5. Telegram
A telegram contact carries only a chat_id. The bot token is one per
jaque instance and is a process secret, set once via -telegram-token or
JAQUE_TELEGRAM_TOKEN, the same posture as SMTP credentials. With the
token empty, every telegram contact fails permanently rather than
silently.
To obtain a token, talk to @BotFather, send
/newbot and keep the token it returns. To find a chat_id, message the
bot (or add it to a group), then read
https://api.telegram.org/bot<token>/getUpdates: the chat.id on the
message you sent is the value for the contact.
6. Security considerations
Every credential an adapter needs -- the SMTP password, the Telegram bot
token -- is a process-level flag or environment variable and never part
of the CUE config, so a configuration can be reviewed and shared without
carrying a secret. The command adapter runs processes on the delivering
host with the arguments the commands map specifies; that map is trusted
configuration. Webhook and Telegram deliveries are outbound HTTPS to
operator-chosen endpoints; the notifier opens no listener of its own
beyond /metrics on -listen.