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,1082 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createHash,
|
|
3
|
+
createPrivateKey,
|
|
4
|
+
createPublicKey,
|
|
5
|
+
sign,
|
|
6
|
+
verify,
|
|
7
|
+
} from "node:crypto";
|
|
8
|
+
import { types as utilTypes } from "node:util";
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA,
|
|
12
|
+
isEvolutionLedgerArtifactResolver,
|
|
13
|
+
} from "./evolution-artifact-ports.js";
|
|
14
|
+
import {
|
|
15
|
+
EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA,
|
|
16
|
+
EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA,
|
|
17
|
+
} from "./evolution-ledger.js";
|
|
18
|
+
import { isGovernedSkillSynthesisAttestorTrustLedger } from "./governed-skill-synthesis-attestor-trust-ledger.js";
|
|
19
|
+
|
|
20
|
+
export const GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_OPERATION_REQUEST_SCHEMA =
|
|
21
|
+
"chainlesschain.governed-skill-synthesis-attestor-trust-operation-request/v1";
|
|
22
|
+
export const GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_APPROVAL_SCHEMA =
|
|
23
|
+
"chainlesschain.governed-skill-synthesis-attestor-trust-approval/v1";
|
|
24
|
+
export const GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZATION_SCHEMA =
|
|
25
|
+
"chainlesschain.governed-skill-synthesis-attestor-trust-authorization/v1";
|
|
26
|
+
export const GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_OPERATIONS_SCHEMA =
|
|
27
|
+
"chainlesschain.governed-skill-synthesis-attestor-trust-operations/v1";
|
|
28
|
+
export const GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZATION_RECORD_SCHEMA =
|
|
29
|
+
"chainlesschain.governed-skill-synthesis-attestor-trust-authorization-record/v1";
|
|
30
|
+
export const GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZATION_LINK_SCHEMA =
|
|
31
|
+
"chainlesschain.governed-skill-synthesis-attestor-trust-authorization-link/v1";
|
|
32
|
+
export const GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZATION_COMMITTED_EVENT =
|
|
33
|
+
"learning.skill-synthesis-attestor-trust.authorization.committed";
|
|
34
|
+
|
|
35
|
+
const ARTIFACT_TYPE = "governed-skill-synthesis-attestor-trust-authorization";
|
|
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 KEY_ID = /^key:ed25519:[a-f0-9]{64}$/u;
|
|
40
|
+
const MAX_OPERATORS = 16;
|
|
41
|
+
const MAX_TTL_MS = 15 * 60 * 1000;
|
|
42
|
+
const MAX_FUTURE_SKEW_MS = 30 * 1000;
|
|
43
|
+
const OPERATIONS = new WeakSet();
|
|
44
|
+
const ISSUERS = new WeakSet();
|
|
45
|
+
const REQUEST_KEYS = new Set([
|
|
46
|
+
"expiresAt",
|
|
47
|
+
"keyId",
|
|
48
|
+
"operation",
|
|
49
|
+
"policyDigest",
|
|
50
|
+
"priorKeyId",
|
|
51
|
+
"publicKeySpki",
|
|
52
|
+
"reason",
|
|
53
|
+
"requestDigest",
|
|
54
|
+
"requestedAt",
|
|
55
|
+
"requiredApprovals",
|
|
56
|
+
"schema",
|
|
57
|
+
"serviceId",
|
|
58
|
+
"tenantId",
|
|
59
|
+
]);
|
|
60
|
+
const APPROVAL_KEYS = new Set([
|
|
61
|
+
"approvedAt",
|
|
62
|
+
"attestation",
|
|
63
|
+
"automated",
|
|
64
|
+
"expiresAt",
|
|
65
|
+
"operatorId",
|
|
66
|
+
"policyDigest",
|
|
67
|
+
"receiptDigest",
|
|
68
|
+
"requestDigest",
|
|
69
|
+
"schema",
|
|
70
|
+
"tenantId",
|
|
71
|
+
]);
|
|
72
|
+
const ATTESTATION_KEYS = new Set(["algorithm", "keyId", "value"]);
|
|
73
|
+
const AUTHORIZATION_KEYS = new Set([
|
|
74
|
+
"approvalReceiptDigests",
|
|
75
|
+
"authorizationDigest",
|
|
76
|
+
"authorizedAt",
|
|
77
|
+
"operatorIds",
|
|
78
|
+
"policyDigest",
|
|
79
|
+
"requestDigest",
|
|
80
|
+
"requiredApprovals",
|
|
81
|
+
"schema",
|
|
82
|
+
"tenantId",
|
|
83
|
+
]);
|
|
84
|
+
const AUTHORIZATION_RECORD_KEYS = new Set([
|
|
85
|
+
"approvals",
|
|
86
|
+
"authorization",
|
|
87
|
+
"recordDigest",
|
|
88
|
+
"request",
|
|
89
|
+
"schema",
|
|
90
|
+
"tenantId",
|
|
91
|
+
]);
|
|
92
|
+
|
|
93
|
+
function canonical(value, seen = new Set()) {
|
|
94
|
+
if (value === null || typeof value === "boolean") {
|
|
95
|
+
return JSON.stringify(value);
|
|
96
|
+
}
|
|
97
|
+
if (typeof value === "number") {
|
|
98
|
+
if (!Number.isFinite(value)) throw new TypeError("value is not finite");
|
|
99
|
+
return JSON.stringify(value);
|
|
100
|
+
}
|
|
101
|
+
if (typeof value === "string") return JSON.stringify(value);
|
|
102
|
+
if (!value || typeof value !== "object" || seen.has(value)) {
|
|
103
|
+
throw new TypeError("value must be acyclic JSON");
|
|
104
|
+
}
|
|
105
|
+
if (utilTypes.isProxy(value))
|
|
106
|
+
throw new TypeError("value must not be a Proxy");
|
|
107
|
+
seen.add(value);
|
|
108
|
+
let output;
|
|
109
|
+
if (Array.isArray(value)) {
|
|
110
|
+
output = `[${value.map((entry) => canonical(entry, seen)).join(",")}]`;
|
|
111
|
+
} else {
|
|
112
|
+
if (Object.getPrototypeOf(value) !== Object.prototype) {
|
|
113
|
+
throw new TypeError("value must use plain objects");
|
|
114
|
+
}
|
|
115
|
+
output = `{${Object.keys(value)
|
|
116
|
+
.sort()
|
|
117
|
+
.map((key) => `${JSON.stringify(key)}:${canonical(value[key], seen)}`)
|
|
118
|
+
.join(",")}}`;
|
|
119
|
+
}
|
|
120
|
+
seen.delete(value);
|
|
121
|
+
return output;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function hash(domain, value) {
|
|
125
|
+
return `sha256:${createHash("sha256")
|
|
126
|
+
.update(domain)
|
|
127
|
+
.update("\0")
|
|
128
|
+
.update(canonical(value))
|
|
129
|
+
.digest("hex")}`;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function exact(value, keys, label) {
|
|
133
|
+
if (
|
|
134
|
+
!value ||
|
|
135
|
+
typeof value !== "object" ||
|
|
136
|
+
Array.isArray(value) ||
|
|
137
|
+
utilTypes.isProxy(value) ||
|
|
138
|
+
Object.getPrototypeOf(value) !== Object.prototype ||
|
|
139
|
+
Reflect.ownKeys(value).length !== keys.size ||
|
|
140
|
+
Reflect.ownKeys(value).some((key) => {
|
|
141
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, key);
|
|
142
|
+
return (
|
|
143
|
+
typeof key !== "string" ||
|
|
144
|
+
!keys.has(key) ||
|
|
145
|
+
!descriptor ||
|
|
146
|
+
!("value" in descriptor) ||
|
|
147
|
+
descriptor.enumerable !== true
|
|
148
|
+
);
|
|
149
|
+
})
|
|
150
|
+
) {
|
|
151
|
+
throw new TypeError(`${label} is invalid`);
|
|
152
|
+
}
|
|
153
|
+
return value;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function dense(value, label, maximum = MAX_OPERATORS) {
|
|
157
|
+
if (
|
|
158
|
+
!Array.isArray(value) ||
|
|
159
|
+
utilTypes.isProxy(value) ||
|
|
160
|
+
Object.getPrototypeOf(value) !== Array.prototype ||
|
|
161
|
+
value.length < 1 ||
|
|
162
|
+
value.length > maximum ||
|
|
163
|
+
Reflect.ownKeys(value).length !== value.length + 1
|
|
164
|
+
) {
|
|
165
|
+
throw new TypeError(`${label} must be a bounded dense array`);
|
|
166
|
+
}
|
|
167
|
+
for (let index = 0; index < value.length; index += 1) {
|
|
168
|
+
const descriptor = Object.getOwnPropertyDescriptor(value, String(index));
|
|
169
|
+
if (!descriptor || !("value" in descriptor) || !descriptor.enumerable) {
|
|
170
|
+
throw new TypeError(`${label} must contain data entries`);
|
|
171
|
+
}
|
|
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 timestamp(value, label) {
|
|
184
|
+
const milliseconds = Date.parse(value);
|
|
185
|
+
if (
|
|
186
|
+
typeof value !== "string" ||
|
|
187
|
+
!Number.isFinite(milliseconds) ||
|
|
188
|
+
new Date(milliseconds).toISOString() !== value
|
|
189
|
+
) {
|
|
190
|
+
throw new TypeError(`${label} is invalid`);
|
|
191
|
+
}
|
|
192
|
+
return milliseconds;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function clock(now, label) {
|
|
196
|
+
const value = Number(now());
|
|
197
|
+
if (!Number.isFinite(value)) throw new TypeError(`${label} clock is invalid`);
|
|
198
|
+
return value;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function keyObject(value, type, label) {
|
|
202
|
+
let key;
|
|
203
|
+
try {
|
|
204
|
+
if (utilTypes.isKeyObject(value)) key = value;
|
|
205
|
+
else
|
|
206
|
+
key =
|
|
207
|
+
type === "private" ? createPrivateKey(value) : createPublicKey(value);
|
|
208
|
+
} catch {
|
|
209
|
+
throw new TypeError(`${label} must be Ed25519`);
|
|
210
|
+
}
|
|
211
|
+
if (key.type !== type || key.asymmetricKeyType !== "ed25519") {
|
|
212
|
+
throw new TypeError(`${label} must be Ed25519`);
|
|
213
|
+
}
|
|
214
|
+
return key;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function encodedPublicKey(value, label) {
|
|
218
|
+
const key = keyObject(value, "public", label);
|
|
219
|
+
const bytes = key.export({ type: "spki", format: "der" });
|
|
220
|
+
return Object.freeze({
|
|
221
|
+
key,
|
|
222
|
+
keyId: `key:ed25519:${createHash("sha256").update(bytes).digest("hex")}`,
|
|
223
|
+
publicKeySpki: bytes.toString("base64url"),
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function decodePublicKey(publicKeySpki, expectedKeyId, label) {
|
|
228
|
+
if (typeof publicKeySpki !== "string") {
|
|
229
|
+
throw new TypeError(`${label} SPKI is invalid`);
|
|
230
|
+
}
|
|
231
|
+
const bytes = Buffer.from(publicKeySpki, "base64url");
|
|
232
|
+
if (bytes.toString("base64url") !== publicKeySpki) {
|
|
233
|
+
throw new TypeError(`${label} SPKI is not canonical`);
|
|
234
|
+
}
|
|
235
|
+
const encoded = encodedPublicKey(
|
|
236
|
+
{ key: bytes, type: "spki", format: "der" },
|
|
237
|
+
label,
|
|
238
|
+
);
|
|
239
|
+
if (encoded.keyId !== expectedKeyId) {
|
|
240
|
+
throw new Error(`${label} keyId does not bind its SPKI`);
|
|
241
|
+
}
|
|
242
|
+
return encoded.key;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function freeze(value) {
|
|
246
|
+
if (value && typeof value === "object" && !Object.isFrozen(value)) {
|
|
247
|
+
for (const child of Object.values(value)) freeze(child);
|
|
248
|
+
Object.freeze(value);
|
|
249
|
+
}
|
|
250
|
+
return value;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function capture(owner, method, label) {
|
|
254
|
+
if (
|
|
255
|
+
!owner ||
|
|
256
|
+
typeof owner !== "object" ||
|
|
257
|
+
utilTypes.isProxy(owner) ||
|
|
258
|
+
typeof owner[method] !== "function"
|
|
259
|
+
) {
|
|
260
|
+
throw new TypeError(`${label}.${method}() is required`);
|
|
261
|
+
}
|
|
262
|
+
return (...args) => Reflect.apply(owner[method], owner, args);
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
function same(left, right) {
|
|
266
|
+
return canonical(left) === canonical(right);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
function buildAuthorization(request, approvals) {
|
|
270
|
+
const accepted = [...approvals].sort((left, right) =>
|
|
271
|
+
left.operatorId.localeCompare(right.operatorId),
|
|
272
|
+
);
|
|
273
|
+
const authorizationCore = {
|
|
274
|
+
schema: GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZATION_SCHEMA,
|
|
275
|
+
tenantId: request.tenantId,
|
|
276
|
+
requestDigest: request.requestDigest,
|
|
277
|
+
policyDigest: request.policyDigest,
|
|
278
|
+
requiredApprovals: request.requiredApprovals,
|
|
279
|
+
operatorIds: accepted.map((receipt) => receipt.operatorId),
|
|
280
|
+
approvalReceiptDigests: accepted.map((receipt) => receipt.receiptDigest),
|
|
281
|
+
authorizedAt: accepted
|
|
282
|
+
.map((receipt) => receipt.approvedAt)
|
|
283
|
+
.sort()
|
|
284
|
+
.at(-1),
|
|
285
|
+
};
|
|
286
|
+
return freeze({
|
|
287
|
+
...authorizationCore,
|
|
288
|
+
authorizationDigest: hash(
|
|
289
|
+
GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZATION_SCHEMA,
|
|
290
|
+
authorizationCore,
|
|
291
|
+
),
|
|
292
|
+
});
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
function authorizationRecordCore(value) {
|
|
296
|
+
const core = structuredClone(value);
|
|
297
|
+
delete core.recordDigest;
|
|
298
|
+
return core;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
export function digestGovernedSkillSynthesisAttestorTrustAuthorizationRecord(
|
|
302
|
+
value,
|
|
303
|
+
) {
|
|
304
|
+
return hash(
|
|
305
|
+
GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZATION_RECORD_SCHEMA,
|
|
306
|
+
authorizationRecordCore(value),
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
function validateAuthorizationRecord(record, descriptor, operators) {
|
|
311
|
+
exact(
|
|
312
|
+
record,
|
|
313
|
+
AUTHORIZATION_RECORD_KEYS,
|
|
314
|
+
"attestor trust authorization record",
|
|
315
|
+
);
|
|
316
|
+
exact(
|
|
317
|
+
record.authorization,
|
|
318
|
+
AUTHORIZATION_KEYS,
|
|
319
|
+
"attestor trust authorization",
|
|
320
|
+
);
|
|
321
|
+
const authorizedAt = timestamp(
|
|
322
|
+
record.authorization.authorizedAt,
|
|
323
|
+
"authorization authorizedAt",
|
|
324
|
+
);
|
|
325
|
+
validateRequest(record.request, descriptor, authorizedAt);
|
|
326
|
+
dense(
|
|
327
|
+
record.approvals,
|
|
328
|
+
"attestor trust authorization approvals",
|
|
329
|
+
operators.size,
|
|
330
|
+
);
|
|
331
|
+
if (
|
|
332
|
+
record.schema !==
|
|
333
|
+
GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZATION_RECORD_SCHEMA ||
|
|
334
|
+
record.tenantId !== descriptor.tenantId ||
|
|
335
|
+
record.approvals.length !== descriptor.requiredApprovals ||
|
|
336
|
+
record.recordDigest !==
|
|
337
|
+
digestGovernedSkillSynthesisAttestorTrustAuthorizationRecord(record)
|
|
338
|
+
) {
|
|
339
|
+
throw new Error("attestor trust authorization record is invalid");
|
|
340
|
+
}
|
|
341
|
+
const seenOperators = new Set();
|
|
342
|
+
const seenKeys = new Set();
|
|
343
|
+
for (const receipt of record.approvals) {
|
|
344
|
+
const operatorId = receipt?.operatorId;
|
|
345
|
+
const operator = operators.get(operatorId);
|
|
346
|
+
if (
|
|
347
|
+
!operator ||
|
|
348
|
+
seenOperators.has(operatorId) ||
|
|
349
|
+
seenKeys.has(operator.keyId)
|
|
350
|
+
) {
|
|
351
|
+
throw new Error("stored attestor trust approvals are not independent");
|
|
352
|
+
}
|
|
353
|
+
normalizeApproval(
|
|
354
|
+
receipt,
|
|
355
|
+
record.request,
|
|
356
|
+
{ ...operator, operatorId },
|
|
357
|
+
authorizedAt,
|
|
358
|
+
);
|
|
359
|
+
seenOperators.add(operatorId);
|
|
360
|
+
seenKeys.add(operator.keyId);
|
|
361
|
+
}
|
|
362
|
+
const expected = buildAuthorization(record.request, record.approvals);
|
|
363
|
+
if (!same(expected, record.authorization)) {
|
|
364
|
+
throw new Error("stored attestor trust authorization is not exactly bound");
|
|
365
|
+
}
|
|
366
|
+
return freeze(structuredClone(record));
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
function parseAuthorizationArtifact(resolution, descriptor, operators) {
|
|
370
|
+
if (
|
|
371
|
+
resolution?.schema !== EVOLUTION_ARTIFACT_RESOLUTION_SCHEMA ||
|
|
372
|
+
resolution.authenticated !== true ||
|
|
373
|
+
resolution.found !== true ||
|
|
374
|
+
!DIGEST.test(resolution.digest ?? "") ||
|
|
375
|
+
!DIGEST.test(resolution.receiptDigest ?? "") ||
|
|
376
|
+
!Buffer.isBuffer(resolution.bytes)
|
|
377
|
+
) {
|
|
378
|
+
throw new Error("attestor trust authorization artifact is incomplete");
|
|
379
|
+
}
|
|
380
|
+
let artifact;
|
|
381
|
+
try {
|
|
382
|
+
artifact = JSON.parse(resolution.bytes.toString("utf8"));
|
|
383
|
+
} catch {
|
|
384
|
+
throw new Error("attestor trust authorization artifact is not JSON");
|
|
385
|
+
}
|
|
386
|
+
if (
|
|
387
|
+
artifact?.schema !== EVOLUTION_DURABLE_ARTIFACT_RECORD_SCHEMA ||
|
|
388
|
+
artifact.tenantId !== descriptor.artifactTenantId ||
|
|
389
|
+
artifact.audience !== descriptor.audience ||
|
|
390
|
+
artifact.purpose !== descriptor.purpose ||
|
|
391
|
+
artifact.retention !== "ledger" ||
|
|
392
|
+
artifact.type !== ARTIFACT_TYPE
|
|
393
|
+
) {
|
|
394
|
+
throw new Error("attestor trust authorization artifact binding is invalid");
|
|
395
|
+
}
|
|
396
|
+
return validateAuthorizationRecord(artifact.value, descriptor, operators);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
function requestCore(value) {
|
|
400
|
+
const core = structuredClone(value);
|
|
401
|
+
delete core.requestDigest;
|
|
402
|
+
return core;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export function digestGovernedSkillSynthesisAttestorTrustOperationRequest(
|
|
406
|
+
value,
|
|
407
|
+
) {
|
|
408
|
+
return hash(
|
|
409
|
+
GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_OPERATION_REQUEST_SCHEMA,
|
|
410
|
+
requestCore(value),
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function approvalCore(value) {
|
|
415
|
+
const core = structuredClone(value);
|
|
416
|
+
delete core.receiptDigest;
|
|
417
|
+
delete core.attestation;
|
|
418
|
+
return core;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
export function digestGovernedSkillSynthesisAttestorTrustApproval(value) {
|
|
422
|
+
return hash(
|
|
423
|
+
GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_APPROVAL_SCHEMA,
|
|
424
|
+
approvalCore(value),
|
|
425
|
+
);
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export function governedSkillSynthesisAttestorTrustApprovalMessage(
|
|
429
|
+
receiptDigest,
|
|
430
|
+
) {
|
|
431
|
+
if (!DIGEST.test(receiptDigest ?? "")) {
|
|
432
|
+
throw new TypeError("attestor trust approval receiptDigest is invalid");
|
|
433
|
+
}
|
|
434
|
+
return Buffer.from(
|
|
435
|
+
`chainlesschain.governed-skill-synthesis-attestor-trust-approval-signature/v1\0${receiptDigest}`,
|
|
436
|
+
"utf8",
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
function validateRequest(value, expected, currentTime) {
|
|
441
|
+
exact(value, REQUEST_KEYS, "attestor trust operation request");
|
|
442
|
+
const requestedAt = timestamp(value.requestedAt, "request requestedAt");
|
|
443
|
+
const expiresAt = timestamp(value.expiresAt, "request expiresAt");
|
|
444
|
+
if (
|
|
445
|
+
value.schema !==
|
|
446
|
+
GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_OPERATION_REQUEST_SCHEMA ||
|
|
447
|
+
value.tenantId !== expected.tenantId ||
|
|
448
|
+
!DIGEST.test(value.policyDigest ?? "") ||
|
|
449
|
+
value.policyDigest !== expected.policyDigest ||
|
|
450
|
+
!Number.isSafeInteger(value.requiredApprovals) ||
|
|
451
|
+
value.requiredApprovals < 1 ||
|
|
452
|
+
value.requiredApprovals > MAX_OPERATORS ||
|
|
453
|
+
value.requiredApprovals !== expected.requiredApprovals ||
|
|
454
|
+
!["register", "rotate", "revoke"].includes(value.operation) ||
|
|
455
|
+
!SERVICE_ID.test(value.serviceId ?? "") ||
|
|
456
|
+
!KEY_ID.test(value.keyId ?? "") ||
|
|
457
|
+
requestedAt > currentTime + MAX_FUTURE_SKEW_MS ||
|
|
458
|
+
expiresAt <= currentTime ||
|
|
459
|
+
expiresAt <= requestedAt ||
|
|
460
|
+
expiresAt > requestedAt + expected.requestTtlMs ||
|
|
461
|
+
value.requestDigest !==
|
|
462
|
+
digestGovernedSkillSynthesisAttestorTrustOperationRequest(value)
|
|
463
|
+
) {
|
|
464
|
+
throw new Error("attestor trust operation request is not authorized");
|
|
465
|
+
}
|
|
466
|
+
if (value.operation === "register") {
|
|
467
|
+
if (
|
|
468
|
+
value.priorKeyId !== null ||
|
|
469
|
+
value.reason !== null ||
|
|
470
|
+
typeof value.publicKeySpki !== "string"
|
|
471
|
+
) {
|
|
472
|
+
throw new Error("attestor trust registration request is invalid");
|
|
473
|
+
}
|
|
474
|
+
decodePublicKey(value.publicKeySpki, value.keyId, "attestor");
|
|
475
|
+
} else if (value.operation === "rotate") {
|
|
476
|
+
if (
|
|
477
|
+
!KEY_ID.test(value.priorKeyId ?? "") ||
|
|
478
|
+
value.priorKeyId === value.keyId ||
|
|
479
|
+
typeof value.publicKeySpki !== "string" ||
|
|
480
|
+
typeof value.reason !== "string" ||
|
|
481
|
+
value.reason.trim() !== value.reason ||
|
|
482
|
+
value.reason.length < 1 ||
|
|
483
|
+
value.reason.length > 2048
|
|
484
|
+
) {
|
|
485
|
+
throw new Error("attestor trust rotation request is invalid");
|
|
486
|
+
}
|
|
487
|
+
decodePublicKey(value.publicKeySpki, value.keyId, "attestor");
|
|
488
|
+
} else if (
|
|
489
|
+
value.priorKeyId !== null ||
|
|
490
|
+
value.publicKeySpki !== null ||
|
|
491
|
+
typeof value.reason !== "string" ||
|
|
492
|
+
value.reason.trim() !== value.reason ||
|
|
493
|
+
value.reason.length < 1 ||
|
|
494
|
+
value.reason.length > 2048
|
|
495
|
+
) {
|
|
496
|
+
throw new Error("attestor trust revocation request is invalid");
|
|
497
|
+
}
|
|
498
|
+
return value;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
function normalizeOperators(value, tenantId) {
|
|
502
|
+
dense(value, "operatorIdentities");
|
|
503
|
+
const operators = new Map();
|
|
504
|
+
const keys = new Set();
|
|
505
|
+
for (const identity of value) {
|
|
506
|
+
exact(
|
|
507
|
+
identity,
|
|
508
|
+
new Set(["operatorId", "publicKey", "tenantId"]),
|
|
509
|
+
"operator identity",
|
|
510
|
+
);
|
|
511
|
+
if (identity.tenantId !== tenantId) {
|
|
512
|
+
throw new Error("operator identity crossed its tenant boundary");
|
|
513
|
+
}
|
|
514
|
+
const operatorId = identifier(identity.operatorId, "operatorId");
|
|
515
|
+
const encoded = encodedPublicKey(identity.publicKey, "operator publicKey");
|
|
516
|
+
if (operators.has(operatorId) || keys.has(encoded.keyId)) {
|
|
517
|
+
throw new Error("operator identity or key is duplicated");
|
|
518
|
+
}
|
|
519
|
+
keys.add(encoded.keyId);
|
|
520
|
+
operators.set(
|
|
521
|
+
operatorId,
|
|
522
|
+
Object.freeze({ keyId: encoded.keyId, publicKey: encoded.key }),
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
return operators;
|
|
526
|
+
}
|
|
527
|
+
|
|
528
|
+
function prepareInput(input, descriptor, now) {
|
|
529
|
+
if (
|
|
530
|
+
!input ||
|
|
531
|
+
typeof input !== "object" ||
|
|
532
|
+
Array.isArray(input) ||
|
|
533
|
+
utilTypes.isProxy(input) ||
|
|
534
|
+
Object.getPrototypeOf(input) !== Object.prototype
|
|
535
|
+
) {
|
|
536
|
+
throw new TypeError("attestor trust operation input is invalid");
|
|
537
|
+
}
|
|
538
|
+
const operation = input.operation;
|
|
539
|
+
const serviceId = input.serviceId;
|
|
540
|
+
if (
|
|
541
|
+
!["register", "rotate", "revoke"].includes(operation) ||
|
|
542
|
+
!SERVICE_ID.test(serviceId ?? "")
|
|
543
|
+
) {
|
|
544
|
+
throw new TypeError("attestor trust operation is invalid");
|
|
545
|
+
}
|
|
546
|
+
let keyId;
|
|
547
|
+
let publicKeySpki;
|
|
548
|
+
let priorKeyId = null;
|
|
549
|
+
let reason = null;
|
|
550
|
+
if (operation === "register" || operation === "rotate") {
|
|
551
|
+
const allowed =
|
|
552
|
+
operation === "register"
|
|
553
|
+
? new Set(["operation", "publicKey", "serviceId"])
|
|
554
|
+
: new Set([
|
|
555
|
+
"operation",
|
|
556
|
+
"priorKeyId",
|
|
557
|
+
"publicKey",
|
|
558
|
+
"reason",
|
|
559
|
+
"serviceId",
|
|
560
|
+
]);
|
|
561
|
+
exact(input, allowed, "attestor trust operation input");
|
|
562
|
+
const encoded = encodedPublicKey(input.publicKey, "attestor publicKey");
|
|
563
|
+
keyId = encoded.keyId;
|
|
564
|
+
publicKeySpki = encoded.publicKeySpki;
|
|
565
|
+
if (operation === "rotate") {
|
|
566
|
+
priorKeyId = input.priorKeyId;
|
|
567
|
+
reason = input.reason;
|
|
568
|
+
if (
|
|
569
|
+
!KEY_ID.test(priorKeyId ?? "") ||
|
|
570
|
+
priorKeyId === keyId ||
|
|
571
|
+
typeof reason !== "string" ||
|
|
572
|
+
reason.trim() !== reason ||
|
|
573
|
+
reason.length < 1 ||
|
|
574
|
+
reason.length > 2048
|
|
575
|
+
) {
|
|
576
|
+
throw new TypeError("attestor trust rotation input is invalid");
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
} else {
|
|
580
|
+
exact(
|
|
581
|
+
input,
|
|
582
|
+
new Set(["keyId", "operation", "reason", "serviceId"]),
|
|
583
|
+
"attestor trust operation input",
|
|
584
|
+
);
|
|
585
|
+
keyId = input.keyId;
|
|
586
|
+
publicKeySpki = null;
|
|
587
|
+
reason = input.reason;
|
|
588
|
+
if (
|
|
589
|
+
!KEY_ID.test(keyId ?? "") ||
|
|
590
|
+
typeof reason !== "string" ||
|
|
591
|
+
reason.trim() !== reason ||
|
|
592
|
+
reason.length < 1 ||
|
|
593
|
+
reason.length > 2048
|
|
594
|
+
) {
|
|
595
|
+
throw new TypeError("attestor trust revocation input is invalid");
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
const requestedAt = new Date(now).toISOString();
|
|
599
|
+
const core = {
|
|
600
|
+
schema: GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_OPERATION_REQUEST_SCHEMA,
|
|
601
|
+
tenantId: descriptor.tenantId,
|
|
602
|
+
serviceId,
|
|
603
|
+
operation,
|
|
604
|
+
keyId,
|
|
605
|
+
publicKeySpki,
|
|
606
|
+
priorKeyId,
|
|
607
|
+
reason,
|
|
608
|
+
policyDigest: descriptor.policyDigest,
|
|
609
|
+
requiredApprovals: descriptor.requiredApprovals,
|
|
610
|
+
requestedAt,
|
|
611
|
+
expiresAt: new Date(now + descriptor.requestTtlMs).toISOString(),
|
|
612
|
+
};
|
|
613
|
+
return freeze({
|
|
614
|
+
...core,
|
|
615
|
+
requestDigest:
|
|
616
|
+
digestGovernedSkillSynthesisAttestorTrustOperationRequest(core),
|
|
617
|
+
});
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
function normalizeApproval(receipt, request, operator, currentTime) {
|
|
621
|
+
exact(receipt, APPROVAL_KEYS, "attestor trust approval");
|
|
622
|
+
const attestation = exact(
|
|
623
|
+
receipt.attestation,
|
|
624
|
+
ATTESTATION_KEYS,
|
|
625
|
+
"attestor trust approval attestation",
|
|
626
|
+
);
|
|
627
|
+
const approvedAt = timestamp(receipt.approvedAt, "approval approvedAt");
|
|
628
|
+
const expiresAt = timestamp(receipt.expiresAt, "approval expiresAt");
|
|
629
|
+
if (
|
|
630
|
+
receipt.schema !==
|
|
631
|
+
GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_APPROVAL_SCHEMA ||
|
|
632
|
+
receipt.tenantId !== request.tenantId ||
|
|
633
|
+
receipt.operatorId !== operator.operatorId ||
|
|
634
|
+
receipt.automated !== false ||
|
|
635
|
+
receipt.requestDigest !== request.requestDigest ||
|
|
636
|
+
receipt.policyDigest !== request.policyDigest ||
|
|
637
|
+
approvedAt < Date.parse(request.requestedAt) - MAX_FUTURE_SKEW_MS ||
|
|
638
|
+
approvedAt > currentTime + MAX_FUTURE_SKEW_MS ||
|
|
639
|
+
approvedAt >= expiresAt ||
|
|
640
|
+
expiresAt !== Date.parse(request.expiresAt) ||
|
|
641
|
+
expiresAt <= currentTime ||
|
|
642
|
+
receipt.receiptDigest !==
|
|
643
|
+
digestGovernedSkillSynthesisAttestorTrustApproval(receipt) ||
|
|
644
|
+
attestation.algorithm !== "Ed25519" ||
|
|
645
|
+
attestation.keyId !== operator.keyId ||
|
|
646
|
+
typeof attestation.value !== "string"
|
|
647
|
+
) {
|
|
648
|
+
throw new Error("attestor trust approval is not exactly bound");
|
|
649
|
+
}
|
|
650
|
+
const signature = Buffer.from(attestation.value, "base64url");
|
|
651
|
+
if (
|
|
652
|
+
signature.length !== 64 ||
|
|
653
|
+
signature.toString("base64url") !== attestation.value ||
|
|
654
|
+
!verify(
|
|
655
|
+
null,
|
|
656
|
+
governedSkillSynthesisAttestorTrustApprovalMessage(receipt.receiptDigest),
|
|
657
|
+
operator.publicKey,
|
|
658
|
+
signature,
|
|
659
|
+
)
|
|
660
|
+
) {
|
|
661
|
+
throw new Error("attestor trust approval signature is invalid");
|
|
662
|
+
}
|
|
663
|
+
return receipt;
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
export function createGovernedSkillSynthesisAttestorTrustOperatorApprovalIssuer({
|
|
667
|
+
tenantId: tenantIdInput,
|
|
668
|
+
operatorId: operatorIdInput,
|
|
669
|
+
privateKey: privateKeyInput,
|
|
670
|
+
now = Date.now,
|
|
671
|
+
} = {}) {
|
|
672
|
+
const tenantId = identifier(tenantIdInput, "tenantId");
|
|
673
|
+
const operatorId = identifier(operatorIdInput, "operatorId");
|
|
674
|
+
if (typeof now !== "function" || utilTypes.isProxy(now)) {
|
|
675
|
+
throw new TypeError("approval issuer clock must be a non-proxy function");
|
|
676
|
+
}
|
|
677
|
+
const privateKey = keyObject(
|
|
678
|
+
privateKeyInput,
|
|
679
|
+
"private",
|
|
680
|
+
"operator privateKey",
|
|
681
|
+
);
|
|
682
|
+
const publicKey = encodedPublicKey(
|
|
683
|
+
createPublicKey(privateKey),
|
|
684
|
+
"operator publicKey",
|
|
685
|
+
);
|
|
686
|
+
const issuer = Object.freeze({
|
|
687
|
+
tenantId,
|
|
688
|
+
operatorId,
|
|
689
|
+
keyId: publicKey.keyId,
|
|
690
|
+
issue(request) {
|
|
691
|
+
const currentTime = clock(now, "approval issuer");
|
|
692
|
+
validateRequest(
|
|
693
|
+
request,
|
|
694
|
+
{
|
|
695
|
+
tenantId,
|
|
696
|
+
policyDigest: request?.policyDigest,
|
|
697
|
+
requiredApprovals: request?.requiredApprovals,
|
|
698
|
+
requestTtlMs: MAX_TTL_MS,
|
|
699
|
+
},
|
|
700
|
+
currentTime,
|
|
701
|
+
);
|
|
702
|
+
const core = {
|
|
703
|
+
schema: GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_APPROVAL_SCHEMA,
|
|
704
|
+
tenantId,
|
|
705
|
+
operatorId,
|
|
706
|
+
automated: false,
|
|
707
|
+
requestDigest: request.requestDigest,
|
|
708
|
+
policyDigest: request.policyDigest,
|
|
709
|
+
approvedAt: new Date(currentTime).toISOString(),
|
|
710
|
+
expiresAt: request.expiresAt,
|
|
711
|
+
};
|
|
712
|
+
const receiptDigest =
|
|
713
|
+
digestGovernedSkillSynthesisAttestorTrustApproval(core);
|
|
714
|
+
return freeze({
|
|
715
|
+
...core,
|
|
716
|
+
receiptDigest,
|
|
717
|
+
attestation: {
|
|
718
|
+
algorithm: "Ed25519",
|
|
719
|
+
keyId: publicKey.keyId,
|
|
720
|
+
value: sign(
|
|
721
|
+
null,
|
|
722
|
+
governedSkillSynthesisAttestorTrustApprovalMessage(receiptDigest),
|
|
723
|
+
privateKey,
|
|
724
|
+
).toString("base64url"),
|
|
725
|
+
},
|
|
726
|
+
});
|
|
727
|
+
},
|
|
728
|
+
});
|
|
729
|
+
ISSUERS.add(issuer);
|
|
730
|
+
return issuer;
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
export function isGovernedSkillSynthesisAttestorTrustOperatorApprovalIssuer(
|
|
734
|
+
value,
|
|
735
|
+
) {
|
|
736
|
+
return ISSUERS.has(value);
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
export function createGovernedSkillSynthesisAttestorTrustOperations({
|
|
740
|
+
tenantId: tenantIdInput,
|
|
741
|
+
authorizationStreamId: authorizationStreamIdInput,
|
|
742
|
+
policyId: policyIdInput,
|
|
743
|
+
revision: revisionInput,
|
|
744
|
+
requiredApprovals: requiredApprovalsInput,
|
|
745
|
+
operatorIdentities,
|
|
746
|
+
trustLedger,
|
|
747
|
+
artifactPorts,
|
|
748
|
+
ledger,
|
|
749
|
+
ledgerArtifactResolver,
|
|
750
|
+
requestTtlMs: requestTtlMsInput = 5 * 60 * 1000,
|
|
751
|
+
now = Date.now,
|
|
752
|
+
} = {}) {
|
|
753
|
+
const tenantId = identifier(tenantIdInput, "tenantId");
|
|
754
|
+
const authorizationStreamId = identifier(
|
|
755
|
+
authorizationStreamIdInput,
|
|
756
|
+
"authorizationStreamId",
|
|
757
|
+
);
|
|
758
|
+
const policyId = identifier(policyIdInput, "policyId");
|
|
759
|
+
const revision = Number(revisionInput);
|
|
760
|
+
const requiredApprovals = Number(requiredApprovalsInput);
|
|
761
|
+
const requestTtlMs = Number(requestTtlMsInput);
|
|
762
|
+
if (!Number.isSafeInteger(revision) || revision < 1) {
|
|
763
|
+
throw new TypeError("attestor trust policy revision is invalid");
|
|
764
|
+
}
|
|
765
|
+
if (typeof now !== "function" || utilTypes.isProxy(now)) {
|
|
766
|
+
throw new TypeError("attestor trust operations clock is invalid");
|
|
767
|
+
}
|
|
768
|
+
if (
|
|
769
|
+
!Number.isSafeInteger(requestTtlMs) ||
|
|
770
|
+
requestTtlMs < 1_000 ||
|
|
771
|
+
requestTtlMs > MAX_TTL_MS
|
|
772
|
+
) {
|
|
773
|
+
throw new TypeError("attestor trust request TTL is invalid");
|
|
774
|
+
}
|
|
775
|
+
if (!isGovernedSkillSynthesisAttestorTrustLedger(trustLedger)) {
|
|
776
|
+
throw new TypeError(
|
|
777
|
+
"a branded attestor trust lifecycle writer is required",
|
|
778
|
+
);
|
|
779
|
+
}
|
|
780
|
+
if (trustLedger.descriptor?.tenantId !== tenantId) {
|
|
781
|
+
throw new Error(
|
|
782
|
+
"attestor trust lifecycle writer crossed its tenant boundary",
|
|
783
|
+
);
|
|
784
|
+
}
|
|
785
|
+
const put = capture(artifactPorts, "putCanonical", "artifactPorts");
|
|
786
|
+
const readLedger = capture(ledger, "read", "ledger");
|
|
787
|
+
const verifyLedger = capture(ledger, "verify", "ledger");
|
|
788
|
+
const appendLedger = capture(ledger, "appendDomainEvent", "ledger");
|
|
789
|
+
if (!isEvolutionLedgerArtifactResolver(ledgerArtifactResolver)) {
|
|
790
|
+
throw new TypeError(
|
|
791
|
+
"a branded EvolutionArtifactPorts ledger resolver is required",
|
|
792
|
+
);
|
|
793
|
+
}
|
|
794
|
+
const operators = normalizeOperators(operatorIdentities, tenantId);
|
|
795
|
+
if (
|
|
796
|
+
!Number.isSafeInteger(requiredApprovals) ||
|
|
797
|
+
requiredApprovals < 1 ||
|
|
798
|
+
requiredApprovals > operators.size
|
|
799
|
+
) {
|
|
800
|
+
throw new TypeError("requiredApprovals must fit the operator registry");
|
|
801
|
+
}
|
|
802
|
+
const policyCore = {
|
|
803
|
+
tenantId,
|
|
804
|
+
policyId,
|
|
805
|
+
revision,
|
|
806
|
+
requiredApprovals,
|
|
807
|
+
operators: [...operators.entries()]
|
|
808
|
+
.map(([operatorId, operator]) => ({
|
|
809
|
+
operatorId,
|
|
810
|
+
keyId: operator.keyId,
|
|
811
|
+
}))
|
|
812
|
+
.sort((left, right) => left.operatorId.localeCompare(right.operatorId)),
|
|
813
|
+
};
|
|
814
|
+
const descriptor = freeze({
|
|
815
|
+
schema: GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_OPERATIONS_SCHEMA,
|
|
816
|
+
artifactTenantId: trustLedger.descriptor.artifactTenantId,
|
|
817
|
+
audience: trustLedger.descriptor.audience,
|
|
818
|
+
purpose: trustLedger.descriptor.purpose,
|
|
819
|
+
authorizationStreamId,
|
|
820
|
+
...policyCore,
|
|
821
|
+
policyDigest: hash(
|
|
822
|
+
"chainlesschain.attestor-trust-operations-policy/v1",
|
|
823
|
+
policyCore,
|
|
824
|
+
),
|
|
825
|
+
requestTtlMs,
|
|
826
|
+
approvalMode:
|
|
827
|
+
requiredApprovals === 1 ? "single-operator" : "multi-operator",
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
const authorizationEvents = () => {
|
|
831
|
+
const events = readLedger();
|
|
832
|
+
if (!Array.isArray(events)) {
|
|
833
|
+
throw new Error("EvolutionLedger returned no authorization events");
|
|
834
|
+
}
|
|
835
|
+
return events.filter(
|
|
836
|
+
(event) =>
|
|
837
|
+
event.schema === EVOLUTION_LEDGER_DOMAIN_EVENT_SCHEMA &&
|
|
838
|
+
event.tenantId === tenantId &&
|
|
839
|
+
event.correlationId === authorizationStreamId &&
|
|
840
|
+
event.type ===
|
|
841
|
+
GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZATION_COMMITTED_EVENT,
|
|
842
|
+
);
|
|
843
|
+
};
|
|
844
|
+
|
|
845
|
+
const resolveAuthorizationEvent = async (event) => {
|
|
846
|
+
const authority = verifyLedger();
|
|
847
|
+
const resolution = await ledgerArtifactResolver({
|
|
848
|
+
epoch: authority.epoch,
|
|
849
|
+
ledgerId: authority.ledgerId,
|
|
850
|
+
ref: event.subjectRef,
|
|
851
|
+
tenantId: descriptor.artifactTenantId,
|
|
852
|
+
});
|
|
853
|
+
if (
|
|
854
|
+
resolution?.ref !== event.subjectRef.ref ||
|
|
855
|
+
resolution?.digest !== event.subjectRef.digest
|
|
856
|
+
) {
|
|
857
|
+
throw new Error("authorization ledger resolved a substituted artifact");
|
|
858
|
+
}
|
|
859
|
+
const record = parseAuthorizationArtifact(
|
|
860
|
+
resolution,
|
|
861
|
+
descriptor,
|
|
862
|
+
operators,
|
|
863
|
+
);
|
|
864
|
+
if (
|
|
865
|
+
event.eventId !==
|
|
866
|
+
`${GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZATION_COMMITTED_EVENT}.${record.request.requestDigest.slice(7)}` ||
|
|
867
|
+
event.decision !== "accepted" ||
|
|
868
|
+
event.timestamp !== record.authorization.authorizedAt ||
|
|
869
|
+
event.sourceRefs.length !== 0 ||
|
|
870
|
+
!Number.isSafeInteger(event.sequence) ||
|
|
871
|
+
event.sequence < 1
|
|
872
|
+
) {
|
|
873
|
+
throw new Error("attestor trust authorization event binding is invalid");
|
|
874
|
+
}
|
|
875
|
+
return { event, record };
|
|
876
|
+
};
|
|
877
|
+
|
|
878
|
+
const authorizationEntry = async (eventId) => {
|
|
879
|
+
const matches = authorizationEvents().filter(
|
|
880
|
+
(event) => event.eventId === eventId,
|
|
881
|
+
);
|
|
882
|
+
if (matches.length > 1) {
|
|
883
|
+
throw new Error("attestor trust authorization event is ambiguous");
|
|
884
|
+
}
|
|
885
|
+
return matches.length === 0 ? null : resolveAuthorizationEvent(matches[0]);
|
|
886
|
+
};
|
|
887
|
+
|
|
888
|
+
const persistAuthorization = async (record) => {
|
|
889
|
+
const eventId = `${GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZATION_COMMITTED_EVENT}.${record.request.requestDigest.slice(7)}`;
|
|
890
|
+
const existing = await authorizationEntry(eventId);
|
|
891
|
+
if (existing) {
|
|
892
|
+
if (!same(existing.record, record)) {
|
|
893
|
+
throw new Error(
|
|
894
|
+
"attestor trust request already has a different durable authorization",
|
|
895
|
+
);
|
|
896
|
+
}
|
|
897
|
+
return freeze({
|
|
898
|
+
authenticated: true,
|
|
899
|
+
durable: true,
|
|
900
|
+
recovered: true,
|
|
901
|
+
recordDigest: record.recordDigest,
|
|
902
|
+
authorizationDigest: record.authorization.authorizationDigest,
|
|
903
|
+
artifactRef: structuredClone(existing.event.subjectRef),
|
|
904
|
+
eventSequence: existing.event.sequence,
|
|
905
|
+
ledgerEventDigest: existing.event.eventDigest,
|
|
906
|
+
});
|
|
907
|
+
}
|
|
908
|
+
const head = verifyLedger();
|
|
909
|
+
const published = put(ARTIFACT_TYPE, record, {
|
|
910
|
+
audience: descriptor.audience,
|
|
911
|
+
purpose: descriptor.purpose,
|
|
912
|
+
retention: "ledger",
|
|
913
|
+
});
|
|
914
|
+
if (
|
|
915
|
+
!published?.ref ||
|
|
916
|
+
published.receipt?.persisted !== true ||
|
|
917
|
+
published.receipt?.readbackVerified !== true ||
|
|
918
|
+
published.receipt?.integrityVerified !== true ||
|
|
919
|
+
published.receipt?.retention !== "ledger"
|
|
920
|
+
) {
|
|
921
|
+
throw new Error("attestor trust authorization was not durably read back");
|
|
922
|
+
}
|
|
923
|
+
let recovered = false;
|
|
924
|
+
try {
|
|
925
|
+
const receipt = appendLedger(
|
|
926
|
+
{
|
|
927
|
+
artifactTenantId: descriptor.artifactTenantId,
|
|
928
|
+
correlationId: authorizationStreamId,
|
|
929
|
+
decision: "accepted",
|
|
930
|
+
eventId,
|
|
931
|
+
reason: `attestor trust ${record.request.operation} authorized by ${requiredApprovals} operator(s)`,
|
|
932
|
+
skillName: null,
|
|
933
|
+
sourceRefs: [],
|
|
934
|
+
subjectRef: published.ref,
|
|
935
|
+
tenantId,
|
|
936
|
+
timestamp: record.authorization.authorizedAt,
|
|
937
|
+
type: GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZATION_COMMITTED_EVENT,
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
expectedHeadDigest: head.headDigest,
|
|
941
|
+
expectedSequence: head.sequence,
|
|
942
|
+
},
|
|
943
|
+
);
|
|
944
|
+
if (receipt?.authenticated !== true || receipt.durable !== true) {
|
|
945
|
+
throw new Error("attestor trust authorization append was not durable");
|
|
946
|
+
}
|
|
947
|
+
} catch (error) {
|
|
948
|
+
const found = await authorizationEntry(eventId);
|
|
949
|
+
if (!found || !same(found.record, record)) throw error;
|
|
950
|
+
recovered = true;
|
|
951
|
+
}
|
|
952
|
+
const stored = await authorizationEntry(eventId);
|
|
953
|
+
if (!stored || !same(stored.record, record)) {
|
|
954
|
+
throw new Error("attestor trust authorization readback differs");
|
|
955
|
+
}
|
|
956
|
+
return freeze({
|
|
957
|
+
authenticated: true,
|
|
958
|
+
durable: true,
|
|
959
|
+
recovered,
|
|
960
|
+
recordDigest: record.recordDigest,
|
|
961
|
+
authorizationDigest: record.authorization.authorizationDigest,
|
|
962
|
+
artifactRef: structuredClone(stored.event.subjectRef),
|
|
963
|
+
eventSequence: stored.event.sequence,
|
|
964
|
+
ledgerEventDigest: stored.event.eventDigest,
|
|
965
|
+
});
|
|
966
|
+
};
|
|
967
|
+
|
|
968
|
+
const operations = Object.freeze({
|
|
969
|
+
descriptor,
|
|
970
|
+
prepare(input) {
|
|
971
|
+
return prepareInput(input, descriptor, clock(now, "operations"));
|
|
972
|
+
},
|
|
973
|
+
async execute({ request, approvals } = {}) {
|
|
974
|
+
const currentTime = clock(now, "operations");
|
|
975
|
+
validateRequest(request, descriptor, currentTime);
|
|
976
|
+
dense(approvals, "attestor trust approvals", operators.size);
|
|
977
|
+
if (approvals.length !== requiredApprovals) {
|
|
978
|
+
throw new Error(
|
|
979
|
+
`attestor trust operation requires exactly ${requiredApprovals} approvals`,
|
|
980
|
+
);
|
|
981
|
+
}
|
|
982
|
+
const accepted = [];
|
|
983
|
+
const seenOperators = new Set();
|
|
984
|
+
const seenKeys = new Set();
|
|
985
|
+
for (const receipt of approvals) {
|
|
986
|
+
const operatorId = receipt?.operatorId;
|
|
987
|
+
const operator = operators.get(operatorId);
|
|
988
|
+
if (
|
|
989
|
+
!operator ||
|
|
990
|
+
seenOperators.has(operatorId) ||
|
|
991
|
+
seenKeys.has(operator.keyId)
|
|
992
|
+
) {
|
|
993
|
+
throw new Error(
|
|
994
|
+
"attestor trust approvals must be from distinct active operators",
|
|
995
|
+
);
|
|
996
|
+
}
|
|
997
|
+
normalizeApproval(
|
|
998
|
+
receipt,
|
|
999
|
+
request,
|
|
1000
|
+
{ ...operator, operatorId },
|
|
1001
|
+
currentTime,
|
|
1002
|
+
);
|
|
1003
|
+
seenOperators.add(operatorId);
|
|
1004
|
+
seenKeys.add(operator.keyId);
|
|
1005
|
+
accepted.push(receipt);
|
|
1006
|
+
}
|
|
1007
|
+
accepted.sort((left, right) =>
|
|
1008
|
+
left.operatorId.localeCompare(right.operatorId),
|
|
1009
|
+
);
|
|
1010
|
+
const authorization = buildAuthorization(request, accepted);
|
|
1011
|
+
const authorizationRecordCore = {
|
|
1012
|
+
schema:
|
|
1013
|
+
GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZATION_RECORD_SCHEMA,
|
|
1014
|
+
tenantId,
|
|
1015
|
+
request: structuredClone(request),
|
|
1016
|
+
approvals: structuredClone(accepted),
|
|
1017
|
+
authorization: structuredClone(authorization),
|
|
1018
|
+
};
|
|
1019
|
+
const authorizationRecord = freeze({
|
|
1020
|
+
...authorizationRecordCore,
|
|
1021
|
+
recordDigest:
|
|
1022
|
+
digestGovernedSkillSynthesisAttestorTrustAuthorizationRecord(
|
|
1023
|
+
authorizationRecordCore,
|
|
1024
|
+
),
|
|
1025
|
+
});
|
|
1026
|
+
validateAuthorizationRecord(authorizationRecord, descriptor, operators);
|
|
1027
|
+
const persistence = await persistAuthorization(authorizationRecord);
|
|
1028
|
+
const authorizationLink = freeze({
|
|
1029
|
+
schema:
|
|
1030
|
+
GOVERNED_SKILL_SYNTHESIS_ATTESTOR_TRUST_AUTHORIZATION_LINK_SCHEMA,
|
|
1031
|
+
authorizationDigest: authorization.authorizationDigest,
|
|
1032
|
+
authorizationStreamId,
|
|
1033
|
+
recordDigest: authorizationRecord.recordDigest,
|
|
1034
|
+
artifactRef: structuredClone(persistence.artifactRef),
|
|
1035
|
+
eventSequence: persistence.eventSequence,
|
|
1036
|
+
});
|
|
1037
|
+
validateRequest(request, descriptor, clock(now, "operations"));
|
|
1038
|
+
let lifecycle;
|
|
1039
|
+
if (request.operation === "register") {
|
|
1040
|
+
lifecycle = await trustLedger.registerKey({
|
|
1041
|
+
serviceId: request.serviceId,
|
|
1042
|
+
publicKey: decodePublicKey(
|
|
1043
|
+
request.publicKeySpki,
|
|
1044
|
+
request.keyId,
|
|
1045
|
+
"attestor",
|
|
1046
|
+
),
|
|
1047
|
+
authorization: authorizationLink,
|
|
1048
|
+
});
|
|
1049
|
+
} else if (request.operation === "rotate") {
|
|
1050
|
+
lifecycle = await trustLedger.rotateKey({
|
|
1051
|
+
serviceId: request.serviceId,
|
|
1052
|
+
priorKeyId: request.priorKeyId,
|
|
1053
|
+
publicKey: decodePublicKey(
|
|
1054
|
+
request.publicKeySpki,
|
|
1055
|
+
request.keyId,
|
|
1056
|
+
"attestor",
|
|
1057
|
+
),
|
|
1058
|
+
reason: request.reason,
|
|
1059
|
+
authorization: authorizationLink,
|
|
1060
|
+
});
|
|
1061
|
+
} else {
|
|
1062
|
+
lifecycle = await trustLedger.revokeKey({
|
|
1063
|
+
serviceId: request.serviceId,
|
|
1064
|
+
keyId: request.keyId,
|
|
1065
|
+
reason: request.reason,
|
|
1066
|
+
authorization: authorizationLink,
|
|
1067
|
+
});
|
|
1068
|
+
}
|
|
1069
|
+
return freeze({
|
|
1070
|
+
authorization,
|
|
1071
|
+
persistence: structuredClone(persistence),
|
|
1072
|
+
lifecycle: structuredClone(lifecycle),
|
|
1073
|
+
});
|
|
1074
|
+
},
|
|
1075
|
+
});
|
|
1076
|
+
OPERATIONS.add(operations);
|
|
1077
|
+
return operations;
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
export function isGovernedSkillSynthesisAttestorTrustOperations(value) {
|
|
1081
|
+
return OPERATIONS.has(value);
|
|
1082
|
+
}
|