@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,366 @@
1
+ // SPDX-License-Identifier: Apache-2.0
2
+ /**
3
+ * Durable Postgres backend for the Action Escrow state-machine store contract.
4
+ *
5
+ * Each transition compares the exact prior serialized snapshot and installs
6
+ * the exact replacement in one SQL statement. Backend errors propagate; the
7
+ * state machine must refuse rather than treating an outage as absent state.
8
+ */
9
+ import crypto from 'node:crypto';
10
+ import { strictJsonGate } from './strict-json.js';
11
+
12
+ export const ACTION_ESCROW_PG_STORE_VERSION = 'EP-ACTION-ESCROW-PG-STORE-v1';
13
+ export const ACTION_ESCROW_STATE_TABLE = 'ep_action_escrow_state';
14
+ export const ACTION_ESCROW_EVENT_TABLE = 'ep_action_escrow_state_events';
15
+ export const ACTION_ESCROW_MAX_STATE_BYTES = 4 * 1024 * 1024;
16
+
17
+ export const ACTION_ESCROW_STATE_DDL = `CREATE TABLE IF NOT EXISTS ${ACTION_ESCROW_STATE_TABLE} (
18
+ agreement_key TEXT PRIMARY KEY,
19
+ revision BIGINT NOT NULL CHECK (revision >= 0),
20
+ record_json TEXT NOT NULL,
21
+ updated_at BIGINT NOT NULL,
22
+ CHECK (octet_length(record_json) <= ${ACTION_ESCROW_MAX_STATE_BYTES})
23
+ );
24
+ CREATE TABLE IF NOT EXISTS ${ACTION_ESCROW_EVENT_TABLE} (
25
+ agreement_key TEXT NOT NULL,
26
+ revision BIGINT NOT NULL CHECK (revision >= 0),
27
+ previous_revision BIGINT NULL,
28
+ record_json TEXT NOT NULL,
29
+ record_digest TEXT NOT NULL CHECK (record_digest ~ '^sha256:[0-9a-f]{64}$'),
30
+ recorded_at BIGINT NOT NULL,
31
+ PRIMARY KEY (agreement_key, revision),
32
+ CHECK (
33
+ (revision = 0 AND previous_revision IS NULL)
34
+ OR previous_revision = revision - 1
35
+ ),
36
+ CHECK (octet_length(record_json) <= ${ACTION_ESCROW_MAX_STATE_BYTES})
37
+ );
38
+ REVOKE ALL ON ${ACTION_ESCROW_STATE_TABLE} FROM PUBLIC;
39
+ REVOKE ALL ON ${ACTION_ESCROW_EVENT_TABLE} FROM PUBLIC;
40
+ REVOKE UPDATE, DELETE, TRUNCATE ON ${ACTION_ESCROW_EVENT_TABLE} FROM PUBLIC;`;
41
+
42
+ /**
43
+ * @param {string} roleName
44
+ */
45
+ export function actionEscrowRuntimeGrantDdl(roleName) {
46
+ if (typeof roleName !== 'string' || !/^[A-Za-z_][A-Za-z0-9_]{0,62}$/.test(roleName)) {
47
+ throw new TypeError('action-escrow runtime role name is invalid');
48
+ }
49
+ const role = `"${roleName}"`;
50
+ return `REVOKE ALL ON ${ACTION_ESCROW_STATE_TABLE} FROM ${role};
51
+ REVOKE ALL ON ${ACTION_ESCROW_EVENT_TABLE} FROM ${role};
52
+ GRANT SELECT, INSERT, UPDATE ON ${ACTION_ESCROW_STATE_TABLE} TO ${role};
53
+ GRANT SELECT, INSERT ON ${ACTION_ESCROW_EVENT_TABLE} TO ${role};
54
+ REVOKE DELETE, TRUNCATE ON ${ACTION_ESCROW_STATE_TABLE} FROM ${role};
55
+ REVOKE UPDATE, DELETE, TRUNCATE ON ${ACTION_ESCROW_EVENT_TABLE} FROM ${role};`;
56
+ }
57
+
58
+ export const ACTION_ESCROW_STATE_SQL = Object.freeze({
59
+ health: `SELECT
60
+ to_regclass('public.${ACTION_ESCROW_STATE_TABLE}') IS NOT NULL AS table_ready,
61
+ to_regclass('public.${ACTION_ESCROW_EVENT_TABLE}') IS NOT NULL AS event_table_ready,
62
+ CASE WHEN to_regclass('public.${ACTION_ESCROW_STATE_TABLE}') IS NULL THEN FALSE
63
+ ELSE has_table_privilege(current_user, to_regclass('public.${ACTION_ESCROW_STATE_TABLE}'), 'SELECT')
64
+ AND has_table_privilege(current_user, to_regclass('public.${ACTION_ESCROW_STATE_TABLE}'), 'INSERT')
65
+ AND has_table_privilege(current_user, to_regclass('public.${ACTION_ESCROW_STATE_TABLE}'), 'UPDATE') END AS can_use,
66
+ CASE WHEN to_regclass('public.${ACTION_ESCROW_EVENT_TABLE}') IS NULL THEN FALSE
67
+ ELSE has_table_privilege(current_user, to_regclass('public.${ACTION_ESCROW_EVENT_TABLE}'), 'SELECT')
68
+ AND has_table_privilege(current_user, to_regclass('public.${ACTION_ESCROW_EVENT_TABLE}'), 'INSERT') END AS can_append_history,
69
+ CASE WHEN to_regclass('public.${ACTION_ESCROW_STATE_TABLE}') IS NULL THEN TRUE
70
+ ELSE (SELECT relowner = (SELECT oid FROM pg_roles WHERE rolname = current_user)
71
+ FROM pg_class WHERE oid = to_regclass('public.${ACTION_ESCROW_STATE_TABLE}')) END AS owns_state_table,
72
+ CASE WHEN to_regclass('public.${ACTION_ESCROW_EVENT_TABLE}') IS NULL THEN TRUE
73
+ ELSE (SELECT relowner = (SELECT oid FROM pg_roles WHERE rolname = current_user)
74
+ FROM pg_class WHERE oid = to_regclass('public.${ACTION_ESCROW_EVENT_TABLE}')) END AS owns_event_table,
75
+ CASE WHEN to_regclass('public.${ACTION_ESCROW_STATE_TABLE}') IS NULL THEN TRUE
76
+ ELSE has_table_privilege(current_user, to_regclass('public.${ACTION_ESCROW_STATE_TABLE}'), 'DELETE')
77
+ OR has_table_privilege(current_user, to_regclass('public.${ACTION_ESCROW_STATE_TABLE}'), 'TRUNCATE') END AS can_destroy_state,
78
+ CASE WHEN to_regclass('public.${ACTION_ESCROW_EVENT_TABLE}') IS NULL THEN TRUE
79
+ ELSE has_table_privilege(current_user, to_regclass('public.${ACTION_ESCROW_EVENT_TABLE}'), 'UPDATE')
80
+ OR has_table_privilege(current_user, to_regclass('public.${ACTION_ESCROW_EVENT_TABLE}'), 'DELETE')
81
+ OR has_table_privilege(current_user, to_regclass('public.${ACTION_ESCROW_EVENT_TABLE}'), 'TRUNCATE') END AS can_mutate_history`,
82
+ read: `SELECT revision, record_json FROM ${ACTION_ESCROW_STATE_TABLE} WHERE agreement_key = $1`,
83
+ history: `SELECT revision, previous_revision, record_json, record_digest, recorded_at
84
+ FROM ${ACTION_ESCROW_EVENT_TABLE}
85
+ WHERE agreement_key = $1
86
+ ORDER BY revision ASC`,
87
+ create: `WITH installed AS (
88
+ INSERT INTO ${ACTION_ESCROW_STATE_TABLE} (agreement_key, revision, record_json, updated_at)
89
+ VALUES ($1, 0, $2, $3)
90
+ ON CONFLICT (agreement_key) DO NOTHING
91
+ RETURNING agreement_key, revision, record_json, updated_at
92
+ ), journaled AS (
93
+ INSERT INTO ${ACTION_ESCROW_EVENT_TABLE}
94
+ (agreement_key, revision, previous_revision, record_json, record_digest, recorded_at)
95
+ SELECT agreement_key, revision, NULL, record_json, $4, updated_at
96
+ FROM installed
97
+ RETURNING revision
98
+ )
99
+ SELECT revision FROM journaled`,
100
+ compareAndSwap: `WITH installed AS (
101
+ UPDATE ${ACTION_ESCROW_STATE_TABLE}
102
+ SET revision = $3, record_json = $4, updated_at = $5
103
+ WHERE agreement_key = $1 AND revision = $2 AND updated_at <= $5
104
+ RETURNING agreement_key, revision, record_json, updated_at
105
+ ), journaled AS (
106
+ INSERT INTO ${ACTION_ESCROW_EVENT_TABLE}
107
+ (agreement_key, revision, previous_revision, record_json, record_digest, recorded_at)
108
+ SELECT agreement_key, revision, $2, record_json, $6, updated_at
109
+ FROM installed
110
+ RETURNING revision
111
+ )
112
+ SELECT revision FROM journaled`,
113
+ });
114
+
115
+ /**
116
+ * @param {*} value
117
+ */
118
+ function validKey(value) {
119
+ return typeof value === 'string'
120
+ && value.length >= 1
121
+ && value.length <= 512
122
+ && !/[\u0000-\u001f\u007f]/.test(value);
123
+ }
124
+
125
+ /**
126
+ * @param {*} value
127
+ */
128
+ function parsedState(value) {
129
+ if (typeof value !== 'string'
130
+ || Buffer.byteLength(value, 'utf8') > ACTION_ESCROW_MAX_STATE_BYTES
131
+ || !strictJsonGate(value).ok) {
132
+ return null;
133
+ }
134
+ try {
135
+ const parsed = JSON.parse(value);
136
+ return parsed !== null
137
+ && typeof parsed === 'object'
138
+ && !Array.isArray(parsed)
139
+ && Object.getPrototypeOf(parsed) === Object.prototype
140
+ ? parsed
141
+ : null;
142
+ } catch {
143
+ return null;
144
+ }
145
+ }
146
+
147
+ /**
148
+ * @param {string} value
149
+ */
150
+ function recordDigest(value) {
151
+ return `sha256:${crypto.createHash('sha256').update(value, 'utf8').digest('hex')}`;
152
+ }
153
+
154
+ /**
155
+ * @param {*} result
156
+ * @param {string} operation
157
+ */
158
+ function assertResult(result, operation) {
159
+ if (!result || typeof result.rowCount !== 'number' || !Number.isSafeInteger(result.rowCount)
160
+ || result.rowCount < 0) {
161
+ throw new Error(`${operation}: malformed Postgres result`);
162
+ }
163
+ return result;
164
+ }
165
+
166
+ export function createActionEscrowPostgresStore({
167
+ query,
168
+ now = Date.now,
169
+ }: {
170
+ query?: (text: string, params: any[]) => Promise<{ rowCount: number; rows?: any[] }>;
171
+ now?: number | (() => number);
172
+ } = {}) {
173
+ if (typeof query !== 'function') {
174
+ throw new TypeError('createActionEscrowPostgresStore requires an async pg-style query function');
175
+ }
176
+ let lastNow = Number.NEGATIVE_INFINITY;
177
+
178
+ function nowMs() {
179
+ const value = typeof now === 'function' ? now() : now;
180
+ if (!Number.isSafeInteger(value) || value < 0) {
181
+ throw new Error('action-escrow store clock must be a non-negative safe-integer epoch millisecond');
182
+ }
183
+ if (value < lastNow) {
184
+ throw new Error(`action-escrow store clock regression refused: ${value} < ${lastNow}`);
185
+ }
186
+ lastNow = value;
187
+ return value;
188
+ }
189
+
190
+ /**
191
+ * @param {*} key
192
+ */
193
+ function assertKey(key) {
194
+ if (!validKey(key)) throw new TypeError('action-escrow agreement key is invalid');
195
+ }
196
+
197
+ /**
198
+ * @param {*} value
199
+ * @param {number} revision
200
+ */
201
+ function assertState(value, revision) {
202
+ const parsed = parsedState(value);
203
+ if (!parsed) {
204
+ throw new TypeError('action-escrow state must be bounded strict JSON text');
205
+ }
206
+ if (!Number.isSafeInteger(parsed.revision)
207
+ || parsed.revision < 0
208
+ || parsed.revision !== revision) {
209
+ throw new TypeError('action-escrow state revision must match the CAS revision');
210
+ }
211
+ }
212
+
213
+ /**
214
+ * @param {string} key
215
+ */
216
+ async function loadHistory(key) {
217
+ // query is validated as a function above; the guard's narrowing does not
218
+ // carry into this hoisted function declaration's closure.
219
+ const result = assertResult(
220
+ await query!(ACTION_ESCROW_STATE_SQL.history, [key]),
221
+ 'action-escrow history',
222
+ );
223
+ if (!Array.isArray(result.rows) || result.rows.length !== result.rowCount) {
224
+ throw new Error('action-escrow history: malformed Postgres result');
225
+ }
226
+ let previousRecordedAt = Number.NEGATIVE_INFINITY;
227
+ const history = result.rows.map(/** @param {*} row @param {number} index */ (row, index) => {
228
+ const revision = Number(row?.revision);
229
+ const previousRevision = row?.previous_revision === null
230
+ ? null
231
+ : Number(row?.previous_revision);
232
+ const recordedAt = Number(row?.recorded_at);
233
+ if (!Number.isSafeInteger(revision)
234
+ || revision !== index
235
+ || previousRevision !== (revision === 0 ? null : revision - 1)
236
+ || !Number.isSafeInteger(recordedAt)
237
+ || recordedAt < 0
238
+ || recordedAt < previousRecordedAt
239
+ || !parsedState(row?.record_json)
240
+ || row?.record_digest !== recordDigest(row.record_json)) {
241
+ throw new Error('action-escrow history: invalid or non-contiguous event');
242
+ }
243
+ assertState(row.record_json, revision);
244
+ previousRecordedAt = recordedAt;
245
+ return Object.freeze({
246
+ revision,
247
+ previous_revision: previousRevision,
248
+ record_json: row.record_json,
249
+ record_digest: row.record_digest,
250
+ recorded_at: recordedAt,
251
+ });
252
+ });
253
+ return Object.freeze(history);
254
+ }
255
+
256
+ async function read(key) {
257
+ assertKey(key);
258
+ for (let attempt = 0; attempt < 2; attempt += 1) {
259
+ const result = assertResult(
260
+ // query is validated as a function above; the guard's narrowing does
261
+ // not carry into this hoisted function declaration's closure.
262
+ await query!(ACTION_ESCROW_STATE_SQL.read, [key]),
263
+ 'action-escrow read',
264
+ );
265
+ if (result.rowCount > 1
266
+ || !Array.isArray(result.rows)
267
+ || result.rows.length !== result.rowCount) {
268
+ throw new Error('action-escrow read: database returned malformed or ambiguous state');
269
+ }
270
+ const history = await loadHistory(key);
271
+ if (result.rowCount === 0) {
272
+ if (history.length === 0) return null;
273
+ continue;
274
+ }
275
+ if (!Number.isSafeInteger(Number(result.rows[0]?.revision))
276
+ || Number(result.rows[0].revision) < 0
277
+ || !parsedState(result.rows[0]?.record_json)) {
278
+ throw new Error('action-escrow read: database returned malformed or ambiguous state');
279
+ }
280
+ const revision = Number(result.rows[0].revision);
281
+ assertState(result.rows[0].record_json, revision);
282
+ const tail = history.at(-1);
283
+ if (tail?.revision === revision
284
+ && tail.record_json === result.rows[0].record_json
285
+ && tail.record_digest === recordDigest(result.rows[0].record_json)) {
286
+ return { revision, value: result.rows[0].record_json };
287
+ }
288
+ }
289
+ throw new Error('action-escrow read: state and append-only journal do not agree');
290
+ }
291
+
292
+ return Object.freeze({
293
+ version: ACTION_ESCROW_PG_STORE_VERSION,
294
+ durable: true,
295
+ atomicExpectedRevisionCas: true,
296
+ linearizableReads: true,
297
+ monotonicRevisions: true,
298
+ nonExpiring: true,
299
+ maxStateBytes: ACTION_ESCROW_MAX_STATE_BYTES,
300
+ async health() {
301
+ const result = assertResult(
302
+ await query(ACTION_ESCROW_STATE_SQL.health, []),
303
+ 'action-escrow health',
304
+ );
305
+ if (result.rowCount !== 1 || !Array.isArray(result.rows) || result.rows.length !== 1) {
306
+ throw new Error('action-escrow health: malformed Postgres result');
307
+ }
308
+ return {
309
+ ok: result.rows[0].table_ready === true
310
+ && result.rows[0].event_table_ready === true
311
+ && result.rows[0].can_use === true
312
+ && result.rows[0].can_append_history === true
313
+ && result.rows[0].owns_state_table === false
314
+ && result.rows[0].owns_event_table === false
315
+ && result.rows[0].can_destroy_state === false
316
+ && result.rows[0].can_mutate_history === false,
317
+ version: ACTION_ESCROW_PG_STORE_VERSION,
318
+ };
319
+ },
320
+ read,
321
+ async readHistory(key) {
322
+ assertKey(key);
323
+ return loadHistory(key);
324
+ },
325
+ async compareAndSwap(key, expectedRevision, value) {
326
+ assertKey(key);
327
+ if (expectedRevision !== null
328
+ && (!Number.isSafeInteger(expectedRevision) || expectedRevision < 0)) {
329
+ throw new TypeError('action-escrow expected revision is invalid');
330
+ }
331
+ const nextRevision = expectedRevision === null ? 0 : expectedRevision + 1;
332
+ assertState(value, nextRevision);
333
+ const sql = expectedRevision === null
334
+ ? ACTION_ESCROW_STATE_SQL.create
335
+ : ACTION_ESCROW_STATE_SQL.compareAndSwap;
336
+ const params = expectedRevision === null
337
+ ? [key, value, nowMs(), recordDigest(value)]
338
+ : [key, expectedRevision, nextRevision, value, nowMs(), recordDigest(value)];
339
+ const result = assertResult(
340
+ await query(sql, params),
341
+ 'action-escrow compareAndSwap',
342
+ );
343
+ if (result.rowCount > 1) {
344
+ throw new Error('action-escrow compareAndSwap affected multiple rows');
345
+ }
346
+ if (result.rowCount === 0) return { applied: false, revision: null };
347
+ if (!Array.isArray(result.rows)
348
+ || result.rows.length !== 1
349
+ || Number(result.rows[0]?.revision) !== nextRevision) {
350
+ throw new Error('action-escrow compareAndSwap returned a malformed revision');
351
+ }
352
+ return { applied: true, revision: nextRevision };
353
+ },
354
+ });
355
+ }
356
+
357
+ export default {
358
+ ACTION_ESCROW_PG_STORE_VERSION,
359
+ ACTION_ESCROW_STATE_TABLE,
360
+ ACTION_ESCROW_EVENT_TABLE,
361
+ ACTION_ESCROW_MAX_STATE_BYTES,
362
+ ACTION_ESCROW_STATE_DDL,
363
+ actionEscrowRuntimeGrantDdl,
364
+ ACTION_ESCROW_STATE_SQL,
365
+ createActionEscrowPostgresStore,
366
+ };