hachure 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +267 -0
- package/conformance/README.md +31 -0
- package/conformance/sf-authority-resolved.json +106 -0
- package/conformance/sf-disputed-blocking.json +78 -0
- package/conformance/sf-reference-bundle-snapshot.json +332 -0
- package/conformance/sf-stale-duration.json +76 -0
- package/conformance/sf-verified-commit.json +68 -0
- package/index.mjs +61 -0
- package/interop-in-toto.md +133 -0
- package/package.json +34 -0
- package/schemas/claim.schema.json +82 -0
- package/schemas/derivation-rule.schema.json +57 -0
- package/schemas/evidence.schema.json +49 -0
- package/schemas/inquiry-record.schema.json +76 -0
- package/schemas/trust-bundle.schema.json +180 -0
- package/schemas/verification-event.schema.json +28 -0
- package/schemas/verification-policy.schema.json +65 -0
- package/status-function.md +225 -0
|
@@ -0,0 +1,332 @@
|
|
|
1
|
+
{
|
|
2
|
+
"now": "2026-06-10T00:00:00.000Z",
|
|
3
|
+
"input": {
|
|
4
|
+
"schemaVersion": 3,
|
|
5
|
+
"source": "kontour-surface-validation-fixtures",
|
|
6
|
+
"identityLinks": [
|
|
7
|
+
{
|
|
8
|
+
"subjects": [
|
|
9
|
+
{
|
|
10
|
+
"subjectType": "repo-surface",
|
|
11
|
+
"subjectId": "example/repo:src/api"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"subjectType": "release-channel",
|
|
15
|
+
"subjectId": "repo-governance:api"
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
"reason": "API source and the named release channel describe the same surface."
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"claims": [
|
|
22
|
+
{
|
|
23
|
+
"id": "claim.repo-governance.api-proof",
|
|
24
|
+
"subjectType": "repo-surface",
|
|
25
|
+
"subjectId": "example/repo:src/api",
|
|
26
|
+
"surface": "repo-governance.developer-evidence",
|
|
27
|
+
"claimType": "software-evidence",
|
|
28
|
+
"fieldOrBehavior": "acceptanceCriteriaLane",
|
|
29
|
+
"value": "npm test passed for API surface change",
|
|
30
|
+
"createdAt": "2026-04-25T00:00:00.000Z",
|
|
31
|
+
"updatedAt": "2026-04-25T00:05:00.000Z",
|
|
32
|
+
"impactLevel": "high",
|
|
33
|
+
"currentIntegrityRef": "commit-or-run-id-placeholder",
|
|
34
|
+
"verificationPolicyId": "policy.software-evidence.commit",
|
|
35
|
+
"subjectAliases": [
|
|
36
|
+
{
|
|
37
|
+
"subjectType": "release-channel",
|
|
38
|
+
"subjectId": "repo-governance:api"
|
|
39
|
+
}
|
|
40
|
+
],
|
|
41
|
+
"confidenceBasis": {
|
|
42
|
+
"sourceQuality": "strong",
|
|
43
|
+
"reviewerAuthority": "system",
|
|
44
|
+
"evidenceStrength": "strong",
|
|
45
|
+
"impactLevel": "high"
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"id": "claim.field-attested-records.registration-status",
|
|
50
|
+
"subjectType": "attested-record",
|
|
51
|
+
"subjectId": "field-attested-records:denver-example-record",
|
|
52
|
+
"surface": "field-attested-records.public-data",
|
|
53
|
+
"claimType": "public-data-field",
|
|
54
|
+
"fieldOrBehavior": "registrationStatus",
|
|
55
|
+
"value": "OPEN",
|
|
56
|
+
"createdAt": "2026-04-01T00:00:00.000Z",
|
|
57
|
+
"updatedAt": "2026-04-01T00:00:00.000Z",
|
|
58
|
+
"impactLevel": "high",
|
|
59
|
+
"verificationPolicyId": "policy.public-data-field.short-lived",
|
|
60
|
+
"confidenceBasis": {
|
|
61
|
+
"sourceQuality": "strong",
|
|
62
|
+
"extractionConfidence": 0.92,
|
|
63
|
+
"corroborationCount": 1,
|
|
64
|
+
"reviewerAuthority": "operator",
|
|
65
|
+
"evidenceStrength": "moderate",
|
|
66
|
+
"impactLevel": "high"
|
|
67
|
+
}
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
"id": "claim.fact-resolution.w2-wages",
|
|
71
|
+
"subjectType": "fact",
|
|
72
|
+
"subjectId": "fact-resolution:demo-case-2025:w2.wages",
|
|
73
|
+
"surface": "fact-resolution.financial-facts",
|
|
74
|
+
"claimType": "verified-financial-fact",
|
|
75
|
+
"fieldOrBehavior": "w2.wages",
|
|
76
|
+
"value": 123456,
|
|
77
|
+
"createdAt": "2026-04-20T00:00:00.000Z",
|
|
78
|
+
"updatedAt": "2026-04-20T00:00:00.000Z",
|
|
79
|
+
"impactLevel": "critical",
|
|
80
|
+
"verificationPolicyId": "policy.financial-fact.historical",
|
|
81
|
+
"confidenceBasis": {
|
|
82
|
+
"sourceQuality": "strong",
|
|
83
|
+
"corroborationCount": 2,
|
|
84
|
+
"reviewerAuthority": "domain_expert",
|
|
85
|
+
"evidenceStrength": "strong",
|
|
86
|
+
"impactLevel": "critical"
|
|
87
|
+
}
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"id": "claim.surface.future-console",
|
|
91
|
+
"subjectType": "roadmap-item",
|
|
92
|
+
"subjectId": "kontour-surface:hosted-console",
|
|
93
|
+
"surface": "surface.roadmap",
|
|
94
|
+
"claimType": "roadmap-capability",
|
|
95
|
+
"fieldOrBehavior": "implementationStatus",
|
|
96
|
+
"value": "planned",
|
|
97
|
+
"createdAt": "2026-04-25T00:00:00.000Z",
|
|
98
|
+
"updatedAt": "2026-04-25T00:00:00.000Z",
|
|
99
|
+
"impactLevel": "medium",
|
|
100
|
+
"verificationPolicyId": "policy.roadmap.manual"
|
|
101
|
+
}
|
|
102
|
+
],
|
|
103
|
+
"evidence": [
|
|
104
|
+
{
|
|
105
|
+
"id": "evidence.repo-governance.test-output",
|
|
106
|
+
"claimId": "claim.repo-governance.api-proof",
|
|
107
|
+
"evidenceType": "test_output",
|
|
108
|
+
"method": "validation",
|
|
109
|
+
"sourceRef": "repo governance evidence check",
|
|
110
|
+
"sourceLocator": "npm test",
|
|
111
|
+
"excerptOrSummary": "84 tests passed for the verified repo state.",
|
|
112
|
+
"observedAt": "2026-04-25T00:05:00.000Z",
|
|
113
|
+
"collectedBy": "repo-governance",
|
|
114
|
+
"integrityRef": "commit-or-run-id-placeholder"
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
"id": "evidence.field-attested-records.source-excerpt",
|
|
118
|
+
"claimId": "claim.field-attested-records.registration-status",
|
|
119
|
+
"evidenceType": "source_excerpt",
|
|
120
|
+
"method": "observation",
|
|
121
|
+
"sourceRef": "https://example.org/camps/register",
|
|
122
|
+
"sourceLocator": "registration section",
|
|
123
|
+
"excerptOrSummary": "Registration is open now for summer sessions.",
|
|
124
|
+
"observedAt": "2026-04-01T00:00:00.000Z",
|
|
125
|
+
"collectedBy": "field-data-crawler"
|
|
126
|
+
},
|
|
127
|
+
{
|
|
128
|
+
"id": "evidence.field-attested-records.admin-attestation",
|
|
129
|
+
"claimId": "claim.field-attested-records.registration-status",
|
|
130
|
+
"evidenceType": "human_attestation",
|
|
131
|
+
"method": "attestation",
|
|
132
|
+
"sourceRef": "record steward review",
|
|
133
|
+
"sourceLocator": "FieldAttestation.registrationStatus",
|
|
134
|
+
"excerptOrSummary": "Admin approved the registration status from the source excerpt.",
|
|
135
|
+
"observedAt": "2026-04-01T00:10:00.000Z",
|
|
136
|
+
"collectedBy": "record-steward"
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
"id": "evidence.fact-resolution.document-citation",
|
|
140
|
+
"claimId": "claim.fact-resolution.w2-wages",
|
|
141
|
+
"evidenceType": "document_citation",
|
|
142
|
+
"method": "corroboration",
|
|
143
|
+
"sourceRef": "W-2 imported document",
|
|
144
|
+
"sourceLocator": "Box 1",
|
|
145
|
+
"excerptOrSummary": "W-2 Box 1 wages matched the verified fact value.",
|
|
146
|
+
"observedAt": "2026-04-20T00:00:00.000Z",
|
|
147
|
+
"collectedBy": "fact-importer",
|
|
148
|
+
"integrityRef": "document-hash-placeholder"
|
|
149
|
+
},
|
|
150
|
+
{
|
|
151
|
+
"id": "evidence.fact-resolution.calculation-trace",
|
|
152
|
+
"claimId": "claim.fact-resolution.w2-wages",
|
|
153
|
+
"evidenceType": "calculation_trace",
|
|
154
|
+
"method": "validation",
|
|
155
|
+
"sourceRef": "fact resolution package",
|
|
156
|
+
"sourceLocator": "verified_facts.w2.wages",
|
|
157
|
+
"excerptOrSummary": "Resolved fact was promoted to verified_facts before downstream analysis.",
|
|
158
|
+
"observedAt": "2026-04-20T00:15:00.000Z",
|
|
159
|
+
"collectedBy": "fact-resolution-service"
|
|
160
|
+
}
|
|
161
|
+
],
|
|
162
|
+
"policies": [
|
|
163
|
+
{
|
|
164
|
+
"id": "policy.software-evidence.commit",
|
|
165
|
+
"claimType": "software-evidence",
|
|
166
|
+
"parentType": "developer-claim",
|
|
167
|
+
"requiredEvidence": [
|
|
168
|
+
"test_output"
|
|
169
|
+
],
|
|
170
|
+
"requiredMethods": [
|
|
171
|
+
"validation"
|
|
172
|
+
],
|
|
173
|
+
"requiresCorroboration": false,
|
|
174
|
+
"acceptanceCriteria": [
|
|
175
|
+
"evidence check passes for the affected commit or diff"
|
|
176
|
+
],
|
|
177
|
+
"reviewAuthority": "repo policy",
|
|
178
|
+
"validityRule": {
|
|
179
|
+
"kind": "commit"
|
|
180
|
+
},
|
|
181
|
+
"stalenessTriggers": [
|
|
182
|
+
"new commit touches the same surface",
|
|
183
|
+
"evidence check changes"
|
|
184
|
+
],
|
|
185
|
+
"conflictRules": [
|
|
186
|
+
"failed evidence check supersedes prior passing evidence"
|
|
187
|
+
],
|
|
188
|
+
"impactLevel": "high",
|
|
189
|
+
"incompatibleStatuses": [
|
|
190
|
+
{
|
|
191
|
+
"statuses": [
|
|
192
|
+
"verified",
|
|
193
|
+
"rejected"
|
|
194
|
+
],
|
|
195
|
+
"message": "A surface cannot be simultaneously verified and rejected at the same commit."
|
|
196
|
+
}
|
|
197
|
+
]
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
"id": "policy.public-data-field.short-lived",
|
|
201
|
+
"claimType": "public-data-field",
|
|
202
|
+
"requiredEvidence": [
|
|
203
|
+
"source_excerpt",
|
|
204
|
+
"human_attestation"
|
|
205
|
+
],
|
|
206
|
+
"requiredMethods": [
|
|
207
|
+
"observation",
|
|
208
|
+
"attestation"
|
|
209
|
+
],
|
|
210
|
+
"requiresCorroboration": true,
|
|
211
|
+
"acceptanceCriteria": [
|
|
212
|
+
"field-level review or crawl approval"
|
|
213
|
+
],
|
|
214
|
+
"reviewAuthority": "operator",
|
|
215
|
+
"validityRule": {
|
|
216
|
+
"kind": "duration",
|
|
217
|
+
"durationDays": 14
|
|
218
|
+
},
|
|
219
|
+
"stalenessTriggers": [
|
|
220
|
+
"source page changes",
|
|
221
|
+
"validity window expires",
|
|
222
|
+
"user report conflicts"
|
|
223
|
+
],
|
|
224
|
+
"conflictRules": [
|
|
225
|
+
"new source excerpt conflicts with approved value"
|
|
226
|
+
],
|
|
227
|
+
"impactLevel": "high"
|
|
228
|
+
},
|
|
229
|
+
{
|
|
230
|
+
"id": "policy.financial-fact.historical",
|
|
231
|
+
"claimType": "verified-financial-fact",
|
|
232
|
+
"requiredEvidence": [
|
|
233
|
+
"document_citation",
|
|
234
|
+
"calculation_trace"
|
|
235
|
+
],
|
|
236
|
+
"requiredMethods": [
|
|
237
|
+
"corroboration",
|
|
238
|
+
"validation"
|
|
239
|
+
],
|
|
240
|
+
"requiresCorroboration": true,
|
|
241
|
+
"acceptanceCriteria": [
|
|
242
|
+
"resolved fact promoted to verified fact"
|
|
243
|
+
],
|
|
244
|
+
"reviewAuthority": "domain expert",
|
|
245
|
+
"validityRule": {
|
|
246
|
+
"kind": "historical"
|
|
247
|
+
},
|
|
248
|
+
"stalenessTriggers": [
|
|
249
|
+
"new source document imported",
|
|
250
|
+
"conflicting candidate appears"
|
|
251
|
+
],
|
|
252
|
+
"conflictRules": [
|
|
253
|
+
"multi-candidate facts require manual resolution"
|
|
254
|
+
],
|
|
255
|
+
"impactLevel": "critical"
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"id": "policy.roadmap.manual",
|
|
259
|
+
"claimType": "roadmap-capability",
|
|
260
|
+
"requiredEvidence": [
|
|
261
|
+
"human_attestation"
|
|
262
|
+
],
|
|
263
|
+
"requiredMethods": [
|
|
264
|
+
"attestation"
|
|
265
|
+
],
|
|
266
|
+
"requiresCorroboration": false,
|
|
267
|
+
"acceptanceCriteria": [
|
|
268
|
+
"explicit roadmap acceptance"
|
|
269
|
+
],
|
|
270
|
+
"reviewAuthority": "product owner",
|
|
271
|
+
"validityRule": {
|
|
272
|
+
"kind": "manual"
|
|
273
|
+
},
|
|
274
|
+
"stalenessTriggers": [
|
|
275
|
+
"roadmap changed"
|
|
276
|
+
],
|
|
277
|
+
"conflictRules": [
|
|
278
|
+
"implementation claim without shipped evidence is unsupported"
|
|
279
|
+
],
|
|
280
|
+
"impactLevel": "medium"
|
|
281
|
+
}
|
|
282
|
+
],
|
|
283
|
+
"events": [
|
|
284
|
+
{
|
|
285
|
+
"id": "event.repo-governance.verified",
|
|
286
|
+
"claimId": "claim.repo-governance.api-proof",
|
|
287
|
+
"status": "verified",
|
|
288
|
+
"actor": "repo-governance",
|
|
289
|
+
"method": "evidence-check",
|
|
290
|
+
"evidenceIds": [
|
|
291
|
+
"evidence.repo-governance.test-output"
|
|
292
|
+
],
|
|
293
|
+
"createdAt": "2026-04-25T00:05:00.000Z",
|
|
294
|
+
"verifiedAt": "2026-04-25T00:05:00.000Z"
|
|
295
|
+
},
|
|
296
|
+
{
|
|
297
|
+
"id": "event.field-attested-records.verified",
|
|
298
|
+
"claimId": "claim.field-attested-records.registration-status",
|
|
299
|
+
"status": "verified",
|
|
300
|
+
"actor": "record-steward",
|
|
301
|
+
"method": "field-attestation",
|
|
302
|
+
"evidenceIds": [
|
|
303
|
+
"evidence.field-attested-records.source-excerpt",
|
|
304
|
+
"evidence.field-attested-records.admin-attestation"
|
|
305
|
+
],
|
|
306
|
+
"createdAt": "2026-04-01T00:10:00.000Z",
|
|
307
|
+
"verifiedAt": "2026-04-01T00:10:00.000Z"
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
"id": "event.fact-resolution.verified",
|
|
311
|
+
"claimId": "claim.fact-resolution.w2-wages",
|
|
312
|
+
"status": "verified",
|
|
313
|
+
"actor": "fact-resolution-service",
|
|
314
|
+
"method": "verified-fact-promotion",
|
|
315
|
+
"evidenceIds": [
|
|
316
|
+
"evidence.fact-resolution.document-citation",
|
|
317
|
+
"evidence.fact-resolution.calculation-trace"
|
|
318
|
+
],
|
|
319
|
+
"createdAt": "2026-04-20T00:15:00.000Z",
|
|
320
|
+
"verifiedAt": "2026-04-20T00:15:00.000Z"
|
|
321
|
+
}
|
|
322
|
+
]
|
|
323
|
+
},
|
|
324
|
+
"expect": {
|
|
325
|
+
"statusByClaimId": {
|
|
326
|
+
"claim.repo-governance.api-proof": "verified",
|
|
327
|
+
"claim.field-attested-records.registration-status": "stale",
|
|
328
|
+
"claim.fact-resolution.w2-wages": "verified",
|
|
329
|
+
"claim.surface.future-console": "unknown"
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"now": "2026-06-10T00:00:00.000Z",
|
|
3
|
+
"input": {
|
|
4
|
+
"schemaVersion": 3,
|
|
5
|
+
"source": "spec-conformance:stale-duration",
|
|
6
|
+
"claims": [
|
|
7
|
+
{
|
|
8
|
+
"id": "claim.public.field",
|
|
9
|
+
"subjectType": "data-field",
|
|
10
|
+
"subjectId": "dataset:registration-status",
|
|
11
|
+
"surface": "public-data.status",
|
|
12
|
+
"claimType": "public-data-field",
|
|
13
|
+
"fieldOrBehavior": "registrationStatus",
|
|
14
|
+
"value": "OPEN",
|
|
15
|
+
"createdAt": "2026-04-01T00:00:00.000Z",
|
|
16
|
+
"updatedAt": "2026-04-01T00:00:00.000Z",
|
|
17
|
+
"impactLevel": "high",
|
|
18
|
+
"verificationPolicyId": "policy.public-data-field.short-lived"
|
|
19
|
+
}
|
|
20
|
+
],
|
|
21
|
+
"evidence": [
|
|
22
|
+
{
|
|
23
|
+
"id": "evidence.public.source-excerpt",
|
|
24
|
+
"claimId": "claim.public.field",
|
|
25
|
+
"evidenceType": "source_excerpt",
|
|
26
|
+
"method": "observation",
|
|
27
|
+
"sourceRef": "https://example.org/register",
|
|
28
|
+
"excerptOrSummary": "Registration is open for summer sessions.",
|
|
29
|
+
"observedAt": "2026-04-01T00:00:00.000Z",
|
|
30
|
+
"collectedBy": "crawler"
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
"id": "evidence.public.human-attestation",
|
|
34
|
+
"claimId": "claim.public.field",
|
|
35
|
+
"evidenceType": "human_attestation",
|
|
36
|
+
"method": "attestation",
|
|
37
|
+
"sourceRef": "record-steward review",
|
|
38
|
+
"excerptOrSummary": "Admin approved registration status from source excerpt.",
|
|
39
|
+
"observedAt": "2026-04-01T00:10:00.000Z",
|
|
40
|
+
"collectedBy": "record-steward"
|
|
41
|
+
}
|
|
42
|
+
],
|
|
43
|
+
"policies": [
|
|
44
|
+
{
|
|
45
|
+
"id": "policy.public-data-field.short-lived",
|
|
46
|
+
"claimType": "public-data-field",
|
|
47
|
+
"requiredEvidence": ["source_excerpt", "human_attestation"],
|
|
48
|
+
"requiredMethods": ["observation", "attestation"],
|
|
49
|
+
"requiresCorroboration": true,
|
|
50
|
+
"acceptanceCriteria": ["field-level review or crawl approval"],
|
|
51
|
+
"reviewAuthority": "operator",
|
|
52
|
+
"validityRule": { "kind": "duration", "durationDays": 14 },
|
|
53
|
+
"stalenessTriggers": ["validity window expires"],
|
|
54
|
+
"conflictRules": [],
|
|
55
|
+
"impactLevel": "high"
|
|
56
|
+
}
|
|
57
|
+
],
|
|
58
|
+
"events": [
|
|
59
|
+
{
|
|
60
|
+
"id": "event.public.verified",
|
|
61
|
+
"claimId": "claim.public.field",
|
|
62
|
+
"status": "verified",
|
|
63
|
+
"actor": "record-steward",
|
|
64
|
+
"method": "field-attestation",
|
|
65
|
+
"evidenceIds": ["evidence.public.source-excerpt", "evidence.public.human-attestation"],
|
|
66
|
+
"createdAt": "2026-04-01T00:10:00.000Z",
|
|
67
|
+
"verifiedAt": "2026-04-01T00:10:00.000Z"
|
|
68
|
+
}
|
|
69
|
+
]
|
|
70
|
+
},
|
|
71
|
+
"expect": {
|
|
72
|
+
"statusByClaimId": {
|
|
73
|
+
"claim.public.field": "stale"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
{
|
|
2
|
+
"now": "2026-06-10T00:00:00.000Z",
|
|
3
|
+
"input": {
|
|
4
|
+
"schemaVersion": 3,
|
|
5
|
+
"source": "spec-conformance:verified-commit",
|
|
6
|
+
"claims": [
|
|
7
|
+
{
|
|
8
|
+
"id": "claim.api.rate-limit",
|
|
9
|
+
"subjectType": "api",
|
|
10
|
+
"subjectId": "public-api",
|
|
11
|
+
"surface": "api.guarantees",
|
|
12
|
+
"claimType": "software-evidence",
|
|
13
|
+
"fieldOrBehavior": "rate limit is enforced",
|
|
14
|
+
"value": "100 requests/minute",
|
|
15
|
+
"createdAt": "2026-04-25T00:00:00.000Z",
|
|
16
|
+
"updatedAt": "2026-04-25T00:05:00.000Z",
|
|
17
|
+
"impactLevel": "high",
|
|
18
|
+
"currentIntegrityRef": "commit:abc123",
|
|
19
|
+
"verificationPolicyId": "policy.software-evidence.commit"
|
|
20
|
+
}
|
|
21
|
+
],
|
|
22
|
+
"evidence": [
|
|
23
|
+
{
|
|
24
|
+
"id": "evidence.api.rate-limit.test",
|
|
25
|
+
"claimId": "claim.api.rate-limit",
|
|
26
|
+
"evidenceType": "test_output",
|
|
27
|
+
"method": "validation",
|
|
28
|
+
"sourceRef": "ci:1847",
|
|
29
|
+
"excerptOrSummary": "Rate-limit tests passed.",
|
|
30
|
+
"observedAt": "2026-04-25T00:05:00.000Z",
|
|
31
|
+
"collectedBy": "ci",
|
|
32
|
+
"integrityRef": "commit:abc123"
|
|
33
|
+
}
|
|
34
|
+
],
|
|
35
|
+
"policies": [
|
|
36
|
+
{
|
|
37
|
+
"id": "policy.software-evidence.commit",
|
|
38
|
+
"claimType": "software-evidence",
|
|
39
|
+
"requiredEvidence": ["test_output"],
|
|
40
|
+
"requiredMethods": ["validation"],
|
|
41
|
+
"requiresCorroboration": false,
|
|
42
|
+
"acceptanceCriteria": ["evidence check passes for the affected commit"],
|
|
43
|
+
"reviewAuthority": "repo policy",
|
|
44
|
+
"validityRule": { "kind": "commit" },
|
|
45
|
+
"stalenessTriggers": ["new commit touches the same surface"],
|
|
46
|
+
"conflictRules": [],
|
|
47
|
+
"impactLevel": "high"
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
"events": [
|
|
51
|
+
{
|
|
52
|
+
"id": "event.api.rate-limit.verified",
|
|
53
|
+
"claimId": "claim.api.rate-limit",
|
|
54
|
+
"status": "verified",
|
|
55
|
+
"actor": "ci",
|
|
56
|
+
"method": "npm test",
|
|
57
|
+
"evidenceIds": ["evidence.api.rate-limit.test"],
|
|
58
|
+
"createdAt": "2026-04-25T00:05:00.000Z",
|
|
59
|
+
"verifiedAt": "2026-04-25T00:05:00.000Z"
|
|
60
|
+
}
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
"expect": {
|
|
64
|
+
"statusByClaimId": {
|
|
65
|
+
"claim.api.rate-limit": "verified"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
package/index.mjs
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* hachure — canonical npm distribution of the Hachure trust format spec.
|
|
3
|
+
*
|
|
4
|
+
* Exports:
|
|
5
|
+
* STATUS_FUNCTION_VERSION — spec-side declaration of the current status
|
|
6
|
+
* derivation algorithm version. Reference
|
|
7
|
+
* implementations must export a matching value.
|
|
8
|
+
* schemas — Map<recordName, parsedSchemaObject> for every
|
|
9
|
+
* normative schema shipped with this package.
|
|
10
|
+
* testVectors — Array<{name, vector}> of all conformance test
|
|
11
|
+
* vectors. Each vector has `input`, `expect`, and
|
|
12
|
+
* `now` fields; run them against your implementation
|
|
13
|
+
* to claim conformance.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { readFileSync, readdirSync } from 'node:fs';
|
|
17
|
+
import { fileURLToPath } from 'node:url';
|
|
18
|
+
import { join, dirname, basename } from 'node:path';
|
|
19
|
+
|
|
20
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
|
|
22
|
+
// ---------------------------------------------------------------------------
|
|
23
|
+
// Spec-side declaration of the status function version.
|
|
24
|
+
// Any implementation claiming conformance at this version must produce the
|
|
25
|
+
// same status outputs as the test vectors for all cases in conformance/.
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
export const STATUS_FUNCTION_VERSION = '1';
|
|
28
|
+
|
|
29
|
+
// ---------------------------------------------------------------------------
|
|
30
|
+
// Schemas — Map of record name (filename without .schema.json) → parsed JSON.
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
function loadSchemas() {
|
|
33
|
+
const schemasDir = join(__dirname, 'schemas');
|
|
34
|
+
const map = new Map();
|
|
35
|
+
for (const file of readdirSync(schemasDir).sort()) {
|
|
36
|
+
if (!file.endsWith('.schema.json')) continue;
|
|
37
|
+
const name = file.replace(/\.schema\.json$/, '');
|
|
38
|
+
map.set(name, JSON.parse(readFileSync(join(schemasDir, file), 'utf8')));
|
|
39
|
+
}
|
|
40
|
+
return map;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export const schemas = loadSchemas();
|
|
44
|
+
|
|
45
|
+
// ---------------------------------------------------------------------------
|
|
46
|
+
// Test vectors — Array of { name, vector } loaded from conformance/*.json.
|
|
47
|
+
// Each vector: { now, input, expect: { statusByClaimId } }
|
|
48
|
+
// ---------------------------------------------------------------------------
|
|
49
|
+
function loadTestVectors() {
|
|
50
|
+
const conformanceDir = join(__dirname, 'conformance');
|
|
51
|
+
const vectors = [];
|
|
52
|
+
for (const file of readdirSync(conformanceDir).sort()) {
|
|
53
|
+
if (!file.endsWith('.json')) continue;
|
|
54
|
+
const name = basename(file, '.json');
|
|
55
|
+
const vector = JSON.parse(readFileSync(join(conformanceDir, file), 'utf8'));
|
|
56
|
+
vectors.push({ name, vector });
|
|
57
|
+
}
|
|
58
|
+
return vectors;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export const testVectors = loadTestVectors();
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# in-toto Interop: TrustBundle as an in-toto Statement v1
|
|
2
|
+
|
|
3
|
+
**Module:** `@kontourai/surface` — `src/interop/in-toto.ts`
|
|
4
|
+
**Public exports:** `toInTotoStatement`, `toDsseEnvelope`, `buildPaeBytes`, `parseDssePayload`
|
|
5
|
+
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
## Overview
|
|
9
|
+
|
|
10
|
+
This module lets a producer publish a TrustBundle as a signed software-supply-chain
|
|
11
|
+
attestation by wrapping it in the in-toto Statement v1 format and optionally enveloping
|
|
12
|
+
it in DSSE (Dead Simple Signing Envelope). It adds no new crypto dependency: all
|
|
13
|
+
cryptographic operations are injected via a caller-supplied `Signer`.
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Statement shape
|
|
18
|
+
|
|
19
|
+
`toInTotoStatement(bundle, { subjects })` produces an in-toto Statement v1:
|
|
20
|
+
|
|
21
|
+
```jsonc
|
|
22
|
+
{
|
|
23
|
+
"_type": "https://in-toto.io/Statement/v1",
|
|
24
|
+
"subject": [
|
|
25
|
+
{
|
|
26
|
+
"name": "<producer-chosen artifact reference>",
|
|
27
|
+
"digest": { "sha256": "<hex>" }
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
"predicateType": "https://hachure.org/v1/bundle",
|
|
31
|
+
"predicate": { /* the TrustBundle verbatim */ }
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
| Field | Value | Note |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| `_type` | `https://in-toto.io/Statement/v1` | Fixed; identifies the in-toto spec version. |
|
|
38
|
+
| `subject` | caller-supplied `[{ name, digest }]` | At least one required. The producer knows which artifact digests are relevant; Surface does not infer them. |
|
|
39
|
+
| `predicateType` | `https://hachure.org/v1/bundle` | Stable URI identifying Kontour trust bundles. |
|
|
40
|
+
| `predicate` | the TrustBundle | The full bundle becomes the predicate; no fields are stripped or remapped. |
|
|
41
|
+
|
|
42
|
+
The `predicateType` URI is stable for the lifetime of `hachure.org/v1`. If the
|
|
43
|
+
TrustBundle schema is broken (a new `apiVersion`), a new predicate type URI will be
|
|
44
|
+
registered rather than reusing this one.
|
|
45
|
+
|
|
46
|
+
---
|
|
47
|
+
|
|
48
|
+
## DSSE envelope and PAE
|
|
49
|
+
|
|
50
|
+
`toDsseEnvelope(statement, signer)` returns a DSSE envelope:
|
|
51
|
+
|
|
52
|
+
```jsonc
|
|
53
|
+
{
|
|
54
|
+
"payloadType": "application/vnd.in-toto+json",
|
|
55
|
+
"payload": "<base64-standard>",
|
|
56
|
+
"signatures": [
|
|
57
|
+
{ "keyid": "<from signer>", "sig": "<base64 signature over PAE>" }
|
|
58
|
+
]
|
|
59
|
+
}
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The payload is the base64-standard-encoded UTF-8 JSON serialisation of the Statement.
|
|
63
|
+
|
|
64
|
+
**Pre-Authentication Encoding (PAE)** — the bytes handed to `signer.sign()`:
|
|
65
|
+
|
|
66
|
+
```
|
|
67
|
+
PAE(type, body) = "DSSEv1" SP DEC(byte_len(type)) SP type SP DEC(byte_len(body)) SP body
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`buildPaeBytes(payloadType, bodyString): Uint8Array` is exported so callers can
|
|
71
|
+
verify the pre-image independently or pass it directly to a WebCrypto
|
|
72
|
+
`SubtleCrypto.sign()` call.
|
|
73
|
+
|
|
74
|
+
### Injecting a signer
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
import { toInTotoStatement, toDsseEnvelope } from "@kontourai/surface/interop/in-toto";
|
|
78
|
+
|
|
79
|
+
const signer = {
|
|
80
|
+
keyid: "my-key-id",
|
|
81
|
+
async sign(paeBytes: Uint8Array): Promise<string> {
|
|
82
|
+
// e.g. WebCrypto:
|
|
83
|
+
const raw = await crypto.subtle.sign("RSASSA-PKCS1-v1_5", privateKey, paeBytes);
|
|
84
|
+
return Buffer.from(raw).toString("base64");
|
|
85
|
+
},
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
const statement = toInTotoStatement(bundle, { subjects });
|
|
89
|
+
const envelope = await toDsseEnvelope(statement, signer);
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
---
|
|
93
|
+
|
|
94
|
+
## Sigstore / Rekor anchoring guidance
|
|
95
|
+
|
|
96
|
+
The DSSE envelope produced by this module is structurally compatible with
|
|
97
|
+
[Sigstore](https://sigstore.dev) tooling:
|
|
98
|
+
|
|
99
|
+
1. **Sign with Sigstore Cosign** — Cosign's `attest` command accepts DSSE envelopes.
|
|
100
|
+
Pass `--type custom` and `--predicate <bundle.json>` together with the
|
|
101
|
+
`predicateType` URI to have Cosign wrap the bundle and upload the signed envelope
|
|
102
|
+
to the [Rekor](https://docs.sigstore.dev/logging/overview/) transparency log.
|
|
103
|
+
|
|
104
|
+
2. **Upload to Rekor directly** — Use the Rekor `hashedrekord` or `dsse` entry type
|
|
105
|
+
with the DSSE envelope JSON. Once uploaded, Rekor returns a `LogEntry` UUID that
|
|
106
|
+
can be stored in a `TrustBundle.proof` field (Kontour Resource Shape) as an
|
|
107
|
+
`IntegrityAnchor` of kind `transparency_log`.
|
|
108
|
+
|
|
109
|
+
3. **Verify** — Retrieve the envelope from Rekor, decode `payload` from base64,
|
|
110
|
+
reconstruct PAE via `buildPaeBytes`, verify the signature against the signer's
|
|
111
|
+
public key, and check the `predicate` against the live `TrustBundle`.
|
|
112
|
+
|
|
113
|
+
---
|
|
114
|
+
|
|
115
|
+
## What Kontour adds on top of a frozen attestation
|
|
116
|
+
|
|
117
|
+
An in-toto Statement is a **frozen attestation**: it captures a point-in-time
|
|
118
|
+
assertion and anchors it cryptographically. That is exactly what the DSSE envelope
|
|
119
|
+
provides.
|
|
120
|
+
|
|
121
|
+
A Kontour `TrustBundle` adds **living status** on top:
|
|
122
|
+
|
|
123
|
+
| Frozen attestation (in-toto envelope) | Living bundle (Surface) |
|
|
124
|
+
|---|---|
|
|
125
|
+
| Status is sealed at signing time. | Status is recomputed from events at query time: `f(claim, events, policy, now)`. |
|
|
126
|
+
| Tamper-evident; content cannot change. | Append-only; new events and evidence accumulate. |
|
|
127
|
+
| Verifier trusts the signer's identity. | Verifier trusts the derivation algorithm (`STATUS_FUNCTION_VERSION`). |
|
|
128
|
+
| Useful for supply-chain audits and legal holds. | Useful for operational dashboards, gates, and consumer inquiries. |
|
|
129
|
+
|
|
130
|
+
The two are complementary: embed the bundle in an in-toto envelope to
|
|
131
|
+
*anchor what was known at release time*; continue querying the live bundle to track
|
|
132
|
+
*what is true now*. An `InquiryRecord` (ADR 0003 §6) records the live status at
|
|
133
|
+
inquiry time, linking the frozen and living views.
|