@agentplat/mesh 0.3.0-alpha.3 → 0.3.0-alpha.5

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/dist/trust.js ADDED
@@ -0,0 +1,934 @@
1
+ import { verifyMeshEnvelope } from "@agentplat/mesh-crypto";
2
+ import { canonicalizeMeshJson, validateMeshEnvelopeContext, } from "@agentplat/mesh-protocol";
3
+ import { normalizeMeshEvidenceAttestationV1, normalizeMeshEvidenceChallengeV1, normalizeMeshEvidenceClaimV1, normalizeMeshEvidenceRetractionV1, normalizeMeshTrustObservationV1, } from "@agentplat/trust/mesh-records";
4
+ import { assertExactKeys, assertIdentifier, assertSafeInteger, assertTrustDigest, createTrustEligibilityRequestV1, deepFreeze, digestScopeV1, digestSubjectV1, digestTrustProfileKeyV1, digestTrustJsonV1, evaluateTrustEligibilityV1, reduceEvidenceTrustStateV1, restoreEvidenceTrustSnapshotV1, sha256TrustBytesV1, validateEvidenceScopeV1, validateEvidenceTrustStateV1, } from "@agentplat/trust";
5
+ const trustDigestPattern = /^[0-9a-f]{64}$/;
6
+ const maximumOriginProofs = 4_096;
7
+ const maximumRemoteObservations = 4_096;
8
+ const maximumAuthorityDigests = 32;
9
+ const maximumEligibilityStateIdentities = 4_096;
10
+ /** Unforgeable, adapter-bound handoff from the verified processor. */
11
+ const concreteAdapterCapabilities = new WeakMap();
12
+ const authorizedAdapterInvocations = new WeakMap();
13
+ const verifiedEligibilityRuntimeStates = new WeakMap();
14
+ const latestEligibilitySnapshotByStateId = new Map();
15
+ /** Creates the Trust reducer adapter; no direct payload route exists. */
16
+ export function createMeshEvidenceTrustAdapterV1(bindingDigest, originVerifierBindingDigest, authorization) {
17
+ if (!trustDigestPattern.test(bindingDigest) ||
18
+ !trustDigestPattern.test(originVerifierBindingDigest) ||
19
+ !authorization ||
20
+ typeof authorization !== "object" ||
21
+ typeof authorization.prepare !== "function") {
22
+ throw new TypeError("Mesh Evidence Trust adapter bindings are invalid");
23
+ }
24
+ const prepare = authorization.prepare.bind(authorization);
25
+ const adapterCapability = Object.freeze({});
26
+ const adapter = {
27
+ bindingDigest,
28
+ prepare: (input) => {
29
+ const result = prepare({
30
+ authorizationState: input.state.authorizationState,
31
+ envelope: input.envelope,
32
+ receivedAt: input.receivedAt,
33
+ });
34
+ if (!result || typeof result !== "object" || result.accepted !== true)
35
+ return result;
36
+ return Object.freeze({
37
+ accepted: true,
38
+ nextAuthorizationState: result.nextAuthorizationState,
39
+ admissionStateDigest: result.admissionStateDigest,
40
+ coordinationAuthorityDigests: Object.freeze([
41
+ ...result.coordinationAuthorityDigests,
42
+ ]),
43
+ replayStateDigest: result.replayStateDigest,
44
+ observationCorrelated: result.observationCorrelated,
45
+ effectiveAtLogicalMs: result.effectiveAtLogicalMs,
46
+ });
47
+ },
48
+ process: (input) => {
49
+ if (!input ||
50
+ typeof input !== "object" ||
51
+ authorizedAdapterInvocations.get(input) !== adapterCapability) {
52
+ return {
53
+ accepted: false,
54
+ code: "trust_transition_rejected",
55
+ state: input?.state,
56
+ };
57
+ }
58
+ authorizedAdapterInvocations.delete(input);
59
+ let originEntry;
60
+ try {
61
+ originEntry = createOriginJournalEntry(input.origin, input.envelope);
62
+ }
63
+ catch {
64
+ return {
65
+ accepted: false,
66
+ code: "trust_transition_rejected",
67
+ state: input.state,
68
+ };
69
+ }
70
+ if (!Object.hasOwn(input.state.originProofs, input.origin.originProofDigest) &&
71
+ Object.keys(input.state.originProofs).length >= maximumOriginProofs) {
72
+ return {
73
+ accepted: false,
74
+ code: "trust_transition_rejected",
75
+ state: input.state,
76
+ };
77
+ }
78
+ const originProofs = {
79
+ ...input.state.originProofs,
80
+ [input.origin.originProofDigest]: originEntry,
81
+ };
82
+ if (input.observation !== undefined) {
83
+ if (!Object.hasOwn(input.state.remoteObservations, input.observation.observationId) &&
84
+ Object.keys(input.state.remoteObservations).length >=
85
+ maximumRemoteObservations) {
86
+ return {
87
+ accepted: false,
88
+ code: "trust_transition_rejected",
89
+ state: input.state,
90
+ };
91
+ }
92
+ const remoteObservations = {
93
+ ...input.state.remoteObservations,
94
+ [input.observation.observationId]: Object.freeze({
95
+ observation: input.observation,
96
+ correlated: input.observationCorrelated === true,
97
+ }),
98
+ };
99
+ return {
100
+ accepted: true,
101
+ duplicate: Object.hasOwn(input.state.remoteObservations, input.observation.observationId),
102
+ state: Object.freeze({
103
+ ...input.state,
104
+ authorizationState: input.preparation.nextAuthorizationState,
105
+ originProofs: Object.freeze(originProofs),
106
+ remoteObservations: Object.freeze(remoteObservations),
107
+ }),
108
+ };
109
+ }
110
+ try {
111
+ const record = input.record;
112
+ const recordId = evidenceRecordId(record);
113
+ const recordDigest = recordId.slice(recordId.indexOf(":") + 1);
114
+ const reduced = reduceEvidenceTrustStateV1(input.state.trust, {
115
+ schemaVersion: 1,
116
+ kind: "record_admitted",
117
+ record,
118
+ origin: "verified_mesh",
119
+ originBindingDigest: input.originBindingDigest,
120
+ originVerifierBindingDigest: input.originVerifierBindingDigest,
121
+ originProofDigest: input.origin.originProofDigest,
122
+ effectiveAtLogicalMs: input.preparation.effectiveAtLogicalMs,
123
+ logicalTimeMs: input.receivedAt,
124
+ }, {
125
+ verifiedMeshAdmissionVerifierRegistry: {
126
+ resolve: (binding) => binding !== originVerifierBindingDigest
127
+ ? null
128
+ : {
129
+ verifierBindingDigest: originVerifierBindingDigest,
130
+ upstreamBindingDigest: input.originBindingDigest,
131
+ verify: (candidate) => {
132
+ const proofEntry = originProofs[candidate.originProofDigest];
133
+ const proof = proofEntry?.descriptor;
134
+ return (proof !== undefined &&
135
+ validateOriginJournalEntry(proofEntry) &&
136
+ candidate.originBindingDigest ===
137
+ input.originBindingDigest &&
138
+ candidate.originVerifierBindingDigest ===
139
+ originVerifierBindingDigest &&
140
+ candidate.recordId === recordId &&
141
+ candidate.recordDigest === recordDigest &&
142
+ candidate.effectiveAtLogicalMs ===
143
+ input.preparation.effectiveAtLogicalMs &&
144
+ proof.normalizedRecordId === recordId &&
145
+ proof.normalizedRecordDigest === recordDigest);
146
+ },
147
+ },
148
+ },
149
+ });
150
+ const duplicate = reduced.effects.some((effect) => effect.reasonCode === "duplicate");
151
+ return {
152
+ accepted: true,
153
+ duplicate,
154
+ state: Object.freeze({
155
+ ...input.state,
156
+ authorizationState: input.preparation.nextAuthorizationState,
157
+ trust: reduced.state,
158
+ originProofs: Object.freeze(originProofs),
159
+ }),
160
+ };
161
+ }
162
+ catch {
163
+ return {
164
+ accepted: false,
165
+ code: "trust_transition_rejected",
166
+ state: input.state,
167
+ };
168
+ }
169
+ },
170
+ };
171
+ const frozenAdapter = Object.freeze(adapter);
172
+ concreteAdapterCapabilities.set(frozenAdapter, adapterCapability);
173
+ return frozenAdapter;
174
+ }
175
+ /** Creates the only Mesh route allowed to create a `verified_mesh` record. */
176
+ export function createMeshEvidenceInboundProcessorV1(options) {
177
+ if (!options ||
178
+ typeof options !== "object" ||
179
+ !options.adapter ||
180
+ !options.resolver ||
181
+ typeof options.resolver.resolve !== "function" ||
182
+ !options.cryptoPolicy ||
183
+ !Array.isArray(options.cryptoPolicy.allowedAlgorithms) ||
184
+ options.cryptoPolicy.allowedAlgorithms.length === 0 ||
185
+ typeof options.adapter.prepare !== "function" ||
186
+ typeof options.adapter.process !== "function" ||
187
+ !trustDigestPattern.test(options.adapter.bindingDigest) ||
188
+ !trustDigestPattern.test(options.originVerifierBindingDigest)) {
189
+ throw new TypeError("Mesh Evidence inbound dependencies are required");
190
+ }
191
+ const adapterCapability = concreteAdapterCapabilities.get(options.adapter);
192
+ const resolver = Object.freeze({
193
+ resolve: options.resolver.resolve.bind(options.resolver),
194
+ });
195
+ const policy = Object.freeze({
196
+ allowedAlgorithms: Object.freeze([
197
+ ...options.cryptoPolicy.allowedAlgorithms,
198
+ ]),
199
+ });
200
+ const configuration = Object.freeze({
201
+ resolver,
202
+ policy,
203
+ adapter: Object.freeze({
204
+ bindingDigest: options.adapter.bindingDigest,
205
+ prepare: options.adapter.prepare.bind(options.adapter),
206
+ process: options.adapter.process.bind(options.adapter),
207
+ }),
208
+ originVerifierBindingDigest: options.originVerifierBindingDigest,
209
+ crypto: options.crypto,
210
+ protocolOptions: options.protocolOptions,
211
+ supportedCriticalExtensions: options.supportedCriticalExtensions === undefined
212
+ ? undefined
213
+ : Object.freeze([...options.supportedCriticalExtensions]),
214
+ adapterCapability,
215
+ });
216
+ const processor = {
217
+ async process(state, request) {
218
+ if (!state ||
219
+ !request ||
220
+ typeof request !== "object" ||
221
+ !Number.isSafeInteger(request.receivedAt) ||
222
+ request.receivedAt < 0)
223
+ return reject(state, "invalid_request");
224
+ if (!isRuntimeState(state))
225
+ return reject(state, "invalid_request");
226
+ const contextual = validateMeshEnvelopeContext(request.envelope, {
227
+ tenantId: state.identity.tenantId,
228
+ meshId: state.identity.meshId,
229
+ peerId: state.identity.peerId,
230
+ receivedAt: request.verifiedAt,
231
+ ...(configuration.supportedCriticalExtensions === undefined
232
+ ? {}
233
+ : {
234
+ supportedCriticalExtensions: configuration.supportedCriticalExtensions,
235
+ }),
236
+ }, configuration.protocolOptions);
237
+ if (!contextual.ok)
238
+ return reject(state, contextual.issues[0]?.code === "invalid_audience"
239
+ ? "audience_mismatch"
240
+ : "scope_mismatch");
241
+ if (!isTrustPayload(contextual.value.payload))
242
+ return reject(state, "unsupported_message_type");
243
+ let verification;
244
+ try {
245
+ verification = await verifyMeshEnvelope({
246
+ envelope: request.envelope,
247
+ resolver: configuration.resolver,
248
+ policy: configuration.policy,
249
+ verifiedAt: request.verifiedAt,
250
+ crypto: configuration.crypto,
251
+ protocolOptions: configuration.protocolOptions,
252
+ });
253
+ }
254
+ catch {
255
+ return reject(state, "crypto_rejected");
256
+ }
257
+ if (!verification.verified)
258
+ return reject(state, "crypto_rejected");
259
+ const envelope = verification.envelope;
260
+ const normalized = normalize(envelope);
261
+ if (!normalized)
262
+ return reject(state, "normalization_failed");
263
+ let prepared;
264
+ try {
265
+ prepared = configuration.adapter.prepare({
266
+ state: state.state,
267
+ envelope,
268
+ receivedAt: request.receivedAt,
269
+ });
270
+ }
271
+ catch {
272
+ return reject(state, "authorization_rejected");
273
+ }
274
+ let acceptedPreparation;
275
+ let preparationSecurity;
276
+ try {
277
+ if (!prepared || typeof prepared !== "object")
278
+ return reject(state, "authorization_rejected");
279
+ const accepted = prepared.accepted;
280
+ if (accepted !== true) {
281
+ const code = prepared.code;
282
+ if (accepted === false &&
283
+ (code === "authorization_rejected" || code === "replay_rejected"))
284
+ return Object.freeze({ accepted: false, code, state });
285
+ return reject(state, "authorization_rejected");
286
+ }
287
+ preparationSecurity = Object.freeze({
288
+ accepted: true,
289
+ admissionStateDigest: prepared.admissionStateDigest,
290
+ coordinationAuthorityDigests: Object.freeze([
291
+ ...prepared.coordinationAuthorityDigests,
292
+ ]),
293
+ replayStateDigest: prepared.replayStateDigest,
294
+ observationCorrelated: prepared.observationCorrelated,
295
+ effectiveAtLogicalMs: prepared.effectiveAtLogicalMs,
296
+ });
297
+ if (!isValidPreparation(preparationSecurity, envelope, request.receivedAt))
298
+ return reject(state, "authorization_rejected");
299
+ acceptedPreparation = prepared;
300
+ }
301
+ catch {
302
+ return reject(state, "authorization_rejected");
303
+ }
304
+ let origin;
305
+ try {
306
+ origin = createOriginProof(envelope, verification.key.keyId, normalized.record === undefined
307
+ ? normalized.observation.observationId
308
+ : evidenceRecordId(normalized.record), preparationSecurity);
309
+ }
310
+ catch {
311
+ return reject(state, "normalization_failed");
312
+ }
313
+ const adapterInput = Object.freeze({
314
+ state: state.state,
315
+ envelope,
316
+ verifiedKeyId: verification.key.keyId,
317
+ receivedAt: request.receivedAt,
318
+ ...(normalized.record === undefined
319
+ ? { observation: normalized.observation }
320
+ : { record: normalized.record }),
321
+ ...(normalized.observation === undefined
322
+ ? {}
323
+ : {
324
+ observationCorrelated: preparationSecurity.observationCorrelated,
325
+ }),
326
+ origin,
327
+ originBindingDigest: configuration.adapter.bindingDigest,
328
+ originVerifierBindingDigest: configuration.originVerifierBindingDigest,
329
+ preparation: acceptedPreparation,
330
+ });
331
+ let transition;
332
+ if (configuration.adapterCapability !== undefined)
333
+ authorizedAdapterInvocations.set(adapterInput, configuration.adapterCapability);
334
+ try {
335
+ transition = configuration.adapter.process(adapterInput);
336
+ }
337
+ catch {
338
+ return reject(state, "trust_transition_rejected");
339
+ }
340
+ finally {
341
+ authorizedAdapterInvocations.delete(adapterInput);
342
+ }
343
+ if (!transition || typeof transition !== "object")
344
+ return reject(state, "trust_transition_rejected");
345
+ if (!isValidAdapterTransition(transition, state.state))
346
+ return reject(state, "trust_transition_rejected");
347
+ if (!transition.accepted)
348
+ return Object.freeze({
349
+ accepted: false,
350
+ code: transition.code,
351
+ state,
352
+ });
353
+ return Object.freeze({
354
+ accepted: true,
355
+ duplicate: transition.duplicate,
356
+ observation: normalized.observation !== undefined,
357
+ state: freezeState(state, transition.state),
358
+ });
359
+ },
360
+ };
361
+ return Object.freeze(processor);
362
+ }
363
+ function normalize(envelope) {
364
+ const material = {
365
+ schemaVersion: 1,
366
+ tenantId: envelope.tenantId,
367
+ meshId: envelope.meshId,
368
+ objectiveId: envelope.objectiveId ?? null,
369
+ senderPeerId: envelope.sender.peerId,
370
+ causationId: envelope.causationId ?? null,
371
+ };
372
+ try {
373
+ switch (envelope.payload.type) {
374
+ case "evidence.claim": {
375
+ const record = normalizeMeshEvidenceClaimV1(material, omit(envelope.payload, ["type", "claimId", "assertionDigest"]));
376
+ return record.claimId === envelope.payload.claimId &&
377
+ record.assertionDigest === envelope.payload.assertionDigest
378
+ ? { record }
379
+ : undefined;
380
+ }
381
+ case "evidence.attest": {
382
+ const record = normalizeMeshEvidenceAttestationV1(material, omit(envelope.payload, ["type", "attestationId"]));
383
+ return record.attestationId === envelope.payload.attestationId
384
+ ? { record }
385
+ : undefined;
386
+ }
387
+ case "evidence.challenge": {
388
+ const record = normalizeMeshEvidenceChallengeV1(material, omit(envelope.payload, ["type", "challengeId"]));
389
+ return record.challengeId === envelope.payload.challengeId
390
+ ? { record }
391
+ : undefined;
392
+ }
393
+ case "evidence.retract": {
394
+ const record = normalizeMeshEvidenceRetractionV1(material, omit(envelope.payload, ["type", "retractionId"]));
395
+ return record.retractionId === envelope.payload.retractionId
396
+ ? { record }
397
+ : undefined;
398
+ }
399
+ case "trust.observation": {
400
+ const observation = normalizeMeshTrustObservationV1(material, omit(envelope.payload, ["type", "observationId"]));
401
+ return observation.observationId === envelope.payload.observationId
402
+ ? { observation }
403
+ : undefined;
404
+ }
405
+ }
406
+ }
407
+ catch {
408
+ return undefined;
409
+ }
410
+ }
411
+ function omit(value, keys) {
412
+ const result = { ...value };
413
+ for (const key of keys)
414
+ delete result[key];
415
+ return result;
416
+ }
417
+ function createOriginProof(envelope, keyId, recordId, security) {
418
+ const canonical = canonicalizeMeshJson(envelope);
419
+ if (!canonical.ok)
420
+ throw new TypeError("verified Mesh envelope is not canonical");
421
+ const signedEnvelopeDigest = sha256TrustBytesV1(new TextEncoder().encode(canonical.value));
422
+ const recordDigest = recordId.slice(recordId.indexOf(":") + 1);
423
+ const descriptor = {
424
+ schemaVersion: 1,
425
+ messageId: envelope.messageId,
426
+ payloadHash: envelope.payloadHash,
427
+ signedEnvelopeDigest,
428
+ senderPeerId: envelope.sender.peerId,
429
+ senderKeyId: keyId,
430
+ admissionStateDigest: security.admissionStateDigest,
431
+ coordinationAuthorityDigests: Object.freeze([...security.coordinationAuthorityDigests].sort()),
432
+ replayStateDigest: security.replayStateDigest,
433
+ normalizedRecordId: recordId,
434
+ normalizedRecordDigest: recordDigest,
435
+ };
436
+ return Object.freeze({
437
+ ...descriptor,
438
+ originProofDigest: digestTrustJsonV1("origin-proof", descriptor),
439
+ });
440
+ }
441
+ function createOriginJournalEntry(descriptor, envelope) {
442
+ const canonical = canonicalizeMeshJson(envelope);
443
+ if (!canonical.ok)
444
+ throw new TypeError("verified Mesh envelope is not canonical");
445
+ const entry = Object.freeze({
446
+ schemaVersion: 1,
447
+ descriptor,
448
+ canonicalSignedEnvelope: canonical.value,
449
+ });
450
+ if (!validateMeshEvidenceOriginJournalEntryV1(entry))
451
+ throw new TypeError("Mesh Evidence origin journal entry is invalid");
452
+ return entry;
453
+ }
454
+ /** Validates retained descriptor + exact canonical signed-envelope bytes. */
455
+ export function validateMeshEvidenceOriginJournalEntryV1(value) {
456
+ if (!value || typeof value !== "object" || Array.isArray(value))
457
+ return false;
458
+ const entry = value;
459
+ if (entry.schemaVersion !== 1 ||
460
+ typeof entry.canonicalSignedEnvelope !== "string" ||
461
+ !entry.descriptor ||
462
+ typeof entry.descriptor !== "object" ||
463
+ Array.isArray(entry.descriptor) ||
464
+ !hasExactKeys(entry, [
465
+ "schemaVersion",
466
+ "descriptor",
467
+ "canonicalSignedEnvelope",
468
+ ]))
469
+ return false;
470
+ const descriptor = entry.descriptor;
471
+ if (!hasExactKeys(descriptor, [
472
+ "schemaVersion",
473
+ "messageId",
474
+ "payloadHash",
475
+ "signedEnvelopeDigest",
476
+ "senderPeerId",
477
+ "senderKeyId",
478
+ "admissionStateDigest",
479
+ "coordinationAuthorityDigests",
480
+ "replayStateDigest",
481
+ "normalizedRecordId",
482
+ "normalizedRecordDigest",
483
+ "originProofDigest",
484
+ ]) ||
485
+ descriptor.schemaVersion !== 1 ||
486
+ !trustDigestPattern.test(String(descriptor.signedEnvelopeDigest)) ||
487
+ !trustDigestPattern.test(String(descriptor.admissionStateDigest)) ||
488
+ !trustDigestPattern.test(String(descriptor.replayStateDigest)) ||
489
+ !trustDigestPattern.test(String(descriptor.normalizedRecordDigest)) ||
490
+ !trustDigestPattern.test(String(descriptor.originProofDigest)) ||
491
+ !Array.isArray(descriptor.coordinationAuthorityDigests) ||
492
+ descriptor.coordinationAuthorityDigests.length > maximumAuthorityDigests ||
493
+ !descriptor.coordinationAuthorityDigests.every((digest) => typeof digest === "string" && trustDigestPattern.test(digest)) ||
494
+ !descriptor.coordinationAuthorityDigests.every((digest, index, values) => index === 0 || values[index - 1] < digest))
495
+ return false;
496
+ for (const key of [
497
+ "messageId",
498
+ "payloadHash",
499
+ "senderPeerId",
500
+ "senderKeyId",
501
+ "normalizedRecordId",
502
+ ])
503
+ if (typeof descriptor[key] !== "string" || descriptor[key].length === 0)
504
+ return false;
505
+ const { originProofDigest: _originProofDigest, ...descriptorBody } = descriptor;
506
+ if (digestTrustJsonV1("origin-proof", descriptorBody) !== descriptor.originProofDigest)
507
+ return false;
508
+ let parsed;
509
+ try {
510
+ parsed = JSON.parse(entry.canonicalSignedEnvelope);
511
+ }
512
+ catch {
513
+ return false;
514
+ }
515
+ const canonical = canonicalizeMeshJson(parsed);
516
+ if (!canonical.ok || canonical.value !== entry.canonicalSignedEnvelope)
517
+ return false;
518
+ const envelope = parsed;
519
+ const sender = envelope.sender;
520
+ const proof = envelope.proof;
521
+ return (envelope.messageId === descriptor.messageId &&
522
+ envelope.payloadHash === descriptor.payloadHash &&
523
+ sender?.peerId === descriptor.senderPeerId &&
524
+ proof?.keyId === descriptor.senderKeyId &&
525
+ sha256TrustBytesV1(new TextEncoder().encode(entry.canonicalSignedEnvelope)) === descriptor.signedEnvelopeDigest &&
526
+ String(descriptor.normalizedRecordId).endsWith(`:${descriptor.normalizedRecordDigest}`));
527
+ }
528
+ function validateOriginJournalEntry(entry) {
529
+ return validateMeshEvidenceOriginJournalEntryV1(entry);
530
+ }
531
+ function hasExactKeys(value, keys) {
532
+ const actual = Object.keys(value).sort();
533
+ const expected = [...keys].sort();
534
+ return (actual.length === expected.length &&
535
+ actual.every((key, index) => key === expected[index]));
536
+ }
537
+ function isValidAdapterTransition(value, previousState) {
538
+ const transition = value;
539
+ if (transition.accepted === true)
540
+ return (hasExactKeys(transition, ["accepted", "duplicate", "state"]) &&
541
+ typeof transition.duplicate === "boolean");
542
+ return (transition.accepted === false &&
543
+ hasExactKeys(transition, ["accepted", "code", "state"]) &&
544
+ [
545
+ "authorization_rejected",
546
+ "replay_rejected",
547
+ "trust_transition_rejected",
548
+ ].includes(String(transition.code)) &&
549
+ transition.state === previousState);
550
+ }
551
+ function evidenceRecordId(record) {
552
+ if ("claimId" in record && "outcome" in record)
553
+ return record.claimId;
554
+ if ("attestationId" in record)
555
+ return record.attestationId;
556
+ if ("challengeId" in record)
557
+ return record.challengeId;
558
+ return record.retractionId;
559
+ }
560
+ function isTrustPayload(payload) {
561
+ return (payload.type === "evidence.claim" ||
562
+ payload.type === "evidence.attest" ||
563
+ payload.type === "evidence.challenge" ||
564
+ payload.type === "evidence.retract" ||
565
+ payload.type === "trust.observation");
566
+ }
567
+ function freezeState(state, next) {
568
+ return Object.freeze({
569
+ schemaVersion: 1,
570
+ identity: Object.freeze({ ...state.identity }),
571
+ state: next,
572
+ });
573
+ }
574
+ function reject(state, code) {
575
+ return Object.freeze({ accepted: false, code, state });
576
+ }
577
+ function isRuntimeState(state) {
578
+ return (state.schemaVersion === 1 &&
579
+ state.identity !== null &&
580
+ typeof state.identity === "object" &&
581
+ typeof state.identity.tenantId === "string" &&
582
+ typeof state.identity.meshId === "string" &&
583
+ typeof state.identity.peerId === "string");
584
+ }
585
+ function isValidPreparation(preparation, envelope, receivedAt) {
586
+ const authorities = preparation.coordinationAuthorityDigests;
587
+ return (preparation.accepted === true &&
588
+ trustDigestPattern.test(preparation.admissionStateDigest) &&
589
+ trustDigestPattern.test(preparation.replayStateDigest) &&
590
+ Array.isArray(authorities) &&
591
+ authorities.length <= maximumAuthorityDigests &&
592
+ authorities.every((digest) => trustDigestPattern.test(digest)) &&
593
+ authorities.every((digest, index) => index === 0 || authorities[index - 1] < digest) &&
594
+ typeof preparation.observationCorrelated === "boolean" &&
595
+ Number.isSafeInteger(preparation.effectiveAtLogicalMs) &&
596
+ preparation.effectiveAtLogicalMs >= 0 &&
597
+ preparation.effectiveAtLogicalMs <= receivedAt &&
598
+ (envelope.payload.scope.kind === "work" ||
599
+ preparation.effectiveAtLogicalMs === receivedAt));
600
+ }
601
+ export function filterMeshCapabilityMatchesWithTrustV1(candidates, mode, resolver) {
602
+ if (mode !== "observe" && mode !== "restrict") {
603
+ throw new TypeError("Mesh Trust eligibility mode is invalid");
604
+ }
605
+ if (new Set(candidates.map((candidate) => candidate.peerId)).size !==
606
+ candidates.length) {
607
+ throw new TypeError("Mesh Trust candidates must be unique");
608
+ }
609
+ const evaluate = resolver.evaluate.bind(resolver);
610
+ const diagnostics = candidates.map((candidate) => {
611
+ let status = "unavailable";
612
+ try {
613
+ status = evaluate(candidate);
614
+ }
615
+ catch {
616
+ status = "unavailable";
617
+ }
618
+ if (!["eligible", "restricted", "quarantined", "unavailable"].includes(status)) {
619
+ status = "unavailable";
620
+ }
621
+ return Object.freeze({ peerId: candidate.peerId, status });
622
+ });
623
+ const unavailable = diagnostics.some((item) => item.status === "unavailable");
624
+ const matches = mode === "observe"
625
+ ? candidates
626
+ : unavailable
627
+ ? []
628
+ : candidates.filter((_, index) => diagnostics[index].status === "eligible");
629
+ return Object.freeze({
630
+ matches: Object.freeze([...matches]),
631
+ diagnostics: Object.freeze(diagnostics),
632
+ unavailable,
633
+ });
634
+ }
635
+ export const MESH_PEER_SUBJECT_MAPPING_DIGEST_V1 = digestTrustJsonV1("mesh-subject-mapping", {
636
+ schemaVersion: 1,
637
+ candidateField: "peerId",
638
+ subjectKind: "peer",
639
+ });
640
+ /**
641
+ * Reconstructs the Trust member of a Mesh transaction only through the strict
642
+ * authenticated snapshot boundary. The caller must supply its current trusted
643
+ * external rollback anchor; structural Trust validation alone is insufficient.
644
+ */
645
+ export function restoreMeshTrustEligibilityRuntimeStateV1(current, snapshot, anchor, protector, options = {}) {
646
+ if (!isRuntimeState(current) ||
647
+ !current.state ||
648
+ typeof current.state !== "object" ||
649
+ !Object.hasOwn(current.state, "authorizationState") ||
650
+ !Object.hasOwn(current.state, "originProofs") ||
651
+ !Object.hasOwn(current.state, "remoteObservations"))
652
+ throw new TypeError("Mesh Trust current transaction is invalid");
653
+ const trust = restoreEvidenceTrustSnapshotV1(snapshot, anchor, protector, options);
654
+ const prior = latestEligibilitySnapshotByStateId.get(snapshot.stateId);
655
+ if (!prior &&
656
+ latestEligibilitySnapshotByStateId.size >= maximumEligibilityStateIdentities)
657
+ throw new TypeError("Mesh Trust eligibility state capacity exceeded");
658
+ if (prior &&
659
+ (snapshot.generation < prior.generation ||
660
+ (snapshot.generation === prior.generation &&
661
+ snapshot.snapshotDigest !== prior.digest)))
662
+ throw new TypeError("Mesh Trust snapshot anchor is not current");
663
+ latestEligibilitySnapshotByStateId.set(snapshot.stateId, {
664
+ generation: snapshot.generation,
665
+ digest: snapshot.snapshotDigest,
666
+ });
667
+ const restored = Object.freeze({
668
+ schemaVersion: 1,
669
+ identity: Object.freeze({ ...current.identity }),
670
+ state: Object.freeze({
671
+ ...current.state,
672
+ trust,
673
+ }),
674
+ });
675
+ verifiedEligibilityRuntimeStates.set(restored, {
676
+ stateId: snapshot.stateId,
677
+ generation: snapshot.generation,
678
+ digest: snapshot.snapshotDigest,
679
+ logicalTimeMs: snapshot.createdAtLogicalMs,
680
+ });
681
+ return restored;
682
+ }
683
+ const stateEligibilityConfigKeys = [
684
+ "schemaVersion",
685
+ "mode",
686
+ "logicalTimeMs",
687
+ "scope",
688
+ "policyId",
689
+ "policyVersion",
690
+ "policyDigest",
691
+ "maximumProfileAgeMs",
692
+ "requirements",
693
+ "subjectMappingDigest",
694
+ "meshEligibilityBindingDigest",
695
+ "profileResolverBindingDigest",
696
+ ];
697
+ export function createMeshTrustStateEligibilityConfigV1(value) {
698
+ try {
699
+ assertExactKeys(value, stateEligibilityConfigKeys, "Mesh Trust config");
700
+ if (value.schemaVersion !== 1 ||
701
+ (value.mode !== "observe" && value.mode !== "restrict"))
702
+ throw new TypeError("Mesh Trust config is invalid");
703
+ assertSafeInteger(value.logicalTimeMs, "logicalTimeMs");
704
+ assertIdentifier(value.policyId, "policyId");
705
+ assertSafeInteger(value.policyVersion, "policyVersion", 1);
706
+ assertTrustDigest(value.policyDigest, "policyDigest");
707
+ assertTrustDigest(value.subjectMappingDigest, "subjectMappingDigest");
708
+ assertTrustDigest(value.meshEligibilityBindingDigest, "meshEligibilityBindingDigest");
709
+ assertTrustDigest(value.profileResolverBindingDigest, "profileResolverBindingDigest");
710
+ if (value.subjectMappingDigest !== MESH_PEER_SUBJECT_MAPPING_DIGEST_V1 ||
711
+ value.meshEligibilityBindingDigest === value.profileResolverBindingDigest)
712
+ throw new TypeError("Mesh Trust config binding is invalid");
713
+ const scope = validateEvidenceScopeV1(value.scope);
714
+ if (scope.kind !== "mesh" &&
715
+ scope.kind !== "objective" &&
716
+ scope.kind !== "work")
717
+ throw new TypeError("Mesh Trust scope is invalid");
718
+ const validationSubject = {
719
+ schemaVersion: 1,
720
+ kind: "peer",
721
+ peerId: "mesh-subject-validation",
722
+ };
723
+ const validationProfileDigest = "0".repeat(64);
724
+ const request = createTrustEligibilityRequestV1({
725
+ schemaVersion: 1,
726
+ tenantId: scope.tenantId,
727
+ subject: validationSubject,
728
+ subjectDigest: digestSubjectV1(validationSubject),
729
+ scope,
730
+ scopeDigest: digestScopeV1(scope),
731
+ policyId: value.policyId,
732
+ policyVersion: value.policyVersion,
733
+ policyDigest: value.policyDigest,
734
+ profileId: `profile:${validationProfileDigest}`,
735
+ profileDigest: validationProfileDigest,
736
+ maximumProfileAgeMs: value.maximumProfileAgeMs,
737
+ requirements: value.requirements,
738
+ });
739
+ return deepFreeze({
740
+ ...value,
741
+ scope,
742
+ requirements: request.requirements,
743
+ });
744
+ }
745
+ catch (error) {
746
+ if (error instanceof TypeError && error.message.startsWith("Mesh Trust"))
747
+ throw error;
748
+ throw new TypeError("Mesh Trust state eligibility config is invalid", {
749
+ cause: error,
750
+ });
751
+ }
752
+ }
753
+ export function digestMeshTrustStateEligibilityConfigV1(value) {
754
+ const config = createMeshTrustStateEligibilityConfigV1(value);
755
+ return digestTrustJsonV1("mesh-eligibility-config", {
756
+ schemaVersion: config.schemaVersion,
757
+ mode: config.mode,
758
+ scope: config.scope,
759
+ policyId: config.policyId,
760
+ policyVersion: config.policyVersion,
761
+ policyDigest: config.policyDigest,
762
+ maximumProfileAgeMs: config.maximumProfileAgeMs,
763
+ requirements: config.requirements,
764
+ subjectMappingDigest: config.subjectMappingDigest,
765
+ });
766
+ }
767
+ function meshTrustBindingsAreCurrent(state, config) {
768
+ const resolver = state.dependencyBindings.find((binding) => binding.bindingDigest === config.profileResolverBindingDigest);
769
+ const integration = state.dependencyBindings.find((binding) => binding.bindingDigest === config.meshEligibilityBindingDigest);
770
+ const isCurrent = (binding) => binding !== undefined &&
771
+ binding.registeredAtLogicalMs <= config.logicalTimeMs &&
772
+ binding.validFromLogicalMs <= config.logicalTimeMs &&
773
+ (binding.validUntilLogicalMs === null ||
774
+ config.logicalTimeMs < binding.validUntilLogicalMs) &&
775
+ state.dependencyBindingHeads.some((head) => head.bindingKind === binding.bindingKind &&
776
+ head.bindingName === binding.bindingName &&
777
+ head.bindingVersion === binding.bindingVersion &&
778
+ head.bindingDigest === binding.bindingDigest);
779
+ return (isCurrent(resolver) &&
780
+ resolver.bindingKind === "profile_resolver" &&
781
+ resolver.policyDigest === config.policyDigest &&
782
+ resolver.subjectMappingDigest === config.subjectMappingDigest &&
783
+ isCurrent(integration) &&
784
+ integration.bindingKind === "mesh_eligibility" &&
785
+ integration.policyDigest === config.policyDigest &&
786
+ integration.subjectMappingDigest === config.subjectMappingDigest &&
787
+ integration.upstreamBindingDigest === resolver.bindingDigest &&
788
+ integration.configurationDigest ===
789
+ digestMeshTrustStateEligibilityConfigV1(config));
790
+ }
791
+ const unavailableMeshTrustDiagnostic = (peerId, reasonCode) => deepFreeze({
792
+ peerId,
793
+ disposition: "unavailable",
794
+ eligibilityDecisionId: null,
795
+ reasonCodes: [reasonCode],
796
+ });
797
+ const decisionDiagnostic = (peerId, decision) => deepFreeze({
798
+ peerId,
799
+ disposition: decision.disposition,
800
+ eligibilityDecisionId: decision.eligibilityDecisionId,
801
+ reasonCodes: decision.reasonCodes,
802
+ });
803
+ export function filterMeshCapabilityMatchesWithTrustStateV1(candidates, current, configValue) {
804
+ const config = createMeshTrustStateEligibilityConfigV1(configValue);
805
+ if (!Array.isArray(candidates))
806
+ throw new TypeError("Mesh Trust candidates are invalid");
807
+ for (const candidate of candidates)
808
+ try {
809
+ assertIdentifier(candidate?.peerId, "candidate.peerId");
810
+ }
811
+ catch (error) {
812
+ throw new TypeError("Mesh Trust candidates are invalid", {
813
+ cause: error,
814
+ });
815
+ }
816
+ if (new Set(candidates.map((candidate) => candidate.peerId)).size !==
817
+ candidates.length)
818
+ throw new TypeError("Mesh Trust candidates must be unique");
819
+ let state = null;
820
+ let sharedFailure = null;
821
+ try {
822
+ const scopeMeshId = "meshId" in config.scope ? config.scope.meshId : undefined;
823
+ const verifiedRuntime = verifiedEligibilityRuntimeStates.get(current);
824
+ const currentSnapshot = verifiedRuntime
825
+ ? latestEligibilitySnapshotByStateId.get(verifiedRuntime.stateId)
826
+ : null;
827
+ if (!verifiedRuntime ||
828
+ !currentSnapshot ||
829
+ verifiedRuntime.generation !== currentSnapshot.generation ||
830
+ verifiedRuntime.digest !== currentSnapshot.digest ||
831
+ config.logicalTimeMs !== verifiedRuntime.logicalTimeMs ||
832
+ !isRuntimeState(current) ||
833
+ !current.state ||
834
+ typeof current.state !== "object" ||
835
+ current.identity.tenantId !== config.scope.tenantId ||
836
+ current.identity.meshId !== scopeMeshId)
837
+ throw new TypeError("Mesh Trust current transaction is invalid");
838
+ state = validateEvidenceTrustStateV1(current.state.trust);
839
+ if (config.logicalTimeMs < state.logicalTimeHighWaterMs)
840
+ sharedFailure = "logical_time_rollback";
841
+ else if (!meshTrustBindingsAreCurrent(state, config))
842
+ sharedFailure = "dependency_binding_invalid";
843
+ }
844
+ catch {
845
+ sharedFailure = "state_conflict";
846
+ }
847
+ const diagnostics = candidates.map((candidate) => {
848
+ if (!state || sharedFailure)
849
+ return unavailableMeshTrustDiagnostic(candidate.peerId, sharedFailure ?? "state_conflict");
850
+ try {
851
+ const subject = {
852
+ schemaVersion: 1,
853
+ kind: "peer",
854
+ peerId: candidate.peerId,
855
+ };
856
+ const subjectDigest = digestSubjectV1(subject);
857
+ const scopeDigest = digestScopeV1(config.scope);
858
+ const profileKey = digestTrustProfileKeyV1({
859
+ tenantId: config.scope.tenantId,
860
+ subjectDigest,
861
+ scopeDigest,
862
+ policyDigest: config.policyDigest,
863
+ });
864
+ const profileHead = state.profileHeads.find((head) => head.profileKey === profileKey);
865
+ if (!profileHead)
866
+ return unavailableMeshTrustDiagnostic(candidate.peerId, "profile_unavailable");
867
+ const decision = evaluateTrustEligibilityV1(state, createTrustEligibilityRequestV1({
868
+ schemaVersion: 1,
869
+ tenantId: config.scope.tenantId,
870
+ subject,
871
+ subjectDigest,
872
+ scope: config.scope,
873
+ scopeDigest,
874
+ policyId: config.policyId,
875
+ policyVersion: config.policyVersion,
876
+ policyDigest: config.policyDigest,
877
+ profileId: profileHead.profileId,
878
+ profileDigest: profileHead.profileDigest,
879
+ maximumProfileAgeMs: config.maximumProfileAgeMs,
880
+ requirements: config.requirements,
881
+ }), config.logicalTimeMs);
882
+ return decisionDiagnostic(candidate.peerId, decision);
883
+ }
884
+ catch {
885
+ return unavailableMeshTrustDiagnostic(candidate.peerId, "profile_unavailable");
886
+ }
887
+ });
888
+ const unavailable = diagnostics.some((diagnostic) => diagnostic.disposition === "unavailable");
889
+ const matches = config.mode === "observe"
890
+ ? candidates
891
+ : unavailable
892
+ ? []
893
+ : candidates.filter((_, index) => diagnostics[index].disposition === "eligible");
894
+ return Object.freeze({
895
+ matches: Object.freeze([...matches]),
896
+ diagnostics: Object.freeze(diagnostics),
897
+ unavailable,
898
+ });
899
+ }
900
+ /** Encodes only the already-redacted projection; it never exports a profile or Fusion input. */
901
+ export function encodeMeshTrustObservationV1(observation) {
902
+ const { schemaVersion: _schemaVersion, observerId: _observerId, observerKind: _observerKind, causationId: _causationId, scope, ...payload } = observation;
903
+ return Object.freeze({
904
+ type: "trust.observation",
905
+ ...payload,
906
+ subject: stripMeshSubject(observation.subject),
907
+ scope: stripMeshScope(scope),
908
+ });
909
+ }
910
+ function stripMeshSubject(subject) {
911
+ const { schemaVersion: _schemaVersion, ...wireSubject } = subject;
912
+ return Object.freeze(wireSubject);
913
+ }
914
+ function stripMeshScope(scope) {
915
+ if (scope.kind === "mesh")
916
+ return Object.freeze({ kind: "mesh" });
917
+ if (scope.kind === "objective")
918
+ return Object.freeze({
919
+ kind: "objective",
920
+ objectiveRevision: scope.objectiveRevision,
921
+ });
922
+ if (scope.kind === "work")
923
+ return Object.freeze({
924
+ kind: "work",
925
+ objectiveRevision: scope.objectiveRevision,
926
+ workItemId: scope.workItemId,
927
+ workItemRevision: scope.workItemRevision,
928
+ assignmentEpoch: scope.assignmentEpoch,
929
+ assignmentAuthorityId: scope.assignmentAuthorityId,
930
+ fencingToken: scope.fencingToken,
931
+ });
932
+ throw new TypeError("Trust observation scope is not Mesh-compatible");
933
+ }
934
+ //# sourceMappingURL=trust.js.map