@emilia-protocol/gate 0.11.0 → 0.14.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 (425) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/README.md +310 -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 +1003 -0
  179. package/dist/index.d.ts.map +1 -0
  180. package/dist/index.js +1737 -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/proposal-to-effect.d.ts +146 -0
  203. package/dist/proposal-to-effect.d.ts.map +1 -0
  204. package/dist/proposal-to-effect.js +427 -0
  205. package/dist/proposal-to-effect.js.map +1 -0
  206. package/dist/protocol-state.d.ts +141 -0
  207. package/dist/protocol-state.d.ts.map +1 -0
  208. package/dist/protocol-state.js +106 -0
  209. package/dist/protocol-state.js.map +1 -0
  210. package/dist/receipt-program.d.ts +59 -0
  211. package/dist/receipt-program.d.ts.map +1 -0
  212. package/dist/receipt-program.js +1053 -0
  213. package/dist/receipt-program.js.map +1 -0
  214. package/dist/reliance-kernel.d.ts +66 -0
  215. package/dist/reliance-kernel.d.ts.map +1 -0
  216. package/dist/reliance-kernel.js +103 -0
  217. package/dist/reliance-kernel.js.map +1 -0
  218. package/dist/reliance-packet.d.ts +67 -0
  219. package/dist/reliance-packet.d.ts.map +1 -0
  220. package/dist/reliance-packet.js +176 -0
  221. package/dist/reliance-packet.js.map +1 -0
  222. package/dist/remedy-program-postgres.d.ts +37 -0
  223. package/dist/remedy-program-postgres.d.ts.map +1 -0
  224. package/dist/remedy-program-postgres.js +291 -0
  225. package/dist/remedy-program-postgres.js.map +1 -0
  226. package/dist/remedy-program-receipt.d.ts +97 -0
  227. package/dist/remedy-program-receipt.d.ts.map +1 -0
  228. package/dist/remedy-program-receipt.js +817 -0
  229. package/dist/remedy-program-receipt.js.map +1 -0
  230. package/dist/remedy-program.d.ts +76 -0
  231. package/dist/remedy-program.d.ts.map +1 -0
  232. package/dist/remedy-program.js +1346 -0
  233. package/dist/remedy-program.js.map +1 -0
  234. package/dist/reports/art14.d.ts +101 -0
  235. package/dist/reports/art14.d.ts.map +1 -0
  236. package/dist/reports/art14.js +340 -0
  237. package/dist/reports/art14.js.map +1 -0
  238. package/dist/reports/assurance-package.d.ts +197 -0
  239. package/dist/reports/assurance-package.d.ts.map +1 -0
  240. package/dist/reports/assurance-package.js +313 -0
  241. package/dist/reports/assurance-package.js.map +1 -0
  242. package/dist/reports/auditor-workpaper.d.ts +194 -0
  243. package/dist/reports/auditor-workpaper.d.ts.map +1 -0
  244. package/dist/reports/auditor-workpaper.js +545 -0
  245. package/dist/reports/auditor-workpaper.js.map +1 -0
  246. package/dist/reports/external-verification.d.ts +76 -0
  247. package/dist/reports/external-verification.d.ts.map +1 -0
  248. package/dist/reports/external-verification.js +209 -0
  249. package/dist/reports/external-verification.js.map +1 -0
  250. package/dist/reports/reperform.d.ts +122 -0
  251. package/dist/reports/reperform.d.ts.map +1 -0
  252. package/dist/reports/reperform.js +432 -0
  253. package/dist/reports/reperform.js.map +1 -0
  254. package/dist/reports/underwriter.d.ts +127 -0
  255. package/dist/reports/underwriter.d.ts.map +1 -0
  256. package/dist/reports/underwriter.js +348 -0
  257. package/dist/reports/underwriter.js.map +1 -0
  258. package/dist/retention.d.ts +88 -0
  259. package/dist/retention.d.ts.map +1 -0
  260. package/dist/retention.js +90 -0
  261. package/dist/retention.js.map +1 -0
  262. package/dist/roster.d.ts +113 -0
  263. package/dist/roster.d.ts.map +1 -0
  264. package/dist/roster.js +261 -0
  265. package/dist/roster.js.map +1 -0
  266. package/dist/runtime-monitor.d.ts +132 -0
  267. package/dist/runtime-monitor.d.ts.map +1 -0
  268. package/dist/runtime-monitor.js +197 -0
  269. package/dist/runtime-monitor.js.map +1 -0
  270. package/dist/settlement.d.ts +68 -0
  271. package/dist/settlement.d.ts.map +1 -0
  272. package/dist/settlement.js +306 -0
  273. package/dist/settlement.js.map +1 -0
  274. package/dist/siem.d.ts +261 -0
  275. package/dist/siem.d.ts.map +1 -0
  276. package/dist/siem.js +247 -0
  277. package/dist/siem.js.map +1 -0
  278. package/dist/store-postgres.d.ts +107 -0
  279. package/dist/store-postgres.d.ts.map +1 -0
  280. package/dist/store-postgres.js +152 -0
  281. package/dist/store-postgres.js.map +1 -0
  282. package/dist/store.d.ts +95 -0
  283. package/dist/store.d.ts.map +1 -0
  284. package/dist/store.js +202 -0
  285. package/dist/store.js.map +1 -0
  286. package/dist/strict-json.d.ts +14 -0
  287. package/dist/strict-json.d.ts.map +1 -0
  288. package/dist/strict-json.js +132 -0
  289. package/dist/strict-json.js.map +1 -0
  290. package/dist/trust-program-adapters.d.ts +122 -0
  291. package/dist/trust-program-adapters.d.ts.map +1 -0
  292. package/dist/trust-program-adapters.js +802 -0
  293. package/dist/trust-program-adapters.js.map +1 -0
  294. package/dist/trust-program-postgres.d.ts +41 -0
  295. package/dist/trust-program-postgres.d.ts.map +1 -0
  296. package/dist/trust-program-postgres.js +346 -0
  297. package/dist/trust-program-postgres.js.map +1 -0
  298. package/dist/trust-program-revocation.d.ts +206 -0
  299. package/dist/trust-program-revocation.d.ts.map +1 -0
  300. package/dist/trust-program-revocation.js +600 -0
  301. package/dist/trust-program-revocation.js.map +1 -0
  302. package/dist/trust-program.d.ts +197 -0
  303. package/dist/trust-program.d.ts.map +1 -0
  304. package/dist/trust-program.js +1351 -0
  305. package/dist/trust-program.js.map +1 -0
  306. package/dist/witness-postgres.d.ts +43 -0
  307. package/dist/witness-postgres.d.ts.map +1 -0
  308. package/dist/witness-postgres.js +92 -0
  309. package/dist/witness-postgres.js.map +1 -0
  310. package/dist/zk-range-proof.d.ts +78 -0
  311. package/dist/zk-range-proof.d.ts.map +1 -0
  312. package/dist/zk-range-proof.js +228 -0
  313. package/dist/zk-range-proof.js.map +1 -0
  314. package/eg1-conformance.js +3 -404
  315. package/enterprise.js +3 -180
  316. package/ep-assure.mjs +1 -0
  317. package/evidence-postgres.js +3 -356
  318. package/evidence.js +4 -360
  319. package/execution-binding.js +3 -212
  320. package/formal-runtime-map.js +4 -0
  321. package/index.js +4 -1249
  322. package/key-registry.js +3 -134
  323. package/mcp.js +3 -109
  324. package/metering.js +3 -226
  325. package/metrics.js +3 -231
  326. package/network-witness.js +3 -499
  327. package/package.json +35 -4
  328. package/proposal-to-effect.js +4 -0
  329. package/receipt-program.js +4 -0
  330. package/reliance-kernel.js +3 -112
  331. package/reliance-packet.js +3 -175
  332. package/remedy-program-postgres.js +4 -0
  333. package/remedy-program-receipt.js +4 -0
  334. package/remedy-program.js +4 -0
  335. package/reports/art14.js +0 -0
  336. package/reports/assurance-package.js +2 -312
  337. package/reports/auditor-workpaper.js +3 -537
  338. package/reports/external-verification.js +3 -222
  339. package/reports/reperform.js +3 -424
  340. package/reports/underwriter.js +3 -339
  341. package/retention.js +3 -84
  342. package/roster.js +3 -264
  343. package/runtime-monitor.js +4 -0
  344. package/settlement.js +3 -299
  345. package/siem.js +3 -235
  346. package/src/action-control-manifest.ts +424 -0
  347. package/src/action-escrow-custodian.ts +427 -0
  348. package/src/action-escrow-evidence.ts +1178 -0
  349. package/src/action-escrow-package.ts +1821 -0
  350. package/src/action-escrow-postgres.ts +366 -0
  351. package/src/action-escrow-state.ts +498 -0
  352. package/src/action-escrow-verifiers.ts +484 -0
  353. package/src/action-escrow.ts +3039 -0
  354. package/src/action-packs.ts +170 -0
  355. package/src/adapters/_kit.ts +117 -0
  356. package/src/adapters/aws.ts +142 -0
  357. package/src/adapters/cloudflare.ts +78 -0
  358. package/src/adapters/gcp.ts +71 -0
  359. package/src/adapters/github.ts +110 -0
  360. package/src/adapters/jira.ts +56 -0
  361. package/src/adapters/k8s.ts +71 -0
  362. package/src/adapters/linear.ts +56 -0
  363. package/src/adapters/salesforce.ts +56 -0
  364. package/src/adapters/stripe.ts +82 -0
  365. package/src/adapters/supabase.ts +122 -0
  366. package/src/adapters/terraform.ts +60 -0
  367. package/src/adapters/vercel.ts +85 -0
  368. package/src/aec-execution.ts +354 -0
  369. package/src/breakglass.ts +647 -0
  370. package/src/capability-receipt.ts +1258 -0
  371. package/src/cf1-conformance.ts +120 -0
  372. package/src/challenge-store.ts +70 -0
  373. package/src/control-plane.ts +374 -0
  374. package/src/coverage.ts +832 -0
  375. package/src/deployment-attestation.ts +259 -0
  376. package/src/eg1-conformance.ts +471 -0
  377. package/src/enterprise.ts +195 -0
  378. package/src/evidence-postgres.ts +374 -0
  379. package/src/evidence.ts +367 -0
  380. package/src/execution-binding.ts +236 -0
  381. package/src/formal-runtime-map.ts +24 -0
  382. package/src/index.ts +2127 -0
  383. package/src/key-registry.ts +150 -0
  384. package/src/mcp.ts +120 -0
  385. package/src/metering.ts +237 -0
  386. package/src/metrics.ts +232 -0
  387. package/src/network-witness.ts +585 -0
  388. package/src/proposal-to-effect.ts +571 -0
  389. package/src/protocol-state.ts +143 -0
  390. package/src/protocol-state.typecheck.ts +94 -0
  391. package/src/receipt-program.ts +1105 -0
  392. package/src/reliance-kernel.ts +119 -0
  393. package/src/reliance-packet.ts +206 -0
  394. package/src/remedy-program-postgres.ts +367 -0
  395. package/src/remedy-program-receipt.ts +891 -0
  396. package/src/remedy-program.ts +1388 -0
  397. package/src/reports/art14.ts +361 -0
  398. package/src/reports/assurance-package.ts +330 -0
  399. package/src/reports/auditor-workpaper.ts +558 -0
  400. package/src/reports/external-verification.ts +232 -0
  401. package/src/reports/reperform.ts +441 -0
  402. package/src/reports/underwriter.ts +356 -0
  403. package/src/retention.ts +100 -0
  404. package/src/roster.ts +273 -0
  405. package/src/runtime-monitor.ts +217 -0
  406. package/src/settlement.ts +313 -0
  407. package/src/siem.ts +247 -0
  408. package/src/store-postgres.ts +167 -0
  409. package/src/store.ts +214 -0
  410. package/src/strict-json.ts +115 -0
  411. package/src/trust-program-adapters.ts +847 -0
  412. package/src/trust-program-postgres.ts +429 -0
  413. package/src/trust-program-revocation.ts +751 -0
  414. package/src/trust-program.ts +1460 -0
  415. package/src/witness-postgres.ts +106 -0
  416. package/src/zk-range-proof.ts +250 -0
  417. package/store-postgres.js +3 -161
  418. package/store.js +4 -198
  419. package/strict-json.js +3 -102
  420. package/trust-program-adapters.js +4 -0
  421. package/trust-program-postgres.js +4 -0
  422. package/trust-program-revocation.js +4 -0
  423. package/trust-program.js +4 -0
  424. package/witness-postgres.js +3 -96
  425. package/zk-range-proof.js +4 -0
@@ -0,0 +1,817 @@
1
+ // @ts-nocheck
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ /**
4
+ * EP-ACTION-REMEDY-RECEIPT-v1
5
+ *
6
+ * A portable, offline-verifiable operator receipt over one exact Remedy
7
+ * Program state snapshot. The receipt preserves the original effect as an
8
+ * immutable fact and describes a later remedy only as a compensating action.
9
+ * It never claims that the original effect was rolled back or erased.
10
+ */
11
+ import crypto from 'node:crypto';
12
+ import { canonicalize } from '../execution-binding.js';
13
+ export const ACTION_REMEDY_RECEIPT_VERSION = 'EP-ACTION-REMEDY-RECEIPT-v1';
14
+ export const REMEDY_PROGRAM_RECEIPT_VERSION = ACTION_REMEDY_RECEIPT_VERSION;
15
+ export const ACTION_REMEDY_RECEIPT_DOMAIN = `${ACTION_REMEDY_RECEIPT_VERSION}\0`;
16
+ const REMEDY_PROGRAM_VERSION = 'EP-GATE-REMEDY-PROGRAM-PROFILE-v1';
17
+ const DIGEST = /^sha256:[0-9a-f]{64}$/;
18
+ const CAID = /^caid:1:[a-z][a-z0-9.-]*\.[1-9][0-9]*:jcs-sha256:[A-Za-z0-9_-]{43}$/;
19
+ const ID = /^[A-Za-z0-9][A-Za-z0-9._:/#@+-]{0,255}$/;
20
+ const BASE64URL = /^[A-Za-z0-9_-]+$/;
21
+ const FORBIDDEN_FIELD_NAMES = new Set(['__proto__', 'prototype', 'constructor']);
22
+ const MAX_CONTEXT_BYTES = 512;
23
+ const MAX_REMEDIES = 1024;
24
+ const TOP_KEYS = new Set(['version', 'issuer', 'payload', 'content_digest', 'signature']);
25
+ const ISSUER_KEYS = new Set(['issuer', 'tenant', 'environment', 'audience', 'key_id']);
26
+ const PAYLOAD_KEYS = new Set([
27
+ 'case', 'original_effect', 'original_reconciliation', 'remedy', 'semantics',
28
+ ]);
29
+ const CASE_KEYS = new Set([
30
+ 'instance_id', 'revision', 'status', 'state_snapshot_digest', 'updated_at',
31
+ ]);
32
+ const ORIGINAL_KEYS = new Set([
33
+ 'caid', 'action_digest', 'operation_id', 'consequence_mode',
34
+ 'consequence_digest', 'terminal_evidence_digest', 'outcome', 'occurred_at',
35
+ 'evidence_digest',
36
+ ]);
37
+ const REMEDY_KEYS = new Set([
38
+ 'operation_id', 'caid', 'action_digest', 'destination_binding_digest',
39
+ 'units', 'unit', 'owner_mode', 'owner_digest', 'status', 'outcome',
40
+ ]);
41
+ const SEMANTICS_KEYS = new Set(['original_effect', 'remedy_effect', 'rollback']);
42
+ const SIGNATURE_KEYS = new Set(['algorithm', 'value']);
43
+ const EXPECTED_KEYS = new Set([
44
+ 'original_operation_id', 'original_action_digest',
45
+ 'original_terminal_evidence_digest', 'case_instance_id', 'case_revision',
46
+ 'case_status', 'remedy_operation_id', 'remedy_action_digest', 'remedy_caid',
47
+ 'destination_binding_digest', 'units', 'unit', 'owner_mode', 'owner_digest',
48
+ ]);
49
+ const STATE_KEYS = new Set([
50
+ 'version', 'instance_id', 'tenant_id', 'environment', 'audience', 'status',
51
+ 'revision', 'created_at', 'updated_at', 'original', 'remedy_profile_digest',
52
+ 'destination_binding_digest', 'max_remedy_units', 'unit', 'remedied_units',
53
+ 'remaining_units', 'used_evidence_ids', 'used_evidence_digests',
54
+ 'original_reconciliation', 'revocation', 'dispute', 'active_remedy',
55
+ 'remedies', 'resolution',
56
+ 'create_request_digest',
57
+ ]);
58
+ const ATTEMPT_KEYS = new Set([
59
+ 'evidence_id', 'evidence_digest', 'dispute_id', 'original_operation_id',
60
+ 'remedy_operation_id', 'remedy_caid', 'remedy_action_digest',
61
+ 'consequence_mode', 'capability_template_digest', 'escrow_profile_digest',
62
+ 'destination_binding_digest', 'units', 'unit', 'authorized_at',
63
+ 'request_digest', 'status', 'claim_token_digest', 'claimed_at',
64
+ 'claim_request_digest', 'outcome', 'outcome_evidence',
65
+ 'finalize_request_digest', 'reconciliation', 'reconcile_request_digest',
66
+ ]);
67
+ const OUTCOME_EVIDENCE_KEYS = new Set([
68
+ 'evidence_id', 'evidence_digest', 'remedy_operation_id',
69
+ 'remedy_action_digest', 'destination_binding_digest', 'units', 'unit',
70
+ 'outcome', 'observed_at',
71
+ ]);
72
+ const DISPUTE_KEYS = new Set([
73
+ 'dispute_id', 'evidence_id', 'evidence_digest', 'challenger_id',
74
+ 'requested_units', 'opened_at', 'original_operation_id',
75
+ 'original_action_digest', 'request_digest',
76
+ ]);
77
+ const REVOCATION_KEYS = new Set([
78
+ 'evidence_id', 'evidence_digest', 'target_operation_id', 'action_digest',
79
+ 'authority_id', 'revoked_at', 'effect', 'request_digest',
80
+ ]);
81
+ const RESOLUTION_KEYS = new Set([
82
+ 'evidence_id', 'evidence_digest', 'outcome', 'resolved_at', 'dispute_id',
83
+ 'request_digest',
84
+ ]);
85
+ const CASE_STATUSES = new Set([
86
+ 'effect_executed', 'effect_indeterminate', 'disputed', 'remedy_authorized',
87
+ 'remedy_claimed', 'remedy_indeterminate', 'partially_remedied', 'remedied',
88
+ 'resolved_no_remedy',
89
+ 'original_proved_no_effect',
90
+ ]);
91
+ const REMEDY_STATUSES = new Set([
92
+ 'authorized', 'claimed', 'indeterminate', 'executed', 'proved_no_effect',
93
+ ]);
94
+ const REMEDY_OUTCOMES = new Set(['executed', 'proved_no_effect', 'indeterminate']);
95
+ function isRecord(value) {
96
+ if (value === null || typeof value !== 'object' || Array.isArray(value))
97
+ return false;
98
+ const prototype = Object.getPrototypeOf(value);
99
+ return prototype === Object.prototype || prototype === null;
100
+ }
101
+ function isDataRecord(value) {
102
+ return isRecord(value) && Reflect.ownKeys(value).every((key) => {
103
+ if (typeof key !== 'string')
104
+ return false;
105
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
106
+ return descriptor?.enumerable === true && Object.hasOwn(descriptor, 'value');
107
+ });
108
+ }
109
+ function exactKeys(value, keys) {
110
+ return isDataRecord(value)
111
+ && Reflect.ownKeys(value).length === keys.size
112
+ && Object.keys(value).every((key) => keys.has(key));
113
+ }
114
+ function assertNoPrototypeNamedFields(value) {
115
+ if (value === null || typeof value !== 'object')
116
+ return;
117
+ const stack = [value];
118
+ const seen = new WeakSet();
119
+ while (stack.length > 0) {
120
+ const current = stack.pop();
121
+ if (seen.has(current))
122
+ continue;
123
+ seen.add(current);
124
+ for (const key of Reflect.ownKeys(current)) {
125
+ if (typeof key === 'string' && FORBIDDEN_FIELD_NAMES.has(key)) {
126
+ throw new TypeError(`prototype-named field ${key} is forbidden`);
127
+ }
128
+ const descriptor = Object.getOwnPropertyDescriptor(current, key);
129
+ if (!descriptor || !Object.hasOwn(descriptor, 'value'))
130
+ continue;
131
+ const child = descriptor.value;
132
+ if (typeof child === 'number' && Object.is(child, -0)) {
133
+ throw new TypeError('negative zero is not canonical JSON');
134
+ }
135
+ if (child !== null && typeof child === 'object')
136
+ stack.push(child);
137
+ }
138
+ }
139
+ }
140
+ function canonicalCopy(value, label) {
141
+ try {
142
+ assertNoPrototypeNamedFields(value);
143
+ return JSON.parse(canonicalize(value));
144
+ }
145
+ catch (error) {
146
+ if (error instanceof TypeError && /prototype-named field/.test(error.message))
147
+ throw error;
148
+ throw new TypeError(`${label} must be bounded canonical JSON`);
149
+ }
150
+ }
151
+ function deepFreeze(value) {
152
+ if (value === null || typeof value !== 'object')
153
+ return value;
154
+ const stack = [value];
155
+ const seen = new WeakSet();
156
+ while (stack.length > 0) {
157
+ const current = stack.pop();
158
+ if (seen.has(current))
159
+ continue;
160
+ seen.add(current);
161
+ for (const child of Object.values(current)) {
162
+ if (child !== null && typeof child === 'object')
163
+ stack.push(child);
164
+ }
165
+ Object.freeze(current);
166
+ }
167
+ return value;
168
+ }
169
+ function validId(value) {
170
+ return typeof value === 'string' && ID.test(value)
171
+ && !FORBIDDEN_FIELD_NAMES.has(value);
172
+ }
173
+ function validContext(value) {
174
+ return typeof value === 'string' && value.length > 0
175
+ && Buffer.byteLength(value, 'utf8') <= MAX_CONTEXT_BYTES
176
+ && !/[\u0000-\u001f\u007f]/.test(value)
177
+ && !FORBIDDEN_FIELD_NAMES.has(value);
178
+ }
179
+ function strictInstant(value) {
180
+ if (typeof value !== 'string'
181
+ || !/^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/.test(value))
182
+ return NaN;
183
+ const parsed = Date.parse(value);
184
+ return Number.isFinite(parsed) && new Date(parsed).toISOString() === value ? parsed : NaN;
185
+ }
186
+ function canonicalDigest(value) {
187
+ return `sha256:${crypto.createHash('sha256').update(canonicalize(value)).digest('hex')}`;
188
+ }
189
+ function strictBase64url(value, length) {
190
+ if (typeof value !== 'string' || !BASE64URL.test(value) || value.length % 4 === 1)
191
+ return null;
192
+ const bytes = Buffer.from(value, 'base64url');
193
+ if (bytes.toString('base64url') !== value
194
+ || (length !== undefined && bytes.length !== length))
195
+ return null;
196
+ return bytes;
197
+ }
198
+ function publicKey(value) {
199
+ try {
200
+ if (value instanceof crypto.KeyObject) {
201
+ return value.type === 'public' && value.asymmetricKeyType === 'ed25519' ? value : null;
202
+ }
203
+ const bytes = strictBase64url(value);
204
+ if (!bytes)
205
+ return null;
206
+ const key = crypto.createPublicKey({ key: bytes, type: 'spki', format: 'der' });
207
+ return key.asymmetricKeyType === 'ed25519' ? key : null;
208
+ }
209
+ catch {
210
+ return null;
211
+ }
212
+ }
213
+ function privateKey(value) {
214
+ try {
215
+ const key = value instanceof crypto.KeyObject ? value : crypto.createPrivateKey(value);
216
+ if (key.type !== 'private' || key.asymmetricKeyType !== 'ed25519')
217
+ throw new Error('wrong key');
218
+ return key;
219
+ }
220
+ catch {
221
+ throw new TypeError('remedy receipt private key must be Ed25519');
222
+ }
223
+ }
224
+ function publicKeyB64u(value) {
225
+ return crypto.createPublicKey(value)
226
+ .export({ type: 'spki', format: 'der' }).toString('base64url');
227
+ }
228
+ function canonicalSignature(value) {
229
+ const bytes = Buffer.isBuffer(value)
230
+ ? Buffer.from(value)
231
+ : value instanceof Uint8Array ? Buffer.from(value)
232
+ : strictBase64url(value, 64);
233
+ if (!bytes || bytes.length !== 64) {
234
+ throw new TypeError('remedy receipt signer returned a malformed Ed25519 signature');
235
+ }
236
+ const encoded = bytes.toString('base64url');
237
+ if (typeof value === 'string' && value !== encoded) {
238
+ throw new TypeError('remedy receipt signer returned a noncanonical signature');
239
+ }
240
+ return { bytes, encoded };
241
+ }
242
+ function validOriginal(value) {
243
+ return exactKeys(value, ORIGINAL_KEYS)
244
+ && typeof value.caid === 'string' && CAID.test(value.caid)
245
+ && typeof value.action_digest === 'string' && DIGEST.test(value.action_digest)
246
+ && validId(value.operation_id)
247
+ && validContext(value.consequence_mode)
248
+ && typeof value.consequence_digest === 'string' && DIGEST.test(value.consequence_digest)
249
+ && typeof value.terminal_evidence_digest === 'string'
250
+ && DIGEST.test(value.terminal_evidence_digest)
251
+ && ['executed', 'indeterminate'].includes(value.outcome)
252
+ && Number.isFinite(strictInstant(value.occurred_at))
253
+ && typeof value.evidence_digest === 'string' && DIGEST.test(value.evidence_digest);
254
+ }
255
+ function validOutcomeEvidence(value) {
256
+ return exactKeys(value, OUTCOME_EVIDENCE_KEYS)
257
+ && validId(value.evidence_id)
258
+ && typeof value.evidence_digest === 'string' && DIGEST.test(value.evidence_digest)
259
+ && validId(value.remedy_operation_id)
260
+ && typeof value.remedy_action_digest === 'string' && DIGEST.test(value.remedy_action_digest)
261
+ && typeof value.destination_binding_digest === 'string'
262
+ && DIGEST.test(value.destination_binding_digest)
263
+ && Number.isSafeInteger(value.units) && value.units > 0
264
+ && validContext(value.unit)
265
+ && REMEDY_OUTCOMES.has(value.outcome)
266
+ && Number.isFinite(strictInstant(value.observed_at));
267
+ }
268
+ function ownerDigest(value) {
269
+ if (value.consequence_mode === 'receipt-program'
270
+ && typeof value.capability_template_digest === 'string'
271
+ && DIGEST.test(value.capability_template_digest)
272
+ && value.escrow_profile_digest === null)
273
+ return value.capability_template_digest;
274
+ if (value.consequence_mode === 'action-escrow'
275
+ && value.capability_template_digest === null
276
+ && typeof value.escrow_profile_digest === 'string'
277
+ && DIGEST.test(value.escrow_profile_digest))
278
+ return value.escrow_profile_digest;
279
+ return null;
280
+ }
281
+ function validNullableDigest(value) {
282
+ return value === null || (typeof value === 'string' && DIGEST.test(value));
283
+ }
284
+ function validAttempt(value) {
285
+ return exactKeys(value, ATTEMPT_KEYS)
286
+ && validId(value.evidence_id)
287
+ && typeof value.evidence_digest === 'string' && DIGEST.test(value.evidence_digest)
288
+ && validId(value.dispute_id) && validId(value.original_operation_id)
289
+ && validId(value.remedy_operation_id)
290
+ && typeof value.remedy_caid === 'string' && CAID.test(value.remedy_caid)
291
+ && typeof value.remedy_action_digest === 'string' && DIGEST.test(value.remedy_action_digest)
292
+ && ownerDigest(value) !== null
293
+ && typeof value.destination_binding_digest === 'string'
294
+ && DIGEST.test(value.destination_binding_digest)
295
+ && Number.isSafeInteger(value.units) && value.units > 0
296
+ && validContext(value.unit)
297
+ && Number.isFinite(strictInstant(value.authorized_at))
298
+ && typeof value.request_digest === 'string' && DIGEST.test(value.request_digest)
299
+ && REMEDY_STATUSES.has(value.status)
300
+ && validNullableDigest(value.claim_token_digest)
301
+ && (value.claimed_at === null || Number.isFinite(strictInstant(value.claimed_at)))
302
+ && validNullableDigest(value.claim_request_digest)
303
+ && (value.outcome === null || REMEDY_OUTCOMES.has(value.outcome))
304
+ && (value.outcome_evidence === null || validOutcomeEvidence(value.outcome_evidence))
305
+ && validNullableDigest(value.finalize_request_digest)
306
+ && (value.reconciliation === null || validOutcomeEvidence(value.reconciliation))
307
+ && validNullableDigest(value.reconcile_request_digest);
308
+ }
309
+ function validDispute(value) {
310
+ return exactKeys(value, DISPUTE_KEYS)
311
+ && validId(value.dispute_id) && validId(value.evidence_id)
312
+ && typeof value.evidence_digest === 'string' && DIGEST.test(value.evidence_digest)
313
+ && validContext(value.challenger_id)
314
+ && Number.isSafeInteger(value.requested_units) && value.requested_units > 0
315
+ && Number.isFinite(strictInstant(value.opened_at))
316
+ && validId(value.original_operation_id)
317
+ && typeof value.original_action_digest === 'string' && DIGEST.test(value.original_action_digest)
318
+ && typeof value.request_digest === 'string' && DIGEST.test(value.request_digest);
319
+ }
320
+ function validRevocation(value) {
321
+ return exactKeys(value, REVOCATION_KEYS)
322
+ && validId(value.evidence_id)
323
+ && typeof value.evidence_digest === 'string' && DIGEST.test(value.evidence_digest)
324
+ && validId(value.target_operation_id)
325
+ && typeof value.action_digest === 'string' && DIGEST.test(value.action_digest)
326
+ && validContext(value.authority_id)
327
+ && Number.isFinite(strictInstant(value.revoked_at))
328
+ && value.effect === 'future_authority_only'
329
+ && typeof value.request_digest === 'string' && DIGEST.test(value.request_digest);
330
+ }
331
+ function validResolution(value) {
332
+ return exactKeys(value, RESOLUTION_KEYS)
333
+ && validId(value.evidence_id)
334
+ && typeof value.evidence_digest === 'string' && DIGEST.test(value.evidence_digest)
335
+ && value.outcome === 'no_remedy'
336
+ && Number.isFinite(strictInstant(value.resolved_at))
337
+ && validId(value.dispute_id)
338
+ && typeof value.request_digest === 'string' && DIGEST.test(value.request_digest);
339
+ }
340
+ function validOriginalReconciliation(value) {
341
+ return exactKeys(value, new Set([
342
+ 'evidence_id', 'evidence_digest', 'original_operation_id',
343
+ 'original_action_digest', 'terminal_evidence_digest', 'outcome',
344
+ 'observed_at', 'request_digest',
345
+ ]))
346
+ && validId(value.evidence_id)
347
+ && typeof value.evidence_digest === 'string' && DIGEST.test(value.evidence_digest)
348
+ && validId(value.original_operation_id)
349
+ && typeof value.original_action_digest === 'string' && DIGEST.test(value.original_action_digest)
350
+ && typeof value.terminal_evidence_digest === 'string' && DIGEST.test(value.terminal_evidence_digest)
351
+ && ['executed', 'proved_no_effect'].includes(value.outcome)
352
+ && Number.isFinite(strictInstant(value.observed_at))
353
+ && typeof value.request_digest === 'string' && DIGEST.test(value.request_digest);
354
+ }
355
+ function validState(state) {
356
+ if (!exactKeys(state, STATE_KEYS)
357
+ || state.version !== REMEDY_PROGRAM_VERSION
358
+ || !validId(state.instance_id)
359
+ || !validContext(state.tenant_id)
360
+ || !validContext(state.environment)
361
+ || !validContext(state.audience)
362
+ || !CASE_STATUSES.has(state.status)
363
+ || !Number.isSafeInteger(state.revision) || state.revision < 0
364
+ || !Number.isFinite(strictInstant(state.created_at))
365
+ || !Number.isFinite(strictInstant(state.updated_at))
366
+ || strictInstant(state.updated_at) < strictInstant(state.created_at)
367
+ || !validOriginal(state.original)
368
+ || typeof state.remedy_profile_digest !== 'string' || !DIGEST.test(state.remedy_profile_digest)
369
+ || typeof state.destination_binding_digest !== 'string'
370
+ || !DIGEST.test(state.destination_binding_digest)
371
+ || !Number.isSafeInteger(state.max_remedy_units) || state.max_remedy_units < 1
372
+ || !validContext(state.unit)
373
+ || !Number.isSafeInteger(state.remedied_units) || state.remedied_units < 0
374
+ || !Number.isSafeInteger(state.remaining_units) || state.remaining_units < 0
375
+ || state.remedied_units + state.remaining_units !== state.max_remedy_units
376
+ || !Array.isArray(state.used_evidence_ids)
377
+ || !state.used_evidence_ids.every(validId)
378
+ || new Set(state.used_evidence_ids).size !== state.used_evidence_ids.length
379
+ || !Array.isArray(state.used_evidence_digests)
380
+ || !state.used_evidence_digests.every((entry) => (typeof entry === 'string' && DIGEST.test(entry)))
381
+ || new Set(state.used_evidence_digests).size !== state.used_evidence_digests.length
382
+ || !Array.isArray(state.remedies) || state.remedies.length > MAX_REMEDIES
383
+ || !state.remedies.every(validAttempt)
384
+ || (state.active_remedy !== null && !validAttempt(state.active_remedy))
385
+ || (state.dispute !== null && !validDispute(state.dispute))
386
+ || (state.revocation !== null && !validRevocation(state.revocation))
387
+ || (state.original_reconciliation !== null
388
+ && !validOriginalReconciliation(state.original_reconciliation))
389
+ || (state.resolution !== null && !validResolution(state.resolution))
390
+ || typeof state.create_request_digest !== 'string'
391
+ || !DIGEST.test(state.create_request_digest))
392
+ return false;
393
+ if (state.original_reconciliation !== null
394
+ && (state.original.outcome !== 'indeterminate'
395
+ || state.original_reconciliation.original_operation_id !== state.original.operation_id
396
+ || state.original_reconciliation.original_action_digest !== state.original.action_digest
397
+ || state.original_reconciliation.terminal_evidence_digest !== state.original.terminal_evidence_digest
398
+ || strictInstant(state.original_reconciliation.observed_at) < strictInstant(state.original.occurred_at))) {
399
+ return false;
400
+ }
401
+ const attempts = [
402
+ ...(state.active_remedy === null ? [] : [state.active_remedy]),
403
+ ...state.remedies,
404
+ ];
405
+ return attempts.every((attempt) => (attempt.original_operation_id === state.original.operation_id
406
+ && attempt.destination_binding_digest === state.destination_binding_digest
407
+ && attempt.unit === state.unit));
408
+ }
409
+ function selectAttempt(state, remedyOperationId) {
410
+ const attempts = [
411
+ ...(state.active_remedy === null ? [] : [state.active_remedy]),
412
+ ...state.remedies,
413
+ ].filter((attempt) => attempt.remedy_operation_id === remedyOperationId);
414
+ if (attempts.length === 0)
415
+ throw new TypeError('remedy operation is absent from the state snapshot');
416
+ if (attempts.length !== 1)
417
+ throw new TypeError('remedy operation is not unique in the state snapshot');
418
+ const attempt = attempts[0];
419
+ if (attempt.remedy_operation_id === state.original.operation_id
420
+ || attempt.remedy_action_digest === state.original.action_digest) {
421
+ throw new TypeError('a remedy receipt must describe a compensating action, never rollback');
422
+ }
423
+ return attempt;
424
+ }
425
+ function expectedBindings(state, attempt) {
426
+ return {
427
+ original_operation_id: state.original.operation_id,
428
+ original_action_digest: state.original.action_digest,
429
+ original_terminal_evidence_digest: state.original.terminal_evidence_digest,
430
+ case_instance_id: state.instance_id,
431
+ case_revision: state.revision,
432
+ case_status: state.status,
433
+ remedy_operation_id: attempt.remedy_operation_id,
434
+ remedy_action_digest: attempt.remedy_action_digest,
435
+ remedy_caid: attempt.remedy_caid,
436
+ destination_binding_digest: attempt.destination_binding_digest,
437
+ units: attempt.units,
438
+ unit: attempt.unit,
439
+ owner_mode: attempt.consequence_mode,
440
+ owner_digest: ownerDigest(attempt),
441
+ };
442
+ }
443
+ function snapshotAndAttempt(value, remedyOperationId) {
444
+ const state = canonicalCopy(value, 'remedy state snapshot');
445
+ if (!validState(state))
446
+ throw new TypeError('remedy state snapshot is invalid');
447
+ if (!validId(remedyOperationId))
448
+ throw new TypeError('remedyOperationId is invalid');
449
+ return { state, attempt: selectAttempt(state, remedyOperationId) };
450
+ }
451
+ /** Derive every relying-party binding that must be independently expected. */
452
+ export function expectedRemedyProgramReceiptBindings(state, remedyOperationId) {
453
+ const selected = snapshotAndAttempt(state, remedyOperationId);
454
+ return deepFreeze(expectedBindings(selected.state, selected.attempt));
455
+ }
456
+ function payloadFor(state, attempt) {
457
+ return {
458
+ case: {
459
+ instance_id: state.instance_id,
460
+ revision: state.revision,
461
+ status: state.status,
462
+ state_snapshot_digest: canonicalDigest(state),
463
+ updated_at: state.updated_at,
464
+ },
465
+ original_effect: canonicalCopy(state.original, 'original effect'),
466
+ original_reconciliation: state.original_reconciliation === null
467
+ ? null : canonicalCopy(state.original_reconciliation, 'original reconciliation'),
468
+ remedy: {
469
+ operation_id: attempt.remedy_operation_id,
470
+ caid: attempt.remedy_caid,
471
+ action_digest: attempt.remedy_action_digest,
472
+ destination_binding_digest: attempt.destination_binding_digest,
473
+ units: attempt.units,
474
+ unit: attempt.unit,
475
+ owner_mode: attempt.consequence_mode,
476
+ owner_digest: ownerDigest(attempt),
477
+ status: attempt.status,
478
+ outcome: attempt.outcome,
479
+ },
480
+ semantics: {
481
+ original_effect: 'immutable_fact',
482
+ remedy_effect: 'compensating_action',
483
+ rollback: false,
484
+ },
485
+ };
486
+ }
487
+ function contentBody(receipt) {
488
+ return {
489
+ version: receipt.version,
490
+ issuer: receipt.issuer,
491
+ payload: receipt.payload,
492
+ };
493
+ }
494
+ function signingBody(receipt) {
495
+ return {
496
+ ...contentBody(receipt),
497
+ content_digest: receipt.content_digest,
498
+ };
499
+ }
500
+ /** Return the exact domain-separated canonical bytes signed by Ed25519. */
501
+ export function remedyProgramReceiptSigningBytes(receipt) {
502
+ if (!isDataRecord(receipt))
503
+ throw new TypeError('remedy receipt signing body is invalid');
504
+ return Buffer.from(ACTION_REMEDY_RECEIPT_DOMAIN + canonicalize(signingBody(receipt)), 'utf8');
505
+ }
506
+ function validIssuer(value) {
507
+ return exactKeys(value, ISSUER_KEYS) && Object.values(value).every(validContext);
508
+ }
509
+ function validExpected(value) {
510
+ return exactKeys(value, EXPECTED_KEYS)
511
+ && validId(value.original_operation_id)
512
+ && typeof value.original_action_digest === 'string' && DIGEST.test(value.original_action_digest)
513
+ && typeof value.original_terminal_evidence_digest === 'string'
514
+ && DIGEST.test(value.original_terminal_evidence_digest)
515
+ && validId(value.case_instance_id)
516
+ && Number.isSafeInteger(value.case_revision) && value.case_revision >= 0
517
+ && CASE_STATUSES.has(value.case_status)
518
+ && validId(value.remedy_operation_id)
519
+ && typeof value.remedy_action_digest === 'string' && DIGEST.test(value.remedy_action_digest)
520
+ && typeof value.remedy_caid === 'string' && CAID.test(value.remedy_caid)
521
+ && typeof value.destination_binding_digest === 'string'
522
+ && DIGEST.test(value.destination_binding_digest)
523
+ && Number.isSafeInteger(value.units) && value.units > 0
524
+ && validContext(value.unit)
525
+ && ['receipt-program', 'action-escrow'].includes(value.owner_mode)
526
+ && typeof value.owner_digest === 'string' && DIGEST.test(value.owner_digest);
527
+ }
528
+ function validPayload(value) {
529
+ return exactKeys(value, PAYLOAD_KEYS)
530
+ && exactKeys(value.case, CASE_KEYS)
531
+ && validId(value.case.instance_id)
532
+ && Number.isSafeInteger(value.case.revision) && value.case.revision >= 0
533
+ && CASE_STATUSES.has(value.case.status)
534
+ && typeof value.case.state_snapshot_digest === 'string'
535
+ && DIGEST.test(value.case.state_snapshot_digest)
536
+ && Number.isFinite(strictInstant(value.case.updated_at))
537
+ && validOriginal(value.original_effect)
538
+ && (value.original_reconciliation === null
539
+ || validOriginalReconciliation(value.original_reconciliation))
540
+ && exactKeys(value.remedy, REMEDY_KEYS)
541
+ && validId(value.remedy.operation_id)
542
+ && typeof value.remedy.caid === 'string' && CAID.test(value.remedy.caid)
543
+ && typeof value.remedy.action_digest === 'string' && DIGEST.test(value.remedy.action_digest)
544
+ && typeof value.remedy.destination_binding_digest === 'string'
545
+ && DIGEST.test(value.remedy.destination_binding_digest)
546
+ && Number.isSafeInteger(value.remedy.units) && value.remedy.units > 0
547
+ && validContext(value.remedy.unit)
548
+ && ['receipt-program', 'action-escrow'].includes(value.remedy.owner_mode)
549
+ && typeof value.remedy.owner_digest === 'string' && DIGEST.test(value.remedy.owner_digest)
550
+ && REMEDY_STATUSES.has(value.remedy.status)
551
+ && (value.remedy.outcome === null || REMEDY_OUTCOMES.has(value.remedy.outcome))
552
+ && exactKeys(value.semantics, SEMANTICS_KEYS)
553
+ && value.semantics.original_effect === 'immutable_fact'
554
+ && value.semantics.remedy_effect === 'compensating_action'
555
+ && value.semantics.rollback === false
556
+ && value.remedy.operation_id !== value.original_effect.operation_id
557
+ && value.remedy.action_digest !== value.original_effect.action_digest;
558
+ }
559
+ function validReceipt(value) {
560
+ return exactKeys(value, TOP_KEYS)
561
+ && value.version === ACTION_REMEDY_RECEIPT_VERSION
562
+ && validIssuer(value.issuer)
563
+ && validPayload(value.payload)
564
+ && typeof value.content_digest === 'string' && DIGEST.test(value.content_digest)
565
+ && exactKeys(value.signature, SIGNATURE_KEYS)
566
+ && value.signature.algorithm === 'Ed25519'
567
+ && strictBase64url(value.signature.value, 64) !== null;
568
+ }
569
+ function sameCanonical(left, right) {
570
+ try {
571
+ return canonicalize(left) === canonicalize(right);
572
+ }
573
+ catch {
574
+ return false;
575
+ }
576
+ }
577
+ function receiptExpected(receipt) {
578
+ return {
579
+ original_operation_id: receipt.payload.original_effect.operation_id,
580
+ original_action_digest: receipt.payload.original_effect.action_digest,
581
+ original_terminal_evidence_digest: receipt.payload.original_effect.terminal_evidence_digest,
582
+ case_instance_id: receipt.payload.case.instance_id,
583
+ case_revision: receipt.payload.case.revision,
584
+ case_status: receipt.payload.case.status,
585
+ remedy_operation_id: receipt.payload.remedy.operation_id,
586
+ remedy_action_digest: receipt.payload.remedy.action_digest,
587
+ remedy_caid: receipt.payload.remedy.caid,
588
+ destination_binding_digest: receipt.payload.remedy.destination_binding_digest,
589
+ units: receipt.payload.remedy.units,
590
+ unit: receipt.payload.remedy.unit,
591
+ owner_mode: receipt.payload.remedy.owner_mode,
592
+ owner_digest: receipt.payload.remedy.owner_digest,
593
+ };
594
+ }
595
+ function configureSigner(options, context) {
596
+ const hasPrivateKey = options.privateKey !== undefined;
597
+ const hasSigner = options.signer !== undefined;
598
+ if (hasPrivateKey === hasSigner)
599
+ throw new TypeError('configure exactly one remedy receipt signer');
600
+ const allowEphemeralState = options.allowEphemeralState === true;
601
+ if (hasPrivateKey) {
602
+ if (!allowEphemeralState) {
603
+ throw new TypeError('production remedy receipt issuance requires an external KMS/HSM signer');
604
+ }
605
+ const key = privateKey(options.privateKey);
606
+ return Object.freeze({
607
+ keyId: context.key_id,
608
+ publicKey: publicKeyB64u(key),
609
+ sign: async (bytes) => crypto.sign(null, bytes, key),
610
+ });
611
+ }
612
+ const signer = options.signer;
613
+ if (!isDataRecord(signer))
614
+ throw new TypeError('remedy receipt signer must be a data object');
615
+ if (!validContext(signer.keyId)
616
+ || typeof signer.publicKey !== 'string'
617
+ || typeof signer.sign !== 'function') {
618
+ throw new TypeError('remedy receipt signer requires keyId, publicKey, and sign(bytes)');
619
+ }
620
+ if (!allowEphemeralState && !['kms', 'hsm'].includes(signer.custody)) {
621
+ throw new TypeError('production remedy receipt signer custody must be kms or hsm');
622
+ }
623
+ if (!publicKey(signer.publicKey)) {
624
+ throw new TypeError('remedy receipt signer public key must be Ed25519 SPKI base64url');
625
+ }
626
+ return Object.freeze({
627
+ keyId: signer.keyId,
628
+ publicKey: signer.publicKey,
629
+ sign: signer.sign,
630
+ });
631
+ }
632
+ /**
633
+ * Issue one receipt. Local private keys require an explicit ephemeral/test
634
+ * opt-in; production issuance requires an external signer declaring KMS/HSM
635
+ * custody. Every returned signature is verified against the configured public
636
+ * key before the receipt leaves this function.
637
+ */
638
+ export async function issueRemedyProgramReceipt(input = {}, options = {}) {
639
+ if (!exactKeys(input, new Set(['state', 'remedyOperationId']))) {
640
+ throw new TypeError('remedy receipt input must contain exactly state and remedyOperationId');
641
+ }
642
+ const selected = snapshotAndAttempt(input.state, input.remedyOperationId);
643
+ const context = canonicalCopy(options.context, 'remedy receipt context');
644
+ if (!validIssuer(context)) {
645
+ throw new TypeError('remedy receipt context must contain exact pinned issuer fields');
646
+ }
647
+ if (context.tenant !== selected.state.tenant_id
648
+ || context.environment !== selected.state.environment
649
+ || context.audience !== selected.state.audience) {
650
+ throw new TypeError('remedy receipt context does not match state tenant/environment/audience');
651
+ }
652
+ const signer = configureSigner(options, context);
653
+ if (signer.keyId !== context.key_id) {
654
+ throw new TypeError('remedy receipt context key_id does not match signer keyId');
655
+ }
656
+ const unsigned = {
657
+ version: ACTION_REMEDY_RECEIPT_VERSION,
658
+ issuer: context,
659
+ payload: payloadFor(selected.state, selected.attempt),
660
+ };
661
+ const signedBody = {
662
+ ...unsigned,
663
+ content_digest: canonicalDigest(unsigned),
664
+ };
665
+ const signingBytes = remedyProgramReceiptSigningBytes(signedBody);
666
+ const signature = canonicalSignature(await signer.sign(Buffer.from(signingBytes)));
667
+ const verificationKey = publicKey(signer.publicKey);
668
+ if (!crypto.verify(null, signingBytes, verificationKey, signature.bytes)) {
669
+ throw new TypeError('remedy receipt signer self-verification failed');
670
+ }
671
+ const receipt = canonicalCopy({
672
+ ...signedBody,
673
+ signature: { algorithm: 'Ed25519', value: signature.encoded },
674
+ }, 'remedy receipt');
675
+ const expected = expectedBindings(selected.state, selected.attempt);
676
+ const selfCheck = verifyRemedyProgramReceipt(receipt, {
677
+ trustedKeys: { [context.key_id]: signer.publicKey },
678
+ expectedIssuer: context,
679
+ state: selected.state,
680
+ expected,
681
+ });
682
+ if (!selfCheck.valid) {
683
+ throw new TypeError(`remedy receipt self-verification failed: ${selfCheck.reason}`);
684
+ }
685
+ return deepFreeze(receipt);
686
+ }
687
+ export const signRemedyProgramReceipt = issueRemedyProgramReceipt;
688
+ export const createRemedyProgramReceipt = issueRemedyProgramReceipt;
689
+ function refusal(reason, checks) {
690
+ return Object.freeze({
691
+ valid: false,
692
+ reason,
693
+ checks: Object.freeze({ ...checks }),
694
+ content_digest: null,
695
+ payload: null,
696
+ });
697
+ }
698
+ /**
699
+ * Verify a receipt without network access. Trust keys, all issuer fields, the
700
+ * exact current state snapshot, and every material original/remedy binding are
701
+ * relying-party inputs; none are accepted from the receipt itself.
702
+ */
703
+ export function verifyRemedyProgramReceipt(receipt, { trustedKeys, expectedIssuer, state, expected, } = {}) {
704
+ const checks = {
705
+ structure: false,
706
+ payload: false,
707
+ content_digest: false,
708
+ issuer_pin: false,
709
+ key: false,
710
+ signature: false,
711
+ state_snapshot: false,
712
+ expected_bindings: false,
713
+ };
714
+ try {
715
+ const snapshot = canonicalCopy(receipt, 'remedy receipt');
716
+ if (!exactKeys(snapshot, TOP_KEYS)
717
+ || snapshot.version !== ACTION_REMEDY_RECEIPT_VERSION
718
+ || !validIssuer(snapshot.issuer)
719
+ || !exactKeys(snapshot.signature, SIGNATURE_KEYS)) {
720
+ return refusal('receipt_structure_invalid', checks);
721
+ }
722
+ checks.structure = true;
723
+ if (!validPayload(snapshot.payload)) {
724
+ return refusal('receipt_structure_invalid', checks);
725
+ }
726
+ checks.payload = true;
727
+ if (typeof snapshot.content_digest !== 'string'
728
+ || !DIGEST.test(snapshot.content_digest)
729
+ || canonicalDigest(contentBody(snapshot)) !== snapshot.content_digest) {
730
+ return refusal('receipt_content_digest_mismatch', checks);
731
+ }
732
+ checks.content_digest = true;
733
+ let issuerPin;
734
+ try {
735
+ issuerPin = canonicalCopy(expectedIssuer, 'expected remedy receipt issuer');
736
+ }
737
+ catch {
738
+ return refusal('receipt_expected_issuer_mismatch', checks);
739
+ }
740
+ checks.issuer_pin = validIssuer(issuerPin) && sameCanonical(snapshot.issuer, issuerPin);
741
+ if (!checks.issuer_pin)
742
+ return refusal('receipt_expected_issuer_mismatch', checks);
743
+ const trusted = isDataRecord(trustedKeys)
744
+ && Object.hasOwn(trustedKeys, snapshot.issuer.key_id)
745
+ ? trustedKeys[snapshot.issuer.key_id] : null;
746
+ const key = publicKey(trusted);
747
+ checks.key = key !== null;
748
+ if (!checks.key)
749
+ return refusal('receipt_key_untrusted', checks);
750
+ const signatureBytes = snapshot.signature.algorithm === 'Ed25519'
751
+ ? strictBase64url(snapshot.signature.value, 64) : null;
752
+ checks.signature = signatureBytes !== null
753
+ && crypto.verify(null, remedyProgramReceiptSigningBytes(snapshot), key, signatureBytes);
754
+ if (!checks.signature)
755
+ return refusal('receipt_signature_invalid', checks);
756
+ let stateCopy;
757
+ try {
758
+ stateCopy = canonicalCopy(state, 'expected remedy state snapshot');
759
+ }
760
+ catch {
761
+ return refusal('receipt_state_snapshot_mismatch', checks);
762
+ }
763
+ checks.state_snapshot = validState(stateCopy)
764
+ && canonicalDigest(stateCopy) === snapshot.payload.case.state_snapshot_digest
765
+ && stateCopy.instance_id === snapshot.payload.case.instance_id
766
+ && stateCopy.revision === snapshot.payload.case.revision
767
+ && stateCopy.status === snapshot.payload.case.status
768
+ && stateCopy.updated_at === snapshot.payload.case.updated_at;
769
+ if (!checks.state_snapshot)
770
+ return refusal('receipt_state_snapshot_mismatch', checks);
771
+ let expectedCopy;
772
+ try {
773
+ expectedCopy = canonicalCopy(expected, 'expected remedy bindings');
774
+ }
775
+ catch {
776
+ return refusal('receipt_expected_binding_mismatch', checks);
777
+ }
778
+ let derivedAttempt;
779
+ try {
780
+ if (!validExpected(expectedCopy)) {
781
+ return refusal('receipt_expected_binding_mismatch', checks);
782
+ }
783
+ derivedAttempt = selectAttempt(stateCopy, expectedCopy.remedy_operation_id);
784
+ }
785
+ catch {
786
+ return refusal('receipt_expected_binding_mismatch', checks);
787
+ }
788
+ checks.expected_bindings = sameCanonical(receiptExpected(snapshot), expectedCopy)
789
+ && sameCanonical(expectedBindings(stateCopy, derivedAttempt), expectedCopy)
790
+ && sameCanonical(payloadFor(stateCopy, derivedAttempt), snapshot.payload);
791
+ if (!checks.expected_bindings) {
792
+ return refusal('receipt_expected_binding_mismatch', checks);
793
+ }
794
+ return deepFreeze({
795
+ valid: true,
796
+ reason: 'verified',
797
+ checks,
798
+ content_digest: snapshot.content_digest,
799
+ payload: canonicalCopy(snapshot.payload, 'verified remedy receipt payload'),
800
+ });
801
+ }
802
+ catch {
803
+ return refusal('receipt_structure_invalid', checks);
804
+ }
805
+ }
806
+ export default {
807
+ ACTION_REMEDY_RECEIPT_VERSION,
808
+ REMEDY_PROGRAM_RECEIPT_VERSION,
809
+ ACTION_REMEDY_RECEIPT_DOMAIN,
810
+ expectedRemedyProgramReceiptBindings,
811
+ remedyProgramReceiptSigningBytes,
812
+ issueRemedyProgramReceipt,
813
+ signRemedyProgramReceipt,
814
+ createRemedyProgramReceipt,
815
+ verifyRemedyProgramReceipt,
816
+ };
817
+ //# sourceMappingURL=remedy-program-receipt.js.map