chainlesschain 0.166.105 → 0.166.107
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.
- package/README.md +182 -8
- package/package.json +2 -2
- package/src/assets/linux-subreaper/linux-arm64/manifest.json +1 -1
- package/src/assets/linux-subreaper/linux-x64/manifest.json +1 -1
- package/src/assets/web-panel/assets/{AIOps-CcD-AVDw.js → AIOps-DCx6L_HW.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CYy-g36A.js → ActionButton-DYm1CN3Z.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DPFDNaBL.js → Analytics-D5ItUS_y.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-PVIuRdgN.js → AppLayout-Cb7XeRdH.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-BXGD3bM-.js → Artifacts-DDDeqCDQ.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-Bu5s_4tu.js → Audit-BRt3x13w.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-BHmgpnzo.js → BackgroundAgents-C_oNALMd.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-C872jURx.js → Backup-PSU2p6CR.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-Bh7xO30H.js → BaseInput-G3oL4hcX.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-DaacK53T.js → Chat-C2cLI10b.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-PYrSKMM_.js → ChatBubbleRenderer-KX1OITf8.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-Cnt7_8I2.js → Checkbox-CHLnEgFD.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-AZRz6kmW.js → Codegen-DZm9g_Bc.js} +1 -1
- package/src/assets/web-panel/assets/{Col-BtOXzMFt.js → Col-D5Z4zSay.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BFmFygS7.js → Community-DHw69NSk.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-2z5CfKpR.js → Compact-B-vjYNWz.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-B5hKrPVF.js → Compliance-DaIYVZQY.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CxEyp2ww.js → Cowork-CoA9U-gF.js} +3 -3
- package/src/assets/web-panel/assets/{Cron-B-8f9_qF.js → Cron-DXqmxlRC.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-XARMOcc4.js → Crosschain-Cdkc3ue1.js} +1 -1
- package/src/assets/web-panel/assets/{DID-DwLtEKAe.js → DID-oKfYyNSR.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-Cjw0L_iA.js → Dashboard-n_ycvgWu.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-BBGWIP7l.js → Dropdown-D38rZ8WY.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DG4kZFkm.js → EmailListRenderer-BjreeBQ1.js} +1 -1
- package/src/assets/web-panel/assets/{EvolutionSettings-D8vuNDde.js → EvolutionSettings-CTxA_SIq.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-COXCzDXI.js → FamilyGuardDashboard-DmECNwV5.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-Btb-aMhW.js → Federation-B2IaaDmQ.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-TO-E6l6e.js → FormItemContext-DgcgqUIk.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-BWXdJCT0.js → GenericCardRenderer-B1-du6Qm.js} +1 -1
- package/src/assets/web-panel/assets/{Git-B6icGhEv.js → Git-XceM21Il.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-C5Y__K6S.js → Governance-CV97iz3z.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-DZGEKnbN.js → Inference-CI7fj305.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph--Kga5yvm.js → KnowledgeGraph-B5mekWRy.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-BXs3EPKj.js → Logs-BlxFA0xY.js} +2 -2
- package/src/assets/web-panel/assets/MarkdownRenderer-rDE-XyMY.js +1 -0
- package/src/assets/web-panel/assets/{Marketplace-JSEAu0su.js → Marketplace-DL1BVCiX.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BUBifkKP.js → McpTools-BPtwrO3c.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-Cb7sE4a-.js → Memory-DuSNw4E-.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BOzmmNV2.js → MobileBridge-DTgY9ywI.js} +1 -1
- package/src/assets/web-panel/assets/MobileProjects-CTpVHJMa.js +1 -0
- package/src/assets/web-panel/assets/{Mtc-DmMWErG1.js → Mtc-h4fCLjXw.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-DnGkRVXF.js → MtcAudit-fqMDY3-w.js} +2 -2
- package/src/assets/web-panel/assets/{Multisig-C3mzascL.js → Multisig-Do3FehBL.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-CrmMZZ1t.js → NLProgramming-CQVY0kQF.js} +1 -1
- package/src/assets/web-panel/assets/Notes-4c3oNvWh.js +7 -0
- package/src/assets/web-panel/assets/{NotificationSettings-gn3UrWwW.js → NotificationSettings-BKr2JD4k.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-Bq4jgzRt.js +1 -0
- package/src/assets/web-panel/assets/{Organization-fMlkdgpW.js → Organization-Dy3XidyA.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DGwCUPU1.js → Overflow-DFF0qN5K.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BDGcpPmf.js → P2P-DAgYYykd.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-CccRPAB9.js → PdhVaultBrowser-DgNHa8b1.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-C33dNECx.js → Permissions-Cev6uSBc.js} +3 -3
- package/src/assets/web-panel/assets/{PersonalDataHub-BeqbCStc.js → PersonalDataHub-DJzbN06K.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-CTmasgbQ.js → Pipeline-CoJWvbQ5.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-ktd37BSp.js → Privacy-DZxV6etm.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-KOGvQtBX.js → ProjectInit-BFW87JnM.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-CUvH_hC6.js → ProjectSettings-LBrRT27p.js} +2 -2
- package/src/assets/web-panel/assets/Projects-O3uz76R1.js +1 -0
- package/src/assets/web-panel/assets/{Providers-C-hmLXlO.js → Providers-CnRbjOYk.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-DzG_rbSL.js → QrScannerModal-BVFnIS3r.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-ShwiATg4.js → QuickAsk-DrcHx5f1.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DkV8Ruep.js → Recommend-CbRgrSJm.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-Ca2dOx_G.js → RemoteSession-B2Uz_ElB.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-DYA5BJ6A.js → Reputation-DS0ljTxL.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DvGDgxLS.js → Row-ClLobFht.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DbwTBSPw.js → RssFeed-DxxcTD0x.js} +3 -3
- package/src/assets/web-panel/assets/{Search-C5XAMQ9T.js → Search-BbHN0Fik.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CKWT60Qt.js → Security-BAVU_EcY.js} +3 -3
- package/src/assets/web-panel/assets/{Services-CZvASlWL.js → Services-CtqRSVq_.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-DFkMVqws.js → Skeleton-IJ8ZKdSZ.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-hslZ_GyP.js → Skills-DXxcznnO.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-BLW5rmZ5.js → Sla-fRkhnA3i.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-Cn4ffCi5.js → SpeechSettings-Bi8sYJ-I.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-D1K0nJuo.js → SyncSettings-CjeplUtS.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-CCRu0xoN.js → Tasks-D4KB2Hbx.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-L19GYuS4.js → Templates-BwVugi8n.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-CZ6HE3PF.js → Tenant-CsPuu5xK.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-BiAwzaBT.js → Terminal-DuaeOPjY.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-DRHSWQT5.js → TimelineRenderer-D-PMrFxO.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-Cm-XATgh.js → Tokens-BXZuiQPZ.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BVXtx3kn.js → Trigger-DgpTaHL3.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-Bb0TmkLJ.js → Trust-Bn7lEGRY.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-Dbt9R7l0.js → UkeySign-zz-8JAAQ.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-z0-Ti8eJ.js → VideoEditing-DCzfQGTc.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-BZh31tI-.js → Wallet-BiRHM_zB.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-DQ0yIr5K.js → WebAuthn-QmPo2JwU.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-DCt_yuWG.js → WorkflowEditor-gL7SsPnY.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BLgtmFeS.js → chat-BysvK4hk.js} +1 -1
- package/src/assets/web-panel/assets/{colors-xU4UH8M1.js → colors-CQq20vqN.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-C4w4v16T.js → compact-item-CAHlOf1X.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-CZM9hAaj.js → createContext-BeF3ESj_.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-D-Iugqxv.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-DvHqEpE7.js → hasIn-CiqnRf-U.js} +1 -1
- package/src/assets/web-panel/assets/{index-BkTKWAgq.js → index-4a0Lmz_y.js} +1 -1
- package/src/assets/web-panel/assets/index-B0Oz2_rQ.js +1 -0
- package/src/assets/web-panel/assets/{index-B0IvKEVO.js → index-B2AOvjgR.js} +1 -1
- package/src/assets/web-panel/assets/{index-BaQZvM4m.js → index-B2xbtl_c.js} +1 -1
- package/src/assets/web-panel/assets/{index-BRhD9RMV.js → index-B5941vyo.js} +1 -1
- package/src/assets/web-panel/assets/{index-CqDiY-jl.js → index-BHpHGGmB.js} +1 -1
- package/src/assets/web-panel/assets/{index-CQEzxnYS.js → index-BOiyW31p.js} +1 -1
- package/src/assets/web-panel/assets/{index-BTxhDCtg.js → index-BcVmt6u_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CPDw21XY.js → index-BfJabF_8.js} +1 -1
- package/src/assets/web-panel/assets/{index-CExiTobS.js → index-Br5tGVbC.js} +1 -1
- package/src/assets/web-panel/assets/{index-QHmhwgfc.js → index-BsnLkZjz.js} +1 -1
- package/src/assets/web-panel/assets/{index-BlN5KPq6.js → index-BtWchYfW.js} +1 -1
- package/src/assets/web-panel/assets/{index-SNV95Zen.js → index-Bvkbj3x-.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHK3CQHq.js → index-CCX0daFQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CPC7OGCe.js → index-CImCEo8S.js} +1 -1
- package/src/assets/web-panel/assets/{index-CKugxsw3.js → index-CTp89vmg.js} +1 -1
- package/src/assets/web-panel/assets/{index-55Rkd1KA.js → index-CcccefJc.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ds6wKTMh.js → index-CeIL3-nY.js} +1 -1
- package/src/assets/web-panel/assets/{index-DifMvnMQ.js → index-CgkUkmEJ.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bf8Wm4ks.js → index-CowbNBJs.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cm00787d.js → index-Cp3IRTDK.js} +1 -1
- package/src/assets/web-panel/assets/{index-C9YnZFv2.js → index-D7q5VH2n.js} +1 -1
- package/src/assets/web-panel/assets/{index-BHg9NoJ5.js → index-DBmGQHEz.js} +3 -3
- package/src/assets/web-panel/assets/{index-B6AQ2b5U.js → index-DHGtFwwR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CYrQnkqA.js → index-DKYbru3i.js} +1 -1
- package/src/assets/web-panel/assets/{index-CyV8fNBg.js → index-DTPUVm71.js} +1 -1
- package/src/assets/web-panel/assets/{index-B4jIcYSv.js → index-DXT7MKkD.js} +1 -1
- package/src/assets/web-panel/assets/{index-B64Vf-8H.js → index-DXsXhR7p.js} +1 -1
- package/src/assets/web-panel/assets/{index-CpiuuIww.js → index-Dqkls2_6.js} +1 -1
- package/src/assets/web-panel/assets/index-DwoycEQa.js +1 -0
- package/src/assets/web-panel/assets/{index-DGdLkss8.js → index-QALafVsT.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ds1tM734.js → index-TdVEaJfk.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bh14b1k0.js → index-ZBuJ6O6g.js} +1 -1
- package/src/assets/web-panel/assets/{index-C6ZrTSw5.js → index-i9aJV9qk.js} +1 -1
- package/src/assets/web-panel/assets/{index-CF9ebAIB.js → index-jJhemX1R.js} +1 -1
- package/src/assets/web-panel/assets/{index-BIWumeQl.js → index-qjaCk1Mj.js} +1 -1
- package/src/assets/web-panel/assets/{index-n6SAUmmI.js → index-xwtRrgVY.js} +1 -1
- package/src/assets/web-panel/assets/{index-CRLP56-W.js → index-yFQWSRoW.js} +1 -1
- package/src/assets/web-panel/assets/{index-wYRjvvbl.js → index-zNkw0IY7.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-C44ZER6c.js → initDefaultProps-BQR2HnKb.js} +1 -1
- package/src/assets/web-panel/assets/{motion-DbNzzScd.js → motion-gAz6l-nZ.js} +1 -1
- package/src/assets/web-panel/assets/{move-BrRrDH2u.js → move-Cy5TN856.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-DZ_T_Pw8.js → mtc-parser-BeT7VrGi.js} +1 -1
- package/src/assets/web-panel/assets/{omit-5QHHk_V3.js → omit-BLN3mlgK.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-BdxEl0G2.js → pickAttrs-DtwZ8u2d.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-CGyD615D.js → placementArrow-ViMwQSa-.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-QqcWAQmN.js → responsiveObserve-dEL3u4E7.js} +1 -1
- package/src/assets/web-panel/assets/{slide-BumQrJ5R.js → slide-l05us-Gz.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-Pf64olM7.js → statusUtils-Bk-Qn2Og.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-DNwm5SQZ.js → styleChecker-Bn186qeY.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-c3gLKs6e.js → useFlexGapSupport-WP-ugR3H.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-TzTd6NDM.js → useFs-1SvoeSCL.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-BP5E_0Fe.js → usePersonalDataHub-CfNU8N3z.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CVEkRfZU.js → vnode-Bd2Ya101.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-C18X_8TJ.js → zoom-Def_MeZv.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +2 -2
- package/src/commands/agent.js +20 -20
- package/src/commands/goal.js +299 -1
- package/src/commands/plugin.js +129 -68
- package/src/constants.js +2 -2
- package/src/data/changelog.json +26 -0
- package/src/lib/cli-goal-catalog.js +129 -0
- package/src/lib/cli-goal-source.js +119 -0
- package/src/lib/coding-task-guidance.js +3 -1
- package/src/lib/config-schema.js +1 -1
- package/src/lib/eval/stream-terminal.js +27 -2
- package/src/lib/evolution/evolution-artifact-ports.js +22 -11
- package/src/lib/evolution/evolution-file-witness.js +86 -63
- package/src/lib/evolution/evolution-ledger-file-backend.js +22 -0
- package/src/lib/evolution/evolution-ledger-file-manifest-catalog.js +34 -35
- package/src/lib/evolution/evolution-ledger-file-manifest-head-store.js +32 -39
- package/src/lib/evolution/evolution-ledger.js +367 -169
- package/src/lib/evolution/evolution-manifest-state-reader.js +58 -0
- package/src/lib/evolution/evolution-physical-state-reader.js +308 -0
- package/src/lib/evolution/evolution-witness-segments.js +12 -13
- package/src/lib/evolution/governed-wiki-pruning-ledger-adapter.js +82 -56
- package/src/lib/evolution/rrsi-observed-text-index.js +17 -1
- package/src/lib/evolution/rrsi-tenant-index-anchor-history.js +5 -0
- package/src/lib/execution-location-failure-site.js +20 -3
- package/src/lib/goal-store.js +8 -1
- package/src/lib/image-input.js +43 -14
- package/src/lib/llm-providers.js +1 -0
- package/src/lib/plugin-runtime/capabilities.js +10 -0
- package/src/lib/plugin-runtime/cli-goal-draft-request.js +138 -0
- package/src/lib/plugin-runtime/cli-goal-drafts.js +413 -0
- package/src/lib/plugin-runtime/cli-goal-extension-host.js +234 -0
- package/src/lib/plugin-runtime/examples/goal-description-drafter/plugin.json +17 -0
- package/src/lib/plugin-runtime/examples/goal-evidence-viewer/plugin.json +25 -0
- package/src/lib/plugin-runtime/examples/goal-progress-viewer/plugin.json +21 -0
- package/src/lib/plugin-runtime/examples/project-delivery-assistant/plugin.json +38 -0
- package/src/lib/plugin-runtime/goal-extension-contract.js +190 -0
- package/src/lib/plugin-runtime/goal-extensions.js +277 -0
- package/src/lib/plugin-runtime/install.js +40 -40
- package/src/lib/plugin-runtime/manifest.js +12 -0
- package/src/lib/plugin-runtime/policy.js +1 -0
- package/src/lib/project-goal-connection.js +225 -0
- package/src/lib/project-goal-draft-connection.js +217 -0
- package/src/lib/provider-http-error.js +4 -5
- package/src/lib/task-model-selector.js +1 -1
- package/src/repl/agent-repl.js +14 -7
- package/src/repl/config-summary.js +1 -1
- package/src/runtime/agent-core.js +1 -1
- package/src/runtime/coding-agent-contract-shared.cjs +3 -2
- package/src/runtime/headless-stream.js +4 -6
- package/src/runtime/policies/agent-policy.js +1 -0
- package/src/assets/web-panel/assets/MarkdownRenderer-BsSiyE-A.js +0 -1
- package/src/assets/web-panel/assets/MobileProjects-ClRlavdF.js +0 -1
- package/src/assets/web-panel/assets/Notes-4S6uts9Q.js +0 -7
- package/src/assets/web-panel/assets/OrderTableRenderer-CDVAlBUf.js +0 -1
- package/src/assets/web-panel/assets/Projects-BvP_Os-1.js +0 -1
- package/src/assets/web-panel/assets/devWarning-BZUZgMkO.js +0 -1
- package/src/assets/web-panel/assets/index--_-pNhk0.js +0 -1
- package/src/assets/web-panel/assets/index-ftwO31cp.js +0 -1
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
withEvolutionDirectoryFileIdentity,
|
|
8
8
|
withEvolutionFileIdentity,
|
|
9
9
|
} from "./evolution-file-identity.js";
|
|
10
|
-
import {
|
|
10
|
+
import { artifactPhysicalIdentity } from "./evolution-artifact-identity.js";
|
|
11
11
|
import { getHomeDir } from "../paths.js";
|
|
12
12
|
import { ensurePrivateDirectory, ensurePrivateFile } from "../secure-fs.js";
|
|
13
13
|
import { withFileLock } from "../with-file-lock.js";
|
|
@@ -59,6 +59,7 @@ export const EVOLUTION_LEDGER_V2_MIGRATION_INTENT =
|
|
|
59
59
|
export const EVOLUTION_LEDGER_V2_CUTOVER_COMPLETED =
|
|
60
60
|
"evolution.ledger-v2.cutover-completed";
|
|
61
61
|
const MIGRATION_SOURCES = new WeakMap();
|
|
62
|
+
const DIRECTORY_BOUNDARIES = new WeakMap();
|
|
62
63
|
|
|
63
64
|
const IDENTITY_DOMAIN = "chainlesschain.evolution-ledger-identity/v1\0";
|
|
64
65
|
const EVENT_DOMAIN = "chainlesschain.evolution-event/v2\0";
|
|
@@ -557,7 +558,7 @@ function isUnsafeAsyncResult(value) {
|
|
|
557
558
|
const descriptor = Object.getOwnPropertyDescriptor(value, "then");
|
|
558
559
|
return Boolean(
|
|
559
560
|
descriptor &&
|
|
560
|
-
|
|
561
|
+
(!("value" in descriptor) || typeof descriptor.value === "function"),
|
|
561
562
|
);
|
|
562
563
|
}
|
|
563
564
|
|
|
@@ -1656,6 +1657,75 @@ function fileFingerprint(stat) {
|
|
|
1656
1657
|
].join(":");
|
|
1657
1658
|
}
|
|
1658
1659
|
|
|
1660
|
+
const PHYSICAL_FILE_FIELDS = Object.freeze([
|
|
1661
|
+
"dev",
|
|
1662
|
+
"ino",
|
|
1663
|
+
"mode",
|
|
1664
|
+
"uid",
|
|
1665
|
+
"gid",
|
|
1666
|
+
"nlink",
|
|
1667
|
+
"size",
|
|
1668
|
+
"mtimeNs",
|
|
1669
|
+
"ctimeNs",
|
|
1670
|
+
]);
|
|
1671
|
+
const PHYSICAL_DIRECTORY_FIELDS = Object.freeze([
|
|
1672
|
+
"dev",
|
|
1673
|
+
"ino",
|
|
1674
|
+
"mode",
|
|
1675
|
+
"uid",
|
|
1676
|
+
"gid",
|
|
1677
|
+
]);
|
|
1678
|
+
const physicalBirthtime = (stat) => artifactPhysicalIdentity(stat).birthtimeMs;
|
|
1679
|
+
const samePhysicalFile = (left, right) =>
|
|
1680
|
+
PHYSICAL_FILE_FIELDS.every((field) => left[field] === right[field]) &&
|
|
1681
|
+
physicalBirthtime(left) === physicalBirthtime(right);
|
|
1682
|
+
const samePhysicalOwner = (left, right) =>
|
|
1683
|
+
left.uid === right.uid &&
|
|
1684
|
+
left.gid === right.gid &&
|
|
1685
|
+
physicalBirthtime(left) === physicalBirthtime(right);
|
|
1686
|
+
|
|
1687
|
+
function assertPhysicalFile(stat, maximum, label) {
|
|
1688
|
+
if (PHYSICAL_FILE_FIELDS.some((field) => typeof stat?.[field] !== "bigint")) {
|
|
1689
|
+
throw ledgerError(
|
|
1690
|
+
"CC_EVOLUTION_LEDGER_CORRUPT",
|
|
1691
|
+
`${label} requires full-precision stat fields`,
|
|
1692
|
+
);
|
|
1693
|
+
}
|
|
1694
|
+
if (
|
|
1695
|
+
!stat.isFile() ||
|
|
1696
|
+
stat.isSymbolicLink() ||
|
|
1697
|
+
stat.nlink !== 1n ||
|
|
1698
|
+
stat.size < 2n ||
|
|
1699
|
+
stat.size > BigInt(maximum)
|
|
1700
|
+
) {
|
|
1701
|
+
throw ledgerError(
|
|
1702
|
+
"CC_EVOLUTION_LEDGER_CORRUPT",
|
|
1703
|
+
`${label} must be a bounded, single-link, non-symlink file`,
|
|
1704
|
+
);
|
|
1705
|
+
}
|
|
1706
|
+
}
|
|
1707
|
+
|
|
1708
|
+
function physicalDirectoryIdentity(stat) {
|
|
1709
|
+
if (
|
|
1710
|
+
PHYSICAL_DIRECTORY_FIELDS.some(
|
|
1711
|
+
(field) => typeof stat?.[field] !== "bigint",
|
|
1712
|
+
) ||
|
|
1713
|
+
!stat.isDirectory() ||
|
|
1714
|
+
stat.isSymbolicLink()
|
|
1715
|
+
) {
|
|
1716
|
+
throw ledgerError(
|
|
1717
|
+
"CC_EVOLUTION_LEDGER_STORE_UNSAFE",
|
|
1718
|
+
"ledger boundary requires a full-precision directory identity",
|
|
1719
|
+
);
|
|
1720
|
+
}
|
|
1721
|
+
return Object.freeze({
|
|
1722
|
+
...Object.fromEntries(
|
|
1723
|
+
PHYSICAL_DIRECTORY_FIELDS.map((field) => [field, stat[field]]),
|
|
1724
|
+
),
|
|
1725
|
+
birthtimeMs: physicalBirthtime(stat),
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1728
|
+
|
|
1659
1729
|
function storeMarkerEntryDigest(storeMarkerFile) {
|
|
1660
1730
|
return domainDigest(STORE_MARKER_ENTRY_DOMAIN, {
|
|
1661
1731
|
contentDigest: storeMarkerFile.contentDigest,
|
|
@@ -1831,9 +1901,11 @@ function defaultAuthorityRoot(rootDir) {
|
|
|
1831
1901
|
export class EvolutionLedger {
|
|
1832
1902
|
#artifactResolver;
|
|
1833
1903
|
#boundaries;
|
|
1904
|
+
#physicalBoundaries;
|
|
1834
1905
|
#clock;
|
|
1835
1906
|
#crashHook;
|
|
1836
1907
|
#fs;
|
|
1908
|
+
#identityFs;
|
|
1837
1909
|
#lock;
|
|
1838
1910
|
#lockClock;
|
|
1839
1911
|
#lockTimeoutMs;
|
|
@@ -1900,6 +1972,17 @@ export class EvolutionLedger {
|
|
|
1900
1972
|
}
|
|
1901
1973
|
|
|
1902
1974
|
this.#fs = captureFs(fsImpl);
|
|
1975
|
+
// Identity helpers only open parent/volume directories through this port.
|
|
1976
|
+
this.#identityFs = Object.freeze({
|
|
1977
|
+
...this.#fs,
|
|
1978
|
+
openSync: (target, flags) =>
|
|
1979
|
+
this.#fs.openSync(
|
|
1980
|
+
target,
|
|
1981
|
+
flags |
|
|
1982
|
+
(this.#fs.constants.O_NONBLOCK || 0) |
|
|
1983
|
+
(this.#fs.constants.O_DIRECTORY || 0),
|
|
1984
|
+
),
|
|
1985
|
+
});
|
|
1903
1986
|
this.#trust = normalizeTrust(trust);
|
|
1904
1987
|
this.#witnessTrust = normalizeTrust(witnessTrust);
|
|
1905
1988
|
this.#witness = captureWitness(witness);
|
|
@@ -1984,6 +2067,23 @@ export class EvolutionLedger {
|
|
|
1984
2067
|
STORE_MARKER_FILE_NAME,
|
|
1985
2068
|
),
|
|
1986
2069
|
});
|
|
2070
|
+
// These per-instance physical pins do not alter the persisted Number
|
|
2071
|
+
// directory identities used by the original store-binding signature.
|
|
2072
|
+
this.#physicalBoundaries = Object.freeze(
|
|
2073
|
+
[
|
|
2074
|
+
this.#paths.rootDir,
|
|
2075
|
+
this.#paths.authorityRootDir,
|
|
2076
|
+
this.#paths.segmentDir,
|
|
2077
|
+
this.#paths.anchorDir,
|
|
2078
|
+
].map((directory) =>
|
|
2079
|
+
Object.freeze({
|
|
2080
|
+
directory,
|
|
2081
|
+
identity: physicalDirectoryIdentity(
|
|
2082
|
+
this.#fs.lstatSync(directory, { bigint: true }),
|
|
2083
|
+
),
|
|
2084
|
+
}),
|
|
2085
|
+
),
|
|
2086
|
+
);
|
|
1987
2087
|
this.#boundaries = deepFreeze(
|
|
1988
2088
|
[
|
|
1989
2089
|
this.#paths.rootDir,
|
|
@@ -2039,6 +2139,53 @@ export class EvolutionLedger {
|
|
|
2039
2139
|
this.#appendEvents(inputs, options, true, true, true),
|
|
2040
2140
|
}),
|
|
2041
2141
|
);
|
|
2142
|
+
// Project the original constructor pins, never a fresh claim of the paths
|
|
2143
|
+
// first supplied to a later caller. Mutable directory metadata is excluded.
|
|
2144
|
+
const directoryBoundary = deepFreeze({
|
|
2145
|
+
schema: "chainlesschain.evolution-ledger-directory-boundary/v1",
|
|
2146
|
+
scope: "source-journal-four-directories",
|
|
2147
|
+
directories: this.#physicalBoundaries.map(
|
|
2148
|
+
({ directory, identity }, index) => ({
|
|
2149
|
+
role: ["events", "authority", "segments", "anchors"][index],
|
|
2150
|
+
path: directory,
|
|
2151
|
+
identity: {
|
|
2152
|
+
...Object.fromEntries(
|
|
2153
|
+
PHYSICAL_DIRECTORY_FIELDS.map((field) => [
|
|
2154
|
+
field,
|
|
2155
|
+
String(identity[field]),
|
|
2156
|
+
]),
|
|
2157
|
+
),
|
|
2158
|
+
birthtimeMs: identity.birthtimeMs,
|
|
2159
|
+
},
|
|
2160
|
+
}),
|
|
2161
|
+
),
|
|
2162
|
+
journalFileIdentityVerified: false,
|
|
2163
|
+
journalContentsVerified: false,
|
|
2164
|
+
witnessDirectoriesVerified: false,
|
|
2165
|
+
retentionStorageVerified: false,
|
|
2166
|
+
fullPhysicalStorageGraphVerified: false,
|
|
2167
|
+
rootAuthorityVerified: false,
|
|
2168
|
+
crossProcessRollbackProtectionVerified: false,
|
|
2169
|
+
tenantWideIndexAuthorityVerified: false,
|
|
2170
|
+
grantsMutationOrPromotionAuthority: false,
|
|
2171
|
+
});
|
|
2172
|
+
const recheck = (...args) => {
|
|
2173
|
+
if (args.length)
|
|
2174
|
+
throw new TypeError(
|
|
2175
|
+
"ledger directory boundary takes no replacement context",
|
|
2176
|
+
);
|
|
2177
|
+
this.#assertBoundaries();
|
|
2178
|
+
return directoryBoundary;
|
|
2179
|
+
};
|
|
2180
|
+
recheck();
|
|
2181
|
+
if (new.target === EvolutionLedger)
|
|
2182
|
+
DIRECTORY_BOUNDARIES.set(
|
|
2183
|
+
this,
|
|
2184
|
+
Object.freeze({
|
|
2185
|
+
descriptor: directoryBoundary,
|
|
2186
|
+
recheck: Object.freeze(recheck),
|
|
2187
|
+
}),
|
|
2188
|
+
);
|
|
2042
2189
|
Object.freeze(this);
|
|
2043
2190
|
}
|
|
2044
2191
|
|
|
@@ -2071,11 +2218,11 @@ export class EvolutionLedger {
|
|
|
2071
2218
|
}
|
|
2072
2219
|
|
|
2073
2220
|
#assertBoundaries() {
|
|
2074
|
-
for (const boundary of this.#
|
|
2221
|
+
for (const boundary of this.#physicalBoundaries) {
|
|
2075
2222
|
let stat;
|
|
2076
2223
|
let canonical;
|
|
2077
2224
|
try {
|
|
2078
|
-
stat = this.#fs.lstatSync(boundary.directory);
|
|
2225
|
+
stat = this.#fs.lstatSync(boundary.directory, { bigint: true });
|
|
2079
2226
|
canonical = realpath(this.#fs, boundary.directory);
|
|
2080
2227
|
} catch (cause) {
|
|
2081
2228
|
if (cause instanceof EvolutionLedgerError) throw cause;
|
|
@@ -2085,10 +2232,12 @@ export class EvolutionLedger {
|
|
|
2085
2232
|
{ cause },
|
|
2086
2233
|
);
|
|
2087
2234
|
}
|
|
2235
|
+
const identity = physicalDirectoryIdentity(stat);
|
|
2088
2236
|
if (
|
|
2089
|
-
!
|
|
2090
|
-
|
|
2091
|
-
|
|
2237
|
+
!PHYSICAL_DIRECTORY_FIELDS.every(
|
|
2238
|
+
(field) => identity[field] === boundary.identity[field],
|
|
2239
|
+
) ||
|
|
2240
|
+
identity.birthtimeMs !== boundary.identity.birthtimeMs ||
|
|
2092
2241
|
!samePath(canonical, boundary.directory)
|
|
2093
2242
|
) {
|
|
2094
2243
|
throw ledgerError(
|
|
@@ -2805,24 +2954,27 @@ export class EvolutionLedger {
|
|
|
2805
2954
|
}
|
|
2806
2955
|
|
|
2807
2956
|
#readCanonicalFile(filePath, maximum, label, { allowMissing = false } = {}) {
|
|
2808
|
-
return withEvolutionFileIdentity(
|
|
2809
|
-
this.#
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2814
|
-
|
|
2815
|
-
|
|
2957
|
+
return withEvolutionFileIdentity(
|
|
2958
|
+
this.#identityFs,
|
|
2959
|
+
filePath,
|
|
2960
|
+
(samePathHandle) =>
|
|
2961
|
+
this.#readCanonicalFileWithIdentity(
|
|
2962
|
+
filePath,
|
|
2963
|
+
maximum,
|
|
2964
|
+
label,
|
|
2965
|
+
allowMissing,
|
|
2966
|
+
samePathHandle,
|
|
2967
|
+
),
|
|
2816
2968
|
);
|
|
2817
2969
|
}
|
|
2818
2970
|
|
|
2819
|
-
#
|
|
2971
|
+
#readPhysicalFileWithIdentity(
|
|
2820
2972
|
filePath,
|
|
2821
2973
|
maximum,
|
|
2822
2974
|
label,
|
|
2823
2975
|
allowMissing,
|
|
2824
2976
|
samePathHandle,
|
|
2825
|
-
{
|
|
2977
|
+
{ trustedParentPath = null } = {},
|
|
2826
2978
|
) {
|
|
2827
2979
|
const heldParent =
|
|
2828
2980
|
trustedParentPath !== null &&
|
|
@@ -2830,9 +2982,12 @@ export class EvolutionLedger {
|
|
|
2830
2982
|
if (!heldParent) this.#assertBoundaries();
|
|
2831
2983
|
let before;
|
|
2832
2984
|
try {
|
|
2833
|
-
before = this.#fs.lstatSync(filePath);
|
|
2985
|
+
before = this.#fs.lstatSync(filePath, { bigint: true });
|
|
2834
2986
|
} catch (cause) {
|
|
2835
|
-
if (allowMissing && cause?.code === "ENOENT")
|
|
2987
|
+
if (allowMissing && cause?.code === "ENOENT") {
|
|
2988
|
+
if (!heldParent) this.#assertBoundaries();
|
|
2989
|
+
return null;
|
|
2990
|
+
}
|
|
2836
2991
|
throw ledgerError(
|
|
2837
2992
|
cause?.code === "ENOENT"
|
|
2838
2993
|
? "CC_EVOLUTION_LEDGER_CORRUPT"
|
|
@@ -2841,31 +2996,36 @@ export class EvolutionLedger {
|
|
|
2841
2996
|
{ cause },
|
|
2842
2997
|
);
|
|
2843
2998
|
}
|
|
2844
|
-
|
|
2845
|
-
!before.isFile() ||
|
|
2846
|
-
before.isSymbolicLink() ||
|
|
2847
|
-
before.nlink !== 1 ||
|
|
2848
|
-
before.size < 2 ||
|
|
2849
|
-
before.size > maximum ||
|
|
2850
|
-
(!heldParent && !samePath(realpath(this.#fs, filePath), filePath))
|
|
2851
|
-
) {
|
|
2852
|
-
throw ledgerError(
|
|
2853
|
-
"CC_EVOLUTION_LEDGER_CORRUPT",
|
|
2854
|
-
`${label} must be a bounded, single-link, non-symlink file`,
|
|
2855
|
-
);
|
|
2856
|
-
}
|
|
2999
|
+
assertPhysicalFile(before, maximum, label);
|
|
2857
3000
|
let descriptor = null;
|
|
2858
3001
|
try {
|
|
3002
|
+
// Preserve the exact legacy Number fingerprint already signed into the
|
|
3003
|
+
// store marker binding. It is sampled inside a separate BigInt window and
|
|
3004
|
+
// is never used as the authority for a physical file/handle comparison.
|
|
3005
|
+
const legacyBefore = this.#fs.lstatSync(filePath);
|
|
3006
|
+
const sampled = this.#fs.lstatSync(filePath, { bigint: true });
|
|
3007
|
+
assertPhysicalFile(sampled, maximum, label);
|
|
3008
|
+
if (
|
|
3009
|
+
!samePhysicalFile(before, sampled) ||
|
|
3010
|
+
(!heldParent && !samePath(realpath(this.#fs, filePath), filePath))
|
|
3011
|
+
) {
|
|
3012
|
+
throw ledgerError(
|
|
3013
|
+
"CC_EVOLUTION_LEDGER_CORRUPT",
|
|
3014
|
+
`${label} changed while its fingerprint was sampled`,
|
|
3015
|
+
);
|
|
3016
|
+
}
|
|
3017
|
+
const fingerprint = fileFingerprint(legacyBefore);
|
|
2859
3018
|
descriptor = this.#fs.openSync(
|
|
2860
3019
|
filePath,
|
|
2861
|
-
this.#fs.constants.O_RDONLY |
|
|
3020
|
+
this.#fs.constants.O_RDONLY |
|
|
3021
|
+
(this.#fs.constants.O_NOFOLLOW || 0) |
|
|
3022
|
+
(this.#fs.constants.O_NONBLOCK || 0),
|
|
2862
3023
|
);
|
|
2863
|
-
const opened = this.#fs.fstatSync(descriptor);
|
|
3024
|
+
const opened = this.#fs.fstatSync(descriptor, { bigint: true });
|
|
3025
|
+
assertPhysicalFile(opened, maximum, label);
|
|
2864
3026
|
if (
|
|
2865
|
-
!opened.isFile() ||
|
|
2866
|
-
opened.nlink !== 1 ||
|
|
2867
3027
|
!samePathHandle(before, opened) ||
|
|
2868
|
-
opened
|
|
3028
|
+
!samePhysicalOwner(before, opened)
|
|
2869
3029
|
) {
|
|
2870
3030
|
throw ledgerError(
|
|
2871
3031
|
"CC_EVOLUTION_LEDGER_CORRUPT",
|
|
@@ -2877,7 +3037,7 @@ export class EvolutionLedger {
|
|
|
2877
3037
|
bytes = readBoundedDescriptor(
|
|
2878
3038
|
this.#fs,
|
|
2879
3039
|
descriptor,
|
|
2880
|
-
opened.size,
|
|
3040
|
+
Number(opened.size),
|
|
2881
3041
|
maximum,
|
|
2882
3042
|
);
|
|
2883
3043
|
} catch (cause) {
|
|
@@ -2887,76 +3047,106 @@ export class EvolutionLedger {
|
|
|
2887
3047
|
{ cause },
|
|
2888
3048
|
);
|
|
2889
3049
|
}
|
|
2890
|
-
const after = this.#fs.fstatSync(descriptor);
|
|
3050
|
+
const after = this.#fs.fstatSync(descriptor, { bigint: true });
|
|
3051
|
+
const legacyAfter = this.#fs.lstatSync(filePath);
|
|
3052
|
+
const afterPath = this.#fs.lstatSync(filePath, { bigint: true });
|
|
3053
|
+
assertPhysicalFile(after, maximum, label);
|
|
3054
|
+
assertPhysicalFile(afterPath, maximum, label);
|
|
2891
3055
|
if (
|
|
2892
|
-
after
|
|
2893
|
-
|
|
2894
|
-
after.size !== opened.size ||
|
|
2895
|
-
bytes.length !== opened.size
|
|
3056
|
+
!samePhysicalFile(opened, after) ||
|
|
3057
|
+
bytes.length !== Number(opened.size)
|
|
2896
3058
|
) {
|
|
2897
3059
|
throw ledgerError(
|
|
2898
3060
|
"CC_EVOLUTION_LEDGER_CORRUPT",
|
|
2899
3061
|
`${label} changed while it was read`,
|
|
2900
3062
|
);
|
|
2901
3063
|
}
|
|
2902
|
-
if (
|
|
2903
|
-
|
|
2904
|
-
|
|
2905
|
-
|
|
2906
|
-
|
|
2907
|
-
|
|
2908
|
-
|
|
2909
|
-
return {
|
|
2910
|
-
bytes,
|
|
2911
|
-
contentDigest: sha256(bytes),
|
|
2912
|
-
fingerprint: fileFingerprint(before),
|
|
2913
|
-
record: null,
|
|
2914
|
-
};
|
|
2915
|
-
}
|
|
2916
|
-
let text;
|
|
2917
|
-
try {
|
|
2918
|
-
text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
2919
|
-
} catch (cause) {
|
|
2920
|
-
throw ledgerError(
|
|
2921
|
-
"CC_EVOLUTION_LEDGER_CORRUPT",
|
|
2922
|
-
`${label} is not valid UTF-8`,
|
|
2923
|
-
{ cause },
|
|
2924
|
-
);
|
|
2925
|
-
}
|
|
2926
|
-
if (!text.endsWith("\n") || text.slice(0, -1).includes("\n")) {
|
|
2927
|
-
throw ledgerError(
|
|
2928
|
-
"CC_EVOLUTION_LEDGER_CORRUPT",
|
|
2929
|
-
`${label} is truncated or not a single canonical record`,
|
|
2930
|
-
);
|
|
2931
|
-
}
|
|
2932
|
-
let record;
|
|
2933
|
-
try {
|
|
2934
|
-
record = JSON.parse(text.slice(0, -1));
|
|
2935
|
-
} catch (cause) {
|
|
3064
|
+
if (
|
|
3065
|
+
!samePhysicalFile(before, afterPath) ||
|
|
3066
|
+
!samePathHandle(afterPath, after) ||
|
|
3067
|
+
!samePhysicalOwner(afterPath, after) ||
|
|
3068
|
+
fileFingerprint(legacyAfter) !== fingerprint ||
|
|
3069
|
+
(!heldParent && !samePath(realpath(this.#fs, filePath), filePath))
|
|
3070
|
+
) {
|
|
2936
3071
|
throw ledgerError(
|
|
2937
3072
|
"CC_EVOLUTION_LEDGER_CORRUPT",
|
|
2938
|
-
`${label}
|
|
2939
|
-
{ cause },
|
|
3073
|
+
`${label} pathname changed during bounded read`,
|
|
2940
3074
|
);
|
|
2941
3075
|
}
|
|
2942
|
-
|
|
3076
|
+
return { bytes, fingerprint };
|
|
3077
|
+
} catch (cause) {
|
|
3078
|
+
if (cause instanceof EvolutionLedgerError) throw cause;
|
|
3079
|
+
throw ledgerError(
|
|
3080
|
+
"CC_EVOLUTION_LEDGER_CORRUPT",
|
|
3081
|
+
`${label} changed during bounded read`,
|
|
3082
|
+
{ cause },
|
|
3083
|
+
);
|
|
3084
|
+
} finally {
|
|
3085
|
+
if (descriptor !== null) this.#fs.closeSync(descriptor);
|
|
3086
|
+
if (!heldParent) this.#assertBoundaries();
|
|
3087
|
+
}
|
|
3088
|
+
}
|
|
3089
|
+
|
|
3090
|
+
#readCanonicalFileWithIdentity(
|
|
3091
|
+
filePath,
|
|
3092
|
+
maximum,
|
|
3093
|
+
label,
|
|
3094
|
+
allowMissing,
|
|
3095
|
+
samePathHandle,
|
|
3096
|
+
{ expectedBytes = null, trustedParentPath = null } = {},
|
|
3097
|
+
) {
|
|
3098
|
+
const file = this.#readPhysicalFileWithIdentity(
|
|
3099
|
+
filePath,
|
|
3100
|
+
maximum,
|
|
3101
|
+
label,
|
|
3102
|
+
allowMissing,
|
|
3103
|
+
samePathHandle,
|
|
3104
|
+
{ trustedParentPath },
|
|
3105
|
+
);
|
|
3106
|
+
if (file === null) return null;
|
|
3107
|
+
const { bytes, fingerprint } = file;
|
|
3108
|
+
if (expectedBytes !== null) {
|
|
3109
|
+
if (!Buffer.isBuffer(expectedBytes) || !bytes.equals(expectedBytes)) {
|
|
2943
3110
|
throw ledgerError(
|
|
2944
3111
|
"CC_EVOLUTION_LEDGER_CORRUPT",
|
|
2945
|
-
`${label}
|
|
3112
|
+
`${label} differs from its signed snapshot bytes`,
|
|
2946
3113
|
);
|
|
2947
3114
|
}
|
|
2948
|
-
return {
|
|
2949
|
-
|
|
2950
|
-
|
|
2951
|
-
|
|
2952
|
-
|
|
2953
|
-
|
|
2954
|
-
|
|
2955
|
-
|
|
2956
|
-
|
|
2957
|
-
|
|
2958
|
-
|
|
3115
|
+
return { bytes, contentDigest: sha256(bytes), fingerprint, record: null };
|
|
3116
|
+
}
|
|
3117
|
+
let text;
|
|
3118
|
+
try {
|
|
3119
|
+
text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
3120
|
+
} catch (cause) {
|
|
3121
|
+
throw ledgerError(
|
|
3122
|
+
"CC_EVOLUTION_LEDGER_CORRUPT",
|
|
3123
|
+
`${label} is not valid UTF-8`,
|
|
3124
|
+
{ cause },
|
|
3125
|
+
);
|
|
3126
|
+
}
|
|
3127
|
+
if (!text.endsWith("\n") || text.slice(0, -1).includes("\n")) {
|
|
3128
|
+
throw ledgerError(
|
|
3129
|
+
"CC_EVOLUTION_LEDGER_CORRUPT",
|
|
3130
|
+
`${label} is truncated or not a single canonical record`,
|
|
3131
|
+
);
|
|
2959
3132
|
}
|
|
3133
|
+
let record;
|
|
3134
|
+
try {
|
|
3135
|
+
record = JSON.parse(text.slice(0, -1));
|
|
3136
|
+
} catch (cause) {
|
|
3137
|
+
throw ledgerError(
|
|
3138
|
+
"CC_EVOLUTION_LEDGER_CORRUPT",
|
|
3139
|
+
`${label} contains invalid JSON`,
|
|
3140
|
+
{ cause },
|
|
3141
|
+
);
|
|
3142
|
+
}
|
|
3143
|
+
if (`${canonicalJson(record)}\n` !== text) {
|
|
3144
|
+
throw ledgerError(
|
|
3145
|
+
"CC_EVOLUTION_LEDGER_CORRUPT",
|
|
3146
|
+
`${label} bytes are not canonical`,
|
|
3147
|
+
);
|
|
3148
|
+
}
|
|
3149
|
+
return { bytes, contentDigest: sha256(bytes), fingerprint, record };
|
|
2960
3150
|
}
|
|
2961
3151
|
|
|
2962
3152
|
#writeImmutable(filePath, bytes, label, onPublished = null) {
|
|
@@ -3435,61 +3625,24 @@ export class EvolutionLedger {
|
|
|
3435
3625
|
);
|
|
3436
3626
|
}
|
|
3437
3627
|
|
|
3438
|
-
#
|
|
3439
|
-
|
|
3440
|
-
|
|
3441
|
-
|
|
3442
|
-
|
|
3443
|
-
|
|
3444
|
-
|
|
3445
|
-
|
|
3446
|
-
|
|
3447
|
-
|
|
3448
|
-
|
|
3449
|
-
|
|
3450
|
-
|
|
3451
|
-
|
|
3452
|
-
|
|
3453
|
-
|
|
3454
|
-
|
|
3455
|
-
|
|
3456
|
-
fileFingerprint(before) !== expected.fingerprint ||
|
|
3457
|
-
!samePath(realpath(this.#fs, filePath), filePath)
|
|
3458
|
-
) {
|
|
3459
|
-
return false;
|
|
3460
|
-
}
|
|
3461
|
-
descriptor = this.#fs.openSync(
|
|
3462
|
-
filePath,
|
|
3463
|
-
this.#fs.constants.O_RDONLY | (this.#fs.constants.O_NOFOLLOW || 0),
|
|
3464
|
-
);
|
|
3465
|
-
const opened = this.#fs.fstatSync(descriptor);
|
|
3466
|
-
if (
|
|
3467
|
-
!opened.isFile() ||
|
|
3468
|
-
opened.nlink !== 1 ||
|
|
3469
|
-
!samePathHandle(before, opened) ||
|
|
3470
|
-
opened.size !== before.size
|
|
3471
|
-
) {
|
|
3472
|
-
return false;
|
|
3473
|
-
}
|
|
3474
|
-
const bytes = readBoundedDescriptor(
|
|
3475
|
-
this.#fs,
|
|
3476
|
-
descriptor,
|
|
3477
|
-
opened.size,
|
|
3478
|
-
EVOLUTION_LEDGER_MAX_EVENT_BYTES,
|
|
3479
|
-
);
|
|
3480
|
-
const after = this.#fs.fstatSync(descriptor);
|
|
3481
|
-
return (
|
|
3482
|
-
after.nlink === 1 &&
|
|
3483
|
-
entryIdentity(after) === entryIdentity(opened) &&
|
|
3484
|
-
samePathHandle(before, after) &&
|
|
3485
|
-
bytes.length === opened.size &&
|
|
3486
|
-
sha256(bytes) === expected.contentDigest
|
|
3487
|
-
);
|
|
3488
|
-
} catch {
|
|
3489
|
-
return false;
|
|
3490
|
-
} finally {
|
|
3491
|
-
if (descriptor !== null) this.#fs.closeSync(descriptor);
|
|
3492
|
-
}
|
|
3628
|
+
#matchesCachedFileWithIdentity(
|
|
3629
|
+
filePath,
|
|
3630
|
+
expected,
|
|
3631
|
+
samePathHandle,
|
|
3632
|
+
trustedParentPath,
|
|
3633
|
+
) {
|
|
3634
|
+
const file = this.#readPhysicalFileWithIdentity(
|
|
3635
|
+
filePath,
|
|
3636
|
+
EVOLUTION_LEDGER_MAX_EVENT_BYTES,
|
|
3637
|
+
"cached ledger file",
|
|
3638
|
+
false,
|
|
3639
|
+
samePathHandle,
|
|
3640
|
+
{ trustedParentPath },
|
|
3641
|
+
);
|
|
3642
|
+
return (
|
|
3643
|
+
file.fingerprint === expected.fingerprint &&
|
|
3644
|
+
sha256(file.bytes) === expected.contentDigest
|
|
3645
|
+
);
|
|
3493
3646
|
}
|
|
3494
3647
|
|
|
3495
3648
|
#snapshotFileRecords(values, label, expectedNames) {
|
|
@@ -3674,7 +3827,7 @@ export class EvolutionLedger {
|
|
|
3674
3827
|
const anchorFingerprints = {};
|
|
3675
3828
|
const segmentFingerprints = {};
|
|
3676
3829
|
withEvolutionDirectoryFileIdentity(
|
|
3677
|
-
this.#
|
|
3830
|
+
this.#identityFs,
|
|
3678
3831
|
this.#paths.anchorDir,
|
|
3679
3832
|
(samePathHandle, _sameStableHandle, { parentPath }) => {
|
|
3680
3833
|
for (let index = 0; index < anchorFiles.length; index += 1) {
|
|
@@ -3712,7 +3865,7 @@ export class EvolutionLedger {
|
|
|
3712
3865
|
},
|
|
3713
3866
|
);
|
|
3714
3867
|
withEvolutionDirectoryFileIdentity(
|
|
3715
|
-
this.#
|
|
3868
|
+
this.#identityFs,
|
|
3716
3869
|
this.#paths.segmentDir,
|
|
3717
3870
|
(samePathHandle, _sameStableHandle, { parentPath }) => {
|
|
3718
3871
|
for (let index = 0; index < segmentFiles.length; index += 1) {
|
|
@@ -3851,27 +4004,52 @@ export class EvolutionLedger {
|
|
|
3851
4004
|
) {
|
|
3852
4005
|
return null;
|
|
3853
4006
|
}
|
|
3854
|
-
|
|
3855
|
-
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
|
|
3859
|
-
)
|
|
3860
|
-
|
|
3861
|
-
|
|
3862
|
-
|
|
3863
|
-
|
|
3864
|
-
|
|
3865
|
-
|
|
3866
|
-
|
|
3867
|
-
|
|
3868
|
-
|
|
3869
|
-
|
|
3870
|
-
|
|
3871
|
-
|
|
3872
|
-
|
|
4007
|
+
const matchesDirectory = (directoryPath, names, fingerprints) => {
|
|
4008
|
+
let matches = true;
|
|
4009
|
+
withEvolutionDirectoryFileIdentity(
|
|
4010
|
+
this.#identityFs,
|
|
4011
|
+
directoryPath,
|
|
4012
|
+
(samePathHandle, _sameStableHandle, { parentPath }) => {
|
|
4013
|
+
for (const name of names) {
|
|
4014
|
+
if (
|
|
4015
|
+
!this.#matchesCachedFileWithIdentity(
|
|
4016
|
+
path.join(directoryPath, name),
|
|
4017
|
+
fingerprints[name],
|
|
4018
|
+
samePathHandle,
|
|
4019
|
+
parentPath,
|
|
4020
|
+
)
|
|
4021
|
+
) {
|
|
4022
|
+
matches = false;
|
|
4023
|
+
break;
|
|
4024
|
+
}
|
|
4025
|
+
}
|
|
4026
|
+
},
|
|
4027
|
+
);
|
|
4028
|
+
return matches;
|
|
4029
|
+
};
|
|
4030
|
+
|
|
4031
|
+
// Keep one boundary check around the entire cached-prefix validation. The
|
|
4032
|
+
// retained directory identity authenticates each child while avoiding a
|
|
4033
|
+
// full store-boundary walk and path realpath for every immutable file.
|
|
4034
|
+
this.#assertBoundaries();
|
|
4035
|
+
let matches = false;
|
|
4036
|
+
try {
|
|
4037
|
+
matches =
|
|
4038
|
+
matchesDirectory(
|
|
4039
|
+
this.#paths.anchorDir,
|
|
4040
|
+
cached.anchorNames,
|
|
4041
|
+
cached.anchorFingerprints,
|
|
4042
|
+
) &&
|
|
4043
|
+
matchesDirectory(
|
|
4044
|
+
this.#paths.segmentDir,
|
|
4045
|
+
cached.segmentNames,
|
|
4046
|
+
cached.segmentFingerprints,
|
|
4047
|
+
);
|
|
4048
|
+
} catch {
|
|
4049
|
+
matches = false;
|
|
3873
4050
|
}
|
|
3874
|
-
|
|
4051
|
+
this.#assertBoundaries();
|
|
4052
|
+
return matches ? cached : null;
|
|
3875
4053
|
}
|
|
3876
4054
|
|
|
3877
4055
|
#rememberState(
|
|
@@ -3881,6 +4059,7 @@ export class EvolutionLedger {
|
|
|
3881
4059
|
anchorFingerprints,
|
|
3882
4060
|
segmentFingerprints,
|
|
3883
4061
|
) {
|
|
4062
|
+
this.#assertBoundaries();
|
|
3884
4063
|
const byEventDigest = new Map();
|
|
3885
4064
|
const byEventId = new Map();
|
|
3886
4065
|
for (const event of state.events) {
|
|
@@ -5265,6 +5444,25 @@ export function captureEvolutionLedgerMigrationSource(ledger) {
|
|
|
5265
5444
|
return source;
|
|
5266
5445
|
}
|
|
5267
5446
|
|
|
5447
|
+
/** Original source journal directories only; no file inventory or installer grant. */
|
|
5448
|
+
export function captureEvolutionLedgerDirectoryBoundary(ledger) {
|
|
5449
|
+
if (arguments.length !== 1)
|
|
5450
|
+
throw new TypeError(
|
|
5451
|
+
"ledger directory boundary capture takes exactly one original ledger",
|
|
5452
|
+
);
|
|
5453
|
+
if (
|
|
5454
|
+
utilTypes.isProxy(ledger) ||
|
|
5455
|
+
!DIRECTORY_BOUNDARIES.has(ledger) ||
|
|
5456
|
+
Object.getPrototypeOf(ledger) !== EvolutionLedger.prototype
|
|
5457
|
+
)
|
|
5458
|
+
throw new TypeError(
|
|
5459
|
+
"a genuine EvolutionLedger directory boundary is required",
|
|
5460
|
+
);
|
|
5461
|
+
const boundary = DIRECTORY_BOUNDARIES.get(ledger);
|
|
5462
|
+
boundary.recheck();
|
|
5463
|
+
return boundary;
|
|
5464
|
+
}
|
|
5465
|
+
|
|
5268
5466
|
function artifactRefsForAudit(input) {
|
|
5269
5467
|
const refs = Object.hasOwn(input, "subjectRef")
|
|
5270
5468
|
? [input.subjectRef, ...input.sourceRefs]
|