@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,1105 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+
3
+ /**
4
+ * Receipt-program execution profile for EMILIA Gate.
5
+ *
6
+ * This module is intentionally a composition kernel, not a second policy
7
+ * engine or ledger. Gate remains the authorization/effect boundary, the
8
+ * capability store remains the atomic budget/replay authority, CAID remains
9
+ * material-action identity, and the Gate evidence log remains the execution
10
+ * history. The kernel freezes those inputs into one signed, offline-checkable
11
+ * execution certificate.
12
+ */
13
+
14
+ import {
15
+ createHash,
16
+ createPrivateKey,
17
+ createPublicKey,
18
+ sign,
19
+ verify,
20
+ } from 'node:crypto';
21
+
22
+ import { canonicalize } from './execution-binding.js';
23
+ import { canonicalEvidenceJson, verifyEvidenceRecord } from './evidence.js';
24
+
25
+ export const RECEIPT_PROGRAM_VERSION = 'EP-RECEIPT-PROGRAM-v1';
26
+ export const RECEIPT_PROGRAM_CERTIFICATE_VERSION = 'EP-RECEIPT-PROGRAM-CERTIFICATE-v1';
27
+ export const RECEIPT_PROGRAM_SIGNATURE_ALGORITHM = 'Ed25519';
28
+
29
+ const CAID_RE = /^caid:1:[a-z][a-z0-9.-]*\.[1-9][0-9]*:jcs-sha256:[A-Za-z0-9_-]{43}$/;
30
+ const SHA256_RE = /^sha256:[0-9a-f]{64}$/;
31
+ const MAX_ID_BYTES = 256;
32
+ const MAX_CANONICAL_BYTES = 1024 * 1024;
33
+ const MAX_PROGRAM_BYTES = 512 * 1024;
34
+ const MAX_RESULT_BYTES = 128 * 1024;
35
+ const MAX_CERTIFICATE_CORE_BYTES = 768 * 1024;
36
+ const FORBIDDEN_RUNTIME_TRUST_FIELDS = new Set([
37
+ 'allowEphemeralState',
38
+ 'certificatePrivateKey',
39
+ 'certificateSigner',
40
+ 'effectTimeoutMs',
41
+ 'gate',
42
+ 'now',
43
+ 'operationIdField',
44
+ 'projectResult',
45
+ 'resolveCaid',
46
+ 'trustedCertificateKeys',
47
+ ]);
48
+
49
+ function sha256(value: any): string {
50
+ return createHash('sha256').update(value).digest('hex');
51
+ }
52
+
53
+ function canonicalDigest(value: any): string {
54
+ return `sha256:${sha256(Buffer.from(canonicalize(value), 'utf8'))}`;
55
+ }
56
+
57
+ function isRecord(value: any): boolean {
58
+ if (value === null || typeof value !== 'object' || Array.isArray(value)) return false;
59
+ const prototype = Object.getPrototypeOf(value);
60
+ return prototype === Object.prototype || prototype === null;
61
+ }
62
+
63
+ function isDataRecord(value: any): boolean {
64
+ if (!isRecord(value)) return false;
65
+ return Reflect.ownKeys(value).every((key) => {
66
+ if (typeof key !== 'string') return false;
67
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
68
+ return descriptor?.enumerable === true && Object.hasOwn(descriptor, 'value');
69
+ });
70
+ }
71
+
72
+ function hasExactKeys(value: any, keys: readonly string[]): boolean {
73
+ if (!isDataRecord(value)) return false;
74
+ const actual = Object.keys(value).sort();
75
+ const expected = [...keys].sort();
76
+ return actual.length === expected.length
77
+ && actual.every((key, index) => key === expected[index]);
78
+ }
79
+
80
+ function validInstant(value: any): boolean {
81
+ if (typeof value !== 'string') return false;
82
+ const timestamp = Date.parse(value);
83
+ return Number.isFinite(timestamp) && new Date(timestamp).toISOString() === value;
84
+ }
85
+
86
+ function deepFreeze(value: any): any {
87
+ if (!value || typeof value !== 'object') return value;
88
+ const stack = [value];
89
+ const seen = new WeakSet();
90
+ while (stack.length) {
91
+ const current = stack.pop();
92
+ if (!current || typeof current !== 'object' || seen.has(current)) continue;
93
+ seen.add(current);
94
+ for (const child of Object.values(current)) stack.push(child);
95
+ Object.freeze(current);
96
+ }
97
+ return value;
98
+ }
99
+
100
+ function cloneCanonical(value: any, label: string, freeze = true, maxBytes = MAX_CANONICAL_BYTES): any {
101
+ let snapshot;
102
+ try {
103
+ // Validate the caller's graph before cloning. The canonicalizer inspects
104
+ // descriptors without invoking accessors and bounds depth, nodes, strings,
105
+ // aliases, arrays, numbers, and Unicode.
106
+ const canonical = canonicalize(value);
107
+ snapshot = structuredClone(value);
108
+ if (Buffer.byteLength(canonical, 'utf8') > maxBytes) {
109
+ throw new Error('canonical value exceeds byte limit');
110
+ }
111
+ } catch {
112
+ throw new TypeError(`${label} must be bounded canonical JSON`);
113
+ }
114
+ return freeze ? deepFreeze(snapshot) : snapshot;
115
+ }
116
+
117
+ function placeholderId(value: any): string {
118
+ return typeof value === 'string' && value.length > 0
119
+ && Buffer.byteLength(value, 'utf8') <= MAX_ID_BYTES
120
+ ? value : 'invalid';
121
+ }
122
+
123
+ function boundedId(value: any, label: string): string {
124
+ if (typeof value !== 'string' || value.length === 0
125
+ || Buffer.byteLength(value, 'utf8') > MAX_ID_BYTES) {
126
+ throw new TypeError(`${label} must be a non-empty string of at most ${MAX_ID_BYTES} bytes`);
127
+ }
128
+ return value;
129
+ }
130
+
131
+ function keyObject(value: any, label: string): any {
132
+ try {
133
+ const key = value?.type === 'private' ? value : createPrivateKey(value);
134
+ if (key.asymmetricKeyType !== 'ed25519') throw new Error('wrong key type');
135
+ return key;
136
+ } catch {
137
+ throw new TypeError(`${label} must be an Ed25519 private key`);
138
+ }
139
+ }
140
+
141
+ function publicKeyB64u(privateKey: any): string {
142
+ return createPublicKey(privateKey).export({ type: 'spki', format: 'der' }).toString('base64url');
143
+ }
144
+
145
+ function publicKeyObject(publicKey: any): any {
146
+ const bytes = Buffer.from(publicKey, 'base64url');
147
+ if (bytes.toString('base64url') !== publicKey) throw new Error('non-canonical public key');
148
+ const key = createPublicKey({
149
+ key: Buffer.from(publicKey, 'base64url'),
150
+ type: 'spki',
151
+ format: 'der',
152
+ });
153
+ if (key.asymmetricKeyType !== 'ed25519') throw new Error('wrong public key type');
154
+ return key;
155
+ }
156
+
157
+ function canonicalSignature(value: any): { bytes: Buffer; encoded: string } {
158
+ const bytes = Buffer.isBuffer(value)
159
+ ? Buffer.from(value)
160
+ : typeof value === 'string' ? Buffer.from(value, 'base64url') : null;
161
+ if (!bytes || bytes.length !== 64) throw new Error('certificate signer returned an invalid signature');
162
+ const encoded = bytes.toString('base64url');
163
+ if (typeof value === 'string' && encoded !== value) {
164
+ throw new Error('certificate signer returned a non-canonical signature');
165
+ }
166
+ return { bytes, encoded };
167
+ }
168
+
169
+ function configureCertificateSigner({
170
+ certificatePrivateKey,
171
+ certificateSigner,
172
+ allowEphemeralState,
173
+ }: any): any {
174
+ if (certificateSigner !== undefined && certificatePrivateKey !== undefined) {
175
+ throw new TypeError('configure exactly one certificate signer');
176
+ }
177
+ if (certificateSigner !== undefined) {
178
+ if (!isDataRecord(certificateSigner)) {
179
+ throw new TypeError('certificateSigner must be a data object');
180
+ }
181
+ const publicKey = certificateSigner.publicKey ?? certificateSigner.publicKeySpkiB64u;
182
+ if (typeof publicKey !== 'string' || typeof certificateSigner.sign !== 'function'
183
+ || typeof certificateSigner.keyId !== 'string') {
184
+ throw new TypeError('certificateSigner requires keyId, publicKey, and async sign(bytes)');
185
+ }
186
+ publicKeyObject(publicKey);
187
+ if (!allowEphemeralState && !['kms', 'hsm'].includes(certificateSigner.custody)) {
188
+ throw new Error('receipt program production certificate signer custody must be kms or hsm');
189
+ }
190
+ return Object.freeze({
191
+ keyId: boundedId(certificateSigner.keyId, 'certificateSigner.keyId'),
192
+ publicKey,
193
+ sign: certificateSigner.sign,
194
+ });
195
+ }
196
+ if (!allowEphemeralState) {
197
+ throw new Error('receipt program production mode requires an external KMS/HSM certificate signer');
198
+ }
199
+ const privateKey = keyObject(certificatePrivateKey, 'certificatePrivateKey');
200
+ return Object.freeze({
201
+ keyId: 'local-dev',
202
+ publicKey: publicKeyB64u(privateKey),
203
+ sign: async (bytes: Buffer) => sign(null, bytes, privateKey),
204
+ });
205
+ }
206
+
207
+ function instant(now: any): string {
208
+ const value = typeof now === 'function' ? now() : now;
209
+ if (!Number.isFinite(Number(value))) throw new TypeError('receipt program clock must return a finite value');
210
+ return new Date(Number(value)).toISOString();
211
+ }
212
+
213
+ function valueAtPath(value: any, path: string): any {
214
+ let current = value;
215
+ for (const segment of path.split('.')) {
216
+ if (!isRecord(current) || !Object.prototype.hasOwnProperty.call(current, segment)) return undefined;
217
+ current = current[segment];
218
+ }
219
+ return current;
220
+ }
221
+
222
+ function normalizeResolvedCaid(value: any): string | null {
223
+ if (typeof value === 'string') return value;
224
+ if (isRecord(value) && value.ok === true && typeof value.caid === 'string') return value.caid;
225
+ return null;
226
+ }
227
+
228
+ function opcodeSteps(opcodes: readonly string[]): any {
229
+ return opcodes.map((opcode, sequence) => Object.freeze({ sequence, opcode }));
230
+ }
231
+
232
+ function operationFromExecution(record: any): any {
233
+ return record?.detail?.capability?.operation_id ?? null;
234
+ }
235
+
236
+ function evidenceReference(record: any): any {
237
+ if (!record) return null;
238
+ return deepFreeze({
239
+ seq: Number.isSafeInteger(record.seq) ? record.seq : null,
240
+ record_id: typeof record.record_id === 'string' ? record.record_id : null,
241
+ hash: typeof record.hash === 'string' ? record.hash : null,
242
+ prev_hash: typeof record.prev_hash === 'string' ? record.prev_hash : null,
243
+ kind: typeof record.kind === 'string' ? record.kind : null,
244
+ allow: typeof record.allow === 'boolean' ? record.allow : null,
245
+ outcome: typeof record.outcome === 'string' ? record.outcome : null,
246
+ authorizes_decision: typeof record.authorizes_decision === 'string'
247
+ ? record.authorizes_decision : null,
248
+ observed_action_hash: typeof record.observed_action_hash === 'string'
249
+ ? record.observed_action_hash : null,
250
+ operation_id: operationFromExecution(record),
251
+ });
252
+ }
253
+
254
+ function certificateCore({
255
+ context,
256
+ program,
257
+ programDigest,
258
+ outcome,
259
+ reason,
260
+ result,
261
+ authorizationRef,
262
+ executionRef,
263
+ steps,
264
+ startedAt,
265
+ completedAt,
266
+ }: any): any {
267
+ return {
268
+ '@version': RECEIPT_PROGRAM_CERTIFICATE_VERSION,
269
+ context,
270
+ program,
271
+ program_digest: programDigest,
272
+ outcome,
273
+ reason,
274
+ result,
275
+ result_digest: result === null ? null : canonicalDigest(result),
276
+ authorization_ref: authorizationRef,
277
+ execution_ref: executionRef,
278
+ steps,
279
+ started_at: startedAt,
280
+ completed_at: completedAt,
281
+ };
282
+ }
283
+
284
+ async function signCertificate(core: any, signer: any): Promise<any> {
285
+ if (Buffer.byteLength(canonicalize(core), 'utf8') > MAX_CERTIFICATE_CORE_BYTES) {
286
+ throw new Error('receipt program certificate exceeds byte limit');
287
+ }
288
+ const stateRoot = canonicalDigest(core);
289
+ const signed = { ...core, state_root: stateRoot };
290
+ const signedBytes = Buffer.from(canonicalize(signed), 'utf8');
291
+ const signature = canonicalSignature(await signer.sign(signedBytes));
292
+ if (!verify(null, signedBytes, publicKeyObject(signer.publicKey), signature.bytes)) {
293
+ throw new Error('certificate signer returned a signature that does not verify');
294
+ }
295
+ return deepFreeze({
296
+ ...signed,
297
+ signature: {
298
+ algorithm: RECEIPT_PROGRAM_SIGNATURE_ALGORITHM,
299
+ public_key: signer.publicKey,
300
+ value: signature.encoded,
301
+ },
302
+ });
303
+ }
304
+
305
+ function makeResult(certificate: any): any {
306
+ return Object.freeze({
307
+ ok: certificate.outcome === 'executed',
308
+ outcome: certificate.outcome,
309
+ reason: certificate.reason,
310
+ result: certificate.result,
311
+ certificate,
312
+ });
313
+ }
314
+
315
+ function failReason(error: any, fallback: string): string {
316
+ return typeof error?.message === 'string' && error.message.startsWith('receipt_program:')
317
+ ? error.message.slice('receipt_program:'.length)
318
+ : fallback;
319
+ }
320
+
321
+ /**
322
+ * Build a receipt-program kernel over an already configured Gate.
323
+ * Trust configuration is constructor-pinned and cannot be supplied per run.
324
+ *
325
+ * options.gate: configured EMILIA Gate
326
+ * options.resolveCaid: synchronous pinned CAID resolver, (action) => string|object
327
+ * options.operationIdField: dot-path to the stable operation id in observed action
328
+ * options.certificatePrivateKey: test/demo-only Ed25519 operator key
329
+ * options.certificateSigner: external KMS/HSM signer
330
+ * options.certificateContext: pinned issuer, tenant, environment, audience, and key id
331
+ * options.projectResult: pinned disclosure projection, (result) => any|Promise<any>
332
+ * options.effectTimeoutMs: provider deadline in milliseconds (default 30000)
333
+ * options.allowEphemeralState: explicit test/demo opt-in (default false)
334
+ * options.now: number|(() => number), default Date.now
335
+ */
336
+ export function createReceiptProgramKernel({
337
+ gate,
338
+ resolveCaid,
339
+ operationIdField,
340
+ certificatePrivateKey,
341
+ certificateSigner,
342
+ certificateContext,
343
+ projectResult = null,
344
+ effectTimeoutMs = 30_000,
345
+ allowEphemeralState = false,
346
+ now = Date.now,
347
+ }: any = {}) {
348
+ if (!gate || typeof gate.run !== 'function' || !gate.evidence) {
349
+ throw new TypeError('createReceiptProgramKernel requires a configured Gate with an evidence log');
350
+ }
351
+ if (typeof resolveCaid !== 'function') {
352
+ throw new TypeError('createReceiptProgramKernel requires a pinned synchronous CAID resolver');
353
+ }
354
+ boundedId(operationIdField, 'operationIdField');
355
+ if (!allowEphemeralState && (gate.evidence.durable !== true || gate.evidence.strict !== true
356
+ || gate.evidence.forkAware !== true || gate.evidence.atomicAppend !== true
357
+ || gate.capabilityStore?.durable !== true)) {
358
+ throw new Error('receipt program production mode requires a durable atomic evidence log and durable capability store');
359
+ }
360
+ if (!Number.isSafeInteger(effectTimeoutMs) || effectTimeoutMs < 1 || effectTimeoutMs > 600_000) {
361
+ throw new TypeError('effectTimeoutMs must be an integer from 1 to 600000');
362
+ }
363
+ if (!allowEphemeralState && typeof projectResult !== 'function') {
364
+ throw new Error('receipt program production mode requires a pinned projectResult function');
365
+ }
366
+ if (projectResult !== null && typeof projectResult !== 'function') {
367
+ throw new TypeError('projectResult must be a function');
368
+ }
369
+ const signer = configureCertificateSigner({
370
+ certificatePrivateKey,
371
+ certificateSigner,
372
+ allowEphemeralState,
373
+ });
374
+ const context = cloneCanonical(certificateContext, 'certificateContext');
375
+ if (!hasExactKeys(context, ['issuer', 'tenant', 'environment', 'audience', 'key_id'])) {
376
+ throw new TypeError('certificateContext must contain exactly issuer, tenant, environment, audience, and key_id');
377
+ }
378
+ for (const field of ['issuer', 'tenant', 'environment', 'audience', 'key_id']) {
379
+ boundedId(context[field], `certificateContext.${field}`);
380
+ }
381
+ if (context.key_id !== signer.keyId) {
382
+ throw new Error('certificateContext.key_id must equal the configured signer keyId');
383
+ }
384
+
385
+ async function issueCertificate(input: any): Promise<any> {
386
+ const core = certificateCore({ ...input, context });
387
+ let certificate;
388
+ try {
389
+ certificate = await signCertificate(core, signer);
390
+ } catch {
391
+ return Object.freeze({
392
+ ok: false,
393
+ outcome: input.outcome,
394
+ reason: 'certificate_signing_failed',
395
+ result: input.result,
396
+ certificate: null,
397
+ certificate_evidence: null,
398
+ });
399
+ }
400
+ try {
401
+ const certificateEvidence = await gate.evidence.record({
402
+ kind: 'receipt_program_certificate',
403
+ program_digest: certificate.program_digest,
404
+ operation_id: certificate.program.operation_id,
405
+ outcome: certificate.outcome,
406
+ state_root: certificate.state_root,
407
+ certificate,
408
+ });
409
+ return Object.freeze({
410
+ ...makeResult(certificate),
411
+ certificate_evidence: deepFreeze(structuredClone(certificateEvidence)),
412
+ });
413
+ } catch {
414
+ return Object.freeze({
415
+ ok: false,
416
+ outcome: input.outcome,
417
+ reason: 'certificate_persistence_failed',
418
+ result: input.result,
419
+ certificate,
420
+ certificate_evidence: null,
421
+ });
422
+ }
423
+ }
424
+
425
+ async function refuseEarly({ program, programDigest, reason, startedAt, matched = false }: any): Promise<any> {
426
+ return issueCertificate({
427
+ program,
428
+ programDigest,
429
+ outcome: 'refused',
430
+ reason,
431
+ result: null,
432
+ authorizationRef: null,
433
+ executionRef: null,
434
+ steps: opcodeSteps(matched
435
+ ? ['RECEIPT', 'MATCH', 'REFUSE', 'CERTIFY']
436
+ : ['RECEIPT', 'REFUSE', 'CERTIFY']),
437
+ startedAt,
438
+ completedAt: instant(now),
439
+ });
440
+ }
441
+
442
+ return Object.freeze({
443
+ version: RECEIPT_PROGRAM_VERSION,
444
+ signer_public_key: signer.publicKey,
445
+ certificate_context: context,
446
+
447
+ /**
448
+ * Execute one consequential receipt instruction through Gate.
449
+ * The effect MUST return a bounded canonical-JSON evidence projection, not
450
+ * a raw provider object. A projection failure occurs after provider entry
451
+ * and is therefore committed as indeterminate.
452
+ */
453
+ async run(request: any = {}, effect: any): Promise<any> {
454
+ const startedAt = instant(now);
455
+ if (!isRecord(request) || typeof effect !== 'function') {
456
+ throw new TypeError('receipt program run requires an object request and effect function');
457
+ }
458
+ const capabilityDescriptor = Object.getOwnPropertyDescriptor(request, 'capability');
459
+ if (!isDataRecord(request) || !capabilityDescriptor
460
+ || !isDataRecord(capabilityDescriptor.value)) {
461
+ const placeholder = deepFreeze({
462
+ '@version': RECEIPT_PROGRAM_VERSION,
463
+ program_id: 'invalid',
464
+ instruction_id: 'invalid',
465
+ operation_id: 'invalid',
466
+ caid: 'invalid',
467
+ action_digest: null,
468
+ capability_receipt_digest: null,
469
+ selector: {},
470
+ observed_action: {},
471
+ });
472
+ return refuseEarly({
473
+ program: placeholder,
474
+ programDigest: canonicalDigest(placeholder),
475
+ reason: 'program_invalid',
476
+ startedAt,
477
+ });
478
+ }
479
+ for (const field of FORBIDDEN_RUNTIME_TRUST_FIELDS) {
480
+ if (Object.prototype.hasOwnProperty.call(request, field)) {
481
+ const placeholder = deepFreeze({
482
+ '@version': RECEIPT_PROGRAM_VERSION,
483
+ program_id: placeholderId(request.programId),
484
+ instruction_id: placeholderId(request.instructionId),
485
+ operation_id: placeholderId(request.capability?.operationId),
486
+ caid: placeholderId(request.caid),
487
+ action_digest: null,
488
+ capability_receipt_digest: null,
489
+ selector: {},
490
+ observed_action: {},
491
+ });
492
+ return refuseEarly({
493
+ program: placeholder,
494
+ programDigest: canonicalDigest(placeholder),
495
+ reason: 'runtime_trust_configuration_refused',
496
+ startedAt,
497
+ });
498
+ }
499
+ }
500
+
501
+ let program: any;
502
+ let programDigest: any;
503
+ let operationId: any;
504
+ let actionDigest: any;
505
+ let executionCapability: any;
506
+ let executionReceipt: any = null;
507
+ try {
508
+ const programId = boundedId(request.programId, 'programId');
509
+ const instructionId = boundedId(request.instructionId, 'instructionId');
510
+ operationId = boundedId(request.capability?.operationId, 'capability.operationId');
511
+ if (!CAID_RE.test(request.caid)) throw new Error('receipt_program:caid_invalid');
512
+ const observedAction = cloneCanonical(request.observedAction, 'observedAction');
513
+ const selector = cloneCanonical(request.selector ?? {}, 'selector');
514
+ const capabilityReceipt = cloneCanonical(request.capability?.capabilityReceipt, 'capabilityReceipt');
515
+ const capabilityProjection = cloneCanonical(request.capability?.action, 'capability.action');
516
+ const secret = Buffer.isBuffer(request.capability?.secret)
517
+ ? Buffer.from(request.capability.secret)
518
+ : request.capability?.secret;
519
+ const shares = request.capability?.shares === undefined
520
+ ? undefined : cloneCanonical(request.capability.shares, 'capability.shares');
521
+ executionCapability = Object.freeze({
522
+ capabilityReceipt: structuredClone(capabilityReceipt),
523
+ ...(shares === undefined ? { secret } : { shares: structuredClone(shares) }),
524
+ action: structuredClone(capabilityProjection),
525
+ operationId,
526
+ });
527
+ executionReceipt = request.receipt === undefined || request.receipt === null
528
+ ? null : cloneCanonical(request.receipt, 'receipt');
529
+ const actionOperationId = valueAtPath(observedAction, operationIdField);
530
+ actionDigest = canonicalDigest(observedAction);
531
+ program = deepFreeze({
532
+ '@version': RECEIPT_PROGRAM_VERSION,
533
+ program_id: programId,
534
+ instruction_id: instructionId,
535
+ operation_id: operationId,
536
+ operation_id_field: operationIdField,
537
+ caid: request.caid,
538
+ action_digest: actionDigest,
539
+ capability_receipt_digest: canonicalDigest(capabilityReceipt),
540
+ capability_projection: capabilityProjection,
541
+ selector,
542
+ observed_action: observedAction,
543
+ });
544
+ if (Buffer.byteLength(canonicalize(program), 'utf8') > MAX_PROGRAM_BYTES) {
545
+ throw new Error('receipt_program:program_too_large');
546
+ }
547
+ programDigest = canonicalDigest(program);
548
+ if (actionOperationId !== operationId) {
549
+ return refuseEarly({ program, programDigest, reason: 'program_operation_binding_failed', startedAt });
550
+ }
551
+ const resolved = resolveCaid(structuredClone(observedAction));
552
+ if (resolved && typeof resolved.then === 'function') {
553
+ return refuseEarly({ program, programDigest, reason: 'caid_resolver_async_refused', startedAt });
554
+ }
555
+ if (normalizeResolvedCaid(resolved) !== request.caid) {
556
+ return refuseEarly({ program, programDigest, reason: 'caid_mismatch', startedAt });
557
+ }
558
+ if (request.expectedProgramDigest !== undefined && request.expectedProgramDigest !== programDigest) {
559
+ return refuseEarly({
560
+ program,
561
+ programDigest,
562
+ reason: 'program_digest_mismatch',
563
+ startedAt,
564
+ matched: true,
565
+ });
566
+ }
567
+ } catch (error) {
568
+ const placeholder = deepFreeze({
569
+ '@version': RECEIPT_PROGRAM_VERSION,
570
+ program_id: placeholderId(request.programId),
571
+ instruction_id: placeholderId(request.instructionId),
572
+ operation_id: placeholderId(request.capability?.operationId),
573
+ caid: placeholderId(request.caid),
574
+ action_digest: null,
575
+ capability_receipt_digest: null,
576
+ selector: {},
577
+ observed_action: {},
578
+ });
579
+ return refuseEarly({
580
+ program: placeholder,
581
+ programDigest: canonicalDigest(placeholder),
582
+ reason: failReason(error, 'program_invalid'),
583
+ startedAt,
584
+ });
585
+ }
586
+
587
+ let projectedResult: any = null;
588
+ let effectEntered = false;
589
+ let gateResult: any = null;
590
+ let caught: any = null;
591
+ try {
592
+ gateResult = await gate.run({
593
+ selector: program.selector,
594
+ receipt: executionReceipt,
595
+ observedAction: program.observed_action,
596
+ capability: executionCapability,
597
+ }, async (authorization: any, operation: any) => {
598
+ effectEntered = true;
599
+ // Gate owns these values. Give provider code frozen copies so it can
600
+ // neither rewrite the decision later used for evidence nor mutate
601
+ // the operation context Gate retains.
602
+ const authorizationSnapshot = deepFreeze(structuredClone(authorization));
603
+ const operationSnapshot = deepFreeze(structuredClone(operation));
604
+ const abortController = new AbortController();
605
+ const providerOperation = Object.freeze({
606
+ ...operationSnapshot,
607
+ signal: abortController.signal,
608
+ });
609
+ let rawResult;
610
+ let timeoutId: any;
611
+ try {
612
+ const provider = Promise.resolve().then(
613
+ () => effect(authorizationSnapshot, providerOperation),
614
+ );
615
+ const deadline = new Promise((_, reject) => {
616
+ timeoutId = setTimeout(() => {
617
+ abortController.abort('receipt_program_effect_timeout');
618
+ reject(new Error('receipt_program_effect_timeout'));
619
+ }, effectTimeoutMs);
620
+ });
621
+ rawResult = await Promise.race([provider, deadline]);
622
+ } catch (cause) {
623
+ const error: any = new Error('receipt_program_provider_failed');
624
+ error.cause = cause;
625
+ throw error;
626
+ } finally {
627
+ if (timeoutId !== undefined) clearTimeout(timeoutId);
628
+ }
629
+ const projection = projectResult === null
630
+ ? rawResult : await projectResult(rawResult);
631
+ projectedResult = cloneCanonical(
632
+ projection,
633
+ 'receipt program effect result',
634
+ true,
635
+ MAX_RESULT_BYTES,
636
+ );
637
+ if (!isRecord(projectedResult)) {
638
+ throw new TypeError('receipt program effect result must be a canonical JSON object');
639
+ }
640
+ return structuredClone(projectedResult);
641
+ });
642
+ } catch (error) {
643
+ caught = error;
644
+ }
645
+
646
+ const caughtOutcome = caught?.emiliaGateOutcome ?? null;
647
+ let executionRecord: any = gateResult?.execution
648
+ ?? (gateResult?.evidence?.kind === 'execution' ? gateResult.evidence : null)
649
+ ?? caughtOutcome?.execution
650
+ ?? null;
651
+ let authorizationRecord: any = gateResult?.authorization?.evidence
652
+ ?? caughtOutcome?.authorizationEvidence
653
+ ?? null;
654
+ const executionRef = evidenceReference(executionRecord);
655
+ const authorizationRef = evidenceReference(authorizationRecord);
656
+
657
+ if (caughtOutcome?.reason === 'execution_evidence_unavailable' && !executionRecord) {
658
+ return Object.freeze({
659
+ ok: false,
660
+ outcome: caughtOutcome.outcome ?? (effectEntered ? 'indeterminate' : 'refused'),
661
+ reason: 'execution_evidence_unavailable',
662
+ result: caughtOutcome.result ?? projectedResult,
663
+ certificate: null,
664
+ certificate_evidence: null,
665
+ });
666
+ }
667
+
668
+ if (gateResult?.ok === true && caught === null) {
669
+ return issueCertificate({
670
+ program,
671
+ programDigest,
672
+ outcome: 'executed',
673
+ reason: null,
674
+ result: projectedResult,
675
+ authorizationRef,
676
+ executionRef,
677
+ steps: opcodeSteps(['RECEIPT', 'MATCH', 'RESERVE', 'EXECUTE', 'COMMIT', 'CERTIFY']),
678
+ startedAt,
679
+ completedAt: instant(now),
680
+ });
681
+ }
682
+
683
+ if (caughtOutcome?.outcome === 'executed') {
684
+ if (!executionRecord) {
685
+ return Object.freeze({
686
+ ok: false,
687
+ outcome: 'executed',
688
+ reason: 'execution_evidence_unavailable',
689
+ result: caughtOutcome.result ?? projectedResult,
690
+ certificate: null,
691
+ certificate_evidence: null,
692
+ });
693
+ }
694
+ return issueCertificate({
695
+ program,
696
+ programDigest,
697
+ outcome: 'executed',
698
+ reason: null,
699
+ result: caughtOutcome.result ?? projectedResult,
700
+ authorizationRef,
701
+ executionRef,
702
+ steps: opcodeSteps(['RECEIPT', 'MATCH', 'RESERVE', 'EXECUTE', 'COMMIT', 'CERTIFY']),
703
+ startedAt,
704
+ completedAt: instant(now),
705
+ });
706
+ }
707
+
708
+ const evidenceIndeterminate = executionRecord?.outcome === 'indeterminate';
709
+ if (effectEntered || evidenceIndeterminate) {
710
+ return issueCertificate({
711
+ program,
712
+ programDigest,
713
+ outcome: 'indeterminate',
714
+ reason: executionRecord ? 'effect_indeterminate' : 'execution_evidence_unavailable',
715
+ result: null,
716
+ authorizationRef,
717
+ executionRef,
718
+ steps: opcodeSteps(['RECEIPT', 'MATCH', 'RESERVE', 'EXECUTE', 'COMMIT_INDETERMINATE', 'HALT', 'CERTIFY']),
719
+ startedAt,
720
+ completedAt: instant(now),
721
+ });
722
+ }
723
+
724
+ const reason = gateResult?.capability?.reason
725
+ ?? gateResult?.refusal?.reason
726
+ ?? gateResult?.authorization?.reason
727
+ ?? failReason(caught, 'gate_refused');
728
+ return issueCertificate({
729
+ program,
730
+ programDigest,
731
+ outcome: 'refused',
732
+ reason,
733
+ result: null,
734
+ authorizationRef,
735
+ executionRef: null,
736
+ steps: opcodeSteps(['RECEIPT', 'MATCH', 'REFUSE', 'CERTIFY']),
737
+ startedAt,
738
+ completedAt: instant(now),
739
+ });
740
+ },
741
+
742
+ /**
743
+ * Explicit crash-recovery path. It scans the durable evidence history only
744
+ * when requested and returns every independently verified certificate for
745
+ * one program digest; it never guesses which attempt a caller intended.
746
+ */
747
+ async recoverCertificates(programDigest: any): Promise<any> {
748
+ if (!SHA256_RE.test(programDigest)) {
749
+ return Object.freeze({ ok: false, reason: 'program_digest_invalid', certificates: [] });
750
+ }
751
+ let records;
752
+ try { records = await gate.evidence.all(); } catch {
753
+ return Object.freeze({ ok: false, reason: 'certificate_recovery_unavailable', certificates: [] });
754
+ }
755
+ if (!Array.isArray(records)) {
756
+ return Object.freeze({ ok: false, reason: 'certificate_recovery_malformed', certificates: [] });
757
+ }
758
+ const recovered: any[] = [];
759
+ for (const record of records) {
760
+ if (record?.kind !== 'receipt_program_certificate'
761
+ || record.program_digest !== programDigest) continue;
762
+ const checked = verifyReceiptProgramCertificate(record.certificate, {
763
+ trustedCertificateKeys: { [context.key_id]: signer.publicKey },
764
+ resolveCaid,
765
+ expectedContext: context,
766
+ certificateEvidence: record,
767
+ verifyCertificateInclusion: (candidate: any) => (
768
+ canonicalEvidenceJson(candidate) === canonicalEvidenceJson(record)
769
+ ),
770
+ requireAtomicCertificateEvidence: !allowEphemeralState,
771
+ });
772
+ if (!checked.ok) {
773
+ return Object.freeze({ ok: false, reason: 'certificate_recovery_invalid', certificates: [] });
774
+ }
775
+ recovered.push(deepFreeze({
776
+ certificate: structuredClone(record.certificate),
777
+ certificate_evidence: structuredClone(record),
778
+ verification: checked,
779
+ }));
780
+ }
781
+ return Object.freeze({
782
+ ok: true,
783
+ reason: null,
784
+ certificates: Object.freeze(recovered),
785
+ });
786
+ },
787
+ });
788
+ }
789
+
790
+ function verificationFailure(reason: string): any {
791
+ return Object.freeze({ ok: false, reason });
792
+ }
793
+
794
+ /**
795
+ * Verify the certificate's operator signature, content addresses, program
796
+ * binding, and Gate evidence linkage. This proves exact certificate integrity
797
+ * under a pinned operator key; it does not prove an external provider told the
798
+ * truth or replace verification of the referenced receipt/capability artifacts.
799
+ *
800
+ * options.trustedCertificateKeys?: Record<string, string>
801
+ * options.resolveCaid?: ((action: any) => any)|null
802
+ * options.expectedContext?: object|null
803
+ * options.certificateEvidence?: any
804
+ * options.verifyCertificateInclusion?: (((record: any, expectation: any) => any)|null)
805
+ * options.requireAtomicCertificateEvidence?: boolean
806
+ */
807
+ export function verifyReceiptProgramCertificate(certificate: any, {
808
+ trustedCertificateKeys = {},
809
+ resolveCaid = null,
810
+ expectedContext = null,
811
+ certificateEvidence = null,
812
+ verifyCertificateInclusion = null,
813
+ requireAtomicCertificateEvidence = false,
814
+ }: any = {}): any {
815
+ try {
816
+ const versionDescriptor = isDataRecord(certificate)
817
+ ? Object.getOwnPropertyDescriptor(certificate, '@version') : null;
818
+ if (versionDescriptor?.value !== RECEIPT_PROGRAM_CERTIFICATE_VERSION) {
819
+ return verificationFailure('certificate_version_invalid');
820
+ }
821
+ const snapshot = cloneCanonical(certificate, 'certificate', false);
822
+ const completeCertificate = structuredClone(snapshot);
823
+ if (!hasExactKeys(snapshot, [
824
+ '@version',
825
+ 'context',
826
+ 'program',
827
+ 'program_digest',
828
+ 'outcome',
829
+ 'reason',
830
+ 'result',
831
+ 'result_digest',
832
+ 'authorization_ref',
833
+ 'execution_ref',
834
+ 'steps',
835
+ 'started_at',
836
+ 'completed_at',
837
+ 'state_root',
838
+ 'signature',
839
+ ])) return verificationFailure('certificate_schema_invalid');
840
+ const signature = snapshot.signature;
841
+ if (!hasExactKeys(signature, ['algorithm', 'public_key', 'value'])
842
+ || signature.algorithm !== RECEIPT_PROGRAM_SIGNATURE_ALGORITHM
843
+ || typeof signature.public_key !== 'string' || typeof signature.value !== 'string') {
844
+ return verificationFailure('certificate_signature_invalid');
845
+ }
846
+ const claimedKeyId = snapshot.context?.key_id;
847
+ if (!isDataRecord(trustedCertificateKeys)
848
+ || typeof claimedKeyId !== 'string'
849
+ || trustedCertificateKeys[claimedKeyId] !== signature.public_key) {
850
+ return verificationFailure('certificate_signer_not_trusted');
851
+ }
852
+ delete snapshot.signature;
853
+ const signatureBytes = Buffer.from(signature.value, 'base64url');
854
+ if (signatureBytes.length !== 64 || signatureBytes.toString('base64url') !== signature.value
855
+ || !verify(
856
+ null,
857
+ Buffer.from(canonicalize(snapshot), 'utf8'),
858
+ publicKeyObject(signature.public_key),
859
+ signatureBytes,
860
+ )) return verificationFailure('certificate_signature_invalid');
861
+
862
+ const stateRoot = snapshot.state_root;
863
+ delete snapshot.state_root;
864
+ if (!SHA256_RE.test(stateRoot) || canonicalDigest(snapshot) !== stateRoot) {
865
+ return verificationFailure('certificate_state_root_mismatch');
866
+ }
867
+ const signed = { ...snapshot, state_root: stateRoot };
868
+ const certificateCoreSnapshot: any = { ...signed };
869
+ delete certificateCoreSnapshot.state_root;
870
+ if (Buffer.byteLength(canonicalize(certificateCoreSnapshot), 'utf8')
871
+ > MAX_CERTIFICATE_CORE_BYTES) {
872
+ return verificationFailure('certificate_size_invalid');
873
+ }
874
+ if (!hasExactKeys(signed.context, ['issuer', 'tenant', 'environment', 'audience', 'key_id'])
875
+ || Object.values(signed.context).some((value) => typeof value !== 'string'
876
+ || value.length === 0 || Buffer.byteLength(value, 'utf8') > MAX_ID_BYTES)) {
877
+ return verificationFailure('certificate_context_invalid');
878
+ }
879
+ if (!isRecord(expectedContext)) {
880
+ return verificationFailure('certificate_context_required');
881
+ }
882
+ let expectedContextSnapshot;
883
+ try { expectedContextSnapshot = cloneCanonical(expectedContext, 'expectedContext'); } catch {
884
+ return verificationFailure('certificate_context_invalid');
885
+ }
886
+ if (canonicalize(expectedContextSnapshot) !== canonicalize(signed.context)) {
887
+ return verificationFailure('certificate_context_mismatch');
888
+ }
889
+ if (!validInstant(signed.started_at) || !validInstant(signed.completed_at)
890
+ || Date.parse(signed.completed_at) < Date.parse(signed.started_at)) {
891
+ return verificationFailure('certificate_time_invalid');
892
+ }
893
+ const program = signed.program;
894
+ if (!isRecord(program) || program['@version'] !== RECEIPT_PROGRAM_VERSION) {
895
+ return verificationFailure('certificate_program_invalid');
896
+ }
897
+ if (Buffer.byteLength(canonicalize(program), 'utf8') > MAX_PROGRAM_BYTES) {
898
+ return verificationFailure('certificate_program_invalid');
899
+ }
900
+ if (!SHA256_RE.test(signed.program_digest) || canonicalDigest(program) !== signed.program_digest) {
901
+ return verificationFailure('certificate_program_digest_mismatch');
902
+ }
903
+ if (!['executed', 'indeterminate', 'refused'].includes(signed.outcome)) {
904
+ return verificationFailure('certificate_outcome_invalid');
905
+ }
906
+ if ((signed.outcome === 'executed' && signed.reason !== null)
907
+ || (signed.outcome !== 'executed' && typeof signed.reason !== 'string')) {
908
+ return verificationFailure('certificate_reason_invalid');
909
+ }
910
+ const completeProgram = hasExactKeys(program, [
911
+ '@version',
912
+ 'program_id',
913
+ 'instruction_id',
914
+ 'operation_id',
915
+ 'operation_id_field',
916
+ 'caid',
917
+ 'action_digest',
918
+ 'capability_receipt_digest',
919
+ 'capability_projection',
920
+ 'selector',
921
+ 'observed_action',
922
+ ])
923
+ && [program.program_id, program.instruction_id, program.operation_id, program.operation_id_field]
924
+ .every((value) => typeof value === 'string' && value.length > 0
925
+ && Buffer.byteLength(value, 'utf8') <= MAX_ID_BYTES)
926
+ && isRecord(program.capability_projection)
927
+ && isRecord(program.selector)
928
+ && isRecord(program.observed_action);
929
+ const executableProgram = completeProgram
930
+ && CAID_RE.test(program.caid)
931
+ && SHA256_RE.test(program.action_digest)
932
+ && SHA256_RE.test(program.capability_receipt_digest)
933
+ && valueAtPath(program.observed_action, program.operation_id_field) === program.operation_id
934
+ && canonicalDigest(program.observed_action) === program.action_digest;
935
+ if (signed.outcome !== 'refused' && !executableProgram) {
936
+ return verificationFailure('certificate_program_invalid');
937
+ }
938
+ if (executableProgram) {
939
+ if (typeof resolveCaid !== 'function') {
940
+ return verificationFailure('certificate_caid_resolver_required');
941
+ }
942
+ const resolved = resolveCaid(structuredClone(program.observed_action));
943
+ if (resolved && typeof resolved.then === 'function') {
944
+ return verificationFailure('certificate_caid_resolver_async_refused');
945
+ }
946
+ const resolvedCaid = normalizeResolvedCaid(resolved);
947
+ if (signed.outcome === 'refused' && signed.reason === 'caid_mismatch') {
948
+ if (resolvedCaid === program.caid) return verificationFailure('certificate_refusal_evidence_mismatch');
949
+ } else if (resolvedCaid !== program.caid) {
950
+ return verificationFailure('certificate_caid_mismatch');
951
+ }
952
+ }
953
+ if (!Array.isArray(signed.steps) || signed.steps.length === 0
954
+ || signed.steps.some((step: any, index: number) => !hasExactKeys(step, ['sequence', 'opcode'])
955
+ || step.sequence !== index || typeof step.opcode !== 'string')) {
956
+ return verificationFailure('certificate_steps_invalid');
957
+ }
958
+ const expectedOpcodes = signed.outcome === 'executed'
959
+ ? ['RECEIPT', 'MATCH', 'RESERVE', 'EXECUTE', 'COMMIT', 'CERTIFY']
960
+ : signed.outcome === 'indeterminate'
961
+ ? ['RECEIPT', 'MATCH', 'RESERVE', 'EXECUTE', 'COMMIT_INDETERMINATE', 'HALT', 'CERTIFY']
962
+ : executableProgram && !['caid_mismatch', 'caid_resolver_async_refused'].includes(signed.reason)
963
+ ? ['RECEIPT', 'MATCH', 'REFUSE', 'CERTIFY']
964
+ : ['RECEIPT', 'REFUSE', 'CERTIFY'];
965
+ if (canonicalize(signed.steps.map((step: any) => step.opcode)) !== canonicalize(expectedOpcodes)) {
966
+ return verificationFailure('certificate_steps_outcome_mismatch');
967
+ }
968
+ if (signed.result === null) {
969
+ if (signed.result_digest !== null) return verificationFailure('certificate_result_digest_mismatch');
970
+ } else {
971
+ if (!isRecord(signed.result)
972
+ || Buffer.byteLength(canonicalize(signed.result), 'utf8') > MAX_RESULT_BYTES) {
973
+ return verificationFailure('certificate_result_invalid');
974
+ }
975
+ if (!SHA256_RE.test(signed.result_digest)
976
+ || canonicalDigest(signed.result) !== signed.result_digest) {
977
+ return verificationFailure('certificate_result_digest_mismatch');
978
+ }
979
+ }
980
+ const authorization = signed.authorization_ref;
981
+ const execution = signed.execution_ref;
982
+ const validReference = (reference: any, kind: string): boolean => reference === null || (hasExactKeys(reference, [
983
+ 'seq', 'record_id', 'hash', 'prev_hash', 'kind', 'allow', 'outcome',
984
+ 'authorizes_decision', 'observed_action_hash', 'operation_id',
985
+ ])
986
+ && Number.isSafeInteger(reference.seq) && reference.seq >= 0
987
+ && (reference.record_id === null || (typeof reference.record_id === 'string'
988
+ && reference.record_id.length > 0
989
+ && Buffer.byteLength(reference.record_id, 'utf8') <= MAX_ID_BYTES))
990
+ && /^[0-9a-f]{64}$/.test(reference.hash)
991
+ && (reference.prev_hash === 'genesis' || /^[0-9a-f]{64}$/.test(reference.prev_hash))
992
+ && reference.kind === kind
993
+ && (reference.allow === null || typeof reference.allow === 'boolean')
994
+ && (reference.outcome === null || (typeof reference.outcome === 'string'
995
+ && reference.outcome.length > 0
996
+ && Buffer.byteLength(reference.outcome, 'utf8') <= MAX_ID_BYTES))
997
+ && (reference.authorizes_decision === null || /^[0-9a-f]{64}$/.test(reference.authorizes_decision))
998
+ && (reference.observed_action_hash === null || /^[0-9a-f]{64}$/.test(reference.observed_action_hash))
999
+ && (reference.operation_id === null || (typeof reference.operation_id === 'string'
1000
+ && reference.operation_id.length > 0
1001
+ && Buffer.byteLength(reference.operation_id, 'utf8') <= MAX_ID_BYTES)));
1002
+ if (!validReference(authorization, 'decision')) return verificationFailure('certificate_authorization_ref_invalid');
1003
+ if (!validReference(execution, 'execution')) return verificationFailure('certificate_execution_ref_invalid');
1004
+ if (authorization && (typeof authorization.allow !== 'boolean'
1005
+ || (authorization.allow === true && typeof authorization.outcome !== 'string'))) {
1006
+ return verificationFailure('certificate_authorization_ref_invalid');
1007
+ }
1008
+ if (execution && (execution.allow !== null
1009
+ || !['executed', 'indeterminate'].includes(execution.outcome))) {
1010
+ return verificationFailure('certificate_execution_ref_invalid');
1011
+ }
1012
+ if (authorization && execution && execution.seq <= authorization.seq) {
1013
+ return verificationFailure('certificate_evidence_order_invalid');
1014
+ }
1015
+ if (authorization && execution && execution.authorizes_decision !== authorization.hash) {
1016
+ return verificationFailure('certificate_evidence_link_mismatch');
1017
+ }
1018
+ const bareActionDigest = executableProgram ? program.action_digest.slice('sha256:'.length) : null;
1019
+ if (authorization && authorization.observed_action_hash !== bareActionDigest) {
1020
+ return verificationFailure('certificate_authorization_binding_mismatch');
1021
+ }
1022
+ if (signed.outcome !== 'refused' && authorization?.allow !== true) {
1023
+ return verificationFailure('certificate_authorization_binding_mismatch');
1024
+ }
1025
+ if (execution && (execution.observed_action_hash !== bareActionDigest
1026
+ || execution.operation_id !== program.operation_id)) {
1027
+ return verificationFailure('certificate_execution_binding_mismatch');
1028
+ }
1029
+ if (signed.outcome === 'executed') {
1030
+ if (signed.result === null || !authorization || !execution || execution.outcome !== 'executed') {
1031
+ return verificationFailure('certificate_executed_evidence_incomplete');
1032
+ }
1033
+ } else if (signed.result !== null) {
1034
+ return verificationFailure('certificate_nonexecuted_result_present');
1035
+ }
1036
+ if (signed.outcome === 'indeterminate') {
1037
+ if (execution === null && signed.reason !== 'execution_evidence_unavailable') {
1038
+ return verificationFailure('certificate_indeterminate_evidence_incomplete');
1039
+ }
1040
+ if (execution !== null && execution.outcome !== 'indeterminate') {
1041
+ return verificationFailure('certificate_indeterminate_evidence_mismatch');
1042
+ }
1043
+ }
1044
+ let certificatePersisted = false;
1045
+ if (certificateEvidence !== null) {
1046
+ const expectedEntry = {
1047
+ kind: 'receipt_program_certificate',
1048
+ program_digest: signed.program_digest,
1049
+ operation_id: program.operation_id,
1050
+ outcome: signed.outcome,
1051
+ state_root: stateRoot,
1052
+ certificate: completeCertificate,
1053
+ };
1054
+ if (!verifyEvidenceRecord(certificateEvidence, {
1055
+ atomicRequired: requireAtomicCertificateEvidence,
1056
+ expectedEntry,
1057
+ })) return verificationFailure('certificate_evidence_invalid');
1058
+ if (typeof verifyCertificateInclusion !== 'function') {
1059
+ return verificationFailure('certificate_evidence_inclusion_verifier_required');
1060
+ }
1061
+ let included;
1062
+ try {
1063
+ included = verifyCertificateInclusion(structuredClone(certificateEvidence), deepFreeze({
1064
+ expected_entry: structuredClone(expectedEntry),
1065
+ expected_context: structuredClone(expectedContextSnapshot),
1066
+ program_digest: signed.program_digest,
1067
+ state_root: stateRoot,
1068
+ atomic_required: requireAtomicCertificateEvidence,
1069
+ }));
1070
+ } catch {
1071
+ return verificationFailure('certificate_evidence_not_included');
1072
+ }
1073
+ if (included && typeof included.then === 'function') {
1074
+ return verificationFailure('certificate_evidence_inclusion_async_refused');
1075
+ }
1076
+ if (included !== true) return verificationFailure('certificate_evidence_not_included');
1077
+ certificatePersisted = true;
1078
+ } else if (requireAtomicCertificateEvidence) {
1079
+ return verificationFailure('certificate_evidence_required');
1080
+ }
1081
+ return Object.freeze({
1082
+ ok: true,
1083
+ certificate_valid: true,
1084
+ execution_succeeded: signed.outcome === 'executed',
1085
+ reason: null,
1086
+ outcome: signed.outcome,
1087
+ program_digest: signed.program_digest,
1088
+ state_root: stateRoot,
1089
+ signer: signature.public_key,
1090
+ evidence_complete: Boolean(authorization && (signed.outcome === 'refused' || execution)),
1091
+ certificate_persisted: certificatePersisted,
1092
+ caid_reperformed: executableProgram,
1093
+ });
1094
+ } catch {
1095
+ return verificationFailure('certificate_malformed');
1096
+ }
1097
+ }
1098
+
1099
+ export default {
1100
+ RECEIPT_PROGRAM_VERSION,
1101
+ RECEIPT_PROGRAM_CERTIFICATE_VERSION,
1102
+ RECEIPT_PROGRAM_SIGNATURE_ALGORITHM,
1103
+ createReceiptProgramKernel,
1104
+ verifyReceiptProgramCertificate,
1105
+ };