build-audit-trails
Build or review tamper-aware audit trails on top of OpenTelemetry spans using autotel and the autotel-audit package (`withAudit`, `setAuditAttributes`…
Skill by jagreehal
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/jagreehal-build-audit-trails
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: design, frontend, backend, testing
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-trails
What's inside
- Skill name
build-audit-trails- Description
- Build or review tamper-aware audit trails on top of OpenTelemetry spans using autotel and the autotel-audit package (`withAudit`, `setAuditAttributes`, `forceKeepAuditEvent`). Covers what counts as auditable, the audit-only span discipline, sampling bypass, HMAC and hash-chain signing with tamper detection, denial logging, redaction, retention, GDPR right-to-erasure via crypto-shredding, separation from operational telemetry, testing audit spans, backend-agnostic audit queries, a production-readiness checklist, and framework wiring (Next.js, Nuxt, Nitro, NestJS, Express, Fastify, Hono, Cloudflare Workers, AWS Lambda, standalone). Use it to design new audit trails or review existing ones for GDPR, HIPAA, SOC 2, PCI-DSS, ISO 27001, SOX, and GxP compliance.
- License
- Apache-2.0
- Instructions
- 457 lines in SKILL.md · view on GitHub
Files (3)
- SKILL.md
- audit-queries.md · references/
- framework-wiring.md · references/
SKILL.md (first lines)
# Build audit trails
An _audit trail_ is a record of who did what to which resource, when, and whether it was permitted. Durable, tamper-evident, and admissible. Operational telemetry (latency, errors, span shapes) is for engineers; audit trails are for compliance, security, and forensics. They overlap technically but differ on every other axis.
autotel lets you express both with the same primitive, a span, but you should keep them on **separate processors** so an audit event never gets dropped by sampling, never gets redacted by a debug rule, and never goes to the same backend as your ops data.
## When to use
- Implementing GDPR / HIPAA / SOC2 / PCI-DSS / ISO 27001 / GxP compliance
- Adding "who did what" trails for admin actions, access reviews, payments
- Recording authorization decisions (allow + deny)
- Building immutable evidence for incident response
- Reviewing an existing audit trail for compliance gaps (see "Review an existing audit trail")
## Quick reference
| Situation | Use |
| --------------------------------------------------- | -------------------------------------------------------- |
| Wrap an action so success/failure is audited + kept | `withAudit(metadata, fn)` from `autotel-audit` |
| Tag the active span with `audit.*` attributes only | `setAuditAttributes(metadata)` |
| Make sure an audit span survives tail sampling | `forceKeepAuditEvent()` |
| Record an authorization denial | `audit({ …, outcome: 'deny', reason })` (Step 1 helper) |
| Full control / framework-agnostic span helper | hand-rolled `audit()` (Step 1) |
| Keep audit data off ops dashboards | `FilteringSpanProcessor` split (Step 3) |
| Prove a record was not altered | HMAC or hash-chain signature (Step 4) |
| Honor a GDPR erasure request on an append-only log | crypto-shredding (Step 6.5) |
| Assert the trail in tests | `createTraceCollector()` from `autotel/testing` (Step 8) |
## The shortest path: the `autotel-audit` package
`autotel-audit` ships this discipline as helpers. Reach for these before hand-rolling:
```typescript
import {
withAudit,
setAuditAttributes,
forceKeepAuditEvent,
} from 'autotel-audit';
// Wrap the action: sets audit.* attributes, force-keeps past tail sampling,
// and tags outcome 'success' / 'failure' automatically.
await withAudit(
{
action: 'user.delete',
resource: 'user',
actorId: 'usr_42',
category: 'admin',
},
async () => db.user.delete({ where: { id } }),
);
// Or tag the current span yourself and opt out of sampling:
setAuditAttributes({
action: 'secret.read',
resource: 'sec_abc',
actorId: 'usr_42',
});
forceKeepAuditEvent();
```
`withAudit` creates no span of its own: it tags the active one, so call it inside `trace()` or framework middleware (with no trace context it runs `fn` untagged; see `onMissingContext`). It marks the span with `autotel.audit = true` and sets the tail-sampling keep flags via `forceKeepAuditEvent`, so audit events are never dropped. The rest of this guide builds the same model from raw spans when you need full control over the schema, signing, and the pipeline split. If you use the package, filter on `autotel.audit` (not `audit`) in Step 3.
## The audit span discipline
An auditable event has six required parts:
| Field | OTel attribute | Example |
| ------------------- | --------------------------------------------------------------- | --------------------------------------------- |
| When | (span timestamp) | `2026-05-04T17:23:11.412Z` |
| Who | `enduser.id` + `enduser.role` | `usr_42`, `admin` |
| Where (acting from) | `client.address`, `network.peer.address`, `user_agent.original` | `203.0.113.5`, `Chrome 121` |
| What | `audit.action` | `secret.read`, `policy.update`, `user.delete` |
| Which resource | `audit.resource.type` + `audit.resource.id` | `secret`, `sec_abc` |
| Outcome | `audit.outcome` (`allow` / `deny`) + `audit.reason` | `deny`, `MFA required` |
Plus useful optional fields: `audit.policy.id` (which policy made the call), `audit.evidence` (linked artefact id), `audit.actor.session.id`.
## Step 1: Define a typed `audit()` helper
Centralise the schema in one place so every site gets it right:
```typescript
import { trace, SpanKind } from '@opentelemetry/api';
type AuditAction =
| 'secret.read'
| 'secret.write'
| 'secret.delete'
| 'policy.update'
| 'user.create'
| 'user.delete'
| 'data.export'
| 'session.assume';
interface AuditPayload {
action: AuditAction;
resource: { type: string; id: string };
outcome: 'allow' | 'deny';
reason?: string;
actor?: { id: string; role?: string; sessionId?: string };
policy?: { id: string };
evidence?: { id: string };
}
const tracer = trace.getTracer('autotel-audit', '1.0.0');
export function audit(payload: AuditPayload): void {
const span = tracer.startSpan(`audit.${payload.action}`, {
kind: SpanKind.INTERNAL,
attributes: {
audit: true,
'audit.action': payload.action,
'audit.outcome': payload.outcome,
'audit.resource.type': payload.resource.type,
'audit.resource.id': payload.resource.id,
...(payload.reason && { 'audit.reason': payload.reason }),
...(payload.actor?.id && { 'enduser.id': payload.actor.id }),
...(payload.actor?.role && { 'enduser.role': payload.actor.role }),
...(payload.actor?.sessionId && {From jagreehal/autotel, Apache-2.0.
More from jagreehal/autotel
- 0.631score
find-observability-gaps
jagreehal
Scores the observability of every entry point in a codebase with `autotel map` and closes the gaps it reports.
SkillAFreshness: A★ 8Claude Code +9 - 0.626score
autotel-investigate
jagreehal
Query OpenTelemetry telemetry (traces, metrics, logs, LLM analytics) via the autotel CLI.
SkillAFreshness: A★ 8Claude Code +9 - 0.626score
autotel-anti-patterns
jagreehal
Common autotel mistakes and how to fix them.
SkillAFreshness: A★ 8Claude Code +9 - 0.626score
autotel-testing
jagreehal
Test autotel instrumentation with createTraceCollector() and InMemorySpanExporter.
SkillAFreshness: A★ 8Claude Code +9 - 0.626score
autotel-core
jagreehal
Explains when to reach for trace vs span vs request logger vs events in Autotel, and the package exports each lives behind.
SkillAFreshness: A★ 8Claude Code +9 - 0.626score
migrate-to-autotel
jagreehal
Migrate an existing observability setup to autotel.
SkillAFreshness: A★ 8Claude Code +9 - 0.626score
production-observability
jagreehal
Takes a Node.js or TypeScript service to production-grade observability with autotel, in an existing codebase or a new one, and keeps it there.
SkillAFreshness: A★ 8Claude Code +9 - 0.626score
design-alertable-metrics
jagreehal
Designs metrics an alarm can actually be written against — picking the attributes that let a rule isolate one failing dependency, pinning metric names as a…
SkillAFreshness: A★ 8Claude Code +9 - 0.626score
autotel-framework-setup
jagreehal
Set up autotel in Hono, Fastify, TanStack Start, Cloudflare Workers, Next.js, Express, MCP, and edge runtimes.
SkillAFreshness: A★ 8Claude Code +9 - 0.626score
autotel-request-logging
jagreehal
Accumulates request-scoped context and emits one wide-event snapshot per request with getRequestLogger(), set(), setLevel(), info/warn/error, and emitNow().
SkillAFreshness: A★ 8Claude Code +9 - 0.626score
autotel-structured-errors
jagreehal
Throws and parses errors that explain themselves with message, why, fix, link, and status, including reusable catalogs via defineErrorCatalog.
SkillAFreshness: A★ 8Claude Code +9 - 0.626score
debug-missing-spans
jagreehal
Troubleshoot when expected OpenTelemetry spans don't reach the backend.
SkillAFreshness: A★ 8Claude Code +9
Compare and alternatives
Featured in
Signals
- Stars
- 8
- Forks
- 3
- Open issues
- 0
- Last commit (this folder)
- Oct 19, 2026 (8 days ago)
- Last release
- Sep 27, 2026 (1 month ago)
From GitHub (fetched Oct 24, 2026).
Details
- License
- Apache-2.0
- Repository
- github.com/jagreehal/autotel
- Website
- jagreehal.github.io/autotel/
- First seen
- Oct 24, 2026
- Last checked
- Oct 24, 2026
- Source
- GitHubgithub-graphql
More from jagreehal
- 0.631score
find-observability-gaps
jagreehal
Scores the observability of every entry point in a codebase with `autotel map` and closes the gaps it reports.
SkillAFreshness: A★ 8Claude Code +9 - 0.626score
autotel-anti-patterns
jagreehal
Common autotel mistakes and how to fix them.
SkillAFreshness: A★ 8Claude Code +9 - 0.626score
autotel-investigate
jagreehal
Query OpenTelemetry telemetry (traces, metrics, logs, LLM analytics) via the autotel CLI.
SkillAFreshness: A★ 8Claude Code +9 - 0.626score
autotel-testing
jagreehal
Test autotel instrumentation with createTraceCollector() and InMemorySpanExporter.
SkillAFreshness: A★ 8Claude Code +9 - 0.626score
autotel-core
jagreehal
Explains when to reach for trace vs span vs request logger vs events in Autotel, and the package exports each lives behind.
SkillAFreshness: A★ 8Claude Code +9 - 0.626score
analyze-traces
jagreehal
Analyze OpenTelemetry traces and structured logs from a running autotel service to debug errors, investigate latency, follow requests across services, and…
SkillAFreshness: A★ 8Claude Code +9
Cite this page
build-audit-trails skill. Agent Mart. https://agentmart.dev/skills/jagreehal-build-audit-trails (last updated 2026-10-24)