chainlesschain 0.166.17 → 0.166.18

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 (210) hide show
  1. package/README.md +14 -7
  2. package/package.json +1 -1
  3. package/src/assets/web-panel/assets/{AIOps-CVW2n2rh.js → AIOps-CQSBl3hY.js} +1 -1
  4. package/src/assets/web-panel/assets/{ActionButton-DtuM163O.js → ActionButton-IQt-vRLt.js} +1 -1
  5. package/src/assets/web-panel/assets/{Analytics-BXvl_qHu.js → Analytics-DkHC8O7k.js} +3 -3
  6. package/src/assets/web-panel/assets/{AppLayout-VDBTFKOp.js → AppLayout-CSti2P2T.js} +3 -3
  7. package/src/assets/web-panel/assets/{Artifacts-BNz2xCiG.js → Artifacts-C57hxn3v.js} +1 -1
  8. package/src/assets/web-panel/assets/{Audit-DTu9Hn9P.js → Audit-DByLntcf.js} +1 -1
  9. package/src/assets/web-panel/assets/{BackgroundAgents-D2OCPzLe.js → BackgroundAgents-D1Bd2EOa.js} +1 -1
  10. package/src/assets/web-panel/assets/{Backup-C9M-vG7G.js → Backup-VstHzHng.js} +1 -1
  11. package/src/assets/web-panel/assets/{BaseInput-BUkCNiUQ.js → BaseInput-CLhdexIf.js} +1 -1
  12. package/src/assets/web-panel/assets/{Chat-OJ2ajF7l.js → Chat-BefC_JZS.js} +6 -6
  13. package/src/assets/web-panel/assets/{ChatBubbleRenderer-BdZt76Ab.js → ChatBubbleRenderer-DbXMvgmn.js} +1 -1
  14. package/src/assets/web-panel/assets/{Checkbox-C1gXKDsc.js → Checkbox-CEY982vx.js} +1 -1
  15. package/src/assets/web-panel/assets/{Codegen-BXbznLg7.js → Codegen-Dcm_DTvJ.js} +1 -1
  16. package/src/assets/web-panel/assets/{Col-CbCq5N0i.js → Col-C8SSu4KA.js} +1 -1
  17. package/src/assets/web-panel/assets/{Community-V8Ks6Yun.js → Community-BVV9vwRR.js} +1 -1
  18. package/src/assets/web-panel/assets/{Compact-DkeHQTpQ.js → Compact-B7c5Vo18.js} +1 -1
  19. package/src/assets/web-panel/assets/{Compliance-JeLKwn4Q.js → Compliance-Tq9jSZ5a.js} +1 -1
  20. package/src/assets/web-panel/assets/{Cowork-azfetcBz.js → Cowork-BJUjT09Q.js} +3 -3
  21. package/src/assets/web-panel/assets/{Cron-Q691i_Gp.js → Cron-CjV9PEaS.js} +2 -2
  22. package/src/assets/web-panel/assets/{Crosschain-BLjDRbr_.js → Crosschain-Dl7ReL8F.js} +1 -1
  23. package/src/assets/web-panel/assets/{DID-D8ptXplK.js → DID-CG8PjWmM.js} +2 -2
  24. package/src/assets/web-panel/assets/{Dashboard-Ctj2VVZL.js → Dashboard-CDBM6wmF.js} +2 -2
  25. package/src/assets/web-panel/assets/{Dropdown-D8VgkMJ8.js → Dropdown-8_55bXTD.js} +1 -1
  26. package/src/assets/web-panel/assets/{EmailListRenderer-CQPe5S6q.js → EmailListRenderer-D3qOU1Cm.js} +1 -1
  27. package/src/assets/web-panel/assets/{FamilyGuardDashboard-ySA-7SiN.js → FamilyGuardDashboard-Bv5oWMZF.js} +1 -1
  28. package/src/assets/web-panel/assets/{Federation-1PMD-fBf.js → Federation-hpZ1xkKd.js} +1 -1
  29. package/src/assets/web-panel/assets/{FormItemContext-C_jr6U4Q.js → FormItemContext-BN7GT191.js} +1 -1
  30. package/src/assets/web-panel/assets/{GenericCardRenderer-Bu3bKf2B.js → GenericCardRenderer-PCRhfA7M.js} +1 -1
  31. package/src/assets/web-panel/assets/{Git-Dv4Fa_T5.js → Git-CIlP2vy7.js} +2 -2
  32. package/src/assets/web-panel/assets/{Governance-DOEy1n1k.js → Governance-81lgm7YL.js} +1 -1
  33. package/src/assets/web-panel/assets/{Inference-DkvqzWcJ.js → Inference-Cs24xMOV.js} +1 -1
  34. package/src/assets/web-panel/assets/{KnowledgeGraph-DdUJK949.js → KnowledgeGraph-CrJLVCZo.js} +1 -1
  35. package/src/assets/web-panel/assets/{Logs-CRelcdUZ.js → Logs-DLyO8RKT.js} +2 -2
  36. package/src/assets/web-panel/assets/{MarkdownRenderer-0AHbbTA4.js → MarkdownRenderer-DBOqijrP.js} +1 -1
  37. package/src/assets/web-panel/assets/{Marketplace-BuuBKsev.js → Marketplace-Dz7AbQZc.js} +1 -1
  38. package/src/assets/web-panel/assets/{McpTools-DlgpEmA1.js → McpTools-BM0aFiWv.js} +3 -3
  39. package/src/assets/web-panel/assets/{Memory-DTfdqUGy.js → Memory-C9SJzENX.js} +2 -2
  40. package/src/assets/web-panel/assets/{MobileBridge-CXLE5ZFO.js → MobileBridge-BO2HnNRk.js} +1 -1
  41. package/src/assets/web-panel/assets/{MobileProjects-Dz25OB0n.js → MobileProjects-CK_jMz0l.js} +1 -1
  42. package/src/assets/web-panel/assets/{Mtc-kEL0fhgD.js → Mtc-CfhrJsdO.js} +2 -2
  43. package/src/assets/web-panel/assets/{MtcAudit-BE4UvFJU.js → MtcAudit-BE2ELxgI.js} +4 -4
  44. package/src/assets/web-panel/assets/{Multisig-CSApSn8c.js → Multisig-CgR_E3Jd.js} +3 -3
  45. package/src/assets/web-panel/assets/{NLProgramming-D11FpKWx.js → NLProgramming-Cw9445_K.js} +1 -1
  46. package/src/assets/web-panel/assets/{Notes-CDL-D2CS.js → Notes-DZimkTrb.js} +3 -3
  47. package/src/assets/web-panel/assets/{NotificationSettings-C0Q6lO-c.js → NotificationSettings-DxHVh4uT.js} +1 -1
  48. package/src/assets/web-panel/assets/OrderTableRenderer-Iy-pnX56.js +1 -0
  49. package/src/assets/web-panel/assets/{Organization-Dc-maVAq.js → Organization-pPXJtWo4.js} +4 -4
  50. package/src/assets/web-panel/assets/{Overflow-B5mUpSvi.js → Overflow-YamdTJwA.js} +1 -1
  51. package/src/assets/web-panel/assets/{P2P-TjXV53wq.js → P2P-BvqP53x7.js} +2 -2
  52. package/src/assets/web-panel/assets/PdhVaultBrowser-Blz1Xn2n.js +7 -0
  53. package/src/assets/web-panel/assets/{Permissions-DKRzQ2O6.js → Permissions-CgFRQqFJ.js} +4 -4
  54. package/src/assets/web-panel/assets/{PersonalDataHub-Dx2eqft8.js → PersonalDataHub-CFeo3-6O.js} +4 -4
  55. package/src/assets/web-panel/assets/{Pipeline-CnYZMB5p.js → Pipeline-BkaRojEK.js} +1 -1
  56. package/src/assets/web-panel/assets/{Privacy-BonKpPnp.js → Privacy-BNZEx2Qp.js} +1 -1
  57. package/src/assets/web-panel/assets/{ProjectInit-DWO0Du3J.js → ProjectInit-HUBOgEEK.js} +2 -2
  58. package/src/assets/web-panel/assets/{ProjectSettings-C9jqyYRv.js → ProjectSettings-CFMmhROS.js} +2 -2
  59. package/src/assets/web-panel/assets/{Projects-CYpKwXbz.js → Projects-Dc517BXp.js} +1 -1
  60. package/src/assets/web-panel/assets/{Providers-CW-fkwJq.js → Providers-DTLf8bgj.js} +1 -1
  61. package/src/assets/web-panel/assets/{QrScannerModal-Cw0J3Zud.js → QrScannerModal-BBNjPszk.js} +1 -1
  62. package/src/assets/web-panel/assets/{QuickAsk-BH4A4IpR.js → QuickAsk-DCsHvk3G.js} +1 -1
  63. package/src/assets/web-panel/assets/{Recommend-CDwMwJus.js → Recommend-Dpr5YSop.js} +1 -1
  64. package/src/assets/web-panel/assets/{RemoteSession-DKIPwzTf.js → RemoteSession-DmKnvqGg.js} +2 -2
  65. package/src/assets/web-panel/assets/{Reputation-CklDj7qS.js → Reputation-CSRfuurX.js} +1 -1
  66. package/src/assets/web-panel/assets/{Row-DJWZKsH0.js → Row-CDj3pQ_w.js} +1 -1
  67. package/src/assets/web-panel/assets/{RssFeed-BfSLCewo.js → RssFeed-DI0a_kNq.js} +2 -2
  68. package/src/assets/web-panel/assets/{Search-B0IST--x.js → Search-jAy7B2nU.js} +1 -1
  69. package/src/assets/web-panel/assets/{Security-h1sYNWtt.js → Security-CkHTqYhc.js} +4 -4
  70. package/src/assets/web-panel/assets/{Services-Gh0-oxI_.js → Services-CP-0Nzga.js} +2 -2
  71. package/src/assets/web-panel/assets/{Skeleton-CNDV_jOs.js → Skeleton-BkF5-p3n.js} +1 -1
  72. package/src/assets/web-panel/assets/{Skills-ByCLTh1n.js → Skills-BDu-utDE.js} +1 -1
  73. package/src/assets/web-panel/assets/{Sla-BfyrJTJz.js → Sla-CQvq_2Qb.js} +1 -1
  74. package/src/assets/web-panel/assets/{SpeechSettings-BQzo9kNX.js → SpeechSettings-T7Ap512g.js} +1 -1
  75. package/src/assets/web-panel/assets/{SyncSettings-B6fS6z_1.js → SyncSettings-Bt-PLvot.js} +2 -2
  76. package/src/assets/web-panel/assets/{Tasks-C-uKCP-l.js → Tasks-yLEcCogg.js} +1 -1
  77. package/src/assets/web-panel/assets/{Templates-D6oDy7wL.js → Templates-ByN3fmbr.js} +1 -1
  78. package/src/assets/web-panel/assets/{Tenant-D3LT2wNQ.js → Tenant-DBI7hITH.js} +1 -1
  79. package/src/assets/web-panel/assets/{Terminal-BteLcv_O.js → Terminal-B77_DlGX.js} +2 -2
  80. package/src/assets/web-panel/assets/{TimelineRenderer-DHHuxfqH.js → TimelineRenderer-CsLTB_QO.js} +1 -1
  81. package/src/assets/web-panel/assets/{Tokens-BRQeykXw.js → Tokens-BV2N6cR8.js} +1 -1
  82. package/src/assets/web-panel/assets/{Trigger-p2XaFWEu.js → Trigger-CjXWGH3p.js} +1 -1
  83. package/src/assets/web-panel/assets/{Trust-BxadBMft.js → Trust-uqXIq3WC.js} +1 -1
  84. package/src/assets/web-panel/assets/{UkeySign-DRBP7LDk.js → UkeySign-D-6qu4vY.js} +1 -1
  85. package/src/assets/web-panel/assets/{VideoEditing-CZ3NZkh7.js → VideoEditing-CxGmx_2e.js} +1 -1
  86. package/src/assets/web-panel/assets/{Wallet-C3NTBN7G.js → Wallet-DSq7WlAg.js} +4 -4
  87. package/src/assets/web-panel/assets/{WebAuthn-C7LVgMlb.js → WebAuthn-CDBiD57O.js} +2 -2
  88. package/src/assets/web-panel/assets/{WorkflowEditor-0_Iveog7.js → WorkflowEditor-Z8f63TSb.js} +1 -1
  89. package/src/assets/web-panel/assets/{chat-DzzluYo3.js → chat-B-UZFBjy.js} +1 -1
  90. package/src/assets/web-panel/assets/{colors-DSMYXpaj.js → colors-CFk7K-7Q.js} +1 -1
  91. package/src/assets/web-panel/assets/{compact-item-W76u1VSV.js → compact-item-X0IVMvgi.js} +1 -1
  92. package/src/assets/web-panel/assets/{createContext-otsESjb5.js → createContext-DWg4j2n6.js} +1 -1
  93. package/src/assets/web-panel/assets/devWarning-B2IW1J_J.js +1 -0
  94. package/src/assets/web-panel/assets/{hasIn-3sNLrvug.js → hasIn-CkVCwNNF.js} +1 -1
  95. package/src/assets/web-panel/assets/{index-BdtmEKat.js → index-5OcZHdzc.js} +1 -1
  96. package/src/assets/web-panel/assets/{index-DS9WYDMf.js → index-B273IY-E.js} +1 -1
  97. package/src/assets/web-panel/assets/{index-NaO9AE-F.js → index-B3-B3FWD.js} +1 -1
  98. package/src/assets/web-panel/assets/{index-BcGJtmHM.js → index-BB4KHHcv.js} +1 -1
  99. package/src/assets/web-panel/assets/index-BCp2ir-1.js +1 -0
  100. package/src/assets/web-panel/assets/{index-pVm_iUre.js → index-BG-GaZri.js} +1 -1
  101. package/src/assets/web-panel/assets/{index-BpcQYzP7.js → index-BPBia4b-.js} +1 -1
  102. package/src/assets/web-panel/assets/{index-BXLx9HfA.js → index-BipwEbTA.js} +1 -1
  103. package/src/assets/web-panel/assets/{index-uSCkbpZH.js → index-BoskgPt9.js} +1 -1
  104. package/src/assets/web-panel/assets/{index-DMmdIpLA.js → index-Btqk6zWb.js} +1 -1
  105. package/src/assets/web-panel/assets/index-Bw4iuzRD.js +1 -0
  106. package/src/assets/web-panel/assets/{index-DLDh1OW3.js → index-BzXjYUmd.js} +1 -1
  107. package/src/assets/web-panel/assets/{index-B9X_jqXe.js → index-C1ZAOJRW.js} +1 -1
  108. package/src/assets/web-panel/assets/{index-vBKPcmAP.js → index-CCLwoATF.js} +1 -1
  109. package/src/assets/web-panel/assets/{index-vUmYkqmu.js → index-CDfSD07S.js} +1 -1
  110. package/src/assets/web-panel/assets/{index-D7A5U2IH.js → index-CLlP1E4X.js} +1 -1
  111. package/src/assets/web-panel/assets/{index-ClOFeZ-t.js → index-CMCEXBRh.js} +1 -1
  112. package/src/assets/web-panel/assets/{index-Cg86EzNp.js → index-CN8HZlLH.js} +1 -1
  113. package/src/assets/web-panel/assets/{index-Ddd4mird.js → index-CNKFsBlz.js} +1 -1
  114. package/src/assets/web-panel/assets/{index-CE7tSow-.js → index-CUBkvFPH.js} +1 -1
  115. package/src/assets/web-panel/assets/{index-GlIOE4ja.js → index-Ca0mij1n.js} +1 -1
  116. package/src/assets/web-panel/assets/{index-DdU7IkKX.js → index-CbIAWbma.js} +1 -1
  117. package/src/assets/web-panel/assets/{index-UUHCwfNq.js → index-CbihMThv.js} +1 -1
  118. package/src/assets/web-panel/assets/{index-BRsD1PT2.js → index-CvOa-IFV.js} +1 -1
  119. package/src/assets/web-panel/assets/{index-C2aht9-M.js → index-Cvwu_rA_.js} +1 -1
  120. package/src/assets/web-panel/assets/{index-BJsQio5O.js → index-D-EJNxgR.js} +1 -1
  121. package/src/assets/web-panel/assets/{index-q7ftLqNt.js → index-D8uEL8Uw.js} +1 -1
  122. package/src/assets/web-panel/assets/{index-B1I1AYJs.js → index-D92g4l3n.js} +1 -1
  123. package/src/assets/web-panel/assets/{index-DZKAzEge.js → index-DCqpl_E_.js} +1 -1
  124. package/src/assets/web-panel/assets/{index-CKoFyHYQ.js → index-DLSBks8h.js} +1 -1
  125. package/src/assets/web-panel/assets/{index-BDlDO2px.js → index-Db_5xhrU.js} +1 -1
  126. package/src/assets/web-panel/assets/{index-CENWwjef.js → index-DcDggpWR.js} +1 -1
  127. package/src/assets/web-panel/assets/{index-DH4cl52p.js → index-DeWKPQGs.js} +1 -1
  128. package/src/assets/web-panel/assets/{index-DTA8Iavv.js → index-DhVHoqGL.js} +3 -3
  129. package/src/assets/web-panel/assets/{index-DmpaC2yk.js → index-DyWlOuak.js} +1 -1
  130. package/src/assets/web-panel/assets/{index-BA8mZxd-.js → index-LU54bChK.js} +1 -1
  131. package/src/assets/web-panel/assets/{index-CtFOt5x1.js → index-OjnAwUJo.js} +1 -1
  132. package/src/assets/web-panel/assets/{index-B5LOYdYm.js → index-P83LDnCz.js} +1 -1
  133. package/src/assets/web-panel/assets/{index-MhQQ3h8q.js → index-_-9k3Tna.js} +1 -1
  134. package/src/assets/web-panel/assets/{initDefaultProps-DF6llUXV.js → initDefaultProps-CFNgY8lJ.js} +1 -1
  135. package/src/assets/web-panel/assets/{motion-vtDV8_5K.js → motion-D-5RrDzn.js} +1 -1
  136. package/src/assets/web-panel/assets/{move-B6c0bdy2.js → move-DN-L0Bd-.js} +1 -1
  137. package/src/assets/web-panel/assets/{mtc-parser-DlLYWuBK.js → mtc-parser-6mgLSeCK.js} +1 -1
  138. package/src/assets/web-panel/assets/{omit-BH0zOxLN.js → omit-ClWW8eTP.js} +1 -1
  139. package/src/assets/web-panel/assets/{pickAttrs-D9di8_In.js → pickAttrs-Vystq3R8.js} +1 -1
  140. package/src/assets/web-panel/assets/{placementArrow-BDSngufM.js → placementArrow-D0H9hK4W.js} +1 -1
  141. package/src/assets/web-panel/assets/{responsiveObserve-C_EawMvP.js → responsiveObserve-B3iNRU-a.js} +1 -1
  142. package/src/assets/web-panel/assets/{slide-B_4wShxA.js → slide-CiMXe6am.js} +1 -1
  143. package/src/assets/web-panel/assets/{statusUtils-CbQu6qJ-.js → statusUtils-CGDZJ1uT.js} +1 -1
  144. package/src/assets/web-panel/assets/{styleChecker-CvXJWYXb.js → styleChecker-D2zA6NkY.js} +1 -1
  145. package/src/assets/web-panel/assets/{useFlexGapSupport-jjY1XknO.js → useFlexGapSupport-C1sYU6QE.js} +1 -1
  146. package/src/assets/web-panel/assets/{useFs-CMXBdsK_.js → useFs-BzUHSjg2.js} +1 -1
  147. package/src/assets/web-panel/assets/{usePersonalDataHub-YbAhhPXx.js → usePersonalDataHub-CxB5yZCq.js} +1 -1
  148. package/src/assets/web-panel/assets/{vnode-BFCTKc0H.js → vnode-BfUfMwtc.js} +1 -1
  149. package/src/assets/web-panel/assets/{zoom-CY9aBl5O.js → zoom-CFYmj1TO.js} +1 -1
  150. package/src/assets/web-panel/index.html +1 -1
  151. package/src/command-help-index.json +1 -1
  152. package/src/commands/agent.js +80 -2
  153. package/src/commands/evolution.js +33 -27
  154. package/src/commands/serve.js +11 -2
  155. package/src/data/changelog.json +5 -5
  156. package/src/gateways/ws/session-protocol.js +1 -0
  157. package/src/gateways/ws/ws-agent-handler.js +70 -2
  158. package/src/gateways/ws/ws-server.js +8 -0
  159. package/src/lib/app-server/cli-agent-kernel-adapter.js +4 -0
  160. package/src/lib/app-server/graph-runtime.js +115 -8
  161. package/src/lib/app-server/server.js +21 -2
  162. package/src/lib/app-server/websocket-transport.js +2 -0
  163. package/src/lib/evolution/agent-evolution-ingress.js +300 -0
  164. package/src/lib/evolution/agent-evolution-runtime-composition-brand.js +22 -0
  165. package/src/lib/evolution/agent-evolution-runtime-composition.js +369 -0
  166. package/src/lib/evolution/evidence-backed-wiki-maintainer.js +752 -0
  167. package/src/lib/evolution/evolution-artifact-ports.js +48 -0
  168. package/src/lib/evolution/evolution-eval-gate.js +11 -4
  169. package/src/lib/evolution/evolution-evidence-artifact-adapter.js +499 -0
  170. package/src/lib/evolution/evolution-evidence-projector.js +61 -1
  171. package/src/lib/evolution/evolution-file-witness.js +601 -0
  172. package/src/lib/evolution/evolution-ledger-file-backend.js +176 -0
  173. package/src/lib/evolution/evolution-ledger-ports.js +353 -16
  174. package/src/lib/evolution/evolution-ledger.js +1355 -54
  175. package/src/lib/evolution/evolution-run-ledger-adapter.js +409 -0
  176. package/src/lib/evolution/evolution-run-wiki-maintenance-source.js +300 -0
  177. package/src/lib/evolution/scheduled-batch-wiki-maintenance-source.js +368 -0
  178. package/src/lib/evolution/skill-candidate-registry.js +649 -0
  179. package/src/lib/evolution/skill-promotion-controller.js +109 -38
  180. package/src/lib/evolution/skill-promotion-review-ledger-adapter.js +451 -0
  181. package/src/lib/evolution/skill-promotion-review.js +793 -0
  182. package/src/lib/evolution/skill-registry-transition-capability.js +55 -0
  183. package/src/lib/evolution/skill-registry-transition-ledger-adapter.js +1280 -0
  184. package/src/lib/evolution/skill-registry-transition-source.js +372 -0
  185. package/src/lib/evolution/skill-release-registry.js +2191 -11
  186. package/src/lib/evolution/skill-target-matrix-eval.js +133 -10
  187. package/src/lib/evolution/structured-memory-agent-control-plane.js +222 -0
  188. package/src/lib/evolution/structured-memory-authority-ledger-adapter.js +351 -0
  189. package/src/lib/evolution/structured-memory-ledger-adapter.js +220 -0
  190. package/src/lib/evolution/structured-memory-policy-receipt-writer.js +164 -0
  191. package/src/lib/evolution/structured-memory-post-compact-hook.js +87 -0
  192. package/src/lib/evolution/structured-memory-promotion-receipt-writer.js +221 -0
  193. package/src/lib/evolution/structured-memory-semantic-review-pipeline.js +313 -0
  194. package/src/lib/evolution/wiki-maintainer-ledger-adapter.js +200 -0
  195. package/src/lib/evolution/wiki-maintenance-trigger-ledger-adapter.js +687 -0
  196. package/src/lib/evolution-system.js +65 -30
  197. package/src/lib/mcp-lifecycle-authority.js +21 -2
  198. package/src/lib/remote-approval-bridge.js +24 -0
  199. package/src/lib/scheduler-kernel/store.js +62 -14
  200. package/src/repl/agent-repl.js +45 -0
  201. package/src/runtime/agent-runtime.js +114 -1
  202. package/src/runtime/headless-runner.js +19 -0
  203. package/src/runtime/headless-stream.js +43 -0
  204. package/src/runtime/runtime-factory.js +19 -1
  205. package/src/assets/web-panel/assets/OrderTableRenderer-ZRE0OKbj.js +0 -1
  206. package/src/assets/web-panel/assets/PdhVaultBrowser-DvV2btIr.js +0 -7
  207. package/src/assets/web-panel/assets/devWarning-BztaXF84.js +0 -1
  208. package/src/assets/web-panel/assets/index-4jP_hG_4.js +0 -1
  209. package/src/assets/web-panel/assets/index-zxwuAXL5.js +0 -1
  210. package/src/lib/learning/learning-hooks.js +0 -123
@@ -26,6 +26,14 @@ export const EVOLUTION_LEDGER_WITNESS_ANCESTRY_SCHEMA =
26
26
  "chainlesschain.evolution-ledger-witness-ancestry/v1";
27
27
  export const EVOLUTION_LEDGER_STORE_MARKER_SCHEMA =
28
28
  "chainlesschain.evolution-ledger-store-marker/v1";
29
+ export const EVOLUTION_LEDGER_AUDIT_EXPORT_SCHEMA =
30
+ "chainlesschain.evolution-ledger-audit-export/v1";
31
+ export const EVOLUTION_LEDGER_AUDIT_ARTIFACT_SCHEMA =
32
+ "chainlesschain.evolution-ledger-audit-artifact/v1";
33
+ export const EVOLUTION_LEDGER_AUDIT_VERIFICATION_SCHEMA =
34
+ "chainlesschain.evolution-ledger-audit-verification/v1";
35
+ export const EVOLUTION_LEDGER_STATE_SNAPSHOT_SCHEMA =
36
+ "chainlesschain.evolution-ledger-state-snapshot/v1";
29
37
  export const EVOLUTION_ARTIFACT_REF_SCHEMA =
30
38
  "chainlesschain.content-addressed-artifact-ref/v1";
31
39
  export const EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA =
@@ -52,9 +60,15 @@ const DISCARD_ACCUMULATOR_DOMAIN =
52
60
  "chainlesschain.evolution-witness-discard-accumulator/v1\0";
53
61
  const ARTIFACT_VALIDATION_DOMAIN =
54
62
  "chainlesschain.evolution-artifact-validation/v1\0";
63
+ const AUDIT_EXPORT_DOMAIN = "chainlesschain.evolution-ledger-audit-export/v1\0";
64
+ const STATE_SNAPSHOT_DOMAIN =
65
+ "chainlesschain.evolution-ledger-state-snapshot/v1\0";
66
+ const STATE_SNAPSHOT_CONTENT_DOMAIN =
67
+ "chainlesschain.evolution-ledger-state-snapshot-content/v1\0";
55
68
  const IDENTITY_FILE_NAME = "identity-v1.json";
56
69
  const STORE_MARKER_FILE_NAME = "store-marker-v1.json";
57
70
  const HEAD_FILE_NAME = "head-v1.json";
71
+ const STATE_SNAPSHOT_FILE_PREFIX = "state-snapshot-v1-";
58
72
  const SEGMENT_DIRECTORY_NAME = "segments-v1";
59
73
  const ANCHOR_DIRECTORY_NAME = "head-anchors-v1";
60
74
  const LOCK_TARGET_NAME = "ledger-v2";
@@ -68,10 +82,15 @@ const SIGNATURE_VALUE_PATTERN = /^[A-Za-z0-9_-]{22,8192}$/u;
68
82
  const RANDOM_TOKEN_PATTERN = /^[A-Za-z0-9-]{16,128}$/u;
69
83
  const SEGMENT_FILE_PATTERN = /^(\d{12})-([a-f0-9]{64})\.json$/u;
70
84
  const ANCHOR_FILE_PATTERN = /^(\d{12})-([a-f0-9]{64})\.json$/u;
85
+ const STATE_SNAPSHOT_FILE_PATTERN = /^state-snapshot-v1-([a-f0-9]{64})\.json$/u;
71
86
  const STAGE_FILE_PATTERN = /^\.stage-(.+)\.([A-Za-z0-9-]{16,128})\.tmp$/u;
72
87
  const HEAD_STAGE_FILE_PATTERN =
73
88
  /^\.replace-head-v1\.json\.([A-Za-z0-9-]{16,128})\.tmp$/u;
74
89
  const MAX_SOURCE_REFS = 256;
90
+ const MAX_AUDIT_ARTIFACT_RECORDS = 1_000_000;
91
+ const MAX_LEDGER_BATCH_QUERIES = 10_000;
92
+ const MAX_STATE_SNAPSHOT_BYTES = 256 * 1024 * 1024;
93
+ const STATE_QUERY_INDEXES = new WeakMap();
75
94
  const TYPED_ARRAY_PROTOTYPE = Object.getPrototypeOf(Uint8Array.prototype);
76
95
  const TYPED_ARRAY_BUFFER_GETTER = Object.getOwnPropertyDescriptor(
77
96
  TYPED_ARRAY_PROTOTYPE,
@@ -371,6 +390,67 @@ const STORE_MARKER_RECORD_KEYS = new Set([
371
390
  const QUERY_SELECTOR_KEYS = new Set(["eventDigest", "eventId", "sequence"]);
372
391
  const QUERY_OPTION_KEYS = new Set(["issueReceipt"]);
373
392
  const VERIFY_RECEIPT_OPTION_KEYS = new Set(["requireCurrentHead"]);
393
+ const AUDIT_EXPORT_CORE_KEYS = new Set([
394
+ "anchors",
395
+ "artifacts",
396
+ "events",
397
+ "exportedAt",
398
+ "identity",
399
+ "schema",
400
+ "storeMarker",
401
+ "witness",
402
+ ]);
403
+ const AUDIT_EXPORT_RECORD_KEYS = new Set([
404
+ ...AUDIT_EXPORT_CORE_KEYS,
405
+ "exportDigest",
406
+ ]);
407
+ const AUDIT_ARTIFACT_KEYS = new Set([
408
+ "artifactRef",
409
+ "byteLength",
410
+ "bytesBase64",
411
+ "eventSequence",
412
+ "receiptDigest",
413
+ "schema",
414
+ "tenantId",
415
+ ]);
416
+ const AUDIT_VERIFY_PORT_KEYS = new Set([
417
+ "trust",
418
+ "verifySignature",
419
+ "verifyWitnessSignature",
420
+ "witnessTrust",
421
+ ]);
422
+ const STATE_SNAPSHOT_CORE_KEYS = new Set([
423
+ "algorithm",
424
+ "anchorDigest",
425
+ "createdAt",
426
+ "epoch",
427
+ "headDigest",
428
+ "identityDigest",
429
+ "keyId",
430
+ "ledgerId",
431
+ "schema",
432
+ "sequence",
433
+ "stateDigest",
434
+ "storeMarkerDigest",
435
+ "storeMarkerEntryDigest",
436
+ "trustPolicyDigest",
437
+ "witnessDigest",
438
+ "witnessGeneration",
439
+ "witnessId",
440
+ ]);
441
+ const STATE_SNAPSHOT_RECORD_KEYS = new Set([
442
+ ...STATE_SNAPSHOT_CORE_KEYS,
443
+ "signature",
444
+ "snapshotDigest",
445
+ "state",
446
+ ]);
447
+ const STATE_SNAPSHOT_CONTENT_KEYS = new Set([
448
+ "anchorFiles",
449
+ "anchors",
450
+ "events",
451
+ "segmentFiles",
452
+ ]);
453
+ const STATE_SNAPSHOT_FILE_KEYS = new Set(["contentDigest", "name"]);
374
454
 
375
455
  const REQUIRED_FS_METHODS = Object.freeze([
376
456
  "closeSync",
@@ -1236,6 +1316,42 @@ function normalizeAnchorCore(value) {
1236
1316
  };
1237
1317
  }
1238
1318
 
1319
+ function normalizeStateSnapshotCore(value) {
1320
+ assertExactKeys(value, STATE_SNAPSHOT_CORE_KEYS, "state snapshot core");
1321
+ if (value.schema !== EVOLUTION_LEDGER_STATE_SNAPSHOT_SCHEMA) {
1322
+ throw ledgerError(
1323
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
1324
+ "state snapshot schema is unsupported",
1325
+ );
1326
+ }
1327
+ return {
1328
+ algorithm: boundedString(value.algorithm, "algorithm", 64),
1329
+ anchorDigest: digest(value.anchorDigest, "anchorDigest"),
1330
+ createdAt: canonicalTimestamp(value.createdAt, "createdAt"),
1331
+ epoch: identifier(value.epoch, "epoch"),
1332
+ headDigest: digest(value.headDigest, "headDigest", { nullable: true }),
1333
+ identityDigest: digest(value.identityDigest, "identityDigest"),
1334
+ keyId: boundedString(value.keyId, "keyId", 256),
1335
+ ledgerId: identifier(value.ledgerId, "ledgerId"),
1336
+ schema: EVOLUTION_LEDGER_STATE_SNAPSHOT_SCHEMA,
1337
+ sequence: safeSequence(value.sequence, "sequence", { allowZero: true }),
1338
+ stateDigest: digest(value.stateDigest, "stateDigest"),
1339
+ storeMarkerDigest: digest(value.storeMarkerDigest, "storeMarkerDigest"),
1340
+ storeMarkerEntryDigest: digest(
1341
+ value.storeMarkerEntryDigest,
1342
+ "storeMarkerEntryDigest",
1343
+ ),
1344
+ trustPolicyDigest: digest(value.trustPolicyDigest, "trustPolicyDigest"),
1345
+ witnessDigest: digest(value.witnessDigest, "witnessDigest"),
1346
+ witnessGeneration: safeSequence(
1347
+ value.witnessGeneration,
1348
+ "witnessGeneration",
1349
+ { allowZero: true },
1350
+ ),
1351
+ witnessId: identifier(value.witnessId, "witnessId"),
1352
+ };
1353
+ }
1354
+
1239
1355
  function normalizeReceiptCore(value, trust, witnessTrust) {
1240
1356
  assertExactKeys(value, RECEIPT_CORE_KEYS, "receipt core");
1241
1357
  if (value.schema !== EVOLUTION_LEDGER_RECEIPT_SCHEMA) {
@@ -1676,6 +1792,10 @@ function anchorFileName(sequence, anchorDigest) {
1676
1792
  return `${sequenceName(sequence)}-${anchorDigest.slice("sha256:".length)}.json`;
1677
1793
  }
1678
1794
 
1795
+ function stateSnapshotFileName(witnessDigest) {
1796
+ return `${STATE_SNAPSHOT_FILE_PREFIX}${witnessDigest.slice("sha256:".length)}.json`;
1797
+ }
1798
+
1679
1799
  function defaultRootDir() {
1680
1800
  return path.join(getHomeDir(), "evolution", "ledger", "events");
1681
1801
  }
@@ -2179,7 +2299,7 @@ export class EvolutionLedger {
2179
2299
  return deepFreeze({ ...core, signature, storeMarkerDigest });
2180
2300
  }
2181
2301
 
2182
- #verifyEvent(record, previous, identity) {
2302
+ #verifyEvent(record, previous, identity, { verifySignature = true } = {}) {
2183
2303
  assertPlainDataObject(record, "event record");
2184
2304
  const schema = Object.getOwnPropertyDescriptor(record, "schema")?.value;
2185
2305
  const domainEvent = schema === EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA;
@@ -2210,16 +2330,23 @@ export class EvolutionLedger {
2210
2330
  { sequence: core.sequence },
2211
2331
  );
2212
2332
  }
2213
- this.#verifyMessage({
2214
- digest: eventDigest,
2215
- message,
2216
- purpose: domainEvent ? "domain-event" : "event",
2217
- signature,
2218
- });
2333
+ if (verifySignature) {
2334
+ this.#verifyMessage({
2335
+ digest: eventDigest,
2336
+ message,
2337
+ purpose: domainEvent ? "domain-event" : "event",
2338
+ signature,
2339
+ });
2340
+ }
2219
2341
  return deepFreeze({ ...core, eventDigest, signature });
2220
2342
  }
2221
2343
 
2222
- #verifyAnchor(record, previous, identity, { standalone = false } = {}) {
2344
+ #verifyAnchor(
2345
+ record,
2346
+ previous,
2347
+ identity,
2348
+ { standalone = false, verifySignature = true } = {},
2349
+ ) {
2223
2350
  assertExactKeys(record, ANCHOR_RECORD_KEYS, "anchor record");
2224
2351
  const core = normalizeAnchorCore(
2225
2352
  Object.fromEntries(
@@ -2247,12 +2374,14 @@ export class EvolutionLedger {
2247
2374
  { sequence: core.sequence },
2248
2375
  );
2249
2376
  }
2250
- this.#verifyMessage({
2251
- digest: anchorDigest,
2252
- message,
2253
- purpose: "anchor",
2254
- signature,
2255
- });
2377
+ if (verifySignature) {
2378
+ this.#verifyMessage({
2379
+ digest: anchorDigest,
2380
+ message,
2381
+ purpose: "anchor",
2382
+ signature,
2383
+ });
2384
+ }
2256
2385
  return deepFreeze({ ...core, anchorDigest, signature });
2257
2386
  }
2258
2387
 
@@ -3004,7 +3133,10 @@ export class EvolutionLedger {
3004
3133
  this.#cleanupDirectoryDebris(this.#paths.anchorDir, ANCHOR_FILE_PATTERN);
3005
3134
  this.#cleanupDirectoryDebris(
3006
3135
  this.#paths.authorityRootDir,
3007
- new RegExp(`^${IDENTITY_FILE_NAME.replace(".", "\\.")}$`, "u"),
3136
+ new RegExp(
3137
+ `^(?:${IDENTITY_FILE_NAME.replace(".", "\\.")}|${STATE_SNAPSHOT_FILE_PATTERN.source.slice(1, -1)})$`,
3138
+ "u",
3139
+ ),
3008
3140
  );
3009
3141
  this.#cleanupDirectoryDebris(
3010
3142
  this.#paths.rootDir,
@@ -3249,6 +3381,331 @@ export class EvolutionLedger {
3249
3381
  }
3250
3382
  }
3251
3383
 
3384
+ #snapshotFileRecords(values, label, expectedNames) {
3385
+ const entries = readDenseDataArray(
3386
+ values,
3387
+ label,
3388
+ EVOLUTION_LEDGER_MAX_EVENTS + 1,
3389
+ );
3390
+ if (entries.length !== expectedNames.length) {
3391
+ throw ledgerError(
3392
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3393
+ `${label} does not cover the current immutable prefix`,
3394
+ );
3395
+ }
3396
+ return entries.map((entry, index) => {
3397
+ assertExactKeys(entry, STATE_SNAPSHOT_FILE_KEYS, `${label} entry`);
3398
+ const normalized = {
3399
+ contentDigest: digest(entry.contentDigest, "contentDigest"),
3400
+ name: boundedString(entry.name, "name", 256),
3401
+ };
3402
+ if (normalized.name !== expectedNames[index]) {
3403
+ throw ledgerError(
3404
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3405
+ `${label} is missing or reordered at index ${index}`,
3406
+ );
3407
+ }
3408
+ return Object.freeze(normalized);
3409
+ });
3410
+ }
3411
+
3412
+ #verifyStateSnapshot(
3413
+ record,
3414
+ identity,
3415
+ storeMarker,
3416
+ witnessed,
3417
+ head,
3418
+ anchorNames,
3419
+ segmentNames,
3420
+ ) {
3421
+ assertExactKeys(record, STATE_SNAPSHOT_RECORD_KEYS, "state snapshot");
3422
+ const core = normalizeStateSnapshotCore(
3423
+ Object.fromEntries(
3424
+ [...STATE_SNAPSHOT_CORE_KEYS].map((key) => [key, record[key]]),
3425
+ ),
3426
+ );
3427
+ const snapshotDigest = digest(record.snapshotDigest, "snapshotDigest");
3428
+ const signature = normalizeSignature(record.signature, this.#trust);
3429
+ const message = signedMessage(STATE_SNAPSHOT_DOMAIN, core);
3430
+ if (snapshotDigest !== sha256(message)) {
3431
+ throw ledgerError(
3432
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3433
+ "state snapshot digest does not match its signed core",
3434
+ );
3435
+ }
3436
+ this.#verifyMessage({
3437
+ digest: snapshotDigest,
3438
+ message,
3439
+ purpose: "state-snapshot",
3440
+ signature,
3441
+ });
3442
+ if (
3443
+ core.algorithm !== this.#trust.algorithm ||
3444
+ core.keyId !== this.#trust.keyId ||
3445
+ core.trustPolicyDigest !== this.#trust.trustPolicyDigest ||
3446
+ core.ledgerId !== identity.ledgerId ||
3447
+ core.epoch !== identity.epoch ||
3448
+ core.identityDigest !== identity.identityDigest ||
3449
+ core.storeMarkerDigest !== storeMarker.storeMarkerDigest ||
3450
+ core.storeMarkerEntryDigest !== identity.storeMarkerEntryDigest ||
3451
+ core.witnessId !== witnessed.witnessId ||
3452
+ core.witnessDigest !== witnessed.witnessDigest ||
3453
+ core.witnessGeneration !== witnessed.generation ||
3454
+ core.sequence !== witnessed.sequence ||
3455
+ core.sequence !== head.sequence ||
3456
+ core.anchorDigest !== head.anchorDigest ||
3457
+ core.headDigest !== head.headDigest
3458
+ ) {
3459
+ throw ledgerError(
3460
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3461
+ "state snapshot is stale or not bound to the current authority",
3462
+ );
3463
+ }
3464
+
3465
+ assertExactKeys(
3466
+ record.state,
3467
+ STATE_SNAPSHOT_CONTENT_KEYS,
3468
+ "state snapshot content",
3469
+ );
3470
+ if (
3471
+ core.stateDigest !==
3472
+ domainDigest(STATE_SNAPSHOT_CONTENT_DOMAIN, record.state)
3473
+ ) {
3474
+ throw ledgerError(
3475
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3476
+ "state snapshot content digest does not match its signed core",
3477
+ );
3478
+ }
3479
+ const anchorRecords = readDenseDataArray(
3480
+ record.state.anchors,
3481
+ "state snapshot anchors",
3482
+ EVOLUTION_LEDGER_MAX_EVENTS + 1,
3483
+ );
3484
+ const eventRecords = readDenseDataArray(
3485
+ record.state.events,
3486
+ "state snapshot events",
3487
+ EVOLUTION_LEDGER_MAX_EVENTS,
3488
+ );
3489
+ if (
3490
+ anchorRecords.length !== eventRecords.length + 1 ||
3491
+ eventRecords.length !== core.sequence
3492
+ ) {
3493
+ throw ledgerError(
3494
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3495
+ "state snapshot chain length does not match its authority checkpoint",
3496
+ );
3497
+ }
3498
+
3499
+ const anchors = [];
3500
+ const events = [];
3501
+ const eventIds = new Set();
3502
+ for (let index = 0; index < anchorRecords.length; index += 1) {
3503
+ const anchor = this.#verifyAnchor(
3504
+ anchorRecords[index],
3505
+ anchors.at(-1),
3506
+ identity,
3507
+ { verifySignature: false },
3508
+ );
3509
+ anchors.push(anchor);
3510
+ if (index === 0) continue;
3511
+ const event = this.#verifyEvent(
3512
+ eventRecords[index - 1],
3513
+ events.at(-1),
3514
+ identity,
3515
+ { verifySignature: false },
3516
+ );
3517
+ if (
3518
+ event.sequence !== anchor.sequence ||
3519
+ event.eventDigest !== anchor.headDigest ||
3520
+ domainDigest(SEGMENT_DOMAIN, event) !== anchor.segmentDigest ||
3521
+ eventIds.has(event.eventId)
3522
+ ) {
3523
+ throw ledgerError(
3524
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3525
+ `state snapshot event/anchor binding failed at sequence ${index}`,
3526
+ );
3527
+ }
3528
+ eventIds.add(event.eventId);
3529
+ events.push(event);
3530
+ }
3531
+ if (anchors.at(-1).anchorDigest !== core.anchorDigest) {
3532
+ throw ledgerError(
3533
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3534
+ "state snapshot chain does not terminate at its signed anchor",
3535
+ );
3536
+ }
3537
+ const derivedAnchorNames = anchors.map((anchor) =>
3538
+ anchorFileName(anchor.sequence, anchor.anchorDigest),
3539
+ );
3540
+ const derivedSegmentNames = anchors
3541
+ .slice(1)
3542
+ .map((anchor) => segmentFileName(anchor.sequence, anchor.segmentDigest));
3543
+ if (
3544
+ derivedAnchorNames.some((name, index) => name !== anchorNames[index]) ||
3545
+ derivedSegmentNames.some((name, index) => name !== segmentNames[index])
3546
+ ) {
3547
+ throw ledgerError(
3548
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3549
+ "state snapshot records do not address the current immutable files",
3550
+ );
3551
+ }
3552
+
3553
+ const anchorFiles = this.#snapshotFileRecords(
3554
+ record.state.anchorFiles,
3555
+ "state snapshot anchor files",
3556
+ anchorNames,
3557
+ );
3558
+ const segmentFiles = this.#snapshotFileRecords(
3559
+ record.state.segmentFiles,
3560
+ "state snapshot segment files",
3561
+ segmentNames,
3562
+ );
3563
+ const anchorFingerprints = {};
3564
+ const segmentFingerprints = {};
3565
+ for (let index = 0; index < anchorFiles.length; index += 1) {
3566
+ const entry = anchorFiles[index];
3567
+ const snapshotRecordDigest = sha256(
3568
+ Buffer.from(`${canonicalJson(anchorRecords[index])}\n`, "utf8"),
3569
+ );
3570
+ if (entry.contentDigest !== snapshotRecordDigest) {
3571
+ throw ledgerError(
3572
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3573
+ `state snapshot anchor bytes are not bound at index ${index}`,
3574
+ );
3575
+ }
3576
+ const file = this.#readCanonicalFile(
3577
+ path.join(this.#paths.anchorDir, entry.name),
3578
+ 64 * 1024,
3579
+ `state snapshot anchor file ${entry.name}`,
3580
+ );
3581
+ if (file.contentDigest !== entry.contentDigest) {
3582
+ throw ledgerError(
3583
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3584
+ `state snapshot anchor file changed: ${entry.name}`,
3585
+ );
3586
+ }
3587
+ anchorFingerprints[entry.name] = Object.freeze({
3588
+ contentDigest: file.contentDigest,
3589
+ fingerprint: file.fingerprint,
3590
+ });
3591
+ }
3592
+ for (let index = 0; index < segmentFiles.length; index += 1) {
3593
+ const entry = segmentFiles[index];
3594
+ const snapshotRecordDigest = sha256(
3595
+ Buffer.from(`${canonicalJson(eventRecords[index])}\n`, "utf8"),
3596
+ );
3597
+ if (entry.contentDigest !== snapshotRecordDigest) {
3598
+ throw ledgerError(
3599
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3600
+ `state snapshot event bytes are not bound at index ${index}`,
3601
+ );
3602
+ }
3603
+ const file = this.#readCanonicalFile(
3604
+ path.join(this.#paths.segmentDir, entry.name),
3605
+ EVOLUTION_LEDGER_MAX_EVENT_BYTES,
3606
+ `state snapshot segment file ${entry.name}`,
3607
+ );
3608
+ if (file.contentDigest !== entry.contentDigest) {
3609
+ throw ledgerError(
3610
+ "CC_EVOLUTION_LEDGER_CORRUPT",
3611
+ `state snapshot segment file changed: ${entry.name}`,
3612
+ );
3613
+ }
3614
+ segmentFingerprints[entry.name] = Object.freeze({
3615
+ contentDigest: file.contentDigest,
3616
+ fingerprint: file.fingerprint,
3617
+ });
3618
+ }
3619
+
3620
+ const maximumAnchor = anchors.at(-1);
3621
+ const maximumEvent = events.at(-1) || null;
3622
+ this.#assertWitnessSnapshot(
3623
+ witnessed,
3624
+ identity,
3625
+ storeMarker,
3626
+ maximumAnchor,
3627
+ maximumEvent,
3628
+ );
3629
+ const state = deepFreeze({
3630
+ anchors,
3631
+ events,
3632
+ head,
3633
+ identity,
3634
+ storeMarker,
3635
+ witness: witnessed,
3636
+ });
3637
+ return this.#rememberState(
3638
+ state,
3639
+ anchorNames,
3640
+ segmentNames,
3641
+ anchorFingerprints,
3642
+ segmentFingerprints,
3643
+ );
3644
+ }
3645
+
3646
+ #tryStateSnapshot(
3647
+ identity,
3648
+ storeMarker,
3649
+ witnessed,
3650
+ head,
3651
+ anchorNames,
3652
+ segmentNames,
3653
+ ) {
3654
+ if (witnessed.status !== "committed") return null;
3655
+ const snapshotPath = path.join(
3656
+ this.#paths.authorityRootDir,
3657
+ stateSnapshotFileName(witnessed.witnessDigest),
3658
+ );
3659
+ try {
3660
+ const snapshotFile = this.#readCanonicalFile(
3661
+ snapshotPath,
3662
+ MAX_STATE_SNAPSHOT_BYTES,
3663
+ "state snapshot",
3664
+ { allowMissing: true },
3665
+ );
3666
+ if (!snapshotFile) return null;
3667
+ return this.#verifyStateSnapshot(
3668
+ snapshotFile.record,
3669
+ identity,
3670
+ storeMarker,
3671
+ witnessed,
3672
+ head,
3673
+ anchorNames,
3674
+ segmentNames,
3675
+ );
3676
+ } catch (cause) {
3677
+ if (cause?.code === "CC_EVOLUTION_LEDGER_STORE_UNSAFE") throw cause;
3678
+ return null;
3679
+ }
3680
+ }
3681
+
3682
+ #pruneStateSnapshots(currentName) {
3683
+ let changed = false;
3684
+ for (const entry of this.#fs.readdirSync(this.#paths.authorityRootDir, {
3685
+ withFileTypes: true,
3686
+ })) {
3687
+ if (!STATE_SNAPSHOT_FILE_PATTERN.test(entry.name)) continue;
3688
+ const snapshotPath = path.join(this.#paths.authorityRootDir, entry.name);
3689
+ const stat = this.#fs.lstatSync(snapshotPath);
3690
+ if (
3691
+ !entry.isFile() ||
3692
+ entry.isSymbolicLink() ||
3693
+ !stat.isFile() ||
3694
+ stat.isSymbolicLink() ||
3695
+ stat.nlink !== 1
3696
+ ) {
3697
+ throw ledgerError(
3698
+ "CC_EVOLUTION_LEDGER_STORE_UNSAFE",
3699
+ `state snapshot retention encountered an unsafe entry: ${entry.name}`,
3700
+ );
3701
+ }
3702
+ if (entry.name === currentName) continue;
3703
+ this.#fs.unlinkSync(snapshotPath);
3704
+ changed = true;
3705
+ }
3706
+ if (changed) syncDirectory(this.#fs, this.#paths.authorityRootDir);
3707
+ }
3708
+
3252
3709
  #cachedPrefix(identity, anchorNames, segmentNames, incremental) {
3253
3710
  const cached = incremental ? this.#stateCache : null;
3254
3711
  if (
@@ -3291,6 +3748,13 @@ export class EvolutionLedger {
3291
3748
  anchorFingerprints,
3292
3749
  segmentFingerprints,
3293
3750
  ) {
3751
+ const byEventDigest = new Map();
3752
+ const byEventId = new Map();
3753
+ for (const event of state.events) {
3754
+ byEventDigest.set(event.eventDigest, event);
3755
+ byEventId.set(event.eventId, event);
3756
+ }
3757
+ STATE_QUERY_INDEXES.set(state, Object.freeze({ byEventDigest, byEventId }));
3294
3758
  this.#stateCache = Object.freeze({
3295
3759
  anchorFingerprints: Object.freeze({ ...anchorFingerprints }),
3296
3760
  anchorNames: Object.freeze([...anchorNames]),
@@ -3416,12 +3880,29 @@ export class EvolutionLedger {
3416
3880
  );
3417
3881
  }
3418
3882
 
3883
+ const authenticatedHead = this.#verifyAnchor(
3884
+ headFile.record,
3885
+ null,
3886
+ identity,
3887
+ { standalone: true },
3888
+ );
3419
3889
  const cached = this.#cachedPrefix(
3420
3890
  identity,
3421
3891
  anchorNames,
3422
3892
  segmentNames,
3423
3893
  incremental,
3424
3894
  );
3895
+ if (!cached) {
3896
+ const snapshotted = this.#tryStateSnapshot(
3897
+ identity,
3898
+ storeMarker,
3899
+ witnessed,
3900
+ authenticatedHead,
3901
+ anchorNames,
3902
+ segmentNames,
3903
+ );
3904
+ if (snapshotted) return snapshotted;
3905
+ }
3425
3906
  const anchors = cached ? [...cached.state.anchors] : [];
3426
3907
  const events = cached ? [...cached.state.events] : [];
3427
3908
  const anchorFingerprints = cached ? { ...cached.anchorFingerprints } : {};
@@ -3536,9 +4017,7 @@ export class EvolutionLedger {
3536
4017
  }
3537
4018
  if (removedOrphan) syncDirectory(this.#fs, this.#paths.segmentDir);
3538
4019
 
3539
- const head = this.#verifyAnchor(headFile.record, null, identity, {
3540
- standalone: true,
3541
- });
4020
+ const head = authenticatedHead;
3542
4021
  const matchingHead = anchors.find(
3543
4022
  (anchor) => anchor.anchorDigest === head.anchorDigest,
3544
4023
  );
@@ -3713,8 +4192,9 @@ export class EvolutionLedger {
3713
4192
  );
3714
4193
  }
3715
4194
 
3716
- #resolveArtifacts(input, identity) {
4195
+ #resolveArtifactEvidence(input, identity, { includeBytes = false } = {}) {
3717
4196
  const validations = [];
4197
+ const artifacts = [];
3718
4198
  const artifactTenantId = Object.hasOwn(input, "artifactTenantId")
3719
4199
  ? input.artifactTenantId
3720
4200
  : input.tenantId;
@@ -3755,7 +4235,7 @@ export class EvolutionLedger {
3755
4235
  `artifact bytes do not match their digest: ${ref.ref}`,
3756
4236
  );
3757
4237
  }
3758
- validations.push({
4238
+ const validation = {
3759
4239
  byteLength: bytes.length,
3760
4240
  digest: ref.digest,
3761
4241
  receiptDigest: digest(
@@ -3763,7 +4243,18 @@ export class EvolutionLedger {
3763
4243
  "artifact resolution receiptDigest",
3764
4244
  ),
3765
4245
  ref: ref.ref,
3766
- });
4246
+ };
4247
+ validations.push(validation);
4248
+ if (includeBytes) {
4249
+ artifacts.push({
4250
+ artifactRef: ref,
4251
+ byteLength: validation.byteLength,
4252
+ bytesBase64: bytes.toString("base64"),
4253
+ receiptDigest: validation.receiptDigest,
4254
+ schema: EVOLUTION_LEDGER_AUDIT_ARTIFACT_SCHEMA,
4255
+ tenantId: artifactTenantId,
4256
+ });
4257
+ }
3767
4258
  } catch (cause) {
3768
4259
  if (cause?.code === "CC_EVOLUTION_LEDGER_ARTIFACT_INVALID") {
3769
4260
  throw cause;
@@ -3775,7 +4266,14 @@ export class EvolutionLedger {
3775
4266
  );
3776
4267
  }
3777
4268
  }
3778
- return domainDigest(ARTIFACT_VALIDATION_DOMAIN, validations);
4269
+ return {
4270
+ artifacts,
4271
+ digest: domainDigest(ARTIFACT_VALIDATION_DOMAIN, validations),
4272
+ };
4273
+ }
4274
+
4275
+ #resolveArtifacts(input, identity) {
4276
+ return this.#resolveArtifactEvidence(input, identity).digest;
3779
4277
  }
3780
4278
 
3781
4279
  #buildEvent(input, previous, identity, artifactValidationDigest) {
@@ -4078,17 +4576,135 @@ export class EvolutionLedger {
4078
4576
  }
4079
4577
 
4080
4578
  #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
- );
4579
+ const indexes = STATE_QUERY_INDEXES.get(state);
4580
+ const event =
4581
+ selector.sequence !== undefined
4582
+ ? state.events[selector.sequence - 1] || null
4583
+ : selector.eventId !== undefined
4584
+ ? indexes?.byEventId.get(selector.eventId) || null
4585
+ : indexes?.byEventDigest.get(selector.eventDigest) || null;
4586
+ return event &&
4587
+ (selector.eventId === undefined || event.eventId === selector.eventId) &&
4588
+ (selector.eventDigest === undefined ||
4589
+ event.eventDigest === selector.eventDigest) &&
4590
+ (selector.sequence === undefined || event.sequence === selector.sequence)
4591
+ ? event
4592
+ : null;
4593
+ }
4594
+
4595
+ #queryResult(state, event, issueReceipt) {
4596
+ if (!event) return null;
4597
+ const anchor = state.anchors[event.sequence];
4598
+ const receipt = issueReceipt
4599
+ ? this.#issueReceipt(state, event, anchor, "verified-existing")
4600
+ : null;
4601
+ return deepFreeze({
4602
+ anchor,
4603
+ authenticated: true,
4604
+ authority: this.#authorityProjection(state),
4605
+ durable: true,
4606
+ event,
4607
+ receipt,
4608
+ schema: EVOLUTION_LEDGER_QUERY_SCHEMA,
4609
+ });
4610
+ }
4611
+
4612
+ checkpointState() {
4613
+ return this.#withLock(() => {
4614
+ const state = this.#loadState({
4615
+ allowInitialize: false,
4616
+ incremental: true,
4617
+ });
4618
+ const snapshotPath = path.join(
4619
+ this.#paths.authorityRootDir,
4620
+ stateSnapshotFileName(state.witness.witnessDigest),
4621
+ );
4622
+ const existing = this.#readCanonicalFile(
4623
+ snapshotPath,
4624
+ MAX_STATE_SNAPSHOT_BYTES,
4625
+ "state snapshot",
4626
+ { allowMissing: true },
4627
+ );
4628
+ if (existing) {
4629
+ this.#verifyStateSnapshot(
4630
+ existing.record,
4631
+ state.identity,
4632
+ state.storeMarker,
4633
+ state.witness,
4634
+ state.head,
4635
+ this.#stateCache.anchorNames,
4636
+ this.#stateCache.segmentNames,
4637
+ );
4638
+ this.#pruneStateSnapshots(path.basename(snapshotPath));
4639
+ return deepFreeze(existing.record);
4640
+ }
4641
+
4642
+ const content = deepFreeze({
4643
+ anchorFiles: this.#stateCache.anchorNames.map((name) => ({
4644
+ contentDigest:
4645
+ this.#stateCache.anchorFingerprints[name].contentDigest,
4646
+ name,
4647
+ })),
4648
+ anchors: state.anchors,
4649
+ events: state.events,
4650
+ segmentFiles: this.#stateCache.segmentNames.map((name) => ({
4651
+ contentDigest:
4652
+ this.#stateCache.segmentFingerprints[name].contentDigest,
4653
+ name,
4654
+ })),
4655
+ });
4656
+ const core = normalizeStateSnapshotCore({
4657
+ algorithm: this.#trust.algorithm,
4658
+ anchorDigest: state.head.anchorDigest,
4659
+ createdAt: clockTimestamp(this.#clock),
4660
+ epoch: state.identity.epoch,
4661
+ headDigest: state.head.headDigest,
4662
+ identityDigest: state.identity.identityDigest,
4663
+ keyId: this.#trust.keyId,
4664
+ ledgerId: state.identity.ledgerId,
4665
+ schema: EVOLUTION_LEDGER_STATE_SNAPSHOT_SCHEMA,
4666
+ sequence: state.head.sequence,
4667
+ stateDigest: domainDigest(STATE_SNAPSHOT_CONTENT_DOMAIN, content),
4668
+ storeMarkerDigest: state.storeMarker.storeMarkerDigest,
4669
+ storeMarkerEntryDigest: state.identity.storeMarkerEntryDigest,
4670
+ trustPolicyDigest: this.#trust.trustPolicyDigest,
4671
+ witnessDigest: state.witness.witnessDigest,
4672
+ witnessGeneration: state.witness.generation,
4673
+ witnessId: state.witness.witnessId,
4674
+ });
4675
+ const signed = this.#signRecord(
4676
+ STATE_SNAPSHOT_DOMAIN,
4677
+ core,
4678
+ "snapshotDigest",
4679
+ "state-snapshot",
4680
+ );
4681
+ const snapshot = deepFreeze({
4682
+ ...signed,
4683
+ state: content,
4684
+ });
4685
+ const bytes = serializeRecord(
4686
+ snapshot,
4687
+ MAX_STATE_SNAPSHOT_BYTES,
4688
+ "state snapshot",
4689
+ );
4690
+ this.#writeImmutable(snapshotPath, bytes, "state snapshot");
4691
+ const persisted = this.#readCanonicalFile(
4692
+ snapshotPath,
4693
+ MAX_STATE_SNAPSHOT_BYTES,
4694
+ "state snapshot",
4695
+ );
4696
+ this.#verifyStateSnapshot(
4697
+ persisted.record,
4698
+ state.identity,
4699
+ state.storeMarker,
4700
+ state.witness,
4701
+ state.head,
4702
+ this.#stateCache.anchorNames,
4703
+ this.#stateCache.segmentNames,
4704
+ );
4705
+ this.#pruneStateSnapshots(path.basename(snapshotPath));
4706
+ return deepFreeze(persisted.record);
4707
+ });
4092
4708
  }
4093
4709
 
4094
4710
  query(selector, options = {}) {
@@ -4098,22 +4714,49 @@ export class EvolutionLedger {
4098
4714
  });
4099
4715
  const issueReceipt = options.issueReceipt !== false;
4100
4716
  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,
4717
+ const state = this.#loadState({
4718
+ allowInitialize: false,
4719
+ incremental: true,
4116
4720
  });
4721
+ return this.#queryResult(
4722
+ state,
4723
+ this.#queryState(state, safeSelector),
4724
+ issueReceipt,
4725
+ );
4726
+ });
4727
+ }
4728
+
4729
+ queryMany(selectors, options = {}) {
4730
+ const entries = readDenseDataArray(
4731
+ selectors,
4732
+ "query selectors",
4733
+ MAX_LEDGER_BATCH_QUERIES,
4734
+ );
4735
+ if (entries.length === 0) {
4736
+ throw ledgerError(
4737
+ "CC_EVOLUTION_LEDGER_SCHEMA_INVALID",
4738
+ "query selectors must not be empty",
4739
+ );
4740
+ }
4741
+ const safeSelectors = entries.map(normalizeQuerySelector);
4742
+ assertExactKeys(options, QUERY_OPTION_KEYS, "query options", {
4743
+ optional: [...QUERY_OPTION_KEYS],
4744
+ });
4745
+ const issueReceipt = options.issueReceipt === true;
4746
+ return this.#withLock(() => {
4747
+ const state = this.#loadState({
4748
+ allowInitialize: false,
4749
+ incremental: true,
4750
+ });
4751
+ return deepFreeze(
4752
+ safeSelectors.map((selector) =>
4753
+ this.#queryResult(
4754
+ state,
4755
+ this.#queryState(state, selector),
4756
+ issueReceipt,
4757
+ ),
4758
+ ),
4759
+ );
4117
4760
  });
4118
4761
  }
4119
4762
 
@@ -4142,7 +4785,10 @@ export class EvolutionLedger {
4142
4785
  );
4143
4786
  }
4144
4787
  return this.#withLock(() => {
4145
- const state = this.#loadState({ allowInitialize: false });
4788
+ const state = this.#loadState({
4789
+ allowInitialize: false,
4790
+ incremental: true,
4791
+ });
4146
4792
  const event = this.#queryState(state, {
4147
4793
  eventDigest: verifiedReceipt.eventDigest,
4148
4794
  eventId: verifiedReceipt.eventId,
@@ -4213,6 +4859,50 @@ export class EvolutionLedger {
4213
4859
  });
4214
4860
  }
4215
4861
 
4862
+ exportAuditBundle() {
4863
+ return this.#withLock(() => {
4864
+ const state = this.#loadState({
4865
+ allowInitialize: false,
4866
+ incremental: true,
4867
+ });
4868
+ const artifacts = [];
4869
+ for (const event of state.events) {
4870
+ const evidence = this.#resolveArtifactEvidence(event, state.identity, {
4871
+ includeBytes: true,
4872
+ });
4873
+ if (evidence.digest !== event.artifactValidationDigest) {
4874
+ throw ledgerError(
4875
+ "CC_EVOLUTION_LEDGER_ARTIFACT_INVALID",
4876
+ `artifact evidence changed since event ${event.sequence} was committed`,
4877
+ );
4878
+ }
4879
+ for (const artifact of evidence.artifacts) {
4880
+ artifacts.push({ ...artifact, eventSequence: event.sequence });
4881
+ if (artifacts.length > MAX_AUDIT_ARTIFACT_RECORDS) {
4882
+ throw ledgerError(
4883
+ "CC_EVOLUTION_LEDGER_CAPACITY_EXCEEDED",
4884
+ "audit export artifact evidence exceeds its bounded capacity",
4885
+ );
4886
+ }
4887
+ }
4888
+ }
4889
+ const core = {
4890
+ anchors: state.anchors,
4891
+ artifacts,
4892
+ events: state.events,
4893
+ exportedAt: clockTimestamp(this.#clock),
4894
+ identity: state.identity,
4895
+ schema: EVOLUTION_LEDGER_AUDIT_EXPORT_SCHEMA,
4896
+ storeMarker: state.storeMarker,
4897
+ witness: state.witness,
4898
+ };
4899
+ return deepFreeze({
4900
+ ...core,
4901
+ exportDigest: domainDigest(AUDIT_EXPORT_DOMAIN, core),
4902
+ });
4903
+ });
4904
+ }
4905
+
4216
4906
  read({ afterSequence = 0, limit = 10_000 } = {}) {
4217
4907
  if (
4218
4908
  !Number.isSafeInteger(afterSequence) ||
@@ -4227,11 +4917,12 @@ export class EvolutionLedger {
4227
4917
  );
4228
4918
  }
4229
4919
  return this.#withLock(() => {
4230
- const state = this.#loadState({ allowInitialize: false });
4920
+ const state = this.#loadState({
4921
+ allowInitialize: false,
4922
+ incremental: true,
4923
+ });
4231
4924
  return deepFreeze(
4232
- state.events
4233
- .filter((event) => event.sequence > afterSequence)
4234
- .slice(0, limit),
4925
+ state.events.slice(afterSequence, afterSequence + limit),
4235
4926
  );
4236
4927
  });
4237
4928
  }
@@ -4267,7 +4958,9 @@ export class EvolutionLedger {
4267
4958
 
4268
4959
  verify() {
4269
4960
  return this.#withLock(() =>
4270
- this.#authorityProjection(this.#loadState({ allowInitialize: false })),
4961
+ this.#authorityProjection(
4962
+ this.#loadState({ allowInitialize: false, incremental: true }),
4963
+ ),
4271
4964
  );
4272
4965
  }
4273
4966
 
@@ -4276,5 +4969,613 @@ export class EvolutionLedger {
4276
4969
  }
4277
4970
  }
4278
4971
 
4972
+ function artifactRefsForAudit(input) {
4973
+ const refs = Object.hasOwn(input, "subjectRef")
4974
+ ? [input.subjectRef, ...input.sourceRefs]
4975
+ : [
4976
+ ...input.sourceRefs,
4977
+ input.parentRef,
4978
+ input.candidateRef,
4979
+ input.diffRef,
4980
+ input.evalRef,
4981
+ input.policyRef,
4982
+ input.actorRef,
4983
+ input.targetRef,
4984
+ ].filter(Boolean);
4985
+ const byRef = new Map();
4986
+ for (const ref of refs) {
4987
+ const existing = byRef.get(ref.ref);
4988
+ if (existing && existing.digest !== ref.digest) {
4989
+ throw ledgerError(
4990
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
4991
+ `audit event binds one artifact ref to conflicting digests: ${ref.ref}`,
4992
+ );
4993
+ }
4994
+ byRef.set(ref.ref, ref);
4995
+ }
4996
+ return [...byRef.values()].sort((left, right) =>
4997
+ compareStrings(left.ref, right.ref),
4998
+ );
4999
+ }
5000
+
5001
+ function verifyAuditSignature({
5002
+ digest: recordDigest,
5003
+ message,
5004
+ port,
5005
+ purpose,
5006
+ signature,
5007
+ trust,
5008
+ witness = false,
5009
+ }) {
5010
+ let valid;
5011
+ try {
5012
+ valid = port({
5013
+ digest: recordDigest,
5014
+ message: Buffer.from(message),
5015
+ purpose,
5016
+ signature: frozenClone(signature),
5017
+ trust,
5018
+ });
5019
+ } catch (cause) {
5020
+ throw ledgerError(
5021
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5022
+ `offline ${purpose} signature verification failed`,
5023
+ { cause },
5024
+ );
5025
+ }
5026
+ if (isUnsafeAsyncResult(valid) || valid !== true) {
5027
+ throw ledgerError(
5028
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5029
+ `offline ${purpose} signature was not accepted synchronously${witness ? " by the witness authority" : ""}`,
5030
+ );
5031
+ }
5032
+ }
5033
+
5034
+ function assertAuditTrustBinding(
5035
+ record,
5036
+ identity,
5037
+ trust,
5038
+ witnessTrust,
5039
+ witnessId,
5040
+ ) {
5041
+ if (
5042
+ record.algorithm !== trust.algorithm ||
5043
+ record.keyId !== trust.keyId ||
5044
+ record.trustPolicyDigest !== trust.trustPolicyDigest ||
5045
+ record.witnessAlgorithm !== witnessTrust.algorithm ||
5046
+ record.witnessId !== witnessId ||
5047
+ record.witnessKeyId !== witnessTrust.keyId ||
5048
+ record.witnessTrustPolicyDigest !== witnessTrust.trustPolicyDigest ||
5049
+ (identity &&
5050
+ (record.ledgerId !== identity.ledgerId ||
5051
+ record.epoch !== identity.epoch ||
5052
+ record.identityDigest !== identity.identityDigest))
5053
+ ) {
5054
+ throw ledgerError(
5055
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5056
+ "offline audit record is not bound to the exported identity and trust roots",
5057
+ );
5058
+ }
5059
+ }
5060
+
5061
+ function verifyAuditIdentity(record, trust, witnessTrust, verifySignature) {
5062
+ assertExactKeys(record, IDENTITY_RECORD_KEYS, "audit identity");
5063
+ const core = normalizeIdentityCore(
5064
+ Object.fromEntries(
5065
+ [...IDENTITY_CORE_KEYS].map((key) => [key, record[key]]),
5066
+ ),
5067
+ );
5068
+ assertAuditTrustBinding(core, null, trust, witnessTrust, core.witnessId);
5069
+ const identityDigest = digest(record.identityDigest, "identityDigest");
5070
+ const signature = normalizeSignature(record.signature, trust);
5071
+ const message = signedMessage(IDENTITY_DOMAIN, core);
5072
+ if (identityDigest !== sha256(message)) {
5073
+ throw ledgerError(
5074
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5075
+ "offline audit identity digest is invalid",
5076
+ );
5077
+ }
5078
+ verifyAuditSignature({
5079
+ digest: identityDigest,
5080
+ message,
5081
+ port: verifySignature,
5082
+ purpose: "identity",
5083
+ signature,
5084
+ trust,
5085
+ });
5086
+ return deepFreeze({ ...core, identityDigest, signature });
5087
+ }
5088
+
5089
+ function verifyAuditStoreMarker(record, identity, trust, witnessTrust, port) {
5090
+ assertExactKeys(record, STORE_MARKER_RECORD_KEYS, "audit store marker");
5091
+ const core = normalizeStoreMarkerCore(
5092
+ Object.fromEntries(
5093
+ [...STORE_MARKER_CORE_KEYS].map((key) => [key, record[key]]),
5094
+ ),
5095
+ );
5096
+ assertAuditTrustBinding(core, null, trust, witnessTrust, identity.witnessId);
5097
+ const storeMarkerDigest = digest(
5098
+ record.storeMarkerDigest,
5099
+ "storeMarkerDigest",
5100
+ );
5101
+ const signature = normalizeSignature(record.signature, trust);
5102
+ const message = signedMessage(STORE_MARKER_DOMAIN, core);
5103
+ if (
5104
+ storeMarkerDigest !== sha256(message) ||
5105
+ core.ledgerId !== identity.ledgerId ||
5106
+ core.epoch !== identity.epoch ||
5107
+ core.createdAt !== identity.createdAt ||
5108
+ core.storeBindingDigest !== identity.storeBindingDigest ||
5109
+ core.storeMarkerId !== identity.storeMarkerId ||
5110
+ storeMarkerDigest !== identity.storeMarkerDigest
5111
+ ) {
5112
+ throw ledgerError(
5113
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5114
+ "offline audit store marker is not bound to the signed identity",
5115
+ );
5116
+ }
5117
+ verifyAuditSignature({
5118
+ digest: storeMarkerDigest,
5119
+ message,
5120
+ port,
5121
+ purpose: "store-marker",
5122
+ signature,
5123
+ trust,
5124
+ });
5125
+ return deepFreeze({ ...core, signature, storeMarkerDigest });
5126
+ }
5127
+
5128
+ function verifyAuditEvent(
5129
+ record,
5130
+ previous,
5131
+ identity,
5132
+ trust,
5133
+ witnessTrust,
5134
+ port,
5135
+ ) {
5136
+ assertPlainDataObject(record, "audit event");
5137
+ const domainEvent = record.schema === EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA;
5138
+ const recordKeys = domainEvent ? DOMAIN_EVENT_RECORD_KEYS : EVENT_RECORD_KEYS;
5139
+ const coreKeys = domainEvent ? DOMAIN_EVENT_CORE_KEYS : EVENT_CORE_KEYS;
5140
+ assertExactKeys(record, recordKeys, "audit event");
5141
+ const core = domainEvent
5142
+ ? normalizeDomainEventCore(
5143
+ Object.fromEntries([...coreKeys].map((key) => [key, record[key]])),
5144
+ )
5145
+ : normalizeEventCore(
5146
+ Object.fromEntries([...coreKeys].map((key) => [key, record[key]])),
5147
+ );
5148
+ assertAuditTrustBinding(
5149
+ core,
5150
+ identity,
5151
+ trust,
5152
+ witnessTrust,
5153
+ identity.witnessId,
5154
+ );
5155
+ const eventDigest = digest(record.eventDigest, "eventDigest");
5156
+ const signature = normalizeSignature(record.signature, trust);
5157
+ const signatureDomain = domainEvent ? DOMAIN_EVENT_DOMAIN : EVENT_DOMAIN;
5158
+ const message = signedMessage(signatureDomain, core);
5159
+ if (
5160
+ core.sequence !== (previous?.sequence || 0) + 1 ||
5161
+ core.prevDigest !== (previous?.eventDigest || null) ||
5162
+ eventDigest !== sha256(message)
5163
+ ) {
5164
+ throw ledgerError(
5165
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5166
+ `offline audit event chain failed at sequence ${core.sequence}`,
5167
+ );
5168
+ }
5169
+ verifyAuditSignature({
5170
+ digest: eventDigest,
5171
+ message,
5172
+ port,
5173
+ purpose: domainEvent ? "domain-event" : "event",
5174
+ signature,
5175
+ trust,
5176
+ });
5177
+ return deepFreeze({ ...core, eventDigest, signature });
5178
+ }
5179
+
5180
+ function verifyAuditAnchor(
5181
+ record,
5182
+ previous,
5183
+ identity,
5184
+ trust,
5185
+ witnessTrust,
5186
+ port,
5187
+ ) {
5188
+ assertExactKeys(record, ANCHOR_RECORD_KEYS, "audit anchor");
5189
+ const core = normalizeAnchorCore(
5190
+ Object.fromEntries([...ANCHOR_CORE_KEYS].map((key) => [key, record[key]])),
5191
+ );
5192
+ assertAuditTrustBinding(
5193
+ core,
5194
+ identity,
5195
+ trust,
5196
+ witnessTrust,
5197
+ identity.witnessId,
5198
+ );
5199
+ const anchorDigest = digest(record.anchorDigest, "anchorDigest");
5200
+ const signature = normalizeSignature(record.signature, trust);
5201
+ const message = signedMessage(ANCHOR_DOMAIN, core);
5202
+ if (
5203
+ core.sequence !== (previous ? previous.sequence + 1 : 0) ||
5204
+ core.previousAnchorDigest !== (previous?.anchorDigest || null) ||
5205
+ anchorDigest !== sha256(message) ||
5206
+ (core.sequence === 0 &&
5207
+ (core.headDigest !== null || core.segmentDigest !== null)) ||
5208
+ (core.sequence > 0 &&
5209
+ (core.headDigest === null || core.segmentDigest === null))
5210
+ ) {
5211
+ throw ledgerError(
5212
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5213
+ `offline audit anchor chain failed at sequence ${core.sequence}`,
5214
+ );
5215
+ }
5216
+ verifyAuditSignature({
5217
+ digest: anchorDigest,
5218
+ message,
5219
+ port,
5220
+ purpose: "anchor",
5221
+ signature,
5222
+ trust,
5223
+ });
5224
+ return deepFreeze({ ...core, anchorDigest, signature });
5225
+ }
5226
+
5227
+ function decodeAuditArtifactBytes(value, expectedLength) {
5228
+ if (typeof value !== "string" || value.length > 24 * 1024 * 1024) {
5229
+ throw ledgerError(
5230
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5231
+ "offline audit artifact encoding is invalid or oversized",
5232
+ );
5233
+ }
5234
+ const bytes = Buffer.from(value, "base64");
5235
+ if (
5236
+ bytes.length !== expectedLength ||
5237
+ bytes.length > EVOLUTION_LEDGER_MAX_ARTIFACT_BYTES ||
5238
+ bytes.toString("base64") !== value
5239
+ ) {
5240
+ throw ledgerError(
5241
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5242
+ "offline audit artifact bytes are not canonical or complete",
5243
+ );
5244
+ }
5245
+ return bytes;
5246
+ }
5247
+
5248
+ function verifyAuditArtifacts(records, events) {
5249
+ const byEventAndRef = new Map();
5250
+ let previousOrder = null;
5251
+ for (const record of records) {
5252
+ assertExactKeys(record, AUDIT_ARTIFACT_KEYS, "audit artifact");
5253
+ if (record.schema !== EVOLUTION_LEDGER_AUDIT_ARTIFACT_SCHEMA) {
5254
+ throw ledgerError(
5255
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5256
+ "offline audit artifact schema is unsupported",
5257
+ );
5258
+ }
5259
+ if (
5260
+ !Number.isSafeInteger(record.eventSequence) ||
5261
+ record.eventSequence < 1 ||
5262
+ record.eventSequence > events.length ||
5263
+ !Number.isSafeInteger(record.byteLength) ||
5264
+ record.byteLength < 0
5265
+ ) {
5266
+ throw ledgerError(
5267
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5268
+ "offline audit artifact bounds are invalid",
5269
+ );
5270
+ }
5271
+ const tenantId = identifier(record.tenantId, "audit artifact tenantId");
5272
+ const artifactRef = normalizeArtifactRef(
5273
+ record.artifactRef,
5274
+ "audit artifactRef",
5275
+ );
5276
+ const receiptDigest = digest(
5277
+ record.receiptDigest,
5278
+ "audit artifact receiptDigest",
5279
+ );
5280
+ const bytes = decodeAuditArtifactBytes(
5281
+ record.bytesBase64,
5282
+ record.byteLength,
5283
+ );
5284
+ if (sha256(bytes) !== artifactRef.digest) {
5285
+ throw ledgerError(
5286
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5287
+ `offline audit artifact digest is invalid: ${artifactRef.ref}`,
5288
+ );
5289
+ }
5290
+ const order = [record.eventSequence, tenantId, artifactRef.ref];
5291
+ if (
5292
+ previousOrder &&
5293
+ (order[0] < previousOrder[0] ||
5294
+ (order[0] === previousOrder[0] &&
5295
+ (compareStrings(order[1], previousOrder[1]) < 0 ||
5296
+ (order[1] === previousOrder[1] &&
5297
+ compareStrings(order[2], previousOrder[2]) <= 0))))
5298
+ ) {
5299
+ throw ledgerError(
5300
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5301
+ "offline audit artifact evidence must be unique and canonically ordered",
5302
+ );
5303
+ }
5304
+ previousOrder = order;
5305
+ const key = `${record.eventSequence}\0${tenantId}\0${artifactRef.ref}`;
5306
+ byEventAndRef.set(key, {
5307
+ artifactRef,
5308
+ byteLength: bytes.length,
5309
+ receiptDigest,
5310
+ });
5311
+ }
5312
+
5313
+ const consumed = new Set();
5314
+ for (const event of events) {
5315
+ const tenantId = Object.hasOwn(event, "artifactTenantId")
5316
+ ? event.artifactTenantId
5317
+ : event.tenantId;
5318
+ const validations = [];
5319
+ for (const ref of artifactRefsForAudit(event)) {
5320
+ const key = `${event.sequence}\0${tenantId}\0${ref.ref}`;
5321
+ const evidence = byEventAndRef.get(key);
5322
+ if (!evidence || evidence.artifactRef.digest !== ref.digest) {
5323
+ throw ledgerError(
5324
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5325
+ `offline audit artifact evidence is missing or substituted: ${ref.ref}`,
5326
+ );
5327
+ }
5328
+ consumed.add(key);
5329
+ validations.push({
5330
+ byteLength: evidence.byteLength,
5331
+ digest: ref.digest,
5332
+ receiptDigest: evidence.receiptDigest,
5333
+ ref: ref.ref,
5334
+ });
5335
+ }
5336
+ if (
5337
+ domainDigest(ARTIFACT_VALIDATION_DOMAIN, validations) !==
5338
+ event.artifactValidationDigest
5339
+ ) {
5340
+ throw ledgerError(
5341
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5342
+ `offline artifact validation receipt changed at event ${event.sequence}`,
5343
+ );
5344
+ }
5345
+ }
5346
+ if (consumed.size !== records.length) {
5347
+ throw ledgerError(
5348
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5349
+ "offline audit contains unreferenced artifact evidence",
5350
+ );
5351
+ }
5352
+ }
5353
+
5354
+ function verifyAuditWitness(
5355
+ record,
5356
+ identity,
5357
+ storeMarker,
5358
+ anchor,
5359
+ event,
5360
+ witnessTrust,
5361
+ port,
5362
+ ) {
5363
+ const witness = normalizeWitnessRecordShape(record, witnessTrust);
5364
+ if (
5365
+ witness.witnessId !== identity.witnessId ||
5366
+ witness.algorithm !== witnessTrust.algorithm ||
5367
+ witness.keyId !== witnessTrust.keyId ||
5368
+ witness.trustPolicyDigest !== witnessTrust.trustPolicyDigest
5369
+ ) {
5370
+ throw ledgerError(
5371
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5372
+ "offline witness is not bound to the independent trust root",
5373
+ );
5374
+ }
5375
+ const core = Object.fromEntries(
5376
+ [...WITNESS_CORE_KEYS].map((key) => [key, witness[key]]),
5377
+ );
5378
+ const message = signedMessage(WITNESS_DOMAIN, core);
5379
+ if (witness.witnessDigest !== sha256(message)) {
5380
+ throw ledgerError(
5381
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5382
+ "offline witness digest is invalid",
5383
+ );
5384
+ }
5385
+ verifyAuditSignature({
5386
+ digest: witness.witnessDigest,
5387
+ message,
5388
+ port,
5389
+ purpose: "witness",
5390
+ signature: witness.signature,
5391
+ trust: witnessTrust,
5392
+ witness: true,
5393
+ });
5394
+ const expected = {
5395
+ algorithm: witnessTrust.algorithm,
5396
+ anchorDigest: anchor.anchorDigest,
5397
+ epoch: identity.epoch,
5398
+ headDigest: anchor.headDigest,
5399
+ identityDigest: identity.identityDigest,
5400
+ keyId: witnessTrust.keyId,
5401
+ ledgerId: identity.ledgerId,
5402
+ payloadDigest: domainDigest(WITNESS_PAYLOAD_DOMAIN, {
5403
+ anchor,
5404
+ event,
5405
+ identity,
5406
+ storeMarker,
5407
+ }),
5408
+ segmentDigest: anchor.segmentDigest,
5409
+ sequence: anchor.sequence,
5410
+ storeMarkerDigest: identity.storeMarkerDigest,
5411
+ storeMarkerEntryDigest: identity.storeMarkerEntryDigest,
5412
+ storeMarkerId: identity.storeMarkerId,
5413
+ trustPolicyDigest: witnessTrust.trustPolicyDigest,
5414
+ witnessId: identity.witnessId,
5415
+ };
5416
+ if (
5417
+ witness.status !== "committed" ||
5418
+ witness.authenticated !== true ||
5419
+ witness.durable !== true ||
5420
+ Object.entries(expected).some(([key, value]) => witness[key] !== value)
5421
+ ) {
5422
+ throw ledgerError(
5423
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5424
+ "offline witness does not authenticate the exported ledger head",
5425
+ );
5426
+ }
5427
+ return witness;
5428
+ }
5429
+
5430
+ export function verifyEvolutionLedgerAuditBundle(bundle, ports) {
5431
+ try {
5432
+ assertExactKeys(ports, AUDIT_VERIFY_PORT_KEYS, "audit verifier ports");
5433
+ if (
5434
+ typeof ports.verifySignature !== "function" ||
5435
+ typeof ports.verifyWitnessSignature !== "function"
5436
+ ) {
5437
+ throw ledgerError(
5438
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5439
+ "offline audit requires synchronous ledger and witness verifiers",
5440
+ );
5441
+ }
5442
+ const verifySignatureImplementation = ports.verifySignature;
5443
+ const verifyWitnessSignatureImplementation = ports.verifyWitnessSignature;
5444
+ const verifySignature = Object.freeze((...args) =>
5445
+ Reflect.apply(verifySignatureImplementation, undefined, args),
5446
+ );
5447
+ const verifyWitnessSignature = Object.freeze((...args) =>
5448
+ Reflect.apply(verifyWitnessSignatureImplementation, undefined, args),
5449
+ );
5450
+ const trust = normalizeTrust(ports.trust);
5451
+ const witnessTrust = normalizeTrust(ports.witnessTrust);
5452
+ assertExactKeys(bundle, AUDIT_EXPORT_RECORD_KEYS, "audit export");
5453
+ if (bundle.schema !== EVOLUTION_LEDGER_AUDIT_EXPORT_SCHEMA) {
5454
+ throw ledgerError(
5455
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5456
+ "offline audit export schema is unsupported",
5457
+ );
5458
+ }
5459
+ canonicalTimestamp(bundle.exportedAt, "audit exportedAt");
5460
+ const core = Object.fromEntries(
5461
+ [...AUDIT_EXPORT_CORE_KEYS].map((key) => [key, bundle[key]]),
5462
+ );
5463
+ const exportDigest = digest(bundle.exportDigest, "audit exportDigest");
5464
+ if (exportDigest !== domainDigest(AUDIT_EXPORT_DOMAIN, core)) {
5465
+ throw ledgerError(
5466
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5467
+ "offline audit export digest is invalid",
5468
+ );
5469
+ }
5470
+ // Capture the fully traversed plain-data graph before invoking either
5471
+ // external trust port. A verifier closure must not be able to mutate the
5472
+ // caller's bundle between signature checks.
5473
+ const capturedBundle = JSON.parse(canonicalJson(bundle));
5474
+ const rawEvents = readDenseDataArray(
5475
+ capturedBundle.events,
5476
+ "audit events",
5477
+ EVOLUTION_LEDGER_MAX_EVENTS,
5478
+ );
5479
+ const rawAnchors = readDenseDataArray(
5480
+ capturedBundle.anchors,
5481
+ "audit anchors",
5482
+ EVOLUTION_LEDGER_MAX_EVENTS + 1,
5483
+ );
5484
+ const rawArtifacts = readDenseDataArray(
5485
+ capturedBundle.artifacts,
5486
+ "audit artifacts",
5487
+ MAX_AUDIT_ARTIFACT_RECORDS,
5488
+ );
5489
+
5490
+ const identity = verifyAuditIdentity(
5491
+ capturedBundle.identity,
5492
+ trust,
5493
+ witnessTrust,
5494
+ verifySignature,
5495
+ );
5496
+ const storeMarker = verifyAuditStoreMarker(
5497
+ capturedBundle.storeMarker,
5498
+ identity,
5499
+ trust,
5500
+ witnessTrust,
5501
+ verifySignature,
5502
+ );
5503
+ const events = [];
5504
+ for (const rawEvent of rawEvents) {
5505
+ events.push(
5506
+ verifyAuditEvent(
5507
+ rawEvent,
5508
+ events.at(-1) || null,
5509
+ identity,
5510
+ trust,
5511
+ witnessTrust,
5512
+ verifySignature,
5513
+ ),
5514
+ );
5515
+ }
5516
+ if (rawAnchors.length !== events.length + 1) {
5517
+ throw ledgerError(
5518
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5519
+ "offline audit anchor coverage is incomplete",
5520
+ );
5521
+ }
5522
+ const anchors = [];
5523
+ for (const rawAnchor of rawAnchors) {
5524
+ const anchor = verifyAuditAnchor(
5525
+ rawAnchor,
5526
+ anchors.at(-1) || null,
5527
+ identity,
5528
+ trust,
5529
+ witnessTrust,
5530
+ verifySignature,
5531
+ );
5532
+ if (anchor.sequence > 0) {
5533
+ const event = events[anchor.sequence - 1];
5534
+ if (
5535
+ !event ||
5536
+ anchor.headDigest !== event.eventDigest ||
5537
+ anchor.segmentDigest !== domainDigest(SEGMENT_DOMAIN, event)
5538
+ ) {
5539
+ throw ledgerError(
5540
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5541
+ `offline audit anchor is not bound to event ${anchor.sequence}`,
5542
+ );
5543
+ }
5544
+ }
5545
+ anchors.push(anchor);
5546
+ }
5547
+ verifyAuditArtifacts(rawArtifacts, events);
5548
+ const head = anchors.at(-1);
5549
+ const witness = verifyAuditWitness(
5550
+ capturedBundle.witness,
5551
+ identity,
5552
+ storeMarker,
5553
+ head,
5554
+ events.at(-1) || null,
5555
+ witnessTrust,
5556
+ verifyWitnessSignature,
5557
+ );
5558
+ return deepFreeze({
5559
+ artifactCount: rawArtifacts.length,
5560
+ eventCount: events.length,
5561
+ exportDigest,
5562
+ headDigest: head.headDigest,
5563
+ identityDigest: identity.identityDigest,
5564
+ ledgerId: identity.ledgerId,
5565
+ schema: EVOLUTION_LEDGER_AUDIT_VERIFICATION_SCHEMA,
5566
+ sequence: head.sequence,
5567
+ valid: true,
5568
+ witnessDigest: witness.witnessDigest,
5569
+ });
5570
+ } catch (cause) {
5571
+ if (cause?.code === "CC_EVOLUTION_LEDGER_AUDIT_INVALID") throw cause;
5572
+ throw ledgerError(
5573
+ "CC_EVOLUTION_LEDGER_AUDIT_INVALID",
5574
+ "offline evolution ledger audit verification failed closed",
5575
+ { cause },
5576
+ );
5577
+ }
5578
+ }
5579
+
4279
5580
  Object.freeze(EvolutionLedger.prototype);
4280
5581
  Object.freeze(EvolutionLedger);