Kodori is event-sourced. Every consequential action — create, version, share, revoke, classify, hold, release, dispose — appends a row to an immutable events table that's hash-chained per tenant. That means the system can prove the audit trail wasn't tampered with after the fact.
/audit shows the chronological event stream with filters by event type. Each row records actor (user, agent, or system), timestamp, payload, and the previous-event hash that links into the chain. Revertable events (collection adds/removes, permission grants/revokes) carry a one-click "Revert" button — clicking it appends a fresh forward event that inverts the prior one, so the chain stays intact.
**Filter the log** by event type (organized into ~15 chip groups: Documents, Permissions & legal, Share links, Productions, Citation + saved-search alerts, Compliance + audit, AEC drawing-set integrity, API keys + digests, Tenancy & billing, etc.) and/or by date range and/or by actor (paste an email substring, agent, system, or actorId). The **Stream** filter takes a substring match against streamId — paste `share-link/`, `legal-hold/<id>`, or any partial id and the audit log filters to events on streams matching that substring. Standard query: paste a matter id from /legal-holds/[id] into the Stream filter and see every event on that hold's audit chain in chronological order. Filters are sticky — applying a stream filter and then clicking a type chip preserves the stream selection.
Per-document history lives on the document detail page ("History" section) — the same events filtered to streamId = document/<id>.
**CSV export** — the "Export filtered to CSV" link respects every filter you've set (types, from / to dates, actor, stream). An auditor downloading "every event on share-link/<id>" gets exactly that subset.
**"Since my last visit" filter.** Admin returning from PTO clicks the "Since my last visit" chip at the top of the filter card to see only events appended while they were away. Per-user — each admin gets their own "since" baseline (your timestamp doesn't reset when a colleague visits /audit). Visible only when you have a previous visit recorded (first-ever visitor: chip hidden). Plays well with the existing type / date / actor / stream filters — they all AND together. Excluded from CSV / JSONL exports because the export's downstream audience may not be the same user; use the from/to date filters for explicit export ranges.
**Inline diff badges on mutation events.** Mutation events that carry a from/to in their payload — tenant settings updates, retention class changes, sensitivity shifts, API key scopes / expiration / rate limit, webhook retry policy — surface a compact `<from> → <to>` chip directly in the row summary. Read the change at-a-glance instead of expanding the row to read the JSON. Multi-field events (tenant.settings-updated) render up to 3 chips with the field name prefixed; single-value diffs render unprefixed. Long values truncate at 30 characters with a hover tooltip showing the full from → to. Non-mutation events (document.created, share-link.accessed, etc.) render unchanged — the extractor returns nothing when there's no recognized diff shape, so the inline-badge presence itself is a signal that "this event changed something."
**Saved filter presets.** The filter combinations admins re-build daily — compliance review, oncall triage, vendor incident lookback — can be saved with a click. Apply your filters, click "+ Save current filter", give it a name, hit Enter. The saved preset shows up as a chip in the Saved row at the top of the filter card. Click the chip to load the filter in one click. Hover the chip to see the saved filter description ("3 types · 2026-04-01 → 2026-04-30 · actor ~ counsel@firm"). Saved filters are per-user, per-tenant — your "monthly SOC 2 review" preset doesn't leak to your colleagues, and theirs don't show up in your bar. Cap is 50 saved filters per user; the × on each chip deletes a preset. Empty filter sets ("save a filter with nothing applied") are refused — saved filters always carry at least one of types / date range / actor / stream.
The audit log is the substrate underneath the SOC 2 Type II posture, the 21 CFR Part 11-capable trail, and any FRCP / depo discovery you might face.
**Verify chain integrity (admin / owner only).** The "Verify chain integrity" button at the top of /audit walks every partition of your tenant's chain (D287 chain-of-chains; D289 per-partition UI) and surfaces a per-partition status list. If anything fails, the failing partition's first-mismatch detail renders inline so you can pinpoint exactly where tampering occurred. The weekly Sunday 02:00 UTC cron (D288) walks the same partitions automatically and emits one audit.verification.completed event per (tenant, partition) onto the audit-verification/<tenantId> stream — auditors filter that stream to see every weekly proof going back arbitrarily far.