Skip to content

Third-Party Audit

Bilateral audit exchange has a fundamental limitation: a malicious agent can maintain two different hash chains and show each counterparty a different history (split-view attack). Third-party audit services solve this by introducing an independent witness that neither party controls.

Architecture

Diagram

Service Identity

A third-party audit service is an INK service role, not a standard INK agent:

ConcernINK AgentAudit Service
IdentityA key-derived tulpa:/ink: agentId or a did:web identity, optionally bound to a human ownerdid:web or did:key, self-sovereign
DiscoveryAgent Card at the versioned discovery path, GET <base>/ink/v1/<agentId>/agent.jsonAgent Card capabilities.thirdPartyAudit.services on each subscribing agent’s card
Auth (inbound)INK auth S3.3INK auth S3.3 (same)
Auth (outbound)Signs with its active Agent Card signing keySigns with its own Ed25519 key
Delegation proofRequiredNot applicable, configured trust

Submission Protocol

Agents submit events alongside normal hash chain maintenance. Submission is asynchronous and non-blocking.

POST /ink/v1/audit/submit
{
"protocol": "ink/0.1",
"type": "network.tulpa.audit_submit",
"from": "did:plc:agent",
"to": "did:web:audit.example.com",
"event": { /* InkAuditEvent */ },
"nonce": "<base64url>",
"timestamp": "2026-03-19T12:00:00Z"
}

Signed Inclusion Receipt

{
"protocol": "ink/0.1",
"type": "network.tulpa.audit_inclusion",
"eventId": "01JBTEST0001",
"treeSize": 48291,
"leafIndex": 48290,
"rootHash": "<SHA-256 hex of Merkle tree root>",
"timestamp": "2026-03-19T12:00:01Z",
"serviceSignature": "<base64url Ed25519, see canonical format below>"
}

Canonical signature format. serviceSignature is an Ed25519 signature over the UTF-8 bytes:

"ink/audit-inclusion/v1\n" || JCS({eventId, leafIndex, treeSize, rootHash, timestamp})

JCS is the RFC 8785 canonicalization of an object whose members are exactly eventId, leafIndex, treeSize, rootHash and timestamp. Nothing else is signed: protocol and type are envelope metadata, and inclusionProof is reconstructible from the tree. This is the same construction the Witness Service documents, and it is normative in ink-auditability.md §7.3.

Submission + Inclusion Flow (Detailed)

What happens step-by-step when an agent submits an event to a third-party audit service.

Diagram

Access Control

The service operates under access-controlled transparency (per SCITT):

  • Events are tagged with messageId and sender/recipient DIDs
  • Only parties to a message (or holders of a valid delegation chain) can query
  • The Merkle tree structure is public; event contents are access-controlled

Merkle Tree Structure

Diagram

Trust Model

The audit service is a semi-trusted witness, not an arbiter:

  • It CANNOT forge events (Ed25519 signatures from agents)
  • It CANNOT modify events without breaking Merkle proofs
  • It CAN suppress events (detectable via consistency proofs)
  • It CAN be unavailable (agents fall back to bilateral exchange)

For high-stakes interactions, agents MAY submit to multiple independent services.

Agent Card Advertisement

{
"capabilities": {
"auditExchange": true,
"thirdPartyAudit": {
"services": [
{
"endpoint": "https://audit.example.com/ink/v1",
"did": "did:web:audit.example.com",
"publicKey": "<Ed25519 public key hex>"
}
],
"submitPolicy": "all"
}
}
}

submitPolicy: all | high_value | none

Implementation Tiers

Tier 1. Lowest effort, highest immediate value

ApproachHow it worksPrivacy
Witness cosigning (C2SP tlog-witness)Periodic checkpoints, independent witnesses verify consistencyWitnesses see only tree size + root hash
Rekor hash notary (Sigstore)Submit checkpoint hashes to Rekor’s public logHash-only, content stays private

Tier 2. Medium effort, stronger guarantees

ApproachHow it worksPrivacy
SCITT serviceCOSE_Sign1-wrapped events, Merkle inclusion receiptsAccess-controlled
INK-native Merkle serviceINK auth, INK message formatFull INK access control

Tier 3. Infrastructure investment

ApproachHow it worksPrivacy
Tessera-based logStatic tile serving with external witnessesFull control
OpenTimestampsDaily Bitcoin anchor for legally defensible timestampsHash-only

Checkpoint Format

INK checkpoints follow the C2SP tlog-checkpoint note format: a three-line body, a blank line, then one or more signature lines. The body is the log origin, the tree size and the lowercase-hex SHA-256 root hash, each terminated by a line feed. The Witness Service serves exactly this format:

<origin>
<treeSize>
<hex root hash>
-- <origin> <base64url Ed25519 signature>

The Ed25519 signature covers the three body lines as <origin>\n<treeSize>\n<rootHash> with no trailing newline, so the origin is the domain separator that binds the signature to one log. A verifier MUST supply the origin it expects and reject a checkpoint whose body origin, or whose matching signature-line origin, is anything else. The body grammar is pinned by ink-merkle-checkpoint.md.