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:

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).

  1. Compute new effective state for the updated node using graph computation.
  2. Log the event to the events table Events Auditing, capturing raw and effective states.
  3. Identify affected nodes: the updated node (if effective changed) plus downstream dependents (via getDownstreamNodes).
  4. For each downstream node, recompute effective state; log events and add to affected list if changed.
  5. Query active rules (suppressed = 0).
  6. For each affected node and matching rule:

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:

MethodEndpointDescription
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}/notificationsList 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