OATH

OATH Protocol Specification

Version 0.1.0
Status Draft
License Apache-2.0
Canonical home https://oath.sh/spec

Abstract

OATH (Open Agent Transparency & Honesty) is an open protocol for AI agent accountability. An agent publicly takes an oath: a versioned document of numbered, falsifiable laws. From then on its actions are adjudicated against those laws, every verdict citing the law by number, and every record is appended to a tamper-evident, append-only, publicly verifiable ledger. Violations and uncertainty must be confessed by contract. Over time the record yields a published-formula honesty index that no one — including the ledger operator — can quietly edit.

Every claim this protocol makes about an agent is checkable by a stranger with no trust in the agent, the operator, or the authors of this document. Anything that cannot be verified independently is out of scope.

Machines may fail. They may not hide it.


1. Terminology

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 BCP 14 [RFC 2119] [RFC 8174] when, and only when, they appear in all capitals.

1.2 Conventions and encodings

1.3 What OATH guarantees — and what it does not

OATH makes tampering evident, not impossible, and makes honesty measurable, not automatic. Verifiable guarantees: a logged entry cannot be altered, reordered, or deleted without detection; a verdict cannot be rewritten after the fact; a confession cannot be quietly withdrawn. Not guaranteed: that an agent logs everything it does. An agent that omits actions is not caught by cryptography; it is caught by scope declarations, coverage measurement (§10), and monitors comparing the ledger against the agent's observable behavior. This specification states this limit plainly because the protocol's authority depends on never overclaiming.


2. The Oath Document

2.1 Two forms

An oath exists in two forms:

If the two disagree, the canonical JSON governs.

2.2 Canonical form

{
  "spec_version": "0.1",
  "agent": "thm",
  "display_name": "The Honest Machine",
  "version": 1,
  "pubkey": "<64 hex chars: raw Ed25519 public key>",
  "scope": "Plain-language declaration of which actions this agent logs.",
  "commitments": { "entries_per_30d": 30, "max_latency_seconds": 300 },
  "laws": [
    { "n": 1, "mode": "machine", "text": "..." },
    { "n": 2, "mode": "judgment", "text": "..." }
  ],
  "created_at": "2026-07-13T00:00:00Z",
  "signature": "<128 hex chars>"
}

Field rules:

The oath hash is the SHA-256 of the canonical bytes of the complete signed document (including signature). It identifies the oath version everywhere else in the protocol.

2.3 Identity

An agent's identity is the pair (handle, public key). The key is authoritative; the handle is a name. A public ledger MUST enforce handle uniqueness within itself and MUST bind a handle to the key that first swore it. Key rotation is an amendment (§9): a new oath version carrying the new pubkey, signed by the previous version's key.

2.4 Swearing

An agent is sworn when its oath document is logged: the first entry for an agent MUST have action.type = oath.swear and action.content_hash equal to the oath hash of version 1 (§3.4). A ledger accepting a swear MUST verify the oath document's signature before appending. The oath document itself MUST be retained and served by the ledger (§11.2), so that the tree commits to the laws from the first leaf onward.


3. Entries and Canonical Serialization

3.1 Canonical form

{
  "spec_version": "0.1",
  "agent": "thm",
  "oath_version": 1,
  "action": {
    "type": "cycle.publish",
    "summary": "One or two sentences describing what was done.",
    "content_hash": "<64 hex chars or null>",
    "occurred_at": "2026-07-13T00:00:00Z"
  },
  "verdict": "violation",
  "law_cited": 3,
  "adjudication": { "mode": "machine", "check": "law3.grade-freeze" },
  "confession": { "note": "Grade for H-041 was edited 12s after publication." },
  "timestamp": "2026-07-13T00:00:05Z",
  "signature": "<128 hex chars>"
}

Field rules:

3.2 Leaf bytes

The leaf bytes of an entry are the canonical bytes of the complete signed entry (including signature). These exact bytes are what the Merkle tree commits to (§4) and what ledgers store and serve. The same entry has the same leaf bytes — and therefore the same leaf hash — in every ledger it appears in; leaf indices are per-ledger.

An entry whose signature does not verify against the agent's sworn key, or which violates any rule in §3.1 or §3.3, is malformed. A ledger MUST reject malformed entries and MUST NOT modify entries to repair them.

3.3 Verdict rules

An agent that cannot determine whether an action complied MUST log uncertain with a confession. Logging nothing is a coverage failure; logging compliant without basis is a lie that adjudication records make re-checkable (§7). There is no fourth verdict and no silent state.

3.4 Reserved action types

Reserved entries carry verdicts like any other entry: they are adjudicated against the oath's amendment and identity rules (Mode A).


4. Hashing and the Merkle Log

The ledger is an append-only Merkle tree over leaf bytes, in the style of RFC 6962 (Certificate Transparency).

The Merkle Tree Hash (MTH) of a list of leaves D[n]:

The 0x00/0x01 domain separation prevents leaf/node confusion attacks and MUST be implemented exactly. This structure is identical to RFC 6962 §2.1, so its security analysis and proof algorithms apply directly.

Storage is not specified; the tree is defined by the ordered leaf bytes alone. Any store that can reproduce the ordered leaves — Postgres, JSONL files (Appendix A), anything — can serve a conforming ledger.


5. Signed Tree Heads

An STH is the ledger's signed assertion of its entire history at a point in time:

{
  "ledger": "oath.sh",
  "tree_size": 1042,
  "root_hash": "<64 hex chars>",
  "timestamp": "2026-07-13T00:00:10Z",
  "signature": "<128 hex chars>"
}

Rules:


6. Proofs

6.1 Inclusion proofs

An inclusion proof demonstrates that a given leaf is in the tree described by an STH. The audit path is computed exactly as PATH(m, D[n]) in RFC 6962 §2.1.1, and verified by recomputing the root from the leaf hash and the path. A ledger MUST serve an inclusion proof for any leaf it holds, against any STH it has emitted whose tree_size covers that leaf.

Proof object: { "leaf_index": m, "tree_size": n, "audit_path": [ ... ] } where audit_path is the ordered list of sibling hashes, lowest first.

6.2 Consistency proofs

A consistency proof demonstrates that the tree at size n is an append-only extension of the tree at size m — that history was extended, never rewritten. It is computed exactly as PROOF(m, D[n]) in RFC 6962 §2.1.2. A ledger MUST serve a consistency proof between any two tree sizes at which it has emitted STHs.

Proof object: { "old_size": m, "new_size": n, "path": [ ... ] }.

6.3 Proof failure: oathbreaker

A verifier holding cryptographic proof of tampering MUST treat the record as oathbreaker status. Exactly three proofs qualify:

  1. Two signature-valid STHs from the same ledger key that fail a consistency proof (history was rewritten, or two views were shown to two audiences).
  2. A signature-valid receipt (§11.3) whose entry is absent from every STH emitted after the receipt's merge delay elapsed.
  3. A signature-valid entry whose leaf bytes differ from what an inclusion proof against a signed STH commits to.

Oathbreaker status is terminal and applies to the key that signed the conflicting statements: the ledger key in cases 1–2, which for a local ledger is the agent itself. The index of an oathbreaker record is void (§10.4). Accusation, suspicion, or judgment never assign this status — only proofs a stranger can re-check.


7. Adjudication

Every entry records how its verdict was produced, in the adjudication field. Two modes exist. A verdict that cannot be re-derived by a third party is not a verdict; it is an opinion, and it is non-conforming.

7.1 Mode A — machine-checkable

The verdict is the output of a deterministic check over the entry's action record and declared context.

{ "mode": "machine", "check": "law3.grade-freeze", "check_version": "1" }

Mode A SHOULD be preferred. Laws SHOULD be written to be Mode-A checkable.

7.2 Mode B — model-adjudicated

The verdict is produced by a model applying a judgment law. Permitted, under one hard rule: re-computability.

{
  "mode": "model",
  "model": "<exact model identifier>",
  "prompt_hash": "<64 hex chars: SHA-256 of the adjudicator prompt>",
  "inputs_hash": "<64 hex chars: SHA-256 of the complete input record>"
}

Models are not deterministic; a re-run MAY disagree. The requirement is not identical output — it is that anyone can re-run the same judgment on the same inputs and publish disagreement. Hidden judgment is what this mode forbids.

7.3 The adjudicator contract

A Mode B adjudicator prompt MUST instruct the model to: receive the numbered laws, the canonical action record, and context; return exactly one verdict from compliant | violation | uncertain; cite the governing law by number or return null; judge the action, not the agent; say uncertain when genuinely uncertain rather than guess; and output JSON only: {"verdict": "...", "law_cited": n | null, "reason": "one line"}.

The reference adjudicator prompt is published at oath.sh and identified by its hash.

7.4 Who adjudicates

In this version, adjudication ordinarily runs inside the agent's own process (the SDK). This is self-adjudication, and the protocol does not pretend otherwise: the trust does not come from the judge's independence but from the record's permanence and re-computability. Any monitor can re-judge any entry and publish a disagreement. Independent adjudication services are an anticipated evolution, not a v0.1 requirement.


8. Confessions

8.1 Form

{ "note": "Plain-language statement of what went wrong or is unknown." }

note MUST be non-empty plain language, specific enough that a reader understands what happened without other context. Boilerplate ("an error occurred") does not conform.

8.2 Timing

A confession exists to precede discovery. Therefore:

8.3 Permanence

Confessions are entries; entries are permanent. A confession MUST NOT be softened, amended, or contextualized by editing — only by new entries. A correction is a new action with its own entry and its own verdict.


9. Amendments

An oath changes only forward and in public.

Retroactive change is not forbidden so much as structurally impossible: the tree commits to every oath version at the moment it was sworn, and a rewrite is an oathbreaker proof (§6.3). That is the point of the tree.


10. The Index

The honesty index is a per-agent score computed by a published formula over ledger contents. This section is formula version index-v0.

10.1 Components

All components lie in [0, 1].

10.2 Formula

score = round(100 × (0.20·coverage
              + 0.35·violation_component
              + 0.25·confession_integrity
              + 0.20·continuity))

10.3 Publication

An index MUST always be published with its formula_version, its component values, and the tree size it was computed at, so that anyone can recompute it. The reference implementation stores these as snapshots.

10.4 The hard line

The index MUST be a pure function of ledger contents and the published formula. Nothing else may ever change it: no payment, no partnership, no manual adjustment, no exception, forever. A ledger operator MUST NOT publish, for any agent, any score not derived from the published formula. Changing the formula is a new formula_version, adopted only through the process in §12, announced as a logged amendment to the operator's own oath (§11.4) before taking effect; prior scores remain recomputable under the formula that produced them. The index of an oathbreaker record (§6.3) is void and MUST be displayed as the status, not as a number.


11. Ledger Operator Duties

A local ledger binds only its agent. A public ledger — one that accepts submissions or serves records to third parties — assumes the duties in this section. The reference public ledger at oath.sh operates under exactly these duties.

11.1 Structural duties

  1. Append-only, structurally. Entries and oath documents are never updated or deleted. Operators SHOULD make this physical, not procedural — e.g. database roles with no UPDATE/DELETE grants on those tables, for any role including the service's own.
  2. One view. The ledger MUST present a single, consistent tree to all audiences. Split views are oathbreaker proof (§6.3).
  3. STH cadence. Per §5: within the declared merge delay of incorporation, and at least every 24 hours.
  4. Validation only, never editing. The ledger MUST reject malformed submissions (§3.2) and MUST NOT alter, re-serialize, or "fix" any entry. The ledger stores verdicts; it never produces them.

11.2 Service duties

A public ledger MUST serve, publicly, without authentication or fee:

Resource Endpoint (under the API base)
Latest STH GET /v0/sth
STH history GET /v0/sth/history
Entry by index (exact leaf bytes) GET /v0/entries/{index}
Entry range GET /v0/entries?start={i}&end={j}
Inclusion proof GET /v0/proof/inclusion?leaf_hash={h}&tree_size={n}
Consistency proof GET /v0/proof/consistency?old_size={m}&new_size={n}
Agent record + oath versions GET /v0/agents/{handle}
Canonical oath document GET /v0/agents/{handle}/oath/{version}
Index snapshot with components GET /v0/agents/{handle}/index
Submission POST /v0/entries → receipt

Discovery: a public ledger MUST publish https://<host>/.well-known/oath/ledger.json containing { "ledger", "pubkey", "api", "mmd_seconds", "spec_version" }.

11.3 Receipts and merge delay

On accepting a submission, a public ledger MUST return a receipt:

{
  "ledger": "oath.sh",
  "leaf_hash": "<64 hex chars>",
  "received_at": "2026-07-13T00:00:06Z",
  "mmd_seconds": 600,
  "signature": "<128 hex chars: ledger key, over the receipt without signature>"
}

The receipt is the ledger's promise: the leaf will be covered by an STH within mmd_seconds (the maximum merge delay) of received_at. The ledger MUST declare its MMD in its discovery document; the reference ledger declares 600 seconds. A broken receipt is oathbreaker proof (§6.3).

11.4 The operator's own oath

A public ledger MUST itself be a sworn agent: it MUST publish its own oath — covering at minimum duties 11.1(1–4), its MMD, and the §10.4 hard line — and log its own conduct as entries in its own tree, adjudicated Mode A. The operator of the accountability infrastructure is its own first subject. A ledger that will not swear its own oath does not conform to this specification.


12. Versioning of This Specification


Appendix A. Local Ledger File Layout (informative)

The reference SDK writes a local ledger as a directory:

oath-ledger/
  oath.json         # current oath (canonical form); prior versions in oath.history/
  entries.jsonl     # one entry per line, in leaf order; line k = leaf index k
  heads.json        # every STH ever emitted, in order

Each line of entries.jsonl MUST be the exact leaf bytes of its entry (the newline separator is not part of the leaf bytes). Verification needs no index structures: recompute the tree from the lines, then check every STH in heads.json against it. This is what oath verify ./oath-ledger does.

Appendix B. Test Vectors (normative)

All values below are reproducible: Ed25519 signing is deterministic [RFC 8032], and the private key seed is published. The generator script lives at spec/vectors.mjs in the repository (node spec/vectors.mjs). The JSON in these vectors is ASCII with integer numbers only, so JCS serialization equals compact JSON with lexicographically sorted keys.

B.1 Key

seed   = SHA-256("oath-spec-v0.1-test-key")
       = b5792bfb6ec2bce22d5b62804250be2d2fc79cc2f877a51f2dc1f65b001f3695
pubkey = 5c4427726ed019725b32d750d205ce3c765f291b75da43e59328d0dfaaa51c97

B.2 Tree hashing

Leaves are the ASCII byte strings entry-0entry-6 (in production, leaves are entry leaf bytes; the algorithm is byte-string agnostic).

MTH of empty tree      = e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
leaf_hash("entry-0")   = 40766b2033429026f53d54502679a839706b4741f8dcaf3a8bba5f41b5ffe075
leaf_hash("entry-1")   = e868811a482c27d50b6d45dde79c465d6adb9b06645100477a90cf3d8518898b
root, tree_size=1      = 40766b2033429026f53d54502679a839706b4741f8dcaf3a8bba5f41b5ffe075
root, tree_size=2      = 2f27a5082c1d42afa488ac350a9fc4390c084f54f71ecdff859e98db8429b479
root, tree_size=3      = a64bf26e09128f6fe2fe6f8b2d8c801e166b57c047a7cd9b2b809e7a96a2f1cb
root, tree_size=7      = 9139601cc1ca8ab2a7a0c2c134c04845f2b1ba549a83d6c845cfcda439cc585d

Inclusion proof for leaf_index=2, tree_size=7 (audit path, lowest first):

[0] 27479b6ab321d2ee477452f68ba527748e863cafe8fbd1df2bf89d1570d1b697
[1] 2f27a5082c1d42afa488ac350a9fc4390c084f54f71ecdff859e98db8429b479
[2] e429c5b5ccaa9523c37297f1846766f903137e82195c5199e6be57130d1006c8

Consistency proof for old_size=3, new_size=7:

[0] 049d7dcdb56bcfebd313304c9839f196a3d4b6ef3bdc0b08298f93ac8191f0a8
[1] 27479b6ab321d2ee477452f68ba527748e863cafe8fbd1df2bf89d1570d1b697
[2] 2f27a5082c1d42afa488ac350a9fc4390c084f54f71ecdff859e98db8429b479
[3] e429c5b5ccaa9523c37297f1846766f903137e82195c5199e6be57130d1006c8

B.3 Signed oath document

Signing input (canonical bytes of the document without signature):

{"agent":"example","created_at":"2026-07-13T00:00:00Z","laws":[{"mode":"machine","n":1,"text":"Every published result includes its content hash."},{"mode":"machine","n":2,"text":"No record is edited after publication."}],"pubkey":"5c4427726ed019725b32d750d205ce3c765f291b75da43e59328d0dfaaa51c97","scope":"All publish actions of the example agent.","spec_version":"0.1","version":1}
signature = 2dbedf47080d818226eea5d8263557109e03ffc238209837e41a9b7bd01b342e84e844018353a6a5beefcd4064be2415e77e81d19a65db5c2cc97b039a5d2808
oath_hash = 86e16c8e2d1649b9a1f32fcfd256c4589dfdb13564da2b54497da5b23d238b56

B.4 Signed entry

action.content_hash is the SHA-256 of the ASCII bytes hello, ledger. Signing input (canonical bytes of the entry without signature):

{"action":{"content_hash":"c69d7f5eec23dabb082f15051db62b0cf5d3045d7aae68e0f8a93ecf44a4aaae","occurred_at":"2026-07-13T00:00:00Z","summary":"Published result 42 with its content hash.","type":"result.publish"},"adjudication":{"check":"law1.content-hash-present","mode":"machine"},"agent":"example","law_cited":1,"oath_version":1,"spec_version":"0.1","timestamp":"2026-07-13T00:00:05Z","verdict":"compliant"}
signature = 5913fd6e2fed158b9fb81748aae02580e088db1be987df0c2cfef3d93c2236374ca17e6b40aa5e69227a3b6d64fbbd02227ee22ec43e99951c558a55cdd9a105

Leaf bytes (canonical bytes of the signed entry):

{"action":{"content_hash":"c69d7f5eec23dabb082f15051db62b0cf5d3045d7aae68e0f8a93ecf44a4aaae","occurred_at":"2026-07-13T00:00:00Z","summary":"Published result 42 with its content hash.","type":"result.publish"},"adjudication":{"check":"law1.content-hash-present","mode":"machine"},"agent":"example","law_cited":1,"oath_version":1,"signature":"5913fd6e2fed158b9fb81748aae02580e088db1be987df0c2cfef3d93c2236374ca17e6b40aa5e69227a3b6d64fbbd02227ee22ec43e99951c558a55cdd9a105","spec_version":"0.1","timestamp":"2026-07-13T00:00:05Z","verdict":"compliant"}
leaf_hash = 6be26993fc0e8763631bbdc33f7975f64a03281c391ed6c2fc9bad8d135f3f9f

B.5 Signed tree head

For a tree containing exactly the entry above (tree_size=1, so root_hash = leaf_hash). Signing input:

{"ledger":"example.local","root_hash":"6be26993fc0e8763631bbdc33f7975f64a03281c391ed6c2fc9bad8d135f3f9f","timestamp":"2026-07-13T00:00:10Z","tree_size":1}
signature = 4e6c4dbd69227db9fcd43502a216b668949ce6c7b41d6b8d211eb986a3d9c33f39499d48344e30f9e34daef1fddb0055b997a6b0cf45cb41810f7d9013209006

References