@kya-os/contracts 1.10.0 → 1.12.1
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/agentshield-api/schemas.d.ts +14 -0
- package/dist/cli.d.ts +93 -0
- package/dist/cli.js +108 -1
- package/dist/handshake.d.ts +77 -0
- package/dist/handshake.js +73 -1
- package/dist/index.js +1 -0
- package/dist/molti/schemas.d.ts +1 -1
- package/dist/policy/schemas.d.ts +160 -160
- package/dist/proof.d.ts +30 -0
- package/dist/proof.js +6 -0
- package/dist/reputation/api.d.ts +0 -17
- package/dist/reputation/api.js +1 -21
- package/dist/review/canonicalize.d.ts +87 -0
- package/dist/review/canonicalize.js +122 -0
- package/dist/review/fix-attestation.d.ts +151 -0
- package/dist/review/fix-attestation.js +105 -0
- package/dist/review/index.d.ts +9 -0
- package/dist/review/index.js +25 -0
- package/dist/review/registry.d.ts +328 -0
- package/dist/review/registry.js +269 -0
- package/dist/review/schemas.d.ts +249 -0
- package/dist/review/schemas.js +102 -0
- package/dist/review/verify.d.ts +37 -0
- package/dist/review/verify.js +58 -0
- package/dist/test.d.ts +12 -12
- package/dist/vault/schemas.d.ts +2 -2
- package/package.json +9 -1
package/dist/proof.d.ts
CHANGED
|
@@ -39,6 +39,8 @@ export declare const ProofMetaSchema: z.ZodObject<{
|
|
|
39
39
|
scopeId: z.ZodOptional<z.ZodString>;
|
|
40
40
|
delegationRef: z.ZodOptional<z.ZodString>;
|
|
41
41
|
clientDid: z.ZodOptional<z.ZodString>;
|
|
42
|
+
toolName: z.ZodOptional<z.ZodString>;
|
|
43
|
+
submissionOutcome: z.ZodOptional<z.ZodEnum<["success", "failure", "denied"]>>;
|
|
42
44
|
}, "strip", z.ZodTypeAny, {
|
|
43
45
|
did: string;
|
|
44
46
|
kid: string;
|
|
@@ -52,6 +54,8 @@ export declare const ProofMetaSchema: z.ZodObject<{
|
|
|
52
54
|
delegationRef?: string | undefined;
|
|
53
55
|
outcome?: "allowed" | "denied" | "step_up_required" | "needs_authorization" | undefined;
|
|
54
56
|
scopeId?: string | undefined;
|
|
57
|
+
toolName?: string | undefined;
|
|
58
|
+
submissionOutcome?: "success" | "denied" | "failure" | undefined;
|
|
55
59
|
}, {
|
|
56
60
|
did: string;
|
|
57
61
|
kid: string;
|
|
@@ -65,6 +69,8 @@ export declare const ProofMetaSchema: z.ZodObject<{
|
|
|
65
69
|
delegationRef?: string | undefined;
|
|
66
70
|
outcome?: "allowed" | "denied" | "step_up_required" | "needs_authorization" | undefined;
|
|
67
71
|
scopeId?: string | undefined;
|
|
72
|
+
toolName?: string | undefined;
|
|
73
|
+
submissionOutcome?: "success" | "denied" | "failure" | undefined;
|
|
68
74
|
}>;
|
|
69
75
|
export declare const DetachedProofSchema: z.ZodObject<{
|
|
70
76
|
jws: z.ZodString;
|
|
@@ -81,6 +87,8 @@ export declare const DetachedProofSchema: z.ZodObject<{
|
|
|
81
87
|
scopeId: z.ZodOptional<z.ZodString>;
|
|
82
88
|
delegationRef: z.ZodOptional<z.ZodString>;
|
|
83
89
|
clientDid: z.ZodOptional<z.ZodString>;
|
|
90
|
+
toolName: z.ZodOptional<z.ZodString>;
|
|
91
|
+
submissionOutcome: z.ZodOptional<z.ZodEnum<["success", "failure", "denied"]>>;
|
|
84
92
|
}, "strip", z.ZodTypeAny, {
|
|
85
93
|
did: string;
|
|
86
94
|
kid: string;
|
|
@@ -94,6 +102,8 @@ export declare const DetachedProofSchema: z.ZodObject<{
|
|
|
94
102
|
delegationRef?: string | undefined;
|
|
95
103
|
outcome?: "allowed" | "denied" | "step_up_required" | "needs_authorization" | undefined;
|
|
96
104
|
scopeId?: string | undefined;
|
|
105
|
+
toolName?: string | undefined;
|
|
106
|
+
submissionOutcome?: "success" | "denied" | "failure" | undefined;
|
|
97
107
|
}, {
|
|
98
108
|
did: string;
|
|
99
109
|
kid: string;
|
|
@@ -107,6 +117,8 @@ export declare const DetachedProofSchema: z.ZodObject<{
|
|
|
107
117
|
delegationRef?: string | undefined;
|
|
108
118
|
outcome?: "allowed" | "denied" | "step_up_required" | "needs_authorization" | undefined;
|
|
109
119
|
scopeId?: string | undefined;
|
|
120
|
+
toolName?: string | undefined;
|
|
121
|
+
submissionOutcome?: "success" | "denied" | "failure" | undefined;
|
|
110
122
|
}>;
|
|
111
123
|
}, "strip", z.ZodTypeAny, {
|
|
112
124
|
jws: string;
|
|
@@ -123,6 +135,8 @@ export declare const DetachedProofSchema: z.ZodObject<{
|
|
|
123
135
|
delegationRef?: string | undefined;
|
|
124
136
|
outcome?: "allowed" | "denied" | "step_up_required" | "needs_authorization" | undefined;
|
|
125
137
|
scopeId?: string | undefined;
|
|
138
|
+
toolName?: string | undefined;
|
|
139
|
+
submissionOutcome?: "success" | "denied" | "failure" | undefined;
|
|
126
140
|
};
|
|
127
141
|
}, {
|
|
128
142
|
jws: string;
|
|
@@ -139,6 +153,8 @@ export declare const DetachedProofSchema: z.ZodObject<{
|
|
|
139
153
|
delegationRef?: string | undefined;
|
|
140
154
|
outcome?: "allowed" | "denied" | "step_up_required" | "needs_authorization" | undefined;
|
|
141
155
|
scopeId?: string | undefined;
|
|
156
|
+
toolName?: string | undefined;
|
|
157
|
+
submissionOutcome?: "success" | "denied" | "failure" | undefined;
|
|
142
158
|
};
|
|
143
159
|
}>;
|
|
144
160
|
export declare const CanonicalHashesSchema: z.ZodObject<{
|
|
@@ -280,6 +296,8 @@ export declare const ProofSubmissionRequestSchema: z.ZodObject<{
|
|
|
280
296
|
scopeId: z.ZodOptional<z.ZodString>;
|
|
281
297
|
delegationRef: z.ZodOptional<z.ZodString>;
|
|
282
298
|
clientDid: z.ZodOptional<z.ZodString>;
|
|
299
|
+
toolName: z.ZodOptional<z.ZodString>;
|
|
300
|
+
submissionOutcome: z.ZodOptional<z.ZodEnum<["success", "failure", "denied"]>>;
|
|
283
301
|
}, "strip", z.ZodTypeAny, {
|
|
284
302
|
did: string;
|
|
285
303
|
kid: string;
|
|
@@ -293,6 +311,8 @@ export declare const ProofSubmissionRequestSchema: z.ZodObject<{
|
|
|
293
311
|
delegationRef?: string | undefined;
|
|
294
312
|
outcome?: "allowed" | "denied" | "step_up_required" | "needs_authorization" | undefined;
|
|
295
313
|
scopeId?: string | undefined;
|
|
314
|
+
toolName?: string | undefined;
|
|
315
|
+
submissionOutcome?: "success" | "denied" | "failure" | undefined;
|
|
296
316
|
}, {
|
|
297
317
|
did: string;
|
|
298
318
|
kid: string;
|
|
@@ -306,6 +326,8 @@ export declare const ProofSubmissionRequestSchema: z.ZodObject<{
|
|
|
306
326
|
delegationRef?: string | undefined;
|
|
307
327
|
outcome?: "allowed" | "denied" | "step_up_required" | "needs_authorization" | undefined;
|
|
308
328
|
scopeId?: string | undefined;
|
|
329
|
+
toolName?: string | undefined;
|
|
330
|
+
submissionOutcome?: "success" | "denied" | "failure" | undefined;
|
|
309
331
|
}>;
|
|
310
332
|
}, "strip", z.ZodTypeAny, {
|
|
311
333
|
jws: string;
|
|
@@ -322,6 +344,8 @@ export declare const ProofSubmissionRequestSchema: z.ZodObject<{
|
|
|
322
344
|
delegationRef?: string | undefined;
|
|
323
345
|
outcome?: "allowed" | "denied" | "step_up_required" | "needs_authorization" | undefined;
|
|
324
346
|
scopeId?: string | undefined;
|
|
347
|
+
toolName?: string | undefined;
|
|
348
|
+
submissionOutcome?: "success" | "denied" | "failure" | undefined;
|
|
325
349
|
};
|
|
326
350
|
}, {
|
|
327
351
|
jws: string;
|
|
@@ -338,6 +362,8 @@ export declare const ProofSubmissionRequestSchema: z.ZodObject<{
|
|
|
338
362
|
delegationRef?: string | undefined;
|
|
339
363
|
outcome?: "allowed" | "denied" | "step_up_required" | "needs_authorization" | undefined;
|
|
340
364
|
scopeId?: string | undefined;
|
|
365
|
+
toolName?: string | undefined;
|
|
366
|
+
submissionOutcome?: "success" | "denied" | "failure" | undefined;
|
|
341
367
|
};
|
|
342
368
|
}>, "many">;
|
|
343
369
|
context: z.ZodOptional<z.ZodObject<{
|
|
@@ -397,6 +423,8 @@ export declare const ProofSubmissionRequestSchema: z.ZodObject<{
|
|
|
397
423
|
delegationRef?: string | undefined;
|
|
398
424
|
outcome?: "allowed" | "denied" | "step_up_required" | "needs_authorization" | undefined;
|
|
399
425
|
scopeId?: string | undefined;
|
|
426
|
+
toolName?: string | undefined;
|
|
427
|
+
submissionOutcome?: "success" | "denied" | "failure" | undefined;
|
|
400
428
|
};
|
|
401
429
|
}[];
|
|
402
430
|
delegation_id?: string | null | undefined;
|
|
@@ -427,6 +455,8 @@ export declare const ProofSubmissionRequestSchema: z.ZodObject<{
|
|
|
427
455
|
delegationRef?: string | undefined;
|
|
428
456
|
outcome?: "allowed" | "denied" | "step_up_required" | "needs_authorization" | undefined;
|
|
429
457
|
scopeId?: string | undefined;
|
|
458
|
+
toolName?: string | undefined;
|
|
459
|
+
submissionOutcome?: "success" | "denied" | "failure" | undefined;
|
|
430
460
|
};
|
|
431
461
|
}[];
|
|
432
462
|
delegation_id?: string | null | undefined;
|
package/dist/proof.js
CHANGED
|
@@ -52,6 +52,12 @@ exports.ProofMetaSchema = zod_1.z.object({
|
|
|
52
52
|
scopeId: zod_1.z.string().optional(),
|
|
53
53
|
delegationRef: zod_1.z.string().optional(),
|
|
54
54
|
clientDid: zod_1.z.string().optional(), // Optional for backward compatibility
|
|
55
|
+
toolName: zod_1.z.string().min(1).optional(),
|
|
56
|
+
// KTA's "did the tool call itself succeed" concept -- distinct from
|
|
57
|
+
// `outcome` above (the ratified DIF policy-decision field). Only at JWS
|
|
58
|
+
// payload serialization time does this get mapped onto the literal wire
|
|
59
|
+
// key `outcome`; the two never share a validation enum here.
|
|
60
|
+
submissionOutcome: zod_1.z.enum(["success", "failure", "denied"]).optional(),
|
|
55
61
|
});
|
|
56
62
|
exports.DetachedProofSchema = zod_1.z.object({
|
|
57
63
|
jws: zod_1.z.string().min(1), // Full compact JWS format (header.payload.signature)
|
package/dist/reputation/api.d.ts
CHANGED
|
@@ -2864,20 +2864,3 @@ export declare const REPUTATION_ENV_VARS: {
|
|
|
2864
2864
|
* Default reputation threshold for trust decisions
|
|
2865
2865
|
*/
|
|
2866
2866
|
export declare const DEFAULT_REPUTATION_THRESHOLD: 76;
|
|
2867
|
-
/**
|
|
2868
|
-
* Built-in API key for Know That AI Reputation Engine
|
|
2869
|
-
*
|
|
2870
|
-
* This key is maintained by @kya-os package maintainers.
|
|
2871
|
-
* Users do NOT need to configure this - registration with KTA is the gate.
|
|
2872
|
-
*
|
|
2873
|
-
* The key is used by ProofService to authenticate reputation updates.
|
|
2874
|
-
* Only agents with `identity.kta.registered === true` will send updates.
|
|
2875
|
-
*
|
|
2876
|
-
* SECURITY NOTE: This is a write-only key for submitting metrics.
|
|
2877
|
-
* It cannot be used to read reputation data without the agent's DID.
|
|
2878
|
-
*/
|
|
2879
|
-
/**
|
|
2880
|
-
* Production API key for MCP-I framework
|
|
2881
|
-
* Provisioned by Know That AI team
|
|
2882
|
-
*/
|
|
2883
|
-
export declare const BUILT_IN_REPUTATION_API_KEY: "Dn/qCC/0udWcGpdnt+IzA5GpWl9JF2g/avuBxlzB";
|
package/dist/reputation/api.js
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* Related Spec: MCP-I §5.2 (Reputation API)
|
|
15
15
|
*/
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.
|
|
17
|
+
exports.DEFAULT_REPUTATION_THRESHOLD = exports.REPUTATION_ENV_VARS = exports.REPUTATION_API_LIMITS = exports.REPUTATION_API_PATHS = exports.REGISTRY_URL = exports.ReputationUpdateRequestSchema = exports.ShadowRegisterResponseSchema = exports.ShadowRegisterRequestSchema = exports.ReputationErrorResponseSchema = exports.ReputationErrorCodeSchema = exports.GetReputationResponseSchema = exports.GetReputationRequestSchema = exports.PredictScoreChangeResponseSchema = exports.PredictScoreChangeRequestSchema = exports.BatchCalculateResponseSchema = exports.BatchResultItemSchema = exports.BatchCalculateRequestSchema = exports.CalculateReputationResponseSchema = exports.CalculateReputationRequestSchema = void 0;
|
|
18
18
|
exports.validateCalculateRequest = validateCalculateRequest;
|
|
19
19
|
exports.validateBatchCalculateRequest = validateBatchCalculateRequest;
|
|
20
20
|
exports.validatePredictRequest = validatePredictRequest;
|
|
@@ -395,23 +395,3 @@ exports.REPUTATION_ENV_VARS = {
|
|
|
395
395
|
* Default reputation threshold for trust decisions
|
|
396
396
|
*/
|
|
397
397
|
exports.DEFAULT_REPUTATION_THRESHOLD = 76;
|
|
398
|
-
// ============================================================================
|
|
399
|
-
// BUILT-IN API KEY
|
|
400
|
-
// ============================================================================
|
|
401
|
-
/**
|
|
402
|
-
* Built-in API key for Know That AI Reputation Engine
|
|
403
|
-
*
|
|
404
|
-
* This key is maintained by @kya-os package maintainers.
|
|
405
|
-
* Users do NOT need to configure this - registration with KTA is the gate.
|
|
406
|
-
*
|
|
407
|
-
* The key is used by ProofService to authenticate reputation updates.
|
|
408
|
-
* Only agents with `identity.kta.registered === true` will send updates.
|
|
409
|
-
*
|
|
410
|
-
* SECURITY NOTE: This is a write-only key for submitting metrics.
|
|
411
|
-
* It cannot be used to read reputation data without the agent's DID.
|
|
412
|
-
*/
|
|
413
|
-
/**
|
|
414
|
-
* Production API key for MCP-I framework
|
|
415
|
-
* Provisioned by Know That AI team
|
|
416
|
-
*/
|
|
417
|
-
exports.BUILT_IN_REPUTATION_API_KEY = "Dn/qCC/0udWcGpdnt+IzA5GpWl9JF2g/avuBxlzB";
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Review Verdict Canonicalization
|
|
3
|
+
*
|
|
4
|
+
* Canonical string construction and digest computation for review
|
|
5
|
+
* verdicts. Isolated from `schemas.ts` so the Zod schemas there stay free
|
|
6
|
+
* of Node.js dependencies — same split as `agent-deployment-utils.ts` vs
|
|
7
|
+
* `agent-deployment.ts` in this package — while every producer and
|
|
8
|
+
* verifier of a `ReviewVerdictBody` computes `findingsDigest` and the
|
|
9
|
+
* signing input from this one implementation. Two implementations that
|
|
10
|
+
* canonicalize differently produce signatures that verify on one side and
|
|
11
|
+
* fail on the other; that failure presents as a security incident, not a
|
|
12
|
+
* serialization bug, so do not re-derive this logic at a call site.
|
|
13
|
+
*
|
|
14
|
+
* **Node.js only** — uses `crypto.createHash`. This runs in the DIDComm
|
|
15
|
+
* reviewer/hub path (`packages/compute`), which is server-side. If an
|
|
16
|
+
* edge/browser producer or verifier is ever needed, port to
|
|
17
|
+
* `crypto.subtle.digest` and add a parity test asserting byte-identical
|
|
18
|
+
* output against this implementation (see `didToSlug`'s JSDoc in
|
|
19
|
+
* `agent-deployment-utils.ts` for the same caveat on the same tradeoff).
|
|
20
|
+
*
|
|
21
|
+
* ## Canonicalization scheme
|
|
22
|
+
*
|
|
23
|
+
* Every scalar field value is passed through `JSON.stringify`: strings are
|
|
24
|
+
* quoted and escaped (so a raw `\n`, `"`, or unicode control character in
|
|
25
|
+
* `summary`/`detail` can never appear literally in the output), and numbers
|
|
26
|
+
* print as plain digits. An **absent** optional field becomes the empty
|
|
27
|
+
* string `''`, which can never collide with a **present** field —
|
|
28
|
+
* `JSON.stringify` never returns `''` for a defined value (an empty string
|
|
29
|
+
* value stringifies to `'""'`, two characters). Because no token can ever
|
|
30
|
+
* contain a raw `\n` (JSON.stringify escapes it inside the string), joining
|
|
31
|
+
* tokens with `\n` is unambiguous: two different `findings` arrays can
|
|
32
|
+
* never produce the same token stream.
|
|
33
|
+
*
|
|
34
|
+
* Field order per finding: `path, line, severity, summary, detail`
|
|
35
|
+
* (`FINDING_FIELD_ORDER`), matching the order `ReviewFinding` is declared
|
|
36
|
+
* in `schemas.ts`.
|
|
37
|
+
*
|
|
38
|
+
* ### Worked example
|
|
39
|
+
*
|
|
40
|
+
* ```ts
|
|
41
|
+
* const findings: ReviewFinding[] = [
|
|
42
|
+
* { path: 'src/foo.ts', line: 42, severity: 'major', summary: 'off by one' },
|
|
43
|
+
* ];
|
|
44
|
+
* // detail is absent -> its token is ''
|
|
45
|
+
* // tokens: '"src/foo.ts"', '42', '"major"', '"off by one"', ''
|
|
46
|
+
* canonicalizeFindings(findings)
|
|
47
|
+
* // => '"src/foo.ts"\n42\n"major"\n"off by one"\n'
|
|
48
|
+
* computeFindingsDigest(findings)
|
|
49
|
+
* // => base64url(SHA-256('"src/foo.ts"\n42\n"major"\n"off by one"\n'))
|
|
50
|
+
* ```
|
|
51
|
+
*
|
|
52
|
+
* The verdict signing input follows the identical scheme over the fixed
|
|
53
|
+
* field order `[repo, prNumber, headSha, verdict, findingsDigest]`
|
|
54
|
+
* (`VERDICT_SIGNING_ORDER`) — see `getVerdictCanonicalSigningString`.
|
|
55
|
+
*
|
|
56
|
+
* @package @kya-os/contracts/review
|
|
57
|
+
*/
|
|
58
|
+
import type { ReviewFinding, ReviewSubject, ReviewVerdict } from './schemas.js';
|
|
59
|
+
/**
|
|
60
|
+
* Field order for a single `ReviewFinding`. Changing this order changes
|
|
61
|
+
* every existing `findingsDigest`.
|
|
62
|
+
*/
|
|
63
|
+
export declare const FINDING_FIELD_ORDER: readonly ["path", "line", "severity", "summary", "detail"];
|
|
64
|
+
/**
|
|
65
|
+
* Field order for the verdict signing input. Changing this order changes
|
|
66
|
+
* every existing signature.
|
|
67
|
+
*/
|
|
68
|
+
export declare const VERDICT_SIGNING_ORDER: readonly ["repo", "prNumber", "headSha", "verdict", "findingsDigest"];
|
|
69
|
+
/**
|
|
70
|
+
* Build the canonical string for a `findings` array. Exported so callers
|
|
71
|
+
* that need to reason about the exact bytes being hashed (tests, audit
|
|
72
|
+
* tooling) do not have to re-derive it.
|
|
73
|
+
*/
|
|
74
|
+
export declare function canonicalizeFindings(findings: readonly ReviewFinding[]): string;
|
|
75
|
+
/**
|
|
76
|
+
* Compute `findingsDigest`: base64url SHA-256 over the canonicalized
|
|
77
|
+
* `findings` array. This is the single implementation every producer and
|
|
78
|
+
* verifier of a `ReviewVerdictBody` must call.
|
|
79
|
+
*/
|
|
80
|
+
export declare function computeFindingsDigest(findings: readonly ReviewFinding[]): string;
|
|
81
|
+
/**
|
|
82
|
+
* Build the canonical signing input for a review verdict's detached JWS:
|
|
83
|
+
* `subject.repo`, `subject.prNumber`, `subject.headSha`, `verdict`,
|
|
84
|
+
* `findingsDigest`, in that fixed order (`VERDICT_SIGNING_ORDER`). The
|
|
85
|
+
* `proof` in `ReviewVerdictBody` signs exactly this string.
|
|
86
|
+
*/
|
|
87
|
+
export declare function getVerdictCanonicalSigningString(subject: ReviewSubject, verdict: ReviewVerdict, findingsDigest: string): string;
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Review Verdict Canonicalization
|
|
4
|
+
*
|
|
5
|
+
* Canonical string construction and digest computation for review
|
|
6
|
+
* verdicts. Isolated from `schemas.ts` so the Zod schemas there stay free
|
|
7
|
+
* of Node.js dependencies — same split as `agent-deployment-utils.ts` vs
|
|
8
|
+
* `agent-deployment.ts` in this package — while every producer and
|
|
9
|
+
* verifier of a `ReviewVerdictBody` computes `findingsDigest` and the
|
|
10
|
+
* signing input from this one implementation. Two implementations that
|
|
11
|
+
* canonicalize differently produce signatures that verify on one side and
|
|
12
|
+
* fail on the other; that failure presents as a security incident, not a
|
|
13
|
+
* serialization bug, so do not re-derive this logic at a call site.
|
|
14
|
+
*
|
|
15
|
+
* **Node.js only** — uses `crypto.createHash`. This runs in the DIDComm
|
|
16
|
+
* reviewer/hub path (`packages/compute`), which is server-side. If an
|
|
17
|
+
* edge/browser producer or verifier is ever needed, port to
|
|
18
|
+
* `crypto.subtle.digest` and add a parity test asserting byte-identical
|
|
19
|
+
* output against this implementation (see `didToSlug`'s JSDoc in
|
|
20
|
+
* `agent-deployment-utils.ts` for the same caveat on the same tradeoff).
|
|
21
|
+
*
|
|
22
|
+
* ## Canonicalization scheme
|
|
23
|
+
*
|
|
24
|
+
* Every scalar field value is passed through `JSON.stringify`: strings are
|
|
25
|
+
* quoted and escaped (so a raw `\n`, `"`, or unicode control character in
|
|
26
|
+
* `summary`/`detail` can never appear literally in the output), and numbers
|
|
27
|
+
* print as plain digits. An **absent** optional field becomes the empty
|
|
28
|
+
* string `''`, which can never collide with a **present** field —
|
|
29
|
+
* `JSON.stringify` never returns `''` for a defined value (an empty string
|
|
30
|
+
* value stringifies to `'""'`, two characters). Because no token can ever
|
|
31
|
+
* contain a raw `\n` (JSON.stringify escapes it inside the string), joining
|
|
32
|
+
* tokens with `\n` is unambiguous: two different `findings` arrays can
|
|
33
|
+
* never produce the same token stream.
|
|
34
|
+
*
|
|
35
|
+
* Field order per finding: `path, line, severity, summary, detail`
|
|
36
|
+
* (`FINDING_FIELD_ORDER`), matching the order `ReviewFinding` is declared
|
|
37
|
+
* in `schemas.ts`.
|
|
38
|
+
*
|
|
39
|
+
* ### Worked example
|
|
40
|
+
*
|
|
41
|
+
* ```ts
|
|
42
|
+
* const findings: ReviewFinding[] = [
|
|
43
|
+
* { path: 'src/foo.ts', line: 42, severity: 'major', summary: 'off by one' },
|
|
44
|
+
* ];
|
|
45
|
+
* // detail is absent -> its token is ''
|
|
46
|
+
* // tokens: '"src/foo.ts"', '42', '"major"', '"off by one"', ''
|
|
47
|
+
* canonicalizeFindings(findings)
|
|
48
|
+
* // => '"src/foo.ts"\n42\n"major"\n"off by one"\n'
|
|
49
|
+
* computeFindingsDigest(findings)
|
|
50
|
+
* // => base64url(SHA-256('"src/foo.ts"\n42\n"major"\n"off by one"\n'))
|
|
51
|
+
* ```
|
|
52
|
+
*
|
|
53
|
+
* The verdict signing input follows the identical scheme over the fixed
|
|
54
|
+
* field order `[repo, prNumber, headSha, verdict, findingsDigest]`
|
|
55
|
+
* (`VERDICT_SIGNING_ORDER`) — see `getVerdictCanonicalSigningString`.
|
|
56
|
+
*
|
|
57
|
+
* @package @kya-os/contracts/review
|
|
58
|
+
*/
|
|
59
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
60
|
+
exports.VERDICT_SIGNING_ORDER = exports.FINDING_FIELD_ORDER = void 0;
|
|
61
|
+
exports.canonicalizeFindings = canonicalizeFindings;
|
|
62
|
+
exports.computeFindingsDigest = computeFindingsDigest;
|
|
63
|
+
exports.getVerdictCanonicalSigningString = getVerdictCanonicalSigningString;
|
|
64
|
+
const crypto_1 = require("crypto");
|
|
65
|
+
/**
|
|
66
|
+
* Field order for a single `ReviewFinding`. Changing this order changes
|
|
67
|
+
* every existing `findingsDigest`.
|
|
68
|
+
*/
|
|
69
|
+
exports.FINDING_FIELD_ORDER = ['path', 'line', 'severity', 'summary', 'detail'];
|
|
70
|
+
/**
|
|
71
|
+
* Field order for the verdict signing input. Changing this order changes
|
|
72
|
+
* every existing signature.
|
|
73
|
+
*/
|
|
74
|
+
exports.VERDICT_SIGNING_ORDER = [
|
|
75
|
+
'repo',
|
|
76
|
+
'prNumber',
|
|
77
|
+
'headSha',
|
|
78
|
+
'verdict',
|
|
79
|
+
'findingsDigest',
|
|
80
|
+
];
|
|
81
|
+
/** A single canonicalization token: `''` for an absent field, else `JSON.stringify(value)`. */
|
|
82
|
+
function canonicalToken(value) {
|
|
83
|
+
return value === undefined ? '' : JSON.stringify(value);
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Build the canonical string for a `findings` array. Exported so callers
|
|
87
|
+
* that need to reason about the exact bytes being hashed (tests, audit
|
|
88
|
+
* tooling) do not have to re-derive it.
|
|
89
|
+
*/
|
|
90
|
+
function canonicalizeFindings(findings) {
|
|
91
|
+
const tokens = [];
|
|
92
|
+
for (const finding of findings) {
|
|
93
|
+
for (const field of exports.FINDING_FIELD_ORDER) {
|
|
94
|
+
tokens.push(canonicalToken(finding[field]));
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return tokens.join('\n');
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Compute `findingsDigest`: base64url SHA-256 over the canonicalized
|
|
101
|
+
* `findings` array. This is the single implementation every producer and
|
|
102
|
+
* verifier of a `ReviewVerdictBody` must call.
|
|
103
|
+
*/
|
|
104
|
+
function computeFindingsDigest(findings) {
|
|
105
|
+
return (0, crypto_1.createHash)('sha256').update(canonicalizeFindings(findings), 'utf8').digest('base64url');
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Build the canonical signing input for a review verdict's detached JWS:
|
|
109
|
+
* `subject.repo`, `subject.prNumber`, `subject.headSha`, `verdict`,
|
|
110
|
+
* `findingsDigest`, in that fixed order (`VERDICT_SIGNING_ORDER`). The
|
|
111
|
+
* `proof` in `ReviewVerdictBody` signs exactly this string.
|
|
112
|
+
*/
|
|
113
|
+
function getVerdictCanonicalSigningString(subject, verdict, findingsDigest) {
|
|
114
|
+
const values = {
|
|
115
|
+
repo: subject.repo,
|
|
116
|
+
prNumber: subject.prNumber,
|
|
117
|
+
headSha: subject.headSha,
|
|
118
|
+
verdict,
|
|
119
|
+
findingsDigest,
|
|
120
|
+
};
|
|
121
|
+
return exports.VERDICT_SIGNING_ORDER.map((field) => canonicalToken(values[field])).join('\n');
|
|
122
|
+
}
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fix Attestation Schema
|
|
3
|
+
*
|
|
4
|
+
* A fix attestation is the answer to a `ReviewVerdictBody` that requested
|
|
5
|
+
* changes: a verdict says "I reviewed `respondsToHeadSha` and found these
|
|
6
|
+
* findings"; a fix attestation says "I pushed `subject.fixedHeadSha` on
|
|
7
|
+
* `subject.repo`#`subject.prNumber` in response to that review." Posted as a
|
|
8
|
+
* signed PR comment (see `dco fix` in `@kya-os/cli`) so the audit trail
|
|
9
|
+
* closes the loop without trusting an unsigned comment body.
|
|
10
|
+
*
|
|
11
|
+
* Pure Zod schemas plus canonicalization only, no `node:crypto` — this
|
|
12
|
+
* module (and this package's `./review` barrel, which re-exports it) stays
|
|
13
|
+
* importable from an edge/browser runtime. The Ed25519 verify counterpart,
|
|
14
|
+
* `verifyFixAttestationProof`, lives in the separate `./review/verify`
|
|
15
|
+
* subpath instead of here for exactly that reason: it needs `node:crypto`
|
|
16
|
+
* for raw Ed25519 verification, and a subpath barrel must not force that
|
|
17
|
+
* dependency on every consumer who only needs the schema/signing-string
|
|
18
|
+
* (see `verify.ts`'s own module doc).
|
|
19
|
+
*
|
|
20
|
+
* @package @kya-os/contracts/review
|
|
21
|
+
*/
|
|
22
|
+
import { z } from 'zod';
|
|
23
|
+
export declare const FixAttestationSubjectSchema: z.ZodObject<{
|
|
24
|
+
/**
|
|
25
|
+
* "owner/repo", e.g. "modelcontextprotocol-identity/xmcp-i". Constrained
|
|
26
|
+
* to GitHub's actual owner/repo character set (alphanumeric, hyphen,
|
|
27
|
+
* underscore, period) rather than a bare non-empty string: this value
|
|
28
|
+
* gets interpolated into a PR-comment marker
|
|
29
|
+
* (`buildFixAttestationCommentBody` in `@kya-os/cli`'s `dco fix`), and a
|
|
30
|
+
* bare `.min(1)` would let a backtick or an HTML comment-closing `-->`
|
|
31
|
+
* through into that markdown.
|
|
32
|
+
*/
|
|
33
|
+
repo: z.ZodString;
|
|
34
|
+
/** GitHub PR number */
|
|
35
|
+
prNumber: z.ZodNumber;
|
|
36
|
+
/** Full 40-character SHA, lowercase — the commit this attestation covers */
|
|
37
|
+
fixedHeadSha: z.ZodString;
|
|
38
|
+
}, "strip", z.ZodTypeAny, {
|
|
39
|
+
repo: string;
|
|
40
|
+
prNumber: number;
|
|
41
|
+
fixedHeadSha: string;
|
|
42
|
+
}, {
|
|
43
|
+
repo: string;
|
|
44
|
+
prNumber: number;
|
|
45
|
+
fixedHeadSha: string;
|
|
46
|
+
}>;
|
|
47
|
+
export type FixAttestationSubject = z.infer<typeof FixAttestationSubjectSchema>;
|
|
48
|
+
export declare const FixAttestationSchema: z.ZodObject<{
|
|
49
|
+
subject: z.ZodObject<{
|
|
50
|
+
/**
|
|
51
|
+
* "owner/repo", e.g. "modelcontextprotocol-identity/xmcp-i". Constrained
|
|
52
|
+
* to GitHub's actual owner/repo character set (alphanumeric, hyphen,
|
|
53
|
+
* underscore, period) rather than a bare non-empty string: this value
|
|
54
|
+
* gets interpolated into a PR-comment marker
|
|
55
|
+
* (`buildFixAttestationCommentBody` in `@kya-os/cli`'s `dco fix`), and a
|
|
56
|
+
* bare `.min(1)` would let a backtick or an HTML comment-closing `-->`
|
|
57
|
+
* through into that markdown.
|
|
58
|
+
*/
|
|
59
|
+
repo: z.ZodString;
|
|
60
|
+
/** GitHub PR number */
|
|
61
|
+
prNumber: z.ZodNumber;
|
|
62
|
+
/** Full 40-character SHA, lowercase — the commit this attestation covers */
|
|
63
|
+
fixedHeadSha: z.ZodString;
|
|
64
|
+
}, "strip", z.ZodTypeAny, {
|
|
65
|
+
repo: string;
|
|
66
|
+
prNumber: number;
|
|
67
|
+
fixedHeadSha: string;
|
|
68
|
+
}, {
|
|
69
|
+
repo: string;
|
|
70
|
+
prNumber: number;
|
|
71
|
+
fixedHeadSha: string;
|
|
72
|
+
}>;
|
|
73
|
+
/** Full 40-character SHA, lowercase — the reviewed head that requested changes */
|
|
74
|
+
respondsToHeadSha: z.ZodString;
|
|
75
|
+
/** The DIDComm fix task this attestation was dispatched for, when applicable */
|
|
76
|
+
taskId: z.ZodOptional<z.ZodString>;
|
|
77
|
+
/** did:<method>:<method-specific-id>, e.g. did:web:knowthat.ai:agents:acme-bot */
|
|
78
|
+
agentDid: z.ZodString;
|
|
79
|
+
/** Detached JWS over `getFixAttestationSigningString(subject, respondsToHeadSha)` */
|
|
80
|
+
proof: z.ZodObject<{
|
|
81
|
+
alg: z.ZodEnum<["Ed25519", "ES256"]>;
|
|
82
|
+
kid: z.ZodOptional<z.ZodString>;
|
|
83
|
+
signature: z.ZodString;
|
|
84
|
+
}, "strip", z.ZodTypeAny, {
|
|
85
|
+
signature: string;
|
|
86
|
+
alg: "Ed25519" | "ES256";
|
|
87
|
+
kid?: string | undefined;
|
|
88
|
+
}, {
|
|
89
|
+
signature: string;
|
|
90
|
+
alg: "Ed25519" | "ES256";
|
|
91
|
+
kid?: string | undefined;
|
|
92
|
+
}>;
|
|
93
|
+
}, "strip", z.ZodTypeAny, {
|
|
94
|
+
agentDid: string;
|
|
95
|
+
subject: {
|
|
96
|
+
repo: string;
|
|
97
|
+
prNumber: number;
|
|
98
|
+
fixedHeadSha: string;
|
|
99
|
+
};
|
|
100
|
+
proof: {
|
|
101
|
+
signature: string;
|
|
102
|
+
alg: "Ed25519" | "ES256";
|
|
103
|
+
kid?: string | undefined;
|
|
104
|
+
};
|
|
105
|
+
respondsToHeadSha: string;
|
|
106
|
+
taskId?: string | undefined;
|
|
107
|
+
}, {
|
|
108
|
+
agentDid: string;
|
|
109
|
+
subject: {
|
|
110
|
+
repo: string;
|
|
111
|
+
prNumber: number;
|
|
112
|
+
fixedHeadSha: string;
|
|
113
|
+
};
|
|
114
|
+
proof: {
|
|
115
|
+
signature: string;
|
|
116
|
+
alg: "Ed25519" | "ES256";
|
|
117
|
+
kid?: string | undefined;
|
|
118
|
+
};
|
|
119
|
+
respondsToHeadSha: string;
|
|
120
|
+
taskId?: string | undefined;
|
|
121
|
+
}>;
|
|
122
|
+
export type FixAttestation = z.infer<typeof FixAttestationSchema>;
|
|
123
|
+
/**
|
|
124
|
+
* Field order for the fix-attestation signing input, fixed forever: changing
|
|
125
|
+
* it changes every existing signature. `"fix-attestation:v1"` is a version
|
|
126
|
+
* tag, not a subject field — bumping the scheme (e.g. adding a field) must
|
|
127
|
+
* bump this string too, so an old signature can never be replayed against a
|
|
128
|
+
* new canonicalization.
|
|
129
|
+
*/
|
|
130
|
+
export declare const FIX_ATTESTATION_SIGNING_ORDER: readonly ["version", "repo", "prNumber", "fixedHeadSha", "respondsToHeadSha"];
|
|
131
|
+
/**
|
|
132
|
+
* Build the canonical signing string for a fix attestation's detached JWS:
|
|
133
|
+
* newline-joined `"fix-attestation:v1"`, `subject.repo`, `subject.prNumber`
|
|
134
|
+
* (decimal), `subject.fixedHeadSha`, `respondsToHeadSha`, in that fixed
|
|
135
|
+
* order (`FIX_ATTESTATION_SIGNING_ORDER`). Every field is required here (no
|
|
136
|
+
* optional-field ambiguity to resolve, unlike `getVerdictCanonicalSigningString`'s
|
|
137
|
+
* findings tokens), so each token is simply `JSON.stringify(value)`.
|
|
138
|
+
*
|
|
139
|
+
* ### Worked example
|
|
140
|
+
*
|
|
141
|
+
* ```ts
|
|
142
|
+
* const subject: FixAttestationSubject = {
|
|
143
|
+
* repo: 'acme/demo',
|
|
144
|
+
* prNumber: 42,
|
|
145
|
+
* fixedHeadSha: 'a'.repeat(40),
|
|
146
|
+
* };
|
|
147
|
+
* getFixAttestationSigningString(subject, 'b'.repeat(40))
|
|
148
|
+
* // => '"fix-attestation:v1"\n"acme/demo"\n42\n"aaaa...aaaa"\n"bbbb...bbbb"'
|
|
149
|
+
* ```
|
|
150
|
+
*/
|
|
151
|
+
export declare function getFixAttestationSigningString(subject: FixAttestationSubject, respondsToHeadSha: string): string;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Fix Attestation Schema
|
|
4
|
+
*
|
|
5
|
+
* A fix attestation is the answer to a `ReviewVerdictBody` that requested
|
|
6
|
+
* changes: a verdict says "I reviewed `respondsToHeadSha` and found these
|
|
7
|
+
* findings"; a fix attestation says "I pushed `subject.fixedHeadSha` on
|
|
8
|
+
* `subject.repo`#`subject.prNumber` in response to that review." Posted as a
|
|
9
|
+
* signed PR comment (see `dco fix` in `@kya-os/cli`) so the audit trail
|
|
10
|
+
* closes the loop without trusting an unsigned comment body.
|
|
11
|
+
*
|
|
12
|
+
* Pure Zod schemas plus canonicalization only, no `node:crypto` — this
|
|
13
|
+
* module (and this package's `./review` barrel, which re-exports it) stays
|
|
14
|
+
* importable from an edge/browser runtime. The Ed25519 verify counterpart,
|
|
15
|
+
* `verifyFixAttestationProof`, lives in the separate `./review/verify`
|
|
16
|
+
* subpath instead of here for exactly that reason: it needs `node:crypto`
|
|
17
|
+
* for raw Ed25519 verification, and a subpath barrel must not force that
|
|
18
|
+
* dependency on every consumer who only needs the schema/signing-string
|
|
19
|
+
* (see `verify.ts`'s own module doc).
|
|
20
|
+
*
|
|
21
|
+
* @package @kya-os/contracts/review
|
|
22
|
+
*/
|
|
23
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
24
|
+
exports.FIX_ATTESTATION_SIGNING_ORDER = exports.FixAttestationSchema = exports.FixAttestationSubjectSchema = void 0;
|
|
25
|
+
exports.getFixAttestationSigningString = getFixAttestationSigningString;
|
|
26
|
+
const zod_1 = require("zod");
|
|
27
|
+
const agent_deployment_js_1 = require("../agent-deployment.js");
|
|
28
|
+
// Reached via the same relative path `schemas.ts` already uses, not a
|
|
29
|
+
// self-package import of `@kya-os/contracts/proof`.
|
|
30
|
+
const signing_spec_js_1 = require("../proof/signing-spec.js");
|
|
31
|
+
exports.FixAttestationSubjectSchema = zod_1.z.object({
|
|
32
|
+
/**
|
|
33
|
+
* "owner/repo", e.g. "modelcontextprotocol-identity/xmcp-i". Constrained
|
|
34
|
+
* to GitHub's actual owner/repo character set (alphanumeric, hyphen,
|
|
35
|
+
* underscore, period) rather than a bare non-empty string: this value
|
|
36
|
+
* gets interpolated into a PR-comment marker
|
|
37
|
+
* (`buildFixAttestationCommentBody` in `@kya-os/cli`'s `dco fix`), and a
|
|
38
|
+
* bare `.min(1)` would let a backtick or an HTML comment-closing `-->`
|
|
39
|
+
* through into that markdown.
|
|
40
|
+
*/
|
|
41
|
+
repo: zod_1.z.string().regex(/^[A-Za-z0-9](?:[A-Za-z0-9._-]*[A-Za-z0-9])?\/[A-Za-z0-9._-]+$/),
|
|
42
|
+
/** GitHub PR number */
|
|
43
|
+
prNumber: zod_1.z.number().int().positive(),
|
|
44
|
+
/** Full 40-character SHA, lowercase — the commit this attestation covers */
|
|
45
|
+
fixedHeadSha: zod_1.z.string().regex(/^[0-9a-f]{40}$/),
|
|
46
|
+
});
|
|
47
|
+
exports.FixAttestationSchema = zod_1.z.object({
|
|
48
|
+
subject: exports.FixAttestationSubjectSchema,
|
|
49
|
+
/** Full 40-character SHA, lowercase — the reviewed head that requested changes */
|
|
50
|
+
respondsToHeadSha: zod_1.z.string().regex(/^[0-9a-f]{40}$/),
|
|
51
|
+
/** The DIDComm fix task this attestation was dispatched for, when applicable */
|
|
52
|
+
taskId: zod_1.z.string().min(1).optional(),
|
|
53
|
+
/** did:<method>:<method-specific-id>, e.g. did:web:knowthat.ai:agents:acme-bot */
|
|
54
|
+
agentDid: agent_deployment_js_1.DidStringSchema,
|
|
55
|
+
/** Detached JWS over `getFixAttestationSigningString(subject, respondsToHeadSha)` */
|
|
56
|
+
proof: signing_spec_js_1.DetachedJwsSchema,
|
|
57
|
+
});
|
|
58
|
+
/**
|
|
59
|
+
* Field order for the fix-attestation signing input, fixed forever: changing
|
|
60
|
+
* it changes every existing signature. `"fix-attestation:v1"` is a version
|
|
61
|
+
* tag, not a subject field — bumping the scheme (e.g. adding a field) must
|
|
62
|
+
* bump this string too, so an old signature can never be replayed against a
|
|
63
|
+
* new canonicalization.
|
|
64
|
+
*/
|
|
65
|
+
exports.FIX_ATTESTATION_SIGNING_ORDER = [
|
|
66
|
+
'version',
|
|
67
|
+
'repo',
|
|
68
|
+
'prNumber',
|
|
69
|
+
'fixedHeadSha',
|
|
70
|
+
'respondsToHeadSha',
|
|
71
|
+
];
|
|
72
|
+
/** A single canonicalization token: `JSON.stringify(value)`, matching canonicalize.ts's scheme. */
|
|
73
|
+
function canonicalToken(value) {
|
|
74
|
+
return JSON.stringify(value);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Build the canonical signing string for a fix attestation's detached JWS:
|
|
78
|
+
* newline-joined `"fix-attestation:v1"`, `subject.repo`, `subject.prNumber`
|
|
79
|
+
* (decimal), `subject.fixedHeadSha`, `respondsToHeadSha`, in that fixed
|
|
80
|
+
* order (`FIX_ATTESTATION_SIGNING_ORDER`). Every field is required here (no
|
|
81
|
+
* optional-field ambiguity to resolve, unlike `getVerdictCanonicalSigningString`'s
|
|
82
|
+
* findings tokens), so each token is simply `JSON.stringify(value)`.
|
|
83
|
+
*
|
|
84
|
+
* ### Worked example
|
|
85
|
+
*
|
|
86
|
+
* ```ts
|
|
87
|
+
* const subject: FixAttestationSubject = {
|
|
88
|
+
* repo: 'acme/demo',
|
|
89
|
+
* prNumber: 42,
|
|
90
|
+
* fixedHeadSha: 'a'.repeat(40),
|
|
91
|
+
* };
|
|
92
|
+
* getFixAttestationSigningString(subject, 'b'.repeat(40))
|
|
93
|
+
* // => '"fix-attestation:v1"\n"acme/demo"\n42\n"aaaa...aaaa"\n"bbbb...bbbb"'
|
|
94
|
+
* ```
|
|
95
|
+
*/
|
|
96
|
+
function getFixAttestationSigningString(subject, respondsToHeadSha) {
|
|
97
|
+
const values = {
|
|
98
|
+
version: 'fix-attestation:v1',
|
|
99
|
+
repo: subject.repo,
|
|
100
|
+
prNumber: subject.prNumber,
|
|
101
|
+
fixedHeadSha: subject.fixedHeadSha,
|
|
102
|
+
respondsToHeadSha,
|
|
103
|
+
};
|
|
104
|
+
return exports.FIX_ATTESTATION_SIGNING_ORDER.map((field) => canonicalToken(values[field])).join('\n');
|
|
105
|
+
}
|