chainlesschain 0.166.14 → 0.166.16

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 (195) hide show
  1. package/package.json +1 -1
  2. package/src/assets/web-panel/assets/{AIOps-C8SyFhOo.js → AIOps-CTxa2Wbq.js} +1 -1
  3. package/src/assets/web-panel/assets/{ActionButton-Dup5gFK0.js → ActionButton-DHrQyYhy.js} +1 -1
  4. package/src/assets/web-panel/assets/{Analytics-8FyOWA7V.js → Analytics-DQiUqcNl.js} +3 -3
  5. package/src/assets/web-panel/assets/{AppLayout-DBzdU9dg.js → AppLayout-_Iz4r6wt.js} +5 -5
  6. package/src/assets/web-panel/assets/{Artifacts-C_0Z8gNy.js → Artifacts-BNs63YHo.js} +1 -1
  7. package/src/assets/web-panel/assets/{Audit-Cf2a-58m.js → Audit-BgHIqRLn.js} +1 -1
  8. package/src/assets/web-panel/assets/{BackgroundAgents-M7w-2zgB.js → BackgroundAgents-DUQ5axBB.js} +1 -1
  9. package/src/assets/web-panel/assets/{Backup-CCTHQXoL.js → Backup-CJ51E9Vw.js} +1 -1
  10. package/src/assets/web-panel/assets/{BaseInput-DjXpeFwJ.js → BaseInput-o-5toA_R.js} +1 -1
  11. package/src/assets/web-panel/assets/{Chat-cAvchsJU.js → Chat-Cu7a-x1L.js} +6 -6
  12. package/src/assets/web-panel/assets/{ChatBubbleRenderer-DvEvxlZv.js → ChatBubbleRenderer-BUyYkAXr.js} +1 -1
  13. package/src/assets/web-panel/assets/{Checkbox-DKs6qvOn.js → Checkbox-DwdmZ71L.js} +1 -1
  14. package/src/assets/web-panel/assets/{Codegen-DlkA7b9X.js → Codegen-BrH2eYiP.js} +1 -1
  15. package/src/assets/web-panel/assets/{Col-CkfdKhyJ.js → Col-BVJQEP2j.js} +1 -1
  16. package/src/assets/web-panel/assets/{Community-iNXMHzj6.js → Community-FGO3vZPq.js} +1 -1
  17. package/src/assets/web-panel/assets/{Compact-DQxPEd09.js → Compact-DYGAFVnT.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compliance-CSKvMCjs.js → Compliance-CRvc8Bdt.js} +1 -1
  19. package/src/assets/web-panel/assets/{Cowork-CkWqazpD.js → Cowork-D6Ah2m7A.js} +3 -3
  20. package/src/assets/web-panel/assets/{Cron-k6W8IzhB.js → Cron-AXbU2ifR.js} +2 -2
  21. package/src/assets/web-panel/assets/{Crosschain-C8MS1OuY.js → Crosschain-C5DXgEFd.js} +1 -1
  22. package/src/assets/web-panel/assets/{DID-DJuCcgiQ.js → DID-Bxh9QV9v.js} +2 -2
  23. package/src/assets/web-panel/assets/{Dashboard-LeThKzp3.js → Dashboard-B-ZHEta-.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dropdown-BvxwgpE4.js → Dropdown-66Ik1jqa.js} +1 -1
  25. package/src/assets/web-panel/assets/{EmailListRenderer-n3e_a1M3.js → EmailListRenderer-CCWk1BIM.js} +1 -1
  26. package/src/assets/web-panel/assets/{FamilyGuardDashboard-509W6W6v.js → FamilyGuardDashboard--9faIULK.js} +1 -1
  27. package/src/assets/web-panel/assets/{Federation-xgZReQm0.js → Federation-CejW9CgX.js} +1 -1
  28. package/src/assets/web-panel/assets/{FormItemContext-jZOhKcQx.js → FormItemContext-acHgcXki.js} +1 -1
  29. package/src/assets/web-panel/assets/{GenericCardRenderer-Jo0ULzHv.js → GenericCardRenderer-D2ghd_K4.js} +1 -1
  30. package/src/assets/web-panel/assets/{Git-BsRcyWQP.js → Git-BOe1YJrV.js} +2 -2
  31. package/src/assets/web-panel/assets/{Governance-JlpMYTAk.js → Governance-BjgzJsof.js} +1 -1
  32. package/src/assets/web-panel/assets/{Inference-CUEz872-.js → Inference-CXCD5rnP.js} +1 -1
  33. package/src/assets/web-panel/assets/{KnowledgeGraph-DgrvvEDU.js → KnowledgeGraph-CHA8QgJT.js} +1 -1
  34. package/src/assets/web-panel/assets/{Logs-Df-KY4U3.js → Logs-DORPMcL6.js} +2 -2
  35. package/src/assets/web-panel/assets/MarkdownRenderer-CPPHz01z.js +1 -0
  36. package/src/assets/web-panel/assets/{Marketplace-kPZDTOeI.js → Marketplace-BCFlo1Yx.js} +1 -1
  37. package/src/assets/web-panel/assets/{McpTools-CRRzPqNX.js → McpTools-Dl_uZAKh.js} +2 -2
  38. package/src/assets/web-panel/assets/{Memory-D6pNSkg9.js → Memory-DupHamwz.js} +2 -2
  39. package/src/assets/web-panel/assets/{MobileBridge-g-pvtwRC.js → MobileBridge-BRSMPJxW.js} +1 -1
  40. package/src/assets/web-panel/assets/{MobileProjects-Bzayfrfe.js → MobileProjects-BHpuzsDl.js} +1 -1
  41. package/src/assets/web-panel/assets/{Mtc-B7S3W-XL.js → Mtc-Ch0jOQfZ.js} +2 -2
  42. package/src/assets/web-panel/assets/{MtcAudit-B8OMEWVb.js → MtcAudit-qzwQrDi7.js} +2 -2
  43. package/src/assets/web-panel/assets/{Multisig-CowRp7GH.js → Multisig-B5wHzUcm.js} +3 -3
  44. package/src/assets/web-panel/assets/{NLProgramming-BkQNyi4c.js → NLProgramming-Hy9uAiA0.js} +1 -1
  45. package/src/assets/web-panel/assets/{Notes-Cm7gtkKz.js → Notes-DkuchMC7.js} +4 -4
  46. package/src/assets/web-panel/assets/{NotificationSettings-CbPTVh9m.js → NotificationSettings-CYZhD7Pi.js} +1 -1
  47. package/src/assets/web-panel/assets/{OrderTableRenderer-Dswi26mJ.js → OrderTableRenderer-DKOoqajt.js} +1 -1
  48. package/src/assets/web-panel/assets/{Organization-BkjQ9yy7.js → Organization-D9AXn6j8.js} +4 -4
  49. package/src/assets/web-panel/assets/{Overflow-DmRFXP7Y.js → Overflow-Bg15J6hX.js} +1 -1
  50. package/src/assets/web-panel/assets/{P2P-DQE1toAu.js → P2P--B54HoQn.js} +2 -2
  51. package/src/assets/web-panel/assets/PdhVaultBrowser-Cz9Kk2Nb.js +7 -0
  52. package/src/assets/web-panel/assets/{Permissions-xSjfOaiU.js → Permissions-DHx38eS1.js} +4 -4
  53. package/src/assets/web-panel/assets/{PersonalDataHub-B9ICLqVP.js → PersonalDataHub-Dw8i2hAs.js} +4 -4
  54. package/src/assets/web-panel/assets/{Pipeline-CUNwyN9E.js → Pipeline-B2o6Ur2G.js} +1 -1
  55. package/src/assets/web-panel/assets/{Privacy-SOoCricX.js → Privacy-8g-h2fnu.js} +1 -1
  56. package/src/assets/web-panel/assets/{ProjectInit-CSaFul0F.js → ProjectInit-DuVTcINx.js} +2 -2
  57. package/src/assets/web-panel/assets/{ProjectSettings-CueiB_oe.js → ProjectSettings-C8njvdZX.js} +2 -2
  58. package/src/assets/web-panel/assets/Projects-Ca9BAfu7.js +1 -0
  59. package/src/assets/web-panel/assets/{Providers-CTqf_31h.js → Providers-wFqgod_W.js} +1 -1
  60. package/src/assets/web-panel/assets/{QrScannerModal-C8ZP31E9.js → QrScannerModal-9NCXrpmA.js} +1 -1
  61. package/src/assets/web-panel/assets/{QuickAsk-CPgn4713.js → QuickAsk-WUTNvFkY.js} +1 -1
  62. package/src/assets/web-panel/assets/{Recommend-8_e0zw0i.js → Recommend-xT4nYmju.js} +1 -1
  63. package/src/assets/web-panel/assets/{RemoteSession-iT7lbGqw.js → RemoteSession-fak1PBGC.js} +3 -3
  64. package/src/assets/web-panel/assets/{Reputation-B4q9SUOz.js → Reputation-D1AkQvD-.js} +1 -1
  65. package/src/assets/web-panel/assets/{Row-CA_nASyL.js → Row-DVfm6xGt.js} +1 -1
  66. package/src/assets/web-panel/assets/{RssFeed-Cwb0E3yv.js → RssFeed-BDunoLSE.js} +3 -3
  67. package/src/assets/web-panel/assets/{Search-D9iVsOxJ.js → Search-OPycRoU3.js} +1 -1
  68. package/src/assets/web-panel/assets/{Security-DPLbYchG.js → Security-C8-Fm3Dt.js} +3 -3
  69. package/src/assets/web-panel/assets/{Services-DGMVL_LF.js → Services-qM_sMpMl.js} +2 -2
  70. package/src/assets/web-panel/assets/{Skeleton-B_6kym0D.js → Skeleton-D3HBJjVF.js} +1 -1
  71. package/src/assets/web-panel/assets/{Skills-Ds6r0dXB.js → Skills-jr67X-tP.js} +1 -1
  72. package/src/assets/web-panel/assets/{Sla-BLHAbBhb.js → Sla-CS3ggyCg.js} +1 -1
  73. package/src/assets/web-panel/assets/{SpeechSettings-CZuddd14.js → SpeechSettings-CTOCYxOi.js} +1 -1
  74. package/src/assets/web-panel/assets/{SyncSettings-CdBq7oSl.js → SyncSettings-D3s3cLT5.js} +2 -2
  75. package/src/assets/web-panel/assets/{Tasks-DfT60Nyl.js → Tasks-CdfoJ25m.js} +1 -1
  76. package/src/assets/web-panel/assets/{Templates-RLfsKy2V.js → Templates-Drb4AI8X.js} +1 -1
  77. package/src/assets/web-panel/assets/{Tenant-Cw8xCNLE.js → Tenant-DzwR3Qkz.js} +1 -1
  78. package/src/assets/web-panel/assets/{Terminal-BOUkNu4f.js → Terminal-D-ESq7xp.js} +2 -2
  79. package/src/assets/web-panel/assets/{TimelineRenderer-CgGZWaRm.js → TimelineRenderer-BK-AUuws.js} +1 -1
  80. package/src/assets/web-panel/assets/{Tokens-CpwjVyir.js → Tokens-xGTouuNp.js} +1 -1
  81. package/src/assets/web-panel/assets/{Trigger-DL21H-eb.js → Trigger-BvZj-v2H.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trust-B3ffFclW.js → Trust-BLC9CGmc.js} +1 -1
  83. package/src/assets/web-panel/assets/{UkeySign-CkBeb5aI.js → UkeySign-BSuCXpmE.js} +1 -1
  84. package/src/assets/web-panel/assets/{VideoEditing-U2iVaE3r.js → VideoEditing-DT3A-_Ql.js} +1 -1
  85. package/src/assets/web-panel/assets/{Wallet-CQk2wzYd.js → Wallet-c1OXao85.js} +4 -4
  86. package/src/assets/web-panel/assets/{WebAuthn-DYIuIYzT.js → WebAuthn-CuA-F7KJ.js} +5 -5
  87. package/src/assets/web-panel/assets/{WorkflowEditor-tg1c2mZQ.js → WorkflowEditor-MANZrf82.js} +1 -1
  88. package/src/assets/web-panel/assets/{chat-CtL61spq.js → chat-D4Y4Ey0O.js} +1 -1
  89. package/src/assets/web-panel/assets/{colors-DKpbeo8s.js → colors-DDH06hKO.js} +1 -1
  90. package/src/assets/web-panel/assets/{compact-item-gPZsrswt.js → compact-item-Cym756Xj.js} +1 -1
  91. package/src/assets/web-panel/assets/{createContext-CfHlwzVr.js → createContext-By4Nb1k-.js} +1 -1
  92. package/src/assets/web-panel/assets/devWarning-KO3pAGeO.js +1 -0
  93. package/src/assets/web-panel/assets/{hasIn-MV47g_6X.js → hasIn-Ch7OY258.js} +1 -1
  94. package/src/assets/web-panel/assets/{index-DMMvlLp9.js → index-6-2eYBXr.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-DsH-i_3d.js → index-6CpOV3Pn.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-CB1HnbNK.js → index-ArMrnx0v.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-DU4rZpiK.js → index-B51otVoZ.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-CRv_uMLV.js → index-BBBUn5v7.js} +1 -1
  99. package/src/assets/web-panel/assets/{index-srBc9U_s.js → index-BFydNU6a.js} +1 -1
  100. package/src/assets/web-panel/assets/{index-DnZ_zS1M.js → index-BIrRX-ra.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-DGtEBodr.js → index-BZB9q-Lp.js} +1 -1
  102. package/src/assets/web-panel/assets/index-Bh4Rgkg2.js +1 -0
  103. package/src/assets/web-panel/assets/{index-BjlPD7GO.js → index-Bo20BfdS.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-C_B9LiKs.js → index-CF3Lhyns.js} +1 -1
  105. package/src/assets/web-panel/assets/{index-Rb7CRMw9.js → index-CGsvEbbq.js} +1 -1
  106. package/src/assets/web-panel/assets/{index-DIeJmlAA.js → index-CJPks33Y.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-xUlgG0Xe.js → index-CT-3FYwd.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-6G0ETFTW.js → index-CUSUzaB8.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-CzfxTSPW.js → index-CV1UC4zo.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-CmPJpLoD.js → index-CV8qXTvb.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-fYcS7NS0.js → index-CZn6dmZe.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Cjv0kKCU.js → index-CjNUTBwe.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Df9sWKpl.js → index-CpudmDhf.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-C9RF4ieu.js → index-CykROEJA.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-Zvkt9xqi.js → index-D5sADPGY.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-yxxXlqBA.js → index-D9le0-dc.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-BBw52sEl.js → index-DAkk1M7x.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-ExAGPAnX.js → index-DNEJzc0r.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-BQp5M0s1.js → index-DNlxihs8.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-Ddcpj1El.js → index-DiewptTL.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-BOyGUuBE.js → index-DjwgJ5Jd.js} +1 -1
  122. package/src/assets/web-panel/assets/index-Dox_coTY.js +1 -0
  123. package/src/assets/web-panel/assets/{index-CmM9jqwe.js → index-DtGValNi.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-DfLyHCWi.js → index-DvnywwUj.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BN-wxSQ8.js → index-OU8DZ9BL.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-D1XB5-t5.js → index-XGeQMvWK.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-C4lF-f_z.js → index-ZY2Qs_Sn.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-C9gtY4nG.js → index-jx7kr6mB.js} +3 -3
  129. package/src/assets/web-panel/assets/{index-C6nC_DyY.js → index-mL3nOcH6.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-Dd19vRwy.js → index-oBsfUbyH.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-bSFrAszw.js → index-q-s04OKn.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-D_Pl1GnW.js → index-tZ5BUdad.js} +1 -1
  133. package/src/assets/web-panel/assets/{initDefaultProps-BeOZoSZ4.js → initDefaultProps-DGcX9iPP.js} +1 -1
  134. package/src/assets/web-panel/assets/{motion-BXE8VMKx.js → motion-2SeL1D8A.js} +1 -1
  135. package/src/assets/web-panel/assets/{move-I9sdvPGX.js → move-3cYbQCpS.js} +1 -1
  136. package/src/assets/web-panel/assets/{mtc-parser-d25zrJd0.js → mtc-parser-IDdxfw0z.js} +1 -1
  137. package/src/assets/web-panel/assets/{omit-D71V_T4e.js → omit-BNswdKO6.js} +1 -1
  138. package/src/assets/web-panel/assets/{pickAttrs-BNZKpIAR.js → pickAttrs-BwCnem2I.js} +1 -1
  139. package/src/assets/web-panel/assets/{placementArrow-jOcgxTTz.js → placementArrow-B9PtZ5rq.js} +1 -1
  140. package/src/assets/web-panel/assets/{responsiveObserve-pta_O8AU.js → responsiveObserve-CPB6iSzV.js} +1 -1
  141. package/src/assets/web-panel/assets/{slide-Dlq6om7z.js → slide-DZgyblZa.js} +1 -1
  142. package/src/assets/web-panel/assets/{statusUtils-BnkO_yRv.js → statusUtils-D82IBdsx.js} +1 -1
  143. package/src/assets/web-panel/assets/{styleChecker-p8NfWmcG.js → styleChecker-qSHtosM-.js} +1 -1
  144. package/src/assets/web-panel/assets/{useFlexGapSupport-KcjZZ5U7.js → useFlexGapSupport-CmWO7h0I.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFs-gqonDMWB.js → useFs-yLrs_ub4.js} +1 -1
  146. package/src/assets/web-panel/assets/{usePersonalDataHub-jIIfE4go.js → usePersonalDataHub-CJ1VW6rh.js} +1 -1
  147. package/src/assets/web-panel/assets/{vnode-Dyo7fBrM.js → vnode-CzY_moQ4.js} +1 -1
  148. package/src/assets/web-panel/assets/{zoom-6l8BMg9W.js → zoom-DAa7rEY5.js} +1 -1
  149. package/src/assets/web-panel/index.html +1 -1
  150. package/src/commands/agent.js +62 -11
  151. package/src/commands/learning.js +71 -1
  152. package/src/commands/team.js +100 -3
  153. package/src/data/changelog.json +29 -3
  154. package/src/harness/worktree-isolator.js +74 -9
  155. package/src/lib/agent-team/task-lease.js +12 -1
  156. package/src/lib/agent-team/team-graph-runtime-adapter.js +6 -0
  157. package/src/lib/agent-team/team-runner.js +4 -0
  158. package/src/lib/agent-team/team-worktree.js +4 -0
  159. package/src/lib/capability-manifest.js +212 -1
  160. package/src/lib/evolution/evolution-artifact-ports.js +3277 -0
  161. package/src/lib/evolution/evolution-eval-gate.js +6442 -0
  162. package/src/lib/evolution/evolution-evidence-projector.js +3786 -0
  163. package/src/lib/evolution/evolution-ledger-ports.js +2689 -0
  164. package/src/lib/evolution/evolution-ledger.js +4280 -0
  165. package/src/lib/evolution/skill-candidate-registry.js +2258 -0
  166. package/src/lib/evolution/skill-execution-manifest.js +942 -0
  167. package/src/lib/evolution/skill-mutation-authority.js +1677 -0
  168. package/src/lib/evolution/skill-promotion-controller.js +506 -0
  169. package/src/lib/evolution/skill-release-registry.js +3277 -0
  170. package/src/lib/evolution/skill-target-matrix-eval.js +3233 -0
  171. package/src/lib/evolution/skill-writer-inventory-manifest.js +998 -0
  172. package/src/lib/evolution/skill-writer-inventory-validator.js +816 -0
  173. package/src/lib/formal-quality-eval-runtime.js +192 -0
  174. package/src/lib/graph-kernel/cutover-contract.js +7 -0
  175. package/src/lib/graph-kernel/cutover-ledger.js +8 -7
  176. package/src/lib/graph-kernel/evidence-digest.js +47 -0
  177. package/src/lib/graph-kernel/graph-runtime-surfaces.json +8 -1
  178. package/src/lib/graph-kernel/production-cutover-evidence.js +342 -15
  179. package/src/lib/graph-kernel/production-source-evidence.js +1599 -0
  180. package/src/lib/graph-kernel/retirement-evidence.js +15 -5
  181. package/src/lib/graph-kernel/runtime-surface-manifest.js +67 -2
  182. package/src/lib/headless-manifest.js +3 -1
  183. package/src/lib/learning/skill-improver.js +311 -43
  184. package/src/lib/learning/skill-synthesizer.js +236 -16
  185. package/src/lib/process-execution-broker/index.js +32 -0
  186. package/src/lib/process-execution-broker/platform-sandbox.js +5 -4
  187. package/src/lib/secure-fs.js +45 -2
  188. package/src/runtime/agent-core.js +17 -5
  189. package/src/runtime/headless-runner.js +16 -9
  190. package/src/assets/web-panel/assets/MarkdownRenderer-BEPDcSwz.js +0 -1
  191. package/src/assets/web-panel/assets/PdhVaultBrowser-BujyoDRG.js +0 -7
  192. package/src/assets/web-panel/assets/Projects-BOeXvU7y.js +0 -1
  193. package/src/assets/web-panel/assets/devWarning-DdqnMhzi.js +0 -1
  194. package/src/assets/web-panel/assets/index-BcK8o4ke.js +0 -1
  195. package/src/assets/web-panel/assets/index-DvbAjyYI.js +0 -1
@@ -0,0 +1,4280 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { types as utilTypes } from "node:util";
5
+ import { getHomeDir } from "../paths.js";
6
+ import { ensurePrivateDirectory, ensurePrivateFile } from "../secure-fs.js";
7
+ import { withFileLock } from "../with-file-lock.js";
8
+
9
+ export const EVOLUTION_LEDGER_EVENT_SCHEMA =
10
+ "chainlesschain.evolution-event/v2";
11
+ export const EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA =
12
+ "chainlesschain.evolution-domain-event/v1";
13
+ export const EVOLUTION_LEDGER_IDENTITY_SCHEMA =
14
+ "chainlesschain.evolution-ledger-identity/v1";
15
+ export const EVOLUTION_LEDGER_ANCHOR_SCHEMA =
16
+ "chainlesschain.evolution-ledger-head-anchor/v1";
17
+ export const EVOLUTION_LEDGER_RECEIPT_SCHEMA =
18
+ "chainlesschain.evolution-ledger-receipt/v2";
19
+ export const EVOLUTION_LEDGER_VERIFICATION_SCHEMA =
20
+ "chainlesschain.evolution-ledger-verification/v2";
21
+ export const EVOLUTION_LEDGER_QUERY_SCHEMA =
22
+ "chainlesschain.evolution-ledger-query-result/v1";
23
+ export const EVOLUTION_LEDGER_WITNESS_SCHEMA =
24
+ "chainlesschain.evolution-ledger-witness/v1";
25
+ export const EVOLUTION_LEDGER_WITNESS_ANCESTRY_SCHEMA =
26
+ "chainlesschain.evolution-ledger-witness-ancestry/v1";
27
+ export const EVOLUTION_LEDGER_STORE_MARKER_SCHEMA =
28
+ "chainlesschain.evolution-ledger-store-marker/v1";
29
+ export const EVOLUTION_ARTIFACT_REF_SCHEMA =
30
+ "chainlesschain.content-addressed-artifact-ref/v1";
31
+ export const EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA =
32
+ "chainlesschain.artifact-resolution/v1";
33
+ export const EVOLUTION_LEDGER_MAX_EVENT_BYTES = 1024 * 1024;
34
+ export const EVOLUTION_LEDGER_MAX_ARTIFACT_BYTES = 16 * 1024 * 1024;
35
+ export const EVOLUTION_LEDGER_MAX_EVENTS = 250_000;
36
+
37
+ const IDENTITY_DOMAIN = "chainlesschain.evolution-ledger-identity/v1\0";
38
+ const EVENT_DOMAIN = "chainlesschain.evolution-event/v2\0";
39
+ const DOMAIN_EVENT_DOMAIN = "chainlesschain.evolution-domain-event/v1\0";
40
+ const SEGMENT_DOMAIN = "chainlesschain.evolution-segment/v1\0";
41
+ const ANCHOR_DOMAIN = "chainlesschain.evolution-ledger-head-anchor/v1\0";
42
+ const RECEIPT_DOMAIN = "chainlesschain.evolution-ledger-receipt/v2\0";
43
+ const WITNESS_DOMAIN = "chainlesschain.evolution-ledger-witness/v1\0";
44
+ const WITNESS_ANCESTRY_DOMAIN =
45
+ "chainlesschain.evolution-ledger-witness-ancestry/v1\0";
46
+ const STORE_MARKER_DOMAIN = "chainlesschain.evolution-ledger-store-marker/v1\0";
47
+ const STORE_MARKER_ENTRY_DOMAIN =
48
+ "chainlesschain.evolution-ledger-store-marker-entry/v1\0";
49
+ const STORE_BINDING_DOMAIN = "chainlesschain.evolution-store-binding/v1\0";
50
+ const WITNESS_PAYLOAD_DOMAIN = "chainlesschain.evolution-witness-payload/v1\0";
51
+ const DISCARD_ACCUMULATOR_DOMAIN =
52
+ "chainlesschain.evolution-witness-discard-accumulator/v1\0";
53
+ const ARTIFACT_VALIDATION_DOMAIN =
54
+ "chainlesschain.evolution-artifact-validation/v1\0";
55
+ const IDENTITY_FILE_NAME = "identity-v1.json";
56
+ const STORE_MARKER_FILE_NAME = "store-marker-v1.json";
57
+ const HEAD_FILE_NAME = "head-v1.json";
58
+ const SEGMENT_DIRECTORY_NAME = "segments-v1";
59
+ const ANCHOR_DIRECTORY_NAME = "head-anchors-v1";
60
+ const LOCK_TARGET_NAME = "ledger-v2";
61
+ const DIGEST_PATTERN = /^sha256:[a-f0-9]{64}$/u;
62
+ const IDENTIFIER_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._:/-]{0,159}$/u;
63
+ const LOWER_IDENTIFIER_PATTERN = /^[a-z][a-z0-9]*(?:[._:/-][a-z0-9]+)*$/u;
64
+ const SKILL_NAME_PATTERN = /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u;
65
+ const URI_PATTERN = /^[a-z][a-z0-9+.-]{1,31}:[^\s\\]+$/u;
66
+ const SIGNATURE_ALGORITHM_PATTERN = /^[a-z0-9][a-z0-9._+-]{0,63}$/u;
67
+ const SIGNATURE_VALUE_PATTERN = /^[A-Za-z0-9_-]{22,8192}$/u;
68
+ const RANDOM_TOKEN_PATTERN = /^[A-Za-z0-9-]{16,128}$/u;
69
+ const SEGMENT_FILE_PATTERN = /^(\d{12})-([a-f0-9]{64})\.json$/u;
70
+ const ANCHOR_FILE_PATTERN = /^(\d{12})-([a-f0-9]{64})\.json$/u;
71
+ const STAGE_FILE_PATTERN = /^\.stage-(.+)\.([A-Za-z0-9-]{16,128})\.tmp$/u;
72
+ const HEAD_STAGE_FILE_PATTERN =
73
+ /^\.replace-head-v1\.json\.([A-Za-z0-9-]{16,128})\.tmp$/u;
74
+ const MAX_SOURCE_REFS = 256;
75
+ const TYPED_ARRAY_PROTOTYPE = Object.getPrototypeOf(Uint8Array.prototype);
76
+ const TYPED_ARRAY_BUFFER_GETTER = Object.getOwnPropertyDescriptor(
77
+ TYPED_ARRAY_PROTOTYPE,
78
+ "buffer",
79
+ ).get;
80
+ const TYPED_ARRAY_BYTE_LENGTH_GETTER = Object.getOwnPropertyDescriptor(
81
+ TYPED_ARRAY_PROTOTYPE,
82
+ "byteLength",
83
+ ).get;
84
+ const TYPED_ARRAY_BYTE_OFFSET_GETTER = Object.getOwnPropertyDescriptor(
85
+ TYPED_ARRAY_PROTOTYPE,
86
+ "byteOffset",
87
+ ).get;
88
+
89
+ const DERIVATION_MODES = new Set([
90
+ "wiki",
91
+ "record-replay",
92
+ "manual-import",
93
+ "legacy-migration",
94
+ "system",
95
+ ]);
96
+ const DECISIONS = new Set([
97
+ "proposed",
98
+ "prepared",
99
+ "accepted",
100
+ "rejected",
101
+ "validated",
102
+ "committed",
103
+ "promoted",
104
+ "rolled-back",
105
+ "quarantined",
106
+ "revoked",
107
+ "failed",
108
+ ]);
109
+ const REVOCATION_STATES = new Set(["not-revoked", "revoked", "tombstoned"]);
110
+ const DURABILITY_MECHANISMS = new Set([
111
+ "authenticated-witness-cas",
112
+ "directory-fsync",
113
+ "verified-existing",
114
+ ]);
115
+ const EMPTY_DISCARD_ACCUMULATOR_DIGEST = domainDigest(
116
+ DISCARD_ACCUMULATOR_DOMAIN,
117
+ [],
118
+ );
119
+
120
+ const ARTIFACT_REF_KEYS = new Set(["digest", "ref", "schema"]);
121
+ const RESOLUTION_KEYS = new Set([
122
+ "authenticated",
123
+ "bytes",
124
+ "digest",
125
+ "found",
126
+ "receiptDigest",
127
+ "ref",
128
+ "schema",
129
+ ]);
130
+ const TRUST_KEYS = new Set(["algorithm", "keyId", "trustPolicyDigest"]);
131
+ const SIGNATURE_KEYS = new Set([
132
+ "algorithm",
133
+ "keyId",
134
+ "trustPolicyDigest",
135
+ "value",
136
+ ]);
137
+ const WITNESS_PORT_KEYS = new Set([
138
+ "compareAndSwap",
139
+ "id",
140
+ "initialize",
141
+ "proveAncestry",
142
+ "read",
143
+ ]);
144
+ const APPEND_INPUT_KEYS = new Set([
145
+ "actorRef",
146
+ "candidateRef",
147
+ "decision",
148
+ "derivationMode",
149
+ "diffRef",
150
+ "evalRef",
151
+ "eventId",
152
+ "parentRef",
153
+ "policyRef",
154
+ "reason",
155
+ "revocationState",
156
+ "runId",
157
+ "skillName",
158
+ "sourceRefs",
159
+ "targetRef",
160
+ "tenantId",
161
+ "timestamp",
162
+ "type",
163
+ ]);
164
+ const DOMAIN_APPEND_INPUT_KEYS = new Set([
165
+ "artifactTenantId",
166
+ "correlationId",
167
+ "decision",
168
+ "eventId",
169
+ "reason",
170
+ "skillName",
171
+ "sourceRefs",
172
+ "subjectRef",
173
+ "tenantId",
174
+ "timestamp",
175
+ "type",
176
+ ]);
177
+ const APPEND_OPTION_KEYS = new Set(["expectedHeadDigest", "expectedSequence"]);
178
+ const EVENT_CORE_KEYS = new Set([
179
+ ...APPEND_INPUT_KEYS,
180
+ "algorithm",
181
+ "artifactValidationDigest",
182
+ "epoch",
183
+ "identityDigest",
184
+ "keyId",
185
+ "ledgerId",
186
+ "prevDigest",
187
+ "schema",
188
+ "sequence",
189
+ "trustPolicyDigest",
190
+ "witnessAlgorithm",
191
+ "witnessId",
192
+ "witnessKeyId",
193
+ "witnessTrustPolicyDigest",
194
+ ]);
195
+ const EVENT_RECORD_KEYS = new Set([
196
+ ...EVENT_CORE_KEYS,
197
+ "eventDigest",
198
+ "signature",
199
+ ]);
200
+ const DOMAIN_EVENT_CORE_KEYS = new Set([
201
+ ...DOMAIN_APPEND_INPUT_KEYS,
202
+ "algorithm",
203
+ "artifactValidationDigest",
204
+ "epoch",
205
+ "identityDigest",
206
+ "keyId",
207
+ "ledgerId",
208
+ "prevDigest",
209
+ "schema",
210
+ "sequence",
211
+ "trustPolicyDigest",
212
+ "witnessAlgorithm",
213
+ "witnessId",
214
+ "witnessKeyId",
215
+ "witnessTrustPolicyDigest",
216
+ ]);
217
+ const DOMAIN_EVENT_RECORD_KEYS = new Set([
218
+ ...DOMAIN_EVENT_CORE_KEYS,
219
+ "eventDigest",
220
+ "signature",
221
+ ]);
222
+ const IDENTITY_CORE_KEYS = new Set([
223
+ "algorithm",
224
+ "createdAt",
225
+ "epoch",
226
+ "keyId",
227
+ "ledgerId",
228
+ "schema",
229
+ "storeBindingDigest",
230
+ "storeMarkerDigest",
231
+ "storeMarkerEntryDigest",
232
+ "storeMarkerId",
233
+ "trustPolicyDigest",
234
+ "witnessAlgorithm",
235
+ "witnessId",
236
+ "witnessKeyId",
237
+ "witnessTrustPolicyDigest",
238
+ ]);
239
+ const IDENTITY_RECORD_KEYS = new Set([
240
+ ...IDENTITY_CORE_KEYS,
241
+ "identityDigest",
242
+ "signature",
243
+ ]);
244
+ const ANCHOR_CORE_KEYS = new Set([
245
+ "algorithm",
246
+ "committedAt",
247
+ "epoch",
248
+ "headDigest",
249
+ "identityDigest",
250
+ "keyId",
251
+ "ledgerId",
252
+ "previousAnchorDigest",
253
+ "schema",
254
+ "segmentDigest",
255
+ "sequence",
256
+ "trustPolicyDigest",
257
+ "witnessAlgorithm",
258
+ "witnessId",
259
+ "witnessKeyId",
260
+ "witnessTrustPolicyDigest",
261
+ ]);
262
+ const ANCHOR_RECORD_KEYS = new Set([
263
+ ...ANCHOR_CORE_KEYS,
264
+ "anchorDigest",
265
+ "signature",
266
+ ]);
267
+ const RECEIPT_CORE_KEYS = new Set([
268
+ "algorithm",
269
+ "anchorDigest",
270
+ "authenticated",
271
+ "committed",
272
+ "committedAt",
273
+ "durabilityMechanism",
274
+ "durable",
275
+ "epoch",
276
+ "eventDigest",
277
+ "eventId",
278
+ "headDigest",
279
+ "headSignature",
280
+ "identityDigest",
281
+ "issuedAt",
282
+ "keyId",
283
+ "ledgerId",
284
+ "persisted",
285
+ "schema",
286
+ "segmentDigest",
287
+ "sequence",
288
+ "trustPolicyDigest",
289
+ "witnessAlgorithm",
290
+ "witnessCheckpoint",
291
+ "witnessDigest",
292
+ "witnessId",
293
+ "witnessKeyId",
294
+ "witnessTrustPolicyDigest",
295
+ ]);
296
+ const RECEIPT_RECORD_KEYS = new Set([
297
+ ...RECEIPT_CORE_KEYS,
298
+ "receiptDigest",
299
+ "signature",
300
+ ]);
301
+ const WITNESS_CORE_KEYS = new Set([
302
+ "algorithm",
303
+ "anchorDigest",
304
+ "authenticated",
305
+ "durable",
306
+ "discardAccumulatorDigest",
307
+ "epoch",
308
+ "headDigest",
309
+ "identityDigest",
310
+ "keyId",
311
+ "ledgerId",
312
+ "generation",
313
+ "payloadDigest",
314
+ "previousWitnessDigest",
315
+ "schema",
316
+ "segmentDigest",
317
+ "sequence",
318
+ "status",
319
+ "storeMarkerDigest",
320
+ "storeMarkerEntryDigest",
321
+ "storeMarkerId",
322
+ "trustPolicyDigest",
323
+ "witnessId",
324
+ ]);
325
+ const WITNESS_RECORD_KEYS = new Set([
326
+ ...WITNESS_CORE_KEYS,
327
+ "signature",
328
+ "witnessDigest",
329
+ ]);
330
+ const WITNESS_ANCESTRY_CORE_KEYS = new Set([
331
+ "algorithm",
332
+ "ancestorDigest",
333
+ "ancestorGeneration",
334
+ "authenticated",
335
+ "descendantDigest",
336
+ "descendantGeneration",
337
+ "durable",
338
+ "epoch",
339
+ "included",
340
+ "keyId",
341
+ "ledgerId",
342
+ "schema",
343
+ "trustPolicyDigest",
344
+ "witnessId",
345
+ ]);
346
+ const WITNESS_ANCESTRY_RECORD_KEYS = new Set([
347
+ ...WITNESS_ANCESTRY_CORE_KEYS,
348
+ "proofDigest",
349
+ "signature",
350
+ ]);
351
+ const STORE_MARKER_CORE_KEYS = new Set([
352
+ "algorithm",
353
+ "createdAt",
354
+ "epoch",
355
+ "keyId",
356
+ "ledgerId",
357
+ "schema",
358
+ "storeBindingDigest",
359
+ "storeMarkerId",
360
+ "trustPolicyDigest",
361
+ "witnessAlgorithm",
362
+ "witnessId",
363
+ "witnessKeyId",
364
+ "witnessTrustPolicyDigest",
365
+ ]);
366
+ const STORE_MARKER_RECORD_KEYS = new Set([
367
+ ...STORE_MARKER_CORE_KEYS,
368
+ "signature",
369
+ "storeMarkerDigest",
370
+ ]);
371
+ const QUERY_SELECTOR_KEYS = new Set(["eventDigest", "eventId", "sequence"]);
372
+ const QUERY_OPTION_KEYS = new Set(["issueReceipt"]);
373
+ const VERIFY_RECEIPT_OPTION_KEYS = new Set(["requireCurrentHead"]);
374
+
375
+ const REQUIRED_FS_METHODS = Object.freeze([
376
+ "closeSync",
377
+ "fstatSync",
378
+ "fsyncSync",
379
+ "linkSync",
380
+ "lstatSync",
381
+ "mkdirSync",
382
+ "openSync",
383
+ "readFileSync",
384
+ "readdirSync",
385
+ "realpathSync",
386
+ "renameSync",
387
+ "rmSync",
388
+ "statSync",
389
+ "unlinkSync",
390
+ "writeFileSync",
391
+ ]);
392
+
393
+ export class EvolutionLedgerError extends Error {
394
+ constructor(code, message, details = {}) {
395
+ super(message, details.cause ? { cause: details.cause } : undefined);
396
+ this.name = "EvolutionLedgerError";
397
+ this.code = code;
398
+ for (const [key, value] of Object.entries(details)) {
399
+ if (key !== "cause") this[key] = value;
400
+ }
401
+ }
402
+ }
403
+
404
+ function ledgerError(code, message, details = {}) {
405
+ return new EvolutionLedgerError(code, message, details);
406
+ }
407
+
408
+ function isPlainObject(value) {
409
+ if (
410
+ !value ||
411
+ typeof value !== "object" ||
412
+ utilTypes.isProxy(value) ||
413
+ Array.isArray(value)
414
+ ) {
415
+ return false;
416
+ }
417
+ const prototype = Object.getPrototypeOf(value);
418
+ return prototype === Object.prototype || prototype === null;
419
+ }
420
+
421
+ function assertPlainDataObject(value, label) {
422
+ if (!isPlainObject(value)) {
423
+ throw ledgerError(
424
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
425
+ `${label} must be a plain object`,
426
+ );
427
+ }
428
+ for (const key of Reflect.ownKeys(value)) {
429
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
430
+ if (typeof key !== "string" || !descriptor || !("value" in descriptor)) {
431
+ throw ledgerError(
432
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
433
+ `${label} must contain only own data properties`,
434
+ );
435
+ }
436
+ }
437
+ }
438
+
439
+ function safeOwnDataValue(value, key) {
440
+ if (!value || typeof value !== "object" || utilTypes.isProxy(value)) {
441
+ return null;
442
+ }
443
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
444
+ return descriptor &&
445
+ "value" in descriptor &&
446
+ typeof descriptor.value === "string"
447
+ ? descriptor.value
448
+ : null;
449
+ }
450
+
451
+ function isUnsafeAsyncResult(value) {
452
+ if (!value || (typeof value !== "object" && typeof value !== "function")) {
453
+ return false;
454
+ }
455
+ if (utilTypes.isProxy(value) || utilTypes.isPromise(value)) return true;
456
+ const descriptor = Object.getOwnPropertyDescriptor(value, "then");
457
+ return Boolean(
458
+ descriptor &&
459
+ (!("value" in descriptor) || typeof descriptor.value === "function"),
460
+ );
461
+ }
462
+
463
+ function assertExactKeys(value, keys, label, { optional = [] } = {}) {
464
+ assertPlainDataObject(value, label);
465
+ const optionalKeys = new Set(optional);
466
+ const actual = Reflect.ownKeys(value);
467
+ if (
468
+ actual.some((key) => typeof key !== "string" || !keys.has(key)) ||
469
+ [...keys].some(
470
+ (key) => !optionalKeys.has(key) && !Object.hasOwn(value, key),
471
+ )
472
+ ) {
473
+ throw ledgerError(
474
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
475
+ `${label} has missing or unsupported fields`,
476
+ );
477
+ }
478
+ }
479
+
480
+ function readDenseDataArray(value, label, maximum) {
481
+ if (
482
+ !value ||
483
+ typeof value !== "object" ||
484
+ utilTypes.isProxy(value) ||
485
+ !Array.isArray(value)
486
+ ) {
487
+ throw ledgerError(
488
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
489
+ `${label} must be an array`,
490
+ );
491
+ }
492
+ const prototype = Object.getPrototypeOf(value);
493
+ if (prototype !== Array.prototype && prototype !== null) {
494
+ throw ledgerError(
495
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
496
+ `${label} must use a safe array prototype`,
497
+ );
498
+ }
499
+ const lengthDescriptor = Object.getOwnPropertyDescriptor(value, "length");
500
+ const length =
501
+ lengthDescriptor && "value" in lengthDescriptor
502
+ ? lengthDescriptor.value
503
+ : null;
504
+ if (
505
+ !Number.isSafeInteger(maximum) ||
506
+ maximum < 0 ||
507
+ !Number.isSafeInteger(length) ||
508
+ length < 0 ||
509
+ length > maximum
510
+ ) {
511
+ throw ledgerError(
512
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
513
+ `${label} exceeds its maximum length of ${maximum}`,
514
+ );
515
+ }
516
+ const allowed = new Set(["length"]);
517
+ for (let index = 0; index < length; index += 1) {
518
+ allowed.add(String(index));
519
+ }
520
+ const actualKeys = Reflect.ownKeys(value);
521
+ for (const key of actualKeys) {
522
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
523
+ if (
524
+ typeof key !== "string" ||
525
+ !allowed.has(key) ||
526
+ !descriptor ||
527
+ !("value" in descriptor)
528
+ ) {
529
+ throw ledgerError(
530
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
531
+ `${label} must be a dense array with only data entries`,
532
+ );
533
+ }
534
+ }
535
+ const entries = new Array(length);
536
+ for (let index = 0; index < length; index += 1) {
537
+ const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
538
+ if (!descriptor || !("value" in descriptor)) {
539
+ throw ledgerError(
540
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
541
+ `${label} must not contain sparse or accessor entries`,
542
+ );
543
+ }
544
+ entries[index] = descriptor.value;
545
+ }
546
+ return entries;
547
+ }
548
+
549
+ function canonicalJson(value) {
550
+ if (value === null || typeof value !== "object") {
551
+ return JSON.stringify(value);
552
+ }
553
+ if (utilTypes.isProxy(value)) {
554
+ throw ledgerError(
555
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
556
+ "canonical JSON must not contain proxies",
557
+ );
558
+ }
559
+ if (Array.isArray(value)) {
560
+ const entries = readDenseDataArray(
561
+ value,
562
+ "canonical JSON array",
563
+ EVOLUTION_LEDGER_MAX_EVENTS,
564
+ );
565
+ const encoded = new Array(entries.length);
566
+ for (let index = 0; index < entries.length; index += 1) {
567
+ encoded[index] = canonicalJson(entries[index]);
568
+ }
569
+ return `[${encoded.join(",")}]`;
570
+ }
571
+ assertPlainDataObject(value, "canonical JSON object");
572
+ const keys = Reflect.ownKeys(value).sort(compareStrings);
573
+ const encoded = new Array(keys.length);
574
+ for (let index = 0; index < keys.length; index += 1) {
575
+ const key = keys[index];
576
+ const descriptor = Object.getOwnPropertyDescriptor(value, key);
577
+ encoded[index] =
578
+ `${JSON.stringify(key)}:${canonicalJson(descriptor.value)}`;
579
+ }
580
+ return `{${encoded.join(",")}}`;
581
+ }
582
+
583
+ function clone(value) {
584
+ return value == null ? value : JSON.parse(JSON.stringify(value));
585
+ }
586
+
587
+ function deepFreeze(value) {
588
+ if (!value || typeof value !== "object" || Object.isFrozen(value)) {
589
+ return value;
590
+ }
591
+ for (const child of Object.values(value)) deepFreeze(child);
592
+ return Object.freeze(value);
593
+ }
594
+
595
+ function frozenClone(value) {
596
+ return deepFreeze(clone(value));
597
+ }
598
+
599
+ function sha256(bytes) {
600
+ return `sha256:${crypto.createHash("sha256").update(bytes).digest("hex")}`;
601
+ }
602
+
603
+ function copyResolvedBytes(value) {
604
+ if (!value || typeof value !== "object" || utilTypes.isProxy(value)) {
605
+ throw ledgerError(
606
+ "CC_EVOLUTION_LEDGER_ARTIFACT_INVALID",
607
+ "artifact bytes must be a non-proxy Buffer or Uint8Array",
608
+ );
609
+ }
610
+ const prototype = Object.getPrototypeOf(value);
611
+ const safeBuffer = prototype === Buffer.prototype && Buffer.isBuffer(value);
612
+ const safeUint8Array =
613
+ prototype === Uint8Array.prototype && utilTypes.isUint8Array(value);
614
+ if (!safeBuffer && !safeUint8Array) {
615
+ throw ledgerError(
616
+ "CC_EVOLUTION_LEDGER_ARTIFACT_INVALID",
617
+ "artifact bytes must use a safe Buffer or Uint8Array prototype",
618
+ );
619
+ }
620
+ try {
621
+ const arrayBuffer = Reflect.apply(TYPED_ARRAY_BUFFER_GETTER, value, []);
622
+ const byteLength = Reflect.apply(TYPED_ARRAY_BYTE_LENGTH_GETTER, value, []);
623
+ const byteOffset = Reflect.apply(TYPED_ARRAY_BYTE_OFFSET_GETTER, value, []);
624
+ if (
625
+ !Number.isSafeInteger(byteLength) ||
626
+ byteLength < 0 ||
627
+ byteLength > EVOLUTION_LEDGER_MAX_ARTIFACT_BYTES ||
628
+ !Number.isSafeInteger(byteOffset) ||
629
+ byteOffset < 0
630
+ ) {
631
+ throw new TypeError("artifact byte view bounds are invalid");
632
+ }
633
+ return Buffer.from(Buffer.from(arrayBuffer, byteOffset, byteLength));
634
+ } catch (cause) {
635
+ throw ledgerError(
636
+ "CC_EVOLUTION_LEDGER_ARTIFACT_INVALID",
637
+ "artifact bytes could not be safely copied",
638
+ { cause },
639
+ );
640
+ }
641
+ }
642
+
643
+ function domainDigest(domain, value) {
644
+ return sha256(
645
+ Buffer.concat([
646
+ Buffer.from(domain, "utf8"),
647
+ Buffer.from(canonicalJson(value), "utf8"),
648
+ ]),
649
+ );
650
+ }
651
+
652
+ function signedMessage(domain, core) {
653
+ return Buffer.concat([
654
+ Buffer.from(domain, "utf8"),
655
+ Buffer.from(canonicalJson(core), "utf8"),
656
+ ]);
657
+ }
658
+
659
+ function compareStrings(left, right) {
660
+ if (left < right) return -1;
661
+ if (left > right) return 1;
662
+ return 0;
663
+ }
664
+
665
+ function boundedString(value, label, maximum) {
666
+ if (
667
+ typeof value !== "string" ||
668
+ value.length < 1 ||
669
+ value.length > maximum ||
670
+ value.trim() !== value ||
671
+ [...value].some((character) => {
672
+ const codePoint = character.codePointAt(0);
673
+ return codePoint < 0x20 || codePoint === 0x7f;
674
+ })
675
+ ) {
676
+ throw ledgerError(
677
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
678
+ `${label} must be a non-empty bounded string without control characters`,
679
+ );
680
+ }
681
+ return value;
682
+ }
683
+
684
+ function identifier(value, label) {
685
+ const normalized = boundedString(value, label, 160);
686
+ if (!IDENTIFIER_PATTERN.test(normalized)) {
687
+ throw ledgerError(
688
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
689
+ `${label} is not a valid identifier`,
690
+ );
691
+ }
692
+ return normalized;
693
+ }
694
+
695
+ function lowerIdentifier(value, label) {
696
+ const normalized = boundedString(value, label, 160);
697
+ if (!LOWER_IDENTIFIER_PATTERN.test(normalized)) {
698
+ throw ledgerError(
699
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
700
+ `${label} must be a lowercase namespaced identifier`,
701
+ );
702
+ }
703
+ return normalized;
704
+ }
705
+
706
+ function digest(value, label, { nullable = false } = {}) {
707
+ if (nullable && value == null) return null;
708
+ if (typeof value !== "string" || !DIGEST_PATTERN.test(value)) {
709
+ throw ledgerError(
710
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
711
+ `${label} must be a lowercase sha256 digest${nullable ? " or null" : ""}`,
712
+ );
713
+ }
714
+ return value;
715
+ }
716
+
717
+ function safeSequence(value, label, { allowZero = false } = {}) {
718
+ if (
719
+ !Number.isSafeInteger(value) ||
720
+ value < (allowZero ? 0 : 1) ||
721
+ value > EVOLUTION_LEDGER_MAX_EVENTS
722
+ ) {
723
+ throw ledgerError(
724
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
725
+ `${label} must be a ${allowZero ? "non-negative" : "positive"} bounded safe integer`,
726
+ );
727
+ }
728
+ return value;
729
+ }
730
+
731
+ function safeCounter(value, label) {
732
+ if (!Number.isSafeInteger(value) || value < 0) {
733
+ throw ledgerError(
734
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
735
+ `${label} must be a non-negative safe integer`,
736
+ );
737
+ }
738
+ return value;
739
+ }
740
+
741
+ function canonicalTimestamp(value, label) {
742
+ const normalized = boundedString(value, label, 64);
743
+ let canonical;
744
+ try {
745
+ canonical = new Date(normalized).toISOString();
746
+ } catch {
747
+ canonical = null;
748
+ }
749
+ if (canonical !== normalized) {
750
+ throw ledgerError(
751
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
752
+ `${label} must be a canonical ISO-8601 timestamp`,
753
+ );
754
+ }
755
+ return normalized;
756
+ }
757
+
758
+ function clockTimestamp(clock) {
759
+ try {
760
+ return new Date(clock()).toISOString();
761
+ } catch (cause) {
762
+ throw ledgerError(
763
+ "CC_EVOLUTION_LEDGER_CLOCK_FAILED",
764
+ "evolution ledger clock did not return a valid instant",
765
+ { cause, commitState: "not-committed" },
766
+ );
767
+ }
768
+ }
769
+
770
+ function normalizeTrust(value) {
771
+ assertExactKeys(value, TRUST_KEYS, "trust");
772
+ const algorithm = boundedString(value.algorithm, "trust.algorithm", 64);
773
+ if (!SIGNATURE_ALGORITHM_PATTERN.test(algorithm)) {
774
+ throw ledgerError(
775
+ "CC_EVOLUTION_LEDGER_TRUST_INVALID",
776
+ "trust.algorithm is invalid",
777
+ );
778
+ }
779
+ const keyId = boundedString(value.keyId, "trust.keyId", 256);
780
+ if (!URI_PATTERN.test(keyId)) {
781
+ throw ledgerError(
782
+ "CC_EVOLUTION_LEDGER_TRUST_INVALID",
783
+ "trust.keyId must be an absolute opaque key URI",
784
+ );
785
+ }
786
+ return deepFreeze({
787
+ algorithm,
788
+ keyId,
789
+ trustPolicyDigest: digest(
790
+ value.trustPolicyDigest,
791
+ "trust.trustPolicyDigest",
792
+ ),
793
+ });
794
+ }
795
+
796
+ function normalizeSignature(value, trust) {
797
+ assertExactKeys(value, SIGNATURE_KEYS, "signature");
798
+ const normalized = {
799
+ algorithm: boundedString(value.algorithm, "signature.algorithm", 64),
800
+ keyId: boundedString(value.keyId, "signature.keyId", 256),
801
+ trustPolicyDigest: digest(
802
+ value.trustPolicyDigest,
803
+ "signature.trustPolicyDigest",
804
+ ),
805
+ value: boundedString(value.value, "signature.value", 8192),
806
+ };
807
+ if (
808
+ normalized.algorithm !== trust.algorithm ||
809
+ normalized.keyId !== trust.keyId ||
810
+ normalized.trustPolicyDigest !== trust.trustPolicyDigest ||
811
+ !SIGNATURE_ALGORITHM_PATTERN.test(normalized.algorithm) ||
812
+ !URI_PATTERN.test(normalized.keyId) ||
813
+ !SIGNATURE_VALUE_PATTERN.test(normalized.value)
814
+ ) {
815
+ throw ledgerError(
816
+ "CC_EVOLUTION_LEDGER_SIGNATURE_INVALID",
817
+ "signature header is not bound to the configured trust policy",
818
+ );
819
+ }
820
+ return normalized;
821
+ }
822
+
823
+ function normalizeArtifactRef(value, label, { nullable = false } = {}) {
824
+ if (nullable && value == null) return null;
825
+ assertExactKeys(value, ARTIFACT_REF_KEYS, label);
826
+ if (value.schema !== EVOLUTION_ARTIFACT_REF_SCHEMA) {
827
+ throw ledgerError(
828
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
829
+ `${label}.schema is unsupported`,
830
+ );
831
+ }
832
+ const ref = boundedString(value.ref, `${label}.ref`, 2048);
833
+ if (!URI_PATTERN.test(ref)) {
834
+ throw ledgerError(
835
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
836
+ `${label}.ref must be an absolute opaque URI without backslashes`,
837
+ );
838
+ }
839
+ return {
840
+ digest: digest(value.digest, `${label}.digest`),
841
+ ref,
842
+ schema: EVOLUTION_ARTIFACT_REF_SCHEMA,
843
+ };
844
+ }
845
+
846
+ function normalizeSourceRefs(
847
+ value,
848
+ { minimum = 1, uniqueByDigest = false, uniqueByRef = false } = {},
849
+ ) {
850
+ const entries = readDenseDataArray(value, "sourceRefs", MAX_SOURCE_REFS);
851
+ if (entries.length < minimum) {
852
+ throw ledgerError(
853
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
854
+ `sourceRefs must contain at least ${minimum} artifact reference${minimum === 1 ? "" : "s"}`,
855
+ );
856
+ }
857
+ const refs = new Array(entries.length);
858
+ for (let index = 0; index < entries.length; index += 1) {
859
+ refs[index] = normalizeArtifactRef(entries[index], `sourceRefs[${index}]`);
860
+ }
861
+ refs.sort(
862
+ (left, right) =>
863
+ compareStrings(left.ref, right.ref) ||
864
+ compareStrings(left.digest, right.digest),
865
+ );
866
+ const identities = new Array(refs.length);
867
+ const locators = new Array(refs.length);
868
+ const digests = new Array(refs.length);
869
+ for (let index = 0; index < refs.length; index += 1) {
870
+ identities[index] = `${refs[index].ref}\0${refs[index].digest}`;
871
+ locators[index] = refs[index].ref;
872
+ digests[index] = refs[index].digest;
873
+ }
874
+ if (
875
+ new Set(identities).size !== identities.length ||
876
+ (uniqueByRef && new Set(locators).size !== locators.length) ||
877
+ (uniqueByDigest && new Set(digests).size !== digests.length)
878
+ ) {
879
+ throw ledgerError(
880
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
881
+ "sourceRefs must not contain duplicates",
882
+ );
883
+ }
884
+ return refs;
885
+ }
886
+
887
+ function normalizeSkillName(value) {
888
+ const normalized = boundedString(value, "skillName", 128);
889
+ if (!SKILL_NAME_PATTERN.test(normalized)) {
890
+ throw ledgerError(
891
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
892
+ "skillName must use kebab-case",
893
+ );
894
+ }
895
+ return normalized;
896
+ }
897
+
898
+ function normalizeNullableIdentifier(value, label) {
899
+ return value === null ? null : identifier(value, label);
900
+ }
901
+
902
+ function normalizeNullableSkillName(value) {
903
+ return value === null ? null : normalizeSkillName(value);
904
+ }
905
+
906
+ function normalizeEnum(value, label, allowed) {
907
+ const normalized = boundedString(value, label, 32);
908
+ if (!allowed.has(normalized)) {
909
+ throw ledgerError(
910
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
911
+ `${label} is unsupported`,
912
+ );
913
+ }
914
+ return normalized;
915
+ }
916
+
917
+ function normalizeAppendInput(value, generatedTimestamp) {
918
+ assertExactKeys(value, APPEND_INPUT_KEYS, "event input", {
919
+ optional: ["timestamp"],
920
+ });
921
+ return {
922
+ actorRef: normalizeArtifactRef(value.actorRef, "actorRef"),
923
+ candidateRef: normalizeArtifactRef(value.candidateRef, "candidateRef"),
924
+ decision: normalizeEnum(value.decision, "decision", DECISIONS),
925
+ derivationMode: normalizeEnum(
926
+ value.derivationMode,
927
+ "derivationMode",
928
+ DERIVATION_MODES,
929
+ ),
930
+ diffRef: normalizeArtifactRef(value.diffRef, "diffRef"),
931
+ evalRef: normalizeArtifactRef(value.evalRef, "evalRef"),
932
+ eventId: identifier(value.eventId, "eventId"),
933
+ parentRef: normalizeArtifactRef(value.parentRef, "parentRef", {
934
+ nullable: true,
935
+ }),
936
+ policyRef: normalizeArtifactRef(value.policyRef, "policyRef"),
937
+ reason: boundedString(value.reason, "reason", 4096),
938
+ revocationState: normalizeEnum(
939
+ value.revocationState,
940
+ "revocationState",
941
+ REVOCATION_STATES,
942
+ ),
943
+ runId: identifier(value.runId, "runId"),
944
+ skillName: normalizeSkillName(value.skillName),
945
+ sourceRefs: normalizeSourceRefs(value.sourceRefs),
946
+ targetRef: normalizeArtifactRef(value.targetRef, "targetRef"),
947
+ tenantId: identifier(value.tenantId, "tenantId"),
948
+ timestamp: canonicalTimestamp(
949
+ Object.hasOwn(value, "timestamp") ? value.timestamp : generatedTimestamp,
950
+ "timestamp",
951
+ ),
952
+ type: lowerIdentifier(value.type, "type"),
953
+ };
954
+ }
955
+
956
+ function normalizeDomainAppendInput(value, generatedTimestamp) {
957
+ assertExactKeys(value, DOMAIN_APPEND_INPUT_KEYS, "domain event input", {
958
+ optional: ["timestamp"],
959
+ });
960
+ const subjectRef = normalizeArtifactRef(value.subjectRef, "subjectRef");
961
+ const sourceRefs = normalizeSourceRefs(value.sourceRefs, {
962
+ minimum: 0,
963
+ uniqueByDigest: true,
964
+ uniqueByRef: true,
965
+ });
966
+ if (
967
+ sourceRefs.some(
968
+ (sourceRef) =>
969
+ sourceRef.ref === subjectRef.ref ||
970
+ sourceRef.digest === subjectRef.digest,
971
+ )
972
+ ) {
973
+ throw ledgerError(
974
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
975
+ "subjectRef must not be aliased by ref or digest in sourceRefs",
976
+ );
977
+ }
978
+ return {
979
+ artifactTenantId: identifier(value.artifactTenantId, "artifactTenantId"),
980
+ correlationId: normalizeNullableIdentifier(
981
+ value.correlationId,
982
+ "correlationId",
983
+ ),
984
+ decision: normalizeEnum(value.decision, "decision", DECISIONS),
985
+ eventId: identifier(value.eventId, "eventId"),
986
+ reason: boundedString(value.reason, "reason", 4096),
987
+ skillName: normalizeNullableSkillName(value.skillName),
988
+ sourceRefs,
989
+ subjectRef,
990
+ tenantId: normalizeNullableIdentifier(value.tenantId, "tenantId"),
991
+ timestamp: canonicalTimestamp(
992
+ Object.hasOwn(value, "timestamp") ? value.timestamp : generatedTimestamp,
993
+ "timestamp",
994
+ ),
995
+ type: lowerIdentifier(value.type, "type"),
996
+ };
997
+ }
998
+
999
+ function normalizeAppendOptions(value) {
1000
+ assertExactKeys(value, APPEND_OPTION_KEYS, "append options", {
1001
+ optional: [...APPEND_OPTION_KEYS],
1002
+ });
1003
+ const expectedSequence = Object.hasOwn(value, "expectedSequence")
1004
+ ? value.expectedSequence
1005
+ : undefined;
1006
+ if (
1007
+ expectedSequence !== undefined &&
1008
+ (!Number.isSafeInteger(expectedSequence) || expectedSequence < 0)
1009
+ ) {
1010
+ throw ledgerError(
1011
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
1012
+ "expectedSequence must be a non-negative safe integer",
1013
+ );
1014
+ }
1015
+ const expectedHeadDigest = Object.hasOwn(value, "expectedHeadDigest")
1016
+ ? digest(value.expectedHeadDigest, "expectedHeadDigest", { nullable: true })
1017
+ : undefined;
1018
+ return { expectedHeadDigest, expectedSequence };
1019
+ }
1020
+
1021
+ function normalizeQuerySelector(value) {
1022
+ assertExactKeys(value, QUERY_SELECTOR_KEYS, "query selector", {
1023
+ optional: [...QUERY_SELECTOR_KEYS],
1024
+ });
1025
+ const selector = {
1026
+ eventDigest: Object.hasOwn(value, "eventDigest")
1027
+ ? digest(value.eventDigest, "eventDigest")
1028
+ : undefined,
1029
+ eventId: Object.hasOwn(value, "eventId")
1030
+ ? identifier(value.eventId, "eventId")
1031
+ : undefined,
1032
+ sequence: Object.hasOwn(value, "sequence")
1033
+ ? safeSequence(value.sequence, "sequence")
1034
+ : undefined,
1035
+ };
1036
+ if (Object.values(selector).every((entry) => entry === undefined)) {
1037
+ throw ledgerError(
1038
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
1039
+ "query selector requires eventId, eventDigest, or sequence",
1040
+ );
1041
+ }
1042
+ return selector;
1043
+ }
1044
+
1045
+ function normalizeEventCore(value) {
1046
+ assertExactKeys(value, EVENT_CORE_KEYS, "event core");
1047
+ if (value.schema !== EVOLUTION_LEDGER_EVENT_SCHEMA) {
1048
+ throw ledgerError(
1049
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
1050
+ "event schema is unsupported",
1051
+ );
1052
+ }
1053
+ const normalizedInput = normalizeAppendInput(
1054
+ Object.fromEntries([...APPEND_INPUT_KEYS].map((key) => [key, value[key]])),
1055
+ value.timestamp,
1056
+ );
1057
+ return {
1058
+ ...normalizedInput,
1059
+ algorithm: boundedString(value.algorithm, "algorithm", 64),
1060
+ artifactValidationDigest: digest(
1061
+ value.artifactValidationDigest,
1062
+ "artifactValidationDigest",
1063
+ ),
1064
+ epoch: identifier(value.epoch, "epoch"),
1065
+ identityDigest: digest(value.identityDigest, "identityDigest"),
1066
+ keyId: boundedString(value.keyId, "keyId", 256),
1067
+ ledgerId: identifier(value.ledgerId, "ledgerId"),
1068
+ prevDigest: digest(value.prevDigest, "prevDigest", { nullable: true }),
1069
+ schema: EVOLUTION_LEDGER_EVENT_SCHEMA,
1070
+ sequence: safeSequence(value.sequence, "sequence"),
1071
+ trustPolicyDigest: digest(value.trustPolicyDigest, "trustPolicyDigest"),
1072
+ witnessAlgorithm: boundedString(
1073
+ value.witnessAlgorithm,
1074
+ "witnessAlgorithm",
1075
+ 64,
1076
+ ),
1077
+ witnessId: identifier(value.witnessId, "witnessId"),
1078
+ witnessKeyId: boundedString(value.witnessKeyId, "witnessKeyId", 256),
1079
+ witnessTrustPolicyDigest: digest(
1080
+ value.witnessTrustPolicyDigest,
1081
+ "witnessTrustPolicyDigest",
1082
+ ),
1083
+ };
1084
+ }
1085
+
1086
+ function normalizeDomainEventCore(value) {
1087
+ assertExactKeys(value, DOMAIN_EVENT_CORE_KEYS, "domain event core");
1088
+ if (value.schema !== EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA) {
1089
+ throw ledgerError(
1090
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
1091
+ "domain event schema is unsupported",
1092
+ );
1093
+ }
1094
+ const normalizedInput = normalizeDomainAppendInput(
1095
+ Object.fromEntries(
1096
+ [...DOMAIN_APPEND_INPUT_KEYS].map((key) => [key, value[key]]),
1097
+ ),
1098
+ value.timestamp,
1099
+ );
1100
+ return {
1101
+ ...normalizedInput,
1102
+ algorithm: boundedString(value.algorithm, "algorithm", 64),
1103
+ artifactValidationDigest: digest(
1104
+ value.artifactValidationDigest,
1105
+ "artifactValidationDigest",
1106
+ ),
1107
+ epoch: identifier(value.epoch, "epoch"),
1108
+ identityDigest: digest(value.identityDigest, "identityDigest"),
1109
+ keyId: boundedString(value.keyId, "keyId", 256),
1110
+ ledgerId: identifier(value.ledgerId, "ledgerId"),
1111
+ prevDigest: digest(value.prevDigest, "prevDigest", { nullable: true }),
1112
+ schema: EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA,
1113
+ sequence: safeSequence(value.sequence, "sequence"),
1114
+ trustPolicyDigest: digest(value.trustPolicyDigest, "trustPolicyDigest"),
1115
+ witnessAlgorithm: boundedString(
1116
+ value.witnessAlgorithm,
1117
+ "witnessAlgorithm",
1118
+ 64,
1119
+ ),
1120
+ witnessId: identifier(value.witnessId, "witnessId"),
1121
+ witnessKeyId: boundedString(value.witnessKeyId, "witnessKeyId", 256),
1122
+ witnessTrustPolicyDigest: digest(
1123
+ value.witnessTrustPolicyDigest,
1124
+ "witnessTrustPolicyDigest",
1125
+ ),
1126
+ };
1127
+ }
1128
+
1129
+ function normalizeIdentityCore(value) {
1130
+ assertExactKeys(value, IDENTITY_CORE_KEYS, "identity core");
1131
+ if (value.schema !== EVOLUTION_LEDGER_IDENTITY_SCHEMA) {
1132
+ throw ledgerError(
1133
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
1134
+ "identity schema is unsupported",
1135
+ );
1136
+ }
1137
+ return {
1138
+ algorithm: boundedString(value.algorithm, "algorithm", 64),
1139
+ createdAt: canonicalTimestamp(value.createdAt, "createdAt"),
1140
+ epoch: identifier(value.epoch, "epoch"),
1141
+ keyId: boundedString(value.keyId, "keyId", 256),
1142
+ ledgerId: identifier(value.ledgerId, "ledgerId"),
1143
+ schema: EVOLUTION_LEDGER_IDENTITY_SCHEMA,
1144
+ storeBindingDigest: digest(value.storeBindingDigest, "storeBindingDigest"),
1145
+ storeMarkerDigest: digest(value.storeMarkerDigest, "storeMarkerDigest"),
1146
+ storeMarkerEntryDigest: digest(
1147
+ value.storeMarkerEntryDigest,
1148
+ "storeMarkerEntryDigest",
1149
+ ),
1150
+ storeMarkerId: identifier(value.storeMarkerId, "storeMarkerId"),
1151
+ trustPolicyDigest: digest(value.trustPolicyDigest, "trustPolicyDigest"),
1152
+ witnessAlgorithm: boundedString(
1153
+ value.witnessAlgorithm,
1154
+ "witnessAlgorithm",
1155
+ 64,
1156
+ ),
1157
+ witnessId: identifier(value.witnessId, "witnessId"),
1158
+ witnessKeyId: boundedString(value.witnessKeyId, "witnessKeyId", 256),
1159
+ witnessTrustPolicyDigest: digest(
1160
+ value.witnessTrustPolicyDigest,
1161
+ "witnessTrustPolicyDigest",
1162
+ ),
1163
+ };
1164
+ }
1165
+
1166
+ function normalizeStoreMarkerCore(value) {
1167
+ assertExactKeys(value, STORE_MARKER_CORE_KEYS, "store marker core");
1168
+ if (value.schema !== EVOLUTION_LEDGER_STORE_MARKER_SCHEMA) {
1169
+ throw ledgerError(
1170
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
1171
+ "store marker schema is unsupported",
1172
+ );
1173
+ }
1174
+ return {
1175
+ algorithm: boundedString(value.algorithm, "algorithm", 64),
1176
+ createdAt: canonicalTimestamp(value.createdAt, "createdAt"),
1177
+ epoch: identifier(value.epoch, "epoch"),
1178
+ keyId: boundedString(value.keyId, "keyId", 256),
1179
+ ledgerId: identifier(value.ledgerId, "ledgerId"),
1180
+ schema: EVOLUTION_LEDGER_STORE_MARKER_SCHEMA,
1181
+ storeBindingDigest: digest(value.storeBindingDigest, "storeBindingDigest"),
1182
+ storeMarkerId: identifier(value.storeMarkerId, "storeMarkerId"),
1183
+ trustPolicyDigest: digest(value.trustPolicyDigest, "trustPolicyDigest"),
1184
+ witnessAlgorithm: boundedString(
1185
+ value.witnessAlgorithm,
1186
+ "witnessAlgorithm",
1187
+ 64,
1188
+ ),
1189
+ witnessId: identifier(value.witnessId, "witnessId"),
1190
+ witnessKeyId: boundedString(value.witnessKeyId, "witnessKeyId", 256),
1191
+ witnessTrustPolicyDigest: digest(
1192
+ value.witnessTrustPolicyDigest,
1193
+ "witnessTrustPolicyDigest",
1194
+ ),
1195
+ };
1196
+ }
1197
+
1198
+ function normalizeAnchorCore(value) {
1199
+ assertExactKeys(value, ANCHOR_CORE_KEYS, "anchor core");
1200
+ if (value.schema !== EVOLUTION_LEDGER_ANCHOR_SCHEMA) {
1201
+ throw ledgerError(
1202
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
1203
+ "anchor schema is unsupported",
1204
+ );
1205
+ }
1206
+ return {
1207
+ algorithm: boundedString(value.algorithm, "algorithm", 64),
1208
+ committedAt: canonicalTimestamp(value.committedAt, "committedAt"),
1209
+ epoch: identifier(value.epoch, "epoch"),
1210
+ headDigest: digest(value.headDigest, "headDigest", { nullable: true }),
1211
+ identityDigest: digest(value.identityDigest, "identityDigest"),
1212
+ keyId: boundedString(value.keyId, "keyId", 256),
1213
+ ledgerId: identifier(value.ledgerId, "ledgerId"),
1214
+ previousAnchorDigest: digest(
1215
+ value.previousAnchorDigest,
1216
+ "previousAnchorDigest",
1217
+ { nullable: true },
1218
+ ),
1219
+ schema: EVOLUTION_LEDGER_ANCHOR_SCHEMA,
1220
+ segmentDigest: digest(value.segmentDigest, "segmentDigest", {
1221
+ nullable: true,
1222
+ }),
1223
+ sequence: safeSequence(value.sequence, "sequence", { allowZero: true }),
1224
+ trustPolicyDigest: digest(value.trustPolicyDigest, "trustPolicyDigest"),
1225
+ witnessAlgorithm: boundedString(
1226
+ value.witnessAlgorithm,
1227
+ "witnessAlgorithm",
1228
+ 64,
1229
+ ),
1230
+ witnessId: identifier(value.witnessId, "witnessId"),
1231
+ witnessKeyId: boundedString(value.witnessKeyId, "witnessKeyId", 256),
1232
+ witnessTrustPolicyDigest: digest(
1233
+ value.witnessTrustPolicyDigest,
1234
+ "witnessTrustPolicyDigest",
1235
+ ),
1236
+ };
1237
+ }
1238
+
1239
+ function normalizeReceiptCore(value, trust, witnessTrust) {
1240
+ assertExactKeys(value, RECEIPT_CORE_KEYS, "receipt core");
1241
+ if (value.schema !== EVOLUTION_LEDGER_RECEIPT_SCHEMA) {
1242
+ throw ledgerError(
1243
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
1244
+ "receipt schema is unsupported",
1245
+ );
1246
+ }
1247
+ const durabilityMechanism = boundedString(
1248
+ value.durabilityMechanism,
1249
+ "durabilityMechanism",
1250
+ 64,
1251
+ );
1252
+ if (!DURABILITY_MECHANISMS.has(durabilityMechanism)) {
1253
+ throw ledgerError(
1254
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
1255
+ "receipt durability mechanism is unsupported",
1256
+ );
1257
+ }
1258
+ if (
1259
+ value.authenticated !== true ||
1260
+ value.committed !== true ||
1261
+ value.durable !== true ||
1262
+ value.persisted !== true
1263
+ ) {
1264
+ throw ledgerError(
1265
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
1266
+ "receipt must assert authenticated, committed, durable persistence",
1267
+ );
1268
+ }
1269
+ return {
1270
+ algorithm: boundedString(value.algorithm, "algorithm", 64),
1271
+ anchorDigest: digest(value.anchorDigest, "anchorDigest"),
1272
+ authenticated: true,
1273
+ committed: true,
1274
+ committedAt: canonicalTimestamp(value.committedAt, "committedAt"),
1275
+ durabilityMechanism,
1276
+ durable: true,
1277
+ epoch: identifier(value.epoch, "epoch"),
1278
+ eventDigest: digest(value.eventDigest, "eventDigest"),
1279
+ eventId: identifier(value.eventId, "eventId"),
1280
+ headDigest: digest(value.headDigest, "headDigest"),
1281
+ headSignature: normalizeSignature(value.headSignature, trust),
1282
+ identityDigest: digest(value.identityDigest, "identityDigest"),
1283
+ issuedAt: canonicalTimestamp(value.issuedAt, "issuedAt"),
1284
+ keyId: boundedString(value.keyId, "keyId", 256),
1285
+ ledgerId: identifier(value.ledgerId, "ledgerId"),
1286
+ persisted: true,
1287
+ schema: EVOLUTION_LEDGER_RECEIPT_SCHEMA,
1288
+ segmentDigest: digest(value.segmentDigest, "segmentDigest"),
1289
+ sequence: safeSequence(value.sequence, "sequence"),
1290
+ trustPolicyDigest: digest(value.trustPolicyDigest, "trustPolicyDigest"),
1291
+ witnessAlgorithm: boundedString(
1292
+ value.witnessAlgorithm,
1293
+ "witnessAlgorithm",
1294
+ 64,
1295
+ ),
1296
+ witnessCheckpoint: normalizeWitnessRecordShape(
1297
+ value.witnessCheckpoint,
1298
+ witnessTrust,
1299
+ ),
1300
+ witnessDigest: digest(value.witnessDigest, "witnessDigest"),
1301
+ witnessId: identifier(value.witnessId, "witnessId"),
1302
+ witnessKeyId: boundedString(value.witnessKeyId, "witnessKeyId", 256),
1303
+ witnessTrustPolicyDigest: digest(
1304
+ value.witnessTrustPolicyDigest,
1305
+ "witnessTrustPolicyDigest",
1306
+ ),
1307
+ };
1308
+ }
1309
+
1310
+ function normalizeWitnessCore(value) {
1311
+ assertExactKeys(value, WITNESS_CORE_KEYS, "witness core");
1312
+ if (value.schema !== EVOLUTION_LEDGER_WITNESS_SCHEMA) {
1313
+ throw ledgerError(
1314
+ "CC_EVOLUTION_LEDGER_WITNESS_INVALID",
1315
+ "witness schema is unsupported",
1316
+ );
1317
+ }
1318
+ if (
1319
+ value.authenticated !== true ||
1320
+ value.durable !== true ||
1321
+ !["absent", "committed"].includes(value.status)
1322
+ ) {
1323
+ throw ledgerError(
1324
+ "CC_EVOLUTION_LEDGER_WITNESS_INVALID",
1325
+ "witness must return authenticated durable state",
1326
+ );
1327
+ }
1328
+ const absent = value.status === "absent";
1329
+ const nullableIdentifier = (entry, label) =>
1330
+ entry == null ? null : identifier(entry, label);
1331
+ const sequence = absent
1332
+ ? value.sequence === null
1333
+ ? null
1334
+ : NaN
1335
+ : safeSequence(value.sequence, "witness.sequence", { allowZero: true });
1336
+ const core = {
1337
+ algorithm: boundedString(value.algorithm, "witness.algorithm", 64),
1338
+ anchorDigest: digest(value.anchorDigest, "witness.anchorDigest", {
1339
+ nullable: absent,
1340
+ }),
1341
+ authenticated: true,
1342
+ durable: true,
1343
+ discardAccumulatorDigest: digest(
1344
+ value.discardAccumulatorDigest,
1345
+ "witness.discardAccumulatorDigest",
1346
+ ),
1347
+ epoch: nullableIdentifier(value.epoch, "witness.epoch"),
1348
+ headDigest: digest(value.headDigest, "witness.headDigest", {
1349
+ nullable: absent || value.sequence === 0,
1350
+ }),
1351
+ identityDigest: digest(value.identityDigest, "witness.identityDigest", {
1352
+ nullable: absent,
1353
+ }),
1354
+ keyId: boundedString(value.keyId, "witness.keyId", 256),
1355
+ ledgerId: nullableIdentifier(value.ledgerId, "witness.ledgerId"),
1356
+ generation: safeCounter(value.generation, "witness.generation"),
1357
+ payloadDigest: digest(value.payloadDigest, "witness.payloadDigest", {
1358
+ nullable: absent,
1359
+ }),
1360
+ previousWitnessDigest: digest(
1361
+ value.previousWitnessDigest,
1362
+ "witness.previousWitnessDigest",
1363
+ { nullable: absent },
1364
+ ),
1365
+ schema: EVOLUTION_LEDGER_WITNESS_SCHEMA,
1366
+ segmentDigest: digest(value.segmentDigest, "witness.segmentDigest", {
1367
+ nullable: absent || value.sequence === 0,
1368
+ }),
1369
+ sequence,
1370
+ status: value.status,
1371
+ storeMarkerDigest: digest(
1372
+ value.storeMarkerDigest,
1373
+ "witness.storeMarkerDigest",
1374
+ { nullable: absent },
1375
+ ),
1376
+ storeMarkerEntryDigest: digest(
1377
+ value.storeMarkerEntryDigest,
1378
+ "witness.storeMarkerEntryDigest",
1379
+ { nullable: absent },
1380
+ ),
1381
+ storeMarkerId: nullableIdentifier(
1382
+ value.storeMarkerId,
1383
+ "witness.storeMarkerId",
1384
+ ),
1385
+ trustPolicyDigest: digest(
1386
+ value.trustPolicyDigest,
1387
+ "witness.trustPolicyDigest",
1388
+ ),
1389
+ witnessId: identifier(value.witnessId, "witness.witnessId"),
1390
+ };
1391
+ if (
1392
+ (absent &&
1393
+ [
1394
+ core.anchorDigest,
1395
+ core.epoch,
1396
+ core.headDigest,
1397
+ core.identityDigest,
1398
+ core.ledgerId,
1399
+ core.payloadDigest,
1400
+ core.segmentDigest,
1401
+ core.sequence,
1402
+ core.storeMarkerDigest,
1403
+ core.storeMarkerEntryDigest,
1404
+ core.storeMarkerId,
1405
+ ].some((entry) => entry !== null)) ||
1406
+ (absent &&
1407
+ (core.generation !== 0 ||
1408
+ core.previousWitnessDigest !== null ||
1409
+ core.discardAccumulatorDigest !== EMPTY_DISCARD_ACCUMULATOR_DIGEST)) ||
1410
+ (!absent &&
1411
+ [
1412
+ core.anchorDigest,
1413
+ core.epoch,
1414
+ core.identityDigest,
1415
+ core.ledgerId,
1416
+ core.payloadDigest,
1417
+ core.storeMarkerDigest,
1418
+ core.storeMarkerEntryDigest,
1419
+ core.storeMarkerId,
1420
+ ].some((entry) => entry === null)) ||
1421
+ (!absent && (core.generation < 1 || core.previousWitnessDigest === null)) ||
1422
+ (!absent &&
1423
+ core.sequence === 0 &&
1424
+ (core.headDigest !== null || core.segmentDigest !== null)) ||
1425
+ (!absent &&
1426
+ core.sequence > 0 &&
1427
+ (core.headDigest === null || core.segmentDigest === null))
1428
+ ) {
1429
+ throw ledgerError(
1430
+ "CC_EVOLUTION_LEDGER_WITNESS_INVALID",
1431
+ "witness state fields are inconsistent",
1432
+ );
1433
+ }
1434
+ return core;
1435
+ }
1436
+
1437
+ function normalizeWitnessRecordShape(value, witnessTrust) {
1438
+ assertExactKeys(value, WITNESS_RECORD_KEYS, "witness record");
1439
+ const core = normalizeWitnessCore(
1440
+ Object.fromEntries([...WITNESS_CORE_KEYS].map((key) => [key, value[key]])),
1441
+ );
1442
+ return {
1443
+ ...core,
1444
+ signature: normalizeSignature(value.signature, witnessTrust),
1445
+ witnessDigest: digest(value.witnessDigest, "witnessDigest"),
1446
+ };
1447
+ }
1448
+
1449
+ function normalizeWitnessAncestryCore(value) {
1450
+ assertExactKeys(value, WITNESS_ANCESTRY_CORE_KEYS, "witness ancestry core");
1451
+ if (
1452
+ value.schema !== EVOLUTION_LEDGER_WITNESS_ANCESTRY_SCHEMA ||
1453
+ value.authenticated !== true ||
1454
+ value.durable !== true ||
1455
+ value.included !== true
1456
+ ) {
1457
+ throw ledgerError(
1458
+ "CC_EVOLUTION_LEDGER_WITNESS_INVALID",
1459
+ "witness ancestry proof must be authenticated, durable, and included",
1460
+ );
1461
+ }
1462
+ return {
1463
+ algorithm: boundedString(value.algorithm, "witness proof.algorithm", 64),
1464
+ ancestorDigest: digest(
1465
+ value.ancestorDigest,
1466
+ "witness proof.ancestorDigest",
1467
+ ),
1468
+ ancestorGeneration: safeCounter(
1469
+ value.ancestorGeneration,
1470
+ "witness proof.ancestorGeneration",
1471
+ ),
1472
+ authenticated: true,
1473
+ descendantDigest: digest(
1474
+ value.descendantDigest,
1475
+ "witness proof.descendantDigest",
1476
+ ),
1477
+ descendantGeneration: safeCounter(
1478
+ value.descendantGeneration,
1479
+ "witness proof.descendantGeneration",
1480
+ ),
1481
+ durable: true,
1482
+ epoch: identifier(value.epoch, "witness proof.epoch"),
1483
+ included: true,
1484
+ keyId: boundedString(value.keyId, "witness proof.keyId", 256),
1485
+ ledgerId: identifier(value.ledgerId, "witness proof.ledgerId"),
1486
+ schema: EVOLUTION_LEDGER_WITNESS_ANCESTRY_SCHEMA,
1487
+ trustPolicyDigest: digest(
1488
+ value.trustPolicyDigest,
1489
+ "witness proof.trustPolicyDigest",
1490
+ ),
1491
+ witnessId: identifier(value.witnessId, "witness proof.witnessId"),
1492
+ };
1493
+ }
1494
+
1495
+ function serializeRecord(record, maximum, label) {
1496
+ const bytes = Buffer.from(`${canonicalJson(record)}\n`, "utf8");
1497
+ if (bytes.length < 2 || bytes.length > maximum) {
1498
+ throw ledgerError(
1499
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
1500
+ `${label} exceeds its bounded canonical representation`,
1501
+ );
1502
+ }
1503
+ return bytes;
1504
+ }
1505
+
1506
+ function entryIdentity(stat) {
1507
+ return `${String(stat.dev)}:${String(stat.ino)}`;
1508
+ }
1509
+
1510
+ function fileFingerprint(stat) {
1511
+ return [
1512
+ entryIdentity(stat),
1513
+ String(stat.size),
1514
+ String(stat.nlink),
1515
+ String(stat.mode),
1516
+ String(stat.mtimeMs),
1517
+ String(stat.ctimeMs),
1518
+ String(stat.birthtimeMs),
1519
+ ].join(":");
1520
+ }
1521
+
1522
+ function storeMarkerEntryDigest(storeMarkerFile) {
1523
+ return domainDigest(STORE_MARKER_ENTRY_DOMAIN, {
1524
+ contentDigest: storeMarkerFile.contentDigest,
1525
+ fingerprint: storeMarkerFile.fingerprint,
1526
+ });
1527
+ }
1528
+
1529
+ function samePath(left, right) {
1530
+ const normalizedLeft = path.resolve(left);
1531
+ const normalizedRight = path.resolve(right);
1532
+ return process.platform === "win32"
1533
+ ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase()
1534
+ : normalizedLeft === normalizedRight;
1535
+ }
1536
+
1537
+ function isContained(root, candidate) {
1538
+ const relation = path.relative(root, candidate);
1539
+ return (
1540
+ relation === "" ||
1541
+ (relation !== ".." &&
1542
+ !relation.startsWith(`..${path.sep}`) &&
1543
+ !path.isAbsolute(relation))
1544
+ );
1545
+ }
1546
+
1547
+ function pathsOverlap(left, right) {
1548
+ return isContained(left, right) || isContained(right, left);
1549
+ }
1550
+
1551
+ function captureWitness(value) {
1552
+ // This is the non-rollback trust boundary. Implementations must keep a
1553
+ // stable id, make initialize create-only/ever-initialized, perform a durable
1554
+ // monotonic CAS, and retain the canonical identity/anchor/event payloads
1555
+ // supplied by this ledger. A compareAndSwap carrying `discard` is an atomic,
1556
+ // irreversible fence: that orphan digest can never later become committed.
1557
+ // Every response is still signature-verified here.
1558
+ assertExactKeys(value, WITNESS_PORT_KEYS, "witness");
1559
+ const id = identifier(value.id, "witness.id");
1560
+ const captured = { id };
1561
+ for (const method of [
1562
+ "read",
1563
+ "initialize",
1564
+ "compareAndSwap",
1565
+ "proveAncestry",
1566
+ ]) {
1567
+ if (typeof value[method] !== "function") {
1568
+ throw ledgerError(
1569
+ "CC_EVOLUTION_LEDGER_WITNESS_REQUIRED",
1570
+ `witness.${method} must be a synchronous trusted port`,
1571
+ );
1572
+ }
1573
+ const implementation = value[method];
1574
+ captured[method] = Object.freeze((...args) =>
1575
+ Reflect.apply(implementation, undefined, args),
1576
+ );
1577
+ }
1578
+ return Object.freeze(captured);
1579
+ }
1580
+
1581
+ function captureFs(fsImpl) {
1582
+ if (!fsImpl || typeof fsImpl !== "object") {
1583
+ throw ledgerError(
1584
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
1585
+ "fsImpl must be a filesystem object",
1586
+ );
1587
+ }
1588
+ const captured = {};
1589
+ for (const method of REQUIRED_FS_METHODS) {
1590
+ if (typeof fsImpl[method] !== "function") {
1591
+ throw ledgerError(
1592
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
1593
+ `fsImpl is missing ${method}`,
1594
+ );
1595
+ }
1596
+ const implementation =
1597
+ method === "realpathSync" &&
1598
+ typeof fsImpl.realpathSync.native === "function"
1599
+ ? fsImpl.realpathSync.native
1600
+ : fsImpl[method];
1601
+ captured[method] = Object.freeze((...args) =>
1602
+ Reflect.apply(implementation, undefined, args),
1603
+ );
1604
+ }
1605
+ if (!fsImpl.constants || typeof fsImpl.constants !== "object") {
1606
+ throw ledgerError(
1607
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
1608
+ "fsImpl.constants is unavailable",
1609
+ );
1610
+ }
1611
+ captured.constants = Object.freeze({ ...fsImpl.constants });
1612
+ return Object.freeze(captured);
1613
+ }
1614
+
1615
+ function realpath(fsPort, value) {
1616
+ try {
1617
+ return path.resolve(fsPort.realpathSync(value));
1618
+ } catch (cause) {
1619
+ throw ledgerError(
1620
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
1621
+ `path could not be resolved: ${value}`,
1622
+ { cause },
1623
+ );
1624
+ }
1625
+ }
1626
+
1627
+ function syncDirectory(fsPort, directory) {
1628
+ let descriptor = null;
1629
+ try {
1630
+ descriptor = fsPort.openSync(directory, "r");
1631
+ fsPort.fsyncSync(descriptor);
1632
+ return "directory-fsync";
1633
+ } catch (cause) {
1634
+ if (
1635
+ process.platform === "win32" &&
1636
+ ["EACCES", "EINVAL", "EISDIR", "EPERM"].includes(cause?.code)
1637
+ ) {
1638
+ return "windows-file-fsync";
1639
+ }
1640
+ throw cause;
1641
+ } finally {
1642
+ if (descriptor !== null) fsPort.closeSync(descriptor);
1643
+ }
1644
+ }
1645
+
1646
+ function randomToken(random) {
1647
+ let token;
1648
+ try {
1649
+ token = String(random());
1650
+ } catch (cause) {
1651
+ throw ledgerError(
1652
+ "CC_EVOLUTION_LEDGER_RANDOM_FAILED",
1653
+ "evolution ledger random source failed",
1654
+ { cause, commitState: "not-committed" },
1655
+ );
1656
+ }
1657
+ if (!RANDOM_TOKEN_PATTERN.test(token)) {
1658
+ throw ledgerError(
1659
+ "CC_EVOLUTION_LEDGER_RANDOM_FAILED",
1660
+ "evolution ledger random token must contain 16-128 safe characters",
1661
+ { commitState: "not-committed" },
1662
+ );
1663
+ }
1664
+ return token;
1665
+ }
1666
+
1667
+ function sequenceName(sequence) {
1668
+ return String(sequence).padStart(12, "0");
1669
+ }
1670
+
1671
+ function segmentFileName(sequence, segmentDigest) {
1672
+ return `${sequenceName(sequence)}-${segmentDigest.slice("sha256:".length)}.json`;
1673
+ }
1674
+
1675
+ function anchorFileName(sequence, anchorDigest) {
1676
+ return `${sequenceName(sequence)}-${anchorDigest.slice("sha256:".length)}.json`;
1677
+ }
1678
+
1679
+ function defaultRootDir() {
1680
+ return path.join(getHomeDir(), "evolution", "ledger", "events");
1681
+ }
1682
+
1683
+ function defaultAuthorityRoot(rootDir) {
1684
+ return path.join(
1685
+ path.dirname(path.resolve(rootDir)),
1686
+ `${path.basename(path.resolve(rootDir))}-authority`,
1687
+ );
1688
+ }
1689
+
1690
+ export class EvolutionLedger {
1691
+ #artifactResolver;
1692
+ #boundaries;
1693
+ #clock;
1694
+ #crashHook;
1695
+ #fs;
1696
+ #lock;
1697
+ #lockClock;
1698
+ #lockTimeoutMs;
1699
+ #paths;
1700
+ #random;
1701
+ #secure;
1702
+ #sign;
1703
+ #stateCache;
1704
+ #storeBindingDigest;
1705
+ #trust;
1706
+ #verifySignature;
1707
+ #verifyWitnessSignature;
1708
+ #witness;
1709
+ #witnessTrust;
1710
+
1711
+ constructor({
1712
+ rootDir = defaultRootDir(),
1713
+ authorityRootDir = defaultAuthorityRoot(rootDir),
1714
+ secure = true,
1715
+ fsImpl = fs,
1716
+ clock = Date.now,
1717
+ lockClock = Date.now,
1718
+ random = () => crypto.randomBytes(16).toString("hex"),
1719
+ trust,
1720
+ sign,
1721
+ verifySignature,
1722
+ verifyWitnessSignature,
1723
+ artifactResolver,
1724
+ witness,
1725
+ witnessTrust,
1726
+ lock = withFileLock,
1727
+ lockTimeoutMs = 10_000,
1728
+ crashHook = null,
1729
+ } = {}) {
1730
+ if (
1731
+ typeof sign !== "function" ||
1732
+ typeof verifySignature !== "function" ||
1733
+ typeof verifyWitnessSignature !== "function" ||
1734
+ typeof artifactResolver !== "function" ||
1735
+ !witness
1736
+ ) {
1737
+ throw ledgerError(
1738
+ "CC_EVOLUTION_LEDGER_TRUST_PORT_REQUIRED",
1739
+ "sign, verifySignature, verifyWitnessSignature, artifactResolver, and witness ports are mandatory",
1740
+ );
1741
+ }
1742
+ if (
1743
+ typeof clock !== "function" ||
1744
+ typeof lockClock !== "function" ||
1745
+ typeof random !== "function" ||
1746
+ typeof lock !== "function" ||
1747
+ (crashHook != null && typeof crashHook !== "function")
1748
+ ) {
1749
+ throw ledgerError(
1750
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
1751
+ "clock, lockClock, random, lock, and crashHook must be functions",
1752
+ );
1753
+ }
1754
+ if (!Number.isSafeInteger(lockTimeoutMs) || lockTimeoutMs < 1) {
1755
+ throw ledgerError(
1756
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
1757
+ "lockTimeoutMs must be a positive safe integer",
1758
+ );
1759
+ }
1760
+
1761
+ this.#fs = captureFs(fsImpl);
1762
+ this.#trust = normalizeTrust(trust);
1763
+ this.#witnessTrust = normalizeTrust(witnessTrust);
1764
+ this.#witness = captureWitness(witness);
1765
+ this.#clock = Object.freeze((...args) => clock(...args));
1766
+ this.#lockClock = Object.freeze((...args) => lockClock(...args));
1767
+ this.#random = Object.freeze((...args) => random(...args));
1768
+ this.#sign = Object.freeze((...args) => sign(...args));
1769
+ this.#verifySignature = Object.freeze((...args) =>
1770
+ verifySignature(...args),
1771
+ );
1772
+ this.#verifyWitnessSignature = Object.freeze((...args) =>
1773
+ verifyWitnessSignature(...args),
1774
+ );
1775
+ this.#artifactResolver = Object.freeze((...args) =>
1776
+ artifactResolver(...args),
1777
+ );
1778
+ this.#lock = Object.freeze((...args) => lock(...args));
1779
+ this.#crashHook =
1780
+ crashHook == null ? null : Object.freeze((...args) => crashHook(...args));
1781
+ this.#lockTimeoutMs = lockTimeoutMs;
1782
+ this.#secure = secure !== false;
1783
+ this.#stateCache = null;
1784
+
1785
+ const requestedRoot = path.resolve(rootDir);
1786
+ const requestedAuthorityRoot = path.resolve(authorityRootDir);
1787
+ if (pathsOverlap(requestedRoot, requestedAuthorityRoot)) {
1788
+ throw ledgerError(
1789
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
1790
+ "event and authority roots must be independent, non-overlapping paths",
1791
+ );
1792
+ }
1793
+ const requestedPaths = {
1794
+ rootDir: requestedRoot,
1795
+ authorityRootDir: requestedAuthorityRoot,
1796
+ segmentDir: path.join(requestedRoot, SEGMENT_DIRECTORY_NAME),
1797
+ anchorDir: path.join(requestedAuthorityRoot, ANCHOR_DIRECTORY_NAME),
1798
+ };
1799
+
1800
+ try {
1801
+ for (const directory of [
1802
+ requestedPaths.rootDir,
1803
+ requestedPaths.authorityRootDir,
1804
+ requestedPaths.segmentDir,
1805
+ requestedPaths.anchorDir,
1806
+ ]) {
1807
+ if (this.#secure) {
1808
+ ensurePrivateDirectory(directory, {
1809
+ applyWindowsAcl: true,
1810
+ failIfUnavailable: true,
1811
+ });
1812
+ } else {
1813
+ this.#fs.mkdirSync(directory, { recursive: true, mode: 0o700 });
1814
+ }
1815
+ }
1816
+
1817
+ const canonicalPaths = {
1818
+ rootDir: realpath(this.#fs, requestedPaths.rootDir),
1819
+ authorityRootDir: realpath(this.#fs, requestedPaths.authorityRootDir),
1820
+ segmentDir: realpath(this.#fs, requestedPaths.segmentDir),
1821
+ anchorDir: realpath(this.#fs, requestedPaths.anchorDir),
1822
+ };
1823
+ if (
1824
+ pathsOverlap(canonicalPaths.rootDir, canonicalPaths.authorityRootDir) ||
1825
+ !isContained(canonicalPaths.rootDir, canonicalPaths.segmentDir) ||
1826
+ !isContained(canonicalPaths.authorityRootDir, canonicalPaths.anchorDir)
1827
+ ) {
1828
+ throw ledgerError(
1829
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
1830
+ "canonical ledger paths crossed their security boundaries",
1831
+ );
1832
+ }
1833
+ this.#paths = deepFreeze({
1834
+ ...canonicalPaths,
1835
+ identityPath: path.join(
1836
+ canonicalPaths.authorityRootDir,
1837
+ IDENTITY_FILE_NAME,
1838
+ ),
1839
+ headPath: path.join(canonicalPaths.authorityRootDir, HEAD_FILE_NAME),
1840
+ lockPath: path.join(canonicalPaths.authorityRootDir, LOCK_TARGET_NAME),
1841
+ storeMarkerPath: path.join(
1842
+ canonicalPaths.rootDir,
1843
+ STORE_MARKER_FILE_NAME,
1844
+ ),
1845
+ });
1846
+ this.#boundaries = deepFreeze(
1847
+ [
1848
+ this.#paths.rootDir,
1849
+ this.#paths.authorityRootDir,
1850
+ this.#paths.segmentDir,
1851
+ this.#paths.anchorDir,
1852
+ ].map((directory) => {
1853
+ const stat = this.#fs.lstatSync(directory);
1854
+ if (!stat.isDirectory() || stat.isSymbolicLink()) {
1855
+ throw ledgerError(
1856
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
1857
+ `ledger boundary is not a regular directory: ${directory}`,
1858
+ );
1859
+ }
1860
+ return { directory, identity: entryIdentity(stat) };
1861
+ }),
1862
+ );
1863
+ this.#storeBindingDigest = domainDigest(
1864
+ STORE_BINDING_DOMAIN,
1865
+ this.#boundaries,
1866
+ );
1867
+ this.#assertBoundaries();
1868
+ this.#withLock(() => this.#loadState({ allowInitialize: true }));
1869
+ } catch (cause) {
1870
+ if (cause instanceof EvolutionLedgerError) throw cause;
1871
+ throw ledgerError(
1872
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
1873
+ "evolution ledger could not initialize its trusted stores",
1874
+ { cause },
1875
+ );
1876
+ }
1877
+
1878
+ Object.freeze(this);
1879
+ }
1880
+
1881
+ get rootDir() {
1882
+ return this.#paths.rootDir;
1883
+ }
1884
+
1885
+ get authorityRootDir() {
1886
+ return this.#paths.authorityRootDir;
1887
+ }
1888
+
1889
+ get segmentDir() {
1890
+ return this.#paths.segmentDir;
1891
+ }
1892
+
1893
+ get anchorDir() {
1894
+ return this.#paths.anchorDir;
1895
+ }
1896
+
1897
+ get identityPath() {
1898
+ return this.#paths.identityPath;
1899
+ }
1900
+
1901
+ get headPath() {
1902
+ return this.#paths.headPath;
1903
+ }
1904
+
1905
+ get storeMarkerPath() {
1906
+ return this.#paths.storeMarkerPath;
1907
+ }
1908
+
1909
+ #assertBoundaries() {
1910
+ for (const boundary of this.#boundaries) {
1911
+ let stat;
1912
+ let canonical;
1913
+ try {
1914
+ stat = this.#fs.lstatSync(boundary.directory);
1915
+ canonical = realpath(this.#fs, boundary.directory);
1916
+ } catch (cause) {
1917
+ if (cause instanceof EvolutionLedgerError) throw cause;
1918
+ throw ledgerError(
1919
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
1920
+ `ledger boundary is unavailable: ${boundary.directory}`,
1921
+ { cause },
1922
+ );
1923
+ }
1924
+ if (
1925
+ !stat.isDirectory() ||
1926
+ stat.isSymbolicLink() ||
1927
+ entryIdentity(stat) !== boundary.identity ||
1928
+ !samePath(canonical, boundary.directory)
1929
+ ) {
1930
+ throw ledgerError(
1931
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
1932
+ `ledger boundary changed or became unsafe: ${boundary.directory}`,
1933
+ );
1934
+ }
1935
+ }
1936
+ }
1937
+
1938
+ #withLock(callback) {
1939
+ const token = randomToken(this.#random);
1940
+ let callbackError = null;
1941
+ try {
1942
+ return this.#lock(
1943
+ this.#paths.lockPath,
1944
+ (...args) => {
1945
+ try {
1946
+ return callback(...args);
1947
+ } catch (cause) {
1948
+ callbackError = cause;
1949
+ throw cause;
1950
+ }
1951
+ },
1952
+ {
1953
+ _fs: this.#fs,
1954
+ _now: this.#lockClock,
1955
+ _ownerToken: () => token,
1956
+ failIfUnavailable: true,
1957
+ timeoutMs: this.#lockTimeoutMs,
1958
+ },
1959
+ );
1960
+ } catch (cause) {
1961
+ if (callbackError === cause) throw cause;
1962
+ if (cause instanceof EvolutionLedgerError) throw cause;
1963
+ throw ledgerError(
1964
+ "CC_EVOLUTION_LEDGER_LOCK_UNAVAILABLE",
1965
+ "evolution ledger lock fence is unavailable",
1966
+ { cause, commitState: "not-committed" },
1967
+ );
1968
+ }
1969
+ }
1970
+
1971
+ #invokeCrashHook(phase, context) {
1972
+ if (!this.#crashHook) return;
1973
+ const result = this.#crashHook(phase, frozenClone(context));
1974
+ if (isUnsafeAsyncResult(result)) {
1975
+ throw ledgerError(
1976
+ "CC_EVOLUTION_LEDGER_CRASH_HOOK_INVALID",
1977
+ "crashHook must be synchronous",
1978
+ );
1979
+ }
1980
+ }
1981
+
1982
+ #signRecord(domain, core, digestField, purpose) {
1983
+ const message = signedMessage(domain, core);
1984
+ const recordDigest = sha256(message);
1985
+ let signature;
1986
+ try {
1987
+ const output = this.#sign({
1988
+ digest: recordDigest,
1989
+ message: Buffer.from(message),
1990
+ purpose,
1991
+ trust: this.#trust,
1992
+ });
1993
+ if (isUnsafeAsyncResult(output)) {
1994
+ throw new TypeError("signing port returned a Promise");
1995
+ }
1996
+ signature = normalizeSignature(output, this.#trust);
1997
+ } catch (cause) {
1998
+ throw ledgerError(
1999
+ "CC_EVOLUTION_LEDGER_SIGNING_FAILED",
2000
+ `evolution ledger ${purpose} signing failed`,
2001
+ { cause, commitState: "not-committed" },
2002
+ );
2003
+ }
2004
+ this.#verifyMessage({
2005
+ digest: recordDigest,
2006
+ message,
2007
+ purpose,
2008
+ signature,
2009
+ });
2010
+ return deepFreeze({ ...core, [digestField]: recordDigest, signature });
2011
+ }
2012
+
2013
+ #verifyMessage({ digest: recordDigest, message, purpose, signature }) {
2014
+ let valid;
2015
+ try {
2016
+ valid = this.#verifySignature({
2017
+ digest: recordDigest,
2018
+ message: Buffer.from(message),
2019
+ purpose,
2020
+ signature: frozenClone(signature),
2021
+ trust: this.#trust,
2022
+ });
2023
+ } catch (cause) {
2024
+ throw ledgerError(
2025
+ "CC_EVOLUTION_LEDGER_SIGNATURE_INVALID",
2026
+ `${purpose} signature verification failed`,
2027
+ { cause },
2028
+ );
2029
+ }
2030
+ if (isUnsafeAsyncResult(valid)) {
2031
+ throw ledgerError(
2032
+ "CC_EVOLUTION_LEDGER_SIGNATURE_INVALID",
2033
+ "verification port must be synchronous",
2034
+ );
2035
+ }
2036
+ if (valid !== true) {
2037
+ throw ledgerError(
2038
+ "CC_EVOLUTION_LEDGER_SIGNATURE_INVALID",
2039
+ `${purpose} signature was rejected by the configured trust policy`,
2040
+ );
2041
+ }
2042
+ }
2043
+
2044
+ #verifyWitnessMessage({ digest: recordDigest, message, purpose, signature }) {
2045
+ let valid;
2046
+ try {
2047
+ valid = this.#verifyWitnessSignature({
2048
+ digest: recordDigest,
2049
+ message: Buffer.from(message),
2050
+ purpose,
2051
+ signature: frozenClone(signature),
2052
+ trust: this.#witnessTrust,
2053
+ });
2054
+ } catch (cause) {
2055
+ throw ledgerError(
2056
+ "CC_EVOLUTION_LEDGER_WITNESS_INVALID",
2057
+ `${purpose} signature verification failed`,
2058
+ { cause },
2059
+ );
2060
+ }
2061
+ if (isUnsafeAsyncResult(valid)) {
2062
+ throw ledgerError(
2063
+ "CC_EVOLUTION_LEDGER_WITNESS_INVALID",
2064
+ "witness verification port must be synchronous",
2065
+ );
2066
+ }
2067
+ if (valid !== true) {
2068
+ throw ledgerError(
2069
+ "CC_EVOLUTION_LEDGER_WITNESS_INVALID",
2070
+ `${purpose} signature was rejected by the witness trust policy`,
2071
+ );
2072
+ }
2073
+ }
2074
+
2075
+ #assertTrustBinding(record, identity = null) {
2076
+ if (
2077
+ record.algorithm !== this.#trust.algorithm ||
2078
+ record.keyId !== this.#trust.keyId ||
2079
+ record.trustPolicyDigest !== this.#trust.trustPolicyDigest ||
2080
+ record.witnessId !== this.#witness.id ||
2081
+ record.witnessAlgorithm !== this.#witnessTrust.algorithm ||
2082
+ record.witnessKeyId !== this.#witnessTrust.keyId ||
2083
+ record.witnessTrustPolicyDigest !==
2084
+ this.#witnessTrust.trustPolicyDigest ||
2085
+ (identity &&
2086
+ (record.ledgerId !== identity.ledgerId ||
2087
+ record.epoch !== identity.epoch ||
2088
+ record.identityDigest !== identity.identityDigest))
2089
+ ) {
2090
+ throw ledgerError(
2091
+ "CC_EVOLUTION_LEDGER_TRUST_MISMATCH",
2092
+ "signed record is not bound to the configured ledger identity and trust policy",
2093
+ );
2094
+ }
2095
+ }
2096
+
2097
+ #assertWitnessTrustBinding(record) {
2098
+ if (
2099
+ record.algorithm !== this.#witnessTrust.algorithm ||
2100
+ record.keyId !== this.#witnessTrust.keyId ||
2101
+ record.trustPolicyDigest !== this.#witnessTrust.trustPolicyDigest ||
2102
+ record.witnessId !== this.#witness.id
2103
+ ) {
2104
+ throw ledgerError(
2105
+ "CC_EVOLUTION_LEDGER_WITNESS_INVALID",
2106
+ "witness record is not bound to the configured independent witness trust root",
2107
+ );
2108
+ }
2109
+ }
2110
+
2111
+ #verifyIdentity(record) {
2112
+ assertExactKeys(record, IDENTITY_RECORD_KEYS, "identity record");
2113
+ const core = normalizeIdentityCore(
2114
+ Object.fromEntries(
2115
+ [...IDENTITY_CORE_KEYS].map((key) => [key, record[key]]),
2116
+ ),
2117
+ );
2118
+ this.#assertTrustBinding(core);
2119
+ if (core.storeBindingDigest !== this.#storeBindingDigest) {
2120
+ throw ledgerError(
2121
+ "CC_EVOLUTION_LEDGER_CORRUPT",
2122
+ "signed ledger identity does not match the current store incarnation",
2123
+ );
2124
+ }
2125
+ const identityDigest = digest(record.identityDigest, "identityDigest");
2126
+ const signature = normalizeSignature(record.signature, this.#trust);
2127
+ const message = signedMessage(IDENTITY_DOMAIN, core);
2128
+ if (identityDigest !== sha256(message)) {
2129
+ throw ledgerError(
2130
+ "CC_EVOLUTION_LEDGER_CORRUPT",
2131
+ "ledger identity digest does not match its signed core",
2132
+ );
2133
+ }
2134
+ this.#verifyMessage({
2135
+ digest: identityDigest,
2136
+ message,
2137
+ purpose: "identity",
2138
+ signature,
2139
+ });
2140
+ return deepFreeze({ ...core, identityDigest, signature });
2141
+ }
2142
+
2143
+ #verifyStoreMarker(record, identity = null) {
2144
+ assertExactKeys(record, STORE_MARKER_RECORD_KEYS, "store marker record");
2145
+ const core = normalizeStoreMarkerCore(
2146
+ Object.fromEntries(
2147
+ [...STORE_MARKER_CORE_KEYS].map((key) => [key, record[key]]),
2148
+ ),
2149
+ );
2150
+ this.#assertTrustBinding(core);
2151
+ const storeMarkerDigest = digest(
2152
+ record.storeMarkerDigest,
2153
+ "storeMarkerDigest",
2154
+ );
2155
+ const signature = normalizeSignature(record.signature, this.#trust);
2156
+ const message = signedMessage(STORE_MARKER_DOMAIN, core);
2157
+ if (
2158
+ storeMarkerDigest !== sha256(message) ||
2159
+ core.storeBindingDigest !== this.#storeBindingDigest ||
2160
+ (identity &&
2161
+ (core.ledgerId !== identity.ledgerId ||
2162
+ core.epoch !== identity.epoch ||
2163
+ core.createdAt !== identity.createdAt ||
2164
+ core.storeBindingDigest !== identity.storeBindingDigest ||
2165
+ core.storeMarkerId !== identity.storeMarkerId ||
2166
+ storeMarkerDigest !== identity.storeMarkerDigest))
2167
+ ) {
2168
+ throw ledgerError(
2169
+ "CC_EVOLUTION_LEDGER_CORRUPT",
2170
+ "store incarnation marker is not bound to the signed ledger identity",
2171
+ );
2172
+ }
2173
+ this.#verifyMessage({
2174
+ digest: storeMarkerDigest,
2175
+ message,
2176
+ purpose: "store-marker",
2177
+ signature,
2178
+ });
2179
+ return deepFreeze({ ...core, signature, storeMarkerDigest });
2180
+ }
2181
+
2182
+ #verifyEvent(record, previous, identity) {
2183
+ assertPlainDataObject(record, "event record");
2184
+ const schema = Object.getOwnPropertyDescriptor(record, "schema")?.value;
2185
+ const domainEvent = schema === EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA;
2186
+ const recordKeys = domainEvent
2187
+ ? DOMAIN_EVENT_RECORD_KEYS
2188
+ : EVENT_RECORD_KEYS;
2189
+ const coreKeys = domainEvent ? DOMAIN_EVENT_CORE_KEYS : EVENT_CORE_KEYS;
2190
+ const signatureDomain = domainEvent ? DOMAIN_EVENT_DOMAIN : EVENT_DOMAIN;
2191
+ assertExactKeys(record, recordKeys, "event record");
2192
+ const coreValue = Object.fromEntries(
2193
+ [...coreKeys].map((key) => [key, record[key]]),
2194
+ );
2195
+ const core = domainEvent
2196
+ ? normalizeDomainEventCore(coreValue)
2197
+ : normalizeEventCore(coreValue);
2198
+ this.#assertTrustBinding(core, identity);
2199
+ const eventDigest = digest(record.eventDigest, "eventDigest");
2200
+ const signature = normalizeSignature(record.signature, this.#trust);
2201
+ const message = signedMessage(signatureDomain, core);
2202
+ if (
2203
+ core.sequence !== (previous?.sequence || 0) + 1 ||
2204
+ core.prevDigest !== (previous?.eventDigest || null) ||
2205
+ eventDigest !== sha256(message)
2206
+ ) {
2207
+ throw ledgerError(
2208
+ "CC_EVOLUTION_LEDGER_CORRUPT",
2209
+ `event chain failed at sequence ${core.sequence}`,
2210
+ { sequence: core.sequence },
2211
+ );
2212
+ }
2213
+ this.#verifyMessage({
2214
+ digest: eventDigest,
2215
+ message,
2216
+ purpose: domainEvent ? "domain-event" : "event",
2217
+ signature,
2218
+ });
2219
+ return deepFreeze({ ...core, eventDigest, signature });
2220
+ }
2221
+
2222
+ #verifyAnchor(record, previous, identity, { standalone = false } = {}) {
2223
+ assertExactKeys(record, ANCHOR_RECORD_KEYS, "anchor record");
2224
+ const core = normalizeAnchorCore(
2225
+ Object.fromEntries(
2226
+ [...ANCHOR_CORE_KEYS].map((key) => [key, record[key]]),
2227
+ ),
2228
+ );
2229
+ this.#assertTrustBinding(core, identity);
2230
+ const anchorDigest = digest(record.anchorDigest, "anchorDigest");
2231
+ const signature = normalizeSignature(record.signature, this.#trust);
2232
+ const message = signedMessage(ANCHOR_DOMAIN, core);
2233
+ if (
2234
+ (!standalone &&
2235
+ core.sequence !== (previous ? previous.sequence + 1 : 0)) ||
2236
+ (!standalone &&
2237
+ core.previousAnchorDigest !== (previous?.anchorDigest || null)) ||
2238
+ anchorDigest !== sha256(message) ||
2239
+ (core.sequence === 0 &&
2240
+ (core.headDigest !== null || core.segmentDigest !== null)) ||
2241
+ (core.sequence > 0 &&
2242
+ (core.headDigest === null || core.segmentDigest === null))
2243
+ ) {
2244
+ throw ledgerError(
2245
+ "CC_EVOLUTION_LEDGER_CORRUPT",
2246
+ `head anchor chain failed at sequence ${core.sequence}`,
2247
+ { sequence: core.sequence },
2248
+ );
2249
+ }
2250
+ this.#verifyMessage({
2251
+ digest: anchorDigest,
2252
+ message,
2253
+ purpose: "anchor",
2254
+ signature,
2255
+ });
2256
+ return deepFreeze({ ...core, anchorDigest, signature });
2257
+ }
2258
+
2259
+ #verifyReceiptRecord(record) {
2260
+ assertExactKeys(record, RECEIPT_RECORD_KEYS, "receipt record");
2261
+ const core = normalizeReceiptCore(
2262
+ Object.fromEntries(
2263
+ [...RECEIPT_CORE_KEYS].map((key) => [key, record[key]]),
2264
+ ),
2265
+ this.#trust,
2266
+ this.#witnessTrust,
2267
+ );
2268
+ this.#assertTrustBinding(core);
2269
+ const receiptDigest = digest(record.receiptDigest, "receiptDigest");
2270
+ const signature = normalizeSignature(record.signature, this.#trust);
2271
+ const message = signedMessage(RECEIPT_DOMAIN, core);
2272
+ if (receiptDigest !== sha256(message)) {
2273
+ throw ledgerError(
2274
+ "CC_EVOLUTION_LEDGER_RECEIPT_INVALID",
2275
+ "receipt digest does not match its signed core",
2276
+ );
2277
+ }
2278
+ try {
2279
+ this.#verifyMessage({
2280
+ digest: receiptDigest,
2281
+ message,
2282
+ purpose: "receipt",
2283
+ signature,
2284
+ });
2285
+ const witnessCheckpoint = this.#verifyWitnessRecord(
2286
+ core.witnessCheckpoint,
2287
+ );
2288
+ if (
2289
+ witnessCheckpoint.status !== "committed" ||
2290
+ witnessCheckpoint.witnessDigest !== core.witnessDigest
2291
+ ) {
2292
+ throw ledgerError(
2293
+ "CC_EVOLUTION_LEDGER_RECEIPT_INVALID",
2294
+ "receipt witness checkpoint is inconsistent",
2295
+ );
2296
+ }
2297
+ } catch (cause) {
2298
+ throw ledgerError(
2299
+ "CC_EVOLUTION_LEDGER_RECEIPT_INVALID",
2300
+ "receipt signature is invalid",
2301
+ { cause },
2302
+ );
2303
+ }
2304
+ return deepFreeze({ ...core, receiptDigest, signature });
2305
+ }
2306
+
2307
+ #verifyWitnessRecord(record) {
2308
+ try {
2309
+ const normalized = normalizeWitnessRecordShape(
2310
+ record,
2311
+ this.#witnessTrust,
2312
+ );
2313
+ const core = Object.fromEntries(
2314
+ [...WITNESS_CORE_KEYS].map((key) => [key, normalized[key]]),
2315
+ );
2316
+ this.#assertWitnessTrustBinding(core);
2317
+ const witnessDigest = normalized.witnessDigest;
2318
+ const signature = normalized.signature;
2319
+ const message = signedMessage(WITNESS_DOMAIN, core);
2320
+ if (witnessDigest !== sha256(message)) {
2321
+ throw ledgerError(
2322
+ "CC_EVOLUTION_LEDGER_WITNESS_INVALID",
2323
+ "witness digest does not match its signed state",
2324
+ );
2325
+ }
2326
+ this.#verifyWitnessMessage({
2327
+ digest: witnessDigest,
2328
+ message,
2329
+ purpose: "witness",
2330
+ signature,
2331
+ });
2332
+ return deepFreeze({ ...core, signature, witnessDigest });
2333
+ } catch (cause) {
2334
+ if (cause?.code === "CC_EVOLUTION_LEDGER_WITNESS_INVALID") throw cause;
2335
+ throw ledgerError(
2336
+ "CC_EVOLUTION_LEDGER_WITNESS_INVALID",
2337
+ "witness returned an invalid authenticated state",
2338
+ { cause },
2339
+ );
2340
+ }
2341
+ }
2342
+
2343
+ #verifyWitnessAncestryRecord(record) {
2344
+ try {
2345
+ assertExactKeys(
2346
+ record,
2347
+ WITNESS_ANCESTRY_RECORD_KEYS,
2348
+ "witness ancestry record",
2349
+ );
2350
+ const core = normalizeWitnessAncestryCore(
2351
+ Object.fromEntries(
2352
+ [...WITNESS_ANCESTRY_CORE_KEYS].map((key) => [key, record[key]]),
2353
+ ),
2354
+ );
2355
+ this.#assertWitnessTrustBinding(core);
2356
+ const proofDigest = digest(record.proofDigest, "witness proofDigest");
2357
+ const signature = normalizeSignature(
2358
+ record.signature,
2359
+ this.#witnessTrust,
2360
+ );
2361
+ const message = signedMessage(WITNESS_ANCESTRY_DOMAIN, core);
2362
+ if (proofDigest !== sha256(message)) {
2363
+ throw ledgerError(
2364
+ "CC_EVOLUTION_LEDGER_WITNESS_INVALID",
2365
+ "witness ancestry digest does not match its signed proof",
2366
+ );
2367
+ }
2368
+ this.#verifyWitnessMessage({
2369
+ digest: proofDigest,
2370
+ message,
2371
+ purpose: "witness-ancestry",
2372
+ signature,
2373
+ });
2374
+ return deepFreeze({ ...core, proofDigest, signature });
2375
+ } catch (cause) {
2376
+ if (cause?.code === "CC_EVOLUTION_LEDGER_WITNESS_INVALID") throw cause;
2377
+ throw ledgerError(
2378
+ "CC_EVOLUTION_LEDGER_WITNESS_INVALID",
2379
+ "witness returned an invalid ancestry proof",
2380
+ { cause },
2381
+ );
2382
+ }
2383
+ }
2384
+
2385
+ #callWitness(method, payload) {
2386
+ let output;
2387
+ try {
2388
+ output = this.#witness[method](frozenClone(payload));
2389
+ } catch (cause) {
2390
+ throw ledgerError(
2391
+ "CC_EVOLUTION_LEDGER_WITNESS_UNAVAILABLE",
2392
+ `witness ${method} failed closed`,
2393
+ { cause },
2394
+ );
2395
+ }
2396
+ if (isUnsafeAsyncResult(output)) {
2397
+ throw ledgerError(
2398
+ "CC_EVOLUTION_LEDGER_WITNESS_UNAVAILABLE",
2399
+ "witness ports must be synchronous",
2400
+ );
2401
+ }
2402
+ return this.#verifyWitnessRecord(output);
2403
+ }
2404
+
2405
+ #callWitnessAncestry(payload) {
2406
+ let output;
2407
+ try {
2408
+ output = this.#witness.proveAncestry(frozenClone(payload));
2409
+ } catch (cause) {
2410
+ throw ledgerError(
2411
+ "CC_EVOLUTION_LEDGER_WITNESS_UNAVAILABLE",
2412
+ "witness ancestry proof failed closed",
2413
+ { cause },
2414
+ );
2415
+ }
2416
+ if (isUnsafeAsyncResult(output)) {
2417
+ throw ledgerError(
2418
+ "CC_EVOLUTION_LEDGER_WITNESS_UNAVAILABLE",
2419
+ "witness ancestry port must be synchronous",
2420
+ );
2421
+ }
2422
+ return this.#verifyWitnessAncestryRecord(output);
2423
+ }
2424
+
2425
+ #proveWitnessAncestry(ancestor, descendant) {
2426
+ const proof = this.#callWitnessAncestry({
2427
+ ancestor,
2428
+ descendant,
2429
+ ledgerTrust: this.#trust,
2430
+ witnessId: this.#witness.id,
2431
+ witnessTrust: this.#witnessTrust,
2432
+ });
2433
+ if (
2434
+ proof.ancestorDigest !== ancestor.witnessDigest ||
2435
+ proof.ancestorGeneration !== ancestor.generation ||
2436
+ proof.descendantDigest !== descendant.witnessDigest ||
2437
+ proof.descendantGeneration !== descendant.generation ||
2438
+ proof.ledgerId !== ancestor.ledgerId ||
2439
+ proof.ledgerId !== descendant.ledgerId ||
2440
+ proof.epoch !== ancestor.epoch ||
2441
+ proof.epoch !== descendant.epoch
2442
+ ) {
2443
+ throw ledgerError(
2444
+ "CC_EVOLUTION_LEDGER_WITNESS_INVALID",
2445
+ "witness ancestry proof is not bound to both checkpoints",
2446
+ );
2447
+ }
2448
+ return proof;
2449
+ }
2450
+
2451
+ #readWitness() {
2452
+ return this.#callWitness("read", {
2453
+ ledgerTrust: this.#trust,
2454
+ witnessTrust: this.#witnessTrust,
2455
+ witnessId: this.#witness.id,
2456
+ });
2457
+ }
2458
+
2459
+ #witnessSnapshot(identity, storeMarker, anchor, event = null) {
2460
+ return deepFreeze({
2461
+ algorithm: this.#witnessTrust.algorithm,
2462
+ anchorDigest: anchor.anchorDigest,
2463
+ epoch: identity.epoch,
2464
+ headDigest: anchor.headDigest,
2465
+ identityDigest: identity.identityDigest,
2466
+ keyId: this.#witnessTrust.keyId,
2467
+ ledgerId: identity.ledgerId,
2468
+ payloadDigest: domainDigest(WITNESS_PAYLOAD_DOMAIN, {
2469
+ anchor,
2470
+ event,
2471
+ identity,
2472
+ storeMarker,
2473
+ }),
2474
+ segmentDigest: anchor.segmentDigest,
2475
+ sequence: anchor.sequence,
2476
+ storeMarkerDigest: identity.storeMarkerDigest,
2477
+ storeMarkerEntryDigest: identity.storeMarkerEntryDigest,
2478
+ storeMarkerId: identity.storeMarkerId,
2479
+ trustPolicyDigest: this.#witnessTrust.trustPolicyDigest,
2480
+ witnessId: this.#witness.id,
2481
+ });
2482
+ }
2483
+
2484
+ #discardAccumulator(previous, discard) {
2485
+ return domainDigest(DISCARD_ACCUMULATOR_DOMAIN, {
2486
+ discard,
2487
+ previousDiscardAccumulatorDigest: previous.discardAccumulatorDigest,
2488
+ previousWitnessDigest: previous.witnessDigest,
2489
+ });
2490
+ }
2491
+
2492
+ #assertWitnessSnapshot(witness, identity, storeMarker, anchor, event = null) {
2493
+ const snapshot = this.#witnessSnapshot(
2494
+ identity,
2495
+ storeMarker,
2496
+ anchor,
2497
+ event,
2498
+ );
2499
+ for (const [key, value] of Object.entries(snapshot)) {
2500
+ if (witness[key] !== value) {
2501
+ throw ledgerError(
2502
+ "CC_EVOLUTION_LEDGER_WITNESS_CONFLICT",
2503
+ `witness does not match the authenticated local ${key}`,
2504
+ );
2505
+ }
2506
+ }
2507
+ }
2508
+
2509
+ #assertWitnessTransition(previous, next, { discard = null } = {}) {
2510
+ const expectedAccumulator = discard
2511
+ ? this.#discardAccumulator(previous, discard)
2512
+ : previous.discardAccumulatorDigest;
2513
+ if (
2514
+ next.generation !== previous.generation + 1 ||
2515
+ next.previousWitnessDigest !== previous.witnessDigest ||
2516
+ next.discardAccumulatorDigest !== expectedAccumulator
2517
+ ) {
2518
+ throw ledgerError(
2519
+ "CC_EVOLUTION_LEDGER_WITNESS_CONFLICT",
2520
+ "witness did not persist the required monotonic transition",
2521
+ );
2522
+ }
2523
+ }
2524
+
2525
+ #confirmWitnessPersistence(expected) {
2526
+ const observed = this.#readWitness();
2527
+ if (observed.witnessDigest !== expected.witnessDigest) {
2528
+ throw ledgerError(
2529
+ "CC_EVOLUTION_LEDGER_WITNESS_CONFLICT",
2530
+ "witness CAS response was not durably observable",
2531
+ );
2532
+ }
2533
+ return observed;
2534
+ }
2535
+
2536
+ #initializeWitness(previous, identity, storeMarker, genesis) {
2537
+ const snapshot = this.#witnessSnapshot(identity, storeMarker, genesis);
2538
+ const witness = this.#callWitness("initialize", {
2539
+ anchor: genesis,
2540
+ expected: previous,
2541
+ identity,
2542
+ ledgerTrust: this.#trust,
2543
+ operationId: identity.identityDigest,
2544
+ snapshot,
2545
+ storeMarker,
2546
+ witnessTrust: this.#witnessTrust,
2547
+ witnessId: this.#witness.id,
2548
+ });
2549
+ if (witness.status !== "committed") {
2550
+ throw ledgerError(
2551
+ "CC_EVOLUTION_LEDGER_WITNESS_CONFLICT",
2552
+ "witness did not commit the ledger bootstrap",
2553
+ );
2554
+ }
2555
+ this.#assertWitnessTransition(previous, witness);
2556
+ this.#assertWitnessSnapshot(witness, identity, storeMarker, genesis);
2557
+ return this.#confirmWitnessPersistence(witness);
2558
+ }
2559
+
2560
+ #advanceWitness(previous, identity, storeMarker, anchor, event) {
2561
+ const next = this.#witnessSnapshot(identity, storeMarker, anchor, event);
2562
+ const witness = this.#callWitness("compareAndSwap", {
2563
+ anchor,
2564
+ expected: previous,
2565
+ event,
2566
+ ledgerTrust: this.#trust,
2567
+ next,
2568
+ operationId: event.eventDigest,
2569
+ storeMarker,
2570
+ witnessTrust: this.#witnessTrust,
2571
+ witnessId: this.#witness.id,
2572
+ });
2573
+ if (witness.status !== "committed") {
2574
+ throw ledgerError(
2575
+ "CC_EVOLUTION_LEDGER_WITNESS_CONFLICT",
2576
+ "witness did not commit the next ledger head",
2577
+ );
2578
+ }
2579
+ this.#assertWitnessTransition(previous, witness);
2580
+ this.#assertWitnessSnapshot(witness, identity, storeMarker, anchor, event);
2581
+ return this.#confirmWitnessPersistence(witness);
2582
+ }
2583
+
2584
+ #fenceUnwitnessedTail(
2585
+ witness,
2586
+ identity,
2587
+ storeMarker,
2588
+ currentAnchor,
2589
+ currentEvent,
2590
+ orphanAnchor,
2591
+ ) {
2592
+ this.#requireDirectoryDurability([
2593
+ this.#paths.segmentDir,
2594
+ this.#paths.anchorDir,
2595
+ ]);
2596
+ const discard = {
2597
+ anchorDigest: orphanAnchor.anchorDigest,
2598
+ headDigest: orphanAnchor.headDigest,
2599
+ segmentDigest: orphanAnchor.segmentDigest,
2600
+ sequence: orphanAnchor.sequence,
2601
+ };
2602
+ const confirmed = this.#callWitness("compareAndSwap", {
2603
+ discard,
2604
+ expected: witness,
2605
+ ledgerTrust: this.#trust,
2606
+ next: this.#witnessSnapshot(
2607
+ identity,
2608
+ storeMarker,
2609
+ currentAnchor,
2610
+ currentEvent,
2611
+ ),
2612
+ operationId: `discard:${orphanAnchor.anchorDigest}`,
2613
+ storeMarker,
2614
+ witnessTrust: this.#witnessTrust,
2615
+ witnessId: this.#witness.id,
2616
+ });
2617
+ if (confirmed.status !== "committed") {
2618
+ throw ledgerError(
2619
+ "CC_EVOLUTION_LEDGER_WITNESS_CONFLICT",
2620
+ "witness changed before the unwitnessed tail was fenced",
2621
+ );
2622
+ }
2623
+ this.#assertWitnessTransition(witness, confirmed, { discard });
2624
+ this.#assertWitnessSnapshot(
2625
+ confirmed,
2626
+ identity,
2627
+ storeMarker,
2628
+ currentAnchor,
2629
+ currentEvent,
2630
+ );
2631
+ return this.#confirmWitnessPersistence(confirmed);
2632
+ }
2633
+
2634
+ #readCanonicalFile(filePath, maximum, label, { allowMissing = false } = {}) {
2635
+ this.#assertBoundaries();
2636
+ let before;
2637
+ try {
2638
+ before = this.#fs.lstatSync(filePath);
2639
+ } catch (cause) {
2640
+ if (allowMissing && cause?.code === "ENOENT") return null;
2641
+ throw ledgerError(
2642
+ cause?.code === "ENOENT"
2643
+ ? "CC_EVOLUTION_LEDGER_CORRUPT"
2644
+ : "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
2645
+ `${label} is missing or inaccessible`,
2646
+ { cause },
2647
+ );
2648
+ }
2649
+ if (
2650
+ !before.isFile() ||
2651
+ before.isSymbolicLink() ||
2652
+ before.nlink !== 1 ||
2653
+ before.size < 2 ||
2654
+ before.size > maximum ||
2655
+ !samePath(realpath(this.#fs, filePath), filePath)
2656
+ ) {
2657
+ throw ledgerError(
2658
+ "CC_EVOLUTION_LEDGER_CORRUPT",
2659
+ `${label} must be a bounded, single-link, non-symlink file`,
2660
+ );
2661
+ }
2662
+ let descriptor = null;
2663
+ try {
2664
+ descriptor = this.#fs.openSync(
2665
+ filePath,
2666
+ this.#fs.constants.O_RDONLY | (this.#fs.constants.O_NOFOLLOW || 0),
2667
+ );
2668
+ const opened = this.#fs.fstatSync(descriptor);
2669
+ if (
2670
+ !opened.isFile() ||
2671
+ opened.nlink !== 1 ||
2672
+ entryIdentity(opened) !== entryIdentity(before) ||
2673
+ opened.size !== before.size
2674
+ ) {
2675
+ throw ledgerError(
2676
+ "CC_EVOLUTION_LEDGER_CORRUPT",
2677
+ `${label} changed while it was opened`,
2678
+ );
2679
+ }
2680
+ const bytes = this.#fs.readFileSync(descriptor);
2681
+ const after = this.#fs.fstatSync(descriptor);
2682
+ if (
2683
+ after.nlink !== 1 ||
2684
+ entryIdentity(after) !== entryIdentity(opened) ||
2685
+ after.size !== opened.size ||
2686
+ bytes.length !== opened.size
2687
+ ) {
2688
+ throw ledgerError(
2689
+ "CC_EVOLUTION_LEDGER_CORRUPT",
2690
+ `${label} changed while it was read`,
2691
+ );
2692
+ }
2693
+ let text;
2694
+ try {
2695
+ text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
2696
+ } catch (cause) {
2697
+ throw ledgerError(
2698
+ "CC_EVOLUTION_LEDGER_CORRUPT",
2699
+ `${label} is not valid UTF-8`,
2700
+ { cause },
2701
+ );
2702
+ }
2703
+ if (!text.endsWith("\n") || text.slice(0, -1).includes("\n")) {
2704
+ throw ledgerError(
2705
+ "CC_EVOLUTION_LEDGER_CORRUPT",
2706
+ `${label} is truncated or not a single canonical record`,
2707
+ );
2708
+ }
2709
+ let record;
2710
+ try {
2711
+ record = JSON.parse(text.slice(0, -1));
2712
+ } catch (cause) {
2713
+ throw ledgerError(
2714
+ "CC_EVOLUTION_LEDGER_CORRUPT",
2715
+ `${label} contains invalid JSON`,
2716
+ { cause },
2717
+ );
2718
+ }
2719
+ if (`${canonicalJson(record)}\n` !== text) {
2720
+ throw ledgerError(
2721
+ "CC_EVOLUTION_LEDGER_CORRUPT",
2722
+ `${label} bytes are not canonical`,
2723
+ );
2724
+ }
2725
+ return {
2726
+ bytes,
2727
+ contentDigest: sha256(bytes),
2728
+ fingerprint: fileFingerprint(after),
2729
+ record,
2730
+ };
2731
+ } finally {
2732
+ if (descriptor !== null) this.#fs.closeSync(descriptor);
2733
+ this.#assertBoundaries();
2734
+ }
2735
+ }
2736
+
2737
+ #writeImmutable(filePath, bytes, label, onPublished = null) {
2738
+ const directory = path.dirname(filePath);
2739
+ const temporaryPath = path.join(
2740
+ directory,
2741
+ `.stage-${path.basename(filePath)}.${randomToken(this.#random)}.tmp`,
2742
+ );
2743
+ if (!isContained(directory, temporaryPath)) {
2744
+ throw ledgerError(
2745
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
2746
+ `${label} staging path escaped its directory`,
2747
+ );
2748
+ }
2749
+ let descriptor = null;
2750
+ let temporaryExists = false;
2751
+ let published = false;
2752
+ try {
2753
+ this.#assertBoundaries();
2754
+ descriptor = this.#fs.openSync(temporaryPath, "wx", 0o600);
2755
+ temporaryExists = true;
2756
+ this.#fs.writeFileSync(descriptor, bytes);
2757
+ this.#fs.fsyncSync(descriptor);
2758
+ const staged = this.#fs.fstatSync(descriptor);
2759
+ if (
2760
+ !staged.isFile() ||
2761
+ staged.nlink !== 1 ||
2762
+ staged.size !== bytes.length
2763
+ ) {
2764
+ throw ledgerError(
2765
+ "CC_EVOLUTION_LEDGER_WRITE_FAILED",
2766
+ `${label} was not staged completely`,
2767
+ );
2768
+ }
2769
+ this.#fs.closeSync(descriptor);
2770
+ descriptor = null;
2771
+ if (this.#secure) {
2772
+ ensurePrivateFile(temporaryPath, {
2773
+ applyWindowsAcl: true,
2774
+ failIfUnavailable: true,
2775
+ });
2776
+ }
2777
+ this.#fs.linkSync(temporaryPath, filePath);
2778
+ published = true;
2779
+ if (onPublished) onPublished(temporaryPath);
2780
+ this.#fs.unlinkSync(temporaryPath);
2781
+ temporaryExists = false;
2782
+ if (this.#secure) {
2783
+ ensurePrivateFile(filePath, {
2784
+ applyWindowsAcl: true,
2785
+ failIfUnavailable: true,
2786
+ });
2787
+ }
2788
+ const finalStat = this.#fs.lstatSync(filePath);
2789
+ if (
2790
+ !finalStat.isFile() ||
2791
+ finalStat.isSymbolicLink() ||
2792
+ finalStat.nlink !== 1 ||
2793
+ finalStat.size !== bytes.length
2794
+ ) {
2795
+ throw ledgerError(
2796
+ "CC_EVOLUTION_LEDGER_WRITE_FAILED",
2797
+ `${label} publication did not produce a single-link artifact`,
2798
+ );
2799
+ }
2800
+ descriptor = this.#fs.openSync(
2801
+ filePath,
2802
+ this.#fs.constants.O_RDWR | (this.#fs.constants.O_NOFOLLOW || 0),
2803
+ );
2804
+ this.#fs.fsyncSync(descriptor);
2805
+ this.#fs.closeSync(descriptor);
2806
+ descriptor = null;
2807
+ return syncDirectory(this.#fs, directory);
2808
+ } catch (cause) {
2809
+ if (cause instanceof EvolutionLedgerError) throw cause;
2810
+ throw ledgerError(
2811
+ "CC_EVOLUTION_LEDGER_WRITE_FAILED",
2812
+ `${label} immutable publication failed`,
2813
+ { cause, published },
2814
+ );
2815
+ } finally {
2816
+ if (descriptor !== null) {
2817
+ try {
2818
+ this.#fs.closeSync(descriptor);
2819
+ } catch {
2820
+ // Preserve the persistence error.
2821
+ }
2822
+ }
2823
+ if (temporaryExists && !published) {
2824
+ try {
2825
+ this.#fs.unlinkSync(temporaryPath);
2826
+ } catch {
2827
+ // Unpublished staging debris is recovered under the next lock.
2828
+ }
2829
+ }
2830
+ }
2831
+ }
2832
+
2833
+ #replaceHead(bytes) {
2834
+ const temporaryPath = path.join(
2835
+ this.#paths.authorityRootDir,
2836
+ `.replace-${HEAD_FILE_NAME}.${randomToken(this.#random)}.tmp`,
2837
+ );
2838
+ let descriptor = null;
2839
+ let renamed = false;
2840
+ try {
2841
+ descriptor = this.#fs.openSync(temporaryPath, "wx", 0o600);
2842
+ this.#fs.writeFileSync(descriptor, bytes);
2843
+ this.#fs.fsyncSync(descriptor);
2844
+ const staged = this.#fs.fstatSync(descriptor);
2845
+ if (
2846
+ !staged.isFile() ||
2847
+ staged.nlink !== 1 ||
2848
+ staged.size !== bytes.length
2849
+ ) {
2850
+ throw ledgerError(
2851
+ "CC_EVOLUTION_LEDGER_WRITE_FAILED",
2852
+ "HEAD anchor was not staged completely",
2853
+ );
2854
+ }
2855
+ this.#fs.closeSync(descriptor);
2856
+ descriptor = null;
2857
+ if (this.#secure) {
2858
+ ensurePrivateFile(temporaryPath, {
2859
+ applyWindowsAcl: true,
2860
+ failIfUnavailable: true,
2861
+ });
2862
+ }
2863
+ this.#fs.renameSync(temporaryPath, this.#paths.headPath);
2864
+ renamed = true;
2865
+ if (this.#secure) {
2866
+ ensurePrivateFile(this.#paths.headPath, {
2867
+ applyWindowsAcl: true,
2868
+ failIfUnavailable: true,
2869
+ });
2870
+ }
2871
+ const headStat = this.#fs.lstatSync(this.#paths.headPath);
2872
+ if (
2873
+ !headStat.isFile() ||
2874
+ headStat.isSymbolicLink() ||
2875
+ headStat.nlink !== 1 ||
2876
+ headStat.size !== bytes.length
2877
+ ) {
2878
+ throw ledgerError(
2879
+ "CC_EVOLUTION_LEDGER_WRITE_FAILED",
2880
+ "HEAD replacement did not produce a single-link artifact",
2881
+ );
2882
+ }
2883
+ descriptor = this.#fs.openSync(
2884
+ this.#paths.headPath,
2885
+ this.#fs.constants.O_RDWR | (this.#fs.constants.O_NOFOLLOW || 0),
2886
+ );
2887
+ this.#fs.fsyncSync(descriptor);
2888
+ this.#fs.closeSync(descriptor);
2889
+ descriptor = null;
2890
+ return syncDirectory(this.#fs, this.#paths.authorityRootDir);
2891
+ } catch (cause) {
2892
+ if (cause instanceof EvolutionLedgerError) throw cause;
2893
+ throw ledgerError(
2894
+ renamed
2895
+ ? "CC_EVOLUTION_LEDGER_COMMIT_UNKNOWN"
2896
+ : "CC_EVOLUTION_LEDGER_WRITE_FAILED",
2897
+ renamed
2898
+ ? "HEAD replacement may be durable"
2899
+ : "HEAD replacement failed before publication",
2900
+ { cause, commitState: renamed ? "unknown" : "not-committed" },
2901
+ );
2902
+ } finally {
2903
+ if (descriptor !== null) {
2904
+ try {
2905
+ this.#fs.closeSync(descriptor);
2906
+ } catch {
2907
+ // Preserve the persistence error.
2908
+ }
2909
+ }
2910
+ if (!renamed) {
2911
+ try {
2912
+ this.#fs.unlinkSync(temporaryPath);
2913
+ } catch {
2914
+ // A uniquely named HEAD staging file is recovered on reopen.
2915
+ }
2916
+ }
2917
+ }
2918
+ }
2919
+
2920
+ #requireDirectoryDurability(directories) {
2921
+ for (const directory of directories) {
2922
+ let mechanism;
2923
+ try {
2924
+ mechanism = syncDirectory(this.#fs, directory);
2925
+ } catch (cause) {
2926
+ throw ledgerError(
2927
+ "CC_EVOLUTION_LEDGER_DURABILITY_UNAVAILABLE",
2928
+ `directory durability is unavailable for ${directory}`,
2929
+ { cause, commitState: "not-committed" },
2930
+ );
2931
+ }
2932
+ if (mechanism !== "directory-fsync") {
2933
+ throw ledgerError(
2934
+ "CC_EVOLUTION_LEDGER_DURABILITY_UNAVAILABLE",
2935
+ "the platform cannot durably publish ledger objects before witness CAS",
2936
+ { commitState: "not-committed", mechanism },
2937
+ );
2938
+ }
2939
+ }
2940
+ }
2941
+
2942
+ #cleanupDirectoryDebris(directory, targetPattern) {
2943
+ let changed = false;
2944
+ const entries = this.#fs.readdirSync(directory, { withFileTypes: true });
2945
+ for (const entry of entries) {
2946
+ const match = STAGE_FILE_PATTERN.exec(entry.name);
2947
+ if (!entry.name.startsWith(".stage-")) continue;
2948
+ if (!match) {
2949
+ throw ledgerError(
2950
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
2951
+ `malformed staging artifact: ${entry.name}`,
2952
+ );
2953
+ }
2954
+ const targetName = match[1];
2955
+ if (!targetPattern.test(targetName)) {
2956
+ throw ledgerError(
2957
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
2958
+ `unrecognized staging artifact: ${entry.name}`,
2959
+ );
2960
+ }
2961
+ const temporaryPath = path.join(directory, entry.name);
2962
+ const targetPath = path.join(directory, targetName);
2963
+ const temporaryStat = this.#fs.lstatSync(temporaryPath);
2964
+ if (!temporaryStat.isFile() || temporaryStat.isSymbolicLink()) {
2965
+ throw ledgerError(
2966
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
2967
+ `staging artifact is unsafe: ${entry.name}`,
2968
+ );
2969
+ }
2970
+ let targetStat = null;
2971
+ try {
2972
+ targetStat = this.#fs.lstatSync(targetPath);
2973
+ } catch (cause) {
2974
+ if (cause?.code !== "ENOENT") throw cause;
2975
+ }
2976
+ if (
2977
+ targetStat &&
2978
+ (!targetStat.isFile() ||
2979
+ targetStat.isSymbolicLink() ||
2980
+ entryIdentity(targetStat) !== entryIdentity(temporaryStat))
2981
+ ) {
2982
+ throw ledgerError(
2983
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
2984
+ `staging artifact conflicts with its authoritative path: ${entry.name}`,
2985
+ );
2986
+ }
2987
+ this.#fs.unlinkSync(temporaryPath);
2988
+ changed = true;
2989
+ if (targetStat) {
2990
+ const recovered = this.#fs.lstatSync(targetPath);
2991
+ if (recovered.nlink !== 1) {
2992
+ throw ledgerError(
2993
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
2994
+ `hard-link debris could not be removed: ${entry.name}`,
2995
+ );
2996
+ }
2997
+ }
2998
+ }
2999
+ if (changed) syncDirectory(this.#fs, directory);
3000
+ }
3001
+
3002
+ #cleanupDebris() {
3003
+ this.#cleanupDirectoryDebris(this.#paths.segmentDir, SEGMENT_FILE_PATTERN);
3004
+ this.#cleanupDirectoryDebris(this.#paths.anchorDir, ANCHOR_FILE_PATTERN);
3005
+ this.#cleanupDirectoryDebris(
3006
+ this.#paths.authorityRootDir,
3007
+ new RegExp(`^${IDENTITY_FILE_NAME.replace(".", "\\.")}$`, "u"),
3008
+ );
3009
+ this.#cleanupDirectoryDebris(
3010
+ this.#paths.rootDir,
3011
+ new RegExp(`^${STORE_MARKER_FILE_NAME.replace(".", "\\.")}$`, "u"),
3012
+ );
3013
+ let changed = false;
3014
+ for (const entry of this.#fs.readdirSync(this.#paths.authorityRootDir, {
3015
+ withFileTypes: true,
3016
+ })) {
3017
+ if (!entry.name.startsWith(".replace-")) continue;
3018
+ if (!HEAD_STAGE_FILE_PATTERN.test(entry.name)) {
3019
+ throw ledgerError(
3020
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
3021
+ `malformed HEAD staging artifact: ${entry.name}`,
3022
+ );
3023
+ }
3024
+ const filePath = path.join(this.#paths.authorityRootDir, entry.name);
3025
+ const stat = this.#fs.lstatSync(filePath);
3026
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1) {
3027
+ throw ledgerError(
3028
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
3029
+ "HEAD staging debris is unsafe",
3030
+ );
3031
+ }
3032
+ this.#fs.unlinkSync(filePath);
3033
+ changed = true;
3034
+ }
3035
+ if (changed) syncDirectory(this.#fs, this.#paths.authorityRootDir);
3036
+ }
3037
+
3038
+ #listFiles(directory, pattern, label) {
3039
+ const output = [];
3040
+ for (const entry of this.#fs.readdirSync(directory, {
3041
+ withFileTypes: true,
3042
+ })) {
3043
+ if (entry.name.startsWith(".stage-")) continue;
3044
+ if (
3045
+ !entry.isFile() ||
3046
+ entry.isSymbolicLink() ||
3047
+ !pattern.test(entry.name)
3048
+ ) {
3049
+ throw ledgerError(
3050
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
3051
+ `${label} contains an unrecognized entry: ${entry.name}`,
3052
+ );
3053
+ }
3054
+ output.push(entry.name);
3055
+ }
3056
+ return output.sort();
3057
+ }
3058
+
3059
+ #buildStoreMarker({ createdAt, epoch, ledgerId, storeMarkerId }) {
3060
+ const core = normalizeStoreMarkerCore({
3061
+ algorithm: this.#trust.algorithm,
3062
+ createdAt,
3063
+ epoch,
3064
+ keyId: this.#trust.keyId,
3065
+ ledgerId,
3066
+ schema: EVOLUTION_LEDGER_STORE_MARKER_SCHEMA,
3067
+ storeBindingDigest: this.#storeBindingDigest,
3068
+ storeMarkerId,
3069
+ trustPolicyDigest: this.#trust.trustPolicyDigest,
3070
+ witnessAlgorithm: this.#witnessTrust.algorithm,
3071
+ witnessId: this.#witness.id,
3072
+ witnessKeyId: this.#witnessTrust.keyId,
3073
+ witnessTrustPolicyDigest: this.#witnessTrust.trustPolicyDigest,
3074
+ });
3075
+ return this.#signRecord(
3076
+ STORE_MARKER_DOMAIN,
3077
+ core,
3078
+ "storeMarkerDigest",
3079
+ "store-marker",
3080
+ );
3081
+ }
3082
+
3083
+ #buildNewStoreMarker() {
3084
+ const createdAt = clockTimestamp(this.#clock);
3085
+ const epoch = `epoch-${randomToken(this.#random)}`;
3086
+ const ledgerId = `ledger-${randomToken(this.#random)}`;
3087
+ const storeMarkerId = `marker-${randomToken(this.#random)}`;
3088
+ return this.#buildStoreMarker({
3089
+ createdAt,
3090
+ epoch,
3091
+ ledgerId,
3092
+ storeMarkerId,
3093
+ });
3094
+ }
3095
+
3096
+ #buildIdentity(storeMarker, storeMarkerFile) {
3097
+ const core = normalizeIdentityCore({
3098
+ algorithm: this.#trust.algorithm,
3099
+ createdAt: storeMarker.createdAt,
3100
+ epoch: storeMarker.epoch,
3101
+ keyId: this.#trust.keyId,
3102
+ ledgerId: storeMarker.ledgerId,
3103
+ schema: EVOLUTION_LEDGER_IDENTITY_SCHEMA,
3104
+ storeBindingDigest: this.#storeBindingDigest,
3105
+ storeMarkerDigest: storeMarker.storeMarkerDigest,
3106
+ storeMarkerEntryDigest: storeMarkerEntryDigest(storeMarkerFile),
3107
+ storeMarkerId: storeMarker.storeMarkerId,
3108
+ trustPolicyDigest: this.#trust.trustPolicyDigest,
3109
+ witnessAlgorithm: this.#witnessTrust.algorithm,
3110
+ witnessId: this.#witness.id,
3111
+ witnessKeyId: this.#witnessTrust.keyId,
3112
+ witnessTrustPolicyDigest: this.#witnessTrust.trustPolicyDigest,
3113
+ });
3114
+ return this.#signRecord(
3115
+ IDENTITY_DOMAIN,
3116
+ core,
3117
+ "identityDigest",
3118
+ "identity",
3119
+ );
3120
+ }
3121
+
3122
+ #buildAnchor(identity, previous, event = null, segmentDigest = null) {
3123
+ const core = normalizeAnchorCore({
3124
+ algorithm: this.#trust.algorithm,
3125
+ committedAt: event ? clockTimestamp(this.#clock) : identity.createdAt,
3126
+ epoch: identity.epoch,
3127
+ headDigest: event?.eventDigest || null,
3128
+ identityDigest: identity.identityDigest,
3129
+ keyId: this.#trust.keyId,
3130
+ ledgerId: identity.ledgerId,
3131
+ previousAnchorDigest: previous?.anchorDigest || null,
3132
+ schema: EVOLUTION_LEDGER_ANCHOR_SCHEMA,
3133
+ segmentDigest,
3134
+ sequence: event?.sequence || 0,
3135
+ trustPolicyDigest: this.#trust.trustPolicyDigest,
3136
+ witnessAlgorithm: this.#witnessTrust.algorithm,
3137
+ witnessId: this.#witness.id,
3138
+ witnessKeyId: this.#witnessTrust.keyId,
3139
+ witnessTrustPolicyDigest: this.#witnessTrust.trustPolicyDigest,
3140
+ });
3141
+ return this.#signRecord(ANCHOR_DOMAIN, core, "anchorDigest", "anchor");
3142
+ }
3143
+
3144
+ #initializeEmptyLedger() {
3145
+ const storeMarker = this.#buildNewStoreMarker();
3146
+ this.#writeImmutable(
3147
+ this.#paths.storeMarkerPath,
3148
+ serializeRecord(storeMarker, 64 * 1024, "store marker"),
3149
+ "store incarnation marker",
3150
+ );
3151
+ const storeMarkerFile = this.#readCanonicalFile(
3152
+ this.#paths.storeMarkerPath,
3153
+ 64 * 1024,
3154
+ "store incarnation marker",
3155
+ );
3156
+ this.#verifyStoreMarker(storeMarkerFile.record);
3157
+ const identity = this.#buildIdentity(storeMarker, storeMarkerFile);
3158
+ const identityBytes = serializeRecord(identity, 64 * 1024, "identity");
3159
+ this.#writeImmutable(
3160
+ this.#paths.identityPath,
3161
+ identityBytes,
3162
+ "ledger identity",
3163
+ );
3164
+ const genesis = this.#buildAnchor(identity, null);
3165
+ const genesisPath = path.join(
3166
+ this.#paths.anchorDir,
3167
+ anchorFileName(0, genesis.anchorDigest),
3168
+ );
3169
+ const genesisBytes = serializeRecord(genesis, 64 * 1024, "genesis anchor");
3170
+ this.#writeImmutable(genesisPath, genesisBytes, "genesis anchor");
3171
+ this.#replaceHead(genesisBytes);
3172
+ }
3173
+
3174
+ #recoverUnwitnessedBootstrap(identity, anchorNames, segmentNames, headFile) {
3175
+ if (segmentNames.length !== 0 || headFile || anchorNames.length > 1) {
3176
+ throw ledgerError(
3177
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3178
+ "unwitnessed bootstrap contains non-recoverable local material",
3179
+ );
3180
+ }
3181
+ const genesis = this.#buildAnchor(identity, null);
3182
+ const expectedName = anchorFileName(0, genesis.anchorDigest);
3183
+ let genesisRecord = genesis;
3184
+ if (anchorNames.length === 0) {
3185
+ const bytes = serializeRecord(genesis, 64 * 1024, "genesis anchor");
3186
+ this.#writeImmutable(
3187
+ path.join(this.#paths.anchorDir, expectedName),
3188
+ bytes,
3189
+ "recovered bootstrap genesis anchor",
3190
+ );
3191
+ } else {
3192
+ if (anchorNames[0] !== expectedName) {
3193
+ throw ledgerError(
3194
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3195
+ "unwitnessed bootstrap genesis filename is inconsistent",
3196
+ );
3197
+ }
3198
+ const persisted = this.#readCanonicalFile(
3199
+ path.join(this.#paths.anchorDir, expectedName),
3200
+ 64 * 1024,
3201
+ "bootstrap genesis anchor",
3202
+ );
3203
+ genesisRecord = this.#verifyAnchor(persisted.record, null, identity);
3204
+ }
3205
+ this.#replaceHead(
3206
+ serializeRecord(genesisRecord, 64 * 1024, "genesis anchor"),
3207
+ );
3208
+ }
3209
+
3210
+ #matchesCachedFile(filePath, expected) {
3211
+ let descriptor = null;
3212
+ try {
3213
+ const before = this.#fs.lstatSync(filePath);
3214
+ if (
3215
+ !before.isFile() ||
3216
+ before.isSymbolicLink() ||
3217
+ before.nlink !== 1 ||
3218
+ fileFingerprint(before) !== expected.fingerprint ||
3219
+ !samePath(realpath(this.#fs, filePath), filePath)
3220
+ ) {
3221
+ return false;
3222
+ }
3223
+ descriptor = this.#fs.openSync(
3224
+ filePath,
3225
+ this.#fs.constants.O_RDONLY | (this.#fs.constants.O_NOFOLLOW || 0),
3226
+ );
3227
+ const opened = this.#fs.fstatSync(descriptor);
3228
+ if (
3229
+ !opened.isFile() ||
3230
+ opened.nlink !== 1 ||
3231
+ entryIdentity(opened) !== entryIdentity(before) ||
3232
+ opened.size !== before.size
3233
+ ) {
3234
+ return false;
3235
+ }
3236
+ const bytes = this.#fs.readFileSync(descriptor);
3237
+ const after = this.#fs.fstatSync(descriptor);
3238
+ return (
3239
+ after.nlink === 1 &&
3240
+ entryIdentity(after) === entryIdentity(opened) &&
3241
+ fileFingerprint(after) === expected.fingerprint &&
3242
+ bytes.length === opened.size &&
3243
+ sha256(bytes) === expected.contentDigest
3244
+ );
3245
+ } catch {
3246
+ return false;
3247
+ } finally {
3248
+ if (descriptor !== null) this.#fs.closeSync(descriptor);
3249
+ }
3250
+ }
3251
+
3252
+ #cachedPrefix(identity, anchorNames, segmentNames, incremental) {
3253
+ const cached = incremental ? this.#stateCache : null;
3254
+ if (
3255
+ !cached ||
3256
+ cached.state.identity.identityDigest !== identity.identityDigest ||
3257
+ cached.anchorNames.length > anchorNames.length ||
3258
+ cached.segmentNames.length > segmentNames.length ||
3259
+ cached.anchorNames.some((name, index) => name !== anchorNames[index]) ||
3260
+ cached.segmentNames.some((name, index) => name !== segmentNames[index])
3261
+ ) {
3262
+ return null;
3263
+ }
3264
+ for (const name of cached.anchorNames) {
3265
+ if (
3266
+ !this.#matchesCachedFile(
3267
+ path.join(this.#paths.anchorDir, name),
3268
+ cached.anchorFingerprints[name],
3269
+ )
3270
+ ) {
3271
+ return null;
3272
+ }
3273
+ }
3274
+ for (const name of cached.segmentNames) {
3275
+ if (
3276
+ !this.#matchesCachedFile(
3277
+ path.join(this.#paths.segmentDir, name),
3278
+ cached.segmentFingerprints[name],
3279
+ )
3280
+ ) {
3281
+ return null;
3282
+ }
3283
+ }
3284
+ return cached;
3285
+ }
3286
+
3287
+ #rememberState(
3288
+ state,
3289
+ anchorNames,
3290
+ segmentNames,
3291
+ anchorFingerprints,
3292
+ segmentFingerprints,
3293
+ ) {
3294
+ this.#stateCache = Object.freeze({
3295
+ anchorFingerprints: Object.freeze({ ...anchorFingerprints }),
3296
+ anchorNames: Object.freeze([...anchorNames]),
3297
+ segmentFingerprints: Object.freeze({ ...segmentFingerprints }),
3298
+ segmentNames: Object.freeze([...segmentNames]),
3299
+ state,
3300
+ });
3301
+ return state;
3302
+ }
3303
+
3304
+ #loadState({ allowInitialize = false, incremental = false } = {}) {
3305
+ this.#assertBoundaries();
3306
+ this.#cleanupDebris();
3307
+ let witnessed = this.#readWitness();
3308
+
3309
+ const identityFile = this.#readCanonicalFile(
3310
+ this.#paths.identityPath,
3311
+ 64 * 1024,
3312
+ "ledger identity",
3313
+ { allowMissing: true },
3314
+ );
3315
+ let storeMarkerFile = this.#readCanonicalFile(
3316
+ this.#paths.storeMarkerPath,
3317
+ 64 * 1024,
3318
+ "store incarnation marker",
3319
+ { allowMissing: true },
3320
+ );
3321
+ let anchorNames = this.#listFiles(
3322
+ this.#paths.anchorDir,
3323
+ ANCHOR_FILE_PATTERN,
3324
+ "anchor directory",
3325
+ );
3326
+ let segmentNames = this.#listFiles(
3327
+ this.#paths.segmentDir,
3328
+ SEGMENT_FILE_PATTERN,
3329
+ "segment directory",
3330
+ );
3331
+ let headFile = this.#readCanonicalFile(
3332
+ this.#paths.headPath,
3333
+ 64 * 1024,
3334
+ "ledger HEAD",
3335
+ { allowMissing: true },
3336
+ );
3337
+
3338
+ if (!identityFile) {
3339
+ if (
3340
+ witnessed.status !== "absent" ||
3341
+ anchorNames.length ||
3342
+ segmentNames.length ||
3343
+ headFile
3344
+ ) {
3345
+ throw ledgerError(
3346
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3347
+ "ledger identity is missing from an initialized or non-empty authority",
3348
+ );
3349
+ }
3350
+ if (!allowInitialize) {
3351
+ throw ledgerError(
3352
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3353
+ "ledger identity is missing after initialization",
3354
+ );
3355
+ }
3356
+ if (storeMarkerFile) {
3357
+ const storeMarker = this.#verifyStoreMarker(storeMarkerFile.record);
3358
+ const identity = this.#buildIdentity(storeMarker, storeMarkerFile);
3359
+ this.#writeImmutable(
3360
+ this.#paths.identityPath,
3361
+ serializeRecord(identity, 64 * 1024, "identity"),
3362
+ "recovered ledger identity",
3363
+ );
3364
+ return this.#loadState({
3365
+ allowInitialize: false,
3366
+ incremental: false,
3367
+ });
3368
+ }
3369
+ this.#initializeEmptyLedger();
3370
+ return this.#loadState({ allowInitialize: false, incremental: false });
3371
+ }
3372
+
3373
+ const identity = this.#verifyIdentity(identityFile.record);
3374
+ if (!storeMarkerFile) {
3375
+ throw ledgerError(
3376
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3377
+ "initialized ledger is missing its signed store incarnation marker",
3378
+ );
3379
+ }
3380
+ const storeMarker = this.#verifyStoreMarker(
3381
+ storeMarkerFile.record,
3382
+ identity,
3383
+ );
3384
+ if (
3385
+ identity.storeMarkerEntryDigest !==
3386
+ storeMarkerEntryDigest(storeMarkerFile)
3387
+ ) {
3388
+ throw ledgerError(
3389
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3390
+ "store incarnation marker file instance was replaced or replayed",
3391
+ );
3392
+ }
3393
+ if (anchorNames.length === 0 || !headFile) {
3394
+ if (witnessed.status === "absent") {
3395
+ this.#recoverUnwitnessedBootstrap(
3396
+ identity,
3397
+ anchorNames,
3398
+ segmentNames,
3399
+ headFile,
3400
+ );
3401
+ return this.#loadState({
3402
+ allowInitialize: false,
3403
+ incremental: false,
3404
+ });
3405
+ }
3406
+ throw ledgerError(
3407
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3408
+ "initialized ledger is missing its immutable genesis or authenticated HEAD",
3409
+ );
3410
+ }
3411
+
3412
+ if (anchorNames.length > EVOLUTION_LEDGER_MAX_EVENTS + 1) {
3413
+ throw ledgerError(
3414
+ "CC_EVOLUTION_LEDGER_CAPACITY_EXCEEDED",
3415
+ "anchor chain exceeds ledger capacity",
3416
+ );
3417
+ }
3418
+
3419
+ const cached = this.#cachedPrefix(
3420
+ identity,
3421
+ anchorNames,
3422
+ segmentNames,
3423
+ incremental,
3424
+ );
3425
+ const anchors = cached ? [...cached.state.anchors] : [];
3426
+ const events = cached ? [...cached.state.events] : [];
3427
+ const anchorFingerprints = cached ? { ...cached.anchorFingerprints } : {};
3428
+ const segmentFingerprints = cached ? { ...cached.segmentFingerprints } : {};
3429
+ const expectedSegments = new Set();
3430
+ const eventIds = new Set(events.map((event) => event.eventId));
3431
+ for (let index = 0; index < anchorNames.length; index += 1) {
3432
+ const name = anchorNames[index];
3433
+ const match = ANCHOR_FILE_PATTERN.exec(name);
3434
+ const fileSequence = Number(match[1]);
3435
+ if (fileSequence !== index) {
3436
+ throw ledgerError(
3437
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3438
+ `anchor prefix is missing or reordered at sequence ${index}`,
3439
+ );
3440
+ }
3441
+ if (index < anchors.length) {
3442
+ if (index > 0) {
3443
+ expectedSegments.add(
3444
+ segmentFileName(index, anchors[index].segmentDigest),
3445
+ );
3446
+ }
3447
+ continue;
3448
+ }
3449
+ const anchorFile = this.#readCanonicalFile(
3450
+ path.join(this.#paths.anchorDir, name),
3451
+ 64 * 1024,
3452
+ `anchor ${index}`,
3453
+ );
3454
+ const anchor = this.#verifyAnchor(
3455
+ anchorFile.record,
3456
+ anchors.at(-1),
3457
+ identity,
3458
+ );
3459
+ if (name !== anchorFileName(anchor.sequence, anchor.anchorDigest)) {
3460
+ throw ledgerError(
3461
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3462
+ `anchor filename is not content-addressed at sequence ${index}`,
3463
+ );
3464
+ }
3465
+ anchorFingerprints[name] = Object.freeze({
3466
+ contentDigest: anchorFile.contentDigest,
3467
+ fingerprint: anchorFile.fingerprint,
3468
+ });
3469
+ anchors.push(anchor);
3470
+ if (index === 0) continue;
3471
+
3472
+ const segmentName = segmentFileName(index, anchor.segmentDigest);
3473
+ expectedSegments.add(segmentName);
3474
+ const segmentFile = this.#readCanonicalFile(
3475
+ path.join(this.#paths.segmentDir, segmentName),
3476
+ EVOLUTION_LEDGER_MAX_EVENT_BYTES,
3477
+ `event segment ${index}`,
3478
+ );
3479
+ const event = this.#verifyEvent(
3480
+ segmentFile.record,
3481
+ events.at(-1),
3482
+ identity,
3483
+ );
3484
+ const segmentDigest = domainDigest(SEGMENT_DOMAIN, event);
3485
+ if (
3486
+ event.sequence !== index ||
3487
+ anchor.headDigest !== event.eventDigest ||
3488
+ anchor.segmentDigest !== segmentDigest ||
3489
+ segmentName !== segmentFileName(event.sequence, segmentDigest) ||
3490
+ eventIds.has(event.eventId)
3491
+ ) {
3492
+ throw ledgerError(
3493
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3494
+ `event/anchor binding failed at sequence ${index}`,
3495
+ );
3496
+ }
3497
+ segmentFingerprints[segmentName] = Object.freeze({
3498
+ contentDigest: segmentFile.contentDigest,
3499
+ fingerprint: segmentFile.fingerprint,
3500
+ });
3501
+ eventIds.add(event.eventId);
3502
+ events.push(event);
3503
+ }
3504
+
3505
+ let removedOrphan = false;
3506
+ for (const name of segmentNames) {
3507
+ if (expectedSegments.has(name)) continue;
3508
+ const match = SEGMENT_FILE_PATTERN.exec(name);
3509
+ const sequence = Number(match[1]);
3510
+ if (sequence !== events.length + 1) {
3511
+ throw ledgerError(
3512
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3513
+ `unanchored segment is not a recoverable tail: ${name}`,
3514
+ );
3515
+ }
3516
+ const filePath = path.join(this.#paths.segmentDir, name);
3517
+ const segmentFile = this.#readCanonicalFile(
3518
+ filePath,
3519
+ EVOLUTION_LEDGER_MAX_EVENT_BYTES,
3520
+ "unanchored tail segment",
3521
+ );
3522
+ const event = this.#verifyEvent(
3523
+ segmentFile.record,
3524
+ events.at(-1),
3525
+ identity,
3526
+ );
3527
+ const segmentDigest = domainDigest(SEGMENT_DOMAIN, event);
3528
+ if (name !== segmentFileName(sequence, segmentDigest)) {
3529
+ throw ledgerError(
3530
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3531
+ "unanchored tail segment is not content-addressed",
3532
+ );
3533
+ }
3534
+ this.#fs.unlinkSync(filePath);
3535
+ removedOrphan = true;
3536
+ }
3537
+ if (removedOrphan) syncDirectory(this.#fs, this.#paths.segmentDir);
3538
+
3539
+ const head = this.#verifyAnchor(headFile.record, null, identity, {
3540
+ standalone: true,
3541
+ });
3542
+ const matchingHead = anchors.find(
3543
+ (anchor) => anchor.anchorDigest === head.anchorDigest,
3544
+ );
3545
+ if (!matchingHead || matchingHead.sequence !== head.sequence) {
3546
+ throw ledgerError(
3547
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3548
+ "HEAD is not one of the authenticated immutable anchors",
3549
+ );
3550
+ }
3551
+ let maximumAnchor = anchors.at(-1);
3552
+ if (head.sequence > maximumAnchor.sequence) {
3553
+ throw ledgerError(
3554
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3555
+ "HEAD points beyond the immutable anchor chain",
3556
+ );
3557
+ }
3558
+ let authoritativeHead = head;
3559
+ if (witnessed.status === "absent") {
3560
+ if (
3561
+ anchors.length !== 1 ||
3562
+ events.length !== 0 ||
3563
+ head.sequence !== 0 ||
3564
+ maximumAnchor.sequence !== 0
3565
+ ) {
3566
+ throw ledgerError(
3567
+ "CC_EVOLUTION_LEDGER_WITNESS_CONFLICT",
3568
+ "an absent witness cannot bootstrap a previously used ledger",
3569
+ );
3570
+ }
3571
+ this.#requireDirectoryDurability([
3572
+ this.#paths.rootDir,
3573
+ this.#paths.segmentDir,
3574
+ this.#paths.authorityRootDir,
3575
+ this.#paths.anchorDir,
3576
+ ]);
3577
+ witnessed = this.#initializeWitness(
3578
+ witnessed,
3579
+ identity,
3580
+ storeMarker,
3581
+ maximumAnchor,
3582
+ );
3583
+ } else {
3584
+ if (witnessed.sequence > maximumAnchor.sequence) {
3585
+ throw ledgerError(
3586
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3587
+ "authenticated witness is ahead of the local immutable prefix",
3588
+ );
3589
+ }
3590
+ const witnessedAnchor = anchors[witnessed.sequence];
3591
+ if (!witnessedAnchor) {
3592
+ throw ledgerError(
3593
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3594
+ "authenticated witness does not have a local immutable anchor",
3595
+ );
3596
+ }
3597
+ const witnessedEvent =
3598
+ witnessed.sequence === 0 ? null : events[witnessed.sequence - 1];
3599
+ this.#assertWitnessSnapshot(
3600
+ witnessed,
3601
+ identity,
3602
+ storeMarker,
3603
+ witnessedAnchor,
3604
+ witnessedEvent,
3605
+ );
3606
+ if (witnessed.sequence < maximumAnchor.sequence) {
3607
+ if (
3608
+ head.anchorDigest !== witnessed.anchorDigest ||
3609
+ maximumAnchor.sequence !== witnessed.sequence + 1
3610
+ ) {
3611
+ throw ledgerError(
3612
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3613
+ "local immutable tail diverges from the authenticated witness",
3614
+ );
3615
+ }
3616
+ witnessed = this.#fenceUnwitnessedTail(
3617
+ witnessed,
3618
+ identity,
3619
+ storeMarker,
3620
+ witnessedAnchor,
3621
+ witnessedEvent,
3622
+ maximumAnchor,
3623
+ );
3624
+ const orphanAnchorName = anchorNames.at(-1);
3625
+ const orphanSegmentName = segmentFileName(
3626
+ maximumAnchor.sequence,
3627
+ maximumAnchor.segmentDigest,
3628
+ );
3629
+ this.#fs.unlinkSync(path.join(this.#paths.anchorDir, orphanAnchorName));
3630
+ syncDirectory(this.#fs, this.#paths.anchorDir);
3631
+ this.#fs.unlinkSync(
3632
+ path.join(this.#paths.segmentDir, orphanSegmentName),
3633
+ );
3634
+ syncDirectory(this.#fs, this.#paths.segmentDir);
3635
+ anchorNames.pop();
3636
+ anchors.pop();
3637
+ events.pop();
3638
+ expectedSegments.delete(orphanSegmentName);
3639
+ delete anchorFingerprints[orphanAnchorName];
3640
+ delete segmentFingerprints[orphanSegmentName];
3641
+ maximumAnchor = anchors.at(-1);
3642
+ }
3643
+ }
3644
+ if (witnessed.sequence !== maximumAnchor.sequence) {
3645
+ throw ledgerError(
3646
+ "CC_EVOLUTION_LEDGER_WITNESS_CONFLICT",
3647
+ "local immutable head is not committed by the authenticated witness",
3648
+ );
3649
+ }
3650
+ const maximumEvent =
3651
+ maximumAnchor.sequence === 0 ? null : events[maximumAnchor.sequence - 1];
3652
+ this.#assertWitnessSnapshot(
3653
+ witnessed,
3654
+ identity,
3655
+ storeMarker,
3656
+ maximumAnchor,
3657
+ maximumEvent,
3658
+ );
3659
+ if (head.sequence < maximumAnchor.sequence) {
3660
+ const bytes = serializeRecord(maximumAnchor, 64 * 1024, "head anchor");
3661
+ this.#replaceHead(bytes);
3662
+ authoritativeHead = maximumAnchor;
3663
+ } else if (head.anchorDigest !== maximumAnchor.anchorDigest) {
3664
+ throw ledgerError(
3665
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3666
+ "HEAD digest diverges from the authenticated witness",
3667
+ );
3668
+ }
3669
+
3670
+ const verifiedState = deepFreeze({
3671
+ anchors,
3672
+ events,
3673
+ head: authoritativeHead,
3674
+ identity,
3675
+ storeMarker,
3676
+ witness: witnessed,
3677
+ });
3678
+ return this.#rememberState(
3679
+ verifiedState,
3680
+ anchorNames,
3681
+ [...expectedSegments].sort(),
3682
+ anchorFingerprints,
3683
+ segmentFingerprints,
3684
+ );
3685
+ }
3686
+
3687
+ #artifactRefs(input) {
3688
+ const refs = Object.hasOwn(input, "subjectRef")
3689
+ ? [input.subjectRef, ...input.sourceRefs]
3690
+ : [
3691
+ ...input.sourceRefs,
3692
+ input.parentRef,
3693
+ input.candidateRef,
3694
+ input.diffRef,
3695
+ input.evalRef,
3696
+ input.policyRef,
3697
+ input.actorRef,
3698
+ input.targetRef,
3699
+ ].filter(Boolean);
3700
+ const byRef = new Map();
3701
+ for (const ref of refs) {
3702
+ const existing = byRef.get(ref.ref);
3703
+ if (existing && existing.digest !== ref.digest) {
3704
+ throw ledgerError(
3705
+ "CC_EVOLUTION_LEDGER_ARTIFACT_INVALID",
3706
+ `artifact ref is bound to conflicting digests: ${ref.ref}`,
3707
+ );
3708
+ }
3709
+ byRef.set(ref.ref, ref);
3710
+ }
3711
+ return [...byRef.values()].sort((left, right) =>
3712
+ compareStrings(left.ref, right.ref),
3713
+ );
3714
+ }
3715
+
3716
+ #resolveArtifacts(input, identity) {
3717
+ const validations = [];
3718
+ const artifactTenantId = Object.hasOwn(input, "artifactTenantId")
3719
+ ? input.artifactTenantId
3720
+ : input.tenantId;
3721
+ for (const ref of this.#artifactRefs(input)) {
3722
+ let resolution;
3723
+ try {
3724
+ resolution = this.#artifactResolver({
3725
+ epoch: identity.epoch,
3726
+ ledgerId: identity.ledgerId,
3727
+ ref: frozenClone(ref),
3728
+ tenantId: artifactTenantId,
3729
+ });
3730
+ } catch (cause) {
3731
+ throw ledgerError(
3732
+ "CC_EVOLUTION_LEDGER_ARTIFACT_UNAVAILABLE",
3733
+ `artifact resolver failed for ${ref.ref}`,
3734
+ { cause, ref: ref.ref },
3735
+ );
3736
+ }
3737
+ try {
3738
+ assertExactKeys(resolution, RESOLUTION_KEYS, "artifact resolution");
3739
+ if (
3740
+ resolution.schema !== EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA ||
3741
+ resolution.found !== true ||
3742
+ resolution.authenticated !== true ||
3743
+ resolution.ref !== ref.ref ||
3744
+ resolution.digest !== ref.digest
3745
+ ) {
3746
+ throw ledgerError(
3747
+ "CC_EVOLUTION_LEDGER_ARTIFACT_INVALID",
3748
+ `artifact resolution is not authenticated and exactly bound: ${ref.ref}`,
3749
+ );
3750
+ }
3751
+ const bytes = copyResolvedBytes(resolution.bytes);
3752
+ if (sha256(bytes) !== ref.digest) {
3753
+ throw ledgerError(
3754
+ "CC_EVOLUTION_LEDGER_ARTIFACT_INVALID",
3755
+ `artifact bytes do not match their digest: ${ref.ref}`,
3756
+ );
3757
+ }
3758
+ validations.push({
3759
+ byteLength: bytes.length,
3760
+ digest: ref.digest,
3761
+ receiptDigest: digest(
3762
+ resolution.receiptDigest,
3763
+ "artifact resolution receiptDigest",
3764
+ ),
3765
+ ref: ref.ref,
3766
+ });
3767
+ } catch (cause) {
3768
+ if (cause?.code === "CC_EVOLUTION_LEDGER_ARTIFACT_INVALID") {
3769
+ throw cause;
3770
+ }
3771
+ throw ledgerError(
3772
+ "CC_EVOLUTION_LEDGER_ARTIFACT_INVALID",
3773
+ `artifact resolution failed strict validation: ${ref.ref}`,
3774
+ { cause },
3775
+ );
3776
+ }
3777
+ }
3778
+ return domainDigest(ARTIFACT_VALIDATION_DOMAIN, validations);
3779
+ }
3780
+
3781
+ #buildEvent(input, previous, identity, artifactValidationDigest) {
3782
+ const core = normalizeEventCore({
3783
+ ...input,
3784
+ algorithm: this.#trust.algorithm,
3785
+ artifactValidationDigest,
3786
+ epoch: identity.epoch,
3787
+ identityDigest: identity.identityDigest,
3788
+ keyId: this.#trust.keyId,
3789
+ ledgerId: identity.ledgerId,
3790
+ prevDigest: previous?.eventDigest || null,
3791
+ schema: EVOLUTION_LEDGER_EVENT_SCHEMA,
3792
+ sequence: (previous?.sequence || 0) + 1,
3793
+ trustPolicyDigest: this.#trust.trustPolicyDigest,
3794
+ witnessAlgorithm: this.#witnessTrust.algorithm,
3795
+ witnessId: this.#witness.id,
3796
+ witnessKeyId: this.#witnessTrust.keyId,
3797
+ witnessTrustPolicyDigest: this.#witnessTrust.trustPolicyDigest,
3798
+ });
3799
+ return this.#signRecord(EVENT_DOMAIN, core, "eventDigest", "event");
3800
+ }
3801
+
3802
+ #buildDomainEvent(input, previous, identity, artifactValidationDigest) {
3803
+ const core = normalizeDomainEventCore({
3804
+ ...input,
3805
+ algorithm: this.#trust.algorithm,
3806
+ artifactValidationDigest,
3807
+ epoch: identity.epoch,
3808
+ identityDigest: identity.identityDigest,
3809
+ keyId: this.#trust.keyId,
3810
+ ledgerId: identity.ledgerId,
3811
+ prevDigest: previous?.eventDigest || null,
3812
+ schema: EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA,
3813
+ sequence: (previous?.sequence || 0) + 1,
3814
+ trustPolicyDigest: this.#trust.trustPolicyDigest,
3815
+ witnessAlgorithm: this.#witnessTrust.algorithm,
3816
+ witnessId: this.#witness.id,
3817
+ witnessKeyId: this.#witnessTrust.keyId,
3818
+ witnessTrustPolicyDigest: this.#witnessTrust.trustPolicyDigest,
3819
+ });
3820
+ return this.#signRecord(
3821
+ DOMAIN_EVENT_DOMAIN,
3822
+ core,
3823
+ "eventDigest",
3824
+ "domain-event",
3825
+ );
3826
+ }
3827
+
3828
+ #issueReceipt(state, event, anchor, durabilityMechanism) {
3829
+ const core = normalizeReceiptCore(
3830
+ {
3831
+ algorithm: this.#trust.algorithm,
3832
+ anchorDigest: anchor.anchorDigest,
3833
+ authenticated: true,
3834
+ committed: true,
3835
+ committedAt: anchor.committedAt,
3836
+ durabilityMechanism,
3837
+ durable: true,
3838
+ epoch: state.identity.epoch,
3839
+ eventDigest: event.eventDigest,
3840
+ eventId: event.eventId,
3841
+ headDigest: anchor.headDigest,
3842
+ headSignature: anchor.signature,
3843
+ identityDigest: state.identity.identityDigest,
3844
+ issuedAt: clockTimestamp(this.#clock),
3845
+ keyId: this.#trust.keyId,
3846
+ ledgerId: state.identity.ledgerId,
3847
+ persisted: true,
3848
+ schema: EVOLUTION_LEDGER_RECEIPT_SCHEMA,
3849
+ segmentDigest: anchor.segmentDigest,
3850
+ sequence: event.sequence,
3851
+ trustPolicyDigest: this.#trust.trustPolicyDigest,
3852
+ witnessAlgorithm: this.#witnessTrust.algorithm,
3853
+ witnessCheckpoint: state.witness,
3854
+ witnessDigest: state.witness.witnessDigest,
3855
+ witnessId: this.#witness.id,
3856
+ witnessKeyId: this.#witnessTrust.keyId,
3857
+ witnessTrustPolicyDigest: this.#witnessTrust.trustPolicyDigest,
3858
+ },
3859
+ this.#trust,
3860
+ this.#witnessTrust,
3861
+ );
3862
+ return this.#signRecord(RECEIPT_DOMAIN, core, "receiptDigest", "receipt");
3863
+ }
3864
+
3865
+ append(input, options = {}) {
3866
+ return this.#appendEvent(input, options, false);
3867
+ }
3868
+
3869
+ appendDomainEvent(input, options = {}) {
3870
+ return this.#appendEvent(input, options, true);
3871
+ }
3872
+
3873
+ #appendEvent(input, options, domainEvent) {
3874
+ const safeOptions = normalizeAppendOptions(options);
3875
+ const state = {
3876
+ event: null,
3877
+ receipt: null,
3878
+ witnessAttempted: false,
3879
+ witnessPublished: false,
3880
+ };
3881
+ try {
3882
+ return this.#withLock(() => {
3883
+ const current = this.#loadState({
3884
+ allowInitialize: false,
3885
+ incremental: true,
3886
+ });
3887
+ const previous = current.events.at(-1) || null;
3888
+ if (
3889
+ safeOptions.expectedSequence !== undefined &&
3890
+ safeOptions.expectedSequence !== (previous?.sequence || 0)
3891
+ ) {
3892
+ throw ledgerError(
3893
+ "CC_EVOLUTION_LEDGER_HEAD_CONFLICT",
3894
+ "ledger sequence changed before append",
3895
+ {
3896
+ actualSequence: previous?.sequence || 0,
3897
+ expectedSequence: safeOptions.expectedSequence,
3898
+ },
3899
+ );
3900
+ }
3901
+ if (
3902
+ safeOptions.expectedHeadDigest !== undefined &&
3903
+ safeOptions.expectedHeadDigest !== (previous?.eventDigest || null)
3904
+ ) {
3905
+ throw ledgerError(
3906
+ "CC_EVOLUTION_LEDGER_HEAD_CONFLICT",
3907
+ "ledger head digest changed before append",
3908
+ {
3909
+ actualHeadDigest: previous?.eventDigest || null,
3910
+ expectedHeadDigest: safeOptions.expectedHeadDigest,
3911
+ },
3912
+ );
3913
+ }
3914
+ const generatedTimestamp = clockTimestamp(this.#clock);
3915
+ const normalized = domainEvent
3916
+ ? normalizeDomainAppendInput(input, generatedTimestamp)
3917
+ : normalizeAppendInput(input, generatedTimestamp);
3918
+ if (
3919
+ current.events.some((event) => event.eventId === normalized.eventId)
3920
+ ) {
3921
+ throw ledgerError(
3922
+ "CC_EVOLUTION_LEDGER_EVENT_CONFLICT",
3923
+ `eventId already exists: ${normalized.eventId}`,
3924
+ );
3925
+ }
3926
+ if (current.events.length >= EVOLUTION_LEDGER_MAX_EVENTS) {
3927
+ throw ledgerError(
3928
+ "CC_EVOLUTION_LEDGER_CAPACITY_EXCEEDED",
3929
+ "evolution ledger capacity is exhausted",
3930
+ );
3931
+ }
3932
+ const artifactValidationDigest = this.#resolveArtifacts(
3933
+ normalized,
3934
+ current.identity,
3935
+ );
3936
+ const event = domainEvent
3937
+ ? this.#buildDomainEvent(
3938
+ normalized,
3939
+ previous,
3940
+ current.identity,
3941
+ artifactValidationDigest,
3942
+ )
3943
+ : this.#buildEvent(
3944
+ normalized,
3945
+ previous,
3946
+ current.identity,
3947
+ artifactValidationDigest,
3948
+ );
3949
+ state.event = event;
3950
+ const segmentDigest = domainDigest(SEGMENT_DOMAIN, event);
3951
+ const segmentPath = path.join(
3952
+ this.#paths.segmentDir,
3953
+ segmentFileName(event.sequence, segmentDigest),
3954
+ );
3955
+ const eventBytes = serializeRecord(
3956
+ event,
3957
+ EVOLUTION_LEDGER_MAX_EVENT_BYTES,
3958
+ "event segment",
3959
+ );
3960
+ this.#writeImmutable(
3961
+ segmentPath,
3962
+ eventBytes,
3963
+ "event segment",
3964
+ (temporaryPath) => {
3965
+ this.#invokeCrashHook("after-segment-link", {
3966
+ eventDigest: event.eventDigest,
3967
+ temporaryPath: path.basename(temporaryPath),
3968
+ });
3969
+ },
3970
+ );
3971
+ this.#invokeCrashHook("after-segment", {
3972
+ eventDigest: event.eventDigest,
3973
+ segmentDigest,
3974
+ });
3975
+
3976
+ const anchor = this.#buildAnchor(
3977
+ current.identity,
3978
+ current.anchors.at(-1),
3979
+ event,
3980
+ segmentDigest,
3981
+ );
3982
+ const anchorPath = path.join(
3983
+ this.#paths.anchorDir,
3984
+ anchorFileName(anchor.sequence, anchor.anchorDigest),
3985
+ );
3986
+ const anchorBytes = serializeRecord(anchor, 64 * 1024, "head anchor");
3987
+ this.#writeImmutable(
3988
+ anchorPath,
3989
+ anchorBytes,
3990
+ "head anchor",
3991
+ (temporaryPath) => {
3992
+ this.#invokeCrashHook("after-anchor-link", {
3993
+ anchorDigest: anchor.anchorDigest,
3994
+ temporaryPath: path.basename(temporaryPath),
3995
+ });
3996
+ },
3997
+ );
3998
+ this.#invokeCrashHook("after-anchor", {
3999
+ anchorDigest: anchor.anchorDigest,
4000
+ eventDigest: event.eventDigest,
4001
+ });
4002
+ this.#requireDirectoryDurability([
4003
+ this.#paths.segmentDir,
4004
+ this.#paths.anchorDir,
4005
+ ]);
4006
+ state.witnessAttempted = true;
4007
+ const witnessed = this.#advanceWitness(
4008
+ current.witness,
4009
+ current.identity,
4010
+ current.storeMarker,
4011
+ anchor,
4012
+ event,
4013
+ );
4014
+ state.witnessPublished = true;
4015
+ this.#invokeCrashHook("after-witness", {
4016
+ anchorDigest: anchor.anchorDigest,
4017
+ eventDigest: event.eventDigest,
4018
+ witnessDigest: witnessed.witnessDigest,
4019
+ });
4020
+ this.#replaceHead(anchorBytes);
4021
+ this.#invokeCrashHook("after-head", {
4022
+ anchorDigest: anchor.anchorDigest,
4023
+ eventDigest: event.eventDigest,
4024
+ });
4025
+
4026
+ const persisted = this.#loadState({
4027
+ allowInitialize: false,
4028
+ incremental: true,
4029
+ });
4030
+ const persistedEvent = persisted.events.at(-1);
4031
+ const persistedAnchor = persisted.anchors.at(-1);
4032
+ if (
4033
+ persistedEvent?.eventDigest !== event.eventDigest ||
4034
+ persistedAnchor?.anchorDigest !== anchor.anchorDigest ||
4035
+ persisted.head.anchorDigest !== anchor.anchorDigest
4036
+ ) {
4037
+ throw ledgerError(
4038
+ "CC_EVOLUTION_LEDGER_WRITE_FAILED",
4039
+ "event was not recovered under the authenticated HEAD",
4040
+ );
4041
+ }
4042
+ state.receipt = this.#issueReceipt(
4043
+ persisted,
4044
+ persistedEvent,
4045
+ persistedAnchor,
4046
+ "authenticated-witness-cas",
4047
+ );
4048
+ return state.receipt;
4049
+ });
4050
+ } catch (cause) {
4051
+ if (state.witnessAttempted || state.receipt) {
4052
+ throw ledgerError(
4053
+ "CC_EVOLUTION_LEDGER_COMMIT_UNKNOWN",
4054
+ "an authenticated anchor may be committed; reopen and query by eventId",
4055
+ {
4056
+ cause,
4057
+ commitState: "unknown",
4058
+ eventDigest: state.event?.eventDigest || null,
4059
+ eventId: state.event?.eventId || safeOwnDataValue(input, "eventId"),
4060
+ witnessPublished: state.witnessPublished,
4061
+ },
4062
+ );
4063
+ }
4064
+ if (cause instanceof EvolutionLedgerError) {
4065
+ if (!cause.commitState) cause.commitState = "not-committed";
4066
+ throw cause;
4067
+ }
4068
+ throw ledgerError(
4069
+ "CC_EVOLUTION_LEDGER_WRITE_FAILED",
4070
+ "evolution event failed before an authenticated anchor was published",
4071
+ {
4072
+ cause,
4073
+ commitState: "not-committed",
4074
+ eventId: safeOwnDataValue(input, "eventId"),
4075
+ },
4076
+ );
4077
+ }
4078
+ }
4079
+
4080
+ #queryState(state, selector) {
4081
+ return (
4082
+ state.events.find(
4083
+ (event) =>
4084
+ (selector.eventId === undefined ||
4085
+ event.eventId === selector.eventId) &&
4086
+ (selector.eventDigest === undefined ||
4087
+ event.eventDigest === selector.eventDigest) &&
4088
+ (selector.sequence === undefined ||
4089
+ event.sequence === selector.sequence),
4090
+ ) || null
4091
+ );
4092
+ }
4093
+
4094
+ query(selector, options = {}) {
4095
+ const safeSelector = normalizeQuerySelector(selector);
4096
+ assertExactKeys(options, QUERY_OPTION_KEYS, "query options", {
4097
+ optional: [...QUERY_OPTION_KEYS],
4098
+ });
4099
+ const issueReceipt = options.issueReceipt !== false;
4100
+ return this.#withLock(() => {
4101
+ const state = this.#loadState({ allowInitialize: false });
4102
+ const event = this.#queryState(state, safeSelector);
4103
+ if (!event) return null;
4104
+ const anchor = state.anchors[event.sequence];
4105
+ const receipt = issueReceipt
4106
+ ? this.#issueReceipt(state, event, anchor, "verified-existing")
4107
+ : null;
4108
+ return deepFreeze({
4109
+ anchor,
4110
+ authenticated: true,
4111
+ authority: this.#authorityProjection(state),
4112
+ durable: true,
4113
+ event,
4114
+ receipt,
4115
+ schema: EVOLUTION_LEDGER_QUERY_SCHEMA,
4116
+ });
4117
+ });
4118
+ }
4119
+
4120
+ findByEventId(eventId) {
4121
+ return this.query({ eventId }, { issueReceipt: false })?.event || null;
4122
+ }
4123
+
4124
+ recoverReceipt(selector) {
4125
+ return this.query(selector, { issueReceipt: true })?.receipt || null;
4126
+ }
4127
+
4128
+ verifyReceipt(receipt, options = {}) {
4129
+ assertExactKeys(options, VERIFY_RECEIPT_OPTION_KEYS, "receipt options", {
4130
+ optional: [...VERIFY_RECEIPT_OPTION_KEYS],
4131
+ });
4132
+ const requireCurrentHead = options.requireCurrentHead === true;
4133
+ let verifiedReceipt;
4134
+ try {
4135
+ verifiedReceipt = this.#verifyReceiptRecord(receipt);
4136
+ } catch (cause) {
4137
+ if (cause?.code === "CC_EVOLUTION_LEDGER_RECEIPT_INVALID") throw cause;
4138
+ throw ledgerError(
4139
+ "CC_EVOLUTION_LEDGER_RECEIPT_INVALID",
4140
+ "receipt failed strict schema, trust, digest, or signature validation",
4141
+ { cause },
4142
+ );
4143
+ }
4144
+ return this.#withLock(() => {
4145
+ const state = this.#loadState({ allowInitialize: false });
4146
+ const event = this.#queryState(state, {
4147
+ eventDigest: verifiedReceipt.eventDigest,
4148
+ eventId: verifiedReceipt.eventId,
4149
+ sequence: verifiedReceipt.sequence,
4150
+ });
4151
+ const anchor = state.anchors[verifiedReceipt.sequence];
4152
+ const witnessCheckpoint = verifiedReceipt.witnessCheckpoint;
4153
+ const witnessAnchor = state.anchors[witnessCheckpoint.sequence];
4154
+ const witnessEvent =
4155
+ witnessCheckpoint.sequence === 0
4156
+ ? null
4157
+ : state.events[witnessCheckpoint.sequence - 1];
4158
+ let witnessSnapshotValid = false;
4159
+ let witnessAncestryValid = false;
4160
+ try {
4161
+ if (witnessAnchor) {
4162
+ this.#assertWitnessSnapshot(
4163
+ witnessCheckpoint,
4164
+ state.identity,
4165
+ state.storeMarker,
4166
+ witnessAnchor,
4167
+ witnessEvent,
4168
+ );
4169
+ witnessSnapshotValid = true;
4170
+ }
4171
+ if (witnessCheckpoint.witnessDigest === state.witness.witnessDigest) {
4172
+ witnessAncestryValid = true;
4173
+ } else if (witnessCheckpoint.generation < state.witness.generation) {
4174
+ this.#proveWitnessAncestry(witnessCheckpoint, state.witness);
4175
+ witnessAncestryValid = true;
4176
+ }
4177
+ } catch {
4178
+ witnessSnapshotValid = false;
4179
+ witnessAncestryValid = false;
4180
+ }
4181
+ if (
4182
+ !event ||
4183
+ !anchor ||
4184
+ !witnessSnapshotValid ||
4185
+ !witnessAncestryValid ||
4186
+ verifiedReceipt.ledgerId !== state.identity.ledgerId ||
4187
+ verifiedReceipt.epoch !== state.identity.epoch ||
4188
+ verifiedReceipt.identityDigest !== state.identity.identityDigest ||
4189
+ verifiedReceipt.anchorDigest !== anchor.anchorDigest ||
4190
+ verifiedReceipt.headDigest !== anchor.headDigest ||
4191
+ verifiedReceipt.segmentDigest !== anchor.segmentDigest ||
4192
+ witnessCheckpoint.witnessDigest !== verifiedReceipt.witnessDigest ||
4193
+ witnessCheckpoint.sequence < verifiedReceipt.sequence ||
4194
+ canonicalJson(verifiedReceipt.headSignature) !==
4195
+ canonicalJson(anchor.signature) ||
4196
+ (requireCurrentHead &&
4197
+ verifiedReceipt.anchorDigest !== state.head.anchorDigest)
4198
+ ) {
4199
+ throw ledgerError(
4200
+ "CC_EVOLUTION_LEDGER_RECEIPT_INVALID",
4201
+ "receipt is not bound to the authenticated ledger event and head",
4202
+ );
4203
+ }
4204
+ return deepFreeze({
4205
+ anchor,
4206
+ authenticated: true,
4207
+ authority: this.#authorityProjection(state),
4208
+ durable: true,
4209
+ event,
4210
+ receipt: verifiedReceipt,
4211
+ valid: true,
4212
+ });
4213
+ });
4214
+ }
4215
+
4216
+ read({ afterSequence = 0, limit = 10_000 } = {}) {
4217
+ if (
4218
+ !Number.isSafeInteger(afterSequence) ||
4219
+ afterSequence < 0 ||
4220
+ !Number.isSafeInteger(limit) ||
4221
+ limit < 1 ||
4222
+ limit > EVOLUTION_LEDGER_MAX_EVENTS
4223
+ ) {
4224
+ throw ledgerError(
4225
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
4226
+ "read bounds are invalid",
4227
+ );
4228
+ }
4229
+ return this.#withLock(() => {
4230
+ const state = this.#loadState({ allowInitialize: false });
4231
+ return deepFreeze(
4232
+ state.events
4233
+ .filter((event) => event.sequence > afterSequence)
4234
+ .slice(0, limit),
4235
+ );
4236
+ });
4237
+ }
4238
+
4239
+ #authorityProjection(state) {
4240
+ return deepFreeze({
4241
+ algorithm: this.#trust.algorithm,
4242
+ anchorDigest: state.head.anchorDigest,
4243
+ authenticated: true,
4244
+ durable: true,
4245
+ epoch: state.identity.epoch,
4246
+ eventCount: state.events.length,
4247
+ headDigest: state.head.headDigest,
4248
+ headSignature: state.head.signature,
4249
+ identityDigest: state.identity.identityDigest,
4250
+ identitySignature: state.identity.signature,
4251
+ keyId: this.#trust.keyId,
4252
+ ledgerId: state.identity.ledgerId,
4253
+ schema: EVOLUTION_LEDGER_VERIFICATION_SCHEMA,
4254
+ sequence: state.head.sequence,
4255
+ status: "verified",
4256
+ storeMarkerDigest: state.identity.storeMarkerDigest,
4257
+ storeMarkerEntryDigest: state.identity.storeMarkerEntryDigest,
4258
+ storeMarkerId: state.identity.storeMarkerId,
4259
+ trustPolicyDigest: this.#trust.trustPolicyDigest,
4260
+ verifiedAt: clockTimestamp(this.#clock),
4261
+ witnessDiscardAccumulatorDigest: state.witness.discardAccumulatorDigest,
4262
+ witnessDigest: state.witness.witnessDigest,
4263
+ witnessGeneration: state.witness.generation,
4264
+ witnessId: this.#witness.id,
4265
+ });
4266
+ }
4267
+
4268
+ verify() {
4269
+ return this.#withLock(() =>
4270
+ this.#authorityProjection(this.#loadState({ allowInitialize: false })),
4271
+ );
4272
+ }
4273
+
4274
+ getAuthority() {
4275
+ return this.verify();
4276
+ }
4277
+ }
4278
+
4279
+ Object.freeze(EvolutionLedger.prototype);
4280
+ Object.freeze(EvolutionLedger);