@primust/verifier 1.0.0 → 1.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.
Files changed (49) hide show
  1. package/dist/chunk-LTWQK3HT.js +432 -0
  2. package/dist/chunk-NOADQWB6.js +3012 -0
  3. package/dist/cli.d.ts +3 -2
  4. package/dist/cli.js +309 -361
  5. package/dist/index.d.ts +335 -13
  6. package/dist/index.js +1181 -13
  7. package/dist/tsa-chain-7KSQ5LAH.js +235 -0
  8. package/dist/v29-envelope-GFVVA2S6.js +42 -0
  9. package/package.json +7 -8
  10. package/dist/bounded-trace.d.ts +0 -46
  11. package/dist/bounded-trace.d.ts.map +0 -1
  12. package/dist/bounded-trace.js +0 -558
  13. package/dist/bounded-trace.js.map +0 -1
  14. package/dist/cli.d.ts.map +0 -1
  15. package/dist/cli.js.map +0 -1
  16. package/dist/index.d.ts.map +0 -1
  17. package/dist/index.js.map +0 -1
  18. package/dist/key-cache.d.ts +0 -20
  19. package/dist/key-cache.d.ts.map +0 -1
  20. package/dist/key-cache.js +0 -68
  21. package/dist/key-cache.js.map +0 -1
  22. package/dist/scoped.d.ts +0 -35
  23. package/dist/scoped.d.ts.map +0 -1
  24. package/dist/scoped.js +0 -582
  25. package/dist/scoped.js.map +0 -1
  26. package/dist/types.d.ts +0 -60
  27. package/dist/types.d.ts.map +0 -1
  28. package/dist/types.js +0 -5
  29. package/dist/types.js.map +0 -1
  30. package/dist/upstream_resolver.d.ts +0 -60
  31. package/dist/upstream_resolver.d.ts.map +0 -1
  32. package/dist/upstream_resolver.js +0 -126
  33. package/dist/upstream_resolver.js.map +0 -1
  34. package/dist/v29-envelope.d.ts +0 -55
  35. package/dist/v29-envelope.d.ts.map +0 -1
  36. package/dist/v29-envelope.js +0 -450
  37. package/dist/v29-envelope.js.map +0 -1
  38. package/dist/verifier.d.ts +0 -36
  39. package/dist/verifier.d.ts.map +0 -1
  40. package/dist/verifier.js +0 -1235
  41. package/dist/verifier.js.map +0 -1
  42. package/dist/verifier.test.d.ts +0 -2
  43. package/dist/verifier.test.d.ts.map +0 -1
  44. package/dist/verifier.test.js +0 -395
  45. package/dist/verifier.test.js.map +0 -1
  46. package/dist/verify-html-template.d.ts +0 -45
  47. package/dist/verify-html-template.d.ts.map +0 -1
  48. package/dist/verify-html-template.js +0 -182
  49. package/dist/verify-html-template.js.map +0 -1
@@ -0,0 +1,432 @@
1
+ // src/v29-envelope.ts
2
+ import { createHash, createPublicKey, verify as cryptoVerify } from "crypto";
3
+ var ENVELOPE_VERSION = "v0.1";
4
+ var ALLOWED_KINDS = /* @__PURE__ */ new Set([
5
+ "governance_check",
6
+ "outbound_action",
7
+ "token_authority_snapshot",
8
+ "workspace_observation",
9
+ "contention_event",
10
+ "scope_claim_lifecycle",
11
+ "scope_claim_emitted",
12
+ "turn_context",
13
+ "tool_execution"
14
+ ]);
15
+ var ALLOWED_TRUST_EDGES = /* @__PURE__ */ new Set(["A2T", "A2M", "A2H", "A2A", "A2S", "A2D"]);
16
+ var PROOF_TIER_HIERARCHY = [
17
+ "attestation",
18
+ "witnessed",
19
+ "execution",
20
+ "operator_bound",
21
+ "verifiable_inference",
22
+ "mathematical"
23
+ ];
24
+ var TIER_INDEX = Object.fromEntries(
25
+ PROOF_TIER_HIERARCHY.map((t, i) => [t, i])
26
+ );
27
+ var v29Pass = () => ({ ok: true, reasonCode: null });
28
+ var v29Fail = (code, detail = "") => ({
29
+ ok: false,
30
+ reasonCode: code,
31
+ detail
32
+ });
33
+ function sortValue(v) {
34
+ if (v === null || typeof v !== "object") return v;
35
+ if (Array.isArray(v)) return v.map(sortValue);
36
+ const out = {};
37
+ for (const k of Object.keys(v).sort()) {
38
+ out[k] = sortValue(v[k]);
39
+ }
40
+ return out;
41
+ }
42
+ function canonicalJson(value) {
43
+ return JSON.stringify(sortValue(value));
44
+ }
45
+ function canonicalHash(value) {
46
+ return "sha256:" + createHash("sha256").update(canonicalJson(value)).digest("hex");
47
+ }
48
+ function validateEnvelopeShape(envelope) {
49
+ if (typeof envelope !== "object" || envelope === null) {
50
+ return v29Fail("envelope_not_object");
51
+ }
52
+ const e = envelope;
53
+ if (e.envelope_version !== ENVELOPE_VERSION) {
54
+ return v29Fail(
55
+ "envelope_version_mismatch",
56
+ `expected ${ENVELOPE_VERSION}, got ${JSON.stringify(e.envelope_version)}`
57
+ );
58
+ }
59
+ for (const k of ["run_header", "records", "aggregations"]) {
60
+ if (!(k in e)) return v29Fail("envelope_missing_key", k);
61
+ }
62
+ const header = e.run_header;
63
+ if (typeof header !== "object" || header === null) {
64
+ return v29Fail("run_header_not_object");
65
+ }
66
+ for (const k of ["run_id", "org_id", "opened_at", "target_environment"]) {
67
+ if (!(k in header)) return v29Fail("run_header_missing_key", k);
68
+ }
69
+ const records = e.records;
70
+ if (!Array.isArray(records)) return v29Fail("records_not_array");
71
+ for (let i = 0; i < records.length; i++) {
72
+ const r = records[i];
73
+ if (typeof r !== "object" || r === null) {
74
+ return v29Fail("record_not_object", `records[${i}]`);
75
+ }
76
+ if (!ALLOWED_KINDS.has(String(r.kind))) {
77
+ return v29Fail("record_kind_unknown", `records[${i}].kind=${JSON.stringify(r.kind)}`);
78
+ }
79
+ if (r.trust_edge != null && !ALLOWED_TRUST_EDGES.has(String(r.trust_edge))) {
80
+ return v29Fail(
81
+ "record_trust_edge_unknown",
82
+ `records[${i}].trust_edge=${JSON.stringify(r.trust_edge)}`
83
+ );
84
+ }
85
+ }
86
+ return v29Pass();
87
+ }
88
+ function reproduceAggregations(records) {
89
+ const proofMix = {};
90
+ for (const t of PROOF_TIER_HIERARCHY) proofMix[t] = 0;
91
+ const trustEdges = {};
92
+ let floorIdx = null;
93
+ let proven = 0;
94
+ let totalWithTier = 0;
95
+ for (const r of records) {
96
+ const tier = r.tier_computed;
97
+ if (typeof tier === "string" && tier in proofMix) {
98
+ proofMix[tier] += 1;
99
+ totalWithTier += 1;
100
+ const idx = TIER_INDEX[tier];
101
+ if (floorIdx === null || idx < floorIdx) floorIdx = idx;
102
+ if (idx > 0) proven += 1;
103
+ }
104
+ const edge = r.trust_edge;
105
+ if (typeof edge === "string" && ALLOWED_TRUST_EDGES.has(edge)) {
106
+ const slot = trustEdges[edge] ?? { count: 0, tier_floor: null };
107
+ slot.count += 1;
108
+ if (typeof tier === "string" && tier in TIER_INDEX) {
109
+ if (slot.tier_floor === null || TIER_INDEX[tier] < TIER_INDEX[slot.tier_floor]) {
110
+ slot.tier_floor = tier;
111
+ }
112
+ }
113
+ trustEdges[edge] = slot;
114
+ }
115
+ }
116
+ const provableSurface = totalWithTier > 0 ? Math.round(proven / totalWithTier * 1e4) / 1e4 : 0;
117
+ return {
118
+ trust_edges_observed: trustEdges,
119
+ proof_mix: proofMix,
120
+ proof_level_floor: floorIdx === null ? null : PROOF_TIER_HIERARCHY[floorIdx],
121
+ provable_surface: provableSurface,
122
+ provable_surface_breakdown: {
123
+ records_with_tier: totalWithTier,
124
+ records_proven_above_attestation: proven
125
+ }
126
+ };
127
+ }
128
+ function validateAggregations(envelope) {
129
+ const stated = envelope.aggregations ?? {};
130
+ const expected = reproduceAggregations(
131
+ envelope.records ?? []
132
+ );
133
+ if (canonicalJson(stated) !== canonicalJson(expected)) {
134
+ return v29Fail(
135
+ "aggregation_reproduction_mismatch",
136
+ "stated aggregations differ from per-record reproduction"
137
+ );
138
+ }
139
+ return v29Pass();
140
+ }
141
+ function decodeJsonb(v) {
142
+ if (v == null) return null;
143
+ if (typeof v === "string") {
144
+ try {
145
+ return JSON.parse(v);
146
+ } catch {
147
+ return null;
148
+ }
149
+ }
150
+ return v;
151
+ }
152
+ function reproduceTierCeiling(harnessManifest, surface, tierClaimed) {
153
+ const ceilings = decodeJsonb(harnessManifest.surface_tier_ceilings) ?? {};
154
+ const ceiling = ceilings[surface];
155
+ if (ceiling == null) {
156
+ return { effective_tier: tierClaimed, tier_ceiling_applied: null };
157
+ }
158
+ const claimIdx = TIER_INDEX[tierClaimed] ?? -1;
159
+ const ceilIdx = TIER_INDEX[ceiling] ?? -1;
160
+ if (claimIdx <= ceilIdx || claimIdx === -1) {
161
+ return { effective_tier: tierClaimed, tier_ceiling_applied: null };
162
+ }
163
+ return { effective_tier: ceiling, tier_ceiling_applied: ceiling };
164
+ }
165
+ function validateRecordsAgainstHarness(records, harnessManifest) {
166
+ if (!harnessManifest) return v29Pass();
167
+ for (let i = 0; i < records.length; i++) {
168
+ const r = records[i];
169
+ const tierClaimed = r.tier_claimed;
170
+ const surface = r.surface;
171
+ let stored = r.tier_ceiling_applied;
172
+ if (typeof stored === "string" && stored.length === 0) stored = null;
173
+ if (typeof tierClaimed !== "string" || typeof surface !== "string") continue;
174
+ const reproduced = reproduceTierCeiling(harnessManifest, surface, tierClaimed);
175
+ if (reproduced.tier_ceiling_applied !== (stored ?? null)) {
176
+ return v29Fail(
177
+ "harness_tier_ceiling_reproduction_mismatch",
178
+ `records[${i}] surface=${surface} tier_claimed=${tierClaimed}`
179
+ );
180
+ }
181
+ }
182
+ return v29Pass();
183
+ }
184
+ function validateRuntimeBindingHash(binding) {
185
+ const stored = binding.binding_body_canonical_hash;
186
+ let body = binding.binding_body_canonical_json;
187
+ if (!body || typeof stored !== "string") {
188
+ return v29Fail("runtime_binding_missing_body_or_hash");
189
+ }
190
+ if (typeof body === "string") {
191
+ try {
192
+ body = JSON.parse(body);
193
+ } catch {
194
+ return v29Fail("runtime_binding_body_not_json");
195
+ }
196
+ }
197
+ const reproduced = canonicalHash(body);
198
+ if (reproduced !== stored) {
199
+ return v29Fail(
200
+ "runtime_binding_hash_reproduction_mismatch",
201
+ `stored=${stored.slice(0, 24)}\u2026 reproduced=${reproduced.slice(0, 24)}\u2026`
202
+ );
203
+ }
204
+ return v29Pass();
205
+ }
206
+ function reproduceManifestCanonicalHash(manifest, manifestKind) {
207
+ let body;
208
+ if (manifestKind === "agent") {
209
+ body = {
210
+ agent_manifest_id: manifest.agent_manifest_id,
211
+ manifest_version: manifest.manifest_version,
212
+ org_id: manifest.org_id,
213
+ issuer_id: manifest.issuer_id,
214
+ agent_name: manifest.agent_name,
215
+ agent_kind: manifest.agent_kind,
216
+ agent_vendor: manifest.agent_vendor,
217
+ capability_grants: decodeJsonb(manifest.capability_grants),
218
+ scope_expressions: decodeJsonb(manifest.scope_expressions),
219
+ reversibility_overrides: decodeJsonb(manifest.reversibility_overrides) ?? {},
220
+ model_profile_id: manifest.model_profile_id ?? null,
221
+ agent_behavior_profile_id: manifest.agent_behavior_profile_id ?? null,
222
+ applicable_action_profiles: decodeJsonb(manifest.applicable_action_profiles) ?? [],
223
+ issued_at: manifest.issued_at,
224
+ expires_at: manifest.expires_at
225
+ };
226
+ } else {
227
+ body = {
228
+ harness_manifest_id: manifest.harness_manifest_id,
229
+ manifest_version: manifest.manifest_version,
230
+ org_id: manifest.org_id,
231
+ issuer_id: manifest.issuer_id,
232
+ harness_name: manifest.harness_name,
233
+ harness_bundle_version: manifest.harness_bundle_version,
234
+ config_hash: manifest.config_hash,
235
+ native_config_hashes: decodeJsonb(manifest.native_config_hashes) ?? {},
236
+ surface_tier_ceilings: decodeJsonb(manifest.surface_tier_ceilings),
237
+ declared_blind_spots: decodeJsonb(manifest.declared_blind_spots) ?? [],
238
+ detector_posture: decodeJsonb(manifest.detector_posture) ?? {},
239
+ active_check_classes: decodeJsonb(manifest.active_check_classes) ?? [],
240
+ issued_at: manifest.issued_at,
241
+ expires_at: manifest.expires_at
242
+ };
243
+ }
244
+ return canonicalHash(body);
245
+ }
246
+ function validateManifestCanonicalHash(manifest, manifestKind) {
247
+ const stated = manifest.canonical_json_hash;
248
+ if (typeof stated !== "string") return v29Fail("manifest_missing_canonical_hash");
249
+ const reproduced = reproduceManifestCanonicalHash(manifest, manifestKind);
250
+ if (reproduced !== stated) {
251
+ return v29Fail(
252
+ "manifest_canonical_hash_reproduction_mismatch",
253
+ `${manifestKind}: stored=${stated.slice(0, 24)}\u2026 reproduced=${reproduced.slice(0, 24)}\u2026`
254
+ );
255
+ }
256
+ return v29Pass();
257
+ }
258
+ function canonicalBodyBytes(body) {
259
+ return Buffer.from(canonicalJson(body), "utf-8");
260
+ }
261
+ function decodeBase64Tolerant(s) {
262
+ const normalized = s.replace(/-/g, "+").replace(/_/g, "/");
263
+ const padded = normalized + "=".repeat((4 - normalized.length % 4) % 4);
264
+ return Buffer.from(padded, "base64");
265
+ }
266
+ function verifyEd25519(bodyCanonicalBytes, signatureB64, publicKeyBytes) {
267
+ if (!bodyCanonicalBytes.length || !signatureB64 || publicKeyBytes.length !== 32) {
268
+ return false;
269
+ }
270
+ try {
271
+ const sigBytes = decodeBase64Tolerant(signatureB64);
272
+ const der = Buffer.concat([
273
+ Buffer.from([48, 42, 48, 5, 6, 3, 43, 101, 112, 3, 33, 0]),
274
+ Buffer.from(publicKeyBytes)
275
+ ]);
276
+ const keyObj = createPublicKey({ key: der, format: "der", type: "spki" });
277
+ return cryptoVerify(null, bodyCanonicalBytes, keyObj, sigBytes);
278
+ } catch {
279
+ return false;
280
+ }
281
+ }
282
+ function manifestCanonicalBody(manifest, kind) {
283
+ if (kind === "agent") {
284
+ return {
285
+ agent_manifest_id: manifest.agent_manifest_id ?? null,
286
+ manifest_version: manifest.manifest_version ?? null,
287
+ org_id: manifest.org_id ?? null,
288
+ issuer_id: manifest.issuer_id ?? null,
289
+ agent_name: manifest.agent_name ?? null,
290
+ agent_kind: manifest.agent_kind ?? null,
291
+ agent_vendor: manifest.agent_vendor ?? null,
292
+ capability_grants: decodeJsonb(manifest.capability_grants) ?? null,
293
+ scope_expressions: decodeJsonb(manifest.scope_expressions) ?? null,
294
+ reversibility_overrides: decodeJsonb(manifest.reversibility_overrides) ?? {},
295
+ model_profile_id: manifest.model_profile_id ?? null,
296
+ agent_behavior_profile_id: manifest.agent_behavior_profile_id ?? null,
297
+ applicable_action_profiles: decodeJsonb(manifest.applicable_action_profiles) ?? [],
298
+ issued_at: manifest.issued_at ?? null,
299
+ expires_at: manifest.expires_at ?? null
300
+ };
301
+ }
302
+ return {
303
+ harness_manifest_id: manifest.harness_manifest_id ?? null,
304
+ manifest_version: manifest.manifest_version ?? null,
305
+ org_id: manifest.org_id ?? null,
306
+ issuer_id: manifest.issuer_id ?? null,
307
+ harness_name: manifest.harness_name ?? null,
308
+ harness_bundle_version: manifest.harness_bundle_version ?? null,
309
+ config_hash: manifest.config_hash ?? null,
310
+ native_config_hashes: decodeJsonb(manifest.native_config_hashes) ?? {},
311
+ surface_tier_ceilings: decodeJsonb(manifest.surface_tier_ceilings) ?? {},
312
+ declared_blind_spots: decodeJsonb(manifest.declared_blind_spots) ?? [],
313
+ detector_posture: decodeJsonb(manifest.detector_posture) ?? {},
314
+ active_check_classes: decodeJsonb(manifest.active_check_classes) ?? [],
315
+ issued_at: manifest.issued_at ?? null,
316
+ expires_at: manifest.expires_at ?? null
317
+ };
318
+ }
319
+ function validateRuntimeBindingSignature(binding, pubkeyResolver) {
320
+ if (!pubkeyResolver) return v29Pass();
321
+ const sig = binding.signature;
322
+ const kid = binding.kms_kid;
323
+ let body = binding.binding_body_canonical_json;
324
+ if (!body || typeof sig !== "string" || typeof kid !== "string") {
325
+ return v29Fail(
326
+ "runtime_binding_signature_missing",
327
+ `body=${!!body} sig=${!!sig} kid=${!!kid}`
328
+ );
329
+ }
330
+ if (typeof body === "string") {
331
+ try {
332
+ body = JSON.parse(body);
333
+ } catch {
334
+ return v29Fail("runtime_binding_body_not_json");
335
+ }
336
+ }
337
+ let pubkey;
338
+ try {
339
+ pubkey = pubkeyResolver(kid);
340
+ } catch (e) {
341
+ return v29Fail("runtime_binding_pubkey_unresolvable", `kid=${kid}: ${e}`);
342
+ }
343
+ if (!verifyEd25519(canonicalBodyBytes(body), sig, pubkey)) {
344
+ return v29Fail("runtime_binding_signature_invalid");
345
+ }
346
+ return v29Pass();
347
+ }
348
+ function validateManifestSignature(manifest, manifestKind, pubkeyResolver) {
349
+ if (!pubkeyResolver) return v29Pass();
350
+ const sig = manifest.signature;
351
+ const kid = manifest.kms_kid;
352
+ if (typeof sig !== "string" || typeof kid !== "string") {
353
+ return v29Fail(
354
+ `${manifestKind}_manifest_signature_missing`,
355
+ `sig=${!!sig} kid=${!!kid}`
356
+ );
357
+ }
358
+ let pubkey;
359
+ try {
360
+ pubkey = pubkeyResolver(kid);
361
+ } catch (e) {
362
+ return v29Fail(`${manifestKind}_manifest_pubkey_unresolvable`, `kid=${kid}: ${e}`);
363
+ }
364
+ const body = manifestCanonicalBody(manifest, manifestKind);
365
+ if (!verifyEd25519(canonicalBodyBytes(body), sig, pubkey)) {
366
+ return v29Fail(`${manifestKind}_manifest_signature_invalid`);
367
+ }
368
+ return v29Pass();
369
+ }
370
+ function verifyV29(opts) {
371
+ if (opts.requireSignatures && !opts.pubkeyResolver) {
372
+ return v29Fail(
373
+ "signature_resolver_required",
374
+ "requireSignatures=true but no pubkeyResolver was provided"
375
+ );
376
+ }
377
+ let res = validateEnvelopeShape(opts.envelope);
378
+ if (!res.ok) return res;
379
+ res = validateAggregations(opts.envelope);
380
+ if (!res.ok) return res;
381
+ const rh = opts.envelope.run_header ?? {};
382
+ const bound = rh.bound_manifests ?? {};
383
+ const harnessBlock = opts.harnessManifest ?? bound.harness;
384
+ const agentBlock = opts.agentManifest ?? bound.agent;
385
+ const resolver = opts.pubkeyResolver ?? null;
386
+ res = validateRecordsAgainstHarness(
387
+ opts.envelope.records ?? [],
388
+ harnessBlock ?? null
389
+ );
390
+ if (!res.ok) return res;
391
+ if (opts.runtimeBinding) {
392
+ res = validateRuntimeBindingHash(opts.runtimeBinding);
393
+ if (!res.ok) return res;
394
+ res = validateRuntimeBindingSignature(opts.runtimeBinding, resolver);
395
+ if (!res.ok) return res;
396
+ }
397
+ if (agentBlock) {
398
+ res = validateManifestCanonicalHash(agentBlock, "agent");
399
+ if (!res.ok) return res;
400
+ res = validateManifestSignature(agentBlock, "agent", resolver);
401
+ if (!res.ok) return res;
402
+ }
403
+ if (harnessBlock) {
404
+ res = validateManifestCanonicalHash(harnessBlock, "harness");
405
+ if (!res.ok) return res;
406
+ res = validateManifestSignature(harnessBlock, "harness", resolver);
407
+ if (!res.ok) return res;
408
+ }
409
+ return v29Pass();
410
+ }
411
+
412
+ export {
413
+ ENVELOPE_VERSION,
414
+ ALLOWED_KINDS,
415
+ ALLOWED_TRUST_EDGES,
416
+ PROOF_TIER_HIERARCHY,
417
+ v29Pass,
418
+ v29Fail,
419
+ canonicalJson,
420
+ canonicalHash,
421
+ validateEnvelopeShape,
422
+ reproduceAggregations,
423
+ validateAggregations,
424
+ reproduceTierCeiling,
425
+ validateRecordsAgainstHarness,
426
+ validateRuntimeBindingHash,
427
+ reproduceManifestCanonicalHash,
428
+ validateManifestCanonicalHash,
429
+ validateRuntimeBindingSignature,
430
+ validateManifestSignature,
431
+ verifyV29
432
+ };