Index

Data That Must Survive

Audit History Is Different from Application Logging

When a cart appears to expire, support may ask:

Which cart changed?
What was its prior state?
What state followed?
Who or what caused the transition?
Which policy allowed it?

An engineer investigating the same report may ask:

Which request handled it?
Which worker retried?
How long did the database call take?
Which exception and stack occurred?
What else happened on that host?

Both groups need evidence. They do not need the same record.

Audit history explains durable business change. Application logs explain software execution.

An audit event names the business transition

A useful audit event can stand on its own after the request and process which created it have disappeared:

{
  "event": "cart.state.changed",
  "subject_id": "cart_demo_27",
  "actor_id": "user_demo_4",
  "tenant_id": "tenant_demo_2",
  "from_state": "pending",
  "to_state": "paid",
  "schema_version": 1,
  "occurred_at": "2026-11-01T10:15:00Z"
}

The record answers who changed which subject and how. Depending on the domain, it may also need reason, authority, policy version, approved values, or a link to an immutable command.

An operational log line might describe the same moment:

{
  "level": "info",
  "message": "state update completed",
  "correlation_id": "trace_demo_9",
  "duration_ms": 42,
  "attempt": 2,
  "worker": "queue-3"
}

That line is useful for latency, retries, and execution flow. It does not prove what durable state changed unless the application treats it as a business record and gives it the corresponding guarantees.

Subject, actor, and cause are separate fields

“User changed cart” collapses several relationships.

The audit subject is the entity whose durable history changed. The actor is the authenticated principal exercising authority. The cause may be a scheduled policy, an external message, or an anonymous guest action. The tenant scopes where the event belongs.

These can differ:

subject:
    payment attempt

actor:
    support operator

cause:
    approved manual recovery

tenant:
    business account which owns the attempt

If the action is anonymous, record that fact instead of inventing an actor. If a service acts on behalf of a user, preserve both identities when the distinction matters.

The retained lifecycle logger associates a subject when one exists, records an authenticated cause or anonymous cause, and adds tenant and owner identity to its versioned envelope. Those choices make later authorization and explanation possible.

Audit schemas should evolve deliberately

Logs often accept loosely structured context because their primary reader is a human during a bounded investigation. Audit history lives longer and develops multiple readers: support tools, exports, reconciliation jobs, investigations, and migrations.

The retained envelope includes a schema version. That creates an explicit branch for future readers:

version 1:
    from_state and to_state are strings

version 2:
    transition also carries policy revision

Without a version, readers infer meaning from field presence and event dates. That becomes fragile when old and new producers coexist.

Versioning does not require a new format for every added optional field. It is needed when interpretation or required structure changes. The reader contract, not release activity, decides.

An allowlist is safer than logging whatever context exists

Audit records attract sensitive data because they are retained and easy to search. Passing an entire request, model, exception, or third-party response into an audit library can preserve secrets and personal data far beyond their intended lifetime.

The retained logger accepts only named scalar fields and lists of scalar values. It deliberately excludes request bodies, credentials, free-form messages, validation payloads, contact data, tracking data, and unknown properties.

For a raw session identifier, it stores a keyed fingerprint:

HMAC(application key, context + session identifier)

That allows equality checks within the key’s trust boundary without storing the original session value. A plain hash would still permit guessing when the input space is predictable.

Even a fingerprint remains data with a purpose and retention requirement. Key rotation changes correlation behavior, and access to the key changes the security model. “Hashed” is not a synonym for anonymous.

Correlation connects the records without merging their jobs

The same correlation identifier can appear in the audit event and application logs:

audit query:
    show transitions for this subject

log query:
    show execution around this correlation id

Support can begin with the durable history, identify the transition and trace, then hand the correlation identifier to engineering for request-level detail.

This is better than copying stack traces and HTTP payloads into the audit record. It preserves the separation:

  • audit history retains the minimum business evidence;
  • operational logs retain execution detail for their shorter diagnostic lifecycle; and
  • correlation provides a controlled join when both are available.

Correlation is not completeness. If one store drops an event, a shared identifier cannot recreate it.

Storage guarantees decide whether history is authoritative

The strongest audit ledger usually needs the event written atomically with the business transition or through an outbox committed in the same transaction. It also needs append-only controls, durable delivery, retention policy, authorization, and monitoring for gaps.

The retained lifecycle logger makes a different trade-off. It catches activity persistence failures, reports them, and allows the business action to continue. Tests explicitly preserve that behavior.

That is a reasonable availability choice for operational history, but it sets a hard claim boundary:

when written:
    durable, structured, queryable lifecycle evidence

when the audit write fails:
    business transition may succeed without its activity event

This cannot be called a guaranteed compliance ledger. It can help explain production behavior, but absence of an event does not prove absence of a transition.

If the domain requires complete financial, security, or regulatory evidence, move the guarantee to the transaction boundary and verify delivery from there.

Logs and audit history have different retention pressures

Application logs can be large. They include repeated retries, debug context, timings, stack traces, and infrastructure noise. Their value often decays after the operational investigation window.

Audit history should be smaller and more deliberate, but it may need to live as long as the business record it explains. That longer life increases the cost of every unnecessary field.

Define both policies:

operational logs:
    retention window
    searchable fields
    redaction
    sampling
    access

audit history:
    covered transitions
    completeness guarantee
    schema versions
    immutability controls
    retention and erasure
    access and export

The retained artifacts do not establish a legal retention period or immutable storage guarantee. The article therefore does not invent either.

Tests should ask the questions each store promises to answer

An audit regression should verify:

  • subject, actor or anonymous cause, and tenant;
  • before and after state;
  • reason or policy when required;
  • schema version;
  • allowed fields;
  • excluded secrets and personal data;
  • correlation behavior; and
  • what happens when persistence fails.

An operational logging test, when useful, should verify a stable diagnostic contract rather than punctuation:

  • trace propagation;
  • severity for an actionable condition;
  • safe error classification;
  • retry or duration context; and
  • redaction.

The retained tests inspect encoded activity properties to prove raw session, request, secret, address, tracking, and unknown data do not survive. They also prove a failed activity save does not fail the business call.

The fixture asks both stores the same questions

The DATA-11 fixture creates one synthetic business transition, then derives an allowlisted audit event and an operational log record.

The audit event can answer subject, actor, tenant, state change, and policy. The log can answer attempt, duration, worker, and error detail. Both carry the same correlation identifier. The fixture also drops a simulated audit write and marks the resulting history incomplete while the business transition succeeds.

It does not execute private code, prove durable storage, model legal retention, or establish an incident rate.

Choose which absence you are willing to tolerate

Before calling a record an audit trail, answer:

Which transitions must be covered?
Can business state commit if the audit write fails?
How are events delivered and reconciled?
Can a writer alter or delete history?
Which fields are necessary?
Which fields are prohibited?
How do schemas evolve?
How long is each event retained?
What does absence of an event prove?

Application logs remain essential. They explain how code reached a result. Audit history explains what the result changed.

The difference becomes concrete at the failure boundary: if losing the record must stop the transition, it belongs in the durable business design. If the transition may continue, the history is useful evidence with an acknowledged gap—not proof that nothing happened.