← Security and risk guides

How to spec audit logging

How-To Security and risk Intermediate 1161009HOWTO-1161009

HOWTO-1161009Security and riskIntermediate

This guide shows you how to spec audit logging with SpecDD in a spec-driven development workflow.

Audit logs are only useful when the right events are recorded, at the right boundary, with enough context to review what happened, and without leaking sensitive data. SpecDD lets you keep those requirements close to the code that performs the sensitive action.

Short answer

Use Must to require audit events for sensitive behavior, Exposes when event names are part of the local contract, Must not to forbid secrets or private payloads in audit data, Handles for failure cases, and Done when for event content checks. Put event requirements in the spec that owns the action, not only in a central logging document.

When to use this guide

Use this guide when code performs or denies:

Steps

1. Choose the audit event owner

Put audit requirements in the spec that owns the sensitive action.

Spec: Account Role Change

Purpose:
  Change account roles through an authorized administrative action.

Owns:
  ./account-role-change.ts
  ./account-role-change.test.ts

A central audit adapter spec can own delivery mechanics. The action spec should still say which action requires an audit event.

2. Name the auditable events

Use Must for required event behavior. Use Exposes when event names are part of the local contract.

Exposes:
  AccountRoleChangeAccepted audit event
  AccountRoleChangeDenied audit event

Must:
  Record AccountRoleChangeAccepted after an authorized role change is committed.
  Record AccountRoleChangeDenied when a role change request is rejected.

Use project naming conventions for event names. If event naming is a project-wide convention, document that convention in .specdd/bootstrap.project.md.

3. Define required fields

Write the fields needed for review and investigation.

Must:
  Include actor account id, target account id, previous role, new role, result, and request id in role-change audit events.
  Include timestamp through the project audit sink.

Typical fields:

Do not include fields just because they are available. Include what the event needs and what policy allows.

4. Forbid sensitive payload data

Use Must not for sensitive fields that must not appear in audit events.

Must not:
  Include raw passwords, session tokens, reset tokens, API keys, or secret values in audit events.
  Include full payment card data in audit events.
  Include raw export contents in audit events.

Audit logs often live longer and are read by more people than application logs. Keep payloads intentionally small.

5. Specify failure and ordering behavior

Decide what happens if audit delivery fails.

Handles:
  audit sink unavailable
  role change denied
  role change commit failure

Must:
  Record accepted audit event only after the role change is committed.
  Record denied audit event without changing the target role.

Do not invent failure semantics in the spec if the audit platform already defines them. Reference the audit adapter or runbook when delivery, buffering, retry, or fail-closed behavior is owned elsewhere.

6. Add scenarios

Scenario: authorized role change
  Given an administrator may change account roles
  When the administrator changes a member role
  Then the role is changed
  And AccountRoleChangeAccepted is recorded

Scenario: denied role change
  Given a viewer may not change account roles
  When the viewer requests a role change
  Then the role change is denied
  And AccountRoleChangeDenied is recorded

Denied scenarios are especially important because they prove that failed attempts remain visible.

7. Put event checks in Done when

Done when:
  Authorized role change records AccountRoleChangeAccepted with actor id, target id, previous role, new role, result, and request id.
  Denied role change records AccountRoleChangeDenied without changing the target role.
  Role-change audit events do not include session tokens or secret values.
  Existing audit adapter checks pass.

Reviewers should be able to inspect event content, not only the fact that a logger was called.

Example

Spec: Payment Refund

Purpose:
  Refund captured payments through the approved billing workflow.

Owns:
  ./payment-refund.ts
  ./payment-refund.test.ts

Depends on:
  ../billing-audit/billing-audit.sdd
  ../payment-provider/payment-provider-port.sdd

Exposes:
  PaymentRefundAccepted audit event
  PaymentRefundDenied audit event
  PaymentRefundFailed audit event

Must:
  Require refund authorization before calling the payment provider.
  Record PaymentRefundAccepted after the refund request is accepted.
  Record PaymentRefundDenied when authorization denies the refund.
  Record PaymentRefundFailed when provider refund fails after authorization.

Must not:
  Record PaymentRefundAccepted before authorization succeeds.
  Include raw payment provider credentials or full payment card data in audit events.
  Emit success audit events for denied refunds.

Done when:
  Authorized refund records PaymentRefundAccepted.
  Denied refund records PaymentRefundDenied without provider call.
  Provider failure records PaymentRefundFailed without raw provider credentials.

Common mistakes

How to verify the result

Audit logging is well specified when:

← Security and risk guides