Notifications
Notifications in the depends project alert users to changes in the effective state of nodes via webhooks or emails. Effective state changes propagate through the dependency graph, ensuring notifications reflect the "worst-case" status across dependencies. This design decouples raw node states from alerts, focusing on actionable impacts.
The system triggers only on actual effective state transitions (green, yellow, red), computed recursively from dependencies. Rules define conditions for firing notifications, stored per namespace in the notification_rules table.
Notification Rules
Each namespace supports multiple rules, identified by a unique id. A rule specifies:
watch: Node ID to monitor (*for all nodes).on_state: Effective states that trigger it (e.g.,red,yellow,red, or*).url: Webhook endpoint (optional).email: Recipient address (optional; defaults to token owner's email).secret: HMAC signing secret for webhooks (optional).ack: Boolean; if true, suppresses the rule after firing until manually acknowledged.ack_token: Opaque token for ack URLs (generated ifackis true).suppressed: Temporary disable flag.last_fired_at: Timestamp of last dispatch.
Rules require either url or email. The schema enforces this:
CREATE TABLE IF NOT EXISTS notification_rules (
ns_id INTEGER NOT NULL REFERENCES namespaces(ns_id) ON DELETE CASCADE,
id TEXT NOT NULL,
watch TEXT NOT NULL DEFAULT '*',
on_state TEXT NOT NULL DEFAULT 'red',
url TEXT,
email TEXT,
secret TEXT,
ack INTEGER NOT NULL DEFAULT 0,
ack_token TEXT,
suppressed INTEGER NOT NULL DEFAULT 0,
last_fired_at TEXT,
CHECK (url IS NOT NULL OR email IS NOT NULL),
PRIMARY KEY (ns_id, id)
);
Suppressed rules (e.g., after ack firing) skip dispatch until reset.
Triggering Logic
Notifications dispatch via dispatchNotifications in src/notify/dispatcher.ts, invoked from state update handlers (e.g., PUT /nodes/{namespace}/{nodeId} in src/routes/state.ts).
- Compute new effective state for the updated node using graph computation.
- Log the event to the
eventstable Events Auditing, capturing raw and effective states. - Identify affected nodes: the updated node (if effective changed) plus downstream dependents (via
getDownstreamNodes). - For each downstream node, recompute effective state; log events and add to affected list if changed.
- Query active rules (
suppressed = 0). - For each affected node and matching rule:
- Match
watch(node ID or*) andon_state. - Build [payload](#payload-format).
- Send webhook/email if configured.
- Charge credits best-effort (2 per delivery).
- Update
last_fired_at; suppress ifack.
No notifications fire if no effective states change. Propagation to downstream ensures alerts for indirect impacts (e.g., a deep dependency fails).
Example matcher functions:
function ruleMatchesState(rule: NotificationRule, state: string): boolean {
if (rule.on_state === "*") return true;
const states = rule.on_state.split(",");
return states.includes(state);
}
function ruleMatchesNode(rule: NotificationRule, nodeId: string): boolean {
return rule.watch === "*" || rule.watch === nodeId;
}
Payload Format
Both webhooks and emails use a standard WebhookPayload:
export interface WebhookPayload {
event: string; // "effective_state_changed"
namespace: string;
node_id: string;
state: string; // Raw state (matches effective_state)
effective_state: string; // New effective state
previous_effective_state: string;
reason: string | null;
solution: string | null;
triggered_rule: string; // Rule ID
timestamp: string; // ISO
ack_url?: string; // If ack enabled
title: string; // e.g. "ns/node is red"
body: string; // Reason or prev state
}
Emails render HTML via Eta templates; subjects include state emojis (🔴 🟡 🟢).
Webhooks
sendWebhook in src/notify/webhook.ts POSTs JSON payloads with retries (3 attempts, exponential backoff). If secret provided, signs with X-Signature (HMAC-SHA256 hex).
Failures log details (status, error) but do not block dispatch.
Emails
sendEmail in src/notify/email.ts uses Nodemailer (SMTP env vars required: SMTP_HOST, etc.). Skips if unconfigured, logging email_skipped. Renders notification.eta template.
Signup emails (separate) send API tokens.
API Endpoints
Handled in src/routes/notifications.ts:
| Method | Endpoint | Description |
|---|---|---|
PUT | /v1/namespaces/{ns}/notifications/{ruleId} | Create/update rule. Body: {watch?, on?, url?, email?, secret?, ack?}. on array/string → comma-joined. |
GET | /v1/namespaces/{ns}/notifications | List rules (formatted, secrets masked). |
DELETE | /v1/namespaces/{ns}/notifications/{ruleId} | Delete rule. |
POST | /v1/ack/{ack_token} | Unsuppress ack-enabled rule. |
Validation ensures id, delivery method. Emails pull from token owner.
See API reference for full details.
Integration
Notifications tie into core model states, graph computation for propagation/TTL, events auditing for history, and rate limiting on API. Self-hosted mode Operating Modes requires SMTP for emails; hosted uses managed infra. Credits charge per delivery Cli.
Recent changes
- Created: Added notifications.md wiki page on alerts, rules, and effective state changes