---
eip: 8328
title: Subject-Linked Compliance Event Log
description: Defines append-only subject-linked compliance records with attribution, evidence, indexing, and correction provenance
author: Chris Turner <c.turner@kula.com>, David Hay (@david-hay), Reagan Simpson (@krumg111), Collins Musyimi (@Musyimi97)
discussions-to: https://ethereum-magicians.org/t/erc-8328-subject-linked-compliance-event-log/28937
status: Draft
type: Standards Track
category: ERC
created: 2026-07-05
requires: 165
---

## Abstract

This ERC defines an append-only interface for subject-linked compliance event
records. Each record includes a subject, event type, outcome, technical actor,
claimed authority, involved parties, evidence commitment, optional evidence
location, versioned payload profile, operation reference, occurrence time, and
recording time.

Events are indexed per subject and by event type. Corrections are recorded as
new events linked to earlier records. A forward pointer on the corrected record
prevents correction forks and allows consumers to resolve the terminal event in
a correction chain.

This ERC is a reporting interface. It does not define compliance policy,
identity verification, transfer restrictions, legal authority, or regulatory
compliance. Stored records are attributable assertions, not proof that the
reported action occurred or was lawful.

## Motivation

Compliance-relevant lifecycle actions are currently represented through
application-specific events, token-local state changes, generic attestations,
and off-chain databases. This fragmentation makes it difficult for contracts,
indexers, auditors, and reporting systems to query comparable records across
implementations.

Existing token and compliance standards primarily define token behavior,
holder eligibility, transfer validation, or entity classification. Those
capabilities do not provide a common stored record for subject-level lifecycle
actions such as issuance, redemption, freezing, know-your-customer (KYC)
status changes, regulatory holds, policy changes, and forced transfers.

A shared event-log interface provides:

- one query surface for records attached to an application-defined subject;
- explicit separation between the recorder and the claimed authority;
- structured party roles rather than untyped address arrays;
- evidence commitments and optional retrieval references;
- versioned payload profiles for interoperable event-specific data;
- per-type indexing without scanning an entire subject history; and
- append-only, fork-free correction provenance.

The log can be called by a token, compliance module, governance executor,
multisig, or other authorized recorder. It does not require any particular
token standard or enforcement architecture.

## Specification

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
"OPTIONAL" in this document are to be interpreted as described in
[RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) and
[RFC 8174](https://www.rfc-editor.org/rfc/rfc8174).

### Definitions

A **subject** is an application-defined entity identified by `subjectId` and,
optionally, contextualized by `subjectType`.

A **compliance event** is a stored assertion about a compliance-relevant action
or state transition concerning a subject.

An **actor** is `msg.sender` at the time `recordEvent` is called. It identifies
the technical recorder, not necessarily the human decision-maker or legal
authority.

An **authority** is the recorder's claimed legal, regulatory, contractual, or
governance basis for the event. The claim is not verified by the log.

A **party** is an EVM address associated with an explicit role in the event.

A **payload profile** is a versioned schema declaring how `payload` is encoded.

A **correction chain** is a linear sequence of events linked by
`correctsIndex` and `correctedByIndex`.

A **terminal event** is an event whose `correctedByIndex` equals
`NO_CORRECTED_BY`.

### Sentinel Values

Implementations MUST use:

```solidity
uint256 constant NO_CORRECTION = type(uint256).max;
uint256 constant NO_CORRECTED_BY = 0;
```

`NO_CORRECTION` indicates that an event does not correct an earlier event.

`NO_CORRECTED_BY` indicates that an event has no successor correction. Event
index zero is safe for this sentinel because a correction's index is always
greater than the index it corrects and therefore can never be zero.

### Core Interface

A compliant log MUST implement:

```solidity
interface IComplianceEventLog {
    struct Party {
        address addr;
        bytes32 role;
    }

    struct ComplianceEvent {
        bytes32 subjectId;
        bytes32 subjectType;
        bytes32 eventType;
        bytes32 outcome;
        address actor;
        bytes32 authority;
        Party[] parties;
        bytes32 evidenceHash;
        string evidenceURI;
        bytes32 payloadProfileId;
        bytes payload;
        bytes32 operationRef;
        uint64 occurredAt;
        uint64 recordedAt;
        uint256 correctsIndex;
        uint256 correctedByIndex;
    }

    event ComplianceEventRecorded(
        bytes32 indexed subjectId,
        bytes32 indexed eventType,
        address indexed actor,
        uint256 eventIndex,
        bytes32 outcome,
        bytes32 authority,
        uint64 occurredAt,
        uint256 correctsIndex
    );

    function recordEvent(
        bytes32 subjectId,
        bytes32 subjectType,
        bytes32 eventType,
        bytes32 outcome,
        bytes32 authority,
        Party[] calldata parties,
        bytes32 evidenceHash,
        string calldata evidenceURI,
        bytes32 payloadProfileId,
        bytes calldata payload,
        bytes32 operationRef,
        uint64 occurredAt,
        uint256 correctsIndex
    ) external returns (uint256 eventIndex);

    function getEvent(
        bytes32 subjectId,
        uint256 eventIndex
    ) external view returns (ComplianceEvent memory);

    function currentEventIndex(
        bytes32 subjectId,
        uint256 eventIndex
    ) external view returns (uint256);

    function isEventCurrent(
        bytes32 subjectId,
        uint256 eventIndex
    ) external view returns (bool);

    function eventCount(
        bytes32 subjectId
    ) external view returns (uint256);

    function eventCountByType(
        bytes32 subjectId,
        bytes32 eventType
    ) external view returns (uint256);

    function eventByTypeAt(
        bytes32 subjectId,
        bytes32 eventType,
        uint256 ordinal
    ) external view returns (uint256 eventIndex);

    function lastRecordedEventByType(
        bytes32 subjectId,
        bytes32 eventType
    ) external view returns (uint256 eventIndex);
}
```

### Recording Semantics

Event indices MUST be zero-based and scoped per `subjectId`. The returned
`eventIndex` MUST equal the subject's event count immediately before the new
event is appended.

`recordEvent` MUST be restricted to authorized recorders. The authorization
mechanism is implementation defined and MUST be documented.

For every accepted event, the implementation MUST:

- store every supplied field without changing its value, except as explicitly
  specified by this ERC;
- set `actor` to `msg.sender`;
- set `recordedAt` to `uint64(block.timestamp)`;
- initialize `correctedByIndex` to `NO_CORRECTED_BY`;
- append the event index to the subject and event-type index;
- emit `ComplianceEventRecorded`; and
- return the new event index.

The implementation MUST reject the call if `block.timestamp` cannot be
represented as `uint64`.

`evidenceHash` MUST NOT be `bytes32(0)`. `evidenceURI` MAY be empty when the
evidence location is private, unavailable on-chain, or exchanged out of band.
An empty URI does not weaken the requirement for a nonzero commitment.

The parties array MUST contain no more than 10 entries. `payload` MUST contain
no more than 2048 bytes. Empty party arrays and empty payloads are allowed.

This ERC does not require nonzero values for `subjectId`, `subjectType`,
`eventType`, `outcome`, `authority`, `operationRef`, party addresses, party
roles, or `payloadProfileId`. Applications requiring stricter semantics MUST
enforce and document them before calling `recordEvent`.

### Temporal Semantics

`occurredAt` represents when the reported action occurred. `recordedAt`
represents when the record was appended on-chain.

`recordEvent` MUST revert when `occurredAt > block.timestamp`.

Implementations SHOULD impose and document a maximum backdating interval.
Different deployments can require different intervals, so the duration is not
standardized by this ERC.

The log does not independently verify `occurredAt`. It is an assertion by the
recorder.

### Append-Only Semantics

Once recorded, every event field MUST remain immutable except
`correctedByIndex`. Events MUST NOT be deleted.

Updating `correctedByIndex` is permitted only when accepting a valid correction
under the correction rules below.

### Correction Semantics

An original or non-correction event MUST use `NO_CORRECTION` and MUST NOT use
`EVT_CORRECTION`, which is defined in the Event Types section below:

```text
correctsIndex = NO_CORRECTION
eventType != EVT_CORRECTION
```

A correction event MUST use:

```text
correctsIndex = index of the corrected event
eventType = EVT_CORRECTION
```

For a correction, `recordEvent` MUST:

- require `correctsIndex` to identify an earlier event under the same
  `subjectId`;
- require the target event's `correctedByIndex` to equal
  `NO_CORRECTED_BY`;
- authorize the correction under the implementation's documented correction
  policy;
- set the target's `correctedByIndex` to the new correction event index; and
- append the correction as a new event.

The correction policy MUST NOT permit an ordinary recorder to correct another
actor's event merely because both addresses can record events. It MAY authorize
the original actor, a designated corrector, or an administrator. The policy
MUST be documented.

Each event can be corrected at most once, preventing forks. A correction event
can itself be corrected later, producing a linear chain.

The correcting record does not rewrite the original event's event type or
payload. Recorders MUST place the corrected assertion in the new event's fields
or an application-defined correction payload. Consumers MUST interpret the
terminal event under the applicable profile and application policy.

### Current-State Queries

`currentEventIndex` MUST revert when `eventIndex >= eventCount(subjectId)`.
Otherwise, it MUST follow `correctedByIndex` until reaching
`NO_CORRECTED_BY` and return the terminal event index. Calling it on a terminal
event MUST return the supplied index.

`isEventCurrent` MUST revert when `eventIndex >= eventCount(subjectId)` and
otherwise return whether `correctedByIndex == NO_CORRECTED_BY`.

Because corrections always point to earlier events and each event has at most
one successor, conforming correction chains cannot contain cycles or branches.

### Event Retrieval and Counting

`getEvent` MUST return the complete stored event and MUST revert when
`eventIndex >= eventCount(subjectId)`.

`eventCount` MUST return the number of events stored under a subject.

`eventCountByType` MUST return the number of events recorded with the exact
`eventType` under a subject.

`eventByTypeAt` MUST return the event index at the specified zero-based ordinal
and MUST revert when the ordinal is outside the type-specific index.

`lastRecordedEventByType` MUST return the greatest event index recorded with
the exact event type and MUST revert when no matching event exists.

`lastRecordedEventByType` describes recording order. It does not select the
event with the greatest `occurredAt` and does not resolve correction chains.

Correction events are indexed under `EVT_CORRECTION`, not under the event type
they correct. Consumers resolving an earlier event MUST use
`currentEventIndex` rather than assuming the last event of the original type is
its current state.

### Subject Identifiers

`subjectId` and `subjectType` are opaque to the log. Applications SHOULD use a
documented, domain-separated derivation and MUST NOT assume that equal subject
identifiers from independent logs have equal meaning without an explicit
coordination agreement.

The following subject-type identifiers are defined:

```solidity
bytes32 constant SUBJECT_TOKEN =
    keccak256("ERC-8328:SUBJECT_TYPE:TOKEN");
bytes32 constant SUBJECT_ADDRESS =
    keccak256("ERC-8328:SUBJECT_TYPE:ADDRESS");
bytes32 constant SUBJECT_ASSET =
    keccak256("ERC-8328:SUBJECT_TYPE:ASSET");
bytes32 constant SUBJECT_CASE =
    keccak256("ERC-8328:SUBJECT_TYPE:CASE");
```

Applications MAY define custom subject types using a documented namespace and
version.

### Event Types

The following event-type identifiers are defined:

```solidity
bytes32 constant EVT_ISSUANCE =
    keccak256("ERC-8328:EVENT_TYPE:ISSUANCE:V1");
bytes32 constant EVT_TRANSFER =
    keccak256("ERC-8328:EVENT_TYPE:TRANSFER:V1");
bytes32 constant EVT_REDEMPTION =
    keccak256("ERC-8328:EVENT_TYPE:REDEMPTION:V1");
bytes32 constant EVT_FREEZE =
    keccak256("ERC-8328:EVENT_TYPE:FREEZE:V1");
bytes32 constant EVT_UNFREEZE =
    keccak256("ERC-8328:EVENT_TYPE:UNFREEZE:V1");
bytes32 constant EVT_FORCED_TRANSFER =
    keccak256("ERC-8328:EVENT_TYPE:FORCED_TRANSFER:V1");
bytes32 constant EVT_KYC_APPROVED =
    keccak256("ERC-8328:EVENT_TYPE:KYC_APPROVED:V1");
bytes32 constant EVT_KYC_REVOKED =
    keccak256("ERC-8328:EVENT_TYPE:KYC_REVOKED:V1");
bytes32 constant EVT_KYC_UPDATED =
    keccak256("ERC-8328:EVENT_TYPE:KYC_UPDATED:V1");
bytes32 constant EVT_REGULATORY_HOLD =
    keccak256("ERC-8328:EVENT_TYPE:REGULATORY_HOLD:V1");
bytes32 constant EVT_HOLD_RELEASED =
    keccak256("ERC-8328:EVENT_TYPE:HOLD_RELEASED:V1");
bytes32 constant EVT_ALLOWLIST_ADDED =
    keccak256("ERC-8328:EVENT_TYPE:ALLOWLIST_ADDED:V1");
bytes32 constant EVT_ALLOWLIST_REMOVED =
    keccak256("ERC-8328:EVENT_TYPE:ALLOWLIST_REMOVED:V1");
bytes32 constant EVT_POLICY_CHANGE =
    keccak256("ERC-8328:EVENT_TYPE:POLICY_CHANGE:V1");
bytes32 constant EVT_CORRECTION =
    keccak256("ERC-8328:EVENT_TYPE:CORRECTION:V1");
```

`EVT_TRANSFER` is intended for compliance-significant transfer records, not as
a replacement for a token's ordinary transfer event. Applications SHOULD avoid
duplicating every routine token transfer unless the additional compliance
record is required by their reporting policy.

Custom event types SHOULD use a domain-separated namespace and explicit
version.

### Party Roles

The following party-role identifiers are defined:

```solidity
bytes32 constant ROLE_SENDER =
    keccak256("ERC-8328:PARTY_ROLE:SENDER");
bytes32 constant ROLE_RECEIVER =
    keccak256("ERC-8328:PARTY_ROLE:RECEIVER");
bytes32 constant ROLE_TARGET =
    keccak256("ERC-8328:PARTY_ROLE:TARGET");
bytes32 constant ROLE_BENEFICIARY =
    keccak256("ERC-8328:PARTY_ROLE:BENEFICIARY");
bytes32 constant ROLE_CONTROLLER =
    keccak256("ERC-8328:PARTY_ROLE:CONTROLLER");
bytes32 constant ROLE_SUBJECT =
    keccak256("ERC-8328:PARTY_ROLE:SUBJECT");
```

Custom party roles SHOULD use a documented namespace and version.

The base `Party` type contains an EVM address. It cannot carry an arbitrary
hashed identity without changing the interface. Applications needing private
or non-address party identifiers require a separate extension or SHOULD omit
those parties from the public record.

### Outcomes

The following outcome identifiers are defined:

```solidity
bytes32 constant OUTCOME_APPROVED =
    keccak256("ERC-8328:OUTCOME:APPROVED");
bytes32 constant OUTCOME_DENIED =
    keccak256("ERC-8328:OUTCOME:DENIED");
bytes32 constant OUTCOME_PENDING =
    keccak256("ERC-8328:OUTCOME:PENDING");
bytes32 constant OUTCOME_EXECUTED =
    keccak256("ERC-8328:OUTCOME:EXECUTED");
bytes32 constant OUTCOME_EXPIRED =
    keccak256("ERC-8328:OUTCOME:EXPIRED");
bytes32 constant OUTCOME_REVOKED =
    keccak256("ERC-8328:OUTCOME:REVOKED");
```

This ERC does not define or enforce an event-type and outcome compatibility
matrix. Applications MAY constrain combinations before recording. Consumers
MUST NOT infer that a combination was validated merely because the log accepted
it.

### Authority Identifiers

The following common authority identifiers are defined:

```solidity
bytes32 constant AUTHORITY_INTERNAL_POLICY =
    keccak256("ERC-8328:AUTHORITY:INTERNAL_POLICY:V1");
bytes32 constant AUTHORITY_COURT_ORDER =
    keccak256("ERC-8328:AUTHORITY:COURT_ORDER:V1");
bytes32 constant AUTHORITY_REGULATOR =
    keccak256("ERC-8328:AUTHORITY:REGULATOR:V1");
```

Custom authority identifiers SHOULD use a documented namespace and version.
The field records a claim and does not authenticate the named authority.

### Payload Profiles

The following payload-profile identifiers and ABI encodings are defined:

```solidity
bytes32 constant PAYLOAD_TRANSFER_V1 =
    keccak256("ERC-8328:PAYLOAD:TRANSFER:V1");
// abi.encode(
//     address from,
//     address to,
//     uint256 amount,
//     bytes32 routeRef
// )

bytes32 constant PAYLOAD_FREEZE_V1 =
    keccak256("ERC-8328:PAYLOAD:FREEZE:V1");
// abi.encode(
//     address target,
//     uint256 amount,
//     uint64 expiresAt,
//     bytes32 reason
// )

bytes32 constant PAYLOAD_KYC_V1 =
    keccak256("ERC-8328:PAYLOAD:KYC:V1");
// abi.encode(
//     address subject,
//     bytes32 jurisdiction,
//     bytes32 riskTier,
//     uint64 expiresAt
// )

bytes32 constant PAYLOAD_FORCED_TRANSFER_V1 =
    keccak256("ERC-8328:PAYLOAD:FORCED_TRANSFER:V1");
// abi.encode(
//     address from,
//     address to,
//     uint256 amount,
//     bytes32 legalBasis
// )
```

A recorder declaring one of these profiles MUST encode the payload exactly as
specified. Consumers MUST inspect `payloadProfileId` before decoding.

The log is not required to decode payloads or validate compatibility between a
payload profile, event type, parties, and outcome. Consumers SHOULD reject a
malformed known profile. Unknown profile identifiers MUST be treated as opaque
bytes.

Custom payload profiles SHOULD use a documented namespace and version and MUST
define an exact encoding.

### Operation References

`operationRef` is an application-defined correlation identifier linking the
record to an underlying action or workflow. It MAY be `bytes32(0)` when no such
reference is available.

A contract cannot access its transaction hash or final log index while
executing. Therefore, this ERC does not require a transaction-hash and log-index
derivation. Applications SHOULD compute a correlation identifier before the
underlying action and `recordEvent` calls when both occur in one transaction.

Recording an event in the same transaction as the underlying action provides
stronger linkage than recording it later, but the log still does not prove that
the record accurately describes that action.

### Interface Detection

Compliant logs MUST implement [ERC-165](./eip-165.md) and return `true` for
`type(IComplianceEventLog).interfaceId`.

ERC-165 indicates interface support only. It does not establish recorder
trustworthiness, evidence validity, authority, policy correctness, or legal
compliance.

## Rationale

### Why Subject-Linked Records?

An address-only or token-only key would exclude projects, assets, cases,
policies, and application-defined entities. An opaque subject identifier allows
one query model while leaving identity and namespace semantics to the
application.

### Why Separate Actor and Authority?

The technical account recording an event and the claimed basis for the action
are different facts. A module can execute several actions under different
mandates, while multiple modules can act under the same mandate.

### Why Structured Parties?

An untyped address list cannot distinguish a sender, receiver, target,
beneficiary, or controller. Explicit roles improve machine interpretation while
keeping the set extensible.

### Why Require an Evidence Commitment but Permit an Empty URI?

Every record should commit to the evidence representation used by the recorder,
but public retrieval can be inappropriate for confidential or regulated data.
A nonzero hash preserves the commitment while an optional URI permits private
distribution.

### Why Versioned Payload Profiles?

Opaque bytes without a declared schema prevent interoperable decoding.
Versioned profile identifiers let consumers recognize stable base encodings and
safely preserve unknown custom payloads.

### Why Correction Events Instead of Mutable Records?

Replacing an event would erase the prior assertion. A correction chain retains
the full history and identifies the current terminal record. The forward
pointer and single-successor rule prevent competing corrections to the same
event.

### Why Index Corrections Under Their Own Type?

A correction is a distinct lifecycle action. Indexing it under
`EVT_CORRECTION` preserves the original event-type history and lets consumers
query correction activity directly. Chain-resolution helpers provide current
state when needed.

### Why Store Records Rather Than Emit Events Only?

EVM contracts cannot read historical logs. Storing records allows on-chain
consumers to retrieve event details, traverse correction chains, and iterate by
event type. Emitted events remain useful for off-chain indexing.

### Why Keep Policy Validation Outside the Log?

The same event and outcome identifiers can be used under different legal and
application policies. The log standardizes representation and provenance, not
the rule engine deciding which combinations are valid.

### Prior Art

[ERC-8106](./eip-8106.md) defines [ERC-20](./eip-20.md) value-flow
observations, Compliance Entity and Decentralized Entity (CE/DE)
classification, compliance flags, and `bizId` correlation. This ERC instead
stores subject-linked lifecycle records with authority and evidence fields,
versioned payloads, type indexing, and correction chains. It does not define
CE/DE classification or require each record to correspond to an ERC-20 balance
change.

[ERC-3643](./eip-3643.md) defines regulated-token behavior, identity
registries, compliance modules, and lifecycle operations. This ERC is a
reporting layer that such a system may call; it does not replace enforcement or
identity checks.

[ERC-7943](./eip-7943.md) defines real-world asset (RWA) token behavior
including transfer eligibility, freezing, and forced transfers. This ERC
records attributed lifecycle assertions independently of any token interface.

[ERC-7512](./eip-7512.md) defines an on-chain representation of audit reports.
This ERC instead defines a subject-indexed compliance-event timeline and
correction model.

[ERC-5851](./eip-5851.md) defines on-chain verifiable credentials. Credentials
can authorize or identify a recorder, but they do not define this event-log
schema.

Generic attestation systems can represent compliance assertions through custom
schemas. This ERC defines a dedicated stored interface, base identifiers,
payload profiles, indexing behavior, and correction provenance.

## Backwards Compatibility

This ERC introduces a new interface and does not modify existing token,
identity, attestation, or compliance standards.

An existing token or compliance module can call a companion
`IComplianceEventLog` without changing its base token interface. Systems that do
not integrate with the log are unaffected.

The subject identifier is application defined, so adoption does not require a
particular asset registry or token standard.

## Test Cases

Implementations should test at least:

- zero-based, per-subject event indexing;
- subject isolation and event-type indexing;
- actor assignment to `msg.sender`;
- recording-time assignment and future-event rejection;
- the configured backdating policy;
- rejection of zero evidence commitments;
- empty and non-empty evidence URIs;
- party and payload size boundaries;
- original-event and correction-event guards;
- correction target bounds and same-subject behavior;
- original-actor, administrator, and unauthorized correction paths;
- correction fork prevention and multi-step linear chains;
- `currentEventIndex` from original, intermediate, and terminal events;
- `isEventCurrent` for corrected and terminal events;
- invalid event and ordinal queries;
- recording-order behavior of `lastRecordedEventByType`;
- correction indexing under `EVT_CORRECTION`;
- exact base payload-profile encodings;
- opaque handling of unknown payload profiles;
- acceptance of unconstrained event-type and outcome combinations; and
- positive and negative ERC-165 detection.

## Reference Implementation

A Solidity reference implementation, constants library, unit tests, Medusa
property tests, and independent audit are linked from the official discussion
thread.

The reference implementation:

- uses recorder and administrator roles;
- permits the original actor or an administrator with recorder authority to
  correct an event;
- limits party arrays to 10 entries;
- limits payloads to 2048 bytes;
- rejects events backdated by more than 30 days;
- stores unknown payload profiles without decoding them; and
- does not validate event-type and outcome combinations.

Role design and the 30-day backdating window are reference deployment choices.
The size limits and externally observable interface behavior are requirements
of this ERC.

## Security Considerations

### Recorder Trust

The log proves that an authorized address recorded particular bytes. It does
not prove that the record is true. A compromised, malicious, or incorrectly
authorized recorder can submit false or misleading events.

### Claimed Authority

`authority` is self-asserted. A recorder can claim a court order, regulator, or
internal policy that does not exist or does not authorize the action. Consumers
must verify authority evidence independently.

### Underlying Action Verification

A compliance event is not proof that the underlying issuance, transfer,
freeze, KYC decision, or other action occurred. Consumers requiring that proof
must verify the referenced operation and its relationship to the record.

### Correction Authorization

Fork prevention does not determine who is entitled to correct a record. A weak
correction policy can let one recorder supersede another recorder's assertions.
Implementations must document and enforce correction authority.

### Long Correction Chains

`currentEventIndex` traverses on-chain correction pointers. Although chains are
linear and acyclic, a long chain can consume substantial gas or make an on-chain
call impractical. Applications should avoid unnecessary repeated corrections
and may resolve long histories off-chain.

### Backdating

`occurredAt` is supplied by the recorder. Rejecting future timestamps prevents
one class of invalid input but does not establish historical accuracy. A
documented backdating limit reduces, but does not eliminate, fabricated history.

### Privacy and Data Protection

All event fields, dynamic payloads, party addresses, and URIs stored on a public
chain are permanently observable. Implementations must not place personal,
confidential, investigative, or legally restricted information on-chain merely
because the interface permits it.

Public-chain deployments should use opaque or salted subject identifiers,
minimal party arrays, generalized outcomes, redacted payloads, and evidence
commitments whose preimages are distributed through appropriate access
controls. Hashing low-entropy personal data without a secret salt does not
provide meaningful privacy.

### Evidence Availability and Ambiguity

A nonzero evidence hash does not make evidence available or identify the
hashing and document scheme by itself. Implementations must document how
evidence commitments are derived and how authorized consumers obtain the
preimage.

### Payload Confusion

The log can store malformed known payloads and arbitrary unknown profiles. A
consumer that decodes without first checking `payloadProfileId` can
misinterpret attacker-controlled bytes. Consumers must use profile-aware
decoding and reject malformed known profiles.

### Event-Type and Outcome Confusion

The base log does not validate event-type and outcome combinations. Consumers
must not treat a stored combination as policy-approved unless the recorder's
application enforces the applicable matrix.

### Storage Growth and Retrieval Costs

The log is append-only and grows monotonically. Authorization, bounded dynamic
fields, reporting-frequency policy, and operational monitoring are necessary
to control storage costs and spam.

### Event and Storage Interpretation

Off-chain indexers can reconstruct timelines from events, but on-chain
contracts cannot read historical logs. On-chain consumers must use storage
getters. Indexers should reconcile events with storage when resolving correction
chains and current state.

## Copyright

Copyright and related rights waived via [CC0](../LICENSE.md).
