@thehonestmachine/oath-sdk 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +7 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/localLedger.d.ts +73 -0
- package/dist/localLedger.d.ts.map +1 -0
- package/dist/localLedger.js +195 -0
- package/dist/localLedger.js.map +1 -0
- package/dist/remote.d.ts +38 -0
- package/dist/remote.d.ts.map +1 -0
- package/dist/remote.js +78 -0
- package/dist/remote.js.map +1 -0
- package/dist/verify.d.ts +43 -0
- package/dist/verify.d.ts.map +1 -0
- package/dist/verify.js +278 -0
- package/dist/verify.js.map +1 -0
- package/package.json +55 -0
- package/src/index.ts +17 -0
- package/src/localLedger.ts +255 -0
- package/src/remote.ts +112 -0
- package/src/verify.ts +331 -0
package/dist/verify.d.ts
ADDED
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { type OathDoc, type SignedTreeHead } from '@thehonestmachine/oath-core';
|
|
2
|
+
export interface CheckResult {
|
|
3
|
+
name: string;
|
|
4
|
+
ok: boolean;
|
|
5
|
+
detail: string;
|
|
6
|
+
}
|
|
7
|
+
export interface VerificationReport {
|
|
8
|
+
ok: boolean;
|
|
9
|
+
source: string;
|
|
10
|
+
agents: string[];
|
|
11
|
+
treeSize: number;
|
|
12
|
+
checks: CheckResult[];
|
|
13
|
+
}
|
|
14
|
+
/** Everything a monitor needs, however it was obtained. All of it public. */
|
|
15
|
+
export interface LedgerData {
|
|
16
|
+
source: string;
|
|
17
|
+
/** Exact leaf bytes, position = leaf index. */
|
|
18
|
+
leafLines: string[];
|
|
19
|
+
/** Every signed tree head, in emission order. */
|
|
20
|
+
heads: SignedTreeHead[];
|
|
21
|
+
/** Keys allowed to sign tree heads (agent key locally, ledger key remotely). */
|
|
22
|
+
sthPubkeys: string[];
|
|
23
|
+
/** Oath version chains per agent handle. */
|
|
24
|
+
oathsByAgent: Map<string, OathDoc[]>;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Verify ledger contents from nothing but public data (SPEC.md §6, §11):
|
|
28
|
+
* recompute the tree, re-check every signature, every tree head, every oath
|
|
29
|
+
* chain, and the consistency of the whole history. Needs no keys and no
|
|
30
|
+
* trust in the operator — this is what a monitor runs.
|
|
31
|
+
*/
|
|
32
|
+
export declare function verifyLedgerData(data: LedgerData): VerificationReport;
|
|
33
|
+
/** Verify a local ledger directory (SPEC.md Appendix A). */
|
|
34
|
+
export declare function verifyLocalLedger(dir: string): VerificationReport;
|
|
35
|
+
/**
|
|
36
|
+
* Verify a remote public ledger over its read API (SPEC.md §11.2), starting
|
|
37
|
+
* from its discovery document. Entries beyond the latest tree head are
|
|
38
|
+
* within the merge delay and are not judged yet.
|
|
39
|
+
*/
|
|
40
|
+
export declare function verifyRemoteLedger(url: string, options?: {
|
|
41
|
+
fetch?: typeof fetch;
|
|
42
|
+
}): Promise<VerificationReport>;
|
|
43
|
+
//# sourceMappingURL=verify.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.d.ts","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"AAAA,OAAO,EAkBL,KAAK,OAAO,EACZ,KAAK,cAAc,EACpB,MAAM,6BAA6B,CAAC;AAKrC,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,WAAW,EAAE,CAAC;CACvB;AAED,6EAA6E;AAC7E,MAAM,WAAW,UAAU;IACzB,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,iDAAiD;IACjD,KAAK,EAAE,cAAc,EAAE,CAAC;IACxB,gFAAgF;IAChF,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,4CAA4C;IAC5C,YAAY,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,CAAC,CAAC;CACtC;AAED;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,UAAU,GAAG,kBAAkB,CAiLrE;AAUD,4DAA4D;AAC5D,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,MAAM,GAAG,kBAAkB,CAuBjE;AAED;;;;GAIG;AACH,wBAAsB,kBAAkB,CACtC,GAAG,EAAE,MAAM,EACX,OAAO,GAAE;IAAE,KAAK,CAAC,EAAE,OAAO,KAAK,CAAA;CAAO,GACrC,OAAO,CAAC,kBAAkB,CAAC,CAmD7B"}
|
package/dist/verify.js
ADDED
|
@@ -0,0 +1,278 @@
|
|
|
1
|
+
import { assertEntryUnderOath, assertValidAmendment, assertValidEntry, assertValidOathDoc, assertValidSth, bytesToHex, canonicalize, consistencyPath, hexToBytes, leafHash, oathHash, rootFromLeafHashes, utf8Bytes, verifyConsistency, verifyOathSignature, verifySthSignature, } from '@thehonestmachine/oath-core';
|
|
2
|
+
import { readFileSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { LEDGER_FILES, readLeafLines, readOathVersions } from './localLedger.js';
|
|
5
|
+
/**
|
|
6
|
+
* Verify ledger contents from nothing but public data (SPEC.md §6, §11):
|
|
7
|
+
* recompute the tree, re-check every signature, every tree head, every oath
|
|
8
|
+
* chain, and the consistency of the whole history. Needs no keys and no
|
|
9
|
+
* trust in the operator — this is what a monitor runs.
|
|
10
|
+
*/
|
|
11
|
+
export function verifyLedgerData(data) {
|
|
12
|
+
const { leafLines, heads, sthPubkeys, oathsByAgent } = data;
|
|
13
|
+
const checks = [];
|
|
14
|
+
const agents = [...oathsByAgent.keys()];
|
|
15
|
+
checks.push({
|
|
16
|
+
name: 'files',
|
|
17
|
+
ok: true,
|
|
18
|
+
detail: `${agents.length} agent(s), ${leafLines.length} entries, ${heads.length} tree heads`,
|
|
19
|
+
});
|
|
20
|
+
// 1. Oath chains: v1 self-signed, every later version a valid amendment (§2, §9).
|
|
21
|
+
const chainProblems = [];
|
|
22
|
+
for (const [handle, versions] of oathsByAgent) {
|
|
23
|
+
try {
|
|
24
|
+
if (versions.length === 0)
|
|
25
|
+
throw new Error('no oath versions');
|
|
26
|
+
versions.forEach((doc) => assertValidOathDoc(doc));
|
|
27
|
+
if (versions[0].version !== 1)
|
|
28
|
+
throw new Error('chain does not start at version 1');
|
|
29
|
+
if (!verifyOathSignature(versions[0])) {
|
|
30
|
+
throw new Error('v1 signature does not verify against its own pubkey');
|
|
31
|
+
}
|
|
32
|
+
for (let i = 1; i < versions.length; i++) {
|
|
33
|
+
assertValidAmendment(versions[i - 1], versions[i]);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
catch (error) {
|
|
37
|
+
chainProblems.push(`${handle}: ${error.message}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
checks.push({
|
|
41
|
+
name: 'oath-chain',
|
|
42
|
+
ok: chainProblems.length === 0,
|
|
43
|
+
detail: chainProblems.length === 0
|
|
44
|
+
? `${agents.length} chain(s), forward-only, signatures verify`
|
|
45
|
+
: chainProblems.join('; '),
|
|
46
|
+
});
|
|
47
|
+
// 2. Every entry: canonical bytes, valid shape, signed under its oath version (§3).
|
|
48
|
+
const entries = [];
|
|
49
|
+
const entryProblems = [];
|
|
50
|
+
leafLines.forEach((line, i) => {
|
|
51
|
+
try {
|
|
52
|
+
const parsed = JSON.parse(line);
|
|
53
|
+
if (canonicalize(parsed) !== line)
|
|
54
|
+
throw new Error('line is not in canonical (JCS) form');
|
|
55
|
+
assertValidEntry(parsed);
|
|
56
|
+
const versions = oathsByAgent.get(parsed.agent);
|
|
57
|
+
const oath = versions?.find((o) => o.version === parsed.oath_version);
|
|
58
|
+
if (oath === undefined) {
|
|
59
|
+
throw new Error(`cites unknown oath ${parsed.agent} v${parsed.oath_version}`);
|
|
60
|
+
}
|
|
61
|
+
assertEntryUnderOath(parsed, oath);
|
|
62
|
+
entries.push(parsed);
|
|
63
|
+
}
|
|
64
|
+
catch (error) {
|
|
65
|
+
entries.push(null);
|
|
66
|
+
entryProblems.push(`entry ${i}: ${error.message}`);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
checks.push({
|
|
70
|
+
name: 'entries',
|
|
71
|
+
ok: entryProblems.length === 0,
|
|
72
|
+
detail: entryProblems.length === 0
|
|
73
|
+
? `${leafLines.length} entries valid, canonical, and signed`
|
|
74
|
+
: entryProblems.join('; '),
|
|
75
|
+
});
|
|
76
|
+
// 3. Sworn records: each agent's first entry is its oath.swear; every
|
|
77
|
+
// amendment is committed to by an oath.amend entry (§2.4, §9).
|
|
78
|
+
const recordProblems = [];
|
|
79
|
+
for (const [handle, versions] of oathsByAgent) {
|
|
80
|
+
const own = entries.filter((e) => e !== null && e.agent === handle);
|
|
81
|
+
const first = own[0];
|
|
82
|
+
const v1Hash = versions.length > 0 ? safeOathHash(versions[0]) : null;
|
|
83
|
+
if (first === undefined || first.action.type !== 'oath.swear' || first.action.content_hash !== v1Hash) {
|
|
84
|
+
recordProblems.push(`${handle}: first entry is not oath.swear committing to oath v1`);
|
|
85
|
+
}
|
|
86
|
+
// Every declared amendment must be committed to by exactly its own leaf...
|
|
87
|
+
const validAmendHashes = new Set();
|
|
88
|
+
for (let v = 2; v <= versions.length; v++) {
|
|
89
|
+
const hash = safeOathHash(versions[v - 1]);
|
|
90
|
+
if (hash !== null)
|
|
91
|
+
validAmendHashes.add(hash);
|
|
92
|
+
if (!own.some((e) => e.action.type === 'oath.amend' && e.action.content_hash === hash)) {
|
|
93
|
+
recordProblems.push(`${handle}: no oath.amend entry commits to oath v${v}`);
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
// ...and no reserved oath-lifecycle leaf may be forged. A stranger's
|
|
97
|
+
// monitor is the trust anchor: an extra oath.swear, or an oath.amend
|
|
98
|
+
// committing to an oath that was never served, must fail verification.
|
|
99
|
+
own.forEach((e, i) => {
|
|
100
|
+
if (e.action.type === 'oath.swear' && (i !== 0 || e.action.content_hash !== v1Hash)) {
|
|
101
|
+
recordProblems.push(`${handle}: leaf with a forged/misplaced oath.swear (only leaf 0 may swear v1)`);
|
|
102
|
+
}
|
|
103
|
+
if (e.action.type === 'oath.amend' && !validAmendHashes.has(e.action.content_hash ?? '')) {
|
|
104
|
+
recordProblems.push(`${handle}: oath.amend commits to an oath version the ledger never served`);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
checks.push({
|
|
109
|
+
name: 'sworn-record',
|
|
110
|
+
ok: recordProblems.length === 0,
|
|
111
|
+
detail: recordProblems.length === 0
|
|
112
|
+
? 'every agent swore at its first leaf; every amendment logged'
|
|
113
|
+
: recordProblems.join('; '),
|
|
114
|
+
});
|
|
115
|
+
// 4. Tree heads: recompute every root, verify every signature (§5).
|
|
116
|
+
const leafHashes = leafLines.map((line) => leafHash(utf8Bytes(line)));
|
|
117
|
+
const headProblems = [];
|
|
118
|
+
let previousSize = 0;
|
|
119
|
+
heads.forEach((head, i) => {
|
|
120
|
+
try {
|
|
121
|
+
assertValidSth(head);
|
|
122
|
+
if (head.tree_size < previousSize)
|
|
123
|
+
throw new Error('tree size decreased');
|
|
124
|
+
previousSize = head.tree_size;
|
|
125
|
+
if (head.tree_size > leafHashes.length) {
|
|
126
|
+
throw new Error(`covers ${head.tree_size} leaves but only ${leafHashes.length} are known`);
|
|
127
|
+
}
|
|
128
|
+
const recomputed = bytesToHex(rootFromLeafHashes(leafHashes.slice(0, head.tree_size)));
|
|
129
|
+
if (recomputed !== head.root_hash) {
|
|
130
|
+
throw new Error(`root hash does not match the recomputed tree at size ${head.tree_size}`);
|
|
131
|
+
}
|
|
132
|
+
if (!sthPubkeys.some((pubkey) => verifySthSignature(head, pubkey))) {
|
|
133
|
+
throw new Error('signature does not verify against any accepted key');
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
catch (error) {
|
|
137
|
+
headProblems.push(`tree head ${i}: ${error.message}`);
|
|
138
|
+
}
|
|
139
|
+
});
|
|
140
|
+
if (leafLines.length > 0 && (heads.length === 0 || heads[heads.length - 1].tree_size !== leafLines.length)) {
|
|
141
|
+
headProblems.push('the latest tree head does not cover every entry (SPEC.md §5)');
|
|
142
|
+
}
|
|
143
|
+
checks.push({
|
|
144
|
+
name: 'tree-heads',
|
|
145
|
+
ok: headProblems.length === 0,
|
|
146
|
+
detail: headProblems.length === 0
|
|
147
|
+
? `${heads.length} tree heads recomputed and signature-verified`
|
|
148
|
+
: headProblems.join('; '),
|
|
149
|
+
});
|
|
150
|
+
// 5. Consistency between every consecutive pair of tree heads (§6.2).
|
|
151
|
+
const consistencyProblems = [];
|
|
152
|
+
let pairs = 0;
|
|
153
|
+
for (let i = 1; i < heads.length; i++) {
|
|
154
|
+
const prev = heads[i - 1];
|
|
155
|
+
const next = heads[i];
|
|
156
|
+
if (next.tree_size > leafHashes.length || prev.tree_size > next.tree_size)
|
|
157
|
+
continue; // reported above
|
|
158
|
+
const ok = verifyConsistency({
|
|
159
|
+
oldSize: prev.tree_size,
|
|
160
|
+
newSize: next.tree_size,
|
|
161
|
+
oldRoot: hexToBytes(prev.root_hash),
|
|
162
|
+
newRoot: hexToBytes(next.root_hash),
|
|
163
|
+
path: consistencyPath(prev.tree_size, leafHashes.slice(0, next.tree_size)),
|
|
164
|
+
});
|
|
165
|
+
pairs++;
|
|
166
|
+
if (!ok) {
|
|
167
|
+
consistencyProblems.push(`tree heads ${i - 1} -> ${i} (sizes ${prev.tree_size} -> ${next.tree_size}) are not consistent: history was rewritten`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
checks.push({
|
|
171
|
+
name: 'consistency',
|
|
172
|
+
ok: consistencyProblems.length === 0,
|
|
173
|
+
detail: consistencyProblems.length === 0
|
|
174
|
+
? `${pairs} consecutive pair(s) prove append-only history`
|
|
175
|
+
: consistencyProblems.join('; '),
|
|
176
|
+
});
|
|
177
|
+
return {
|
|
178
|
+
ok: checks.every((c) => c.ok),
|
|
179
|
+
source: data.source,
|
|
180
|
+
agents,
|
|
181
|
+
treeSize: leafLines.length,
|
|
182
|
+
checks,
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function safeOathHash(doc) {
|
|
186
|
+
try {
|
|
187
|
+
return oathHash(doc);
|
|
188
|
+
}
|
|
189
|
+
catch {
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/** Verify a local ledger directory (SPEC.md Appendix A). */
|
|
194
|
+
export function verifyLocalLedger(dir) {
|
|
195
|
+
let data;
|
|
196
|
+
try {
|
|
197
|
+
const oathVersions = readOathVersions(dir);
|
|
198
|
+
const agent = oathVersions[0].agent;
|
|
199
|
+
data = {
|
|
200
|
+
source: dir,
|
|
201
|
+
leafLines: readLeafLines(dir),
|
|
202
|
+
heads: JSON.parse(readFileSync(join(dir, LEDGER_FILES.heads), 'utf8')),
|
|
203
|
+
// A local ledger's agent key doubles as its ledger key (§5).
|
|
204
|
+
sthPubkeys: oathVersions.map((doc) => doc.pubkey),
|
|
205
|
+
oathsByAgent: new Map([[agent, oathVersions]]),
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
catch (error) {
|
|
209
|
+
return {
|
|
210
|
+
ok: false,
|
|
211
|
+
source: dir,
|
|
212
|
+
agents: [],
|
|
213
|
+
treeSize: 0,
|
|
214
|
+
checks: [{ name: 'files', ok: false, detail: `unreadable ledger: ${error.message}` }],
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
return verifyLedgerData(data);
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Verify a remote public ledger over its read API (SPEC.md §11.2), starting
|
|
221
|
+
* from its discovery document. Entries beyond the latest tree head are
|
|
222
|
+
* within the merge delay and are not judged yet.
|
|
223
|
+
*/
|
|
224
|
+
export async function verifyRemoteLedger(url, options = {}) {
|
|
225
|
+
const f = options.fetch ?? fetch;
|
|
226
|
+
const base = url.replace(/\/+$/, '');
|
|
227
|
+
const get = async (path) => {
|
|
228
|
+
const response = await f(`${base}${path}`);
|
|
229
|
+
if (!response.ok)
|
|
230
|
+
throw new Error(`GET ${path} -> ${response.status}`);
|
|
231
|
+
return response.json();
|
|
232
|
+
};
|
|
233
|
+
try {
|
|
234
|
+
const discovery = (await get('/v0/ledger.json'));
|
|
235
|
+
const heads = (await get('/v0/sth/history'));
|
|
236
|
+
const covered = heads.length > 0 ? heads[heads.length - 1].tree_size : 0;
|
|
237
|
+
const leafLines = [];
|
|
238
|
+
for (let start = 0; start < covered; start += 1000) {
|
|
239
|
+
const page = (await get(`/v0/entries?start=${start}&end=${Math.min(start + 999, covered - 1)}`));
|
|
240
|
+
for (const row of page.entries) {
|
|
241
|
+
leafLines[row.leaf_index] = row.leaf_bytes;
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
const handles = new Set();
|
|
245
|
+
for (const line of leafLines) {
|
|
246
|
+
try {
|
|
247
|
+
const agent = JSON.parse(line).agent;
|
|
248
|
+
if (typeof agent === 'string')
|
|
249
|
+
handles.add(agent);
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
/* reported by verifyLedgerData */
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
const oathsByAgent = new Map();
|
|
256
|
+
for (const handle of handles) {
|
|
257
|
+
const record = (await get(`/v0/agents/${handle}`));
|
|
258
|
+
oathsByAgent.set(handle, record.oath_versions);
|
|
259
|
+
}
|
|
260
|
+
return verifyLedgerData({
|
|
261
|
+
source: `${discovery.ledger} (${base})`,
|
|
262
|
+
leafLines,
|
|
263
|
+
heads,
|
|
264
|
+
sthPubkeys: [discovery.pubkey],
|
|
265
|
+
oathsByAgent,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
catch (error) {
|
|
269
|
+
return {
|
|
270
|
+
ok: false,
|
|
271
|
+
source: base,
|
|
272
|
+
agents: [],
|
|
273
|
+
treeSize: 0,
|
|
274
|
+
checks: [{ name: 'files', ok: false, detail: `unreachable ledger: ${error.message}` }],
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
//# sourceMappingURL=verify.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"verify.js","sourceRoot":"","sources":["../src/verify.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@thehonestmachine/oath-sdk",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "The OATH developer surface: swear an oath, log entries, and maintain a verifiable local ledger on disk.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/index.js",
|
|
8
|
+
"types": "./dist/index.d.ts",
|
|
9
|
+
"exports": {
|
|
10
|
+
".": {
|
|
11
|
+
"types": "./dist/index.d.ts",
|
|
12
|
+
"default": "./dist/index.js"
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"src",
|
|
18
|
+
"LICENSE"
|
|
19
|
+
],
|
|
20
|
+
"homepage": "https://oath.sh",
|
|
21
|
+
"dependencies": {
|
|
22
|
+
"@thehonestmachine/oath-core": "0.1.0"
|
|
23
|
+
},
|
|
24
|
+
"devDependencies": {
|
|
25
|
+
"@types/node": "^22.15.0",
|
|
26
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
27
|
+
"typescript": "^5.8.3",
|
|
28
|
+
"vitest": "^3.2.4"
|
|
29
|
+
},
|
|
30
|
+
"repository": {
|
|
31
|
+
"type": "git",
|
|
32
|
+
"url": "git+https://github.com/aamir222686/oath.git",
|
|
33
|
+
"directory": "packages/sdk"
|
|
34
|
+
},
|
|
35
|
+
"publishConfig": {
|
|
36
|
+
"access": "public"
|
|
37
|
+
},
|
|
38
|
+
"engines": {
|
|
39
|
+
"node": ">=20"
|
|
40
|
+
},
|
|
41
|
+
"keywords": [
|
|
42
|
+
"oath",
|
|
43
|
+
"ai-agents",
|
|
44
|
+
"accountability",
|
|
45
|
+
"transparency",
|
|
46
|
+
"merkle",
|
|
47
|
+
"certificate-transparency"
|
|
48
|
+
],
|
|
49
|
+
"scripts": {
|
|
50
|
+
"build": "tsc -p tsconfig.json",
|
|
51
|
+
"typecheck": "tsc -p tsconfig.json --noEmit",
|
|
52
|
+
"test": "vitest run",
|
|
53
|
+
"coverage": "vitest run --coverage"
|
|
54
|
+
}
|
|
55
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export {
|
|
2
|
+
LocalLedger,
|
|
3
|
+
LEDGER_FILES,
|
|
4
|
+
readOathVersions,
|
|
5
|
+
readLeafLines,
|
|
6
|
+
type EntryFields,
|
|
7
|
+
type AppendResult,
|
|
8
|
+
} from './localLedger.js';
|
|
9
|
+
export { RemoteLedger, type LedgerDiscovery, type SubmitResult } from './remote.js';
|
|
10
|
+
export {
|
|
11
|
+
verifyLedgerData,
|
|
12
|
+
verifyLocalLedger,
|
|
13
|
+
verifyRemoteLedger,
|
|
14
|
+
type LedgerData,
|
|
15
|
+
type VerificationReport,
|
|
16
|
+
type CheckResult,
|
|
17
|
+
} from './verify.js';
|
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
import { appendFileSync, existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import {
|
|
4
|
+
assertValidAmendment,
|
|
5
|
+
assertValidOathDoc,
|
|
6
|
+
assertValidUnsignedOathDoc,
|
|
7
|
+
bytesToHex,
|
|
8
|
+
canonicalize,
|
|
9
|
+
edPublicKey,
|
|
10
|
+
leafBytesOf,
|
|
11
|
+
leafHash,
|
|
12
|
+
oathHash,
|
|
13
|
+
ProtocolError,
|
|
14
|
+
rootFromLeafHashes,
|
|
15
|
+
signEntry,
|
|
16
|
+
signOathDoc,
|
|
17
|
+
signSth,
|
|
18
|
+
SPEC_VERSION,
|
|
19
|
+
utf8Bytes,
|
|
20
|
+
type Action,
|
|
21
|
+
type Adjudication,
|
|
22
|
+
type Confession,
|
|
23
|
+
type Entry,
|
|
24
|
+
type OathDoc,
|
|
25
|
+
type SignedTreeHead,
|
|
26
|
+
type UnsignedEntry,
|
|
27
|
+
type UnsignedOathDoc,
|
|
28
|
+
type Verdict,
|
|
29
|
+
} from '@thehonestmachine/oath-core';
|
|
30
|
+
|
|
31
|
+
/** File layout per SPEC.md Appendix A. */
|
|
32
|
+
export const LEDGER_FILES = {
|
|
33
|
+
oath: 'oath.json',
|
|
34
|
+
history: 'oath.history',
|
|
35
|
+
entries: 'entries.jsonl',
|
|
36
|
+
heads: 'heads.json',
|
|
37
|
+
} as const;
|
|
38
|
+
|
|
39
|
+
export interface EntryFields {
|
|
40
|
+
action: Action;
|
|
41
|
+
verdict: Verdict;
|
|
42
|
+
law_cited: number | null;
|
|
43
|
+
adjudication: Adjudication;
|
|
44
|
+
confession?: Confession;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export interface AppendResult {
|
|
48
|
+
entry: Entry;
|
|
49
|
+
head: SignedTreeHead;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/** RFC 3339 UTC now — Date#toISOString emits exactly the spec's format. */
|
|
53
|
+
function defaultNow(): string {
|
|
54
|
+
return new Date().toISOString();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function historyPath(dir: string, version: number): string {
|
|
58
|
+
return join(dir, LEDGER_FILES.history, `v${version}.json`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* A local ledger (SPEC.md §4 storage note, Appendix A): the agent-operated,
|
|
63
|
+
* on-disk, offline-verifiable form of the OATH log. The agent's key doubles
|
|
64
|
+
* as the ledger key (§5).
|
|
65
|
+
*/
|
|
66
|
+
export class LocalLedger {
|
|
67
|
+
private constructor(
|
|
68
|
+
readonly dir: string,
|
|
69
|
+
private seed: string,
|
|
70
|
+
private readonly oathVersions: OathDoc[],
|
|
71
|
+
private readonly leafHashes: Uint8Array[],
|
|
72
|
+
private readonly heads: SignedTreeHead[],
|
|
73
|
+
private readonly ledgerName: string,
|
|
74
|
+
private readonly now: () => string,
|
|
75
|
+
) {}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Take the oath: sign version 1, create the ledger directory, and log the
|
|
79
|
+
* `oath.swear` entry as leaf 0 (SPEC.md §2.4).
|
|
80
|
+
*/
|
|
81
|
+
static swear(options: {
|
|
82
|
+
dir: string;
|
|
83
|
+
oath: UnsignedOathDoc;
|
|
84
|
+
seed: string;
|
|
85
|
+
ledgerName?: string;
|
|
86
|
+
now?: () => string;
|
|
87
|
+
}): LocalLedger {
|
|
88
|
+
const { dir, oath, seed } = options;
|
|
89
|
+
const now = options.now ?? defaultNow;
|
|
90
|
+
assertValidUnsignedOathDoc(oath);
|
|
91
|
+
if (oath.version !== 1) {
|
|
92
|
+
throw new ProtocolError('an agent swears at version 1; later versions are amendments (SPEC.md §9)');
|
|
93
|
+
}
|
|
94
|
+
if (oath.pubkey !== edPublicKey(seed)) {
|
|
95
|
+
throw new ProtocolError('oath pubkey does not match the signing seed');
|
|
96
|
+
}
|
|
97
|
+
if (existsSync(join(dir, LEDGER_FILES.oath))) {
|
|
98
|
+
throw new ProtocolError(`a ledger already exists at ${dir}`);
|
|
99
|
+
}
|
|
100
|
+
const doc = signOathDoc(oath, seed);
|
|
101
|
+
// Write oath.json LAST: it is the sentinel the swear/open guards key on
|
|
102
|
+
// (existsSync above, and open() reads it first). Everything else is
|
|
103
|
+
// written and the swear leaf appended before it, so a crash mid-write
|
|
104
|
+
// leaves a directory that a retried swear() cleanly overwrites rather
|
|
105
|
+
// than a half-built ledger that can be neither sworn nor opened.
|
|
106
|
+
mkdirSync(join(dir, LEDGER_FILES.history), { recursive: true });
|
|
107
|
+
writeFileSync(historyPath(dir, 1), canonicalize(doc) + '\n');
|
|
108
|
+
writeFileSync(join(dir, LEDGER_FILES.entries), '');
|
|
109
|
+
writeFileSync(join(dir, LEDGER_FILES.heads), '[]\n');
|
|
110
|
+
const ledger = new LocalLedger(
|
|
111
|
+
dir,
|
|
112
|
+
seed,
|
|
113
|
+
[doc],
|
|
114
|
+
[],
|
|
115
|
+
[],
|
|
116
|
+
options.ledgerName ?? `${doc.agent}.local`,
|
|
117
|
+
now,
|
|
118
|
+
);
|
|
119
|
+
const occurredAt = now();
|
|
120
|
+
ledger.append({
|
|
121
|
+
action: {
|
|
122
|
+
type: 'oath.swear',
|
|
123
|
+
summary: `Swore the oath of ${doc.agent}, version 1.`,
|
|
124
|
+
content_hash: oathHash(doc),
|
|
125
|
+
occurred_at: occurredAt,
|
|
126
|
+
},
|
|
127
|
+
verdict: 'compliant',
|
|
128
|
+
law_cited: null,
|
|
129
|
+
adjudication: { mode: 'machine', check: 'oath.swear' },
|
|
130
|
+
});
|
|
131
|
+
writeFileSync(join(dir, LEDGER_FILES.oath), canonicalize(doc) + '\n');
|
|
132
|
+
return ledger;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Open an existing ledger for appending. Verification is the monitor's job. */
|
|
136
|
+
static open(options: { dir: string; seed: string; now?: () => string }): LocalLedger {
|
|
137
|
+
const { dir, seed } = options;
|
|
138
|
+
const oathVersions = readOathVersions(dir);
|
|
139
|
+
const current = oathVersions[oathVersions.length - 1]!;
|
|
140
|
+
if (current.pubkey !== edPublicKey(seed)) {
|
|
141
|
+
throw new ProtocolError('seed does not match the current oath pubkey');
|
|
142
|
+
}
|
|
143
|
+
const leafHashes = readLeafLines(dir).map((line) => leafHash(utf8Bytes(line)));
|
|
144
|
+
const heads = JSON.parse(readFileSync(join(dir, LEDGER_FILES.heads), 'utf8')) as SignedTreeHead[];
|
|
145
|
+
const ledgerName = heads.length > 0 ? heads[heads.length - 1]!.ledger : `${current.agent}.local`;
|
|
146
|
+
return new LocalLedger(dir, seed, oathVersions, leafHashes, heads, ledgerName, options.now ?? defaultNow);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
get currentOath(): OathDoc {
|
|
150
|
+
return this.oathVersions[this.oathVersions.length - 1]!;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
get treeSize(): number {
|
|
154
|
+
return this.leafHashes.length;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
latestHead(): SignedTreeHead | null {
|
|
158
|
+
return this.heads.length > 0 ? this.heads[this.heads.length - 1]! : null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Sign and append an entry, then emit a fresh STH — the local ledger emits
|
|
163
|
+
* one per append so its contents are always presentable (SPEC.md §5).
|
|
164
|
+
*/
|
|
165
|
+
append(fields: EntryFields): AppendResult {
|
|
166
|
+
const current = this.currentOath;
|
|
167
|
+
const unsigned: UnsignedEntry = {
|
|
168
|
+
spec_version: SPEC_VERSION,
|
|
169
|
+
agent: current.agent,
|
|
170
|
+
oath_version: current.version,
|
|
171
|
+
...fields,
|
|
172
|
+
timestamp: this.now(),
|
|
173
|
+
};
|
|
174
|
+
if (fields.confession === undefined) delete (unsigned as Partial<UnsignedEntry>).confession;
|
|
175
|
+
const entry = signEntry(unsigned, this.seed);
|
|
176
|
+
const line = canonicalize(entry);
|
|
177
|
+
appendFileSync(join(this.dir, LEDGER_FILES.entries), line + '\n');
|
|
178
|
+
this.leafHashes.push(leafHash(utf8Bytes(line)));
|
|
179
|
+
const head = signSth(
|
|
180
|
+
{
|
|
181
|
+
ledger: this.ledgerName,
|
|
182
|
+
tree_size: this.leafHashes.length,
|
|
183
|
+
root_hash: bytesToHex(rootFromLeafHashes(this.leafHashes)),
|
|
184
|
+
timestamp: this.now(),
|
|
185
|
+
},
|
|
186
|
+
this.seed,
|
|
187
|
+
);
|
|
188
|
+
this.heads.push(head);
|
|
189
|
+
writeFileSync(join(this.dir, LEDGER_FILES.heads), JSON.stringify(this.heads, null, 2) + '\n');
|
|
190
|
+
return { entry, head };
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Amend the oath (SPEC.md §9): sign the next version with the current key,
|
|
195
|
+
* write it, and log the `oath.amend` entry. For key rotation pass the new
|
|
196
|
+
* key's seed; future entries and tree heads are signed with it.
|
|
197
|
+
*/
|
|
198
|
+
amend(next: UnsignedOathDoc, options: { summary: string; newSeed?: string }): AppendResult {
|
|
199
|
+
const signed = signOathDoc(next, this.seed);
|
|
200
|
+
assertValidAmendment(this.currentOath, signed);
|
|
201
|
+
const activeSeed = options.newSeed ?? this.seed;
|
|
202
|
+
if (signed.pubkey !== edPublicKey(activeSeed)) {
|
|
203
|
+
throw new ProtocolError('amended oath pubkey does not match the active seed (pass newSeed when rotating keys)');
|
|
204
|
+
}
|
|
205
|
+
// Write order matters for crash recovery. oath.json is the sentinel that
|
|
206
|
+
// readOathVersions keys its version loop on, so it goes LAST. First log
|
|
207
|
+
// the amend entry (still adjudicated under the PRIOR version — the last
|
|
208
|
+
// act of the old oath, §9), then the new history file, then oath.json.
|
|
209
|
+
// A crash before oath.json leaves an OPENABLE ledger at the prior version
|
|
210
|
+
// (with an orphan amend leaf a monitor will flag), never an un-openable
|
|
211
|
+
// one pointing at a version whose history file does not yet exist.
|
|
212
|
+
const result = this.append({
|
|
213
|
+
action: {
|
|
214
|
+
type: 'oath.amend',
|
|
215
|
+
summary: options.summary,
|
|
216
|
+
content_hash: oathHash(signed),
|
|
217
|
+
occurred_at: this.now(),
|
|
218
|
+
},
|
|
219
|
+
verdict: 'compliant',
|
|
220
|
+
law_cited: null,
|
|
221
|
+
adjudication: { mode: 'machine', check: 'oath.amend' },
|
|
222
|
+
});
|
|
223
|
+
writeFileSync(historyPath(this.dir, signed.version), canonicalize(signed) + '\n');
|
|
224
|
+
writeFileSync(join(this.dir, LEDGER_FILES.oath), canonicalize(signed) + '\n');
|
|
225
|
+
this.oathVersions.push(signed);
|
|
226
|
+
this.seed = activeSeed;
|
|
227
|
+
return result;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/** Read and structurally validate the oath version chain from disk. */
|
|
232
|
+
export function readOathVersions(dir: string): OathDoc[] {
|
|
233
|
+
const currentRaw = readFileSync(join(dir, LEDGER_FILES.oath), 'utf8');
|
|
234
|
+
const current = JSON.parse(currentRaw) as unknown;
|
|
235
|
+
assertValidOathDoc(current);
|
|
236
|
+
const versions: OathDoc[] = [];
|
|
237
|
+
for (let v = 1; v <= current.version; v++) {
|
|
238
|
+
const doc = JSON.parse(readFileSync(historyPath(dir, v), 'utf8')) as unknown;
|
|
239
|
+
assertValidOathDoc(doc);
|
|
240
|
+
if (doc.version !== v) {
|
|
241
|
+
throw new ProtocolError(`${LEDGER_FILES.history}/v${v}.json contains version ${doc.version}`);
|
|
242
|
+
}
|
|
243
|
+
versions.push(doc);
|
|
244
|
+
}
|
|
245
|
+
if (canonicalize(versions[versions.length - 1]!) !== canonicalize(current)) {
|
|
246
|
+
throw new ProtocolError('oath.json does not match the latest history version');
|
|
247
|
+
}
|
|
248
|
+
return versions;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
/** Read raw entry lines (the exact leaf bytes, newline-separated). */
|
|
252
|
+
export function readLeafLines(dir: string): string[] {
|
|
253
|
+
const raw = readFileSync(join(dir, LEDGER_FILES.entries), 'utf8');
|
|
254
|
+
return raw === '' ? [] : raw.replace(/\n$/, '').split('\n');
|
|
255
|
+
}
|