Skip to content
Agentmart
Submit a tool

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

IndexedApache-2.0★ 8
Write a review

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.

We use your email only for this newsletter. Privacy

About

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.

Use cases: design, frontend, backend, testing

No 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

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
    First seen
    Oct 24, 2026
    Last checked
    Oct 24, 2026

    More from jagreehal

    Cite this page

    build-audit-trails skill. Agent Mart. https://agentmart.dev/skills/jagreehal-build-audit-trails (last updated 2026-10-24)