build-audit-logs
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.
What this skill does
# 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 | `defineAuditAction('invoice.refund', { target: 'invoice' })` |
| Asserting audits in tests | `mockAudit()` — `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?
| Sink | 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 sinks** | Different audiences (engineers ↔ legal) | Use `auditOnly` per sink; sinks 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 sink is mutable by engineers / admins.
- You may need to prove to a regulator that no events were modified after the fact.
Skip if:
- Sink 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-negotiabRelated in Security
mac-ops
IncludedComprehensive macOS workstation operations — diagnose kernel panics, identify failing drives, audit launchd startup items, decode wake reasons, triage TCC permission denials, manage APFS snapshots, recover from no-boot. Use for: Mac is slow, slow bootup, won't boot, kernel panic, kernel_task hot, mds_stores CPU, photoanalysisd, cloudd, login loop, gray screen, sleep wake failure, drive failing, IO errors, APFS snapshots eating space, Time Machine local snapshots, Spotlight indexing, launchd, LaunchAgent, LaunchDaemon, login items, TCC permissions, Full Disk Access, Screen Recording denied, Gatekeeper, quarantine, com.apple.quarantine, app is damaged, helper tool, /Library/PrivilegedHelperTools, pmset, wake reasons, dark wake, sysdiagnose, panic.ips, DiagnosticReports, configuration profile, MDM profile, remote diagnostics over SSH.
a11y-audit
IncludedRun accessibility audits on web projects combining automated scanning (axe-core, Lighthouse) with WCAG 2.1 AA compliance mapping, manual check guidance, and structured reporting. Output is configurable: markdown report only, markdown plus machine-readable JSON, or markdown plus issue tracker integration. Use this skill whenever the user mentions "accessibility audit", "a11y audit", "WCAG audit", "accessibility check", "compliance scan", or asks to check a web project for accessibility issues. Also trigger when the user wants to verify WCAG conformance or map findings to a specific standard (CAN-ASC-6.2, EN 301 549, ADA/AODA).
erpclaw
IncludedAI-native ERP system with self-extending OS. Full accounting, invoicing, inventory, purchasing, tax, billing, HR, payroll, advanced accounting (ASC 606/842, intercompany, consolidation), and financial reporting. 413 actions across 14 domains, 43 expansion modules. Constitutional guardrails, adversarial audit, schema migration. Double-entry GL, immutable audit trail, US GAAP.
assess
IncludedAssesses and rates quality 0-10 across multiple dimensions (correctness, maintainability, security, performance, testability, simplicity) with pros/cons analysis. Compares against project conventions and prior decisions from memory. Produces structured evaluation reports with actionable improvement suggestions. Use when evaluating code, designs, architectures, or comparing alternative approaches.
spring-boot-security-jwt
IncludedProvides JWT authentication and authorization patterns for Spring Boot 3.5.x covering token generation with JJWT, Bearer/cookie authentication, database/OAuth2 integration, and RBAC/permission-based access control using Spring Security 6.x. Use when implementing authentication or authorization in Spring Boot applications.
code-hardcode-audit
IncludedDetect hardcoded values, magic numbers, and leaked secrets. TRIGGERS - hardcode audit, magic numbers, PLR2004, secret scanning.