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}).
- Primary Node Event: Logs the node's own
statechange (fromprevious_statetonew_state) and itseffective_statechange (computed via Graph Computation).
- Downstream Propagation: Identifies downstream nodes (via Graph Computation). For each:
- Recomputes
effective_state. - If changed (compared to the last event's
new_effective_state), logs an event. - Own
statefields remain unchanged (previous_state=new_state= current ownstate); only effective fields differ. - No
reason/solutionfor propagated events.
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:
?limit=N(1–1000, default 100)?order=desc|asc(byid)?since=ISO-date(e.g.,2024-01-01T00:00:00Z)
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
- Billing/Usage: Monthly event counts track activity (
/v1/namespaces/{ns}/usage). - Effective State: Events provide the prior
effective_statebaseline for propagation. - Invariants: Events enforce Project Invariants like state transitions.
- Server Logs: All API requests (including event queries) log to
log/{date}.logfor delivery audits.
This system ensures transparent, propagatable state auditing without performance overhead on writes.
Recent changes
- Created: Events auditing logs namespace node state transitions with schema and metadata