Closed general

SDK: bridge_audit verb + persistent event-ring API - incident forensics is currently a 5MB log grep, and bridge event rings die on respawn

Colby Knox · 2mo ago ·closed by John Lauer

What happened (the forensic tool existed, but only as a 5 MB grep)

Root-causing adom/altium-bridge#6 hinged on ab's main log: the per-verb caller=(thread "..." reason ...) lines are what identified which thread launched the orphan agent and which web page fired the install. Getting there meant pull_file on a 5 MB Adom Bridge.log and grepping. Separately, bridge_log_read for the altium bridge returned a single line (the exe's startup print), and the bridge's own event history (dialog reaps, watchdog firings) lives in an in-process ring that dies with every respawn, which is how we lost the watchdog evidence during a freeze incident.

Ask

  1. A queryable audit verb: bridge_audit {name, since} returning the verb history ab already logs (verb, caller thread, reason, timestamp, duration, success), so incident forensics is a call, not log spelunking.
  2. An SDK event-ring API: a bridge appends structured events (the altium dialog_log pattern) and ab persists them across respawns and exposes them beside bridge_log_read. Every serious bridge reinvents this ring today, in memory, and loses it exactly when it matters.

Evidence: adom/altium-bridge#6 root-cause thread (caller attribution cracked it), the empty dialog_log after the 17:5x freeze.

2 Replies

John Lauer · 2mo ago

Triaged; design-level, holding for a proper pass (not this ship).

  • bridge_audit {name, since} returning the per-verb history ab already logs (verb, caller thread, reason, timestamp, duration, success) is a clean, high-value verb — ab already has caller provenance (aiThread/reason) and cmdId grouping, so the data exists; it needs a queryable store + the verb. I like it.
  • The SDK persistent event-ring (a bridge's dialog-reap / watchdog history surviving respawn) pairs with #125's version-drift and #126's instance-registry — they're all "the bridge's own operational state should outlive a respawn and be queryable." I'd rather design these three together than bolt on one ring.

Recorded. Will bring a design back here (or on the SDK) rather than piecemeal it.

John Lauer · 2mo ago

Shipped in 2.1.50, both halves.

bridge_audit {bridge?, thread?, verb?, since?, failuresOnly?, installGrantOnly?, limit?}: every verb ab ran, newest first, as structured rows: verb, app/bridge, WHO (aiThread, container, delegate), WHY (reason), origin (relay | local | bridge), server, durationMs, success, errorCode. The rows are captured at the one command chokepoint (handle_command start, the egress command_result end), kept in a 4000-row ring and appended to ~/.adom/audit/commands.jsonl (survives restarts, rotates at 8 MB; the ring re-warms from the file after an upgrade). since takes RFC3339 or 15m / 2h / 3d. The altium#6 question ("which thread launched the orphan agent, and which page fired the install") is one call instead of a 5 MB pull.

bridge_event_log {bridge, event, level?, data?}: the persistent event ring, held by ab. A bridge appends its own operational events (dialog reaps, watchdog firings, instance changes) and they land in ~/.adom/audit/bridges/<bridge>.jsonl, which outlives the bridge's respawn; bridge_audit {bridge} returns them as bridgeEvents next to the command rows. Loopback callers are ungated, so a bridge can call it from its callback path with no ceremony.

Both are in the CLI help and VERBS. Rows exist from 2.1.50 on; earlier history is only in the text log. Closing.

John Lauer closed this · 2mo ago

Log in to reply.