Files
mesh-catalog/modules/audit-logger/audit.ts
T
jschoubben 43625ec03f audit-logger: record the event's own x-event-id (ADR 0047)
The trail's id is now the event's x-event-id — the handle a reader dedups
the at-least-once stream on — not the type@time placeholder the first cut
used. Carries causation/schema through when present.

Claude-Session: https://claude.ai/code/session_01LrgweAeERJYBg88c5cKDzF
2026-09-04 00:26:10 +02:00

31 lines
1.3 KiB
TypeScript

// The audit handler — appends one line per event to an append-only audit log. It is the whole of
// the module's code: an audit logger is not a privileged component, only a module that listens to
// everything and writes it down (novox/hq ADR 0046).
import { appendFile, mkdir } from "node:fs/promises";
import { dirname } from "node:path";
import type { Event } from "@novox/mesh-sdk/events";
/** Where the trail is written. A directory the host applies; one file per node. */
export function auditLogPath(env: NodeJS.ProcessEnv = process.env): string {
return env.AUDIT_LOG ?? "/var/lib/audit-logger/audit.log";
}
/** Append an event to the trail as one JSON line, keeping the metadata an audit needs first. The id
* is the event's own x-event-id (ADR 0047) — the handle a reader dedups the at-least-once trail on. */
export async function record(event: Event, path: string): Promise<void> {
const line =
JSON.stringify({
id: event.id,
type: event.type,
source: event.source,
node: event.node,
at: event.at,
...(event.causationId ? { causationId: event.causationId } : {}),
...(event.schema ? { schema: event.schema } : {}),
body: event.body,
}) + "\n";
await mkdir(dirname(path), { recursive: true }).catch(() => {});
await appendFile(path, line, { mode: 0o600 });
}