Skip to content

Audit Trail

Each agent maintains a per-agent hash-chained audit log with Ed25519 signatures per event.

Audit Event Envelope

InkAuditEvent = {
id: string, // ULID
version: "ink-audit/1",
agentId: string,
agentSignature: string, // Ed25519 over event (minus this field)
// Chain position (SSB-inspired)
sequence: number, // monotonically increasing from 1
previousEventHash: string | null, // SHA-256 of prior event; null for seq=1
// What happened
eventType: InkAuditEventType,
timestamp: string, // ISO 8601
// References
messageId?: string,
correlationId?: string,
counterpartyId?: string,
data?: Record<string, unknown>,
}

Event Types

Message Lifecycle

message.sent · message.received · message.queued · message.delivered · message.acted · message.rejected · message.expired · message.retracted

Receipt Lifecycle

receipt.sent · receipt.received

Delegation

delegation.granted · delegation.used · delegation.revoked · delegation.expired

Connection

connection.requested · connection.accepted · connection.declined

Verification

signature.verified · signature.failed · replay.detected

Hash Chain Structure

Diagram

Tamper Evidence

The audit chain uses both a hash chain and monotonic sequence numbers:

  • sequence: Monotonically increasing integer starting at 1. Gaps indicate deleted or suppressed events.
  • previousEventHash: SHA-256 of the JCS-canonicalized prior event (excluding agentSignature). Null for sequence=1.
  • agentSignature: Ed25519 signature over the event (excluding this field). Proves the agent attested to this event at this chain position.

Fork detection: If an agent presents two different events with the same sequence number, the chain is forked and SHOULD be treated as untrusted.

Audit Exchange Protocol

Agents exchange audit records via POST /ink/v1/audit.

Request

{
"protocol": "ink/0.1",
"type": "network.tulpa.audit_query",
"from": "did:plc:alice",
"to": "did:plc:bob",
"messageId": "msg-123",
"nonce": "<base64url>",
"timestamp": "2026-03-19T12:00:00Z"
}

Response

{
"protocol": "ink/0.1",
"type": "network.tulpa.audit_response",
"messageId": "msg-123",
"events": [ /* InkAuditEvent[] */ ],
"responseSignature": "<Ed25519 over JCS(events)>"
}

The responseSignature allows the requester to prove the responder attested to this specific audit history.

Access Control

The responder MUST verify that the requester’s DID is either the sender or recipient of the referenced messageId. If not, return access_denied.

Dispute Resolution

Diagram

Reconciliation: Agreement vs Divergence

When agents exchange audit records, four outcomes are possible. Implementations MUST handle all four.

Diagram

Retention Policy

  • Message lifecycle events: 12 months minimum
  • Delegation events: lifetime + 12 months
  • Connection events: lifetime + 6 months

Export Format

  • JSON Lines (one InkAuditEvent per line, newline-delimited)
  • File naming: ink-audit-{agentId}-{startDate}-{endDate}.jsonl
  • Trailing line with final hash chain value