@agentplat/inference-control 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/README.md +41 -11
- package/dist/trust.d.ts +246 -0
- package/dist/trust.d.ts.map +1 -0
- package/dist/trust.js +989 -0
- package/dist/trust.js.map +1 -0
- package/package.json +11 -6
package/dist/trust.js
ADDED
|
@@ -0,0 +1,989 @@
|
|
|
1
|
+
import { assertExactKeys, assertSafeInteger, createTrustEligibilityRequestV1, createEvidenceClaimV1, deepFreeze, digestScopeV1, digestSubjectV1, digestTrustProfileKeyV1, digestTrustJsonV1, evaluateTrustEligibilityV1, restoreEvidenceTrustSnapshotV1, validateEvidenceScopeV1, validateEvidenceTrustStateV1, validateTrustSubjectV1, } from "@agentplat/trust";
|
|
2
|
+
import { digestControlJsonV1 } from "./canonical.js";
|
|
3
|
+
import { actionDigest, scopeDigest, } from "./tools.js";
|
|
4
|
+
import { outboundMessageDigest, } from "./messages.js";
|
|
5
|
+
/**
|
|
6
|
+
* Returns an exact, content-free reference to the accepted assessment request.
|
|
7
|
+
* The reference digest commits to the whole request record, including its
|
|
8
|
+
* target and scope, but the Claim only carries this digest and identifiers.
|
|
9
|
+
*/
|
|
10
|
+
export function createAssessmentRequestReferenceV1(request) {
|
|
11
|
+
return controlReference("assessment_request", request.assessmentRequestId, digestControlJsonV1("trace", request));
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Creates a local Claim candidate only; callers decide whether to admit it to
|
|
15
|
+
* Trust state. Candidate references intentionally bind identifiers and
|
|
16
|
+
* digests, never prompts, output, arguments, or message payloads.
|
|
17
|
+
*/
|
|
18
|
+
export function createClaimCandidateFromAcceptedInferenceOutcomeV1(input) {
|
|
19
|
+
return createAcceptedOutcomeClaimCandidate(input, []);
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Converts a terminal, dispatched Action Gateway outcome to a local Claim
|
|
23
|
+
* candidate. It verifies an already accepted assessment; it never creates or
|
|
24
|
+
* consumes an Action Grant.
|
|
25
|
+
*/
|
|
26
|
+
export function createClaimCandidateFromAcceptedActionDispatchV1(input) {
|
|
27
|
+
assertAcceptedAssessment(input.assessmentRequest, input.assessment);
|
|
28
|
+
const dispatcherBindingDigest = digestActionDispatcherBindingV1(input.dispatcher);
|
|
29
|
+
const grant = input.grant;
|
|
30
|
+
if (input.assessmentRequest.checkpoint !== "pre_tool" ||
|
|
31
|
+
input.assessmentRequest.targetKind !== "action" ||
|
|
32
|
+
grant.status !== "dispatched" ||
|
|
33
|
+
grant.assessmentRequestId !== input.assessmentRequest.assessmentRequestId ||
|
|
34
|
+
grant.assessmentId !== input.assessment.assessmentId ||
|
|
35
|
+
grant.assessmentTargetDigest !== input.assessment.targetDigest ||
|
|
36
|
+
grant.scopeDigest !== controlScopeDigest(input.assessmentRequest.scope) ||
|
|
37
|
+
grant.scopeDigest !== controlScopeDigest(grant.scope) ||
|
|
38
|
+
grant.actionBindingId !== input.binding.actionBindingId ||
|
|
39
|
+
grant.actionBindingVersion !== input.binding.actionBindingVersion ||
|
|
40
|
+
grant.namespace !== input.binding.namespace ||
|
|
41
|
+
grant.toolId !== input.binding.toolId ||
|
|
42
|
+
grant.operation !== input.binding.operation ||
|
|
43
|
+
grant.handlerDigest !== input.binding.handlerDigest ||
|
|
44
|
+
input.binding.dispatcherId !== input.dispatcher.dispatcherId ||
|
|
45
|
+
input.binding.dispatcherVersion !== input.dispatcher.dispatcherVersion ||
|
|
46
|
+
input.binding.fencingMode !== input.dispatcher.fencingMode ||
|
|
47
|
+
grant.actionDigest !== actionDigest(grant, input.binding))
|
|
48
|
+
throw new Error("accepted_terminal_outcome_mismatch");
|
|
49
|
+
return createAcceptedOutcomeClaimCandidate(input, [
|
|
50
|
+
controlReference("action_gateway_outcome", grant.grantId, digestControlJsonV1("trace", {
|
|
51
|
+
schemaVersion: 1,
|
|
52
|
+
grantId: grant.grantId,
|
|
53
|
+
stateGeneration: grant.stateGeneration,
|
|
54
|
+
scopeDigest: grant.scopeDigest,
|
|
55
|
+
actionDigest: grant.actionDigest,
|
|
56
|
+
assessmentRequestId: grant.assessmentRequestId,
|
|
57
|
+
assessmentId: grant.assessmentId,
|
|
58
|
+
status: grant.status,
|
|
59
|
+
})),
|
|
60
|
+
trustReference("action_dispatcher_binding", input.dispatcher.dispatcherId, dispatcherBindingDigest),
|
|
61
|
+
]);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Converts a terminal, sent outbound-message outcome to a local Claim
|
|
65
|
+
* candidate. It references digests only and never copies the message body.
|
|
66
|
+
*/
|
|
67
|
+
export function createClaimCandidateFromAcceptedOutboundMessageV1(input) {
|
|
68
|
+
assertAcceptedAssessment(input.assessmentRequest, input.assessment);
|
|
69
|
+
const dispatcherBindingDigest = digestOutboundMessageDispatcherBindingV1(input.dispatcher);
|
|
70
|
+
const attempt = input.attempt;
|
|
71
|
+
if (input.assessmentRequest.checkpoint !== "pre_message" ||
|
|
72
|
+
input.assessmentRequest.targetKind !== "outbound_message" ||
|
|
73
|
+
attempt.status !== "sent" ||
|
|
74
|
+
attempt.assessmentRequestId !==
|
|
75
|
+
input.assessmentRequest.assessmentRequestId ||
|
|
76
|
+
attempt.assessmentId !== input.assessment.assessmentId ||
|
|
77
|
+
attempt.scopeDigest !== controlScopeDigest(input.assessmentRequest.scope) ||
|
|
78
|
+
attempt.messageDigest !== input.assessment.targetDigest ||
|
|
79
|
+
attempt.dispatcherId !== input.dispatcher.dispatcherId ||
|
|
80
|
+
attempt.dispatcherVersion !== input.dispatcher.dispatcherVersion ||
|
|
81
|
+
attempt.dispatcherDigest !== input.dispatcher.dispatcherDigest)
|
|
82
|
+
throw new Error("accepted_terminal_outcome_mismatch");
|
|
83
|
+
return createAcceptedOutcomeClaimCandidate(input, [
|
|
84
|
+
controlReference("outbound_message_gateway_outcome", attempt.messageAttemptId, digestControlJsonV1("trace", {
|
|
85
|
+
schemaVersion: 1,
|
|
86
|
+
messageAttemptId: attempt.messageAttemptId,
|
|
87
|
+
messageId: attempt.messageId,
|
|
88
|
+
scopeDigest: attempt.scopeDigest,
|
|
89
|
+
messageDigest: attempt.messageDigest,
|
|
90
|
+
assessmentRequestId: attempt.assessmentRequestId,
|
|
91
|
+
assessmentId: attempt.assessmentId,
|
|
92
|
+
status: attempt.status,
|
|
93
|
+
})),
|
|
94
|
+
trustReference("outbound_message_dispatcher_binding", input.dispatcher.dispatcherId, dispatcherBindingDigest),
|
|
95
|
+
]);
|
|
96
|
+
}
|
|
97
|
+
export function createInferenceControlTrustClaimMappingV1(input) {
|
|
98
|
+
if (input.schemaVersion !== 1)
|
|
99
|
+
throw new TypeError("trust_mapping_invalid");
|
|
100
|
+
assertIdentifier(input.mappingId, "mappingId");
|
|
101
|
+
assertPositiveInteger(input.mappingVersion, "mappingVersion");
|
|
102
|
+
assertIdentifier(input.sourceId, "sourceId");
|
|
103
|
+
assertIdentifier(input.criterionId, "criterionId");
|
|
104
|
+
if (!["satisfied", "violated", "inconclusive"].includes(input.outcome))
|
|
105
|
+
throw new TypeError("trust_mapping_invalid");
|
|
106
|
+
const mappingDigest = digestTrustJsonV1("dependency-binding", {
|
|
107
|
+
schemaVersion: 1,
|
|
108
|
+
mappingId: input.mappingId,
|
|
109
|
+
mappingVersion: input.mappingVersion,
|
|
110
|
+
sourceId: input.sourceId,
|
|
111
|
+
subject: input.subject,
|
|
112
|
+
scope: input.scope,
|
|
113
|
+
criterionId: input.criterionId,
|
|
114
|
+
outcome: input.outcome,
|
|
115
|
+
});
|
|
116
|
+
return Object.freeze({ ...input, mappingDigest });
|
|
117
|
+
}
|
|
118
|
+
const maximumTrustEligibilityStateIdentities = 4_096;
|
|
119
|
+
const verifiedTrustEligibilityRuntimes = new WeakMap();
|
|
120
|
+
const latestTrustEligibilitySnapshotByStateId = new Map();
|
|
121
|
+
export function digestInferenceControlTrustRuntimeSourceBindingV1(value) {
|
|
122
|
+
assertIdentifier(value.sourceId, "sourceId");
|
|
123
|
+
assertPositiveInteger(value.sourceVersion, "sourceVersion");
|
|
124
|
+
assertTrustDigest(value.protectorBindingDigest, "protectorBindingDigest");
|
|
125
|
+
return digestTrustJsonV1("dependency-binding", {
|
|
126
|
+
schemaVersion: 1,
|
|
127
|
+
kind: "inference_trust_runtime_source",
|
|
128
|
+
sourceId: value.sourceId,
|
|
129
|
+
sourceVersion: value.sourceVersion,
|
|
130
|
+
protectorBindingDigest: value.protectorBindingDigest,
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
export function digestInferenceControlTrustSubjectMappingV1(value) {
|
|
134
|
+
assertIdentifier(value.controlTenantId, "controlTenantId");
|
|
135
|
+
assertIdentifier(value.controlRunId, "controlRunId");
|
|
136
|
+
assertControlDigest(value.controlScopeDigest, "controlScopeDigest");
|
|
137
|
+
const trustSubject = validateTrustSubjectV1(value.trustSubject);
|
|
138
|
+
const trustScope = validateEvidenceScopeV1(value.trustScope);
|
|
139
|
+
if (trustScope.tenantId !== value.controlTenantId)
|
|
140
|
+
throw new TypeError("trust_state_mapping_invalid");
|
|
141
|
+
return digestTrustJsonV1("inference-subject-mapping", {
|
|
142
|
+
schemaVersion: 1,
|
|
143
|
+
controlTenantId: value.controlTenantId,
|
|
144
|
+
controlRunId: value.controlRunId,
|
|
145
|
+
controlScopeDigest: value.controlScopeDigest,
|
|
146
|
+
trustSubjectDigest: digestSubjectV1(trustSubject),
|
|
147
|
+
trustScopeDigest: digestScopeV1(trustScope),
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
const stateEligibilityConfigKeys = [
|
|
151
|
+
"schemaVersion",
|
|
152
|
+
"operation",
|
|
153
|
+
"mode",
|
|
154
|
+
"controlTenantId",
|
|
155
|
+
"controlRunId",
|
|
156
|
+
"controlScopeDigest",
|
|
157
|
+
"trustSubject",
|
|
158
|
+
"trustScope",
|
|
159
|
+
"policyId",
|
|
160
|
+
"policyVersion",
|
|
161
|
+
"policyDigest",
|
|
162
|
+
"maximumProfileAgeMs",
|
|
163
|
+
"requirements",
|
|
164
|
+
"subjectMappingDigest",
|
|
165
|
+
"runtimeSourceBindingDigest",
|
|
166
|
+
"profileResolverBindingDigest",
|
|
167
|
+
"boundaryBindingDigest",
|
|
168
|
+
"baseBindingDigest",
|
|
169
|
+
];
|
|
170
|
+
export function createInferenceControlTrustStateEligibilityConfigV1(value) {
|
|
171
|
+
try {
|
|
172
|
+
assertExactKeys(value, stateEligibilityConfigKeys, "Inference Control Trust config");
|
|
173
|
+
if (value.schemaVersion !== 1 ||
|
|
174
|
+
!["model", "action", "outbound_message"].includes(value.operation) ||
|
|
175
|
+
(value.mode !== "observe" && value.mode !== "restrict"))
|
|
176
|
+
throw new TypeError("trust_state_config_invalid");
|
|
177
|
+
assertIdentifier(value.policyId, "policyId");
|
|
178
|
+
assertSafeInteger(value.policyVersion, "policyVersion", 1);
|
|
179
|
+
assertTrustDigest(value.policyDigest, "policyDigest");
|
|
180
|
+
for (const digest of [
|
|
181
|
+
value.subjectMappingDigest,
|
|
182
|
+
value.runtimeSourceBindingDigest,
|
|
183
|
+
value.profileResolverBindingDigest,
|
|
184
|
+
value.boundaryBindingDigest,
|
|
185
|
+
value.baseBindingDigest,
|
|
186
|
+
])
|
|
187
|
+
assertTrustDigest(digest, "Trust binding digest");
|
|
188
|
+
if (value.profileResolverBindingDigest === value.boundaryBindingDigest ||
|
|
189
|
+
value.subjectMappingDigest !==
|
|
190
|
+
digestInferenceControlTrustSubjectMappingV1(value))
|
|
191
|
+
throw new TypeError("trust_state_config_invalid");
|
|
192
|
+
const trustSubject = validateTrustSubjectV1(value.trustSubject);
|
|
193
|
+
const trustScope = validateEvidenceScopeV1(value.trustScope);
|
|
194
|
+
const validationProfileDigest = "0".repeat(64);
|
|
195
|
+
const request = createTrustEligibilityRequestV1({
|
|
196
|
+
schemaVersion: 1,
|
|
197
|
+
tenantId: value.controlTenantId,
|
|
198
|
+
subject: trustSubject,
|
|
199
|
+
subjectDigest: digestSubjectV1(trustSubject),
|
|
200
|
+
scope: trustScope,
|
|
201
|
+
scopeDigest: digestScopeV1(trustScope),
|
|
202
|
+
policyId: value.policyId,
|
|
203
|
+
policyVersion: value.policyVersion,
|
|
204
|
+
policyDigest: value.policyDigest,
|
|
205
|
+
profileId: `profile:${validationProfileDigest}`,
|
|
206
|
+
profileDigest: validationProfileDigest,
|
|
207
|
+
maximumProfileAgeMs: value.maximumProfileAgeMs,
|
|
208
|
+
requirements: value.requirements,
|
|
209
|
+
});
|
|
210
|
+
return deepFreeze({
|
|
211
|
+
...value,
|
|
212
|
+
trustSubject,
|
|
213
|
+
trustScope,
|
|
214
|
+
requirements: request.requirements,
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
catch (error) {
|
|
218
|
+
throw new TypeError("trust_state_config_invalid", { cause: error });
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
export function digestInferenceControlTrustStateEligibilityConfigV1(value) {
|
|
222
|
+
const config = createInferenceControlTrustStateEligibilityConfigV1(value);
|
|
223
|
+
return digestTrustJsonV1("inference-eligibility-config", {
|
|
224
|
+
schemaVersion: config.schemaVersion,
|
|
225
|
+
operation: config.operation,
|
|
226
|
+
mode: config.mode,
|
|
227
|
+
controlTenantId: config.controlTenantId,
|
|
228
|
+
controlRunId: config.controlRunId,
|
|
229
|
+
controlScopeDigest: config.controlScopeDigest,
|
|
230
|
+
trustSubject: config.trustSubject,
|
|
231
|
+
trustScope: config.trustScope,
|
|
232
|
+
policyId: config.policyId,
|
|
233
|
+
policyVersion: config.policyVersion,
|
|
234
|
+
policyDigest: config.policyDigest,
|
|
235
|
+
maximumProfileAgeMs: config.maximumProfileAgeMs,
|
|
236
|
+
requirements: config.requirements,
|
|
237
|
+
subjectMappingDigest: config.subjectMappingDigest,
|
|
238
|
+
runtimeSourceBindingDigest: config.runtimeSourceBindingDigest,
|
|
239
|
+
baseBindingDigest: config.baseBindingDigest,
|
|
240
|
+
});
|
|
241
|
+
}
|
|
242
|
+
export function restoreInferenceControlTrustEligibilityRuntimeV1(snapshot, anchor, protector, runtimeSourceBindingDigest, options = {}) {
|
|
243
|
+
assertTrustDigest(runtimeSourceBindingDigest, "runtimeSourceBindingDigest");
|
|
244
|
+
const state = restoreEvidenceTrustSnapshotV1(snapshot, anchor, protector, options);
|
|
245
|
+
const prior = latestTrustEligibilitySnapshotByStateId.get(snapshot.stateId);
|
|
246
|
+
if (!prior &&
|
|
247
|
+
latestTrustEligibilitySnapshotByStateId.size >=
|
|
248
|
+
maximumTrustEligibilityStateIdentities)
|
|
249
|
+
throw new TypeError("trust_state_capacity_exceeded");
|
|
250
|
+
if (prior &&
|
|
251
|
+
(snapshot.generation < prior.generation ||
|
|
252
|
+
(snapshot.generation === prior.generation &&
|
|
253
|
+
snapshot.snapshotDigest !== prior.snapshotDigest)))
|
|
254
|
+
throw new TypeError("trust_state_snapshot_not_current");
|
|
255
|
+
latestTrustEligibilitySnapshotByStateId.set(snapshot.stateId, {
|
|
256
|
+
generation: snapshot.generation,
|
|
257
|
+
snapshotDigest: snapshot.snapshotDigest,
|
|
258
|
+
});
|
|
259
|
+
const rollbackAnchor = deepFreeze({ ...anchor });
|
|
260
|
+
const runtimeId = digestTrustJsonV1("dependency-binding", {
|
|
261
|
+
schemaVersion: 1,
|
|
262
|
+
kind: "inference_trust_eligibility_runtime",
|
|
263
|
+
stateId: snapshot.stateId,
|
|
264
|
+
generation: snapshot.generation,
|
|
265
|
+
snapshotDigest: snapshot.snapshotDigest,
|
|
266
|
+
logicalTimeMs: snapshot.createdAtLogicalMs,
|
|
267
|
+
runtimeSourceBindingDigest,
|
|
268
|
+
rollbackAnchor,
|
|
269
|
+
});
|
|
270
|
+
const runtime = Object.freeze({
|
|
271
|
+
schemaVersion: 1,
|
|
272
|
+
runtimeId,
|
|
273
|
+
});
|
|
274
|
+
verifiedTrustEligibilityRuntimes.set(runtime, {
|
|
275
|
+
runtimeId,
|
|
276
|
+
stateId: snapshot.stateId,
|
|
277
|
+
generation: snapshot.generation,
|
|
278
|
+
snapshotDigest: snapshot.snapshotDigest,
|
|
279
|
+
logicalTimeMs: snapshot.createdAtLogicalMs,
|
|
280
|
+
runtimeSourceBindingDigest,
|
|
281
|
+
rollbackAnchor,
|
|
282
|
+
state,
|
|
283
|
+
});
|
|
284
|
+
return runtime;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Evaluates an exact Inference Control target against a current, strictly
|
|
288
|
+
* restored Trust snapshot. Runtime failures are projected as unavailable;
|
|
289
|
+
* malformed construction-time configuration remains a caller error.
|
|
290
|
+
*/
|
|
291
|
+
export function evaluateInferenceControlTrustStateEligibilityV1(integration, target) {
|
|
292
|
+
const snapshot = snapshotTrustStateIntegration(integration);
|
|
293
|
+
assertStateEligibilityTargetBinding(snapshot.config, target);
|
|
294
|
+
const result = evaluateTrustStateEligibilityFromSnapshot(snapshot, target);
|
|
295
|
+
emitTrustStateDiagnostic(snapshot, target, result);
|
|
296
|
+
return result;
|
|
297
|
+
}
|
|
298
|
+
/** Derives the model boundary binding from its local implementation identity. */
|
|
299
|
+
export function digestInferenceControlTrustModelBoundaryV1(boundary) {
|
|
300
|
+
validateTrustModelBoundaryIdentity(boundary);
|
|
301
|
+
return digestTrustJsonV1("dependency-binding", {
|
|
302
|
+
schemaVersion: 1,
|
|
303
|
+
kind: "model_boundary",
|
|
304
|
+
modelBoundaryId: boundary.modelBoundaryId,
|
|
305
|
+
modelBoundaryVersion: boundary.modelBoundaryVersion,
|
|
306
|
+
implementationDigest: boundary.implementationDigest,
|
|
307
|
+
});
|
|
308
|
+
}
|
|
309
|
+
/** Derives the real Action dispatcher binding from its public identity. */
|
|
310
|
+
export function digestActionDispatcherBindingV1(dispatcher) {
|
|
311
|
+
validateActionDispatcherIdentity(dispatcher);
|
|
312
|
+
return digestTrustJsonV1("dependency-binding", {
|
|
313
|
+
schemaVersion: 1,
|
|
314
|
+
kind: "action_dispatcher",
|
|
315
|
+
dispatcherId: dispatcher.dispatcherId,
|
|
316
|
+
dispatcherVersion: dispatcher.dispatcherVersion,
|
|
317
|
+
fencingMode: dispatcher.fencingMode,
|
|
318
|
+
});
|
|
319
|
+
}
|
|
320
|
+
/** Derives the real Message dispatcher binding, including its dispatcher digest. */
|
|
321
|
+
export function digestOutboundMessageDispatcherBindingV1(dispatcher) {
|
|
322
|
+
validateMessageDispatcherIdentity(dispatcher);
|
|
323
|
+
return digestTrustJsonV1("dependency-binding", {
|
|
324
|
+
schemaVersion: 1,
|
|
325
|
+
kind: "outbound_message_dispatcher",
|
|
326
|
+
dispatcherId: dispatcher.dispatcherId,
|
|
327
|
+
dispatcherVersion: dispatcher.dispatcherVersion,
|
|
328
|
+
dispatcherDigest: dispatcher.dispatcherDigest,
|
|
329
|
+
fencingMode: dispatcher.fencingMode,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
export function digestTrustEligibilityBindingV1(integration, baseBindingDigest) {
|
|
333
|
+
const snapshot = snapshotIntegration(integration);
|
|
334
|
+
assertTrustDigest(baseBindingDigest, "baseBindingDigest");
|
|
335
|
+
return digestTrustEligibilityBindingFromSnapshot(snapshot, baseBindingDigest);
|
|
336
|
+
}
|
|
337
|
+
/**
|
|
338
|
+
* Runs a model-boundary eligibility check before starting the supplied model
|
|
339
|
+
* operation. Existing executors remain untouched unless this helper is used.
|
|
340
|
+
*/
|
|
341
|
+
export function runWithTrustEligibilityV1(integration, target, run) {
|
|
342
|
+
const snapshot = snapshotIntegration(integration);
|
|
343
|
+
assertTrustDigest(integration.modelBindingDigest, "modelBindingDigest");
|
|
344
|
+
const bindingDigest = digestTrustEligibilityBindingFromSnapshot(snapshot, integration.modelBindingDigest);
|
|
345
|
+
requireOrObserveEligibility(snapshot, target, bindingDigest);
|
|
346
|
+
return run();
|
|
347
|
+
}
|
|
348
|
+
export function wrapActionDispatcherWithTrustV1(dispatcher, integration, mapTarget) {
|
|
349
|
+
const base = snapshotActionDispatcher(dispatcher);
|
|
350
|
+
const snapshot = snapshotIntegration(integration);
|
|
351
|
+
const bindingDigest = digestTrustEligibilityBindingFromSnapshot(snapshot, digestActionDispatcherBindingV1(base));
|
|
352
|
+
const map = mapTarget.bind(undefined);
|
|
353
|
+
return Object.freeze({
|
|
354
|
+
dispatcherId: base.dispatcherId,
|
|
355
|
+
dispatcherVersion: base.dispatcherVersion,
|
|
356
|
+
fencingMode: base.fencingMode,
|
|
357
|
+
trustBindingDigest: bindingDigest,
|
|
358
|
+
dispatch: async (input) => {
|
|
359
|
+
const target = map(input);
|
|
360
|
+
assertActionEligibilityTargetBinding(target, input);
|
|
361
|
+
requireOrObserveEligibility(snapshot, target, bindingDigest);
|
|
362
|
+
return base.dispatch(input);
|
|
363
|
+
},
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
export function wrapOutboundMessageDispatcherWithTrustV1(dispatcher, integration, mapTarget) {
|
|
367
|
+
const base = snapshotOutboundMessageDispatcher(dispatcher);
|
|
368
|
+
const snapshot = snapshotIntegration(integration);
|
|
369
|
+
const bindingDigest = digestTrustEligibilityBindingFromSnapshot(snapshot, digestOutboundMessageDispatcherBindingV1(base));
|
|
370
|
+
const map = mapTarget.bind(undefined);
|
|
371
|
+
return Object.freeze({
|
|
372
|
+
dispatcherId: base.dispatcherId,
|
|
373
|
+
dispatcherVersion: base.dispatcherVersion,
|
|
374
|
+
dispatcherDigest: base.dispatcherDigest,
|
|
375
|
+
fencingMode: base.fencingMode,
|
|
376
|
+
trustBindingDigest: bindingDigest,
|
|
377
|
+
send: async (input) => {
|
|
378
|
+
const target = map(input);
|
|
379
|
+
assertOutboundMessageEligibilityTargetBinding(target, input);
|
|
380
|
+
requireOrObserveEligibility(snapshot, target, bindingDigest);
|
|
381
|
+
return base.send(input);
|
|
382
|
+
},
|
|
383
|
+
});
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* Runs a model operation only after sampling the current authenticated Trust
|
|
387
|
+
* runtime. No asynchronous gap is introduced between the final check and run.
|
|
388
|
+
*/
|
|
389
|
+
export function runWithTrustStateEligibilityV1(integration, target, boundary) {
|
|
390
|
+
return wrapModelBoundaryWithTrustStateV1(boundary, integration).run(target);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* Construction-binds the model implementation and passes the exact target
|
|
394
|
+
* checked by Trust to that same boundary.
|
|
395
|
+
*/
|
|
396
|
+
export function wrapModelBoundaryWithTrustStateV1(boundary, integration) {
|
|
397
|
+
const base = snapshotTrustModelBoundary(boundary);
|
|
398
|
+
const snapshot = snapshotTrustStateIntegration(integration);
|
|
399
|
+
const baseBindingDigest = digestInferenceControlTrustModelBoundaryV1(base);
|
|
400
|
+
if (snapshot.config.operation !== "model" ||
|
|
401
|
+
snapshot.config.baseBindingDigest !== baseBindingDigest)
|
|
402
|
+
throw new TypeError("trust_state_binding_mismatch");
|
|
403
|
+
return Object.freeze({
|
|
404
|
+
modelBoundaryId: base.modelBoundaryId,
|
|
405
|
+
modelBoundaryVersion: base.modelBoundaryVersion,
|
|
406
|
+
implementationDigest: base.implementationDigest,
|
|
407
|
+
trustBindingDigest: snapshot.config.boundaryBindingDigest,
|
|
408
|
+
run: (targetValue) => {
|
|
409
|
+
const target = snapshotTrustEligibilityTarget(targetValue);
|
|
410
|
+
assertStateEligibilityTargetBinding(snapshot.config, target);
|
|
411
|
+
const result = requireTrustStateEligibility(snapshot, target);
|
|
412
|
+
const output = base.run(target);
|
|
413
|
+
emitTrustStateDiagnostic(snapshot, target, result);
|
|
414
|
+
return output;
|
|
415
|
+
},
|
|
416
|
+
});
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* Adds a state-backed Trust predicate to an Action dispatcher without
|
|
420
|
+
* changing its grant, authority, reservation, or fencing contracts.
|
|
421
|
+
*/
|
|
422
|
+
export function wrapActionDispatcherWithTrustStateV1(dispatcher, integration) {
|
|
423
|
+
const base = snapshotActionDispatcher(dispatcher);
|
|
424
|
+
const baseBindingDigest = digestActionDispatcherBindingV1(base);
|
|
425
|
+
const snapshot = snapshotTrustStateIntegration(integration);
|
|
426
|
+
if (snapshot.config.operation !== "action" ||
|
|
427
|
+
snapshot.config.baseBindingDigest !== baseBindingDigest)
|
|
428
|
+
throw new TypeError("trust_state_binding_mismatch");
|
|
429
|
+
return Object.freeze({
|
|
430
|
+
dispatcherId: base.dispatcherId,
|
|
431
|
+
dispatcherVersion: base.dispatcherVersion,
|
|
432
|
+
fencingMode: base.fencingMode,
|
|
433
|
+
trustBindingDigest: snapshot.config.boundaryBindingDigest,
|
|
434
|
+
dispatch: async (input) => {
|
|
435
|
+
const target = Object.freeze({
|
|
436
|
+
schemaVersion: 1,
|
|
437
|
+
operation: "action",
|
|
438
|
+
tenantId: input.context.tenant.tenantId,
|
|
439
|
+
runId: snapshot.config.controlRunId,
|
|
440
|
+
scopeDigest: input.permit.scopeDigest,
|
|
441
|
+
targetDigest: input.permit.actionDigest,
|
|
442
|
+
});
|
|
443
|
+
assertActionEligibilityTargetBinding(target, input);
|
|
444
|
+
assertStateEligibilityTargetBinding(snapshot.config, target);
|
|
445
|
+
const result = requireTrustStateEligibility(snapshot, target);
|
|
446
|
+
const output = base.dispatch(input);
|
|
447
|
+
emitTrustStateDiagnostic(snapshot, target, result);
|
|
448
|
+
return output;
|
|
449
|
+
},
|
|
450
|
+
});
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* Adds a state-backed Trust predicate to an outbound Message dispatcher while
|
|
454
|
+
* preserving the base dispatcher's attempt and fencing behavior.
|
|
455
|
+
*/
|
|
456
|
+
export function wrapOutboundMessageDispatcherWithTrustStateV1(dispatcher, integration) {
|
|
457
|
+
const base = snapshotOutboundMessageDispatcher(dispatcher);
|
|
458
|
+
const baseBindingDigest = digestOutboundMessageDispatcherBindingV1(base);
|
|
459
|
+
const snapshot = snapshotTrustStateIntegration(integration);
|
|
460
|
+
if (snapshot.config.operation !== "outbound_message" ||
|
|
461
|
+
snapshot.config.baseBindingDigest !== baseBindingDigest)
|
|
462
|
+
throw new TypeError("trust_state_binding_mismatch");
|
|
463
|
+
return Object.freeze({
|
|
464
|
+
dispatcherId: base.dispatcherId,
|
|
465
|
+
dispatcherVersion: base.dispatcherVersion,
|
|
466
|
+
dispatcherDigest: base.dispatcherDigest,
|
|
467
|
+
fencingMode: base.fencingMode,
|
|
468
|
+
trustBindingDigest: snapshot.config.boundaryBindingDigest,
|
|
469
|
+
send: async (input) => {
|
|
470
|
+
const target = Object.freeze({
|
|
471
|
+
schemaVersion: 1,
|
|
472
|
+
operation: "outbound_message",
|
|
473
|
+
tenantId: input.message.tenantId,
|
|
474
|
+
runId: input.message.runId,
|
|
475
|
+
scopeDigest: input.permit.scopeDigest,
|
|
476
|
+
targetDigest: input.permit.messageDigest,
|
|
477
|
+
});
|
|
478
|
+
assertOutboundMessageEligibilityTargetBinding(target, input);
|
|
479
|
+
assertStateEligibilityTargetBinding(snapshot.config, target);
|
|
480
|
+
const result = requireTrustStateEligibility(snapshot, target);
|
|
481
|
+
const output = base.send(input);
|
|
482
|
+
emitTrustStateDiagnostic(snapshot, target, result);
|
|
483
|
+
return output;
|
|
484
|
+
},
|
|
485
|
+
});
|
|
486
|
+
}
|
|
487
|
+
function snapshotTrustStateIntegration(integration) {
|
|
488
|
+
if (!integration || typeof integration !== "object")
|
|
489
|
+
throw new TypeError("trust_state_integration_invalid");
|
|
490
|
+
const config = createInferenceControlTrustStateEligibilityConfigV1(integration.config);
|
|
491
|
+
const source = integration.runtime;
|
|
492
|
+
if (!source ||
|
|
493
|
+
typeof source !== "object" ||
|
|
494
|
+
typeof source.current !== "function" ||
|
|
495
|
+
(integration.onDiagnostic !== undefined &&
|
|
496
|
+
typeof integration.onDiagnostic !== "function"))
|
|
497
|
+
throw new TypeError("trust_state_integration_invalid");
|
|
498
|
+
const runtimeSourceBindingDigest = digestInferenceControlTrustRuntimeSourceBindingV1(source);
|
|
499
|
+
if (source.bindingDigest !== runtimeSourceBindingDigest ||
|
|
500
|
+
config.runtimeSourceBindingDigest !== runtimeSourceBindingDigest)
|
|
501
|
+
throw new TypeError("trust_state_integration_invalid");
|
|
502
|
+
return Object.freeze({
|
|
503
|
+
config,
|
|
504
|
+
current: source.current.bind(source),
|
|
505
|
+
runtimeSourceBindingDigest,
|
|
506
|
+
protectorBindingDigest: source.protectorBindingDigest,
|
|
507
|
+
onDiagnostic: integration.onDiagnostic === undefined
|
|
508
|
+
? undefined
|
|
509
|
+
: integration.onDiagnostic.bind(integration),
|
|
510
|
+
});
|
|
511
|
+
}
|
|
512
|
+
function assertStateEligibilityTargetBinding(config, target) {
|
|
513
|
+
validateTarget(target);
|
|
514
|
+
if (target.operation !== config.operation ||
|
|
515
|
+
target.tenantId !== config.controlTenantId ||
|
|
516
|
+
target.runId !== config.controlRunId ||
|
|
517
|
+
target.scopeDigest !== config.controlScopeDigest)
|
|
518
|
+
throw new Error("trust_eligibility_target_mismatch");
|
|
519
|
+
}
|
|
520
|
+
function requireTrustStateEligibility(integration, target) {
|
|
521
|
+
const result = evaluateTrustStateEligibilityFromSnapshot(integration, target);
|
|
522
|
+
if (integration.config.mode === "restrict" && result.status !== "eligible") {
|
|
523
|
+
emitTrustStateDiagnostic(integration, target, result);
|
|
524
|
+
throw new Error("trust_eligibility_restricted");
|
|
525
|
+
}
|
|
526
|
+
return result;
|
|
527
|
+
}
|
|
528
|
+
function emitTrustStateDiagnostic(integration, target, result) {
|
|
529
|
+
try {
|
|
530
|
+
integration.onDiagnostic?.(Object.freeze({
|
|
531
|
+
schemaVersion: 1,
|
|
532
|
+
operation: target.operation,
|
|
533
|
+
mode: integration.config.mode,
|
|
534
|
+
status: result.status,
|
|
535
|
+
bindingDigest: integration.config.boundaryBindingDigest,
|
|
536
|
+
}));
|
|
537
|
+
}
|
|
538
|
+
catch {
|
|
539
|
+
// Diagnostics are redacted, observational, and cannot alter delegation.
|
|
540
|
+
}
|
|
541
|
+
}
|
|
542
|
+
function unavailableTrustStateEligibility(reasonCode) {
|
|
543
|
+
return deepFreeze({
|
|
544
|
+
status: "unavailable",
|
|
545
|
+
eligibilityDecisionId: null,
|
|
546
|
+
reasonCodes: [reasonCode],
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
function trustStateDecisionResult(decision) {
|
|
550
|
+
return deepFreeze({
|
|
551
|
+
status: decision.disposition,
|
|
552
|
+
eligibilityDecisionId: decision.eligibilityDecisionId,
|
|
553
|
+
reasonCodes: decision.reasonCodes,
|
|
554
|
+
});
|
|
555
|
+
}
|
|
556
|
+
function trustDependencyBindingIsCurrent(state, binding, logicalTimeMs) {
|
|
557
|
+
return (binding !== undefined &&
|
|
558
|
+
binding.registeredAtLogicalMs <= logicalTimeMs &&
|
|
559
|
+
binding.validFromLogicalMs <= logicalTimeMs &&
|
|
560
|
+
(binding.validUntilLogicalMs === null ||
|
|
561
|
+
logicalTimeMs < binding.validUntilLogicalMs) &&
|
|
562
|
+
state.dependencyBindingHeads.some((head) => head.bindingKind === binding.bindingKind &&
|
|
563
|
+
head.bindingName === binding.bindingName &&
|
|
564
|
+
head.bindingVersion === binding.bindingVersion &&
|
|
565
|
+
head.bindingDigest === binding.bindingDigest));
|
|
566
|
+
}
|
|
567
|
+
function inferenceTrustBindingsAreCurrent(state, config, logicalTimeMs) {
|
|
568
|
+
const resolver = state.dependencyBindings.find((binding) => binding.bindingDigest === config.profileResolverBindingDigest);
|
|
569
|
+
const boundary = state.dependencyBindings.find((binding) => binding.bindingDigest === config.boundaryBindingDigest);
|
|
570
|
+
const expectedBoundaryKind = config.operation === "model"
|
|
571
|
+
? "model_boundary"
|
|
572
|
+
: config.operation === "action"
|
|
573
|
+
? "action_dispatcher"
|
|
574
|
+
: "message_dispatcher";
|
|
575
|
+
return (trustDependencyBindingIsCurrent(state, resolver, logicalTimeMs) &&
|
|
576
|
+
resolver.bindingKind === "profile_resolver" &&
|
|
577
|
+
resolver.policyDigest === config.policyDigest &&
|
|
578
|
+
resolver.subjectMappingDigest === config.subjectMappingDigest &&
|
|
579
|
+
trustDependencyBindingIsCurrent(state, boundary, logicalTimeMs) &&
|
|
580
|
+
boundary.bindingKind === expectedBoundaryKind &&
|
|
581
|
+
boundary.policyDigest === config.policyDigest &&
|
|
582
|
+
boundary.subjectMappingDigest === config.subjectMappingDigest &&
|
|
583
|
+
boundary.upstreamBindingDigest === resolver.bindingDigest &&
|
|
584
|
+
boundary.configurationDigest ===
|
|
585
|
+
digestInferenceControlTrustStateEligibilityConfigV1(config) &&
|
|
586
|
+
boundary.implementationDigest === config.baseBindingDigest);
|
|
587
|
+
}
|
|
588
|
+
function rollbackAnchorMatches(left, right) {
|
|
589
|
+
return (left.schemaVersion === 1 &&
|
|
590
|
+
left.stateId === right.stateId &&
|
|
591
|
+
left.requiredGeneration === right.requiredGeneration &&
|
|
592
|
+
left.requiredSnapshotDigest === right.requiredSnapshotDigest &&
|
|
593
|
+
left.minimumLogicalHighWaterMs === right.minimumLogicalHighWaterMs &&
|
|
594
|
+
left.protectorBindingDigest === right.protectorBindingDigest);
|
|
595
|
+
}
|
|
596
|
+
function evaluateTrustStateEligibilityFromSnapshot(integration, target) {
|
|
597
|
+
const config = integration.config;
|
|
598
|
+
let state;
|
|
599
|
+
let logicalTimeMs;
|
|
600
|
+
try {
|
|
601
|
+
const sample = integration.current();
|
|
602
|
+
assertExactKeys(sample, ["schemaVersion", "runtime", "rollbackAnchor"], "Inference Control Trust runtime sample");
|
|
603
|
+
if (sample.schemaVersion !== 1)
|
|
604
|
+
return unavailableTrustStateEligibility("state_conflict");
|
|
605
|
+
const runtime = sample.runtime;
|
|
606
|
+
assertExactKeys(runtime, ["schemaVersion", "runtimeId"], "Inference Control Trust runtime");
|
|
607
|
+
assertExactKeys(sample.rollbackAnchor, [
|
|
608
|
+
"schemaVersion",
|
|
609
|
+
"stateId",
|
|
610
|
+
"requiredGeneration",
|
|
611
|
+
"requiredSnapshotDigest",
|
|
612
|
+
"minimumLogicalHighWaterMs",
|
|
613
|
+
"protectorBindingDigest",
|
|
614
|
+
], "Inference Control Trust rollback anchor");
|
|
615
|
+
const metadata = runtime && typeof runtime === "object"
|
|
616
|
+
? verifiedTrustEligibilityRuntimes.get(runtime)
|
|
617
|
+
: undefined;
|
|
618
|
+
const latest = metadata
|
|
619
|
+
? latestTrustEligibilitySnapshotByStateId.get(metadata.stateId)
|
|
620
|
+
: undefined;
|
|
621
|
+
if (!metadata ||
|
|
622
|
+
!latest ||
|
|
623
|
+
runtime.schemaVersion !== 1 ||
|
|
624
|
+
runtime.runtimeId !== metadata.runtimeId ||
|
|
625
|
+
metadata.runtimeSourceBindingDigest !==
|
|
626
|
+
integration.runtimeSourceBindingDigest ||
|
|
627
|
+
metadata.rollbackAnchor.protectorBindingDigest !==
|
|
628
|
+
integration.protectorBindingDigest ||
|
|
629
|
+
!rollbackAnchorMatches(sample.rollbackAnchor, metadata.rollbackAnchor) ||
|
|
630
|
+
latest.generation !== metadata.generation ||
|
|
631
|
+
latest.snapshotDigest !== metadata.snapshotDigest)
|
|
632
|
+
return unavailableTrustStateEligibility("state_conflict");
|
|
633
|
+
state = validateEvidenceTrustStateV1(metadata.state);
|
|
634
|
+
logicalTimeMs = metadata.logicalTimeMs;
|
|
635
|
+
if (state.stateId !== metadata.stateId ||
|
|
636
|
+
logicalTimeMs < state.logicalTimeHighWaterMs)
|
|
637
|
+
return unavailableTrustStateEligibility("logical_time_rollback");
|
|
638
|
+
}
|
|
639
|
+
catch {
|
|
640
|
+
return unavailableTrustStateEligibility("state_conflict");
|
|
641
|
+
}
|
|
642
|
+
try {
|
|
643
|
+
if (!inferenceTrustBindingsAreCurrent(state, config, logicalTimeMs))
|
|
644
|
+
return unavailableTrustStateEligibility("dependency_binding_invalid");
|
|
645
|
+
const subjectDigest = digestSubjectV1(config.trustSubject);
|
|
646
|
+
const trustScopeDigest = digestScopeV1(config.trustScope);
|
|
647
|
+
const profileKey = digestTrustProfileKeyV1({
|
|
648
|
+
tenantId: config.controlTenantId,
|
|
649
|
+
subjectDigest,
|
|
650
|
+
scopeDigest: trustScopeDigest,
|
|
651
|
+
policyDigest: config.policyDigest,
|
|
652
|
+
});
|
|
653
|
+
const profileHead = state.profileHeads.find((head) => head.profileKey === profileKey);
|
|
654
|
+
if (!profileHead)
|
|
655
|
+
return unavailableTrustStateEligibility("profile_unavailable");
|
|
656
|
+
const decision = evaluateTrustEligibilityV1(state, createTrustEligibilityRequestV1({
|
|
657
|
+
schemaVersion: 1,
|
|
658
|
+
tenantId: config.controlTenantId,
|
|
659
|
+
subject: config.trustSubject,
|
|
660
|
+
subjectDigest,
|
|
661
|
+
scope: config.trustScope,
|
|
662
|
+
scopeDigest: trustScopeDigest,
|
|
663
|
+
policyId: config.policyId,
|
|
664
|
+
policyVersion: config.policyVersion,
|
|
665
|
+
policyDigest: config.policyDigest,
|
|
666
|
+
profileId: profileHead.profileId,
|
|
667
|
+
profileDigest: profileHead.profileDigest,
|
|
668
|
+
maximumProfileAgeMs: config.maximumProfileAgeMs,
|
|
669
|
+
requirements: config.requirements,
|
|
670
|
+
}), logicalTimeMs);
|
|
671
|
+
return trustStateDecisionResult(decision);
|
|
672
|
+
}
|
|
673
|
+
catch {
|
|
674
|
+
return unavailableTrustStateEligibility("profile_unavailable");
|
|
675
|
+
}
|
|
676
|
+
}
|
|
677
|
+
function createAcceptedOutcomeClaimCandidate(input, terminalReferences) {
|
|
678
|
+
if (input.schemaVersion !== 1)
|
|
679
|
+
throw new TypeError("accepted_outcome_invalid");
|
|
680
|
+
assertAcceptedAssessment(input.assessmentRequest, input.assessment);
|
|
681
|
+
assertControlDigest(input.assessorBindingDigest, "assessorBindingDigest");
|
|
682
|
+
assertControlDigest(input.dependencyBindingDigest, "dependencyBindingDigest");
|
|
683
|
+
const mapping = validateClaimMapping(input.mapping);
|
|
684
|
+
const scopeDigest = controlScopeDigest(input.assessmentRequest.scope);
|
|
685
|
+
const assessmentDigest = digestControlJsonV1("trace", {
|
|
686
|
+
schemaVersion: 1,
|
|
687
|
+
assessmentId: input.assessment.assessmentId,
|
|
688
|
+
assessmentRequestId: input.assessment.assessmentRequestId,
|
|
689
|
+
requestGeneration: input.assessment.requestGeneration,
|
|
690
|
+
targetDigest: input.assessment.targetDigest,
|
|
691
|
+
checkpoint: input.assessment.checkpoint,
|
|
692
|
+
disposition: input.assessment.disposition,
|
|
693
|
+
});
|
|
694
|
+
const reasonCodesDigest = digestControlJsonV1("trace", [
|
|
695
|
+
...input.assessment.reasonCodes,
|
|
696
|
+
]);
|
|
697
|
+
const references = [
|
|
698
|
+
controlReference("assessment", input.assessment.assessmentId, assessmentDigest),
|
|
699
|
+
createAssessmentRequestReferenceV1(input.assessmentRequest),
|
|
700
|
+
controlReference("assessor_binding", input.assessment.assessorId, input.assessorBindingDigest),
|
|
701
|
+
controlReference("control_scope", input.assessmentRequest.runId, scopeDigest),
|
|
702
|
+
controlReference("dependency_binding", input.assessment.policyId, input.dependencyBindingDigest),
|
|
703
|
+
controlReference("outcome_reason_codes", input.assessment.assessmentId, reasonCodesDigest),
|
|
704
|
+
trustReference("trust_mapping", mapping.mappingId, mapping.mappingDigest),
|
|
705
|
+
...terminalReferences,
|
|
706
|
+
].sort(referenceOrder);
|
|
707
|
+
return createEvidenceClaimV1({
|
|
708
|
+
schemaVersion: 1,
|
|
709
|
+
sourceId: mapping.sourceId,
|
|
710
|
+
sourceKind: "local",
|
|
711
|
+
causationId: input.assessment.assessmentId,
|
|
712
|
+
subject: mapping.subject,
|
|
713
|
+
scope: mapping.scope,
|
|
714
|
+
criterionId: mapping.criterionId,
|
|
715
|
+
outcome: mapping.outcome,
|
|
716
|
+
content: null,
|
|
717
|
+
basisReferences: references,
|
|
718
|
+
observedAt: input.observedAt,
|
|
719
|
+
});
|
|
720
|
+
}
|
|
721
|
+
function assertActionEligibilityTargetBinding(target, input) {
|
|
722
|
+
if (target.operation !== "action" ||
|
|
723
|
+
target.targetDigest !== input.permit.actionDigest ||
|
|
724
|
+
target.scopeDigest !== input.permit.scopeDigest ||
|
|
725
|
+
target.tenantId !== input.context.tenant.tenantId ||
|
|
726
|
+
target.runId !== input.context.runId)
|
|
727
|
+
throw new Error("trust_eligibility_target_mismatch");
|
|
728
|
+
}
|
|
729
|
+
function assertOutboundMessageEligibilityTargetBinding(target, input) {
|
|
730
|
+
const message = input.message;
|
|
731
|
+
const { messageDigest: declaredMessageDigest, ...unsignedMessage } = message;
|
|
732
|
+
const actualMessageDigest = outboundMessageDigest(unsignedMessage);
|
|
733
|
+
const actualScopeDigest = scopeDigest(message.scope);
|
|
734
|
+
if (target.operation !== "outbound_message" ||
|
|
735
|
+
target.targetDigest !== actualMessageDigest ||
|
|
736
|
+
target.scopeDigest !== actualScopeDigest ||
|
|
737
|
+
target.tenantId !== message.tenantId ||
|
|
738
|
+
target.runId !== message.runId ||
|
|
739
|
+
declaredMessageDigest !== actualMessageDigest ||
|
|
740
|
+
input.permit.messageDigest !== actualMessageDigest ||
|
|
741
|
+
input.permit.scopeDigest !== actualScopeDigest)
|
|
742
|
+
throw new Error("trust_eligibility_target_mismatch");
|
|
743
|
+
}
|
|
744
|
+
function requireOrObserveEligibility(integration, target, bindingDigest) {
|
|
745
|
+
validateTarget(target);
|
|
746
|
+
let status = "unavailable";
|
|
747
|
+
try {
|
|
748
|
+
const decision = integration.resolver.resolve(target);
|
|
749
|
+
status = eligibilityStatus(integration, target, decision) ?? "mismatch";
|
|
750
|
+
}
|
|
751
|
+
catch {
|
|
752
|
+
status = "unavailable";
|
|
753
|
+
}
|
|
754
|
+
try {
|
|
755
|
+
integration.onDiagnostic?.(Object.freeze({
|
|
756
|
+
schemaVersion: 1,
|
|
757
|
+
operation: target.operation,
|
|
758
|
+
mode: integration.policy.mode,
|
|
759
|
+
status,
|
|
760
|
+
bindingDigest,
|
|
761
|
+
}));
|
|
762
|
+
}
|
|
763
|
+
catch {
|
|
764
|
+
// Diagnostics are observational and cannot influence delegation.
|
|
765
|
+
}
|
|
766
|
+
if (integration.policy.mode === "restrict" && status !== "eligible")
|
|
767
|
+
throw new Error("trust_eligibility_restricted");
|
|
768
|
+
}
|
|
769
|
+
function eligibilityStatus(integration, target, decision) {
|
|
770
|
+
if (!decision || decision.schemaVersion !== 1)
|
|
771
|
+
return undefined;
|
|
772
|
+
if (!isEligibilityStatus(decision.status))
|
|
773
|
+
return undefined;
|
|
774
|
+
if (decision.policyDigest !== integration.policy.policyDigest ||
|
|
775
|
+
decision.resolverDigest !== integration.resolver.resolverDigest ||
|
|
776
|
+
decision.mappingDigest !== integration.mapping.mappingDigest ||
|
|
777
|
+
decision.scopeDigest !== target.scopeDigest ||
|
|
778
|
+
decision.targetDigest !== target.targetDigest)
|
|
779
|
+
return "mismatch";
|
|
780
|
+
return decision.status;
|
|
781
|
+
}
|
|
782
|
+
function assertAcceptedAssessment(request, assessment) {
|
|
783
|
+
if (request.status !== "accepted" ||
|
|
784
|
+
assessment.disposition !== "allow" ||
|
|
785
|
+
request.assessmentRequestId !== assessment.assessmentRequestId ||
|
|
786
|
+
request.requestGeneration !== assessment.requestGeneration ||
|
|
787
|
+
request.runId !== assessment.runId ||
|
|
788
|
+
request.tenantId !== assessment.tenantId ||
|
|
789
|
+
request.policyId !== assessment.policyId ||
|
|
790
|
+
request.policyVersion !== assessment.policyVersion ||
|
|
791
|
+
request.checkpoint !== assessment.checkpoint ||
|
|
792
|
+
request.assessorId !== assessment.assessorId ||
|
|
793
|
+
request.assessorVersion !== assessment.assessorVersion ||
|
|
794
|
+
request.targetKind !== assessment.targetKind ||
|
|
795
|
+
request.targetDigest !== assessment.targetDigest ||
|
|
796
|
+
request.zoneDigest !== assessment.zoneDigest ||
|
|
797
|
+
request.provenanceDigest !== assessment.provenanceDigest ||
|
|
798
|
+
controlScopeDigest(request.scope) !== controlScopeDigest(assessment.scope))
|
|
799
|
+
throw new Error("accepted_outcome_mismatch");
|
|
800
|
+
}
|
|
801
|
+
function snapshotIntegration(integration) {
|
|
802
|
+
validateIntegration(integration);
|
|
803
|
+
const resolver = integration.resolver;
|
|
804
|
+
const onDiagnostic = integration.onDiagnostic;
|
|
805
|
+
return Object.freeze({
|
|
806
|
+
policy: Object.freeze({ ...integration.policy }),
|
|
807
|
+
resolver: Object.freeze({
|
|
808
|
+
resolverId: resolver.resolverId,
|
|
809
|
+
resolverVersion: resolver.resolverVersion,
|
|
810
|
+
resolverDigest: resolver.resolverDigest,
|
|
811
|
+
resolve: resolver.resolve.bind(resolver),
|
|
812
|
+
}),
|
|
813
|
+
mapping: Object.freeze({ ...integration.mapping }),
|
|
814
|
+
onDiagnostic: onDiagnostic === undefined ? undefined : onDiagnostic.bind(integration),
|
|
815
|
+
});
|
|
816
|
+
}
|
|
817
|
+
function snapshotTrustModelBoundary(boundary) {
|
|
818
|
+
validateTrustModelBoundaryIdentity(boundary);
|
|
819
|
+
if (typeof boundary.run !== "function")
|
|
820
|
+
throw new TypeError("model_boundary_invalid");
|
|
821
|
+
return Object.freeze({
|
|
822
|
+
modelBoundaryId: boundary.modelBoundaryId,
|
|
823
|
+
modelBoundaryVersion: boundary.modelBoundaryVersion,
|
|
824
|
+
implementationDigest: boundary.implementationDigest,
|
|
825
|
+
run: boundary.run.bind(boundary),
|
|
826
|
+
});
|
|
827
|
+
}
|
|
828
|
+
function snapshotActionDispatcher(dispatcher) {
|
|
829
|
+
validateActionDispatcherIdentity(dispatcher);
|
|
830
|
+
if (typeof dispatcher.dispatch !== "function")
|
|
831
|
+
throw new TypeError("dispatcher_invalid");
|
|
832
|
+
return Object.freeze({
|
|
833
|
+
dispatcherId: dispatcher.dispatcherId,
|
|
834
|
+
dispatcherVersion: dispatcher.dispatcherVersion,
|
|
835
|
+
fencingMode: dispatcher.fencingMode,
|
|
836
|
+
dispatch: dispatcher.dispatch.bind(dispatcher),
|
|
837
|
+
});
|
|
838
|
+
}
|
|
839
|
+
function snapshotOutboundMessageDispatcher(dispatcher) {
|
|
840
|
+
validateMessageDispatcherIdentity(dispatcher);
|
|
841
|
+
if (typeof dispatcher.send !== "function")
|
|
842
|
+
throw new TypeError("dispatcher_invalid");
|
|
843
|
+
return Object.freeze({
|
|
844
|
+
dispatcherId: dispatcher.dispatcherId,
|
|
845
|
+
dispatcherVersion: dispatcher.dispatcherVersion,
|
|
846
|
+
dispatcherDigest: dispatcher.dispatcherDigest,
|
|
847
|
+
fencingMode: dispatcher.fencingMode,
|
|
848
|
+
send: dispatcher.send.bind(dispatcher),
|
|
849
|
+
});
|
|
850
|
+
}
|
|
851
|
+
function digestTrustEligibilityBindingFromSnapshot(integration, baseBindingDigest) {
|
|
852
|
+
return digestTrustJsonV1("dependency-binding", {
|
|
853
|
+
schemaVersion: 1,
|
|
854
|
+
policyDigest: integration.policy.policyDigest,
|
|
855
|
+
resolverDigest: integration.resolver.resolverDigest,
|
|
856
|
+
mappingDigest: integration.mapping.mappingDigest,
|
|
857
|
+
baseBindingDigest,
|
|
858
|
+
});
|
|
859
|
+
}
|
|
860
|
+
function validateClaimMapping(mapping) {
|
|
861
|
+
const expected = createInferenceControlTrustClaimMappingV1({
|
|
862
|
+
schemaVersion: mapping.schemaVersion,
|
|
863
|
+
mappingId: mapping.mappingId,
|
|
864
|
+
mappingVersion: mapping.mappingVersion,
|
|
865
|
+
sourceId: mapping.sourceId,
|
|
866
|
+
subject: mapping.subject,
|
|
867
|
+
scope: mapping.scope,
|
|
868
|
+
criterionId: mapping.criterionId,
|
|
869
|
+
outcome: mapping.outcome,
|
|
870
|
+
});
|
|
871
|
+
if (expected.mappingDigest !== mapping.mappingDigest)
|
|
872
|
+
throw new Error("trust_mapping_mismatch");
|
|
873
|
+
return expected;
|
|
874
|
+
}
|
|
875
|
+
function controlScopeDigest(scope) {
|
|
876
|
+
return digestControlJsonV1("scope", scope);
|
|
877
|
+
}
|
|
878
|
+
function controlReference(referenceType, referenceId, referenceDigest) {
|
|
879
|
+
return {
|
|
880
|
+
schemaVersion: 1,
|
|
881
|
+
kind: "control_record",
|
|
882
|
+
referenceType,
|
|
883
|
+
referenceId,
|
|
884
|
+
referenceDigest,
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
function trustReference(referenceType, referenceId, referenceDigest) {
|
|
888
|
+
return {
|
|
889
|
+
schemaVersion: 1,
|
|
890
|
+
kind: "external",
|
|
891
|
+
referenceType,
|
|
892
|
+
referenceId,
|
|
893
|
+
referenceDigest,
|
|
894
|
+
};
|
|
895
|
+
}
|
|
896
|
+
function referenceOrder(left, right) {
|
|
897
|
+
const leftKey = `${left.kind}\u0000${left.referenceType}\u0000${left.referenceId}\u0000${left.referenceDigest}`;
|
|
898
|
+
const rightKey = `${right.kind}\u0000${right.referenceType}\u0000${right.referenceId}\u0000${right.referenceDigest}`;
|
|
899
|
+
return leftKey < rightKey ? -1 : leftKey > rightKey ? 1 : 0;
|
|
900
|
+
}
|
|
901
|
+
function validateIntegration(integration) {
|
|
902
|
+
const { policy, resolver, mapping } = integration;
|
|
903
|
+
assertIdentifier(policy.policyId, "policyId");
|
|
904
|
+
assertPositiveInteger(policy.policyVersion, "policyVersion");
|
|
905
|
+
assertTrustDigest(policy.policyDigest, "policyDigest");
|
|
906
|
+
if (policy.mode !== "observe" && policy.mode !== "restrict")
|
|
907
|
+
throw new TypeError("trust_policy_invalid");
|
|
908
|
+
assertIdentifier(resolver.resolverId, "resolverId");
|
|
909
|
+
assertPositiveInteger(resolver.resolverVersion, "resolverVersion");
|
|
910
|
+
assertTrustDigest(resolver.resolverDigest, "resolverDigest");
|
|
911
|
+
if (typeof resolver.resolve !== "function")
|
|
912
|
+
throw new TypeError("trust_resolver_invalid");
|
|
913
|
+
assertIdentifier(mapping.mappingId, "mappingId");
|
|
914
|
+
assertPositiveInteger(mapping.mappingVersion, "mappingVersion");
|
|
915
|
+
assertTrustDigest(mapping.mappingDigest, "mappingDigest");
|
|
916
|
+
if (integration.onDiagnostic !== undefined &&
|
|
917
|
+
typeof integration.onDiagnostic !== "function")
|
|
918
|
+
throw new TypeError("trust_diagnostic_invalid");
|
|
919
|
+
}
|
|
920
|
+
function validateTrustModelBoundaryIdentity(boundary) {
|
|
921
|
+
assertIdentifier(boundary.modelBoundaryId, "modelBoundaryId");
|
|
922
|
+
assertPositiveInteger(boundary.modelBoundaryVersion, "modelBoundaryVersion");
|
|
923
|
+
assertTrustDigest(boundary.implementationDigest, "implementationDigest");
|
|
924
|
+
}
|
|
925
|
+
function validateActionDispatcherIdentity(dispatcher) {
|
|
926
|
+
assertIdentifier(dispatcher.dispatcherId, "dispatcherId");
|
|
927
|
+
assertPositiveInteger(dispatcher.dispatcherVersion, "dispatcherVersion");
|
|
928
|
+
if (dispatcher.fencingMode !== "local_only" &&
|
|
929
|
+
dispatcher.fencingMode !== "downstream_atomic")
|
|
930
|
+
throw new TypeError("dispatcher_invalid");
|
|
931
|
+
}
|
|
932
|
+
function validateMessageDispatcherIdentity(dispatcher) {
|
|
933
|
+
validateActionDispatcherIdentity(dispatcher);
|
|
934
|
+
assertControlDigest(dispatcher.dispatcherDigest, "dispatcherDigest");
|
|
935
|
+
}
|
|
936
|
+
function validateTarget(target) {
|
|
937
|
+
if (target.schemaVersion !== 1 ||
|
|
938
|
+
!["model", "action", "outbound_message"].includes(target.operation))
|
|
939
|
+
throw new TypeError("trust_eligibility_target_invalid");
|
|
940
|
+
assertIdentifier(target.tenantId, "tenantId");
|
|
941
|
+
assertIdentifier(target.runId, "runId");
|
|
942
|
+
assertControlDigest(target.scopeDigest, "scopeDigest");
|
|
943
|
+
assertControlDigest(target.targetDigest, "targetDigest");
|
|
944
|
+
}
|
|
945
|
+
function snapshotTrustEligibilityTarget(target) {
|
|
946
|
+
if (!target || typeof target !== "object")
|
|
947
|
+
throw new TypeError("trust_eligibility_target_invalid");
|
|
948
|
+
const snapshot = Object.freeze({
|
|
949
|
+
schemaVersion: target.schemaVersion,
|
|
950
|
+
operation: target.operation,
|
|
951
|
+
tenantId: target.tenantId,
|
|
952
|
+
runId: target.runId,
|
|
953
|
+
scopeDigest: target.scopeDigest,
|
|
954
|
+
targetDigest: target.targetDigest,
|
|
955
|
+
});
|
|
956
|
+
validateTarget(snapshot);
|
|
957
|
+
return snapshot;
|
|
958
|
+
}
|
|
959
|
+
function isEligibilityStatus(value) {
|
|
960
|
+
return (typeof value === "string" &&
|
|
961
|
+
[
|
|
962
|
+
"eligible",
|
|
963
|
+
"restricted",
|
|
964
|
+
"unavailable",
|
|
965
|
+
"stale",
|
|
966
|
+
"mismatch",
|
|
967
|
+
"quarantined",
|
|
968
|
+
].includes(value));
|
|
969
|
+
}
|
|
970
|
+
function assertIdentifier(value, label) {
|
|
971
|
+
if (typeof value !== "string" ||
|
|
972
|
+
value.length === 0 ||
|
|
973
|
+
value.length > 256 ||
|
|
974
|
+
/[\u0000-\u001f\u007f]/u.test(value))
|
|
975
|
+
throw new TypeError(`${label}_invalid`);
|
|
976
|
+
}
|
|
977
|
+
function assertPositiveInteger(value, label) {
|
|
978
|
+
if (!Number.isSafeInteger(value) || value < 1)
|
|
979
|
+
throw new TypeError(`${label}_invalid`);
|
|
980
|
+
}
|
|
981
|
+
function assertControlDigest(value, label) {
|
|
982
|
+
if (typeof value !== "string" || !/^sha256:[0-9a-f]{64}$/u.test(value))
|
|
983
|
+
throw new TypeError(`${label}_invalid`);
|
|
984
|
+
}
|
|
985
|
+
function assertTrustDigest(value, label) {
|
|
986
|
+
if (typeof value !== "string" || !/^[0-9a-f]{64}$/u.test(value))
|
|
987
|
+
throw new TypeError(`${label}_invalid`);
|
|
988
|
+
}
|
|
989
|
+
//# sourceMappingURL=trust.js.map
|