build-audit-logs
Build or review audit trails in TypeScript/JavaScript apps using evlog (pipelines, typed actions, denials, retention, compliance-style reviews).
Skill by edmundmiller
Install
Works with Claude Code, Claude apps, Codex, Cursor, Gemini CLI, GitHub Copilot, VS Code, OpenCode, Goose, Windsurf. Read the repository before you give any tool access to your data.
npx agentmarket install skill/edmundmiller-build-audit-logs
Asks which of your tools to add it to (Claude Code, Codex, Cursor, VS Code, Windsurf, Gemini CLI, Claude Desktop) and shows the change first. See https://agentmart.dev/cli.
Get the weekly diff
The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.
About
Use cases: security
Reviews
Write a reviewNo reviews yet. Installed it? Reviews come from verified installers only. How reviews work
Permissions
What this skill can do and what it asks for, from its SKILL.md and code. Checked Oct 25, 2026.
- Tools
- No tools found
- Secrets
- None asked for
- Install scripts
- None found
Network hosts it talks to 1
codeload.github.com· found in install command
Files it touches
~/.claude/skills/build-audit-logs
What's inside
- Skill name
build-audit-logs- Description
- Build or review audit trails in TypeScript/JavaScript apps using evlog (pipelines, typed actions, denials, retention, compliance-style reviews). For application code, not for extending the evlog package.
- License
- MIT
- Instructions
- 476 lines in SKILL.md · view on GitHub
Files (4)
- LICENSE
- SKILL.md
- SOURCE.md
- framework-wiring.md · references/
SKILL.md (first lines)
# Build or Review an Audit System with evlog
For **application developers** who either need to add an audit trail to their product, or who already have one and want it reviewed. Walks through the design calls, the end-to-end implementation, and a review checklist for an existing setup.
This skill assumes the audit lives in **your app**. To extend the evlog package itself (new audit helper, new drain wrapper), see the contributor skills under `.agents/skills/`.
## Quick reference: call-site cheat sheet
When you already know the system is wired and just need to remember the API:
| Situation | Helper |
| ----------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Inside a request handler, action succeeded | `log.audit({ action, actor, target, outcome: 'success' })` |
| Inside a request handler, AuthZ denial | `log.audit.deny('reason', { action, actor, target })` |
| Standalone job / script / CLI (no request) | `audit({ action, actor, target, outcome })` |
| Auto-record success / failure / denied for a function | `withAudit({ action, target }, fn)` |
| Recording a state change | add `changes: auditDiff(before, after)` |
| Centralised typed action vocabulary | `defineAuditCatalog('billing', { INVOICE_REFUND: { target: 'invoice' } })`, or `defineAuditAction('invoice.refund', { target: 'invoice' })` for one-offs |
| Asserting audits in tests | `mockAudit()`, then `assertAudit()` or `toIncludeAuditOf()` |
`AuditFields` schema (always provide `action`, `actor`, `outcome`; `target` strongly recommended; the rest is filled in for you):
```ts
interface AuditFields {
action: string // 'invoice.refund'
actor: { type: 'user' | 'system' | 'api' | 'agent', id: string, email?, displayName?, model?, tools?, reason?, promptId? }
outcome: 'success' | 'failure' | 'denied'
target?: { type: string, id: string, [k: string]: unknown }
reason?: string
changes?: { before?: unknown, after?: unknown, patch?: AuditPatchOp[] }
causationId?: string
correlationId?: string
version?: number // defaults to AUDIT_SCHEMA_VERSION
idempotencyKey?: string // auto-derived from action+actor+target+timestamp
context?: { requestId?, traceId?, ip?, userAgent?, tenantId?, ... } // filled by auditEnricher
signature?: string // added by signed(drain, { strategy: 'hmac' })
prevHash?: string // added by signed(drain, { strategy: 'hash-chain' })
hash?: string // added by signed(drain, { strategy: 'hash-chain' })
}
```
## What "audit logging" actually means
An audit log answers a forensic question: **who did what, on which resource, when, from where, with which outcome.** That's a different shape from observability logs, which is why the operational rules differ:
| | Audit log | Observability log |
| ---------- | --------------------------------------- | ------------------------------ |
| Question | "Who tried to do what, was it allowed?" | "How did this request behave?" |
| Sampling | Never (force-keep) | Often (head + tail) |
| Retention | 1 – 7 years (compliance) | 30 – 90 days |
| Mutability | Append-only, tamper-evident | Mutable, lossy |
| Audience | Auditors, security, legal | Engineers |
| Storage | Often dedicated (separate dataset / DB) | Shared with telemetry |
evlog ships the audit layer as a thin extension of its wide-event pipeline (a typed `audit` field on `BaseWideEvent` plus a few helpers and drain wrappers). The point is that you compose with the primitives the app already uses: same drains, same enrichers, same redact, same framework integration. There is no parallel system to maintain.
## Mental model
```text
log.audit(...) ──► sets event.audit ──► force-keep ──► auditEnricher ──► redact ──► every drain
└─► auditOnly(signed(fsDrain))
```
| Building block | Role | Required? |
| ----------------------------------------- | ------------------------------------------------------------------- | --------------------- |
| `log.audit()` / `audit()` / `withAudit()` | Sets `event.audit` and force-keeps the event | Yes |
| `auditEnricher()` | Auto-fills `event.audit.context` (req / trace / ip / ua / tenantId) | Recommended |
| `auditOnly(drain)` | Filters the drain to events with `event.audit` set | Recommended |
| `signed(drain, ...)` | Adds tamper-evident integrity (HMAC or hash-chain) | Optional (compliance) |
| `auditRedactPreset` | Strict PII preset for audit events | Recommended |
| `mockAudit()` | Captures audit events in tests | Yes (in tests) |
## Design calls before writing code
Make these explicit and write them down somewhere a security reviewer can find. Without a written rule, the system can't be audited. Auditors look for the policy first, then the enforcement.
### 1. Where do audits live?
| Drain | Use when | Trade-offs |
| ------------------------------ | ------------------------------------------- | --------------------------------------------------------------------------------------- |
| **FS** (`evlog/fs` + `signed`) | Self-hosted, simple, you control the disk | Manual rotation/backup; single-process unless you persist hash-chain `state` externally |
| **Dedicated Axiom dataset** | You already use Axiom | Easy queries, separate retention/billing; cost scales with volume |
| **Postgres / Neon / Aurora** | You want SQL queries, joins with app data | Need a schema, indexes, retention job; idempotency key prevents duplicates |
| **S3 + Object Lock** | Append-only WORM compliance (HIPAA / FINRA) | Read latency; pair with a queryable mirror (Athena) |
| **Multiple drains** | Different audiences (engineers ↔ legal) | Use `auditOnly` per drain; drains fail in isolation by design |
> **Rule of thumb.** Pick at least two: a queryable one (Axiom / Postgres) for day-to-day forensics + an append-only one (FS journal with hash-chain, or S3 Object Lock) as the compliance artefact. The two-drain pattern protects against vendor outages and admin mistakes on the queryable side.
### 2. Do you need integrity (`signed`)?
Yes if any of:
- A compliance framework requires tamper-evidence (SOC2 CC7, HIPAA §164.312(c)(1), PCI 10.5).
- The drain is mutable by engineers / admins.
- You may need to prove to a regulator that no events were modified after the fact.
Skip if:
- Drain is already WORM (S3 Object Lock, BigQuery append-only, Postgres with row-level immutability + monitored DDL).
- You're prototyping.
Strategies:
- `'hmac'`: per-event signature; quick to verify; rotate `secret` annually and embed a key id (extend `AuditFields`).
- `'hash-chain'`: sequence integrity; deleting a row breaks the chain forward; persist `state.{load,save}` if you run multiple processes (Redis is the typical store).
### 3. Multi-tenancy?
If the app is multi-tenant, **tenant isolation on every audit event is non-negotiable.** A query that mixes tenants is a privacy incident. Wire it once in the enricher:
```ts
auditEnricher({ tenantId: (ctx) => resolveTenant(ctx) });
```
If the app uses Better Auth, `auditEnricher` can also bridge the authenticated session into `audit.context`. See the Better Auth integration (`evlog/better-auth`, https://www.evlog.dev/use-cases/better-auth/overview) for wiring `identifyUser` alongside the audit pipeline.
Then either (a) partition the audit dataset by `audit.context.tenantId`, or (b) one drain per tenant if hard isolation is required. Never query audits without a tenant filter.
### 4. RetentionFrom edmundmiller/dotfiles, MIT.
More from edmundmiller/dotfiles
- 0.671score
analyze-logs
HugoRCD
Analyze application logs from the .evlog/logs/ directory.
SkillAFreshness: A★ 80Claude Code +9 - 0.671score
excalidraw-cli
edmundmiller
Create, inspect, update, and locally render Excalidraw scenes with the Excalidraw CLI.
SkillAFreshness: A★ 80Claude Code +9 - 0.668score
code-search
edmundmiller
Compares semantic, text, and structural search strategies.
SkillAFreshness: A★ 80Claude Code +9 - 0.665score
verify
edmundmiller
Verify dotfiles changes at runtime without switching the system.
SkillAFreshness: A★ 80Claude Code +9 - 0.636score
auditing-decision-data
edmundmiller
Checks whether observations support a consequential decision.
SkillBFreshness: B★ 80Claude Code +9 - 0.636score
challenging-expert-assumptions
edmundmiller
Examines the assumptions and evidence behind categorical technical claims.
SkillBFreshness: B★ 80Claude Code +9 - 0.633score
done
edmundmiller
Close an explicit repository task by shaping, landing, publishing, proving, and cleaning it up; also handles narrower commit, publish, and…
SkillBFreshness: B★ 80Claude Code +9 - 0.633score
hass-config-flow
edmundmiller
Uses Home Assistant APIs on NixOS for integration config flows, entity/device inspection, and token setup.
SkillBFreshness: B★ 80Claude Code +9 - 0.633score
derive-web-api-client
edmundmiller
Derives a task-focused API client from authorized HAR/browser traffic.
SkillBFreshness: B★ 80Claude Code +9 - 0.633score
honcho-integration
edmundmiller
Integrates Honcho memory into Python or TypeScript agents.
SkillBFreshness: B★ 80Claude Code +9 - 0.633score
home-assistant-best-practices
edmundmiller
Selects Home Assistant automation, helper, device-control, and dashboard patterns.
SkillBFreshness: B★ 80Claude Code +9 - 0.633score
hass-declarative
edmundmiller
Manages Nix-owned HA automations, scenes, scripts, and entity identity.
SkillBFreshness: B★ 80Claude Code +9
Signals
- Stars
- 80
- Forks
- 6
- Open issues
- 4
- Last commit (this folder)
- Oct 22, 2026 (6 days ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- MIT
- Repository
- github.com/edmundmiller/dotfiles
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from edmundmiller
- 0.671score
excalidraw-cli
edmundmiller
Create, inspect, update, and locally render Excalidraw scenes with the Excalidraw CLI.
SkillAFreshness: A★ 80Claude Code +9 - 0.668score
code-search
edmundmiller
Compares semantic, text, and structural search strategies.
SkillAFreshness: A★ 80Claude Code +9 - 0.665score
verify
edmundmiller
Verify dotfiles changes at runtime without switching the system.
SkillAFreshness: A★ 80Claude Code +9 - 0.636score
auditing-decision-data
edmundmiller
Checks whether observations support a consequential decision.
SkillBFreshness: B★ 80Claude Code +9 - 0.636score
challenging-expert-assumptions
edmundmiller
Examines the assumptions and evidence behind categorical technical claims.
SkillBFreshness: B★ 80Claude Code +9 - 0.633score
home-assistant-best-practices
edmundmiller
Selects Home Assistant automation, helper, device-control, and dashboard patterns.
SkillBFreshness: B★ 80Claude Code +9
Cite this page
build-audit-logs skill. Agent Mart. https://agentmart.dev/skills/edmundmiller-build-audit-logs (last updated 2026-10-24)