@emilia-protocol/gate 0.11.0 → 0.13.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 (419) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +252 -0
  3. package/action-control-manifest.js +3 -268
  4. package/action-escrow-custodian.js +3 -394
  5. package/action-escrow-evidence.js +3 -858
  6. package/action-escrow-package.js +3 -1668
  7. package/action-escrow-postgres.js +3 -337
  8. package/action-escrow-state.js +3 -396
  9. package/action-escrow-verifiers.js +3 -331
  10. package/action-escrow.js +3 -2447
  11. package/action-packs.js +3 -140
  12. package/adapters/_kit.js +3 -104
  13. package/adapters/aws.js +3 -108
  14. package/adapters/cloudflare.js +3 -54
  15. package/adapters/gcp.js +3 -70
  16. package/adapters/github.js +3 -109
  17. package/adapters/jira.js +3 -55
  18. package/adapters/k8s.js +3 -70
  19. package/adapters/linear.js +3 -55
  20. package/adapters/salesforce.js +3 -55
  21. package/adapters/stripe.js +3 -81
  22. package/adapters/supabase.js +3 -121
  23. package/adapters/terraform.js +3 -59
  24. package/adapters/vercel.js +3 -84
  25. package/aec-execution.js +3 -312
  26. package/breakglass.js +3 -582
  27. package/capability-receipt.js +4 -0
  28. package/cf1-conformance.js +3 -106
  29. package/challenge-store.js +3 -69
  30. package/control-plane.js +3 -340
  31. package/coverage.js +3 -721
  32. package/demo.mjs +1 -1
  33. package/deployment-attestation.js +3 -247
  34. package/dist/action-control-manifest.d.ts +82 -0
  35. package/dist/action-control-manifest.d.ts.map +1 -0
  36. package/dist/action-control-manifest.js +422 -0
  37. package/dist/action-control-manifest.js.map +1 -0
  38. package/dist/action-escrow-custodian.d.ts +74 -0
  39. package/dist/action-escrow-custodian.d.ts.map +1 -0
  40. package/dist/action-escrow-custodian.js +363 -0
  41. package/dist/action-escrow-custodian.js.map +1 -0
  42. package/dist/action-escrow-evidence.d.ts +130 -0
  43. package/dist/action-escrow-evidence.d.ts.map +1 -0
  44. package/dist/action-escrow-evidence.js +1012 -0
  45. package/dist/action-escrow-evidence.js.map +1 -0
  46. package/dist/action-escrow-package.d.ts +10 -0
  47. package/dist/action-escrow-package.d.ts.map +1 -0
  48. package/dist/action-escrow-package.js +1583 -0
  49. package/dist/action-escrow-package.js.map +1 -0
  50. package/dist/action-escrow-postgres.d.ts +62 -0
  51. package/dist/action-escrow-postgres.d.ts.map +1 -0
  52. package/dist/action-escrow-postgres.js +339 -0
  53. package/dist/action-escrow-postgres.js.map +1 -0
  54. package/dist/action-escrow-state.d.ts +141 -0
  55. package/dist/action-escrow-state.d.ts.map +1 -0
  56. package/dist/action-escrow-state.js +413 -0
  57. package/dist/action-escrow-state.js.map +1 -0
  58. package/dist/action-escrow-verifiers.d.ts +65 -0
  59. package/dist/action-escrow-verifiers.d.ts.map +1 -0
  60. package/dist/action-escrow-verifiers.js +421 -0
  61. package/dist/action-escrow-verifiers.js.map +1 -0
  62. package/dist/action-escrow.d.ts +112 -0
  63. package/dist/action-escrow.d.ts.map +1 -0
  64. package/dist/action-escrow.js +2661 -0
  65. package/dist/action-escrow.js.map +1 -0
  66. package/dist/action-packs.d.ts +54 -0
  67. package/dist/action-packs.d.ts.map +1 -0
  68. package/dist/action-packs.js +143 -0
  69. package/dist/action-packs.js.map +1 -0
  70. package/dist/adapters/_kit.d.ts +50 -0
  71. package/dist/adapters/_kit.d.ts.map +1 -0
  72. package/dist/adapters/_kit.js +98 -0
  73. package/dist/adapters/_kit.js.map +1 -0
  74. package/dist/adapters/aws.d.ts +158 -0
  75. package/dist/adapters/aws.d.ts.map +1 -0
  76. package/dist/adapters/aws.js +136 -0
  77. package/dist/adapters/aws.js.map +1 -0
  78. package/dist/adapters/cloudflare.d.ts +116 -0
  79. package/dist/adapters/cloudflare.d.ts.map +1 -0
  80. package/dist/adapters/cloudflare.js +75 -0
  81. package/dist/adapters/cloudflare.js.map +1 -0
  82. package/dist/adapters/gcp.d.ts +139 -0
  83. package/dist/adapters/gcp.d.ts.map +1 -0
  84. package/dist/adapters/gcp.js +69 -0
  85. package/dist/adapters/gcp.js.map +1 -0
  86. package/dist/adapters/github.d.ts +123 -0
  87. package/dist/adapters/github.d.ts.map +1 -0
  88. package/dist/adapters/github.js +105 -0
  89. package/dist/adapters/github.js.map +1 -0
  90. package/dist/adapters/jira.d.ts +109 -0
  91. package/dist/adapters/jira.d.ts.map +1 -0
  92. package/dist/adapters/jira.js +54 -0
  93. package/dist/adapters/jira.js.map +1 -0
  94. package/dist/adapters/k8s.d.ts +139 -0
  95. package/dist/adapters/k8s.d.ts.map +1 -0
  96. package/dist/adapters/k8s.js +69 -0
  97. package/dist/adapters/k8s.js.map +1 -0
  98. package/dist/adapters/linear.d.ts +109 -0
  99. package/dist/adapters/linear.d.ts.map +1 -0
  100. package/dist/adapters/linear.js +54 -0
  101. package/dist/adapters/linear.js.map +1 -0
  102. package/dist/adapters/salesforce.d.ts +109 -0
  103. package/dist/adapters/salesforce.d.ts.map +1 -0
  104. package/dist/adapters/salesforce.js +54 -0
  105. package/dist/adapters/salesforce.js.map +1 -0
  106. package/dist/adapters/stripe.d.ts +116 -0
  107. package/dist/adapters/stripe.d.ts.map +1 -0
  108. package/dist/adapters/stripe.js +77 -0
  109. package/dist/adapters/stripe.js.map +1 -0
  110. package/dist/adapters/supabase.d.ts +127 -0
  111. package/dist/adapters/supabase.d.ts.map +1 -0
  112. package/dist/adapters/supabase.js +112 -0
  113. package/dist/adapters/supabase.js.map +1 -0
  114. package/dist/adapters/terraform.d.ts +109 -0
  115. package/dist/adapters/terraform.d.ts.map +1 -0
  116. package/dist/adapters/terraform.js +58 -0
  117. package/dist/adapters/terraform.js.map +1 -0
  118. package/dist/adapters/vercel.d.ts +114 -0
  119. package/dist/adapters/vercel.d.ts.map +1 -0
  120. package/dist/adapters/vercel.js +81 -0
  121. package/dist/adapters/vercel.js.map +1 -0
  122. package/dist/aec-execution.d.ts +107 -0
  123. package/dist/aec-execution.d.ts.map +1 -0
  124. package/dist/aec-execution.js +334 -0
  125. package/dist/aec-execution.js.map +1 -0
  126. package/dist/breakglass.d.ts +205 -0
  127. package/dist/breakglass.d.ts.map +1 -0
  128. package/dist/breakglass.js +627 -0
  129. package/dist/breakglass.js.map +1 -0
  130. package/dist/capability-receipt.d.ts +601 -0
  131. package/dist/capability-receipt.d.ts.map +1 -0
  132. package/dist/capability-receipt.js +1177 -0
  133. package/dist/capability-receipt.js.map +1 -0
  134. package/dist/cf1-conformance.d.ts +83 -0
  135. package/dist/cf1-conformance.d.ts.map +1 -0
  136. package/dist/cf1-conformance.js +94 -0
  137. package/dist/cf1-conformance.js.map +1 -0
  138. package/dist/challenge-store.d.ts +20 -0
  139. package/dist/challenge-store.d.ts.map +1 -0
  140. package/dist/challenge-store.js +62 -0
  141. package/dist/challenge-store.js.map +1 -0
  142. package/dist/control-plane.d.ts +136 -0
  143. package/dist/control-plane.d.ts.map +1 -0
  144. package/dist/control-plane.js +349 -0
  145. package/dist/control-plane.js.map +1 -0
  146. package/dist/coverage.d.ts +156 -0
  147. package/dist/coverage.d.ts.map +1 -0
  148. package/dist/coverage.js +768 -0
  149. package/dist/coverage.js.map +1 -0
  150. package/dist/deployment-attestation.d.ts +93 -0
  151. package/dist/deployment-attestation.d.ts.map +1 -0
  152. package/dist/deployment-attestation.js +262 -0
  153. package/dist/deployment-attestation.js.map +1 -0
  154. package/dist/eg1-conformance.d.ts +286 -0
  155. package/dist/eg1-conformance.d.ts.map +1 -0
  156. package/dist/eg1-conformance.js +379 -0
  157. package/dist/eg1-conformance.js.map +1 -0
  158. package/dist/enterprise.d.ts +88 -0
  159. package/dist/enterprise.d.ts.map +1 -0
  160. package/dist/enterprise.js +194 -0
  161. package/dist/enterprise.js.map +1 -0
  162. package/dist/evidence-postgres.d.ts +123 -0
  163. package/dist/evidence-postgres.d.ts.map +1 -0
  164. package/dist/evidence-postgres.js +343 -0
  165. package/dist/evidence-postgres.js.map +1 -0
  166. package/dist/evidence.d.ts +113 -0
  167. package/dist/evidence.d.ts.map +1 -0
  168. package/dist/evidence.js +369 -0
  169. package/dist/evidence.js.map +1 -0
  170. package/dist/execution-binding.d.ts +49 -0
  171. package/dist/execution-binding.d.ts.map +1 -0
  172. package/dist/execution-binding.js +243 -0
  173. package/dist/execution-binding.js.map +1 -0
  174. package/dist/formal-runtime-map.d.ts +42 -0
  175. package/dist/formal-runtime-map.d.ts.map +1 -0
  176. package/dist/formal-runtime-map.js +23 -0
  177. package/dist/formal-runtime-map.js.map +1 -0
  178. package/dist/index.d.ts +1002 -0
  179. package/dist/index.d.ts.map +1 -0
  180. package/dist/index.js +1736 -0
  181. package/dist/index.js.map +1 -0
  182. package/dist/key-registry.d.ts +60 -0
  183. package/dist/key-registry.d.ts.map +1 -0
  184. package/dist/key-registry.js +144 -0
  185. package/dist/key-registry.js.map +1 -0
  186. package/dist/mcp.d.ts +53 -0
  187. package/dist/mcp.d.ts.map +1 -0
  188. package/dist/mcp.js +112 -0
  189. package/dist/mcp.js.map +1 -0
  190. package/dist/metering.d.ts +80 -0
  191. package/dist/metering.d.ts.map +1 -0
  192. package/dist/metering.js +227 -0
  193. package/dist/metering.js.map +1 -0
  194. package/dist/metrics.d.ts +68 -0
  195. package/dist/metrics.d.ts.map +1 -0
  196. package/dist/metrics.js +242 -0
  197. package/dist/metrics.js.map +1 -0
  198. package/dist/network-witness.d.ts +167 -0
  199. package/dist/network-witness.d.ts.map +1 -0
  200. package/dist/network-witness.js +520 -0
  201. package/dist/network-witness.js.map +1 -0
  202. package/dist/protocol-state.d.ts +141 -0
  203. package/dist/protocol-state.d.ts.map +1 -0
  204. package/dist/protocol-state.js +106 -0
  205. package/dist/protocol-state.js.map +1 -0
  206. package/dist/receipt-program.d.ts +59 -0
  207. package/dist/receipt-program.d.ts.map +1 -0
  208. package/dist/receipt-program.js +1053 -0
  209. package/dist/receipt-program.js.map +1 -0
  210. package/dist/reliance-kernel.d.ts +66 -0
  211. package/dist/reliance-kernel.d.ts.map +1 -0
  212. package/dist/reliance-kernel.js +103 -0
  213. package/dist/reliance-kernel.js.map +1 -0
  214. package/dist/reliance-packet.d.ts +67 -0
  215. package/dist/reliance-packet.d.ts.map +1 -0
  216. package/dist/reliance-packet.js +176 -0
  217. package/dist/reliance-packet.js.map +1 -0
  218. package/dist/remedy-program-postgres.d.ts +37 -0
  219. package/dist/remedy-program-postgres.d.ts.map +1 -0
  220. package/dist/remedy-program-postgres.js +291 -0
  221. package/dist/remedy-program-postgres.js.map +1 -0
  222. package/dist/remedy-program-receipt.d.ts +97 -0
  223. package/dist/remedy-program-receipt.d.ts.map +1 -0
  224. package/dist/remedy-program-receipt.js +817 -0
  225. package/dist/remedy-program-receipt.js.map +1 -0
  226. package/dist/remedy-program.d.ts +76 -0
  227. package/dist/remedy-program.d.ts.map +1 -0
  228. package/dist/remedy-program.js +1346 -0
  229. package/dist/remedy-program.js.map +1 -0
  230. package/dist/reports/art14.d.ts +101 -0
  231. package/dist/reports/art14.d.ts.map +1 -0
  232. package/dist/reports/art14.js +340 -0
  233. package/dist/reports/art14.js.map +1 -0
  234. package/dist/reports/assurance-package.d.ts +197 -0
  235. package/dist/reports/assurance-package.d.ts.map +1 -0
  236. package/dist/reports/assurance-package.js +313 -0
  237. package/dist/reports/assurance-package.js.map +1 -0
  238. package/dist/reports/auditor-workpaper.d.ts +194 -0
  239. package/dist/reports/auditor-workpaper.d.ts.map +1 -0
  240. package/dist/reports/auditor-workpaper.js +545 -0
  241. package/dist/reports/auditor-workpaper.js.map +1 -0
  242. package/dist/reports/external-verification.d.ts +76 -0
  243. package/dist/reports/external-verification.d.ts.map +1 -0
  244. package/dist/reports/external-verification.js +209 -0
  245. package/dist/reports/external-verification.js.map +1 -0
  246. package/dist/reports/reperform.d.ts +122 -0
  247. package/dist/reports/reperform.d.ts.map +1 -0
  248. package/dist/reports/reperform.js +432 -0
  249. package/dist/reports/reperform.js.map +1 -0
  250. package/dist/reports/underwriter.d.ts +127 -0
  251. package/dist/reports/underwriter.d.ts.map +1 -0
  252. package/dist/reports/underwriter.js +348 -0
  253. package/dist/reports/underwriter.js.map +1 -0
  254. package/dist/retention.d.ts +88 -0
  255. package/dist/retention.d.ts.map +1 -0
  256. package/dist/retention.js +90 -0
  257. package/dist/retention.js.map +1 -0
  258. package/dist/roster.d.ts +113 -0
  259. package/dist/roster.d.ts.map +1 -0
  260. package/dist/roster.js +261 -0
  261. package/dist/roster.js.map +1 -0
  262. package/dist/runtime-monitor.d.ts +132 -0
  263. package/dist/runtime-monitor.d.ts.map +1 -0
  264. package/dist/runtime-monitor.js +197 -0
  265. package/dist/runtime-monitor.js.map +1 -0
  266. package/dist/settlement.d.ts +68 -0
  267. package/dist/settlement.d.ts.map +1 -0
  268. package/dist/settlement.js +306 -0
  269. package/dist/settlement.js.map +1 -0
  270. package/dist/siem.d.ts +261 -0
  271. package/dist/siem.d.ts.map +1 -0
  272. package/dist/siem.js +247 -0
  273. package/dist/siem.js.map +1 -0
  274. package/dist/store-postgres.d.ts +107 -0
  275. package/dist/store-postgres.d.ts.map +1 -0
  276. package/dist/store-postgres.js +152 -0
  277. package/dist/store-postgres.js.map +1 -0
  278. package/dist/store.d.ts +95 -0
  279. package/dist/store.d.ts.map +1 -0
  280. package/dist/store.js +202 -0
  281. package/dist/store.js.map +1 -0
  282. package/dist/strict-json.d.ts +14 -0
  283. package/dist/strict-json.d.ts.map +1 -0
  284. package/dist/strict-json.js +132 -0
  285. package/dist/strict-json.js.map +1 -0
  286. package/dist/trust-program-adapters.d.ts +122 -0
  287. package/dist/trust-program-adapters.d.ts.map +1 -0
  288. package/dist/trust-program-adapters.js +802 -0
  289. package/dist/trust-program-adapters.js.map +1 -0
  290. package/dist/trust-program-postgres.d.ts +41 -0
  291. package/dist/trust-program-postgres.d.ts.map +1 -0
  292. package/dist/trust-program-postgres.js +346 -0
  293. package/dist/trust-program-postgres.js.map +1 -0
  294. package/dist/trust-program-revocation.d.ts +206 -0
  295. package/dist/trust-program-revocation.d.ts.map +1 -0
  296. package/dist/trust-program-revocation.js +600 -0
  297. package/dist/trust-program-revocation.js.map +1 -0
  298. package/dist/trust-program.d.ts +197 -0
  299. package/dist/trust-program.d.ts.map +1 -0
  300. package/dist/trust-program.js +1351 -0
  301. package/dist/trust-program.js.map +1 -0
  302. package/dist/witness-postgres.d.ts +43 -0
  303. package/dist/witness-postgres.d.ts.map +1 -0
  304. package/dist/witness-postgres.js +92 -0
  305. package/dist/witness-postgres.js.map +1 -0
  306. package/dist/zk-range-proof.d.ts +78 -0
  307. package/dist/zk-range-proof.d.ts.map +1 -0
  308. package/dist/zk-range-proof.js +228 -0
  309. package/dist/zk-range-proof.js.map +1 -0
  310. package/eg1-conformance.js +3 -404
  311. package/enterprise.js +3 -180
  312. package/ep-assure.mjs +1 -0
  313. package/evidence-postgres.js +3 -356
  314. package/evidence.js +4 -360
  315. package/execution-binding.js +3 -212
  316. package/formal-runtime-map.js +4 -0
  317. package/index.js +4 -1249
  318. package/key-registry.js +3 -134
  319. package/mcp.js +3 -109
  320. package/metering.js +3 -226
  321. package/metrics.js +3 -231
  322. package/network-witness.js +3 -499
  323. package/package.json +31 -2
  324. package/receipt-program.js +4 -0
  325. package/reliance-kernel.js +3 -112
  326. package/reliance-packet.js +3 -175
  327. package/remedy-program-postgres.js +4 -0
  328. package/remedy-program-receipt.js +4 -0
  329. package/remedy-program.js +4 -0
  330. package/reports/art14.js +0 -0
  331. package/reports/assurance-package.js +2 -312
  332. package/reports/auditor-workpaper.js +3 -537
  333. package/reports/external-verification.js +3 -222
  334. package/reports/reperform.js +3 -424
  335. package/reports/underwriter.js +3 -339
  336. package/retention.js +3 -84
  337. package/roster.js +3 -264
  338. package/runtime-monitor.js +4 -0
  339. package/settlement.js +3 -299
  340. package/siem.js +3 -235
  341. package/src/action-control-manifest.ts +424 -0
  342. package/src/action-escrow-custodian.ts +427 -0
  343. package/src/action-escrow-evidence.ts +1178 -0
  344. package/src/action-escrow-package.ts +1821 -0
  345. package/src/action-escrow-postgres.ts +366 -0
  346. package/src/action-escrow-state.ts +498 -0
  347. package/src/action-escrow-verifiers.ts +484 -0
  348. package/src/action-escrow.ts +3039 -0
  349. package/src/action-packs.ts +170 -0
  350. package/src/adapters/_kit.ts +117 -0
  351. package/src/adapters/aws.ts +142 -0
  352. package/src/adapters/cloudflare.ts +78 -0
  353. package/src/adapters/gcp.ts +71 -0
  354. package/src/adapters/github.ts +110 -0
  355. package/src/adapters/jira.ts +56 -0
  356. package/src/adapters/k8s.ts +71 -0
  357. package/src/adapters/linear.ts +56 -0
  358. package/src/adapters/salesforce.ts +56 -0
  359. package/src/adapters/stripe.ts +82 -0
  360. package/src/adapters/supabase.ts +122 -0
  361. package/src/adapters/terraform.ts +60 -0
  362. package/src/adapters/vercel.ts +85 -0
  363. package/src/aec-execution.ts +354 -0
  364. package/src/breakglass.ts +647 -0
  365. package/src/capability-receipt.ts +1258 -0
  366. package/src/cf1-conformance.ts +120 -0
  367. package/src/challenge-store.ts +70 -0
  368. package/src/control-plane.ts +374 -0
  369. package/src/coverage.ts +832 -0
  370. package/src/deployment-attestation.ts +259 -0
  371. package/src/eg1-conformance.ts +471 -0
  372. package/src/enterprise.ts +195 -0
  373. package/src/evidence-postgres.ts +374 -0
  374. package/src/evidence.ts +367 -0
  375. package/src/execution-binding.ts +236 -0
  376. package/src/formal-runtime-map.ts +24 -0
  377. package/src/index.ts +2122 -0
  378. package/src/key-registry.ts +150 -0
  379. package/src/mcp.ts +120 -0
  380. package/src/metering.ts +237 -0
  381. package/src/metrics.ts +232 -0
  382. package/src/network-witness.ts +585 -0
  383. package/src/protocol-state.ts +143 -0
  384. package/src/protocol-state.typecheck.ts +94 -0
  385. package/src/receipt-program.ts +1105 -0
  386. package/src/reliance-kernel.ts +119 -0
  387. package/src/reliance-packet.ts +206 -0
  388. package/src/remedy-program-postgres.ts +367 -0
  389. package/src/remedy-program-receipt.ts +891 -0
  390. package/src/remedy-program.ts +1388 -0
  391. package/src/reports/art14.ts +361 -0
  392. package/src/reports/assurance-package.ts +330 -0
  393. package/src/reports/auditor-workpaper.ts +558 -0
  394. package/src/reports/external-verification.ts +232 -0
  395. package/src/reports/reperform.ts +441 -0
  396. package/src/reports/underwriter.ts +356 -0
  397. package/src/retention.ts +100 -0
  398. package/src/roster.ts +273 -0
  399. package/src/runtime-monitor.ts +217 -0
  400. package/src/settlement.ts +313 -0
  401. package/src/siem.ts +247 -0
  402. package/src/store-postgres.ts +167 -0
  403. package/src/store.ts +214 -0
  404. package/src/strict-json.ts +115 -0
  405. package/src/trust-program-adapters.ts +847 -0
  406. package/src/trust-program-postgres.ts +429 -0
  407. package/src/trust-program-revocation.ts +751 -0
  408. package/src/trust-program.ts +1460 -0
  409. package/src/witness-postgres.ts +106 -0
  410. package/src/zk-range-proof.ts +250 -0
  411. package/store-postgres.js +3 -161
  412. package/store.js +4 -198
  413. package/strict-json.js +3 -102
  414. package/trust-program-adapters.js +4 -0
  415. package/trust-program-postgres.js +4 -0
  416. package/trust-program-revocation.js +4 -0
  417. package/trust-program.js +4 -0
  418. package/witness-postgres.js +3 -96
  419. package/zk-range-proof.js +4 -0
@@ -0,0 +1,498 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * EP-ACTION-ESCROW-STATE-STATEMENT-v1
4
+ *
5
+ * A portable, operator-signed statement over one exact durable Action Escrow
6
+ * snapshot. The signature authenticates an operator statement; it does not
7
+ * prove the operator's database was complete or that a custodian moved money.
8
+ */
9
+ import crypto from 'node:crypto';
10
+ import { canonicalize, hashCanonical } from './execution-binding.js';
11
+ import { ACTION_ESCROW_EVIDENCE_STAGES } from './action-escrow-evidence.js';
12
+
13
+ export const ACTION_ESCROW_STATE_STATEMENT_VERSION = 'EP-ACTION-ESCROW-STATE-STATEMENT-v1';
14
+ export const ACTION_ESCROW_STATE_STATEMENT_DOMAIN = `${ACTION_ESCROW_STATE_STATEMENT_VERSION}\0`;
15
+
16
+ const HASH = /^sha256:[0-9a-f]{64}$/;
17
+ const ID = /^[A-Za-z0-9][A-Za-z0-9._:/#@+-]{0,255}$/;
18
+ const BASE64URL = /^[A-Za-z0-9_-]+$/;
19
+ const TOP_KEYS = new Set(['version', 'issuer', 'payload', 'statement_digest', 'signature']);
20
+ const ISSUER_KEYS = new Set(['operator_id', 'key_id']);
21
+ const PAYLOAD_KEYS = new Set([
22
+ 'statement_id',
23
+ 'agreement_id',
24
+ 'binding_digest',
25
+ 'action_digest',
26
+ 'profile_digest',
27
+ 'state',
28
+ 'revision',
29
+ 'amendment_digests',
30
+ 'state_record_digest',
31
+ 'previous_statement_digest',
32
+ 'occurred_at',
33
+ ]);
34
+ const SIGNATURE_KEYS = new Set(['algorithm', 'signature_b64u']);
35
+
36
+ function isRecord(value: unknown): value is Record<string, any> {
37
+ return value !== null
38
+ && typeof value === 'object'
39
+ && !Array.isArray(value)
40
+ && (Object.getPrototypeOf(value) === Object.prototype
41
+ || Object.getPrototypeOf(value) === null);
42
+ }
43
+
44
+ /**
45
+ * @param {*} value
46
+ * @param {Set<string>} keys
47
+ */
48
+ function exactKeys(value, keys) {
49
+ return isRecord(value)
50
+ && Object.keys(value).length === keys.size
51
+ && Object.keys(value).every((key) => keys.has(key));
52
+ }
53
+
54
+ /** @param {*} value */
55
+ function strictInstant(value) {
56
+ if (typeof value !== 'string') return NaN;
57
+ const match = value.match(
58
+ /^(\d{4})-(\d{2})-(\d{2})T(\d{2}):(\d{2}):(\d{2})(?:\.\d{1,9})?Z$/,
59
+ );
60
+ if (!match) return NaN;
61
+ const [, year, month, day, hour, minute, second] = match;
62
+ const calendar = new Date(0);
63
+ calendar.setUTCFullYear(Number(year), Number(month) - 1, Number(day));
64
+ calendar.setUTCHours(Number(hour), Number(minute), Number(second), 0);
65
+ if (calendar.toISOString().slice(0, 19)
66
+ !== `${year}-${month}-${day}T${hour}:${minute}:${second}`) return NaN;
67
+ const parsed = Date.parse(value);
68
+ return Number.isFinite(parsed) ? parsed : NaN;
69
+ }
70
+
71
+ /** @param {*} value */
72
+ function boundedCanonicalCopy(value) {
73
+ let nodes = 0;
74
+ let bytes = 0;
75
+ const seen = new WeakSet();
76
+
77
+ /**
78
+ * @param {*} current
79
+ * @param {number} depth
80
+ * @returns {*}
81
+ */
82
+ function copy(current, depth) {
83
+ nodes += 1;
84
+ if (nodes > 50_000 || depth > 64) throw new TypeError('state statement exceeds resource limits');
85
+ if (current === null || typeof current === 'boolean') return current;
86
+ if (typeof current === 'string') {
87
+ bytes += Buffer.byteLength(current, 'utf8');
88
+ if (bytes > 4 * 1024 * 1024) throw new TypeError('state statement exceeds string limit');
89
+ return current;
90
+ }
91
+ if (typeof current === 'number') {
92
+ if (!Number.isSafeInteger(current) || Object.is(current, -0)) {
93
+ throw new TypeError('state statement contains a non-canonical number');
94
+ }
95
+ return current;
96
+ }
97
+ if (!isRecord(current) && !Array.isArray(current)) {
98
+ throw new TypeError('state statement is not canonical JSON');
99
+ }
100
+ if (seen.has(current)) throw new TypeError('state statement contains an alias or cycle');
101
+ seen.add(current);
102
+ if (Array.isArray(current)) return current.map((entry) => copy(entry, depth + 1));
103
+ return Object.fromEntries(
104
+ Object.entries(current).map(([key, entry]) => [key, copy(entry, depth + 1)]),
105
+ );
106
+ }
107
+
108
+ return copy(value, 0);
109
+ }
110
+
111
+ /** @param {*} value */
112
+ function canonicalHash(value) {
113
+ return `sha256:${hashCanonical(value)}`;
114
+ }
115
+
116
+ /**
117
+ * @typedef {Object} StateStatementLike
118
+ * @property {string} [version]
119
+ * @property {*} [issuer]
120
+ * @property {*} [payload]
121
+ * @property {string} [statement_digest]
122
+ * @property {*} [signature]
123
+ */
124
+
125
+ /** @param {StateStatementLike} statement */
126
+ function signingBody(statement) {
127
+ return {
128
+ version: statement.version,
129
+ issuer: statement.issuer,
130
+ payload: statement.payload,
131
+ };
132
+ }
133
+
134
+ /** @param {StateStatementLike} statement */
135
+ function stateSigningBytes(statement) {
136
+ const body = boundedCanonicalCopy(signingBody(statement));
137
+ return Buffer.from(
138
+ ACTION_ESCROW_STATE_STATEMENT_DOMAIN + canonicalize(body),
139
+ 'utf8',
140
+ );
141
+ }
142
+
143
+ /** @param {*} value */
144
+ function deepFreeze(value) {
145
+ if (!value || typeof value !== 'object' || Object.isFrozen(value)) return value;
146
+ Object.freeze(value);
147
+ for (const child of Object.values(value)) deepFreeze(child);
148
+ return value;
149
+ }
150
+
151
+ /**
152
+ * @param {*} value
153
+ * @param {number} [length]
154
+ */
155
+ function strictBase64url(value, length?: number) {
156
+ if (typeof value !== 'string' || !BASE64URL.test(value) || value.length % 4 === 1) return null;
157
+ const bytes = Buffer.from(value, 'base64url');
158
+ if (bytes.toString('base64url') !== value || (length !== undefined && bytes.length !== length)) return null;
159
+ return bytes;
160
+ }
161
+
162
+ /** @param {*} value */
163
+ function validDigestList(value) {
164
+ return Array.isArray(value)
165
+ && value.length <= 1024
166
+ && value.every((entry) => typeof entry === 'string' && HASH.test(entry))
167
+ && new Set(value).size === value.length;
168
+ }
169
+
170
+ /** @param {*} payload */
171
+ function payloadValid(payload) {
172
+ return exactKeys(payload, PAYLOAD_KEYS)
173
+ && typeof payload.statement_id === 'string' && ID.test(payload.statement_id)
174
+ && typeof payload.agreement_id === 'string' && ID.test(payload.agreement_id)
175
+ && HASH.test(payload.binding_digest)
176
+ && HASH.test(payload.action_digest)
177
+ && HASH.test(payload.profile_digest)
178
+ && ACTION_ESCROW_EVIDENCE_STAGES.includes(payload.state)
179
+ && Number.isSafeInteger(payload.revision) && payload.revision >= 0
180
+ && validDigestList(payload.amendment_digests)
181
+ && HASH.test(payload.state_record_digest)
182
+ && (payload.previous_statement_digest === null || HASH.test(payload.previous_statement_digest))
183
+ && Number.isFinite(strictInstant(payload.occurred_at));
184
+ }
185
+
186
+ /**
187
+ * @typedef {Object} StateStatementChecks
188
+ * @property {boolean} structure
189
+ * @property {boolean} payload
190
+ * @property {boolean} issuer_pin
191
+ * @property {boolean} signature
192
+ * @property {boolean} statement_digest
193
+ * @property {boolean} state_record
194
+ * @property {boolean} expected_bindings
195
+ * @property {boolean} time
196
+ */
197
+
198
+ /**
199
+ * @param {string} reason
200
+ * @param {StateStatementChecks} checks
201
+ */
202
+ function refuse(reason, checks) {
203
+ return {
204
+ valid: false,
205
+ reason,
206
+ checks,
207
+ statement_digest: null,
208
+ agreement_id: null,
209
+ binding_digest: null,
210
+ action_digest: null,
211
+ profile_digest: null,
212
+ state: null,
213
+ revision: null,
214
+ amendment_digests: [],
215
+ };
216
+ }
217
+
218
+ /**
219
+ * Sign one exact state snapshot. Issuance may throw on invalid local input;
220
+ * verification below never throws.
221
+ */
222
+ export function signActionEscrowStateStatement({
223
+ statementId,
224
+ agreementId,
225
+ bindingDigest,
226
+ actionDigest,
227
+ profileDigest,
228
+ state,
229
+ revision,
230
+ amendmentDigests = [],
231
+ stateRecord,
232
+ previousStatementDigest = null,
233
+ occurredAt,
234
+ }: {
235
+ statementId?: string;
236
+ agreementId?: string;
237
+ bindingDigest?: string;
238
+ actionDigest?: string;
239
+ profileDigest?: string;
240
+ state?: string;
241
+ revision?: number;
242
+ amendmentDigests?: string[];
243
+ stateRecord?: unknown;
244
+ previousStatementDigest?: string | null;
245
+ occurredAt?: string;
246
+ } = {}, {
247
+ operatorId,
248
+ keyId,
249
+ privateKey,
250
+ }: {
251
+ operatorId?: string;
252
+ keyId?: string;
253
+ privateKey?: crypto.KeyObject | Parameters<typeof crypto.createPrivateKey>[0];
254
+ } = {}) {
255
+ const stateRecordCopy = boundedCanonicalCopy(stateRecord);
256
+ const statement = {
257
+ version: ACTION_ESCROW_STATE_STATEMENT_VERSION,
258
+ issuer: {
259
+ operator_id: operatorId,
260
+ key_id: keyId,
261
+ },
262
+ payload: {
263
+ statement_id: statementId,
264
+ agreement_id: agreementId,
265
+ binding_digest: bindingDigest,
266
+ action_digest: actionDigest,
267
+ profile_digest: profileDigest,
268
+ state,
269
+ revision,
270
+ amendment_digests: boundedCanonicalCopy(amendmentDigests),
271
+ state_record_digest: canonicalHash(stateRecordCopy),
272
+ previous_statement_digest: previousStatementDigest,
273
+ occurred_at: occurredAt,
274
+ },
275
+ };
276
+ if (!exactKeys(statement.issuer, ISSUER_KEYS)
277
+ || typeof operatorId !== 'string' || !ID.test(operatorId)
278
+ || typeof keyId !== 'string' || !ID.test(keyId)
279
+ || !payloadValid(statement.payload)) {
280
+ throw new TypeError('action-escrow state statement input is invalid');
281
+ }
282
+ // privateKey may be omitted by a caller (optional field); createPrivateKey
283
+ // throws on undefined input, matching this function's documented
284
+ // throw-on-invalid-local-input contract.
285
+ const key = privateKey instanceof crypto.KeyObject ? privateKey : crypto.createPrivateKey(privateKey!);
286
+ if (key.asymmetricKeyType !== 'ed25519') {
287
+ throw new TypeError('action-escrow state statement key must be Ed25519');
288
+ }
289
+ const bytes = stateSigningBytes(statement);
290
+ const statementDigest = `sha256:${crypto.createHash('sha256').update(bytes).digest('hex')}`;
291
+ return deepFreeze({
292
+ ...statement,
293
+ statement_digest: statementDigest,
294
+ signature: {
295
+ algorithm: 'Ed25519',
296
+ signature_b64u: crypto.sign(null, bytes, key).toString('base64url'),
297
+ },
298
+ });
299
+ }
300
+
301
+ /**
302
+ * Verify one state statement against an exact snapshot and relying-party pins.
303
+ *
304
+ * @param {*} statement
305
+ */
306
+ export function verifyActionEscrowStateStatement(statement, {
307
+ trustedKeys,
308
+ stateRecord,
309
+ expectedAgreementId,
310
+ expectedBindingDigest,
311
+ expectedActionDigest,
312
+ expectedProfileDigest,
313
+ expectedState,
314
+ expectedRevision,
315
+ expectedAmendmentDigests,
316
+ expectedPreviousStatementDigest,
317
+ now,
318
+ }: {
319
+ trustedKeys?: unknown;
320
+ stateRecord?: unknown;
321
+ expectedAgreementId?: string;
322
+ expectedBindingDigest?: string;
323
+ expectedActionDigest?: string;
324
+ expectedProfileDigest?: string;
325
+ expectedState?: string;
326
+ expectedRevision?: number;
327
+ expectedAmendmentDigests?: string[];
328
+ expectedPreviousStatementDigest?: string | null;
329
+ now?: Date | number | string;
330
+ } = {}) {
331
+ const checks = {
332
+ structure: false,
333
+ payload: false,
334
+ issuer_pin: false,
335
+ signature: false,
336
+ statement_digest: false,
337
+ state_record: false,
338
+ expected_bindings: false,
339
+ time: false,
340
+ };
341
+ try {
342
+ checks.structure = exactKeys(statement, TOP_KEYS)
343
+ && statement.version === ACTION_ESCROW_STATE_STATEMENT_VERSION
344
+ && exactKeys(statement.issuer, ISSUER_KEYS)
345
+ && exactKeys(statement.signature, SIGNATURE_KEYS);
346
+ if (!checks.structure) return refuse('malformed_state_statement', checks);
347
+
348
+ checks.payload = payloadValid(statement.payload);
349
+ if (!checks.payload) return refuse('invalid_state_payload', checks);
350
+
351
+ const pin = isRecord(trustedKeys) && Object.hasOwn(trustedKeys, statement.issuer.key_id)
352
+ ? trustedKeys[statement.issuer.key_id]
353
+ : null;
354
+ checks.issuer_pin = exactKeys(pin, new Set(['operator_id', 'public_key']))
355
+ && pin.operator_id === statement.issuer.operator_id
356
+ && typeof pin.public_key === 'string';
357
+ if (!checks.issuer_pin) return refuse('operator_key_not_pinned', checks);
358
+
359
+ const publicBytes = strictBase64url(pin.public_key);
360
+ const signatureBytes = strictBase64url(statement.signature.signature_b64u, 64);
361
+ let publicKey: crypto.KeyObject | null = null;
362
+ try {
363
+ // publicBytes may be null here (checked below via publicBytes !== null);
364
+ // createPublicKey rejects a null key and the catch maps that to null.
365
+ publicKey = crypto.createPublicKey({ key: publicBytes as Buffer, format: 'der', type: 'spki' });
366
+ } catch {
367
+ publicKey = null;
368
+ }
369
+ const bytes = stateSigningBytes(statement);
370
+ checks.signature = statement.signature.algorithm === 'Ed25519'
371
+ && publicBytes !== null
372
+ && signatureBytes !== null
373
+ && publicKey?.asymmetricKeyType === 'ed25519'
374
+ && crypto.verify(null, bytes, publicKey, signatureBytes);
375
+ if (!checks.signature) return refuse('state_signature_invalid', checks);
376
+
377
+ const digest = `sha256:${crypto.createHash('sha256').update(bytes).digest('hex')}`;
378
+ checks.statement_digest = statement.statement_digest === digest;
379
+ if (!checks.statement_digest) return refuse('state_statement_digest_mismatch', checks);
380
+
381
+ checks.state_record = canonicalHash(boundedCanonicalCopy(stateRecord))
382
+ === statement.payload.state_record_digest;
383
+ if (!checks.state_record) return refuse('state_record_digest_mismatch', checks);
384
+
385
+ const amendments = expectedAmendmentDigests;
386
+ checks.expected_bindings = typeof expectedAgreementId === 'string'
387
+ && statement.payload.agreement_id === expectedAgreementId
388
+ && statement.payload.binding_digest === expectedBindingDigest
389
+ && statement.payload.action_digest === expectedActionDigest
390
+ && statement.payload.profile_digest === expectedProfileDigest
391
+ && statement.payload.state === expectedState
392
+ && statement.payload.revision === expectedRevision
393
+ && Array.isArray(amendments)
394
+ && statement.payload.amendment_digests.length === amendments.length
395
+ && statement.payload.amendment_digests.every(
396
+ /**
397
+ * @param {string} entry
398
+ * @param {number} index
399
+ */
400
+ (entry, index) => entry === amendments[index],
401
+ )
402
+ && statement.payload.previous_statement_digest === expectedPreviousStatementDigest;
403
+ if (!checks.expected_bindings) return refuse('state_expected_binding_mismatch', checks);
404
+
405
+ const evaluation = now instanceof Date
406
+ ? now.getTime()
407
+ : typeof now === 'number' ? now : strictInstant(now);
408
+ checks.time = Number.isFinite(evaluation)
409
+ && strictInstant(statement.payload.occurred_at) <= evaluation;
410
+ if (!checks.time) return refuse('state_statement_from_future', checks);
411
+
412
+ return {
413
+ valid: true,
414
+ reason: 'verified',
415
+ checks,
416
+ statement_digest: digest,
417
+ agreement_id: statement.payload.agreement_id,
418
+ binding_digest: statement.payload.binding_digest,
419
+ action_digest: statement.payload.action_digest,
420
+ profile_digest: statement.payload.profile_digest,
421
+ state: statement.payload.state,
422
+ revision: statement.payload.revision,
423
+ amendment_digests: [...statement.payload.amendment_digests],
424
+ };
425
+ } catch {
426
+ return refuse('malformed_state_statement', checks);
427
+ }
428
+ }
429
+
430
+ /**
431
+ * Build the callback expected by verifyActionEscrowEvidencePackage. The
432
+ * package carries both the exact durable snapshot and the signed statement
433
+ * over it; trust keys and time remain verifier configuration.
434
+ */
435
+ export function createActionEscrowStatePackageVerifier({
436
+ trustedKeys,
437
+ now,
438
+ minimumRevision = 0,
439
+ }: {
440
+ trustedKeys?: unknown;
441
+ now?: Date | number | string;
442
+ minimumRevision?: number;
443
+ } = {}) {
444
+ if (!Number.isSafeInteger(minimumRevision) || minimumRevision < 0) {
445
+ throw new TypeError('minimumRevision must be a non-negative safe integer');
446
+ }
447
+ const pinnedKeys = boundedCanonicalCopy(trustedKeys);
448
+ /**
449
+ * @param {*} packaged
450
+ */
451
+ return async function verifyPackagedState(packaged, expected: {
452
+ agreementId?: string;
453
+ bindingDigest?: string;
454
+ actionDigest?: string;
455
+ profileDigest?: string;
456
+ stage?: string;
457
+ amendmentDigests?: string[];
458
+ } = {}) {
459
+ if (!exactKeys(packaged, new Set(['snapshot', 'statement']))
460
+ || !isRecord(packaged.statement?.payload)
461
+ || !isRecord(packaged.snapshot)
462
+ || packaged.statement.payload.state !== packaged.snapshot.state
463
+ || packaged.statement.payload.revision !== packaged.snapshot.revision
464
+ || packaged.statement.payload.revision < minimumRevision) {
465
+ return refuse('malformed_packaged_state', {
466
+ structure: false,
467
+ payload: false,
468
+ issuer_pin: false,
469
+ signature: false,
470
+ statement_digest: false,
471
+ state_record: false,
472
+ expected_bindings: false,
473
+ time: false,
474
+ });
475
+ }
476
+ return verifyActionEscrowStateStatement(packaged.statement, {
477
+ trustedKeys: pinnedKeys,
478
+ stateRecord: packaged.snapshot,
479
+ expectedAgreementId: expected.agreementId,
480
+ expectedBindingDigest: expected.bindingDigest,
481
+ expectedActionDigest: expected.actionDigest,
482
+ expectedProfileDigest: expected.profileDigest,
483
+ expectedState: expected.stage,
484
+ expectedRevision: packaged.statement.payload.revision,
485
+ expectedAmendmentDigests: expected.amendmentDigests,
486
+ expectedPreviousStatementDigest: packaged.statement.payload.previous_statement_digest,
487
+ now,
488
+ });
489
+ };
490
+ }
491
+
492
+ export default {
493
+ ACTION_ESCROW_STATE_STATEMENT_VERSION,
494
+ ACTION_ESCROW_STATE_STATEMENT_DOMAIN,
495
+ signActionEscrowStateStatement,
496
+ verifyActionEscrowStateStatement,
497
+ createActionEscrowStatePackageVerifier,
498
+ };