chainlesschain 0.166.37 → 0.166.39
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 +7 -2
- package/scripts/governed-learning-local-attestor-service.mjs +222 -0
- package/scripts/governed-learning-local-attestor-trust-approval-service.mjs +338 -0
- package/scripts/governed-learning-local-attestor-trust-operations-service.mjs +575 -0
- package/scripts/governed-learning-windows-secure-pipe-broker.ps1 +375 -0
- package/src/assets/web-panel/assets/{AIOps-B_Nt1Qe4.js → AIOps-qWUdiFr5.js} +1 -1
- package/src/assets/web-panel/assets/{ActionButton-CHQbie4X.js → ActionButton-B_f7go3E.js} +1 -1
- package/src/assets/web-panel/assets/{Analytics-Bm9364Lw.js → Analytics-hSGFlSkB.js} +3 -3
- package/src/assets/web-panel/assets/{AppLayout-DTwlIh2v.js → AppLayout-CCYGgj71.js} +3 -3
- package/src/assets/web-panel/assets/{Artifacts-OibP0qB4.js → Artifacts-BdS1H9gq.js} +1 -1
- package/src/assets/web-panel/assets/{Audit-B8Fwavkg.js → Audit-Cck9IebC.js} +1 -1
- package/src/assets/web-panel/assets/{BackgroundAgents-p1Vb2K2B.js → BackgroundAgents-DZzoM_QS.js} +1 -1
- package/src/assets/web-panel/assets/{Backup-BI3QT-wI.js → Backup-D8H_QtGu.js} +1 -1
- package/src/assets/web-panel/assets/{BaseInput-BW_VRWWu.js → BaseInput-CTsNF_pu.js} +1 -1
- package/src/assets/web-panel/assets/{Chat-CUEhDQfq.js → Chat-CcEiTYY8.js} +4 -4
- package/src/assets/web-panel/assets/{ChatBubbleRenderer-C5UVXdVE.js → ChatBubbleRenderer-9BY1DPuc.js} +1 -1
- package/src/assets/web-panel/assets/{Checkbox-URYsaaxv.js → Checkbox-BPB9NNJx.js} +1 -1
- package/src/assets/web-panel/assets/{Codegen-DJAwC80a.js → Codegen-CKKMf7jD.js} +1 -1
- package/src/assets/web-panel/assets/{Col-DfRKFzc9.js → Col-DpSSB4Uu.js} +1 -1
- package/src/assets/web-panel/assets/{Community-GOkGHuly.js → Community-CfR6ikoH.js} +1 -1
- package/src/assets/web-panel/assets/{Compact-CIMs13NC.js → Compact-CqnkGGjK.js} +1 -1
- package/src/assets/web-panel/assets/{Compliance-BE-TQIxV.js → Compliance-9IzhdVQb.js} +1 -1
- package/src/assets/web-panel/assets/{Cowork-CYmbJiWw.js → Cowork-eXsgH33N.js} +4 -4
- package/src/assets/web-panel/assets/{Cron-Be3OIWyM.js → Cron-DvkzHFRi.js} +2 -2
- package/src/assets/web-panel/assets/{Crosschain-2xB2HS2D.js → Crosschain--az9d9f7.js} +1 -1
- package/src/assets/web-panel/assets/{DID-B_lJO2yF.js → DID-_Eb4kpnJ.js} +2 -2
- package/src/assets/web-panel/assets/{Dashboard-DnVWqUER.js → Dashboard-aDVisdXj.js} +2 -2
- package/src/assets/web-panel/assets/{Dropdown-C7ZJvHVa.js → Dropdown-oQ54XzS9.js} +1 -1
- package/src/assets/web-panel/assets/{EmailListRenderer-DLevT-0F.js → EmailListRenderer-BUPdMau1.js} +1 -1
- package/src/assets/web-panel/assets/{FamilyGuardDashboard-BwhQ5BRU.js → FamilyGuardDashboard-DtQTOCQI.js} +1 -1
- package/src/assets/web-panel/assets/{Federation-BkGI-Xkq.js → Federation-Cpnb3Moe.js} +1 -1
- package/src/assets/web-panel/assets/{FormItemContext-BgIIbMd7.js → FormItemContext-DmsrJkeU.js} +1 -1
- package/src/assets/web-panel/assets/{GenericCardRenderer-qL-qN8Nb.js → GenericCardRenderer-CpFDYnh3.js} +1 -1
- package/src/assets/web-panel/assets/{Git-BTzgrC4U.js → Git-HFG17p7Q.js} +2 -2
- package/src/assets/web-panel/assets/{Governance-p28vdCN4.js → Governance-D5Z5c7N8.js} +1 -1
- package/src/assets/web-panel/assets/{Inference-Cqephrur.js → Inference-DH159h74.js} +1 -1
- package/src/assets/web-panel/assets/{KnowledgeGraph-C3v5Q4Zp.js → KnowledgeGraph-DuL1pcJD.js} +1 -1
- package/src/assets/web-panel/assets/{Logs-T6f0w32w.js → Logs-Gqn8hFsM.js} +2 -2
- package/src/assets/web-panel/assets/{MarkdownRenderer-Dj5XPm3S.js → MarkdownRenderer-NHJaejHI.js} +1 -1
- package/src/assets/web-panel/assets/{Marketplace-ClDsvHDH.js → Marketplace-BArcWifg.js} +1 -1
- package/src/assets/web-panel/assets/{McpTools-C4P-_snx.js → McpTools-Q1BfBSnd.js} +2 -2
- package/src/assets/web-panel/assets/{Memory-BZZfG6y6.js → Memory-Cz8nRL7N.js} +2 -2
- package/src/assets/web-panel/assets/{MobileBridge-DpIv_BLs.js → MobileBridge-CRuxLRJw.js} +1 -1
- package/src/assets/web-panel/assets/{MobileProjects-Buz3NIQg.js → MobileProjects-B1CQIAGp.js} +1 -1
- package/src/assets/web-panel/assets/{Mtc-HS2PJB79.js → Mtc-CFhXZbPk.js} +2 -2
- package/src/assets/web-panel/assets/{MtcAudit-UmBrcU27.js → MtcAudit-C69LqcZ5.js} +4 -4
- package/src/assets/web-panel/assets/{Multisig-C00sXiZf.js → Multisig-y7yZeTTG.js} +3 -3
- package/src/assets/web-panel/assets/{NLProgramming-Dj1ng_ph.js → NLProgramming-DgjhoWea.js} +1 -1
- package/src/assets/web-panel/assets/{Notes-C4LEVKlk.js → Notes-DRc2WTDh.js} +4 -4
- package/src/assets/web-panel/assets/{NotificationSettings-BWXwnekv.js → NotificationSettings-CLPx24yn.js} +1 -1
- package/src/assets/web-panel/assets/{OrderTableRenderer-CZHfK8Nn.js → OrderTableRenderer-CMSNA4CO.js} +1 -1
- package/src/assets/web-panel/assets/{Organization-CFEtFPrV.js → Organization-Dm6LIyhZ.js} +4 -4
- package/src/assets/web-panel/assets/{Overflow-DSz4MJK1.js → Overflow-DP4DOYd-.js} +1 -1
- package/src/assets/web-panel/assets/{P2P-DyTEytwG.js → P2P-HFwvtFI1.js} +2 -2
- package/src/assets/web-panel/assets/{PdhVaultBrowser-B5QNGLVx.js → PdhVaultBrowser-D9gPfKRM.js} +3 -3
- package/src/assets/web-panel/assets/{Permissions-wEC_X7x4.js → Permissions-Bdtxniwh.js} +4 -4
- package/src/assets/web-panel/assets/{PersonalDataHub-If5GZlGZ.js → PersonalDataHub-Dgl2AbES.js} +3 -3
- package/src/assets/web-panel/assets/{Pipeline-LB4gqqmX.js → Pipeline-DpZrzYwK.js} +1 -1
- package/src/assets/web-panel/assets/{Privacy-QVLhoxcN.js → Privacy-EQ551c0t.js} +1 -1
- package/src/assets/web-panel/assets/{ProjectInit-FaAbTYTo.js → ProjectInit-xUSeczsM.js} +2 -2
- package/src/assets/web-panel/assets/{ProjectSettings-DhMeNA21.js → ProjectSettings-B66Sr_t8.js} +2 -2
- package/src/assets/web-panel/assets/{Projects-5L4lkWAZ.js → Projects-B4zikj95.js} +1 -1
- package/src/assets/web-panel/assets/{Providers-DhZFp8fO.js → Providers-DjLbqQPh.js} +1 -1
- package/src/assets/web-panel/assets/{QrScannerModal-BZuJ7_xi.js → QrScannerModal-KVsAQCd_.js} +1 -1
- package/src/assets/web-panel/assets/{QuickAsk-TXddPYYb.js → QuickAsk-C4noJXu8.js} +1 -1
- package/src/assets/web-panel/assets/{Recommend-DDuXIKYt.js → Recommend-Cn15clqY.js} +1 -1
- package/src/assets/web-panel/assets/{RemoteSession-CDrTXImv.js → RemoteSession-DdonMFwH.js} +3 -3
- package/src/assets/web-panel/assets/{Reputation-B2xKaKzt.js → Reputation-Bqe3NwWN.js} +1 -1
- package/src/assets/web-panel/assets/{Row-DzAjWAOX.js → Row-CnxBgjkD.js} +1 -1
- package/src/assets/web-panel/assets/{RssFeed-CKH38BLF.js → RssFeed-CSRrgZyK.js} +2 -2
- package/src/assets/web-panel/assets/{Search-CuPqJYoQ.js → Search-DTUYUfFx.js} +1 -1
- package/src/assets/web-panel/assets/{Security-DmAd2hxz.js → Security-BHExHVIm.js} +4 -4
- package/src/assets/web-panel/assets/{Services-Bt3nljnY.js → Services-B_IWqPes.js} +2 -2
- package/src/assets/web-panel/assets/{Skeleton-CP7RH093.js → Skeleton-C_oAtzmX.js} +1 -1
- package/src/assets/web-panel/assets/{Skills-DKNeMBTx.js → Skills-maJNokSL.js} +1 -1
- package/src/assets/web-panel/assets/{Sla-CbpM8Nrd.js → Sla-CMM_ZsLH.js} +1 -1
- package/src/assets/web-panel/assets/{SpeechSettings-DTk-vTzF.js → SpeechSettings-D7w_oNwK.js} +1 -1
- package/src/assets/web-panel/assets/{SyncSettings-DKiplwmc.js → SyncSettings-D0Zl3NIr.js} +2 -2
- package/src/assets/web-panel/assets/{Tasks-z-lOm8fx.js → Tasks-Bs0H0ctR.js} +1 -1
- package/src/assets/web-panel/assets/{Templates-Rb-Q0DGX.js → Templates-C6T6oCpR.js} +1 -1
- package/src/assets/web-panel/assets/{Tenant-C2sqDUnR.js → Tenant-ByL-kAYO.js} +1 -1
- package/src/assets/web-panel/assets/{Terminal-DcyzskEE.js → Terminal-6JXNhc3b.js} +2 -2
- package/src/assets/web-panel/assets/{TimelineRenderer-lg0Eam7N.js → TimelineRenderer-KiuUtCsl.js} +1 -1
- package/src/assets/web-panel/assets/{Tokens-D9USIMIA.js → Tokens-C0TWTIER.js} +1 -1
- package/src/assets/web-panel/assets/{Trigger-BQpaQJ5k.js → Trigger-DpC5aaJu.js} +1 -1
- package/src/assets/web-panel/assets/{Trust-C-Mik04s.js → Trust-lqqflCwv.js} +1 -1
- package/src/assets/web-panel/assets/{UkeySign-DrnZ_o56.js → UkeySign-BYSlVfBc.js} +1 -1
- package/src/assets/web-panel/assets/{VideoEditing-BDOsoZi0.js → VideoEditing-DX08oo_R.js} +1 -1
- package/src/assets/web-panel/assets/{Wallet-Ct_bleI7.js → Wallet-CcIFPvzL.js} +4 -4
- package/src/assets/web-panel/assets/{WebAuthn-B6b6FDao.js → WebAuthn-D8DfxfVT.js} +5 -5
- package/src/assets/web-panel/assets/{WorkflowEditor-ZUJMaIHr.js → WorkflowEditor-Csadpaln.js} +1 -1
- package/src/assets/web-panel/assets/{chat-BII20OJy.js → chat-CewHWc_T.js} +1 -1
- package/src/assets/web-panel/assets/{colors-DEsJRV3n.js → colors-B8at89r3.js} +1 -1
- package/src/assets/web-panel/assets/{compact-item-DoDYAzN9.js → compact-item-v_gxtF5-.js} +1 -1
- package/src/assets/web-panel/assets/{createContext-o_nofnVW.js → createContext-BOwFpJqA.js} +1 -1
- package/src/assets/web-panel/assets/devWarning-DenB_kSS.js +1 -0
- package/src/assets/web-panel/assets/{hasIn-CAsABTHC.js → hasIn-DjHUzt7E.js} +1 -1
- package/src/assets/web-panel/assets/{index-CeJSTcXw.js → index-1Tfwr_GA.js} +1 -1
- package/src/assets/web-panel/assets/{index-CVQSl1uY.js → index-5qmLo1us.js} +1 -1
- package/src/assets/web-panel/assets/index-B8bNwM9U.js +1 -0
- package/src/assets/web-panel/assets/{index-CQE-WMEU.js → index-BC20EQ7h.js} +1 -1
- package/src/assets/web-panel/assets/{index-DciLb_Ww.js → index-BGrmPk8T.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bqyg3PME.js → index-BPJ7uL2Q.js} +1 -1
- package/src/assets/web-panel/assets/{index-DU2Rpx1m.js → index-BRNyE3Kb.js} +1 -1
- package/src/assets/web-panel/assets/{index-CeWJqXNX.js → index-BVPsegMN.js} +1 -1
- package/src/assets/web-panel/assets/{index-DWZNYecl.js → index-B_egGFcR.js} +3 -3
- package/src/assets/web-panel/assets/{index-TSwbtCYG.js → index-BdZLA7k9.js} +1 -1
- package/src/assets/web-panel/assets/{index-BrIIJ2s-.js → index-Bk_Te9OR.js} +1 -1
- package/src/assets/web-panel/assets/{index-D5Fd4tdL.js → index-Bz8C8VXx.js} +1 -1
- package/src/assets/web-panel/assets/{index-B4SfcwZA.js → index-C7MHbmwn.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bn2ZPUXO.js → index-CFL1Hewx.js} +1 -1
- package/src/assets/web-panel/assets/{index-DsoFTdA2.js → index-CV1SgaBd.js} +1 -1
- package/src/assets/web-panel/assets/{index-B9hYHPhs.js → index-CXIPAeAB.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dgt1mKby.js → index-Ca8YZU2B.js} +1 -1
- package/src/assets/web-panel/assets/{index-DRAA2McB.js → index-Caf9UjhG.js} +1 -1
- package/src/assets/web-panel/assets/{index-BcYbQoY7.js → index-Cb1uNDOv.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bycj011d.js → index-CquUCUzS.js} +1 -1
- package/src/assets/web-panel/assets/{index-BzMA6Ra3.js → index-CwHoF-TP.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dbb34cAr.js → index-Cxk0mCoQ.js} +1 -1
- package/src/assets/web-panel/assets/{index-BxT8yTwS.js → index-D3o-pacB.js} +1 -1
- package/src/assets/web-panel/assets/{index-DrsAF-Ar.js → index-DA3n0Y28.js} +1 -1
- package/src/assets/web-panel/assets/index-DC0cpgIP.js +1 -0
- package/src/assets/web-panel/assets/{index-B9Z75LfA.js → index-DRRK_8Eg.js} +1 -1
- package/src/assets/web-panel/assets/{index-kIyLNyoF.js → index-DUMvVGyf.js} +1 -1
- package/src/assets/web-panel/assets/{index-DQmX_-hk.js → index-DdHdbXpy.js} +1 -1
- package/src/assets/web-panel/assets/{index-tCeNfxv4.js → index-Ddw05Myq.js} +1 -1
- package/src/assets/web-panel/assets/{index-BDRh3v44.js → index-Dfrb3Prv.js} +1 -1
- package/src/assets/web-panel/assets/{index-Bmwo5wQG.js → index-DiKaYIdo.js} +1 -1
- package/src/assets/web-panel/assets/{index-C1YDdyQ1.js → index-DmZLZsyC.js} +1 -1
- package/src/assets/web-panel/assets/{index-Dhnau6uw.js → index-FgUrhq_H.js} +1 -1
- package/src/assets/web-panel/assets/{index-Cf3sFEoE.js → index-Khw7wzVk.js} +1 -1
- package/src/assets/web-panel/assets/{index-B0ApxOMw.js → index-San3zacc.js} +1 -1
- package/src/assets/web-panel/assets/{index-D_CMB_wC.js → index-coOi37of.js} +1 -1
- package/src/assets/web-panel/assets/{index-BnbnmRqg.js → index-hyeBuocp.js} +1 -1
- package/src/assets/web-panel/assets/{index-DDQzBK3l.js → index-uk59xr0W.js} +1 -1
- package/src/assets/web-panel/assets/{index-CE2J04dA.js → index-wjvn8-9F.js} +1 -1
- package/src/assets/web-panel/assets/{initDefaultProps-CNjeeoKE.js → initDefaultProps-Id2dFdjh.js} +1 -1
- package/src/assets/web-panel/assets/{motion-CBT8IZDm.js → motion-DRxtwxng.js} +1 -1
- package/src/assets/web-panel/assets/{move-h1MOxjqv.js → move-BDYvdTb6.js} +1 -1
- package/src/assets/web-panel/assets/{mtc-parser-C_3pqNmn.js → mtc-parser-cz8LGI8Z.js} +1 -1
- package/src/assets/web-panel/assets/{omit-CMUnPFDk.js → omit-DHJeh6tH.js} +1 -1
- package/src/assets/web-panel/assets/{pickAttrs-p8_c1xq0.js → pickAttrs-XJEQIp7e.js} +1 -1
- package/src/assets/web-panel/assets/{placementArrow-D6O67r23.js → placementArrow-AFcf8lEn.js} +1 -1
- package/src/assets/web-panel/assets/{responsiveObserve-DquhepAK.js → responsiveObserve-CDEeBxs1.js} +1 -1
- package/src/assets/web-panel/assets/{slide-CZ-m6gmS.js → slide-CvbVfi6T.js} +1 -1
- package/src/assets/web-panel/assets/{statusUtils-k9ejZFtd.js → statusUtils-DtCyuEnq.js} +1 -1
- package/src/assets/web-panel/assets/{styleChecker-NX6_137R.js → styleChecker--UgmTsGY.js} +1 -1
- package/src/assets/web-panel/assets/{useFlexGapSupport-bCqR70lP.js → useFlexGapSupport-D9i1as-Y.js} +1 -1
- package/src/assets/web-panel/assets/{useFs-2l0vSqyY.js → useFs-CJx3zw01.js} +1 -1
- package/src/assets/web-panel/assets/{usePersonalDataHub-1yccm4O7.js → usePersonalDataHub-ZsIKYdZv.js} +1 -1
- package/src/assets/web-panel/assets/{vnode-CxQzajDo.js → vnode-B2RGEQG4.js} +1 -1
- package/src/assets/web-panel/assets/{zoom-CrTKwFyb.js → zoom-ob-0px2z.js} +1 -1
- package/src/assets/web-panel/index.html +1 -1
- package/src/command-help-index.json +1 -1
- package/src/commands/evolution-attestor-trust.js +113 -0
- package/src/commands/evolution.js +2 -0
- package/src/commands/learning.js +34 -5
- package/src/data/changelog.json +26 -0
- package/src/lib/evolution/evolution-artifact-ports.js +21 -0
- package/src/lib/evolution/evolution-deployment-loader.js +56 -0
- package/src/lib/evolution/evolution-immutable-ledger-segment-store.js +812 -0
- package/src/lib/evolution/evolution-ledger-file-manifest-catalog.js +218 -0
- package/src/lib/evolution/evolution-ledger-manifest-catalog.js +472 -0
- package/src/lib/evolution/evolution-ledger-manifest-chain.js +997 -0
- package/src/lib/evolution/evolution-ledger-manifest-head-store.js +408 -0
- package/src/lib/evolution/evolution-ledger-manifest-witness-adapter.js +554 -0
- package/src/lib/evolution/evolution-ledger-v2-manifest-backend.js +460 -0
- package/src/lib/evolution/governed-skill-synthesis-attestor-ipc-endpoint.js +94 -0
- package/src/lib/evolution/governed-skill-synthesis-attestor-trust-approval-client.js +485 -0
- package/src/lib/evolution/governed-skill-synthesis-attestor-trust-ipc-capability.js +200 -0
- package/src/lib/evolution/governed-skill-synthesis-attestor-trust-ledger.js +1222 -0
- package/src/lib/evolution/governed-skill-synthesis-attestor-trust-operations-cli-host.js +802 -0
- package/src/lib/evolution/governed-skill-synthesis-attestor-trust-operations-client.js +445 -0
- package/src/lib/evolution/governed-skill-synthesis-attestor-trust-operations.js +1082 -0
- package/src/lib/evolution/governed-skill-synthesis-attestor-trust-operator-registry.js +1453 -0
- package/src/lib/evolution/governed-skill-synthesis-candidate-evaluator.js +147 -0
- package/src/lib/evolution/governed-skill-synthesis-cli-host.js +115 -0
- package/src/lib/evolution/governed-skill-synthesis-evaluation-ledger-adapter.js +715 -0
- package/src/lib/evolution/governed-skill-synthesis-external-attestor.js +499 -0
- package/src/lib/evolution/governed-skill-synthesis-model-evaluator.js +514 -0
- package/src/lib/evolution/governed-skill-synthesis-process-attestor-worker.mjs +208 -0
- package/src/lib/evolution/governed-skill-synthesis-process-attestor.js +557 -0
- package/src/lib/evolution/governed-skill-synthesis-process-grader-worker.mjs +143 -0
- package/src/lib/evolution/governed-skill-synthesis-process-grader.js +444 -0
- package/src/lib/evolution/governed-skill-synthesis-provider-chat.js +220 -0
- package/src/lib/evolution/governed-skill-synthesis-windows-secure-pipe-host.js +236 -0
- package/src/lib/hook-audit-store.js +68 -2
- package/src/lib/hooks-v2-runtime.js +52 -26
- package/src/lib/learning/skill-synthesizer.js +66 -5
- package/src/lib/remote-read-loop-guard.js +30 -3
- package/src/assets/web-panel/assets/devWarning-0tgDCszN.js +0 -1
- package/src/assets/web-panel/assets/index-CFF8XEFE.js +0 -1
- package/src/assets/web-panel/assets/index-DVUEISry.js +0 -1
|
@@ -0,0 +1,1222 @@
|
|
|
1
|
+
import { createHash, createPublicKey } 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_REF_SCHEMA,
|
|
10
|
+
EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA,
|
|
11
|
+
EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA,
|
|
12
|
+
} from "./evolution-ledger.js";
|
|
13
|
+
import {
|
|
14
|
+
getGovernedSkillSynthesisExternalAttestationIdentity,
|
|
15
|
+
verifyGovernedSkillSynthesisExternalAttestation,
|
|
16
|
+
} from "./governed-skill-synthesis-external-attestor.js";
|
|
17
|
+
|
|
18
|
+
export const GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_RECORD_SCHEMA =
|
|
19
|
+
"chainlesschain.governed-skill-synthesis-attestor-trust-record/v1";
|
|
20
|
+
export const GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZED_RECORD_SCHEMA =
|
|
21
|
+
"chainlesschain.governed-skill-synthesis-attestor-trust-record/v2";
|
|
22
|
+
export const GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_VERIFIER_SCHEMA =
|
|
23
|
+
"chainlesschain.governed-skill-synthesis-attestor-trust-verifier/v1";
|
|
24
|
+
export const GOVERNED_SKILL_SYNTHESIS_ATTESTOR_KEY_REGISTERED_EVENT =
|
|
25
|
+
"learning.skill-synthesis-attestor-key.registered";
|
|
26
|
+
export const GOVERNED_SKILL_SYNTHESIS_ATTESTOR_KEY_ROTATED_EVENT =
|
|
27
|
+
"learning.skill-synthesis-attestor-key.rotated";
|
|
28
|
+
export const GOVERNED_SKILL_SYNTHESIS_ATTESTOR_KEY_REVOKED_EVENT =
|
|
29
|
+
"learning.skill-synthesis-attestor-key.revoked";
|
|
30
|
+
export const GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_CORRUPT_CODE =
|
|
31
|
+
"CC_LEARNING_SYNTHESIS_ATTESTOR_TRUST_CORRUPT";
|
|
32
|
+
|
|
33
|
+
const ARTIFACT_TYPE = "governed-skill-synthesis-attestor-trust-record";
|
|
34
|
+
const TRUST_LEDGERS = new WeakSet();
|
|
35
|
+
const VERIFIERS = new WeakSet();
|
|
36
|
+
const DIGEST = /^sha256:[a-f0-9]{64}$/u;
|
|
37
|
+
const ID = /^[A-Za-z0-9][A-Za-z0-9._:@/-]{0,255}$/u;
|
|
38
|
+
const SERVICE_ID = /^[a-z][a-z0-9]*(?:[.:_-][a-z0-9]+){1,7}$/u;
|
|
39
|
+
const RECORD_KEYS = new Set([
|
|
40
|
+
"effectiveAt",
|
|
41
|
+
"keyId",
|
|
42
|
+
"operation",
|
|
43
|
+
"priorKeyId",
|
|
44
|
+
"priorPublicKeySpki",
|
|
45
|
+
"publicKeySpki",
|
|
46
|
+
"reason",
|
|
47
|
+
"recordDigest",
|
|
48
|
+
"schema",
|
|
49
|
+
"serviceId",
|
|
50
|
+
"tenantId",
|
|
51
|
+
]);
|
|
52
|
+
const AUTHORIZED_RECORD_KEYS = new Set([...RECORD_KEYS, "authorization"]);
|
|
53
|
+
const AUTHORIZATION_LINK_KEYS = new Set([
|
|
54
|
+
"artifactRef",
|
|
55
|
+
"authorizationDigest",
|
|
56
|
+
"authorizationStreamId",
|
|
57
|
+
"eventSequence",
|
|
58
|
+
"recordDigest",
|
|
59
|
+
"schema",
|
|
60
|
+
]);
|
|
61
|
+
const ARTIFACT_REF_KEYS = new Set(["digest", "ref", "schema"]);
|
|
62
|
+
const AUTHORIZATION_ARTIFACT_TYPE =
|
|
63
|
+
"governed-skill-synthesis-attestor-trust-authorization";
|
|
64
|
+
const AUTHORIZATION_RECORD_SCHEMA =
|
|
65
|
+
"chainlesschain.governed-skill-synthesis-attestor-trust-authorization-record/v1";
|
|
66
|
+
const AUTHORIZATION_SCHEMA =
|
|
67
|
+
"chainlesschain.governed-skill-synthesis-attestor-trust-authorization/v1";
|
|
68
|
+
const AUTHORIZATION_REQUEST_SCHEMA =
|
|
69
|
+
"chainlesschain.governed-skill-synthesis-attestor-trust-operation-request/v1";
|
|
70
|
+
const APPROVAL_SCHEMA =
|
|
71
|
+
"chainlesschain.governed-skill-synthesis-attestor-trust-approval/v1";
|
|
72
|
+
const AUTHORIZATION_LINK_SCHEMA =
|
|
73
|
+
"chainlesschain.governed-skill-synthesis-attestor-trust-authorization-link/v1";
|
|
74
|
+
const AUTHORIZATION_EVENT_TYPE =
|
|
75
|
+
"learning.skill-synthesis-attestor-trust.authorization.committed";
|
|
76
|
+
const AUTHORIZATION_RECORD_KEYS = new Set([
|
|
77
|
+
"approvals",
|
|
78
|
+
"authorization",
|
|
79
|
+
"recordDigest",
|
|
80
|
+
"request",
|
|
81
|
+
"schema",
|
|
82
|
+
"tenantId",
|
|
83
|
+
]);
|
|
84
|
+
const AUTHORIZATION_KEYS = new Set([
|
|
85
|
+
"approvalReceiptDigests",
|
|
86
|
+
"authorizationDigest",
|
|
87
|
+
"authorizedAt",
|
|
88
|
+
"operatorIds",
|
|
89
|
+
"policyDigest",
|
|
90
|
+
"requestDigest",
|
|
91
|
+
"requiredApprovals",
|
|
92
|
+
"schema",
|
|
93
|
+
"tenantId",
|
|
94
|
+
]);
|
|
95
|
+
const AUTHORIZATION_REQUEST_KEYS = new Set([
|
|
96
|
+
"expiresAt",
|
|
97
|
+
"keyId",
|
|
98
|
+
"operation",
|
|
99
|
+
"policyDigest",
|
|
100
|
+
"priorKeyId",
|
|
101
|
+
"publicKeySpki",
|
|
102
|
+
"reason",
|
|
103
|
+
"requestDigest",
|
|
104
|
+
"requestedAt",
|
|
105
|
+
"requiredApprovals",
|
|
106
|
+
"schema",
|
|
107
|
+
"serviceId",
|
|
108
|
+
"tenantId",
|
|
109
|
+
]);
|
|
110
|
+
const APPROVAL_KEYS = new Set([
|
|
111
|
+
"approvedAt",
|
|
112
|
+
"attestation",
|
|
113
|
+
"automated",
|
|
114
|
+
"expiresAt",
|
|
115
|
+
"operatorId",
|
|
116
|
+
"policyDigest",
|
|
117
|
+
"receiptDigest",
|
|
118
|
+
"requestDigest",
|
|
119
|
+
"schema",
|
|
120
|
+
"tenantId",
|
|
121
|
+
]);
|
|
122
|
+
const APPROVAL_ATTESTATION_KEYS = new Set(["algorithm", "keyId", "value"]);
|
|
123
|
+
const EVENT_TYPES = new Set([
|
|
124
|
+
GOVERNED_SKILL_SYNTHESIS_ATTESTOR_KEY_REGISTERED_EVENT,
|
|
125
|
+
GOVERNED_SKILL_SYNTHESIS_ATTESTOR_KEY_ROTATED_EVENT,
|
|
126
|
+
GOVERNED_SKILL_SYNTHESIS_ATTESTOR_KEY_REVOKED_EVENT,
|
|
127
|
+
]);
|
|
128
|
+
|
|
129
|
+
function corrupt(message) {
|
|
130
|
+
const error = new Error(message);
|
|
131
|
+
error.code = GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_CORRUPT_CODE;
|
|
132
|
+
throw error;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function canonical(value) {
|
|
136
|
+
if (value === null || typeof value !== "object") return JSON.stringify(value);
|
|
137
|
+
if (Array.isArray(value)) return `[${value.map(canonical).join(",")}]`;
|
|
138
|
+
return `{${Object.keys(value)
|
|
139
|
+
.sort()
|
|
140
|
+
.map((key) => `${JSON.stringify(key)}:${canonical(value[key])}`)
|
|
141
|
+
.join(",")}}`;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function hash(domain, value) {
|
|
145
|
+
return `sha256:${createHash("sha256")
|
|
146
|
+
.update(domain)
|
|
147
|
+
.update("\0")
|
|
148
|
+
.update(canonical(value))
|
|
149
|
+
.digest("hex")}`;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function exact(value, keys, label) {
|
|
153
|
+
if (
|
|
154
|
+
!value ||
|
|
155
|
+
typeof value !== "object" ||
|
|
156
|
+
Array.isArray(value) ||
|
|
157
|
+
utilTypes.isProxy(value) ||
|
|
158
|
+
Object.getPrototypeOf(value) !== Object.prototype ||
|
|
159
|
+
Reflect.ownKeys(value).length !== keys.size ||
|
|
160
|
+
Reflect.ownKeys(value).some((key) => {
|
|
161
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
162
|
+
return (
|
|
163
|
+
typeof key !== "string" ||
|
|
164
|
+
!keys.has(key) ||
|
|
165
|
+
!descriptor ||
|
|
166
|
+
!("value" in descriptor) ||
|
|
167
|
+
descriptor.enumerable !== true
|
|
168
|
+
);
|
|
169
|
+
})
|
|
170
|
+
) {
|
|
171
|
+
corrupt(`${label} is invalid`);
|
|
172
|
+
}
|
|
173
|
+
return value;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function identifier(value, label) {
|
|
177
|
+
if (typeof value !== "string" || !ID.test(value)) {
|
|
178
|
+
throw new TypeError(`${label} is invalid`);
|
|
179
|
+
}
|
|
180
|
+
return value;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function serviceId(value) {
|
|
184
|
+
if (typeof value !== "string" || !SERVICE_ID.test(value)) {
|
|
185
|
+
throw new TypeError("attestor serviceId is invalid");
|
|
186
|
+
}
|
|
187
|
+
return value;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function reason(value) {
|
|
191
|
+
if (
|
|
192
|
+
typeof value !== "string" ||
|
|
193
|
+
value.trim() !== value ||
|
|
194
|
+
value.length < 1 ||
|
|
195
|
+
value.length > 2048
|
|
196
|
+
) {
|
|
197
|
+
throw new TypeError("attestor key lifecycle reason is invalid");
|
|
198
|
+
}
|
|
199
|
+
return value;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function capture(owner, method, label) {
|
|
203
|
+
if (
|
|
204
|
+
!owner ||
|
|
205
|
+
typeof owner !== "object" ||
|
|
206
|
+
utilTypes.isProxy(owner) ||
|
|
207
|
+
typeof owner[method] !== "function"
|
|
208
|
+
) {
|
|
209
|
+
throw new TypeError(`${label}.${method}() is required`);
|
|
210
|
+
}
|
|
211
|
+
return (...args) => Reflect.apply(owner[method], owner, args);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function freeze(value) {
|
|
215
|
+
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
216
|
+
for (const child of Object.values(value)) freeze(child);
|
|
217
|
+
Object.freeze(value);
|
|
218
|
+
}
|
|
219
|
+
return value;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function normalizeDescriptor(input) {
|
|
223
|
+
return Object.freeze({
|
|
224
|
+
tenantId: identifier(input?.tenantId, "tenantId"),
|
|
225
|
+
artifactTenantId: identifier(input?.artifactTenantId, "artifactTenantId"),
|
|
226
|
+
streamId: identifier(input?.streamId, "streamId"),
|
|
227
|
+
audience: identifier(input?.audience, "audience"),
|
|
228
|
+
purpose: identifier(input?.purpose, "purpose"),
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function encodePublicKey(value) {
|
|
233
|
+
let publicKey;
|
|
234
|
+
try {
|
|
235
|
+
if (
|
|
236
|
+
(utilTypes.isKeyObject(value) && value.type !== "public") ||
|
|
237
|
+
(typeof value === "string" && /PRIVATE KEY/u.test(value))
|
|
238
|
+
) {
|
|
239
|
+
throw new TypeError("private keys are not accepted");
|
|
240
|
+
}
|
|
241
|
+
publicKey = utilTypes.isKeyObject(value) ? value : createPublicKey(value);
|
|
242
|
+
} catch {
|
|
243
|
+
throw new TypeError("attestor publicKey must be Ed25519");
|
|
244
|
+
}
|
|
245
|
+
if (publicKey.asymmetricKeyType !== "ed25519") {
|
|
246
|
+
throw new TypeError("attestor publicKey must be Ed25519");
|
|
247
|
+
}
|
|
248
|
+
const bytes = publicKey.export({ format: "der", type: "spki" });
|
|
249
|
+
return Object.freeze({
|
|
250
|
+
keyId: `key:ed25519:${createHash("sha256").update(bytes).digest("hex")}`,
|
|
251
|
+
publicKeySpki: bytes.toString("base64url"),
|
|
252
|
+
});
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function decodePublicKey(publicKeySpki, keyId) {
|
|
256
|
+
if (typeof publicKeySpki !== "string") corrupt("attestor SPKI is invalid");
|
|
257
|
+
const bytes = Buffer.from(publicKeySpki, "base64url");
|
|
258
|
+
if (bytes.toString("base64url") !== publicKeySpki) {
|
|
259
|
+
corrupt("attestor SPKI is not canonical base64url");
|
|
260
|
+
}
|
|
261
|
+
let publicKey;
|
|
262
|
+
try {
|
|
263
|
+
publicKey = createPublicKey({ key: bytes, format: "der", type: "spki" });
|
|
264
|
+
} catch {
|
|
265
|
+
corrupt("attestor SPKI is invalid");
|
|
266
|
+
}
|
|
267
|
+
if (publicKey.asymmetricKeyType !== "ed25519") {
|
|
268
|
+
corrupt("attestor SPKI is not Ed25519");
|
|
269
|
+
}
|
|
270
|
+
const expected = `key:ed25519:${createHash("sha256")
|
|
271
|
+
.update(bytes)
|
|
272
|
+
.digest("hex")}`;
|
|
273
|
+
if (keyId !== expected) corrupt("attestor keyId does not bind its SPKI");
|
|
274
|
+
return publicKey;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
function recordCore(value) {
|
|
278
|
+
const core = structuredClone(value);
|
|
279
|
+
delete core.recordDigest;
|
|
280
|
+
return core;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function digestGovernedSkillSynthesisAttestorTrustRecord(value) {
|
|
284
|
+
const schema =
|
|
285
|
+
value?.schema ===
|
|
286
|
+
GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZED_RECORD_SCHEMA
|
|
287
|
+
? GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZED_RECORD_SCHEMA
|
|
288
|
+
: GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_RECORD_SCHEMA;
|
|
289
|
+
return hash(schema, recordCore(value));
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
function normalizeAuthorizationLink(value) {
|
|
293
|
+
if (value === null) return null;
|
|
294
|
+
exact(value, AUTHORIZATION_LINK_KEYS, "attestor trust authorization link");
|
|
295
|
+
exact(
|
|
296
|
+
value.artifactRef,
|
|
297
|
+
ARTIFACT_REF_KEYS,
|
|
298
|
+
"attestor trust authorization artifact ref",
|
|
299
|
+
);
|
|
300
|
+
if (
|
|
301
|
+
value.schema !== AUTHORIZATION_LINK_SCHEMA ||
|
|
302
|
+
!DIGEST.test(value.authorizationDigest ?? "") ||
|
|
303
|
+
!ID.test(value.authorizationStreamId ?? "") ||
|
|
304
|
+
!DIGEST.test(value.recordDigest ?? "") ||
|
|
305
|
+
!Number.isSafeInteger(value.eventSequence) ||
|
|
306
|
+
value.eventSequence < 1 ||
|
|
307
|
+
value.artifactRef.schema !== EVOLUTION_ARTIFACT_REF_SCHEMA ||
|
|
308
|
+
!DIGEST.test(value.artifactRef.digest ?? "") ||
|
|
309
|
+
typeof value.artifactRef.ref !== "string" ||
|
|
310
|
+
value.artifactRef.ref.length < 1 ||
|
|
311
|
+
value.artifactRef.ref.length > 2048
|
|
312
|
+
) {
|
|
313
|
+
corrupt("attestor trust authorization link is invalid");
|
|
314
|
+
}
|
|
315
|
+
return freeze(structuredClone(value));
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function validateRecord(value, descriptor) {
|
|
319
|
+
const authorized =
|
|
320
|
+
value?.schema ===
|
|
321
|
+
GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZED_RECORD_SCHEMA;
|
|
322
|
+
exact(
|
|
323
|
+
value,
|
|
324
|
+
authorized ? AUTHORIZED_RECORD_KEYS : RECORD_KEYS,
|
|
325
|
+
"attestor trust record",
|
|
326
|
+
);
|
|
327
|
+
if (
|
|
328
|
+
(!authorized &&
|
|
329
|
+
value.schema !== GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_RECORD_SCHEMA) ||
|
|
330
|
+
value.tenantId !== descriptor.tenantId ||
|
|
331
|
+
!["register", "rotate", "revoke"].includes(value.operation) ||
|
|
332
|
+
!SERVICE_ID.test(value.serviceId ?? "") ||
|
|
333
|
+
!/^key:ed25519:[a-f0-9]{64}$/u.test(value.keyId ?? "") ||
|
|
334
|
+
!Number.isFinite(Date.parse(value.effectiveAt)) ||
|
|
335
|
+
new Date(Date.parse(value.effectiveAt)).toISOString() !==
|
|
336
|
+
value.effectiveAt ||
|
|
337
|
+
value.recordDigest !==
|
|
338
|
+
digestGovernedSkillSynthesisAttestorTrustRecord(value)
|
|
339
|
+
) {
|
|
340
|
+
corrupt("attestor trust record binding is invalid");
|
|
341
|
+
}
|
|
342
|
+
if (authorized) normalizeAuthorizationLink(value.authorization);
|
|
343
|
+
decodePublicKey(value.publicKeySpki, value.keyId);
|
|
344
|
+
if (value.operation === "register") {
|
|
345
|
+
if (
|
|
346
|
+
value.priorKeyId !== null ||
|
|
347
|
+
value.priorPublicKeySpki !== null ||
|
|
348
|
+
value.reason !== null
|
|
349
|
+
) {
|
|
350
|
+
corrupt("attestor registration record is invalid");
|
|
351
|
+
}
|
|
352
|
+
} else if (value.operation === "rotate") {
|
|
353
|
+
if (
|
|
354
|
+
!/^key:ed25519:[a-f0-9]{64}$/u.test(value.priorKeyId ?? "") ||
|
|
355
|
+
value.priorKeyId === value.keyId ||
|
|
356
|
+
typeof value.priorPublicKeySpki !== "string" ||
|
|
357
|
+
typeof value.reason !== "string" ||
|
|
358
|
+
value.reason.trim() !== value.reason ||
|
|
359
|
+
value.reason.length < 1 ||
|
|
360
|
+
value.reason.length > 2048
|
|
361
|
+
) {
|
|
362
|
+
corrupt("attestor rotation record is invalid");
|
|
363
|
+
}
|
|
364
|
+
decodePublicKey(value.priorPublicKeySpki, value.priorKeyId);
|
|
365
|
+
} else if (
|
|
366
|
+
value.priorKeyId !== null ||
|
|
367
|
+
value.priorPublicKeySpki !== null ||
|
|
368
|
+
typeof value.reason !== "string" ||
|
|
369
|
+
value.reason.trim() !== value.reason ||
|
|
370
|
+
value.reason.length < 1 ||
|
|
371
|
+
value.reason.length > 2048
|
|
372
|
+
) {
|
|
373
|
+
corrupt("attestor revocation record is invalid");
|
|
374
|
+
}
|
|
375
|
+
return freeze(structuredClone(value));
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
function eventType(operation) {
|
|
379
|
+
if (operation === "register") {
|
|
380
|
+
return GOVERNED_SKILL_SYNTHESIS_ATTESTOR_KEY_REGISTERED_EVENT;
|
|
381
|
+
}
|
|
382
|
+
if (operation === "rotate") {
|
|
383
|
+
return GOVERNED_SKILL_SYNTHESIS_ATTESTOR_KEY_ROTATED_EVENT;
|
|
384
|
+
}
|
|
385
|
+
return GOVERNED_SKILL_SYNTHESIS_ATTESTOR_KEY_REVOKED_EVENT;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
function eventId(record) {
|
|
389
|
+
return `${eventType(record.operation)}.${hash("attestor-trust-event", {
|
|
390
|
+
serviceId: record.serviceId,
|
|
391
|
+
keyId: record.keyId,
|
|
392
|
+
}).slice(7)}`;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
function sameLifecycleIntent(left, right) {
|
|
396
|
+
const strip = (value) => {
|
|
397
|
+
const copy = recordCore(value);
|
|
398
|
+
delete copy.effectiveAt;
|
|
399
|
+
return copy;
|
|
400
|
+
};
|
|
401
|
+
return canonical(strip(left)) === canonical(strip(right));
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
function parseArtifact(resolution, descriptor) {
|
|
405
|
+
if (
|
|
406
|
+
resolution?.schema !== EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA ||
|
|
407
|
+
resolution.authenticated !== true ||
|
|
408
|
+
resolution.found !== true ||
|
|
409
|
+
!DIGEST.test(resolution.digest ?? "") ||
|
|
410
|
+
!DIGEST.test(resolution.receiptDigest ?? "") ||
|
|
411
|
+
!Buffer.isBuffer(resolution.bytes)
|
|
412
|
+
) {
|
|
413
|
+
corrupt("attestor trust artifact resolution is incomplete");
|
|
414
|
+
}
|
|
415
|
+
let artifact;
|
|
416
|
+
try {
|
|
417
|
+
artifact = JSON.parse(resolution.bytes.toString("utf8"));
|
|
418
|
+
} catch {
|
|
419
|
+
corrupt("attestor trust artifact is not JSON");
|
|
420
|
+
}
|
|
421
|
+
if (
|
|
422
|
+
artifact?.schema !== EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA ||
|
|
423
|
+
artifact.tenantId !== descriptor.artifactTenantId ||
|
|
424
|
+
artifact.audience !== descriptor.audience ||
|
|
425
|
+
artifact.purpose !== descriptor.purpose ||
|
|
426
|
+
artifact.retention !== "ledger" ||
|
|
427
|
+
artifact.type !== ARTIFACT_TYPE
|
|
428
|
+
) {
|
|
429
|
+
corrupt("attestor trust durable artifact binding is invalid");
|
|
430
|
+
}
|
|
431
|
+
return validateRecord(artifact.value, descriptor);
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
function parseAuthorizationArtifact(resolution, descriptor, link) {
|
|
435
|
+
if (
|
|
436
|
+
resolution?.schema !== EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA ||
|
|
437
|
+
resolution.authenticated !== true ||
|
|
438
|
+
resolution.found !== true ||
|
|
439
|
+
resolution.digest !== link.artifactRef.digest ||
|
|
440
|
+
!DIGEST.test(resolution.receiptDigest ?? "") ||
|
|
441
|
+
!Buffer.isBuffer(resolution.bytes)
|
|
442
|
+
) {
|
|
443
|
+
corrupt("attestor trust authorization artifact is incomplete");
|
|
444
|
+
}
|
|
445
|
+
let artifact;
|
|
446
|
+
try {
|
|
447
|
+
artifact = JSON.parse(resolution.bytes.toString("utf8"));
|
|
448
|
+
} catch {
|
|
449
|
+
corrupt("attestor trust authorization artifact is not JSON");
|
|
450
|
+
}
|
|
451
|
+
if (
|
|
452
|
+
artifact?.schema !== EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA ||
|
|
453
|
+
artifact.tenantId !== descriptor.artifactTenantId ||
|
|
454
|
+
artifact.audience !== descriptor.audience ||
|
|
455
|
+
artifact.purpose !== descriptor.purpose ||
|
|
456
|
+
artifact.retention !== "ledger" ||
|
|
457
|
+
artifact.type !== AUTHORIZATION_ARTIFACT_TYPE
|
|
458
|
+
) {
|
|
459
|
+
corrupt("attestor trust authorization artifact binding is invalid");
|
|
460
|
+
}
|
|
461
|
+
const record = artifact.value;
|
|
462
|
+
exact(
|
|
463
|
+
record,
|
|
464
|
+
AUTHORIZATION_RECORD_KEYS,
|
|
465
|
+
"attestor trust authorization record",
|
|
466
|
+
);
|
|
467
|
+
exact(
|
|
468
|
+
record.request,
|
|
469
|
+
AUTHORIZATION_REQUEST_KEYS,
|
|
470
|
+
"attestor trust authorization request",
|
|
471
|
+
);
|
|
472
|
+
exact(
|
|
473
|
+
record.authorization,
|
|
474
|
+
AUTHORIZATION_KEYS,
|
|
475
|
+
"attestor trust authorization",
|
|
476
|
+
);
|
|
477
|
+
const recordCore = structuredClone(record);
|
|
478
|
+
delete recordCore.recordDigest;
|
|
479
|
+
const requestCore = structuredClone(record.request);
|
|
480
|
+
delete requestCore.requestDigest;
|
|
481
|
+
const authorizationCore = structuredClone(record.authorization);
|
|
482
|
+
delete authorizationCore.authorizationDigest;
|
|
483
|
+
const approvalsValid =
|
|
484
|
+
Array.isArray(record.approvals) &&
|
|
485
|
+
record.approvals.every((approval, index) => {
|
|
486
|
+
try {
|
|
487
|
+
exact(approval, APPROVAL_KEYS, "attestor trust approval");
|
|
488
|
+
exact(
|
|
489
|
+
approval.attestation,
|
|
490
|
+
APPROVAL_ATTESTATION_KEYS,
|
|
491
|
+
"attestor trust approval attestation",
|
|
492
|
+
);
|
|
493
|
+
} catch {
|
|
494
|
+
return false;
|
|
495
|
+
}
|
|
496
|
+
const approvalCore = structuredClone(approval);
|
|
497
|
+
delete approvalCore.receiptDigest;
|
|
498
|
+
delete approvalCore.attestation;
|
|
499
|
+
return (
|
|
500
|
+
approval.schema === APPROVAL_SCHEMA &&
|
|
501
|
+
approval.tenantId === descriptor.tenantId &&
|
|
502
|
+
approval.automated === false &&
|
|
503
|
+
approval.requestDigest === record.request.requestDigest &&
|
|
504
|
+
approval.policyDigest === record.request.policyDigest &&
|
|
505
|
+
approval.receiptDigest === hash(APPROVAL_SCHEMA, approvalCore) &&
|
|
506
|
+
approval.attestation.algorithm === "Ed25519" &&
|
|
507
|
+
/^key:ed25519:[a-f0-9]{64}$/u.test(approval.attestation.keyId ?? "") &&
|
|
508
|
+
typeof approval.attestation.value === "string" &&
|
|
509
|
+
record.authorization.operatorIds[index] === approval.operatorId &&
|
|
510
|
+
record.authorization.approvalReceiptDigests[index] ===
|
|
511
|
+
approval.receiptDigest
|
|
512
|
+
);
|
|
513
|
+
});
|
|
514
|
+
if (
|
|
515
|
+
record.schema !== AUTHORIZATION_RECORD_SCHEMA ||
|
|
516
|
+
record.tenantId !== descriptor.tenantId ||
|
|
517
|
+
!Array.isArray(record.approvals) ||
|
|
518
|
+
record.approvals.length !== record.authorization.requiredApprovals ||
|
|
519
|
+
record.request.schema !== AUTHORIZATION_REQUEST_SCHEMA ||
|
|
520
|
+
record.request.requestDigest !==
|
|
521
|
+
hash(AUTHORIZATION_REQUEST_SCHEMA, requestCore) ||
|
|
522
|
+
!Number.isSafeInteger(record.request.requiredApprovals) ||
|
|
523
|
+
record.request.requiredApprovals < 1 ||
|
|
524
|
+
record.request.requiredApprovals > 16 ||
|
|
525
|
+
record.recordDigest !== hash(AUTHORIZATION_RECORD_SCHEMA, recordCore) ||
|
|
526
|
+
record.recordDigest !== link.recordDigest ||
|
|
527
|
+
record.authorization.schema !== AUTHORIZATION_SCHEMA ||
|
|
528
|
+
record.authorization.tenantId !== descriptor.tenantId ||
|
|
529
|
+
record.authorization.requestDigest !== record.request.requestDigest ||
|
|
530
|
+
record.authorization.policyDigest !== record.request.policyDigest ||
|
|
531
|
+
record.authorization.requiredApprovals !==
|
|
532
|
+
record.request.requiredApprovals ||
|
|
533
|
+
!Array.isArray(record.authorization.operatorIds) ||
|
|
534
|
+
!Array.isArray(record.authorization.approvalReceiptDigests) ||
|
|
535
|
+
record.authorization.operatorIds.length !== record.approvals.length ||
|
|
536
|
+
record.authorization.approvalReceiptDigests.length !==
|
|
537
|
+
record.approvals.length ||
|
|
538
|
+
new Set(record.authorization.operatorIds).size !==
|
|
539
|
+
record.authorization.operatorIds.length ||
|
|
540
|
+
!approvalsValid ||
|
|
541
|
+
record.authorization.authorizationDigest !==
|
|
542
|
+
hash(AUTHORIZATION_SCHEMA, authorizationCore) ||
|
|
543
|
+
record.authorization.authorizationDigest !== link.authorizationDigest
|
|
544
|
+
) {
|
|
545
|
+
corrupt("attestor trust authorization record binding is invalid");
|
|
546
|
+
}
|
|
547
|
+
return record;
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
function verificationInput(value) {
|
|
551
|
+
if (
|
|
552
|
+
!value ||
|
|
553
|
+
typeof value !== "object" ||
|
|
554
|
+
Array.isArray(value) ||
|
|
555
|
+
utilTypes.isProxy(value) ||
|
|
556
|
+
Object.getPrototypeOf(value) !== Object.prototype
|
|
557
|
+
) {
|
|
558
|
+
return null;
|
|
559
|
+
}
|
|
560
|
+
const descriptors = Object.getOwnPropertyDescriptors(value);
|
|
561
|
+
const required = new Set([
|
|
562
|
+
"attestation",
|
|
563
|
+
"candidateDigest",
|
|
564
|
+
"descriptor",
|
|
565
|
+
"receiptDigest",
|
|
566
|
+
]);
|
|
567
|
+
const allowed = new Set([...required, "ledgerContext"]);
|
|
568
|
+
if (
|
|
569
|
+
Reflect.ownKeys(descriptors).some(
|
|
570
|
+
(key) =>
|
|
571
|
+
typeof key !== "string" ||
|
|
572
|
+
!allowed.has(key) ||
|
|
573
|
+
!("value" in descriptors[key]) ||
|
|
574
|
+
descriptors[key].enumerable !== true,
|
|
575
|
+
) ||
|
|
576
|
+
[...required].some((key) => !Object.hasOwn(descriptors, key))
|
|
577
|
+
) {
|
|
578
|
+
return null;
|
|
579
|
+
}
|
|
580
|
+
return Object.fromEntries(
|
|
581
|
+
Object.entries(descriptors).map(([key, entry]) => [key, entry.value]),
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
export class GovernedSkillSynthesisAttestorTrustLedger {
|
|
586
|
+
constructor({
|
|
587
|
+
descriptor: input,
|
|
588
|
+
artifactPorts,
|
|
589
|
+
ledger,
|
|
590
|
+
ledgerArtifactResolver,
|
|
591
|
+
now = Date.now,
|
|
592
|
+
} = {}) {
|
|
593
|
+
this.descriptor = normalizeDescriptor(input);
|
|
594
|
+
this._put = capture(artifactPorts, "putCanonical", "artifactPorts");
|
|
595
|
+
this._read = capture(ledger, "read", "ledger");
|
|
596
|
+
this._verify = capture(ledger, "verify", "ledger");
|
|
597
|
+
this._append = capture(ledger, "appendDomainEvent", "ledger");
|
|
598
|
+
if (!isEvolutionLedgerArtifactResolver(ledgerArtifactResolver)) {
|
|
599
|
+
throw new TypeError(
|
|
600
|
+
"a branded EvolutionArtifactPorts ledger resolver is required",
|
|
601
|
+
);
|
|
602
|
+
}
|
|
603
|
+
if (typeof now !== "function" || utilTypes.isProxy(now)) {
|
|
604
|
+
throw new TypeError("attestor trust clock must be a non-proxy function");
|
|
605
|
+
}
|
|
606
|
+
this._resolve = ledgerArtifactResolver;
|
|
607
|
+
this._now = now;
|
|
608
|
+
TRUST_LEDGERS.add(this);
|
|
609
|
+
Object.freeze(this);
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
_events() {
|
|
613
|
+
const events = this._read();
|
|
614
|
+
if (!Array.isArray(events)) corrupt("EvolutionLedger returned no events");
|
|
615
|
+
return events.filter(
|
|
616
|
+
(event) =>
|
|
617
|
+
event.schema === EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA &&
|
|
618
|
+
event.tenantId === this.descriptor.tenantId &&
|
|
619
|
+
event.correlationId === this.descriptor.streamId &&
|
|
620
|
+
EVENT_TYPES.has(event.type),
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
async _resolveEvent(event) {
|
|
625
|
+
const authority = this._verify();
|
|
626
|
+
const resolution = await this._resolve({
|
|
627
|
+
epoch: authority.epoch,
|
|
628
|
+
ledgerId: authority.ledgerId,
|
|
629
|
+
ref: event.subjectRef,
|
|
630
|
+
tenantId: this.descriptor.artifactTenantId,
|
|
631
|
+
});
|
|
632
|
+
if (
|
|
633
|
+
resolution?.ref !== event.subjectRef.ref ||
|
|
634
|
+
resolution?.digest !== event.subjectRef.digest
|
|
635
|
+
) {
|
|
636
|
+
corrupt("attestor trust ledger resolved a substituted artifact");
|
|
637
|
+
}
|
|
638
|
+
const record = parseArtifact(resolution, this.descriptor);
|
|
639
|
+
if (
|
|
640
|
+
event.type !== eventType(record.operation) ||
|
|
641
|
+
event.eventId !== eventId(record) ||
|
|
642
|
+
event.timestamp !== record.effectiveAt ||
|
|
643
|
+
event.decision !== "committed" ||
|
|
644
|
+
!Number.isSafeInteger(event.sequence) ||
|
|
645
|
+
event.sequence < 1
|
|
646
|
+
) {
|
|
647
|
+
corrupt("attestor trust event binding is invalid");
|
|
648
|
+
}
|
|
649
|
+
if (record.authorization === undefined) {
|
|
650
|
+
if (!Array.isArray(event.sourceRefs) || event.sourceRefs.length !== 0) {
|
|
651
|
+
corrupt("legacy attestor trust event has unexpected authorization");
|
|
652
|
+
}
|
|
653
|
+
} else {
|
|
654
|
+
await this._validateAuthorizationLineage(record, event, authority);
|
|
655
|
+
}
|
|
656
|
+
return { event, record };
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
async _validateAuthorizationLineage(record, event, authority) {
|
|
660
|
+
const link = normalizeAuthorizationLink(record.authorization);
|
|
661
|
+
const authorizationEvents = this._read().filter(
|
|
662
|
+
(candidate) =>
|
|
663
|
+
candidate.schema === EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA &&
|
|
664
|
+
candidate.sequence === link.eventSequence &&
|
|
665
|
+
candidate.type === AUTHORIZATION_EVENT_TYPE &&
|
|
666
|
+
candidate.tenantId === this.descriptor.tenantId &&
|
|
667
|
+
candidate.correlationId === link.authorizationStreamId,
|
|
668
|
+
);
|
|
669
|
+
if (
|
|
670
|
+
authorizationEvents.length !== 1 ||
|
|
671
|
+
link.eventSequence >= event.sequence ||
|
|
672
|
+
!Array.isArray(event.sourceRefs) ||
|
|
673
|
+
event.sourceRefs.length !== 1 ||
|
|
674
|
+
canonical(event.sourceRefs[0]) !== canonical(link.artifactRef) ||
|
|
675
|
+
canonical(authorizationEvents[0].subjectRef) !==
|
|
676
|
+
canonical(link.artifactRef) ||
|
|
677
|
+
authorizationEvents[0].decision !== "accepted"
|
|
678
|
+
) {
|
|
679
|
+
corrupt("attestor trust authorization lineage is invalid");
|
|
680
|
+
}
|
|
681
|
+
const resolution = await this._resolve({
|
|
682
|
+
epoch: authority.epoch,
|
|
683
|
+
ledgerId: authority.ledgerId,
|
|
684
|
+
ref: link.artifactRef,
|
|
685
|
+
tenantId: this.descriptor.artifactTenantId,
|
|
686
|
+
});
|
|
687
|
+
if (
|
|
688
|
+
resolution?.ref !== link.artifactRef.ref ||
|
|
689
|
+
resolution?.digest !== link.artifactRef.digest
|
|
690
|
+
) {
|
|
691
|
+
corrupt("attestor trust authorization resolution was substituted");
|
|
692
|
+
}
|
|
693
|
+
const authorization = parseAuthorizationArtifact(
|
|
694
|
+
resolution,
|
|
695
|
+
this.descriptor,
|
|
696
|
+
link,
|
|
697
|
+
);
|
|
698
|
+
const request = authorization.request;
|
|
699
|
+
if (
|
|
700
|
+
request.tenantId !== record.tenantId ||
|
|
701
|
+
request.serviceId !== record.serviceId ||
|
|
702
|
+
request.operation !== record.operation ||
|
|
703
|
+
request.keyId !== record.keyId ||
|
|
704
|
+
(record.operation !== "revoke" &&
|
|
705
|
+
request.publicKeySpki !== record.publicKeySpki) ||
|
|
706
|
+
(record.operation === "revoke" && request.publicKeySpki !== null) ||
|
|
707
|
+
request.priorKeyId !== record.priorKeyId ||
|
|
708
|
+
request.reason !== record.reason ||
|
|
709
|
+
authorizationEvents[0].timestamp !==
|
|
710
|
+
authorization.authorization.authorizedAt
|
|
711
|
+
) {
|
|
712
|
+
corrupt("attestor trust authorization does not bind the lifecycle event");
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
async _entry(targetEventId) {
|
|
717
|
+
const matches = this._events().filter(
|
|
718
|
+
(event) => event.eventId === targetEventId,
|
|
719
|
+
);
|
|
720
|
+
if (matches.length > 1) corrupt("attestor trust event is ambiguous");
|
|
721
|
+
return matches.length === 0 ? null : this._resolveEvent(matches[0]);
|
|
722
|
+
}
|
|
723
|
+
|
|
724
|
+
async _states() {
|
|
725
|
+
const states = new Map();
|
|
726
|
+
for (const event of this._events()) {
|
|
727
|
+
const entry = await this._resolveEvent(event);
|
|
728
|
+
const { record } = entry;
|
|
729
|
+
const state = states.get(record.serviceId) ?? {
|
|
730
|
+
active: null,
|
|
731
|
+
retired: new Map(),
|
|
732
|
+
revoked: new Set(),
|
|
733
|
+
seen: new Set(),
|
|
734
|
+
};
|
|
735
|
+
if (record.operation === "register") {
|
|
736
|
+
if (state.active || state.seen.size > 0) {
|
|
737
|
+
corrupt("attestor service has duplicate registration");
|
|
738
|
+
}
|
|
739
|
+
state.active = {
|
|
740
|
+
keyId: record.keyId,
|
|
741
|
+
publicKeySpki: record.publicKeySpki,
|
|
742
|
+
registeredSequence: event.sequence,
|
|
743
|
+
};
|
|
744
|
+
state.seen.add(record.keyId);
|
|
745
|
+
} else if (record.operation === "rotate") {
|
|
746
|
+
if (
|
|
747
|
+
!state.active ||
|
|
748
|
+
state.active.keyId !== record.priorKeyId ||
|
|
749
|
+
state.active.publicKeySpki !== record.priorPublicKeySpki ||
|
|
750
|
+
state.seen.has(record.keyId)
|
|
751
|
+
) {
|
|
752
|
+
corrupt("attestor rotation does not follow the active key");
|
|
753
|
+
}
|
|
754
|
+
state.retired.set(state.active.keyId, {
|
|
755
|
+
...state.active,
|
|
756
|
+
retiredSequence: event.sequence,
|
|
757
|
+
});
|
|
758
|
+
state.active = {
|
|
759
|
+
keyId: record.keyId,
|
|
760
|
+
publicKeySpki: record.publicKeySpki,
|
|
761
|
+
registeredSequence: event.sequence,
|
|
762
|
+
};
|
|
763
|
+
state.seen.add(record.keyId);
|
|
764
|
+
} else {
|
|
765
|
+
const activeMatch = state.active?.keyId === record.keyId;
|
|
766
|
+
const retired = state.retired.get(record.keyId);
|
|
767
|
+
const stored = activeMatch ? state.active : retired;
|
|
768
|
+
if (!stored || stored.publicKeySpki !== record.publicKeySpki) {
|
|
769
|
+
corrupt("attestor revocation has no matching trusted key");
|
|
770
|
+
}
|
|
771
|
+
if (activeMatch) state.active = null;
|
|
772
|
+
else state.retired.delete(record.keyId);
|
|
773
|
+
state.revoked.add(record.keyId);
|
|
774
|
+
}
|
|
775
|
+
states.set(record.serviceId, state);
|
|
776
|
+
}
|
|
777
|
+
return states;
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
async _stateSnapshot() {
|
|
781
|
+
const before = this._verify();
|
|
782
|
+
const states = await this._states();
|
|
783
|
+
const after = this._verify();
|
|
784
|
+
if (
|
|
785
|
+
before.ledgerId !== after.ledgerId ||
|
|
786
|
+
before.epoch !== after.epoch ||
|
|
787
|
+
before.sequence !== after.sequence ||
|
|
788
|
+
before.headDigest !== after.headDigest
|
|
789
|
+
) {
|
|
790
|
+
const error = new Error("attestor trust ledger changed during read");
|
|
791
|
+
error.code = "CC_LEARNING_SYNTHESIS_ATTESTOR_TRUST_CONFLICT";
|
|
792
|
+
throw error;
|
|
793
|
+
}
|
|
794
|
+
return { authority: after, states };
|
|
795
|
+
}
|
|
796
|
+
|
|
797
|
+
async _appendRecord(record, expectedHead) {
|
|
798
|
+
const id = eventId(record);
|
|
799
|
+
const existing = await this._entry(id);
|
|
800
|
+
if (existing) {
|
|
801
|
+
if (!sameLifecycleIntent(existing.record, record)) {
|
|
802
|
+
corrupt("attestor trust event identity resolved different content");
|
|
803
|
+
}
|
|
804
|
+
return Object.freeze({
|
|
805
|
+
authenticated: true,
|
|
806
|
+
durable: true,
|
|
807
|
+
recovered: true,
|
|
808
|
+
});
|
|
809
|
+
}
|
|
810
|
+
const head = this._verify();
|
|
811
|
+
if (
|
|
812
|
+
!expectedHead ||
|
|
813
|
+
head.ledgerId !== expectedHead.ledgerId ||
|
|
814
|
+
head.epoch !== expectedHead.epoch ||
|
|
815
|
+
head.sequence !== expectedHead.sequence ||
|
|
816
|
+
head.headDigest !== expectedHead.headDigest
|
|
817
|
+
) {
|
|
818
|
+
const error = new Error("attestor trust ledger changed before commit");
|
|
819
|
+
error.code = "CC_LEARNING_SYNTHESIS_ATTESTOR_TRUST_CONFLICT";
|
|
820
|
+
throw error;
|
|
821
|
+
}
|
|
822
|
+
const published = this._put(ARTIFACT_TYPE, record, {
|
|
823
|
+
audience: this.descriptor.audience,
|
|
824
|
+
purpose: this.descriptor.purpose,
|
|
825
|
+
retention: "ledger",
|
|
826
|
+
});
|
|
827
|
+
if (
|
|
828
|
+
!published?.ref ||
|
|
829
|
+
published.receipt?.persisted !== true ||
|
|
830
|
+
published.receipt?.readbackVerified !== true ||
|
|
831
|
+
published.receipt?.integrityVerified !== true ||
|
|
832
|
+
published.receipt?.retention !== "ledger"
|
|
833
|
+
) {
|
|
834
|
+
corrupt("attestor trust artifact was not durably read back");
|
|
835
|
+
}
|
|
836
|
+
let receipt;
|
|
837
|
+
try {
|
|
838
|
+
receipt = this._append(
|
|
839
|
+
{
|
|
840
|
+
artifactTenantId: this.descriptor.artifactTenantId,
|
|
841
|
+
correlationId: this.descriptor.streamId,
|
|
842
|
+
decision: "committed",
|
|
843
|
+
eventId: id,
|
|
844
|
+
reason: `governed synthesis attestor key ${record.operation}`,
|
|
845
|
+
skillName: null,
|
|
846
|
+
sourceRefs:
|
|
847
|
+
record.authorization === undefined
|
|
848
|
+
? []
|
|
849
|
+
: [record.authorization.artifactRef],
|
|
850
|
+
subjectRef: published.ref,
|
|
851
|
+
tenantId: this.descriptor.tenantId,
|
|
852
|
+
timestamp: record.effectiveAt,
|
|
853
|
+
type: eventType(record.operation),
|
|
854
|
+
},
|
|
855
|
+
{
|
|
856
|
+
expectedHeadDigest: head.headDigest,
|
|
857
|
+
expectedSequence: head.sequence,
|
|
858
|
+
},
|
|
859
|
+
);
|
|
860
|
+
} catch (error) {
|
|
861
|
+
const recovered = await this._entry(id);
|
|
862
|
+
if (!recovered || !sameLifecycleIntent(recovered.record, record)) {
|
|
863
|
+
throw error;
|
|
864
|
+
}
|
|
865
|
+
receipt = { authenticated: true, durable: true, recovered: true };
|
|
866
|
+
}
|
|
867
|
+
if (receipt?.authenticated !== true || receipt.durable !== true) {
|
|
868
|
+
corrupt("attestor trust ledger append was not durably confirmed");
|
|
869
|
+
}
|
|
870
|
+
const stored = await this._entry(id);
|
|
871
|
+
if (!stored || canonical(stored.record) !== canonical(record)) {
|
|
872
|
+
corrupt("attestor trust ledger readback differs after commit");
|
|
873
|
+
}
|
|
874
|
+
return Object.freeze({
|
|
875
|
+
authenticated: true,
|
|
876
|
+
durable: true,
|
|
877
|
+
recovered: receipt.recovered === true,
|
|
878
|
+
});
|
|
879
|
+
}
|
|
880
|
+
|
|
881
|
+
_timestamp() {
|
|
882
|
+
const milliseconds = Number(this._now());
|
|
883
|
+
if (!Number.isFinite(milliseconds)) {
|
|
884
|
+
throw new TypeError("attestor trust clock is invalid");
|
|
885
|
+
}
|
|
886
|
+
return new Date(milliseconds).toISOString();
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
async registerKey({
|
|
890
|
+
serviceId: inputServiceId,
|
|
891
|
+
publicKey,
|
|
892
|
+
authorization = null,
|
|
893
|
+
} = {}) {
|
|
894
|
+
const normalizedServiceId = serviceId(inputServiceId);
|
|
895
|
+
const encoded = encodePublicKey(publicKey);
|
|
896
|
+
const normalizedAuthorization = normalizeAuthorizationLink(authorization);
|
|
897
|
+
const existing = await this._entry(
|
|
898
|
+
eventId({
|
|
899
|
+
operation: "register",
|
|
900
|
+
serviceId: normalizedServiceId,
|
|
901
|
+
keyId: encoded.keyId,
|
|
902
|
+
}),
|
|
903
|
+
);
|
|
904
|
+
if (existing) {
|
|
905
|
+
if (
|
|
906
|
+
existing.record.operation !== "register" ||
|
|
907
|
+
existing.record.serviceId !== normalizedServiceId ||
|
|
908
|
+
existing.record.publicKeySpki !== encoded.publicKeySpki ||
|
|
909
|
+
canonical(existing.record.authorization ?? null) !==
|
|
910
|
+
canonical(normalizedAuthorization)
|
|
911
|
+
) {
|
|
912
|
+
corrupt("attestor registration event identity was reused");
|
|
913
|
+
}
|
|
914
|
+
return Object.freeze({
|
|
915
|
+
authenticated: true,
|
|
916
|
+
durable: true,
|
|
917
|
+
recovered: true,
|
|
918
|
+
serviceId: normalizedServiceId,
|
|
919
|
+
keyId: encoded.keyId,
|
|
920
|
+
operation: "register",
|
|
921
|
+
recordDigest: existing.record.recordDigest,
|
|
922
|
+
authorizationDigest:
|
|
923
|
+
existing.record.authorization?.authorizationDigest ?? null,
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
const snapshot = await this._stateSnapshot();
|
|
927
|
+
const states = snapshot.states;
|
|
928
|
+
if (states.has(normalizedServiceId)) {
|
|
929
|
+
throw new Error("attestor service is already registered");
|
|
930
|
+
}
|
|
931
|
+
return this._commitLifecycle(
|
|
932
|
+
{
|
|
933
|
+
operation: "register",
|
|
934
|
+
serviceId: normalizedServiceId,
|
|
935
|
+
...encoded,
|
|
936
|
+
priorKeyId: null,
|
|
937
|
+
priorPublicKeySpki: null,
|
|
938
|
+
reason: null,
|
|
939
|
+
...(normalizedAuthorization === null
|
|
940
|
+
? {}
|
|
941
|
+
: { authorization: normalizedAuthorization }),
|
|
942
|
+
},
|
|
943
|
+
snapshot.authority,
|
|
944
|
+
);
|
|
945
|
+
}
|
|
946
|
+
|
|
947
|
+
async rotateKey({
|
|
948
|
+
serviceId: inputServiceId,
|
|
949
|
+
priorKeyId,
|
|
950
|
+
publicKey,
|
|
951
|
+
reason: inputReason,
|
|
952
|
+
authorization = null,
|
|
953
|
+
} = {}) {
|
|
954
|
+
const normalizedServiceId = serviceId(inputServiceId);
|
|
955
|
+
const normalizedPriorKeyId = identifier(priorKeyId, "priorKeyId");
|
|
956
|
+
const encoded = encodePublicKey(publicKey);
|
|
957
|
+
const normalizedReason = reason(inputReason);
|
|
958
|
+
const normalizedAuthorization = normalizeAuthorizationLink(authorization);
|
|
959
|
+
const existing = await this._entry(
|
|
960
|
+
eventId({
|
|
961
|
+
operation: "rotate",
|
|
962
|
+
serviceId: normalizedServiceId,
|
|
963
|
+
keyId: encoded.keyId,
|
|
964
|
+
}),
|
|
965
|
+
);
|
|
966
|
+
if (existing) {
|
|
967
|
+
if (
|
|
968
|
+
existing.record.operation !== "rotate" ||
|
|
969
|
+
existing.record.serviceId !== normalizedServiceId ||
|
|
970
|
+
existing.record.priorKeyId !== normalizedPriorKeyId ||
|
|
971
|
+
existing.record.publicKeySpki !== encoded.publicKeySpki ||
|
|
972
|
+
existing.record.reason !== normalizedReason ||
|
|
973
|
+
canonical(existing.record.authorization ?? null) !==
|
|
974
|
+
canonical(normalizedAuthorization)
|
|
975
|
+
) {
|
|
976
|
+
corrupt("attestor rotation event identity was reused");
|
|
977
|
+
}
|
|
978
|
+
return Object.freeze({
|
|
979
|
+
authenticated: true,
|
|
980
|
+
durable: true,
|
|
981
|
+
recovered: true,
|
|
982
|
+
serviceId: normalizedServiceId,
|
|
983
|
+
keyId: encoded.keyId,
|
|
984
|
+
operation: "rotate",
|
|
985
|
+
recordDigest: existing.record.recordDigest,
|
|
986
|
+
authorizationDigest:
|
|
987
|
+
existing.record.authorization?.authorizationDigest ?? null,
|
|
988
|
+
});
|
|
989
|
+
}
|
|
990
|
+
const snapshot = await this._stateSnapshot();
|
|
991
|
+
const state = snapshot.states.get(normalizedServiceId);
|
|
992
|
+
if (!state?.active || state.active.keyId !== normalizedPriorKeyId) {
|
|
993
|
+
throw new Error("attestor prior key is not currently active");
|
|
994
|
+
}
|
|
995
|
+
if (state.seen.has(encoded.keyId)) {
|
|
996
|
+
throw new Error("attestor keyId cannot be reused");
|
|
997
|
+
}
|
|
998
|
+
return this._commitLifecycle(
|
|
999
|
+
{
|
|
1000
|
+
operation: "rotate",
|
|
1001
|
+
serviceId: normalizedServiceId,
|
|
1002
|
+
...encoded,
|
|
1003
|
+
priorKeyId: state.active.keyId,
|
|
1004
|
+
priorPublicKeySpki: state.active.publicKeySpki,
|
|
1005
|
+
reason: normalizedReason,
|
|
1006
|
+
...(normalizedAuthorization === null
|
|
1007
|
+
? {}
|
|
1008
|
+
: { authorization: normalizedAuthorization }),
|
|
1009
|
+
},
|
|
1010
|
+
snapshot.authority,
|
|
1011
|
+
);
|
|
1012
|
+
}
|
|
1013
|
+
|
|
1014
|
+
async revokeKey({
|
|
1015
|
+
serviceId: inputServiceId,
|
|
1016
|
+
keyId,
|
|
1017
|
+
reason: inputReason,
|
|
1018
|
+
authorization = null,
|
|
1019
|
+
} = {}) {
|
|
1020
|
+
const normalizedServiceId = serviceId(inputServiceId);
|
|
1021
|
+
const normalizedKeyId = identifier(keyId, "keyId");
|
|
1022
|
+
const normalizedReason = reason(inputReason);
|
|
1023
|
+
const normalizedAuthorization = normalizeAuthorizationLink(authorization);
|
|
1024
|
+
const existing = await this._entry(
|
|
1025
|
+
eventId({
|
|
1026
|
+
operation: "revoke",
|
|
1027
|
+
serviceId: normalizedServiceId,
|
|
1028
|
+
keyId: normalizedKeyId,
|
|
1029
|
+
}),
|
|
1030
|
+
);
|
|
1031
|
+
if (existing) {
|
|
1032
|
+
if (
|
|
1033
|
+
existing.record.operation !== "revoke" ||
|
|
1034
|
+
existing.record.serviceId !== normalizedServiceId ||
|
|
1035
|
+
existing.record.reason !== normalizedReason ||
|
|
1036
|
+
canonical(existing.record.authorization ?? null) !==
|
|
1037
|
+
canonical(normalizedAuthorization)
|
|
1038
|
+
) {
|
|
1039
|
+
corrupt("attestor revocation event identity was reused");
|
|
1040
|
+
}
|
|
1041
|
+
return Object.freeze({
|
|
1042
|
+
authenticated: true,
|
|
1043
|
+
durable: true,
|
|
1044
|
+
recovered: true,
|
|
1045
|
+
serviceId: normalizedServiceId,
|
|
1046
|
+
keyId: normalizedKeyId,
|
|
1047
|
+
operation: "revoke",
|
|
1048
|
+
recordDigest: existing.record.recordDigest,
|
|
1049
|
+
authorizationDigest:
|
|
1050
|
+
existing.record.authorization?.authorizationDigest ?? null,
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
const snapshot = await this._stateSnapshot();
|
|
1054
|
+
const state = snapshot.states.get(normalizedServiceId);
|
|
1055
|
+
const trusted =
|
|
1056
|
+
state?.active?.keyId === normalizedKeyId
|
|
1057
|
+
? state.active
|
|
1058
|
+
: state?.retired.get(normalizedKeyId);
|
|
1059
|
+
if (!trusted) throw new Error("attestor key is not currently trusted");
|
|
1060
|
+
return this._commitLifecycle(
|
|
1061
|
+
{
|
|
1062
|
+
operation: "revoke",
|
|
1063
|
+
serviceId: normalizedServiceId,
|
|
1064
|
+
keyId: normalizedKeyId,
|
|
1065
|
+
publicKeySpki: trusted.publicKeySpki,
|
|
1066
|
+
priorKeyId: null,
|
|
1067
|
+
priorPublicKeySpki: null,
|
|
1068
|
+
reason: normalizedReason,
|
|
1069
|
+
...(normalizedAuthorization === null
|
|
1070
|
+
? {}
|
|
1071
|
+
: { authorization: normalizedAuthorization }),
|
|
1072
|
+
},
|
|
1073
|
+
snapshot.authority,
|
|
1074
|
+
);
|
|
1075
|
+
}
|
|
1076
|
+
|
|
1077
|
+
async _commitLifecycle(input, expectedHead) {
|
|
1078
|
+
const core = {
|
|
1079
|
+
schema:
|
|
1080
|
+
input.authorization === undefined
|
|
1081
|
+
? GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_RECORD_SCHEMA
|
|
1082
|
+
: GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZED_RECORD_SCHEMA,
|
|
1083
|
+
tenantId: this.descriptor.tenantId,
|
|
1084
|
+
...input,
|
|
1085
|
+
effectiveAt: this._timestamp(),
|
|
1086
|
+
};
|
|
1087
|
+
const record = freeze({
|
|
1088
|
+
...core,
|
|
1089
|
+
recordDigest: digestGovernedSkillSynthesisAttestorTrustRecord(core),
|
|
1090
|
+
});
|
|
1091
|
+
validateRecord(record, this.descriptor);
|
|
1092
|
+
const result = await this._appendRecord(record, expectedHead);
|
|
1093
|
+
return Object.freeze({
|
|
1094
|
+
...result,
|
|
1095
|
+
serviceId: record.serviceId,
|
|
1096
|
+
keyId: record.keyId,
|
|
1097
|
+
operation: record.operation,
|
|
1098
|
+
recordDigest: record.recordDigest,
|
|
1099
|
+
authorizationDigest: record.authorization?.authorizationDigest ?? null,
|
|
1100
|
+
});
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
createVerifier({ serviceId: inputServiceId } = {}) {
|
|
1104
|
+
const normalizedServiceId = serviceId(inputServiceId);
|
|
1105
|
+
const authority = this._verify();
|
|
1106
|
+
const descriptor = Object.freeze({
|
|
1107
|
+
schema: GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_VERIFIER_SCHEMA,
|
|
1108
|
+
tenantId: this.descriptor.tenantId,
|
|
1109
|
+
serviceId: normalizedServiceId,
|
|
1110
|
+
ledgerId: authority.ledgerId,
|
|
1111
|
+
epoch: authority.epoch,
|
|
1112
|
+
isolation: "durable-ledger-key-lifecycle",
|
|
1113
|
+
});
|
|
1114
|
+
const verifyAttestation = async (value) => {
|
|
1115
|
+
const input = verificationInput(value);
|
|
1116
|
+
if (!input) return false;
|
|
1117
|
+
const identity = getGovernedSkillSynthesisExternalAttestationIdentity(
|
|
1118
|
+
input.attestation,
|
|
1119
|
+
);
|
|
1120
|
+
if (!identity || identity.serviceId !== normalizedServiceId) return false;
|
|
1121
|
+
const snapshot = await this._stateSnapshot();
|
|
1122
|
+
const currentAuthority = snapshot.authority;
|
|
1123
|
+
if (
|
|
1124
|
+
currentAuthority.ledgerId !== descriptor.ledgerId ||
|
|
1125
|
+
currentAuthority.epoch !== descriptor.epoch
|
|
1126
|
+
) {
|
|
1127
|
+
return false;
|
|
1128
|
+
}
|
|
1129
|
+
let sequence = null;
|
|
1130
|
+
if (input.ledgerContext !== undefined) {
|
|
1131
|
+
try {
|
|
1132
|
+
exact(
|
|
1133
|
+
input.ledgerContext,
|
|
1134
|
+
new Set(["epoch", "ledgerId", "sequence"]),
|
|
1135
|
+
"attestor verification ledger context",
|
|
1136
|
+
);
|
|
1137
|
+
} catch {
|
|
1138
|
+
return false;
|
|
1139
|
+
}
|
|
1140
|
+
if (
|
|
1141
|
+
input.ledgerContext.ledgerId !== descriptor.ledgerId ||
|
|
1142
|
+
input.ledgerContext.epoch !== descriptor.epoch ||
|
|
1143
|
+
!Number.isSafeInteger(input.ledgerContext.sequence) ||
|
|
1144
|
+
input.ledgerContext.sequence < 1 ||
|
|
1145
|
+
input.ledgerContext.sequence > currentAuthority.sequence + 1
|
|
1146
|
+
) {
|
|
1147
|
+
return false;
|
|
1148
|
+
}
|
|
1149
|
+
sequence = input.ledgerContext.sequence;
|
|
1150
|
+
}
|
|
1151
|
+
const state = snapshot.states.get(normalizedServiceId);
|
|
1152
|
+
if (!state || state.revoked.has(identity.keyId)) return false;
|
|
1153
|
+
let trusted = null;
|
|
1154
|
+
if (state.active?.keyId === identity.keyId) {
|
|
1155
|
+
if (sequence === null || sequence >= state.active.registeredSequence) {
|
|
1156
|
+
trusted = state.active;
|
|
1157
|
+
}
|
|
1158
|
+
} else {
|
|
1159
|
+
const retired = state.retired.get(identity.keyId);
|
|
1160
|
+
if (
|
|
1161
|
+
sequence !== null &&
|
|
1162
|
+
retired &&
|
|
1163
|
+
sequence >= retired.registeredSequence &&
|
|
1164
|
+
sequence < retired.retiredSequence
|
|
1165
|
+
) {
|
|
1166
|
+
trusted = retired;
|
|
1167
|
+
}
|
|
1168
|
+
}
|
|
1169
|
+
if (!trusted) return false;
|
|
1170
|
+
return verifyGovernedSkillSynthesisExternalAttestation(
|
|
1171
|
+
{
|
|
1172
|
+
receiptDigest: input.receiptDigest,
|
|
1173
|
+
candidateDigest: input.candidateDigest,
|
|
1174
|
+
descriptor: input.descriptor,
|
|
1175
|
+
attestation: input.attestation,
|
|
1176
|
+
},
|
|
1177
|
+
{
|
|
1178
|
+
publicKey: decodePublicKey(trusted.publicKeySpki, trusted.keyId),
|
|
1179
|
+
},
|
|
1180
|
+
);
|
|
1181
|
+
};
|
|
1182
|
+
Object.freeze(verifyAttestation);
|
|
1183
|
+
const verifier = Object.freeze({ descriptor, verifyAttestation });
|
|
1184
|
+
VERIFIERS.add(verifier);
|
|
1185
|
+
return verifier;
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
|
|
1189
|
+
export function createGovernedSkillSynthesisAttestorTrustLedger(options) {
|
|
1190
|
+
return new GovernedSkillSynthesisAttestorTrustLedger(options);
|
|
1191
|
+
}
|
|
1192
|
+
|
|
1193
|
+
export function isGovernedSkillSynthesisAttestorTrustLedger(value) {
|
|
1194
|
+
return TRUST_LEDGERS.has(value);
|
|
1195
|
+
}
|
|
1196
|
+
|
|
1197
|
+
export function createGovernedSkillSynthesisAttestorTrustVerifier(
|
|
1198
|
+
options = {},
|
|
1199
|
+
) {
|
|
1200
|
+
exact(
|
|
1201
|
+
options,
|
|
1202
|
+
new Set([
|
|
1203
|
+
"artifactPorts",
|
|
1204
|
+
"descriptor",
|
|
1205
|
+
"ledger",
|
|
1206
|
+
"ledgerArtifactResolver",
|
|
1207
|
+
"serviceId",
|
|
1208
|
+
]),
|
|
1209
|
+
"attestor trust verifier options",
|
|
1210
|
+
);
|
|
1211
|
+
const data = Object.getOwnPropertyDescriptors(options);
|
|
1212
|
+
return new GovernedSkillSynthesisAttestorTrustLedger({
|
|
1213
|
+
descriptor: data.descriptor.value,
|
|
1214
|
+
artifactPorts: data.artifactPorts.value,
|
|
1215
|
+
ledger: data.ledger.value,
|
|
1216
|
+
ledgerArtifactResolver: data.ledgerArtifactResolver.value,
|
|
1217
|
+
}).createVerifier({ serviceId: data.serviceId.value });
|
|
1218
|
+
}
|
|
1219
|
+
|
|
1220
|
+
export function isGovernedSkillSynthesisAttestorTrustVerifier(value) {
|
|
1221
|
+
return VERIFIERS.has(value);
|
|
1222
|
+
}
|