@emilia-protocol/gate 0.10.0 → 0.13.0

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