Events Auditing

Events auditing logs all state transitions for nodes in a namespace, including both direct (own) state changes and propagated effective state changes to downstream dependents. This creates an immutable, queryable history of the dependency graph's evolution, enabling debugging, compliance, and analysis of state propagation. Events capture the previous and new values for both own state and effective_state, along with optional reason and solution metadata.

Events do not directly log webhook or email delivery outcomes—those are tracked via last_fired_at and suppressed fields in the Notifications rules table, server request logs, and structured logs in log/*.log files. However, events trigger Notifications, providing context for delivery audits.

Database Schema

Events reside in the events table, linked to a namespace via ns_id. Each event records a snapshot of state transition details.

CREATE TABLE IF NOT EXISTS events (
  id          INTEGER PRIMARY KEY AUTOINCREMENT,
  ns_id       INTEGER NOT NULL REFERENCES namespaces(ns_id) ON DELETE CASCADE,
  node_id     TEXT NOT NULL,
  previous_state TEXT,
  new_state   TEXT NOT NULL,
  previous_effective_state TEXT,
  new_effective_state TEXT NOT NULL,
  reason      TEXT,
  solution    TEXT,
  created_at  TEXT NOT NULL DEFAULT (datetime('now'))
);

Supporting indexes enable efficient queries:

CREATE INDEX IF NOT EXISTS idx_events_ns ON events(ns_id, created_at);
CREATE INDEX IF NOT EXISTS idx_events_node ON events(ns_id, node_id, created_at);
CREATE INDEX IF NOT EXISTS idx_events_node_id ON events(ns_id, node_id, id);

This schema supports fast retrieval by namespace, node, or time range. See Database Schema for the full schema.

Event Logging Process

Events are logged exclusively via dispatchNotifications in src/notify/dispatcher.ts, invoked after own state updates in the Api Reference state endpoint (PUT /v1/namespaces/{namespace}/nodes/{nodeId}/{state}).

  1. Primary Node Event: Logs the node's own state change (from previous_state to new_state) and its effective_state change (computed via Graph Computation).
  1. Downstream Propagation: Identifies downstream nodes (via Graph Computation). For each:

This design ensures auditing captures full propagation effects without redundant own-state logs.

Example from dispatchNotifications:

db.query(
  `INSERT INTO events (ns_id, node_id, previous_state, new_state, previous_effective_state, new_effective_state, reason, solution)
   VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
).run(
  nsId,
  nodeId,
  previousState,
  newState,
  previousEffectiveState,
  newEffectiveState,
  reason ?? null,
  solution ?? null,
);

For downstream nodes:

db.query(
  `INSERT INTO events (ns_id, node_id, previous_state, new_state, previous_effective_state, new_effective_state, reason, solution)
   VALUES (?, ?, ?, ?, ?, ?, NULL, NULL)`,
).run(nsId, downId, downNode.state, downNode.state, prevEff, newEff);

Events are inserted before Notifications dispatch, ensuring notifications reference fresh state.

New nodes trigger an event with previous_state and previous_effective_state as null.

CLI Access

The depends events command queries events via the API, supporting namespace/node filtering.

depends events [namespace/]nodeId? --limit N --json

Example output (non-JSON):

2024-01-15T10:30:00Z  db/migration  yellow → red — Migration failed: timeout
  2024-01-15T10:29:45Z  api/user-auth (new) → yellow

Implementation in src/cli/commands/events.ts fetches /events/{ns}/{nodeId}?limit={limit}&order=desc, formats with colors, and shows (new) for initial events.

See Cli.

API Access

GET /v1/namespaces/{namespace}/events/{nodeId?} supports pagination and filtering:

Returns { events: Event[] }, where Event matches the table schema.

Handled in src/routes/events.ts. Requires Operating Modes; respects Rate Limiting.

See Api Reference.

Retention and Purging

Events older than 30 days are automatically purged hourly via purgeExpiredEvents in src/purge.ts:

const cutoff = new Date(Date.now() - 30 * 24 * 60 * 60 * 1000).toISOString();
db.query("DELETE FROM events WHERE created_at < ?").run(cutoff);

This balances audit history with storage efficiency. See Data Purging.

Usage in Other Components

This system ensures transparent, propagatable state auditing without performance overhead on writes.

Recent changes