Curtis AI Audit

approvedunreviewed

by Jordan Newell

Local-first audit trail for AI agent activity. Every agent inference gets an append-only markdown receipt in your vault.

Updated todayMIT
Curtis AI Audit — green C ring with a check

Curtis AI Audit

Local-first audit trail for AI agent activity in your Obsidian vault. Every agent inference gets an append-only markdown receipt — a journal you keep on your own agents, not a forensic system, and honest about the difference (see SPEC.md).

Speaks OM-1 (Obsidian Messenger): agents, other plugins, or a shell one-liner can all file receipts — see INTEGRATION.md. The messenger plugin is not required — the audit inbox is just a file.

What it does

  • Receipts — one audit.v1 block per agent action: who, what, when, model, duration, tokens, status, plus a rendered human summary.
  • Per-day ledger — append-only shards under Audit/ledger/YYYY-MM-DD.md. Envelope = the receipt as canonical JSON; body = the summary. Readable in Obsidian, parseable by anything.
  • Ingest — polls the OM-1 audit inbox (Messenger/audit/inbox.md) with proper cursor semantics; junk is rejected, redeliveries are deduped.
  • Agent hooks — Install agent audit hooks writes vault-scoped configs (Claude Code, OpenCode) so local agents file their own receipts through the bundled writer (bin/curtis-audit-file.mjs); Codex and Gemini get manual recipes. Metadata only — tool input content is never recorded. Source installs only: the writer ships with the repo, not with plugin releases (Paths A and B work everywhere).
  • Direct API — in-process plugins can skip the bus: window.curtisAudit.record({ agent, model, tokens, ... }).
  • Query — the read side: window.curtisAudit.query({ agent, status, since, ... }) for status bars, dashboards, and other plugins.
  • Verify — detects malformed blocks, duplicate ids/sequences, reordered shards, and per-agent sequence gaps (deletions leave holes; no crypto, no false promises).
  • Digest — a rolling Audit/digest.md: counts, statuses, tokens, durations, a per-day breakdown, errors.
  • Retention off by default — an audit trail that silently ages out is a policy contradiction. Opt in if you want it; each prune is itself recorded as a receipt, so the gaps it leaves are self-explaining.

Install

From a release: download main.js, manifest.json, and styles.css into <vault>/.obsidian/plugins/curtis-ai-audit/, then enable the plugin in Obsidian (Settings → Community plugins). Once accepted into the community directory it installs from there directly.

From source: clone this repo into <vault>/.obsidian/plugins/curtis-ai-audit/, run npm install && npm run build, and enable it in Obsidian.

Usage

Ribbon icon or commands:

commandwhat it does
Check audit inbox nowpoll + ingest, with a summary notice
Verify ledgerintegrity scan; details to console
Write digest(re)write Audit/digest.md and open it
Prune ledger nowmanual retention pass (needs retention > 0)
Open today's ledgeropen/create today's shard
Install agent audit hookswrite vault-scoped hook configs (Path C)
Uninstall agent audit hooksremove exactly what install added

Other plugins (same window):

const receipt = await window.curtisAudit.record({
  agent: "curtis-ai-chat",
  action: "inference",          // optional, default "other"
  model: "gpt-4o",
  provider: "openai",
  durationMs: 1234,
  tokens: { input: 812, output: 341 },
  subject: "Notes/question.md",
  summary: "Answered a question about the spec",
});
// also: .poll(), .verify(), .digest(days?), .query(filter)
// workspace event: "curtis-audit:receipt"

The read side, for dashboards and status bars:

const errors = await window.curtisAudit.query({
  agent: "curtis-ai-chat",
  status: "error",
  since: "2026-10-01",   // bounds apply to `recorded` (local write time)
  order: "desc",         // newest first (the default)
  limit: 20,
});

Anything that can append to a file (other device, script, agent):

<!-- messenger: {"id":"msg_x","from":"research-agent","to":"audit","ts":"2026-10-10T12:00:00.000Z","replyTo":null} -->
{"action":"inference","model":"gemini-2.5-pro","summary":"Summarized a paper"}
<!-- /messenger -->

See INTEGRATION.md for wiring up curtis-ai-chat.

Testing

npm test        # 107 unit tests, in-memory vault, no Obsidian needed
npm run build   # typecheck + bundle to main.js

Status

v0.2.2 — the local-first tier, deliberately. Tamper-evidence, retention enforcement, and reporting are the next tier up and are not pretended here. Requires Obsidian 1.13.0+ (declarative settings).

License

MIT © Jordan Newell

For plugin developers

Search results and similarity scores are powered by semantic analysis of your plugin's README. If your plugin isn't appearing for searches you'd expect, try updating your README to clearly describe your plugin's purpose, features, and use cases.