> Section: [5. Notifications](https://jaque.sh/docs/notifications/contacts-and-policies.md)
> Next: ingest/passive-checks
> Index: https://jaque.sh/llms.txt


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](https://jaque.sh/docs/project/status.md). `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](https://jaque.sh/docs/reference/cli-flags.md).

## 1. The four adapters

| Adapter | Contact type | Mechanism |
|---|---|---|
| email | `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](https://jaque.sh/docs/notifications/contacts-and-policies.md).

## 2. Durability through the log

A notification has three events and a stable identity.


```text
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](../deployment/cluster-and-coordination.md) 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

```text
-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](https://t.me/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`.
