@gate-forge/core 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.
Files changed (242) hide show
  1. package/LICENSE +202 -0
  2. package/dist/baselines/adoption.d.ts +76 -0
  3. package/dist/baselines/adoption.d.ts.map +1 -0
  4. package/dist/baselines/adoption.js +163 -0
  5. package/dist/baselines/adoption.js.map +1 -0
  6. package/dist/baselines/index.d.ts +102 -0
  7. package/dist/baselines/index.d.ts.map +1 -0
  8. package/dist/baselines/index.js +186 -0
  9. package/dist/baselines/index.js.map +1 -0
  10. package/dist/canonical-json.d.ts +51 -0
  11. package/dist/canonical-json.d.ts.map +1 -0
  12. package/dist/canonical-json.js +112 -0
  13. package/dist/canonical-json.js.map +1 -0
  14. package/dist/classifier/bind.d.ts +64 -0
  15. package/dist/classifier/bind.d.ts.map +1 -0
  16. package/dist/classifier/bind.js +188 -0
  17. package/dist/classifier/bind.js.map +1 -0
  18. package/dist/classifier/classify.d.ts +154 -0
  19. package/dist/classifier/classify.d.ts.map +1 -0
  20. package/dist/classifier/classify.js +1207 -0
  21. package/dist/classifier/classify.js.map +1 -0
  22. package/dist/classifier/glob.d.ts +48 -0
  23. package/dist/classifier/glob.d.ts.map +1 -0
  24. package/dist/classifier/glob.js +104 -0
  25. package/dist/classifier/glob.js.map +1 -0
  26. package/dist/classifier/index.d.ts +10 -0
  27. package/dist/classifier/index.d.ts.map +1 -0
  28. package/dist/classifier/index.js +21 -0
  29. package/dist/classifier/index.js.map +1 -0
  30. package/dist/classifier/schema.d.ts +150 -0
  31. package/dist/classifier/schema.d.ts.map +1 -0
  32. package/dist/classifier/schema.js +139 -0
  33. package/dist/classifier/schema.js.map +1 -0
  34. package/dist/config/index.d.ts +259 -0
  35. package/dist/config/index.d.ts.map +1 -0
  36. package/dist/config/index.js +523 -0
  37. package/dist/config/index.js.map +1 -0
  38. package/dist/fingerprints.d.ts +56 -0
  39. package/dist/fingerprints.d.ts.map +1 -0
  40. package/dist/fingerprints.js +59 -0
  41. package/dist/fingerprints.js.map +1 -0
  42. package/dist/graph/build.d.ts +16 -0
  43. package/dist/graph/build.d.ts.map +1 -0
  44. package/dist/graph/build.js +423 -0
  45. package/dist/graph/build.js.map +1 -0
  46. package/dist/graph/index.d.ts +12 -0
  47. package/dist/graph/index.d.ts.map +1 -0
  48. package/dist/graph/index.js +10 -0
  49. package/dist/graph/index.js.map +1 -0
  50. package/dist/graph/schema.d.ts +448 -0
  51. package/dist/graph/schema.d.ts.map +1 -0
  52. package/dist/graph/schema.js +237 -0
  53. package/dist/graph/schema.js.map +1 -0
  54. package/dist/graph/symbols.d.ts +129 -0
  55. package/dist/graph/symbols.d.ts.map +1 -0
  56. package/dist/graph/symbols.js +177 -0
  57. package/dist/graph/symbols.js.map +1 -0
  58. package/dist/graph/util.d.ts +40 -0
  59. package/dist/graph/util.d.ts.map +1 -0
  60. package/dist/graph/util.js +41 -0
  61. package/dist/graph/util.js.map +1 -0
  62. package/dist/index.d.ts +683 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +563 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/mapping/resolve.d.ts +177 -0
  67. package/dist/mapping/resolve.d.ts.map +1 -0
  68. package/dist/mapping/resolve.js +523 -0
  69. package/dist/mapping/resolve.js.map +1 -0
  70. package/dist/policy/coverage.d.ts +79 -0
  71. package/dist/policy/coverage.d.ts.map +1 -0
  72. package/dist/policy/coverage.js +119 -0
  73. package/dist/policy/coverage.js.map +1 -0
  74. package/dist/policy/evaluate.d.ts +275 -0
  75. package/dist/policy/evaluate.d.ts.map +1 -0
  76. package/dist/policy/evaluate.js +382 -0
  77. package/dist/policy/evaluate.js.map +1 -0
  78. package/dist/policy/index.d.ts +15 -0
  79. package/dist/policy/index.d.ts.map +1 -0
  80. package/dist/policy/index.js +14 -0
  81. package/dist/policy/index.js.map +1 -0
  82. package/dist/policy/trusted.d.ts +59 -0
  83. package/dist/policy/trusted.d.ts.map +1 -0
  84. package/dist/policy/trusted.js +96 -0
  85. package/dist/policy/trusted.js.map +1 -0
  86. package/dist/provenance.d.ts +170 -0
  87. package/dist/provenance.d.ts.map +1 -0
  88. package/dist/provenance.js +306 -0
  89. package/dist/provenance.js.map +1 -0
  90. package/dist/receipt/index.d.ts +74 -0
  91. package/dist/receipt/index.d.ts.map +1 -0
  92. package/dist/receipt/index.js +154 -0
  93. package/dist/receipt/index.js.map +1 -0
  94. package/dist/report/index.d.ts +100 -0
  95. package/dist/report/index.d.ts.map +1 -0
  96. package/dist/report/index.js +355 -0
  97. package/dist/report/index.js.map +1 -0
  98. package/dist/schemas/adoption.d.ts +40 -0
  99. package/dist/schemas/adoption.d.ts.map +1 -0
  100. package/dist/schemas/adoption.js +87 -0
  101. package/dist/schemas/adoption.js.map +1 -0
  102. package/dist/schemas/baseline.d.ts +19 -0
  103. package/dist/schemas/baseline.d.ts.map +1 -0
  104. package/dist/schemas/baseline.js +45 -0
  105. package/dist/schemas/baseline.js.map +1 -0
  106. package/dist/schemas/claim.d.ts +32 -0
  107. package/dist/schemas/claim.d.ts.map +1 -0
  108. package/dist/schemas/claim.js +34 -0
  109. package/dist/schemas/claim.js.map +1 -0
  110. package/dist/schemas/classification-policy.d.ts +84 -0
  111. package/dist/schemas/classification-policy.d.ts.map +1 -0
  112. package/dist/schemas/classification-policy.js +130 -0
  113. package/dist/schemas/classification-policy.js.map +1 -0
  114. package/dist/schemas/classification-signal.d.ts +125 -0
  115. package/dist/schemas/classification-signal.d.ts.map +1 -0
  116. package/dist/schemas/classification-signal.js +126 -0
  117. package/dist/schemas/classification-signal.js.map +1 -0
  118. package/dist/schemas/classification.d.ts +120 -0
  119. package/dist/schemas/classification.d.ts.map +1 -0
  120. package/dist/schemas/classification.js +154 -0
  121. package/dist/schemas/classification.js.map +1 -0
  122. package/dist/schemas/common.d.ts +73 -0
  123. package/dist/schemas/common.d.ts.map +1 -0
  124. package/dist/schemas/common.js +60 -0
  125. package/dist/schemas/common.js.map +1 -0
  126. package/dist/schemas/coverage-policy.d.ts +89 -0
  127. package/dist/schemas/coverage-policy.d.ts.map +1 -0
  128. package/dist/schemas/coverage-policy.js +81 -0
  129. package/dist/schemas/coverage-policy.js.map +1 -0
  130. package/dist/schemas/evidence.d.ts +65 -0
  131. package/dist/schemas/evidence.d.ts.map +1 -0
  132. package/dist/schemas/evidence.js +64 -0
  133. package/dist/schemas/evidence.js.map +1 -0
  134. package/dist/schemas/execution-result.d.ts +214 -0
  135. package/dist/schemas/execution-result.d.ts.map +1 -0
  136. package/dist/schemas/execution-result.js +240 -0
  137. package/dist/schemas/execution-result.js.map +1 -0
  138. package/dist/schemas/gate-receipt.d.ts +86 -0
  139. package/dist/schemas/gate-receipt.d.ts.map +1 -0
  140. package/dist/schemas/gate-receipt.js +193 -0
  141. package/dist/schemas/gate-receipt.js.map +1 -0
  142. package/dist/schemas/index.d.ts +22 -0
  143. package/dist/schemas/index.d.ts.map +1 -0
  144. package/dist/schemas/index.js +22 -0
  145. package/dist/schemas/index.js.map +1 -0
  146. package/dist/schemas/obligation.d.ts +37 -0
  147. package/dist/schemas/obligation.d.ts.map +1 -0
  148. package/dist/schemas/obligation.js +46 -0
  149. package/dist/schemas/obligation.js.map +1 -0
  150. package/dist/schemas/plugin.d.ts +19 -0
  151. package/dist/schemas/plugin.d.ts.map +1 -0
  152. package/dist/schemas/plugin.js +20 -0
  153. package/dist/schemas/plugin.js.map +1 -0
  154. package/dist/schemas/policy.d.ts +73 -0
  155. package/dist/schemas/policy.d.ts.map +1 -0
  156. package/dist/schemas/policy.js +46 -0
  157. package/dist/schemas/policy.js.map +1 -0
  158. package/dist/schemas/resource.d.ts +26 -0
  159. package/dist/schemas/resource.d.ts.map +1 -0
  160. package/dist/schemas/resource.js +29 -0
  161. package/dist/schemas/resource.js.map +1 -0
  162. package/dist/schemas/run-manifest.d.ts +72 -0
  163. package/dist/schemas/run-manifest.d.ts.map +1 -0
  164. package/dist/schemas/run-manifest.js +125 -0
  165. package/dist/schemas/run-manifest.js.map +1 -0
  166. package/dist/schemas/runner-adapter.d.ts +169 -0
  167. package/dist/schemas/runner-adapter.d.ts.map +1 -0
  168. package/dist/schemas/runner-adapter.js +2 -0
  169. package/dist/schemas/runner-adapter.js.map +1 -0
  170. package/dist/schemas/test-catalog.d.ts +472 -0
  171. package/dist/schemas/test-catalog.d.ts.map +1 -0
  172. package/dist/schemas/test-catalog.js +311 -0
  173. package/dist/schemas/test-catalog.js.map +1 -0
  174. package/dist/schemas/test-map.d.ts +99 -0
  175. package/dist/schemas/test-map.d.ts.map +1 -0
  176. package/dist/schemas/test-map.js +103 -0
  177. package/dist/schemas/test-map.js.map +1 -0
  178. package/dist/schemas/verdict.d.ts +73 -0
  179. package/dist/schemas/verdict.d.ts.map +1 -0
  180. package/dist/schemas/verdict.js +92 -0
  181. package/dist/schemas/verdict.js.map +1 -0
  182. package/dist/schemas/waiver.d.ts +36 -0
  183. package/dist/schemas/waiver.d.ts.map +1 -0
  184. package/dist/schemas/waiver.js +42 -0
  185. package/dist/schemas/waiver.js.map +1 -0
  186. package/dist/supervision/index.d.ts +170 -0
  187. package/dist/supervision/index.d.ts.map +1 -0
  188. package/dist/supervision/index.js +354 -0
  189. package/dist/supervision/index.js.map +1 -0
  190. package/dist/testing/clock.d.ts +50 -0
  191. package/dist/testing/clock.d.ts.map +1 -0
  192. package/dist/testing/clock.js +75 -0
  193. package/dist/testing/clock.js.map +1 -0
  194. package/dist/testing/env.d.ts +66 -0
  195. package/dist/testing/env.d.ts.map +1 -0
  196. package/dist/testing/env.js +106 -0
  197. package/dist/testing/env.js.map +1 -0
  198. package/dist/testing/gate-runner.d.ts +141 -0
  199. package/dist/testing/gate-runner.d.ts.map +1 -0
  200. package/dist/testing/gate-runner.js +173 -0
  201. package/dist/testing/gate-runner.js.map +1 -0
  202. package/dist/testing/gf19.d.ts +106 -0
  203. package/dist/testing/gf19.d.ts.map +1 -0
  204. package/dist/testing/gf19.js +282 -0
  205. package/dist/testing/gf19.js.map +1 -0
  206. package/dist/testing/index.d.ts +20 -0
  207. package/dist/testing/index.d.ts.map +1 -0
  208. package/dist/testing/index.js +14 -0
  209. package/dist/testing/index.js.map +1 -0
  210. package/dist/testing/red-probe.d.ts +138 -0
  211. package/dist/testing/red-probe.d.ts.map +1 -0
  212. package/dist/testing/red-probe.js +251 -0
  213. package/dist/testing/red-probe.js.map +1 -0
  214. package/dist/testing/temp-repo.d.ts +126 -0
  215. package/dist/testing/temp-repo.d.ts.map +1 -0
  216. package/dist/testing/temp-repo.js +214 -0
  217. package/dist/testing/temp-repo.js.map +1 -0
  218. package/dist/verdict/cause.d.ts +99 -0
  219. package/dist/verdict/cause.d.ts.map +1 -0
  220. package/dist/verdict/cause.js +206 -0
  221. package/dist/verdict/cause.js.map +1 -0
  222. package/dist/verdict/evaluate.d.ts +135 -0
  223. package/dist/verdict/evaluate.d.ts.map +1 -0
  224. package/dist/verdict/evaluate.js +1513 -0
  225. package/dist/verdict/evaluate.js.map +1 -0
  226. package/dist/verdict/index.d.ts +21 -0
  227. package/dist/verdict/index.d.ts.map +1 -0
  228. package/dist/verdict/index.js +20 -0
  229. package/dist/verdict/index.js.map +1 -0
  230. package/dist/verdict/pack-verifiers.d.ts +97 -0
  231. package/dist/verdict/pack-verifiers.d.ts.map +1 -0
  232. package/dist/verdict/pack-verifiers.js +763 -0
  233. package/dist/verdict/pack-verifiers.js.map +1 -0
  234. package/dist/verdict/registry.d.ts +180 -0
  235. package/dist/verdict/registry.d.ts.map +1 -0
  236. package/dist/verdict/registry.js +67 -0
  237. package/dist/verdict/registry.js.map +1 -0
  238. package/dist/waivers/index.d.ts +103 -0
  239. package/dist/waivers/index.d.ts.map +1 -0
  240. package/dist/waivers/index.js +192 -0
  241. package/dist/waivers/index.js.map +1 -0
  242. package/package.json +40 -0
@@ -0,0 +1,170 @@
1
+ import type { RecordOrigin } from './schemas/evidence.js';
2
+ /** The record identity a witness hashes into the service-issued id. */
3
+ export interface RecordIdentity {
4
+ /** Run manifest identity the record was issued under. */
5
+ runId: string;
6
+ /** Obligation the record evidences. */
7
+ obligationId: string;
8
+ /** Evidence kind (e.g. `ui.action`, `persistence.entity`). */
9
+ kind: string;
10
+ /** Witness-issued test attribution. */
11
+ testId: string;
12
+ /** Where the contents came from (drives the trust stamp at issuance). */
13
+ origin: RecordOrigin;
14
+ /** JSON-representable evidence payload. */
15
+ payload: unknown;
16
+ }
17
+ /**
18
+ * Derives the service-issued record id: sha256 over the GF-canonical
19
+ * JSON of exactly the identity fields (pin #1/#7). Deterministic and
20
+ * stable across runs.
21
+ *
22
+ * Args:
23
+ * identity: runId, obligationId, kind, testId, origin, and payload.
24
+ *
25
+ * Returns:
26
+ * string: 64-char lowercase hex digest.
27
+ *
28
+ * Throws:
29
+ * TypeError: when the payload is not JSON-representable (the witness
30
+ * refuses to issue such records; callers issuing must too).
31
+ */
32
+ export declare function recordIdOf(identity: {
33
+ runId: string;
34
+ obligationId: string;
35
+ kind: string;
36
+ testId: string;
37
+ origin: RecordOrigin;
38
+ payload: unknown;
39
+ }): string;
40
+ /**
41
+ * Verifies a lenient record view against its own provenance (check 1):
42
+ * the recordId must be 64-hex, every identity field present, and the
43
+ * id must recompute from the contents. Adversary-controlled input can
44
+ * never throw — any structural problem is `false` (the record then
45
+ * demotes to claimed tier, GF-23).
46
+ *
47
+ * Args:
48
+ * record: an arbitrary record-shaped value (e.g. a `records.json`
49
+ * entry or a witness ledger row).
50
+ *
51
+ * Returns:
52
+ * boolean: true only when the record's own contents hash to its id.
53
+ */
54
+ export declare function isProvenancedRecord(record: unknown): boolean;
55
+ /**
56
+ * True when a record asserts the witnessed tier AND its provenance
57
+ * verifies ({@link isProvenancedRecord}). The single witnessed-tier
58
+ * predicate for the engine and the reporter's trust display, so a
59
+ * record can never display as witnessed while the engine would demote
60
+ * it (or vice versa).
61
+ *
62
+ * Args:
63
+ * record: an arbitrary record-shaped value.
64
+ *
65
+ * Returns:
66
+ * boolean: witnessed tier with verified provenance.
67
+ */
68
+ export declare function isWitnessedRecord(record: unknown): boolean;
69
+ /** Domain tag binding v2 attestation MACs to the ledger envelope format. */
70
+ export declare const ATTESTATION_DOMAIN = "gateforge.ledger.v2";
71
+ /** The only attestation envelope version this code produces or honors. */
72
+ export declare const ATTESTATION_VERSION = 2;
73
+ /**
74
+ * The unsigned v2 attestation body: the witness run identity, the fresh
75
+ * trusted invocation identity, the tested input digest, and the issued
76
+ * record set. The MAC covers exactly these fields plus the domain tag —
77
+ * never `mac` itself.
78
+ */
79
+ export interface AttestationBody {
80
+ /** Witness run UUID. */
81
+ runId: string;
82
+ /** Fresh UUID minted by the trusted test-gates caller per invocation. */
83
+ invocationId: string;
84
+ /** 64-char lowercase hex digest of the canonical input snapshot. */
85
+ inputDigest: string;
86
+ /** Issued record ids (normalized internally: sorted, unique). */
87
+ recordIds: readonly string[];
88
+ }
89
+ /**
90
+ * Computes the witness-ledger MAC (pin #7): HMAC-SHA256 over the
91
+ * GF-canonical JSON of `{runId, recordIds}` keyed by the witness's
92
+ * VERIFIER KEY — a secret handed to the witness and the verifying CLI
93
+ * by the run orchestrator, never to the tested suite (the suite's run
94
+ * token authorizes submissions; it must not authorize attestation).
95
+ * The MAC makes the durable, suite-writable manifest append tamper-
96
+ * evident: changing one id, adding a forged id, or transplanting the
97
+ * set across runs invalidates it.
98
+ *
99
+ * Args:
100
+ * verifierKey: the witness verifier secret (non-empty).
101
+ * runId: the run manifest identity binding the set to THIS run.
102
+ * recordIds: the issued record ids (normalized internally).
103
+ *
104
+ * Returns:
105
+ * string: 64-char lowercase hex HMAC.
106
+ *
107
+ * Throws:
108
+ * TypeError: when the verifier key is empty.
109
+ */
110
+ export declare function ledgerMac(verifierKey: string, runId: string, recordIds: readonly string[]): string;
111
+ /**
112
+ * Verifies a ledger MAC ({@link ledgerMac}) in constant time where the
113
+ * inputs allow it. Adversary-controlled input never throws.
114
+ *
115
+ * Args:
116
+ * verifierKey: the witness verifier secret (non-empty).
117
+ * runId: the run manifest identity the set claims.
118
+ * recordIds: the record-id set the MAC must cover (normalized
119
+ * internally, exactly like the witness computed it).
120
+ * mac: the claimed MAC (64-char lowercase hex).
121
+ *
122
+ * Returns:
123
+ * boolean: true only when the MAC verifies over the exact set.
124
+ */
125
+ export declare function verifyLedgerMac(verifierKey: string, runId: string, recordIds: readonly string[], mac: unknown): boolean;
126
+ /**
127
+ * Computes the v2 attestation MAC (plan §11.3): HMAC-SHA256 over the
128
+ * GF-canonical JSON of `{domain, attestationVersion: 2, runId,
129
+ * invocationId, inputDigest, recordIds}` keyed by the witness's VERIFIER
130
+ * KEY — a secret the tested suite never receives. The fixed domain tag
131
+ * prevents cross-format signature acceptance: a legacy `{runId,
132
+ * recordIds}` MAC can never verify as a v2 attestation, and a v2 MAC
133
+ * can never verify as anything else.
134
+ *
135
+ * Args:
136
+ * verifierKey: the witness verifier secret (non-empty).
137
+ * body: runId, invocationId, 64-hex inputDigest, and issued record ids
138
+ * (normalized internally: deduplicated, codepoint-sorted).
139
+ *
140
+ * Returns:
141
+ * string: 64-char lowercase hex HMAC.
142
+ *
143
+ * Throws:
144
+ * TypeError: when the verifier key is empty, an identity is empty, or
145
+ * the input digest is not 64-char lowercase hex.
146
+ */
147
+ export declare function attestationMac(verifierKey: string, body: AttestationBody): string;
148
+ /**
149
+ * Verifies a v2 attestation MAC ({@link attestationMac}) in constant
150
+ * time where the inputs allow it. Adversary-controlled input never
151
+ * throws. A legacy `{runId, recordIds}` MAC is structurally incapable
152
+ * of verifying here — the domain tag and the extra fields change the
153
+ * signed bytes — so old evidence can never authorize through this path.
154
+ *
155
+ * Args:
156
+ * verifierKey: the witness verifier secret (non-empty).
157
+ * body: the claimed attestation body (runId, invocationId,
158
+ * inputDigest, recordIds).
159
+ * mac: the claimed MAC (64-char lowercase hex).
160
+ *
161
+ * Returns:
162
+ * boolean: true only when the MAC verifies over the exact v2 body.
163
+ */
164
+ export declare function verifyAttestationMac(verifierKey: string, body: {
165
+ runId: unknown;
166
+ invocationId: unknown;
167
+ inputDigest: unknown;
168
+ recordIds: unknown;
169
+ }, mac: unknown): boolean;
170
+ //# sourceMappingURL=provenance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provenance.d.ts","sourceRoot":"","sources":["../src/provenance.ts"],"names":[],"mappings":"AAyCA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,uEAAuE;AACvE,MAAM,WAAW,cAAc;IAC7B,yDAAyD;IACzD,KAAK,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,YAAY,EAAE,MAAM,CAAC;IACrB,8DAA8D;IAC9D,IAAI,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,MAAM,EAAE,YAAY,CAAC;IACrB,2CAA2C;IAC3C,OAAO,EAAE,OAAO,CAAC;CAClB;AAKD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,UAAU,CAAC,QAAQ,EAAE;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,YAAY,CAAC;IACrB,OAAO,EAAE,OAAO,CAAC;CAClB,GAAG,MAAM,CAeT;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAyC5D;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,OAAO,CAQ1D;AAKD,4EAA4E;AAC5E,eAAO,MAAM,kBAAkB,wBAAwB,CAAC;AAExD,0EAA0E;AAC1E,eAAO,MAAM,mBAAmB,IAAI,CAAC;AAKrC;;;;;GAKG;AACH,MAAM,WAAW,eAAe;IAC9B,wBAAwB;IACxB,KAAK,EAAE,MAAM,CAAC;IACd,yEAAyE;IACzE,YAAY,EAAE,MAAM,CAAC;IACrB,oEAAoE;IACpE,WAAW,EAAE,MAAM,CAAC;IACpB,iEAAiE;IACjE,SAAS,EAAE,SAAS,MAAM,EAAE,CAAC;CAC9B;AAWD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,SAAS,CACvB,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,SAAS,MAAM,EAAE,GAC3B,MAAM,CAUR;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAC7B,WAAW,EAAE,MAAM,EACnB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,SAAS,MAAM,EAAE,EAC5B,GAAG,EAAE,OAAO,GACX,OAAO,CAST;AAED;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,MAAM,CAyBjF;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,oBAAoB,CAClC,WAAW,EAAE,MAAM,EACnB,IAAI,EAAE;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,OAAO,CAAC;IAAC,WAAW,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,EACzF,GAAG,EAAE,OAAO,GACX,OAAO,CAmBT"}
@@ -0,0 +1,306 @@
1
+ /**
2
+ * Witness provenance verification (pin #7, ADR 0001 D2c, GF-23).
3
+ *
4
+ * The witness derives every record id as `sha256(canonicalJson({
5
+ * runId, obligationId, kind, testId, payload }))` (pin #1) — a
6
+ * deterministic function of the record's identity alone. That makes
7
+ * provenance CHECKABLE by any consumer without contacting the witness:
8
+ * an entry that never passed through the service cannot carry a record
9
+ * id that both recomputes correctly and appears in an AUTHENTICATED
10
+ * copy of the witness-issued set.
11
+ *
12
+ * Three independent checks exist, and all must pass for witnessed tier:
13
+ * 1. {@link isProvenancedRecord} — the record id recomputes from the
14
+ * record's own contents (structural integrity; catches arbitrary or
15
+ * transplanted hex ids). Verified here, in the engine, so every
16
+ * consumer of `evaluateObligation` gets it.
17
+ * 2. Authenticated single-envelope membership (plan §11.5–§11.6) — the
18
+ * id appears in a v2 attestation envelope ({@link attestationMac})
19
+ * whose integrity is protected by a secret the tested suite never
20
+ * receives: the witness's verifier key. The witness serves the live
21
+ * envelope at `GET /ledger-attestation` (verifier-key header, bound
22
+ * context only) and stamps the durable manifest append with the same
23
+ * signed object. The envelope binds the run id, the fresh invocation
24
+ * id, the tested input digest, and the issued set — one envelope must
25
+ * match all four at once; contexts are never merged. Hash
26
+ * recomputation is public, and the run manifest lives in the
27
+ * suite-writable state directory, so PLAIN manifest membership is
28
+ * trustworthiness-neutral: a hostile suite can fabricate both the
29
+ * records and the id list, but it cannot produce a valid MAC for a
30
+ * context the witness never attested. The legacy {@link ledgerMac}
31
+ * over `{runId, recordIds}` stays readable for diagnostics but NEVER
32
+ * authorizes evidence (it binds no input snapshot).
33
+ * 3. Run identity — record.runId equals the authorizing envelope's
34
+ * runId, so ids cannot be transplanted across runs.
35
+ *
36
+ * Enforcement lives in the CLI's provenance gate (packages/cli), which
37
+ * owns the run manifest and the verifier key surface.
38
+ */
39
+ import { createHmac, timingSafeEqual } from 'node:crypto';
40
+ import { canonicalJson, isJsonValue, sha256Canonical } from './canonical-json.js';
41
+ import { compareStrings } from './graph/util.js';
42
+ /** Shape a 64-char lowercase hex record id must have. */
43
+ const RECORD_ID_PATTERN = /^[0-9a-f]{64}$/;
44
+ /**
45
+ * Derives the service-issued record id: sha256 over the GF-canonical
46
+ * JSON of exactly the identity fields (pin #1/#7). Deterministic and
47
+ * stable across runs.
48
+ *
49
+ * Args:
50
+ * identity: runId, obligationId, kind, testId, origin, and payload.
51
+ *
52
+ * Returns:
53
+ * string: 64-char lowercase hex digest.
54
+ *
55
+ * Throws:
56
+ * TypeError: when the payload is not JSON-representable (the witness
57
+ * refuses to issue such records; callers issuing must too).
58
+ */
59
+ export function recordIdOf(identity) {
60
+ if (!isJsonValue(identity.payload)) {
61
+ throw new TypeError('recordIdOf: payload is not GF-canonical-JSON-representable');
62
+ }
63
+ if (identity.origin !== 'suite-submitted' && identity.origin !== 'engine-observed') {
64
+ throw new TypeError('recordIdOf: origin must be suite-submitted or engine-observed');
65
+ }
66
+ return sha256Canonical({
67
+ runId: identity.runId,
68
+ obligationId: identity.obligationId,
69
+ kind: identity.kind,
70
+ testId: identity.testId,
71
+ origin: identity.origin,
72
+ payload: identity.payload,
73
+ });
74
+ }
75
+ /**
76
+ * Verifies a lenient record view against its own provenance (check 1):
77
+ * the recordId must be 64-hex, every identity field present, and the
78
+ * id must recompute from the contents. Adversary-controlled input can
79
+ * never throw — any structural problem is `false` (the record then
80
+ * demotes to claimed tier, GF-23).
81
+ *
82
+ * Args:
83
+ * record: an arbitrary record-shaped value (e.g. a `records.json`
84
+ * entry or a witness ledger row).
85
+ *
86
+ * Returns:
87
+ * boolean: true only when the record's own contents hash to its id.
88
+ */
89
+ export function isProvenancedRecord(record) {
90
+ if (typeof record !== 'object' || record === null || Array.isArray(record)) {
91
+ return false;
92
+ }
93
+ const candidate = record;
94
+ const recordId = candidate['recordId'];
95
+ if (typeof recordId !== 'string' || !RECORD_ID_PATTERN.test(recordId))
96
+ return false;
97
+ const runId = candidate['runId'];
98
+ const obligationId = candidate['obligationId'];
99
+ const kind = candidate['kind'];
100
+ const testId = candidate['testId'];
101
+ if (typeof runId !== 'string' ||
102
+ runId.length === 0 ||
103
+ typeof obligationId !== 'string' ||
104
+ obligationId.length === 0 ||
105
+ typeof kind !== 'string' ||
106
+ kind.length === 0 ||
107
+ typeof testId !== 'string' ||
108
+ testId.length === 0) {
109
+ return false;
110
+ }
111
+ const payload = candidate['payload'];
112
+ if (!isJsonValue(payload))
113
+ return false;
114
+ const origin = candidate['origin'];
115
+ if (origin !== 'suite-submitted' && origin !== 'engine-observed')
116
+ return false;
117
+ try {
118
+ return (recordIdOf({
119
+ runId,
120
+ obligationId,
121
+ kind,
122
+ testId,
123
+ origin,
124
+ payload,
125
+ }) === recordId);
126
+ }
127
+ catch {
128
+ return false;
129
+ }
130
+ }
131
+ /**
132
+ * True when a record asserts the witnessed tier AND its provenance
133
+ * verifies ({@link isProvenancedRecord}). The single witnessed-tier
134
+ * predicate for the engine and the reporter's trust display, so a
135
+ * record can never display as witnessed while the engine would demote
136
+ * it (or vice versa).
137
+ *
138
+ * Args:
139
+ * record: an arbitrary record-shaped value.
140
+ *
141
+ * Returns:
142
+ * boolean: witnessed tier with verified provenance.
143
+ */
144
+ export function isWitnessedRecord(record) {
145
+ if (typeof record !== 'object' || record === null || Array.isArray(record)) {
146
+ return false;
147
+ }
148
+ return (record['trust'] === 'witnessed' &&
149
+ isProvenancedRecord(record));
150
+ }
151
+ /** Shape a 64-char lowercase hex MAC must have. */
152
+ const MAC_PATTERN = /^[0-9a-f]{64}$/;
153
+ /** Domain tag binding v2 attestation MACs to the ledger envelope format. */
154
+ export const ATTESTATION_DOMAIN = 'gateforge.ledger.v2';
155
+ /** The only attestation envelope version this code produces or honors. */
156
+ export const ATTESTATION_VERSION = 2;
157
+ /** Shape a 64-char lowercase hex input digest must have. */
158
+ const INPUT_DIGEST_PATTERN = /^[0-9a-f]{64}$/;
159
+ /**
160
+ * Normalizes a record-id set for MAC computation: deduplicated and
161
+ * sorted by codepoint, so the witness and every verifier hash the same
162
+ * canonical list regardless of insertion or serialization order.
163
+ */
164
+ function canonicalIdSet(recordIds) {
165
+ return [...new Set(recordIds)].sort(compareStrings);
166
+ }
167
+ /**
168
+ * Computes the witness-ledger MAC (pin #7): HMAC-SHA256 over the
169
+ * GF-canonical JSON of `{runId, recordIds}` keyed by the witness's
170
+ * VERIFIER KEY — a secret handed to the witness and the verifying CLI
171
+ * by the run orchestrator, never to the tested suite (the suite's run
172
+ * token authorizes submissions; it must not authorize attestation).
173
+ * The MAC makes the durable, suite-writable manifest append tamper-
174
+ * evident: changing one id, adding a forged id, or transplanting the
175
+ * set across runs invalidates it.
176
+ *
177
+ * Args:
178
+ * verifierKey: the witness verifier secret (non-empty).
179
+ * runId: the run manifest identity binding the set to THIS run.
180
+ * recordIds: the issued record ids (normalized internally).
181
+ *
182
+ * Returns:
183
+ * string: 64-char lowercase hex HMAC.
184
+ *
185
+ * Throws:
186
+ * TypeError: when the verifier key is empty.
187
+ */
188
+ export function ledgerMac(verifierKey, runId, recordIds) {
189
+ if (typeof verifierKey !== 'string' || verifierKey.length === 0) {
190
+ throw new TypeError('ledgerMac: verifier key must be a non-empty string');
191
+ }
192
+ if (typeof runId !== 'string' || runId.length === 0) {
193
+ throw new TypeError('ledgerMac: runId must be a non-empty string');
194
+ }
195
+ return createHmac('sha256', verifierKey)
196
+ .update(canonicalJson({ runId, recordIds: canonicalIdSet(recordIds) }))
197
+ .digest('hex');
198
+ }
199
+ /**
200
+ * Verifies a ledger MAC ({@link ledgerMac}) in constant time where the
201
+ * inputs allow it. Adversary-controlled input never throws.
202
+ *
203
+ * Args:
204
+ * verifierKey: the witness verifier secret (non-empty).
205
+ * runId: the run manifest identity the set claims.
206
+ * recordIds: the record-id set the MAC must cover (normalized
207
+ * internally, exactly like the witness computed it).
208
+ * mac: the claimed MAC (64-char lowercase hex).
209
+ *
210
+ * Returns:
211
+ * boolean: true only when the MAC verifies over the exact set.
212
+ */
213
+ export function verifyLedgerMac(verifierKey, runId, recordIds, mac) {
214
+ if (typeof mac !== 'string' || !MAC_PATTERN.test(mac))
215
+ return false;
216
+ try {
217
+ const expected = Buffer.from(ledgerMac(verifierKey, runId, recordIds), 'hex');
218
+ const claimed = Buffer.from(mac, 'hex');
219
+ return expected.length === claimed.length && timingSafeEqual(expected, claimed);
220
+ }
221
+ catch {
222
+ return false;
223
+ }
224
+ }
225
+ /**
226
+ * Computes the v2 attestation MAC (plan §11.3): HMAC-SHA256 over the
227
+ * GF-canonical JSON of `{domain, attestationVersion: 2, runId,
228
+ * invocationId, inputDigest, recordIds}` keyed by the witness's VERIFIER
229
+ * KEY — a secret the tested suite never receives. The fixed domain tag
230
+ * prevents cross-format signature acceptance: a legacy `{runId,
231
+ * recordIds}` MAC can never verify as a v2 attestation, and a v2 MAC
232
+ * can never verify as anything else.
233
+ *
234
+ * Args:
235
+ * verifierKey: the witness verifier secret (non-empty).
236
+ * body: runId, invocationId, 64-hex inputDigest, and issued record ids
237
+ * (normalized internally: deduplicated, codepoint-sorted).
238
+ *
239
+ * Returns:
240
+ * string: 64-char lowercase hex HMAC.
241
+ *
242
+ * Throws:
243
+ * TypeError: when the verifier key is empty, an identity is empty, or
244
+ * the input digest is not 64-char lowercase hex.
245
+ */
246
+ export function attestationMac(verifierKey, body) {
247
+ if (typeof verifierKey !== 'string' || verifierKey.length === 0) {
248
+ throw new TypeError('attestationMac: verifier key must be a non-empty string');
249
+ }
250
+ if (typeof body.runId !== 'string' || body.runId.length === 0) {
251
+ throw new TypeError('attestationMac: runId must be a non-empty string');
252
+ }
253
+ if (typeof body.invocationId !== 'string' || body.invocationId.length === 0) {
254
+ throw new TypeError('attestationMac: invocationId must be a non-empty string');
255
+ }
256
+ if (typeof body.inputDigest !== 'string' || !INPUT_DIGEST_PATTERN.test(body.inputDigest)) {
257
+ throw new TypeError('attestationMac: inputDigest must be 64-char lowercase hex');
258
+ }
259
+ return createHmac('sha256', verifierKey)
260
+ .update(canonicalJson({
261
+ domain: ATTESTATION_DOMAIN,
262
+ attestationVersion: ATTESTATION_VERSION,
263
+ runId: body.runId,
264
+ invocationId: body.invocationId,
265
+ inputDigest: body.inputDigest,
266
+ recordIds: canonicalIdSet(body.recordIds),
267
+ }))
268
+ .digest('hex');
269
+ }
270
+ /**
271
+ * Verifies a v2 attestation MAC ({@link attestationMac}) in constant
272
+ * time where the inputs allow it. Adversary-controlled input never
273
+ * throws. A legacy `{runId, recordIds}` MAC is structurally incapable
274
+ * of verifying here — the domain tag and the extra fields change the
275
+ * signed bytes — so old evidence can never authorize through this path.
276
+ *
277
+ * Args:
278
+ * verifierKey: the witness verifier secret (non-empty).
279
+ * body: the claimed attestation body (runId, invocationId,
280
+ * inputDigest, recordIds).
281
+ * mac: the claimed MAC (64-char lowercase hex).
282
+ *
283
+ * Returns:
284
+ * boolean: true only when the MAC verifies over the exact v2 body.
285
+ */
286
+ export function verifyAttestationMac(verifierKey, body, mac) {
287
+ if (typeof mac !== 'string' || !MAC_PATTERN.test(mac))
288
+ return false;
289
+ if (typeof body !== 'object' ||
290
+ body === null ||
291
+ typeof body.runId !== 'string' ||
292
+ typeof body.invocationId !== 'string' ||
293
+ typeof body.inputDigest !== 'string' ||
294
+ !Array.isArray(body.recordIds)) {
295
+ return false;
296
+ }
297
+ try {
298
+ const expected = Buffer.from(attestationMac(verifierKey, body), 'hex');
299
+ const claimed = Buffer.from(mac, 'hex');
300
+ return expected.length === claimed.length && timingSafeEqual(expected, claimed);
301
+ }
302
+ catch {
303
+ return false;
304
+ }
305
+ }
306
+ //# sourceMappingURL=provenance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provenance.js","sourceRoot":"","sources":["../src/provenance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ import { type GateReceipt } from '../schemas/gate-receipt.js';
2
+ /** Domain tag binding receipt MACs to the gate-receipt envelope format. */
3
+ export declare const RECEIPT_DOMAIN = "gateforge.receipt.v1";
4
+ /** The only receipt envelope version this code produces or honors. */
5
+ export declare const RECEIPT_VERSION = 1;
6
+ /** The unsigned receipt body (everything the MAC covers). */
7
+ export type GateReceiptBody = Omit<GateReceipt, 'mac'>;
8
+ /**
9
+ * Computes the receipt MAC (ADR 0005 D3): HMAC-SHA256 over the GF-canonical
10
+ * JSON of `{domain: 'gateforge.receipt.v1', receiptVersion: 1, ...body}`
11
+ * keyed by the witness verifier key — the same secret the tested suite
12
+ * never receives for ledger attestations. The fixed domain tag prevents
13
+ * cross-format signature acceptance in BOTH directions: a v2 ledger MAC
14
+ * can never verify as a receipt, and a receipt MAC can never verify as a
15
+ * ledger attestation.
16
+ *
17
+ * Args:
18
+ * verifierKey: the witness verifier secret (non-empty).
19
+ * body: the unsigned receipt body.
20
+ *
21
+ * Returns:
22
+ * string: 64-char lowercase hex HMAC.
23
+ *
24
+ * Throws:
25
+ * TypeError: when the verifier key is empty.
26
+ */
27
+ export declare function gateReceiptMac(verifierKey: string, body: GateReceiptBody): string;
28
+ /** Typed reasons a receipt candidate failed verification (fail closed). */
29
+ export type ReceiptRejection = 'missing' | 'malformed' | 'mac-fail' | 'input-digest-mismatch' | 'policy-digest-mismatch' | 'selection-digest-mismatch' | 'catalog-digest-mismatch' | 'execution-digest-mismatch' | 'attestation-digest-mismatch' | 'not-clean';
30
+ /** The verification outcome: a validated receipt or a typed rejection. */
31
+ export type ReceiptVerification = {
32
+ ok: true;
33
+ receipt: GateReceipt;
34
+ } | {
35
+ ok: false;
36
+ rejection: ReceiptRejection;
37
+ detail: string;
38
+ };
39
+ /**
40
+ * Verifies a receipt candidate end-to-end (fail closed):
41
+ * 1. structural — the value must parse against the strict v1 schema;
42
+ * 2. authenticity — the HMAC must verify over the exact body (a forged
43
+ * or tampered receipt is a `mac-fail`, never a downgrade);
44
+ * 3. binding — when an expected context is given, every supplied digest
45
+ * must equal the receipt's (mismatches are typed, per-field);
46
+ * 4. content — the sealed verdict summary must be clean (blocking 0).
47
+ * Adversary-controlled input never throws.
48
+ *
49
+ * Args:
50
+ * verifierKey: the witness verifier secret (non-empty).
51
+ * candidate: an arbitrary value (e.g. a parsed receipt.json).
52
+ * expected: optional digests the receipt must match for THIS use.
53
+ *
54
+ * Returns:
55
+ * ReceiptVerification: the validated receipt or a typed rejection.
56
+ */
57
+ export declare function verifyGateReceipt(verifierKey: string, candidate: unknown, expected?: {
58
+ inputDigest?: string;
59
+ trustedPolicyDigest?: string;
60
+ selectionDigest?: string;
61
+ catalogDigest?: string;
62
+ executionResultDigest?: string;
63
+ evidenceAttestationDigest?: string | null;
64
+ }): ReceiptVerification;
65
+ /** The expected-digest shape of {@link verifyGateReceipt} (named for docs). */
66
+ export interface ReceiptVerificationExpectations {
67
+ inputDigest?: string;
68
+ trustedPolicyDigest?: string;
69
+ selectionDigest?: string;
70
+ catalogDigest?: string;
71
+ executionResultDigest?: string;
72
+ evidenceAttestationDigest?: string | null;
73
+ }
74
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/receipt/index.ts"],"names":[],"mappings":"AAiBA,OAAO,EAAqB,KAAK,WAAW,EAAE,MAAM,4BAA4B,CAAC;AAEjF,2EAA2E;AAC3E,eAAO,MAAM,cAAc,yBAAyB,CAAC;AAErD,sEAAsE;AACtE,eAAO,MAAM,eAAe,IAAI,CAAC;AAKjC,6DAA6D;AAC7D,MAAM,MAAM,eAAe,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,CAAC;AAEvD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,eAAe,GAAG,MAAM,CAYjF;AAED,2EAA2E;AAC3E,MAAM,MAAM,gBAAgB,GACxB,SAAS,GACT,WAAW,GACX,UAAU,GACV,uBAAuB,GACvB,wBAAwB,GACxB,2BAA2B,GAC3B,yBAAyB,GACzB,2BAA2B,GAC3B,6BAA6B,GAC7B,WAAW,CAAC;AAEhB,0EAA0E;AAC1E,MAAM,MAAM,mBAAmB,GAC3B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,GAClC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,SAAS,EAAE,gBAAgB,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC;AAE/D;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,iBAAiB,CAC/B,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,OAAO,EAClB,QAAQ,GAAE;IACR,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,yBAAyB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CACtC,GACL,mBAAmB,CAuFrB;AAED,+EAA+E;AAC/E,MAAM,WAAW,+BAA+B;IAC9C,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,qBAAqB,CAAC,EAAE,MAAM,CAAC;IAC/B,yBAAyB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3C"}