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
Service Identity
A third-party audit service is an INK service role, not a standard INK agent:
| Concern | INK Agent | Audit Service |
|---|---|---|
| Identity | A key-derived tulpa:/ink: agentId or a did:web identity, optionally bound to a human owner | did:web or did:key, self-sovereign |
| Discovery | Agent Card at the versioned discovery path, GET <base>/ink/v1/<agentId>/agent.json | Agent Card capabilities.thirdPartyAudit.services on each subscribing agent’s card |
| Auth (inbound) | INK auth S3.3 | INK auth S3.3 (same) |
| Auth (outbound) | Signs with its active Agent Card signing key | Signs with its own Ed25519 key |
| Delegation proof | Required | Not 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.
Access Control
The service operates under access-controlled transparency (per SCITT):
- Events are tagged with
messageIdand 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
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
| Approach | How it works | Privacy |
|---|---|---|
Witness cosigning (C2SP tlog-witness) | Periodic checkpoints, independent witnesses verify consistency | Witnesses see only tree size + root hash |
| Rekor hash notary (Sigstore) | Submit checkpoint hashes to Rekor’s public log | Hash-only, content stays private |
Tier 2. Medium effort, stronger guarantees
| Approach | How it works | Privacy |
|---|---|---|
| SCITT service | COSE_Sign1-wrapped events, Merkle inclusion receipts | Access-controlled |
| INK-native Merkle service | INK auth, INK message format | Full INK access control |
Tier 3. Infrastructure investment
| Approach | How it works | Privacy |
|---|---|---|
| Tessera-based log | Static tile serving with external witnesses | Full control |
| OpenTimestamps | Daily Bitcoin anchor for legally defensible timestamps | Hash-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.