chainlesschain 0.166.18 → 0.166.21
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/package.json +2 -2
- package/src/assets/web-panel/assets/{AIOps-CQSBl3hY.js → AIOps-BjMnsUci.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-IQt-vRLt.js → ActionButton-DdYFP03A.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-DkHC8O7k.js → Analytics-Bb8TuuK_.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-CSti2P2T.js → AppLayout-Kn3TIOtk.js} +5 -5
- package/src/assets/web-panel/assets/{Artifacts-C57hxn3v.js → Artifacts-4IxpqI_b.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-DByLntcf.js → Audit-BOV2caqU.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-D1Bd2EOa.js → BackgroundAgents-B86k-ay4.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-VstHzHng.js → Backup-C5_ie1e3.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-CLhdexIf.js → BaseInput-xpwg6QCW.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-BefC_JZS.js → Chat-CVZOcZau.js} +6 -6
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-DbXMvgmn.js → ChatBubbleRenderer-D8fPYhUu.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-CEY982vx.js → Checkbox-MKTq3zdP.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-Dcm_DTvJ.js → Codegen-JUk3wzep.js} +1 -1
- package/src/assets/web-panel/assets/{Col-C8SSu4KA.js → Col-JPvTA0kC.js} +1 -1
- package/src/assets/web-panel/assets/{Community-BVV9vwRR.js → Community-Tqgl87NQ.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-B7c5Vo18.js → Compact-BuAFYBmN.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-Tq9jSZ5a.js → Compliance-CXS9WIPZ.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-BJUjT09Q.js → Cowork-Ds2bFHrr.js} +2 -2
- package/src/assets/web-panel/assets/{Cron-CjV9PEaS.js → Cron-C_dV_OeW.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-Dl7ReL8F.js → Crosschain-BsO6tIRo.js} +1 -1
- package/src/assets/web-panel/assets/{DID-CG8PjWmM.js → DID-rNOFqBac.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-CDBM6wmF.js → Dashboard-C2fcKKVY.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-8_55bXTD.js → Dropdown-Y7Ndr0X_.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-D3qOU1Cm.js → EmailListRenderer-DcP_uPmL.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-Bv5oWMZF.js → FamilyGuardDashboard-Cwn3gDzk.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-hpZ1xkKd.js → Federation-ClQuOsqb.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BN7GT191.js → FormItemContext-DfwtmlcK.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-PCRhfA7M.js → GenericCardRenderer-zNZhtbC5.js} +1 -1
- package/src/assets/web-panel/assets/{Git-CIlP2vy7.js → Git-BZmdW-G5.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-81lgm7YL.js → Governance-CqBk4lEn.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-Cs24xMOV.js → Inference-Dbmt3ZXk.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-CrJLVCZo.js → KnowledgeGraph-hMTH6qf3.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-DLyO8RKT.js → Logs-CkiYgiW2.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-DBOqijrP.js → MarkdownRenderer-BL1FElXg.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-Dz7AbQZc.js → Marketplace-CbB6wfin.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-BM0aFiWv.js → McpTools-BSwiFtte.js} +3 -3
- package/src/assets/web-panel/assets/{Memory-C9SJzENX.js → Memory-B03PeHrz.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-BO2HnNRk.js → MobileBridge-CRGrnd-X.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-CK_jMz0l.js → MobileProjects-CE_5v2kO.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-CfhrJsdO.js → Mtc-Dp11QVx1.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-BE2ELxgI.js → MtcAudit-CYu-3JBu.js} +5 -5
- package/src/assets/web-panel/assets/{Multisig-CgR_E3Jd.js → Multisig-CGyJ0Xl3.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Cw9445_K.js → NLProgramming-CoWi_ov1.js} +1 -1
- package/src/assets/web-panel/assets/Notes-CgU9DDo9.js +7 -0
- package/src/assets/web-panel/assets/{NotificationSettings-DxHVh4uT.js → NotificationSettings-DX4I2b4W.js} +1 -1
- package/src/assets/web-panel/assets/OrderTableRenderer-CwHc15Zs.js +1 -0
- package/src/assets/web-panel/assets/{Organization-pPXJtWo4.js → Organization-oopLO_tk.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-YamdTJwA.js → Overflow-BFVvjMyf.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-BvqP53x7.js → P2P-CBy5t-Tk.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-Blz1Xn2n.js → PdhVaultBrowser--YWJNsu6.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-CgFRQqFJ.js → Permissions-C2dZ8scb.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-CFeo3-6O.js → PersonalDataHub-Bs3l7Hlx.js} +4 -4
- package/src/assets/web-panel/assets/{Pipeline-BkaRojEK.js → Pipeline-DEBWbZuD.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-BNZEx2Qp.js → Privacy-CjHBb6NU.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-HUBOgEEK.js → ProjectInit-D2WipUz5.js} +2 -2
- package/src/assets/web-panel/assets/ProjectSettings-B6WfnrGB.js +2 -0
- package/src/assets/web-panel/assets/{Projects-Dc517BXp.js → Projects-FF_thRi6.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-DTLf8bgj.js → Providers-CCv5M_Dv.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-BBNjPszk.js → QrScannerModal-CGthxU54.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-DCsHvk3G.js → QuickAsk-CnPmXPFb.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-Dpr5YSop.js → Recommend-Df_tGEa9.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-DmKnvqGg.js → RemoteSession-Z58N0Loe.js} +2 -2
- package/src/assets/web-panel/assets/{Reputation-CSRfuurX.js → Reputation-CZTBQ2Vt.js} +1 -1
- package/src/assets/web-panel/assets/{Row-CDj3pQ_w.js → Row-CdK3YpCQ.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-DI0a_kNq.js → RssFeed-CFMVT5sb.js} +3 -3
- package/src/assets/web-panel/assets/{Search-jAy7B2nU.js → Search-DrF8RIvD.js} +1 -1
- package/src/assets/web-panel/assets/{Security-CkHTqYhc.js → Security-Ddk2f2DM.js} +4 -4
- package/src/assets/web-panel/assets/{Services-CP-0Nzga.js → Services-CCuK-VOo.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-BkF5-p3n.js → Skeleton-DxCpHzeM.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-BDu-utDE.js → Skills-t2oTDgcv.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CQvq_2Qb.js → Sla-Df63M9_R.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-T7Ap512g.js → SpeechSettings-l7ox6KuX.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-Bt-PLvot.js → SyncSettings-COd_tgPp.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-yLEcCogg.js → Tasks-CzDCcrik.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-ByN3fmbr.js → Templates-D-vfxTGA.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-DBI7hITH.js → Tenant-BlpT_Gxc.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-B77_DlGX.js → Terminal-D5rq52Hg.js} +2 -2
- package/src/assets/web-panel/assets/TimelineRenderer-DdoMndkK.js +1 -0
- package/src/assets/web-panel/assets/{Tokens-BV2N6cR8.js → Tokens-BhgBsI6g.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-CjXWGH3p.js → Trigger-DwwzoikZ.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-uqXIq3WC.js → Trust-HkTpzXFb.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-D-6qu4vY.js → UkeySign-D9nk6Ehe.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-CxGmx_2e.js → VideoEditing-BKk0rfoy.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-DSq7WlAg.js → Wallet-IVWIU_Lq.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-CDBiD57O.js → WebAuthn-CFTCpfsG.js} +4 -4
- package/src/assets/web-panel/assets/{WorkflowEditor-Z8f63TSb.js → WorkflowEditor-Dnf4Obif.js} +1 -1
- package/src/assets/web-panel/assets/{chat-B-UZFBjy.js → chat-D2snERdj.js} +1 -1
- package/src/assets/web-panel/assets/{colors-CFk7K-7Q.js → colors-DKEjK1N1.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-X0IVMvgi.js → compact-item-BEDLwQdt.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-DWg4j2n6.js → createContext-DA9iW1a0.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DyhgpXPf.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CkVCwNNF.js → hasIn-5l2xEcY9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BipwEbTA.js → index-5pW4t1iD.js} +1 -1
- package/src/assets/web-panel/assets/{index-5OcZHdzc.js → index-7P1b8HZo.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cvwu_rA_.js → index-B00GeekA.js} +1 -1
- package/src/assets/web-panel/assets/{index-CbIAWbma.js → index-B4QYBliR.js} +1 -1
- package/src/assets/web-panel/assets/{index-CNKFsBlz.js → index-B7_W_rYd.js} +1 -1
- package/src/assets/web-panel/assets/{index-BoskgPt9.js → index-BO9dQjpt.js} +1 -1
- package/src/assets/web-panel/assets/{index-CvOa-IFV.js → index-BZ5lY2zx.js} +1 -1
- package/src/assets/web-panel/assets/{index-_-9k3Tna.js → index-B_eCoZgW.js} +1 -1
- package/src/assets/web-panel/assets/{index-CbihMThv.js → index-BgfImyZI.js} +1 -1
- package/src/assets/web-panel/assets/{index-DLSBks8h.js → index-Bq5P3piS.js} +1 -1
- package/src/assets/web-panel/assets/{index-LU54bChK.js → index-Bqc8tdIF.js} +1 -1
- package/src/assets/web-panel/assets/{index-Btqk6zWb.js → index-C7sKbM5s.js} +1 -1
- package/src/assets/web-panel/assets/{index-D8uEL8Uw.js → index-CGlotREA.js} +1 -1
- package/src/assets/web-panel/assets/{index-CLlP1E4X.js → index-CRPrHE7h.js} +1 -1
- package/src/assets/web-panel/assets/{index-DeWKPQGs.js → index-CX1x4AFn.js} +1 -1
- package/src/assets/web-panel/assets/{index-Db_5xhrU.js → index-CbTPvp2_.js} +1 -1
- package/src/assets/web-panel/assets/{index-CDfSD07S.js → index-CjWltbi_.js} +1 -1
- package/src/assets/web-panel/assets/index-Ck1jhFE0.js +1 -0
- package/src/assets/web-panel/assets/{index-BPBia4b-.js → index-CkMKuqUs.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1ZAOJRW.js → index-CmruhzFi.js} +1 -1
- package/src/assets/web-panel/assets/{index-P83LDnCz.js → index-Csfb-7hw.js} +1 -1
- package/src/assets/web-panel/assets/{index-DcDggpWR.js → index-CxEtQYdi.js} +1 -1
- package/src/assets/web-panel/assets/{index-OjnAwUJo.js → index-D7z4zB_a.js} +1 -1
- package/src/assets/web-panel/assets/{index-D-EJNxgR.js → index-DLoalqRX.js} +1 -1
- package/src/assets/web-panel/assets/{index-B3-B3FWD.js → index-DU2TXJu-.js} +1 -1
- package/src/assets/web-panel/assets/{index-D92g4l3n.js → index-DXet1xol.js} +1 -1
- package/src/assets/web-panel/assets/{index-Ca0mij1n.js → index-Dbd7eEex.js} +1 -1
- package/src/assets/web-panel/assets/{index-DCqpl_E_.js → index-Dbkghqn_.js} +1 -1
- package/src/assets/web-panel/assets/{index-DyWlOuak.js → index-Dg2a9owQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-CUBkvFPH.js → index-DpqtbXdE.js} +1 -1
- package/src/assets/web-panel/assets/{index-DhVHoqGL.js → index-DqwPFbOb.js} +3 -3
- package/src/assets/web-panel/assets/{index-BG-GaZri.js → index-DvV03Ayk.js} +1 -1
- package/src/assets/web-panel/assets/{index-CMCEXBRh.js → index-NJieh3T1.js} +1 -1
- package/src/assets/web-panel/assets/{index-B273IY-E.js → index-Td8ZwyoC.js} +1 -1
- package/src/assets/web-panel/assets/index-c8jJEHVs.js +1 -0
- package/src/assets/web-panel/assets/{index-BzXjYUmd.js → index-e-e40id6.js} +1 -1
- package/src/assets/web-panel/assets/{index-CCLwoATF.js → index-fccZmC_h.js} +1 -1
- package/src/assets/web-panel/assets/{index-CN8HZlLH.js → index-fgYk0QXO.js} +1 -1
- package/src/assets/web-panel/assets/{index-BB4KHHcv.js → index-yYEQXYaU.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-CFNgY8lJ.js → initDefaultProps-CPrcB1TC.js} +1 -1
- package/src/assets/web-panel/assets/{motion-D-5RrDzn.js → motion-BQ7mWRh_.js} +1 -1
- package/src/assets/web-panel/assets/{move-DN-L0Bd-.js → move-CzOEq99p.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-6mgLSeCK.js → mtc-parser-BlQI5jrw.js} +1 -1
- package/src/assets/web-panel/assets/{omit-ClWW8eTP.js → omit-DlW4gxmv.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-Vystq3R8.js → pickAttrs-BBYms5nP.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-D0H9hK4W.js → placementArrow-az2MPJ_7.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-B3iNRU-a.js → responsiveObserve-D1-ibEol.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CiMXe6am.js → slide-DHGqa4GA.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-CGDZJ1uT.js → statusUtils-CYGDo6lD.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-D2zA6NkY.js → styleChecker-Cl2EM9K6.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-C1sYU6QE.js → useFlexGapSupport-DdYhzuv1.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-BzUHSjg2.js → useFs-DWumPuBW.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-CxB5yZCq.js → usePersonalDataHub-DxAO-Aqi.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-BfUfMwtc.js → vnode-CplsQf_N.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CFYmj1TO.js → zoom-Cw5HXNSG.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 +19 -1
- package/src/commands/evolution-knowledge.js +54 -0
- package/src/commands/evolution-workbench.js +83 -0
- package/src/commands/evolution.js +6 -1
- package/src/commands/serve.js +16 -0
- package/src/commands/skill.js +158 -16
- package/src/data/changelog.json +26 -0
- package/src/gateways/ws/session-protocol.js +2 -0
- package/src/gateways/ws/ws-agent-handler.js +124 -69
- package/src/gateways/ws/ws-server.js +19 -0
- package/src/generated/cc-agent-protocol.schema.json +7 -1
- package/src/harness/jsonl-session-store.js +25 -1
- package/src/index.js +1 -1
- package/src/lib/app-server/cli-agent-kernel-adapter.js +8 -0
- package/src/lib/app-server/graph-runtime.js +40 -1
- package/src/lib/app-server/server.js +181 -3
- package/src/lib/app-server/websocket-transport.js +2 -0
- package/src/lib/evolution/agent-evolution-runtime-composition-brand.js +17 -0
- package/src/lib/evolution/agent-evolution-runtime-composition.js +165 -1
- package/src/lib/evolution/controlled-skill-production-pilot.js +912 -0
- package/src/lib/evolution/evolution-artifact-ports.js +21 -0
- package/src/lib/evolution/evolution-raw-crypto-shred.js +185 -0
- package/src/lib/evolution/evolution-workbench-batch-executor.js +195 -0
- package/src/lib/evolution/evolution-workbench-cli-host.js +169 -0
- package/src/lib/evolution/evolution-workbench-metrics-ledger-adapter.js +648 -0
- package/src/lib/evolution/evolution-workbench-metrics.js +943 -0
- package/src/lib/evolution/evolution-workbench-projection.js +734 -0
- package/src/lib/evolution/evolution-workbench-version-control.js +272 -0
- package/src/lib/evolution/governed-knowledge-conflict-merge.js +352 -0
- package/src/lib/evolution/governed-knowledge-dependency-authority.js +273 -0
- package/src/lib/evolution/governed-knowledge-dependency-ledger-executor.js +646 -0
- package/src/lib/evolution/governed-knowledge-merge-ledger-executor.js +644 -0
- package/src/lib/evolution/governed-knowledge-merge-publisher-authority.js +282 -0
- package/src/lib/evolution/governed-knowledge-node-crypto-authority.js +348 -0
- package/src/lib/evolution/governed-knowledge-rbac-approval-authority.js +621 -0
- package/src/lib/evolution/governed-knowledge-review-host.js +249 -0
- package/src/lib/evolution/governed-knowledge-sync-ledger-adapter.js +643 -0
- package/src/lib/evolution/governed-knowledge-sync-merge-publisher.js +259 -0
- package/src/lib/evolution/governed-knowledge-sync.js +485 -0
- package/src/lib/evolution/governed-knowledge-trust-ledger.js +707 -0
- package/src/lib/evolution/governed-skill-marketplace.js +430 -0
- package/src/lib/evolution/governed-wiki-pruning.js +435 -0
- package/src/lib/evolution/skill-outcome-index-authority.js +268 -0
- package/src/lib/evolution/skill-outcome-source-catalog-authority.js +237 -0
- package/src/lib/public-ide-capability-manifest.js +4 -0
- package/src/lib/skill-outcome-authority.js +43 -0
- package/src/lib/skill-outcome-transcript-authority.js +252 -0
- package/src/lib/skill-retrieval-router-benchmark.js +135 -0
- package/src/lib/skill-retrieval-router.js +235 -0
- package/src/lib/skill-vector-authority.js +325 -0
- package/src/lib/skill-vector-process-authority.js +386 -0
- package/src/repl/agent-repl.js +7 -1
- package/src/runtime/agent-core.js +72 -6
- package/src/runtime/agent-runtime.js +42 -0
- package/src/runtime/headless-runner.js +32 -0
- package/src/runtime/headless-stream.js +36 -1
- package/src/runtime/runtime-factory.js +62 -4
- package/src/assets/web-panel/assets/Notes-DZimkTrb.js +0 -7
- package/src/assets/web-panel/assets/OrderTableRenderer-Iy-pnX56.js +0 -1
- package/src/assets/web-panel/assets/ProjectSettings-CFMmhROS.js +0 -2
- package/src/assets/web-panel/assets/TimelineRenderer-CsLTB_QO.js +0 -1
- package/src/assets/web-panel/assets/devWarning-B2IW1J_J.js +0 -1
- package/src/assets/web-panel/assets/index-BCp2ir-1.js +0 -1
- package/src/assets/web-panel/assets/index-Bw4iuzRD.js +0 -1
|
@@ -0,0 +1,707 @@
|
|
|
1
|
+
import { createHash, createPublicKey, verify } from "node:crypto";
|
|
2
|
+
import { types as utilTypes } from "node:util";
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA,
|
|
6
|
+
isEvolutionLedgerArtifactResolver,
|
|
7
|
+
} from "./evolution-artifact-ports.js";
|
|
8
|
+
import {
|
|
9
|
+
EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA,
|
|
10
|
+
EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA,
|
|
11
|
+
} from "./evolution-ledger.js";
|
|
12
|
+
import {
|
|
13
|
+
GOVERNED_KNOWLEDGE_APPROVAL_RECEIPT_SCHEMA,
|
|
14
|
+
createGovernedKnowledgeReviewerRegistry,
|
|
15
|
+
digestGovernedKnowledgeApprovalReceipt,
|
|
16
|
+
governedKnowledgeApprovalSignatureMessage,
|
|
17
|
+
} from "./governed-knowledge-rbac-approval-authority.js";
|
|
18
|
+
|
|
19
|
+
export const GOVERNED_KNOWLEDGE_TRUST_LEDGER_RECORD_SCHEMA =
|
|
20
|
+
"chainlesschain.governed-knowledge-trust-ledger-record/v1";
|
|
21
|
+
export const GOVERNED_KNOWLEDGE_REVIEWER_REGISTERED_EVENT_TYPE =
|
|
22
|
+
"knowledge.trust.reviewer.registered";
|
|
23
|
+
export const GOVERNED_KNOWLEDGE_REVIEWER_REVOKED_EVENT_TYPE =
|
|
24
|
+
"knowledge.trust.reviewer.revoked";
|
|
25
|
+
export const GOVERNED_KNOWLEDGE_APPROVAL_COMMITTED_EVENT_TYPE =
|
|
26
|
+
"knowledge.trust.approval.committed";
|
|
27
|
+
export const GOVERNED_KNOWLEDGE_TRUST_LEDGER_CORRUPT_CODE =
|
|
28
|
+
"CC_GOVERNED_KNOWLEDGE_TRUST_LEDGER_CORRUPT";
|
|
29
|
+
|
|
30
|
+
const ARTIFACT_TYPE = "governed-knowledge-trust-record";
|
|
31
|
+
const DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
32
|
+
const ID = /^[A-Za-z0-9][A-Za-z0-9._:@/-]{0,255}$/u;
|
|
33
|
+
const RECORD_KEYS = new Set([
|
|
34
|
+
"schema",
|
|
35
|
+
"tenantId",
|
|
36
|
+
"kind",
|
|
37
|
+
"reviewerId",
|
|
38
|
+
"keyId",
|
|
39
|
+
"publicKeySpki",
|
|
40
|
+
"status",
|
|
41
|
+
"reason",
|
|
42
|
+
"receipt",
|
|
43
|
+
"receiptDigest",
|
|
44
|
+
"effectiveAt",
|
|
45
|
+
"committedAt",
|
|
46
|
+
"recordDigest",
|
|
47
|
+
]);
|
|
48
|
+
const RECEIPT_KEYS = new Set([
|
|
49
|
+
"schema",
|
|
50
|
+
"tenantId",
|
|
51
|
+
"reviewerId",
|
|
52
|
+
"automated",
|
|
53
|
+
"knowledgeId",
|
|
54
|
+
"scope",
|
|
55
|
+
"scopeId",
|
|
56
|
+
"action",
|
|
57
|
+
"contentDigest",
|
|
58
|
+
"approvedAt",
|
|
59
|
+
"expiresAt",
|
|
60
|
+
"receiptDigest",
|
|
61
|
+
"attestation",
|
|
62
|
+
]);
|
|
63
|
+
const ATTESTATION_KEYS = new Set(["algorithm", "keyId", "value"]);
|
|
64
|
+
|
|
65
|
+
function corrupt(message) {
|
|
66
|
+
const error = new Error(message);
|
|
67
|
+
error.code = GOVERNED_KNOWLEDGE_TRUST_LEDGER_CORRUPT_CODE;
|
|
68
|
+
throw error;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function exact(value, keys, label) {
|
|
72
|
+
if (
|
|
73
|
+
!value ||
|
|
74
|
+
typeof value !== "object" ||
|
|
75
|
+
Array.isArray(value) ||
|
|
76
|
+
utilTypes.isProxy(value) ||
|
|
77
|
+
Reflect.ownKeys(value).length !== keys.size ||
|
|
78
|
+
Reflect.ownKeys(value).some(
|
|
79
|
+
(key) => typeof key !== "string" || !keys.has(key),
|
|
80
|
+
)
|
|
81
|
+
) {
|
|
82
|
+
corrupt(`${label} is invalid`);
|
|
83
|
+
}
|
|
84
|
+
return value;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function identifier(value, label) {
|
|
88
|
+
if (typeof value !== "string" || !ID.test(value)) {
|
|
89
|
+
throw new TypeError(`${label} is invalid`);
|
|
90
|
+
}
|
|
91
|
+
return value;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
function canonical(value) {
|
|
95
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
96
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
97
|
+
return `{${Object.keys(value)
|
|
98
|
+
.sort()
|
|
99
|
+
.map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
|
|
100
|
+
.join(",")}}`;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function hash(domain, value) {
|
|
104
|
+
return `sha256:${createHash("sha256")
|
|
105
|
+
.update(domain)
|
|
106
|
+
.update("\0")
|
|
107
|
+
.update(canonical(value))
|
|
108
|
+
.digest("hex")}`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function capture(owner, method, label) {
|
|
112
|
+
if (
|
|
113
|
+
!owner ||
|
|
114
|
+
typeof owner !== "object" ||
|
|
115
|
+
utilTypes.isProxy(owner) ||
|
|
116
|
+
typeof owner[method] !== "function"
|
|
117
|
+
) {
|
|
118
|
+
throw new TypeError(`${label}.${method}() is required`);
|
|
119
|
+
}
|
|
120
|
+
return (...args) => Reflect.apply(owner[method], owner, args);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function clone(value) {
|
|
124
|
+
return structuredClone(value);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function freeze(value) {
|
|
128
|
+
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
129
|
+
Object.freeze(value);
|
|
130
|
+
for (const child of Object.values(value)) freeze(child);
|
|
131
|
+
}
|
|
132
|
+
return value;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function normalizeDescriptor(input) {
|
|
136
|
+
return Object.freeze({
|
|
137
|
+
tenantId: identifier(input?.tenantId, "tenantId"),
|
|
138
|
+
artifactTenantId: identifier(input?.artifactTenantId, "artifactTenantId"),
|
|
139
|
+
streamId: identifier(input?.streamId, "streamId"),
|
|
140
|
+
audience: identifier(input?.audience, "audience"),
|
|
141
|
+
purpose: identifier(input?.purpose, "purpose"),
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function recordCore(value) {
|
|
146
|
+
const core = clone(value);
|
|
147
|
+
delete core.recordDigest;
|
|
148
|
+
return core;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
export function digestGovernedKnowledgeTrustLedgerRecord(value) {
|
|
152
|
+
return hash(GOVERNED_KNOWLEDGE_TRUST_LEDGER_RECORD_SCHEMA, recordCore(value));
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function decodePublicKey(publicKeySpki, keyId) {
|
|
156
|
+
if (typeof publicKeySpki !== "string")
|
|
157
|
+
corrupt("reviewer public key is invalid");
|
|
158
|
+
const bytes = Buffer.from(publicKeySpki, "base64url");
|
|
159
|
+
if (bytes.toString("base64url") !== publicKeySpki) {
|
|
160
|
+
corrupt("reviewer public key is not canonical base64url");
|
|
161
|
+
}
|
|
162
|
+
let publicKey;
|
|
163
|
+
try {
|
|
164
|
+
publicKey = createPublicKey({ key: bytes, format: "der", type: "spki" });
|
|
165
|
+
} catch {
|
|
166
|
+
corrupt("reviewer public key SPKI is invalid");
|
|
167
|
+
}
|
|
168
|
+
if (publicKey.asymmetricKeyType !== "ed25519") {
|
|
169
|
+
corrupt("reviewer public key is not Ed25519");
|
|
170
|
+
}
|
|
171
|
+
const expectedKeyId = `key:ed25519:${createHash("sha256")
|
|
172
|
+
.update(bytes)
|
|
173
|
+
.digest("hex")}`;
|
|
174
|
+
if (keyId !== expectedKeyId) corrupt("reviewer keyId does not bind its SPKI");
|
|
175
|
+
return publicKey;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function validateRecord(value, descriptor) {
|
|
179
|
+
exact(value, RECORD_KEYS, "knowledge trust ledger record");
|
|
180
|
+
if (
|
|
181
|
+
value.schema !== GOVERNED_KNOWLEDGE_TRUST_LEDGER_RECORD_SCHEMA ||
|
|
182
|
+
value.tenantId !== descriptor.tenantId ||
|
|
183
|
+
!["reviewer", "approval"].includes(value.kind) ||
|
|
184
|
+
value.recordDigest !== digestGovernedKnowledgeTrustLedgerRecord(value)
|
|
185
|
+
) {
|
|
186
|
+
corrupt("knowledge trust record binding is invalid");
|
|
187
|
+
}
|
|
188
|
+
if (value.kind === "reviewer") {
|
|
189
|
+
if (
|
|
190
|
+
!ID.test(value.reviewerId ?? "") ||
|
|
191
|
+
!ID.test(value.keyId ?? "") ||
|
|
192
|
+
!["active", "revoked"].includes(value.status) ||
|
|
193
|
+
value.receipt !== null ||
|
|
194
|
+
value.receiptDigest !== null ||
|
|
195
|
+
value.committedAt !== null ||
|
|
196
|
+
!Number.isFinite(Date.parse(value.effectiveAt)) ||
|
|
197
|
+
new Date(Date.parse(value.effectiveAt)).toISOString() !==
|
|
198
|
+
value.effectiveAt ||
|
|
199
|
+
(value.status === "active"
|
|
200
|
+
? value.reason !== null
|
|
201
|
+
: typeof value.reason !== "string" ||
|
|
202
|
+
value.reason.trim() !== value.reason ||
|
|
203
|
+
value.reason.length < 1 ||
|
|
204
|
+
value.reason.length > 2048)
|
|
205
|
+
) {
|
|
206
|
+
corrupt("reviewer trust record is invalid");
|
|
207
|
+
}
|
|
208
|
+
decodePublicKey(value.publicKeySpki, value.keyId);
|
|
209
|
+
} else {
|
|
210
|
+
if (
|
|
211
|
+
value.reviewerId !== null ||
|
|
212
|
+
value.keyId !== null ||
|
|
213
|
+
value.publicKeySpki !== null ||
|
|
214
|
+
value.status !== null ||
|
|
215
|
+
value.reason !== null ||
|
|
216
|
+
value.effectiveAt !== null ||
|
|
217
|
+
!DIGEST.test(value.receiptDigest ?? "") ||
|
|
218
|
+
!Number.isFinite(Date.parse(value.committedAt)) ||
|
|
219
|
+
new Date(Date.parse(value.committedAt)).toISOString() !==
|
|
220
|
+
value.committedAt
|
|
221
|
+
) {
|
|
222
|
+
corrupt("approval trust record is invalid");
|
|
223
|
+
}
|
|
224
|
+
exact(value.receipt, RECEIPT_KEYS, "stored knowledge approval receipt");
|
|
225
|
+
exact(
|
|
226
|
+
value.receipt.attestation,
|
|
227
|
+
ATTESTATION_KEYS,
|
|
228
|
+
"stored knowledge approval attestation",
|
|
229
|
+
);
|
|
230
|
+
if (
|
|
231
|
+
value.receiptDigest !== value.receipt.receiptDigest ||
|
|
232
|
+
value.receiptDigest !==
|
|
233
|
+
digestGovernedKnowledgeApprovalReceipt(value.receipt)
|
|
234
|
+
) {
|
|
235
|
+
corrupt("approval trust record digest is invalid");
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
return freeze(clone(value));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function parseArtifact(resolution, descriptor) {
|
|
242
|
+
if (
|
|
243
|
+
resolution?.schema !== EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA ||
|
|
244
|
+
resolution.authenticated !== true ||
|
|
245
|
+
resolution.found !== true ||
|
|
246
|
+
!DIGEST.test(resolution.digest ?? "") ||
|
|
247
|
+
!DIGEST.test(resolution.receiptDigest ?? "") ||
|
|
248
|
+
!Buffer.isBuffer(resolution.bytes)
|
|
249
|
+
) {
|
|
250
|
+
corrupt("knowledge trust artifact resolution is incomplete");
|
|
251
|
+
}
|
|
252
|
+
let artifact;
|
|
253
|
+
try {
|
|
254
|
+
artifact = JSON.parse(resolution.bytes.toString("utf8"));
|
|
255
|
+
} catch {
|
|
256
|
+
corrupt("knowledge trust artifact is not JSON");
|
|
257
|
+
}
|
|
258
|
+
if (
|
|
259
|
+
artifact?.schema !== EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA ||
|
|
260
|
+
artifact.tenantId !== descriptor.artifactTenantId ||
|
|
261
|
+
artifact.audience !== descriptor.audience ||
|
|
262
|
+
artifact.purpose !== descriptor.purpose ||
|
|
263
|
+
artifact.retention !== "ledger" ||
|
|
264
|
+
artifact.type !== ARTIFACT_TYPE
|
|
265
|
+
) {
|
|
266
|
+
corrupt("knowledge trust durable artifact binding is invalid");
|
|
267
|
+
}
|
|
268
|
+
return validateRecord(artifact.value, descriptor);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export class GovernedKnowledgeTrustLedger {
|
|
272
|
+
constructor({
|
|
273
|
+
descriptor: input,
|
|
274
|
+
artifactPorts,
|
|
275
|
+
ledger,
|
|
276
|
+
ledgerArtifactResolver,
|
|
277
|
+
now = Date.now,
|
|
278
|
+
} = {}) {
|
|
279
|
+
this.descriptor = normalizeDescriptor(input);
|
|
280
|
+
this._put = capture(artifactPorts, "putCanonical", "artifactPorts");
|
|
281
|
+
this._readLedger = capture(ledger, "read", "ledger");
|
|
282
|
+
this._verifyLedger = capture(ledger, "verify", "ledger");
|
|
283
|
+
this._append = capture(ledger, "appendDomainEvent", "ledger");
|
|
284
|
+
if (!isEvolutionLedgerArtifactResolver(ledgerArtifactResolver)) {
|
|
285
|
+
throw new TypeError(
|
|
286
|
+
"a branded EvolutionArtifactPorts ledger resolver is required",
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
if (typeof now !== "function" || utilTypes.isProxy(now)) {
|
|
290
|
+
throw new TypeError("now must be a non-proxy function");
|
|
291
|
+
}
|
|
292
|
+
this._resolveArtifact = ledgerArtifactResolver;
|
|
293
|
+
this._now = now;
|
|
294
|
+
Object.freeze(this);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
_events(types = null) {
|
|
298
|
+
const events = this._readLedger();
|
|
299
|
+
if (!Array.isArray(events)) corrupt("EvolutionLedger returned no events");
|
|
300
|
+
return events.filter(
|
|
301
|
+
(event) =>
|
|
302
|
+
event.schema === EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA &&
|
|
303
|
+
event.tenantId === this.descriptor.tenantId &&
|
|
304
|
+
event.correlationId === this.descriptor.streamId &&
|
|
305
|
+
(types === null || types.has(event.type)),
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async _resolveEvent(event) {
|
|
310
|
+
const identity = this._verifyLedger();
|
|
311
|
+
const resolution = await this._resolveArtifact({
|
|
312
|
+
epoch: identity.epoch,
|
|
313
|
+
ledgerId: identity.ledgerId,
|
|
314
|
+
ref: event.subjectRef,
|
|
315
|
+
tenantId: this.descriptor.artifactTenantId,
|
|
316
|
+
});
|
|
317
|
+
if (
|
|
318
|
+
resolution?.ref !== event.subjectRef.ref ||
|
|
319
|
+
resolution?.digest !== event.subjectRef.digest
|
|
320
|
+
) {
|
|
321
|
+
corrupt("knowledge trust ledger resolved a substituted artifact");
|
|
322
|
+
}
|
|
323
|
+
const record = parseArtifact(resolution, this.descriptor);
|
|
324
|
+
const expectedType =
|
|
325
|
+
record.kind === "approval"
|
|
326
|
+
? GOVERNED_KNOWLEDGE_APPROVAL_COMMITTED_EVENT_TYPE
|
|
327
|
+
: record.status === "active"
|
|
328
|
+
? GOVERNED_KNOWLEDGE_REVIEWER_REGISTERED_EVENT_TYPE
|
|
329
|
+
: GOVERNED_KNOWLEDGE_REVIEWER_REVOKED_EVENT_TYPE;
|
|
330
|
+
const expectedEventId =
|
|
331
|
+
record.kind === "approval"
|
|
332
|
+
? `${expectedType}.${record.receiptDigest.slice(7)}`
|
|
333
|
+
: `${expectedType}.${record.keyId.slice("key:ed25519:".length)}`;
|
|
334
|
+
const expectedTimestamp =
|
|
335
|
+
record.kind === "approval" ? record.committedAt : record.effectiveAt;
|
|
336
|
+
if (
|
|
337
|
+
event.type !== expectedType ||
|
|
338
|
+
event.eventId !== expectedEventId ||
|
|
339
|
+
event.timestamp !== expectedTimestamp ||
|
|
340
|
+
event.decision !== "committed"
|
|
341
|
+
) {
|
|
342
|
+
corrupt("knowledge trust event binding is invalid");
|
|
343
|
+
}
|
|
344
|
+
return record;
|
|
345
|
+
}
|
|
346
|
+
|
|
347
|
+
async _entry(eventId) {
|
|
348
|
+
const matches = this._events().filter((event) => event.eventId === eventId);
|
|
349
|
+
if (matches.length > 1) corrupt("knowledge trust event is ambiguous");
|
|
350
|
+
return matches.length === 0
|
|
351
|
+
? null
|
|
352
|
+
: { event: matches[0], record: await this._resolveEvent(matches[0]) };
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
async _appendRecord(record, { eventId, type, reason, timestamp }) {
|
|
356
|
+
const existing = await this._entry(eventId);
|
|
357
|
+
if (existing) {
|
|
358
|
+
if (canonical(existing.record) !== canonical(record)) {
|
|
359
|
+
corrupt("knowledge trust event identity resolved different content");
|
|
360
|
+
}
|
|
361
|
+
return Object.freeze({
|
|
362
|
+
authenticated: true,
|
|
363
|
+
durable: true,
|
|
364
|
+
recovered: true,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
const head = this._verifyLedger();
|
|
368
|
+
const published = this._put(ARTIFACT_TYPE, record, {
|
|
369
|
+
audience: this.descriptor.audience,
|
|
370
|
+
purpose: this.descriptor.purpose,
|
|
371
|
+
retention: "ledger",
|
|
372
|
+
});
|
|
373
|
+
if (
|
|
374
|
+
!published?.ref ||
|
|
375
|
+
published.receipt?.persisted !== true ||
|
|
376
|
+
published.receipt?.readbackVerified !== true ||
|
|
377
|
+
published.receipt?.integrityVerified !== true ||
|
|
378
|
+
published.receipt?.retention !== "ledger"
|
|
379
|
+
) {
|
|
380
|
+
corrupt("knowledge trust artifact was not durably read back");
|
|
381
|
+
}
|
|
382
|
+
let receipt;
|
|
383
|
+
try {
|
|
384
|
+
receipt = this._append(
|
|
385
|
+
{
|
|
386
|
+
artifactTenantId: this.descriptor.artifactTenantId,
|
|
387
|
+
correlationId: this.descriptor.streamId,
|
|
388
|
+
decision: "committed",
|
|
389
|
+
eventId,
|
|
390
|
+
reason,
|
|
391
|
+
skillName: null,
|
|
392
|
+
sourceRefs: [],
|
|
393
|
+
subjectRef: published.ref,
|
|
394
|
+
tenantId: this.descriptor.tenantId,
|
|
395
|
+
timestamp,
|
|
396
|
+
type,
|
|
397
|
+
},
|
|
398
|
+
{
|
|
399
|
+
expectedHeadDigest: head.headDigest,
|
|
400
|
+
expectedSequence: head.sequence,
|
|
401
|
+
},
|
|
402
|
+
);
|
|
403
|
+
} catch (error) {
|
|
404
|
+
const recovered = await this._entry(eventId);
|
|
405
|
+
if (!recovered || canonical(recovered.record) !== canonical(record)) {
|
|
406
|
+
throw error;
|
|
407
|
+
}
|
|
408
|
+
receipt = { authenticated: true, durable: true, recovered: true };
|
|
409
|
+
}
|
|
410
|
+
if (receipt?.authenticated !== true || receipt.durable !== true) {
|
|
411
|
+
corrupt("knowledge trust ledger append was not durably confirmed");
|
|
412
|
+
}
|
|
413
|
+
const stored = await this._entry(eventId);
|
|
414
|
+
if (!stored || canonical(stored.record) !== canonical(record)) {
|
|
415
|
+
corrupt("knowledge trust ledger readback differs after commit");
|
|
416
|
+
}
|
|
417
|
+
return Object.freeze({
|
|
418
|
+
authenticated: true,
|
|
419
|
+
durable: true,
|
|
420
|
+
recovered: receipt.recovered === true,
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
async _reviewerStates() {
|
|
425
|
+
const types = new Set([
|
|
426
|
+
GOVERNED_KNOWLEDGE_REVIEWER_REGISTERED_EVENT_TYPE,
|
|
427
|
+
GOVERNED_KNOWLEDGE_REVIEWER_REVOKED_EVENT_TYPE,
|
|
428
|
+
]);
|
|
429
|
+
const states = new Map();
|
|
430
|
+
for (const event of this._events(types)) {
|
|
431
|
+
const record = await this._resolveEvent(event);
|
|
432
|
+
const current = states.get(record.reviewerId) ?? null;
|
|
433
|
+
if (record.status === "active") {
|
|
434
|
+
if (current?.status === "active") {
|
|
435
|
+
corrupt("reviewer has multiple active keys");
|
|
436
|
+
}
|
|
437
|
+
} else if (
|
|
438
|
+
current?.status !== "active" ||
|
|
439
|
+
current.keyId !== record.keyId ||
|
|
440
|
+
current.publicKeySpki !== record.publicKeySpki
|
|
441
|
+
) {
|
|
442
|
+
corrupt("reviewer revocation has no matching active key");
|
|
443
|
+
}
|
|
444
|
+
states.set(record.reviewerId, record);
|
|
445
|
+
}
|
|
446
|
+
return states;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
async registerReviewer({ reviewerId, publicKey } = {}) {
|
|
450
|
+
const normalizedReviewerId = identifier(reviewerId, "reviewerId");
|
|
451
|
+
let key;
|
|
452
|
+
try {
|
|
453
|
+
key = createPublicKey(publicKey);
|
|
454
|
+
} catch {
|
|
455
|
+
key = publicKey;
|
|
456
|
+
}
|
|
457
|
+
if (!key || key.asymmetricKeyType !== "ed25519") {
|
|
458
|
+
throw new TypeError("reviewer publicKey must be Ed25519");
|
|
459
|
+
}
|
|
460
|
+
const spki = key.export({ format: "der", type: "spki" });
|
|
461
|
+
const publicKeySpki = spki.toString("base64url");
|
|
462
|
+
const keyId = `key:ed25519:${createHash("sha256").update(spki).digest("hex")}`;
|
|
463
|
+
const states = await this._reviewerStates();
|
|
464
|
+
const current = states.get(normalizedReviewerId);
|
|
465
|
+
if (current?.status === "active" && current.keyId !== keyId) {
|
|
466
|
+
throw new Error("active reviewer key must be revoked before rotation");
|
|
467
|
+
}
|
|
468
|
+
if (current?.status === "revoked" && current.keyId === keyId) {
|
|
469
|
+
throw new Error("a revoked reviewer key cannot be reactivated");
|
|
470
|
+
}
|
|
471
|
+
const eventId = `${GOVERNED_KNOWLEDGE_REVIEWER_REGISTERED_EVENT_TYPE}.${keyId.slice("key:ed25519:".length)}`;
|
|
472
|
+
const existing = await this._entry(eventId);
|
|
473
|
+
if (existing) {
|
|
474
|
+
if (
|
|
475
|
+
existing.record.reviewerId !== normalizedReviewerId ||
|
|
476
|
+
existing.record.keyId !== keyId ||
|
|
477
|
+
existing.record.status !== "active"
|
|
478
|
+
) {
|
|
479
|
+
corrupt("reviewer registration event identity was reused");
|
|
480
|
+
}
|
|
481
|
+
return Object.freeze({
|
|
482
|
+
authenticated: true,
|
|
483
|
+
durable: true,
|
|
484
|
+
recovered: true,
|
|
485
|
+
reviewerId: normalizedReviewerId,
|
|
486
|
+
keyId,
|
|
487
|
+
});
|
|
488
|
+
}
|
|
489
|
+
const milliseconds = Number(this._now());
|
|
490
|
+
if (!Number.isFinite(milliseconds))
|
|
491
|
+
throw new TypeError("trust ledger clock is invalid");
|
|
492
|
+
const effectiveAt = new Date(milliseconds).toISOString();
|
|
493
|
+
const core = {
|
|
494
|
+
schema: GOVERNED_KNOWLEDGE_TRUST_LEDGER_RECORD_SCHEMA,
|
|
495
|
+
tenantId: this.descriptor.tenantId,
|
|
496
|
+
kind: "reviewer",
|
|
497
|
+
reviewerId: normalizedReviewerId,
|
|
498
|
+
keyId,
|
|
499
|
+
publicKeySpki,
|
|
500
|
+
status: "active",
|
|
501
|
+
reason: null,
|
|
502
|
+
receipt: null,
|
|
503
|
+
receiptDigest: null,
|
|
504
|
+
effectiveAt,
|
|
505
|
+
committedAt: null,
|
|
506
|
+
};
|
|
507
|
+
const record = freeze({
|
|
508
|
+
...core,
|
|
509
|
+
recordDigest: digestGovernedKnowledgeTrustLedgerRecord(core),
|
|
510
|
+
});
|
|
511
|
+
validateRecord(record, this.descriptor);
|
|
512
|
+
const result = await this._appendRecord(record, {
|
|
513
|
+
eventId,
|
|
514
|
+
type: GOVERNED_KNOWLEDGE_REVIEWER_REGISTERED_EVENT_TYPE,
|
|
515
|
+
reason: "governed knowledge reviewer registered",
|
|
516
|
+
timestamp: effectiveAt,
|
|
517
|
+
});
|
|
518
|
+
return Object.freeze({
|
|
519
|
+
...result,
|
|
520
|
+
reviewerId: normalizedReviewerId,
|
|
521
|
+
keyId,
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
|
|
525
|
+
async revokeReviewer({ reviewerId, keyId, reason } = {}) {
|
|
526
|
+
const normalizedReviewerId = identifier(reviewerId, "reviewerId");
|
|
527
|
+
const normalizedKeyId = identifier(keyId, "keyId");
|
|
528
|
+
if (
|
|
529
|
+
typeof reason !== "string" ||
|
|
530
|
+
reason.trim() !== reason ||
|
|
531
|
+
!reason ||
|
|
532
|
+
reason.length > 2048
|
|
533
|
+
) {
|
|
534
|
+
throw new TypeError("reviewer revocation reason is invalid");
|
|
535
|
+
}
|
|
536
|
+
const eventId = `${GOVERNED_KNOWLEDGE_REVIEWER_REVOKED_EVENT_TYPE}.${normalizedKeyId.slice("key:ed25519:".length)}`;
|
|
537
|
+
const existing = await this._entry(eventId);
|
|
538
|
+
if (existing) {
|
|
539
|
+
if (
|
|
540
|
+
existing.record.reviewerId !== normalizedReviewerId ||
|
|
541
|
+
existing.record.keyId !== normalizedKeyId ||
|
|
542
|
+
existing.record.status !== "revoked" ||
|
|
543
|
+
existing.record.reason !== reason
|
|
544
|
+
) {
|
|
545
|
+
corrupt("reviewer revocation event identity was reused");
|
|
546
|
+
}
|
|
547
|
+
return Object.freeze({
|
|
548
|
+
authenticated: true,
|
|
549
|
+
durable: true,
|
|
550
|
+
recovered: true,
|
|
551
|
+
reviewerId: normalizedReviewerId,
|
|
552
|
+
keyId: normalizedKeyId,
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
const states = await this._reviewerStates();
|
|
556
|
+
const current = states.get(normalizedReviewerId);
|
|
557
|
+
if (current?.status !== "active" || current.keyId !== normalizedKeyId) {
|
|
558
|
+
throw new Error("reviewer key is not currently active");
|
|
559
|
+
}
|
|
560
|
+
const milliseconds = Number(this._now());
|
|
561
|
+
if (!Number.isFinite(milliseconds))
|
|
562
|
+
throw new TypeError("trust ledger clock is invalid");
|
|
563
|
+
const effectiveAt = new Date(milliseconds).toISOString();
|
|
564
|
+
const core = {
|
|
565
|
+
...recordCore(current),
|
|
566
|
+
status: "revoked",
|
|
567
|
+
reason,
|
|
568
|
+
effectiveAt,
|
|
569
|
+
};
|
|
570
|
+
const record = freeze({
|
|
571
|
+
...core,
|
|
572
|
+
recordDigest: digestGovernedKnowledgeTrustLedgerRecord(core),
|
|
573
|
+
});
|
|
574
|
+
validateRecord(record, this.descriptor);
|
|
575
|
+
const result = await this._appendRecord(record, {
|
|
576
|
+
eventId,
|
|
577
|
+
type: GOVERNED_KNOWLEDGE_REVIEWER_REVOKED_EVENT_TYPE,
|
|
578
|
+
reason,
|
|
579
|
+
timestamp: effectiveAt,
|
|
580
|
+
});
|
|
581
|
+
return Object.freeze({
|
|
582
|
+
...result,
|
|
583
|
+
reviewerId: normalizedReviewerId,
|
|
584
|
+
keyId: normalizedKeyId,
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
async commitApproval(receiptInput) {
|
|
589
|
+
const receipt = exact(
|
|
590
|
+
receiptInput,
|
|
591
|
+
RECEIPT_KEYS,
|
|
592
|
+
"knowledge approval receipt",
|
|
593
|
+
);
|
|
594
|
+
const attestation = exact(
|
|
595
|
+
receipt.attestation,
|
|
596
|
+
ATTESTATION_KEYS,
|
|
597
|
+
"knowledge approval attestation",
|
|
598
|
+
);
|
|
599
|
+
if (
|
|
600
|
+
receipt.schema !== GOVERNED_KNOWLEDGE_APPROVAL_RECEIPT_SCHEMA ||
|
|
601
|
+
receipt.tenantId !== this.descriptor.tenantId ||
|
|
602
|
+
receipt.receiptDigest !==
|
|
603
|
+
digestGovernedKnowledgeApprovalReceipt(receipt) ||
|
|
604
|
+
attestation.algorithm !== "Ed25519"
|
|
605
|
+
) {
|
|
606
|
+
throw new Error("knowledge approval receipt is invalid");
|
|
607
|
+
}
|
|
608
|
+
const eventId = `${GOVERNED_KNOWLEDGE_APPROVAL_COMMITTED_EVENT_TYPE}.${receipt.receiptDigest.slice(7)}`;
|
|
609
|
+
const existing = await this._entry(eventId);
|
|
610
|
+
if (existing) {
|
|
611
|
+
if (canonical(existing.record.receipt) !== canonical(receipt)) {
|
|
612
|
+
corrupt("approval commit event identity was reused");
|
|
613
|
+
}
|
|
614
|
+
return Object.freeze({
|
|
615
|
+
authenticated: true,
|
|
616
|
+
durable: true,
|
|
617
|
+
recovered: true,
|
|
618
|
+
receiptDigest: receipt.receiptDigest,
|
|
619
|
+
});
|
|
620
|
+
}
|
|
621
|
+
const registry = this.reviewerRegistry();
|
|
622
|
+
const reviewer = await registry.resolve({
|
|
623
|
+
tenantId: this.descriptor.tenantId,
|
|
624
|
+
reviewerId: receipt.reviewerId,
|
|
625
|
+
keyId: attestation.keyId,
|
|
626
|
+
});
|
|
627
|
+
const signature = Buffer.from(attestation.value ?? "", "base64url");
|
|
628
|
+
if (
|
|
629
|
+
!reviewer ||
|
|
630
|
+
signature.length !== 64 ||
|
|
631
|
+
signature.toString("base64url") !== attestation.value ||
|
|
632
|
+
!verify(
|
|
633
|
+
null,
|
|
634
|
+
governedKnowledgeApprovalSignatureMessage(receipt.receiptDigest),
|
|
635
|
+
reviewer.publicKey,
|
|
636
|
+
signature,
|
|
637
|
+
)
|
|
638
|
+
) {
|
|
639
|
+
throw new Error("knowledge approval signer is not currently trusted");
|
|
640
|
+
}
|
|
641
|
+
const milliseconds = Number(this._now());
|
|
642
|
+
if (!Number.isFinite(milliseconds))
|
|
643
|
+
throw new TypeError("trust ledger clock is invalid");
|
|
644
|
+
const committedAt = new Date(milliseconds).toISOString();
|
|
645
|
+
const core = {
|
|
646
|
+
schema: GOVERNED_KNOWLEDGE_TRUST_LEDGER_RECORD_SCHEMA,
|
|
647
|
+
tenantId: this.descriptor.tenantId,
|
|
648
|
+
kind: "approval",
|
|
649
|
+
reviewerId: null,
|
|
650
|
+
keyId: null,
|
|
651
|
+
publicKeySpki: null,
|
|
652
|
+
status: null,
|
|
653
|
+
reason: null,
|
|
654
|
+
receipt: clone(receipt),
|
|
655
|
+
receiptDigest: receipt.receiptDigest,
|
|
656
|
+
effectiveAt: null,
|
|
657
|
+
committedAt,
|
|
658
|
+
};
|
|
659
|
+
const record = freeze({
|
|
660
|
+
...core,
|
|
661
|
+
recordDigest: digestGovernedKnowledgeTrustLedgerRecord(core),
|
|
662
|
+
});
|
|
663
|
+
validateRecord(record, this.descriptor);
|
|
664
|
+
const result = await this._appendRecord(record, {
|
|
665
|
+
eventId,
|
|
666
|
+
type: GOVERNED_KNOWLEDGE_APPROVAL_COMMITTED_EVENT_TYPE,
|
|
667
|
+
reason: "governed knowledge approval committed",
|
|
668
|
+
timestamp: committedAt,
|
|
669
|
+
});
|
|
670
|
+
return Object.freeze({ ...result, receiptDigest: receipt.receiptDigest });
|
|
671
|
+
}
|
|
672
|
+
|
|
673
|
+
read = async ({ tenantId, receiptDigest } = {}) => {
|
|
674
|
+
if (
|
|
675
|
+
tenantId !== this.descriptor.tenantId ||
|
|
676
|
+
!DIGEST.test(receiptDigest ?? "")
|
|
677
|
+
) {
|
|
678
|
+
throw new TypeError("approval lookup boundary is invalid");
|
|
679
|
+
}
|
|
680
|
+
const eventId = `${GOVERNED_KNOWLEDGE_APPROVAL_COMMITTED_EVENT_TYPE}.${receiptDigest.slice(7)}`;
|
|
681
|
+
const entry = await this._entry(eventId);
|
|
682
|
+
return entry?.record.receipt ?? null;
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
reviewerRegistry() {
|
|
686
|
+
return createGovernedKnowledgeReviewerRegistry({
|
|
687
|
+
tenantId: this.descriptor.tenantId,
|
|
688
|
+
resolve: async ({ reviewerId, keyId }) => {
|
|
689
|
+
const states = await this._reviewerStates();
|
|
690
|
+
const current = states.get(reviewerId);
|
|
691
|
+
if (current?.status !== "active" || current.keyId !== keyId)
|
|
692
|
+
return null;
|
|
693
|
+
return {
|
|
694
|
+
tenantId: this.descriptor.tenantId,
|
|
695
|
+
reviewerId,
|
|
696
|
+
keyId,
|
|
697
|
+
publicKey: decodePublicKey(current.publicKeySpki, current.keyId),
|
|
698
|
+
status: "active",
|
|
699
|
+
};
|
|
700
|
+
},
|
|
701
|
+
});
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
approvalReader() {
|
|
705
|
+
return Object.freeze({ read: this.read });
|
|
706
|
+
}
|
|
707
|
+
}
|