@origintrail-official/dkg-agent 10.0.20 → 10.0.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/approved-private-replica.d.ts +54 -0
- package/dist/approved-private-replica.d.ts.map +1 -0
- package/dist/approved-private-replica.js +148 -0
- package/dist/approved-private-replica.js.map +1 -0
- package/dist/bounded-operation.d.ts +2 -0
- package/dist/bounded-operation.d.ts.map +1 -1
- package/dist/bounded-operation.js +5 -0
- package/dist/bounded-operation.js.map +1 -1
- package/dist/chain-finality-confirmations-v1.d.ts +7 -0
- package/dist/chain-finality-confirmations-v1.d.ts.map +1 -0
- package/dist/chain-finality-confirmations-v1.js +9 -0
- package/dist/chain-finality-confirmations-v1.js.map +1 -0
- package/dist/chain-reconciler.d.ts +23 -0
- package/dist/chain-reconciler.d.ts.map +1 -1
- package/dist/chain-reconciler.js +35 -0
- package/dist/chain-reconciler.js.map +1 -1
- package/dist/context-graph-member-proof.d.ts +13 -0
- package/dist/context-graph-member-proof.d.ts.map +1 -1
- package/dist/context-graph-member-proof.js +36 -0
- package/dist/context-graph-member-proof.js.map +1 -1
- package/dist/context-graph-membership-persist-scheduler.d.ts +7 -23
- package/dist/context-graph-membership-persist-scheduler.d.ts.map +1 -1
- package/dist/context-graph-membership-persist-scheduler.js +12 -88
- package/dist/context-graph-membership-persist-scheduler.js.map +1 -1
- package/dist/context-graph-meta-projection.d.ts +13 -2
- package/dist/context-graph-meta-projection.d.ts.map +1 -1
- package/dist/context-graph-meta-projection.js +61 -24
- package/dist/context-graph-meta-projection.js.map +1 -1
- package/dist/context-graph-private-meta-proof.d.ts +5 -0
- package/dist/context-graph-private-meta-proof.d.ts.map +1 -1
- package/dist/context-graph-private-meta-proof.js +23 -0
- package/dist/context-graph-private-meta-proof.js.map +1 -1
- package/dist/context-graph-read-authority.d.ts +13 -0
- package/dist/context-graph-read-authority.d.ts.map +1 -1
- package/dist/context-graph-read-authority.js +23 -3
- package/dist/context-graph-read-authority.js.map +1 -1
- package/dist/context-graph-storage-discovery.d.ts +11 -2
- package/dist/context-graph-storage-discovery.d.ts.map +1 -1
- package/dist/context-graph-storage-discovery.js +11 -2
- package/dist/context-graph-storage-discovery.js.map +1 -1
- package/dist/context-graph-subscription-persist-scheduler.d.ts +52 -0
- package/dist/context-graph-subscription-persist-scheduler.d.ts.map +1 -0
- package/dist/context-graph-subscription-persist-scheduler.js +78 -0
- package/dist/context-graph-subscription-persist-scheduler.js.map +1 -0
- package/dist/curator-dial-address.d.ts +16 -0
- package/dist/curator-dial-address.d.ts.map +1 -0
- package/dist/curator-dial-address.js +58 -0
- package/dist/curator-dial-address.js.map +1 -0
- package/dist/curator-meta-refresh.d.ts +2 -0
- package/dist/curator-meta-refresh.d.ts.map +1 -1
- package/dist/curator-meta-refresh.js +2 -44
- package/dist/curator-meta-refresh.js.map +1 -1
- package/dist/curator-peer-connection.d.ts +21 -0
- package/dist/curator-peer-connection.d.ts.map +1 -0
- package/dist/curator-peer-connection.js +41 -0
- package/dist/curator-peer-connection.js.map +1 -0
- package/dist/dkg-agent-base.d.ts +94 -22
- package/dist/dkg-agent-base.d.ts.map +1 -1
- package/dist/dkg-agent-base.js +97 -144
- package/dist/dkg-agent-base.js.map +1 -1
- package/dist/dkg-agent-cg-registry.d.ts +10 -0
- package/dist/dkg-agent-cg-registry.d.ts.map +1 -1
- package/dist/dkg-agent-cg-registry.js +176 -115
- package/dist/dkg-agent-cg-registry.js.map +1 -1
- package/dist/dkg-agent-cg-resolve.d.ts +6 -4
- package/dist/dkg-agent-cg-resolve.d.ts.map +1 -1
- package/dist/dkg-agent-cg-resolve.js +22 -16
- package/dist/dkg-agent-cg-resolve.js.map +1 -1
- package/dist/dkg-agent-constants.d.ts +4 -2
- package/dist/dkg-agent-constants.d.ts.map +1 -1
- package/dist/dkg-agent-constants.js +4 -2
- package/dist/dkg-agent-constants.js.map +1 -1
- package/dist/dkg-agent-crypto.d.ts +101 -21
- package/dist/dkg-agent-crypto.d.ts.map +1 -1
- package/dist/dkg-agent-crypto.js +670 -152
- package/dist/dkg-agent-crypto.js.map +1 -1
- package/dist/dkg-agent-helpers.d.ts +2 -2
- package/dist/dkg-agent-helpers.d.ts.map +1 -1
- package/dist/dkg-agent-helpers.js +3 -3
- package/dist/dkg-agent-helpers.js.map +1 -1
- package/dist/dkg-agent-join.d.ts +12 -1
- package/dist/dkg-agent-join.d.ts.map +1 -1
- package/dist/dkg-agent-join.js +80 -92
- package/dist/dkg-agent-join.js.map +1 -1
- package/dist/dkg-agent-lifecycle.d.ts +58 -15
- package/dist/dkg-agent-lifecycle.d.ts.map +1 -1
- package/dist/dkg-agent-lifecycle.js +295 -114
- package/dist/dkg-agent-lifecycle.js.map +1 -1
- package/dist/dkg-agent-publish.d.ts +33 -40
- package/dist/dkg-agent-publish.d.ts.map +1 -1
- package/dist/dkg-agent-publish.js +190 -256
- package/dist/dkg-agent-publish.js.map +1 -1
- package/dist/dkg-agent-query.d.ts +7 -0
- package/dist/dkg-agent-query.d.ts.map +1 -1
- package/dist/dkg-agent-query.js +75 -63
- package/dist/dkg-agent-query.js.map +1 -1
- package/dist/dkg-agent-registered-private-empty-vm.d.ts +70 -0
- package/dist/dkg-agent-registered-private-empty-vm.d.ts.map +1 -0
- package/dist/dkg-agent-registered-private-empty-vm.js +133 -0
- package/dist/dkg-agent-registered-private-empty-vm.js.map +1 -0
- package/dist/dkg-agent-rfc64-catalog-auto-publish.d.ts +8 -3
- package/dist/dkg-agent-rfc64-catalog-auto-publish.d.ts.map +1 -1
- package/dist/dkg-agent-rfc64-catalog-auto-publish.js +45 -51
- package/dist/dkg-agent-rfc64-catalog-auto-publish.js.map +1 -1
- package/dist/dkg-agent-rfc64-catalog.d.ts +63 -87
- package/dist/dkg-agent-rfc64-catalog.d.ts.map +1 -1
- package/dist/dkg-agent-rfc64-catalog.js +595 -551
- package/dist/dkg-agent-rfc64-catalog.js.map +1 -1
- package/dist/dkg-agent-swm-host.d.ts +43 -52
- package/dist/dkg-agent-swm-host.d.ts.map +1 -1
- package/dist/dkg-agent-swm-host.js +629 -812
- package/dist/dkg-agent-swm-host.js.map +1 -1
- package/dist/dkg-agent-swm-state.d.ts +15 -0
- package/dist/dkg-agent-swm-state.d.ts.map +1 -1
- package/dist/dkg-agent-swm-state.js +43 -0
- package/dist/dkg-agent-swm-state.js.map +1 -1
- package/dist/dkg-agent-swm-substrate.d.ts +2 -0
- package/dist/dkg-agent-swm-substrate.d.ts.map +1 -1
- package/dist/dkg-agent-swm-substrate.js +110 -53
- package/dist/dkg-agent-swm-substrate.js.map +1 -1
- package/dist/dkg-agent-types.d.ts +62 -26
- package/dist/dkg-agent-types.d.ts.map +1 -1
- package/dist/dkg-agent-types.js.map +1 -1
- package/dist/dkg-agent-vm-promotion.d.ts +2 -0
- package/dist/dkg-agent-vm-promotion.d.ts.map +1 -1
- package/dist/dkg-agent-vm-promotion.js +39 -5
- package/dist/dkg-agent-vm-promotion.js.map +1 -1
- package/dist/dkg-agent.d.ts +8 -1
- package/dist/dkg-agent.d.ts.map +1 -1
- package/dist/dkg-agent.js +75 -45
- package/dist/dkg-agent.js.map +1 -1
- package/dist/exact-graph-content-verifier.d.ts.map +1 -1
- package/dist/exact-graph-content-verifier.js +3 -2
- package/dist/exact-graph-content-verifier.js.map +1 -1
- package/dist/finalization-handler.d.ts +19 -207
- package/dist/finalization-handler.d.ts.map +1 -1
- package/dist/finalization-handler.js +79 -1646
- package/dist/finalization-handler.js.map +1 -1
- package/dist/finalization-recovery-worker.js +1 -1
- package/dist/finalization-recovery-worker.js.map +1 -1
- package/dist/finalized-authority-cold-resolution.d.ts +6 -1
- package/dist/finalized-authority-cold-resolution.d.ts.map +1 -1
- package/dist/finalized-authority-cold-resolution.js +22 -3
- package/dist/finalized-authority-cold-resolution.js.map +1 -1
- package/dist/gossip-session.d.ts +29 -0
- package/dist/gossip-session.d.ts.map +1 -0
- package/dist/gossip-session.js +48 -0
- package/dist/gossip-session.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/context-graph-authority/context-graph-agent-gate-authority.d.ts +5 -5
- package/dist/internal/context-graph-authority/context-graph-agent-gate-authority.d.ts.map +1 -1
- package/dist/internal/context-graph-authority/context-graph-agent-gate-authority.js +59 -49
- package/dist/internal/context-graph-authority/context-graph-agent-gate-authority.js.map +1 -1
- package/dist/internal/context-graph-authority/finalized-target-projection.d.ts +17 -0
- package/dist/internal/context-graph-authority/finalized-target-projection.d.ts.map +1 -0
- package/dist/internal/context-graph-authority/finalized-target-projection.js +36 -0
- package/dist/internal/context-graph-authority/finalized-target-projection.js.map +1 -0
- package/dist/internal/context-graph-authority/swm-transport-authority.d.ts +43 -7
- package/dist/internal/context-graph-authority/swm-transport-authority.d.ts.map +1 -1
- package/dist/internal/context-graph-authority/swm-transport-authority.js +135 -18
- package/dist/internal/context-graph-authority/swm-transport-authority.js.map +1 -1
- package/dist/internal/context-graph-binding-abort.d.ts +3 -0
- package/dist/internal/context-graph-binding-abort.d.ts.map +1 -0
- package/dist/internal/context-graph-binding-abort.js +32 -0
- package/dist/internal/context-graph-binding-abort.js.map +1 -0
- package/dist/internal/context-graph-cache-invalidating-store.d.ts +3 -0
- package/dist/internal/context-graph-cache-invalidating-store.d.ts.map +1 -0
- package/dist/internal/context-graph-cache-invalidating-store.js +167 -0
- package/dist/internal/context-graph-cache-invalidating-store.js.map +1 -0
- package/dist/internal/context-graph-meta-record-copy.d.ts +3 -0
- package/dist/internal/context-graph-meta-record-copy.d.ts.map +1 -0
- package/dist/internal/context-graph-meta-record-copy.js +21 -0
- package/dist/internal/context-graph-meta-record-copy.js.map +1 -0
- package/dist/internal/gossip/host-mode-handler.d.ts +11 -0
- package/dist/internal/gossip/host-mode-handler.d.ts.map +1 -0
- package/dist/internal/gossip/host-mode-handler.js +41 -0
- package/dist/internal/gossip/host-mode-handler.js.map +1 -0
- package/dist/internal/join-encryption-key-bundle.d.ts +10 -0
- package/dist/internal/join-encryption-key-bundle.d.ts.map +1 -0
- package/dist/internal/join-encryption-key-bundle.js +72 -0
- package/dist/internal/join-encryption-key-bundle.js.map +1 -0
- package/dist/internal/knowledge-asset-vm-publish-request.d.ts +31 -0
- package/dist/internal/knowledge-asset-vm-publish-request.d.ts.map +1 -0
- package/dist/internal/knowledge-asset-vm-publish-request.js +91 -0
- package/dist/internal/knowledge-asset-vm-publish-request.js.map +1 -0
- package/dist/internal/lifecycle-sync-policy.d.ts +5 -0
- package/dist/internal/lifecycle-sync-policy.d.ts.map +1 -0
- package/dist/internal/lifecycle-sync-policy.js +12 -0
- package/dist/internal/lifecycle-sync-policy.js.map +1 -0
- package/dist/internal/local-private-member.d.ts +14 -0
- package/dist/internal/local-private-member.d.ts.map +1 -0
- package/dist/internal/local-private-member.js +21 -0
- package/dist/internal/local-private-member.js.map +1 -0
- package/dist/internal/promote/assertion-promote-precommit.d.ts +41 -1
- package/dist/internal/promote/assertion-promote-precommit.d.ts.map +1 -1
- package/dist/internal/promote/assertion-promote-precommit.js +74 -5
- package/dist/internal/promote/assertion-promote-precommit.js.map +1 -1
- package/dist/internal/storage-ack-owned-request.d.ts +16 -0
- package/dist/internal/storage-ack-owned-request.d.ts.map +1 -0
- package/dist/internal/storage-ack-owned-request.js +16 -0
- package/dist/internal/storage-ack-owned-request.js.map +1 -0
- package/dist/internal/vm-reconcile-local-admission-wait.d.ts +96 -0
- package/dist/internal/vm-reconcile-local-admission-wait.d.ts.map +1 -0
- package/dist/internal/vm-reconcile-local-admission-wait.js +226 -0
- package/dist/internal/vm-reconcile-local-admission-wait.js.map +1 -0
- package/dist/internal/vm-reconcile-read-authority.d.ts +27 -0
- package/dist/internal/vm-reconcile-read-authority.d.ts.map +1 -0
- package/dist/internal/vm-reconcile-read-authority.js +33 -0
- package/dist/internal/vm-reconcile-read-authority.js.map +1 -0
- package/dist/internal/vm-reconcile-sweep-admission.d.ts +2 -0
- package/dist/internal/vm-reconcile-sweep-admission.d.ts.map +1 -1
- package/dist/internal/vm-reconcile-sweep.d.ts +16 -4
- package/dist/internal/vm-reconcile-sweep.d.ts.map +1 -1
- package/dist/internal/vm-reconcile-sweep.js +91 -50
- package/dist/internal/vm-reconcile-sweep.js.map +1 -1
- package/dist/internal/workspace-projected-delegatees.d.ts +3 -0
- package/dist/internal/workspace-projected-delegatees.d.ts.map +1 -0
- package/dist/internal/workspace-projected-delegatees.js +34 -0
- package/dist/internal/workspace-projected-delegatees.js.map +1 -0
- package/dist/join-approval-restart-projection.d.ts +14 -0
- package/dist/join-approval-restart-projection.d.ts.map +1 -0
- package/dist/join-approval-restart-projection.js +32 -0
- package/dist/join-approval-restart-projection.js.map +1 -0
- package/dist/keyed-persist-scheduler.d.ts +72 -0
- package/dist/keyed-persist-scheduler.d.ts.map +1 -0
- package/dist/keyed-persist-scheduler.js +140 -0
- package/dist/keyed-persist-scheduler.js.map +1 -0
- package/dist/map-with-concurrency.d.ts +7 -0
- package/dist/map-with-concurrency.d.ts.map +1 -1
- package/dist/map-with-concurrency.js +36 -0
- package/dist/map-with-concurrency.js.map +1 -1
- package/dist/p2p/network-admission-coordinator.d.ts +15 -0
- package/dist/p2p/network-admission-coordinator.d.ts.map +1 -1
- package/dist/p2p/network-admission-coordinator.js +20 -2
- package/dist/p2p/network-admission-coordinator.js.map +1 -1
- package/dist/prepare-unscoped-context-graph-read-checks.d.ts.map +1 -1
- package/dist/prepare-unscoped-context-graph-read-checks.js +12 -0
- package/dist/prepare-unscoped-context-graph-read-checks.js.map +1 -1
- package/dist/registered-context-graph-authority.d.ts +7 -0
- package/dist/registered-context-graph-authority.d.ts.map +1 -1
- package/dist/registered-private-empty-vm-attempt-v1.d.ts +34 -0
- package/dist/registered-private-empty-vm-attempt-v1.d.ts.map +1 -0
- package/dist/registered-private-empty-vm-attempt-v1.js +120 -0
- package/dist/registered-private-empty-vm-attempt-v1.js.map +1 -0
- package/dist/reserved-ka-id-allocation.d.ts +12 -0
- package/dist/reserved-ka-id-allocation.d.ts.map +1 -0
- package/dist/reserved-ka-id-allocation.js +25 -0
- package/dist/reserved-ka-id-allocation.js.map +1 -0
- package/dist/rfc64/authority-rpc-circuit-breaker-v1.d.ts +5 -3
- package/dist/rfc64/authority-rpc-circuit-breaker-v1.d.ts.map +1 -1
- package/dist/rfc64/authority-rpc-circuit-breaker-v1.js +9 -0
- package/dist/rfc64/authority-rpc-circuit-breaker-v1.js.map +1 -1
- package/dist/rfc64/catalog-completion-evidence-v1.d.ts +38 -0
- package/dist/rfc64/catalog-completion-evidence-v1.d.ts.map +1 -0
- package/dist/rfc64/catalog-completion-evidence-v1.js +49 -0
- package/dist/rfc64/catalog-completion-evidence-v1.js.map +1 -0
- package/dist/rfc64/catalog-operational-applied-heads-v1.d.ts +6 -3
- package/dist/rfc64/catalog-operational-applied-heads-v1.d.ts.map +1 -1
- package/dist/rfc64/catalog-operational-applied-heads-v1.js +6 -3
- package/dist/rfc64/catalog-operational-applied-heads-v1.js.map +1 -1
- package/dist/rfc64/catalog-operational-reads-v1.d.ts +13 -0
- package/dist/rfc64/catalog-operational-reads-v1.d.ts.map +1 -0
- package/dist/rfc64/catalog-operational-reads-v1.js +56 -0
- package/dist/rfc64/catalog-operational-reads-v1.js.map +1 -0
- package/dist/rfc64/catalog-operational-targets-v1.d.ts +55 -0
- package/dist/rfc64/catalog-operational-targets-v1.d.ts.map +1 -0
- package/dist/rfc64/catalog-operational-targets-v1.js +379 -0
- package/dist/rfc64/catalog-operational-targets-v1.js.map +1 -0
- package/dist/rfc64/catalog-replay-recovery-runtime-v1.d.ts +2 -0
- package/dist/rfc64/catalog-replay-recovery-runtime-v1.d.ts.map +1 -1
- package/dist/rfc64/catalog-replay-recovery-runtime-v1.js +18 -25
- package/dist/rfc64/catalog-replay-recovery-runtime-v1.js.map +1 -1
- package/dist/rfc64/legacy-swm-boundary-codec-v1.d.ts +25 -0
- package/dist/rfc64/legacy-swm-boundary-codec-v1.d.ts.map +1 -0
- package/dist/rfc64/legacy-swm-boundary-codec-v1.js +127 -0
- package/dist/rfc64/legacy-swm-boundary-codec-v1.js.map +1 -0
- package/dist/rfc64/legacy-swm-boundary-v1.d.ts +12 -0
- package/dist/rfc64/legacy-swm-boundary-v1.d.ts.map +1 -1
- package/dist/rfc64/legacy-swm-boundary-v1.js +83 -144
- package/dist/rfc64/legacy-swm-boundary-v1.js.map +1 -1
- package/dist/rfc64/private-catalog-subscription-readiness-v1.d.ts +33 -0
- package/dist/rfc64/private-catalog-subscription-readiness-v1.d.ts.map +1 -0
- package/dist/rfc64/private-catalog-subscription-readiness-v1.js +97 -0
- package/dist/rfc64/private-catalog-subscription-readiness-v1.js.map +1 -0
- package/dist/rfc64/private-read-roster-v1.d.ts +14 -0
- package/dist/rfc64/private-read-roster-v1.d.ts.map +1 -0
- package/dist/rfc64/private-read-roster-v1.js +57 -0
- package/dist/rfc64/private-read-roster-v1.js.map +1 -0
- package/dist/rfc64/registered-private-empty-vm-proof-v1.d.ts +20 -0
- package/dist/rfc64/registered-private-empty-vm-proof-v1.d.ts.map +1 -0
- package/dist/rfc64/registered-private-empty-vm-proof-v1.js +26 -0
- package/dist/rfc64/registered-private-empty-vm-proof-v1.js.map +1 -0
- package/dist/rfc64/swm-inventory-shadow-runtime-v1.d.ts +3 -1
- package/dist/rfc64/swm-inventory-shadow-runtime-v1.d.ts.map +1 -1
- package/dist/rfc64/swm-inventory-shadow-runtime-v1.js +34 -0
- package/dist/rfc64/swm-inventory-shadow-runtime-v1.js.map +1 -1
- package/dist/sync/auth/pipe-request-tail.d.ts +5 -1
- package/dist/sync/auth/pipe-request-tail.d.ts.map +1 -1
- package/dist/sync/auth/pipe-request-tail.js +13 -3
- package/dist/sync/auth/pipe-request-tail.js.map +1 -1
- package/dist/sync/auth/request-build.d.ts +3 -0
- package/dist/sync/auth/request-build.d.ts.map +1 -1
- package/dist/sync/auth/request-build.js +11 -2
- package/dist/sync/auth/request-build.js.map +1 -1
- package/dist/sync/backpressure.d.ts +27 -16
- package/dist/sync/backpressure.d.ts.map +1 -1
- package/dist/sync/backpressure.js +66 -40
- package/dist/sync/backpressure.js.map +1 -1
- package/dist/sync/catchup-policy.d.ts +2 -0
- package/dist/sync/catchup-policy.d.ts.map +1 -1
- package/dist/sync/catchup-policy.js +2 -0
- package/dist/sync/catchup-policy.js.map +1 -1
- package/dist/sync/exact-assets.d.ts +7 -5
- package/dist/sync/exact-assets.d.ts.map +1 -1
- package/dist/sync/exact-assets.js +13 -8
- package/dist/sync/exact-assets.js.map +1 -1
- package/dist/sync/exact-batch-observation.d.ts +3 -0
- package/dist/sync/exact-batch-observation.d.ts.map +1 -0
- package/dist/sync/exact-batch-observation.js +13 -0
- package/dist/sync/exact-batch-observation.js.map +1 -0
- package/dist/sync/exact-batch-stream-capability.d.ts +24 -0
- package/dist/sync/exact-batch-stream-capability.d.ts.map +1 -0
- package/dist/sync/exact-batch-stream-capability.js +88 -0
- package/dist/sync/exact-batch-stream-capability.js.map +1 -0
- package/dist/sync/exact-batch-stream-contract.d.ts +78 -0
- package/dist/sync/exact-batch-stream-contract.d.ts.map +1 -0
- package/dist/sync/exact-batch-stream-contract.js +206 -0
- package/dist/sync/exact-batch-stream-contract.js.map +1 -0
- package/dist/sync/peer-connection.d.ts.map +1 -1
- package/dist/sync/peer-connection.js +11 -2
- package/dist/sync/peer-connection.js.map +1 -1
- package/dist/sync/priority-admission-queue.d.ts +17 -0
- package/dist/sync/priority-admission-queue.d.ts.map +1 -1
- package/dist/sync/priority-admission-queue.js +58 -37
- package/dist/sync/priority-admission-queue.js.map +1 -1
- package/dist/sync/requester/admission-boundary.d.ts +12 -0
- package/dist/sync/requester/admission-boundary.d.ts.map +1 -0
- package/dist/sync/requester/admission-boundary.js +22 -0
- package/dist/sync/requester/admission-boundary.js.map +1 -0
- package/dist/sync/requester/durable-sync.d.ts +2 -1
- package/dist/sync/requester/durable-sync.d.ts.map +1 -1
- package/dist/sync/requester/durable-sync.js +3 -188
- package/dist/sync/requester/durable-sync.js.map +1 -1
- package/dist/sync/requester/exact-batch-stream-driver.d.ts +83 -0
- package/dist/sync/requester/exact-batch-stream-driver.d.ts.map +1 -0
- package/dist/sync/requester/exact-batch-stream-driver.js +299 -0
- package/dist/sync/requester/exact-batch-stream-driver.js.map +1 -0
- package/dist/sync/requester/exact-batch-stream.d.ts +72 -0
- package/dist/sync/requester/exact-batch-stream.d.ts.map +1 -0
- package/dist/sync/requester/exact-batch-stream.js +267 -0
- package/dist/sync/requester/exact-batch-stream.js.map +1 -0
- package/dist/sync/requester/exact-recovery-transport.d.ts +19 -0
- package/dist/sync/requester/exact-recovery-transport.d.ts.map +1 -0
- package/dist/sync/requester/exact-recovery-transport.js +3 -0
- package/dist/sync/requester/exact-recovery-transport.js.map +1 -0
- package/dist/sync/requester/finalized-swm-twin-reconciliation.js +12 -3
- package/dist/sync/requester/finalized-swm-twin-reconciliation.js.map +1 -1
- package/dist/sync/requester/graph-scoped-materialization.d.ts.map +1 -1
- package/dist/sync/requester/graph-scoped-materialization.js +58 -24
- package/dist/sync/requester/graph-scoped-materialization.js.map +1 -1
- package/dist/sync/requester/page-fetch.d.ts +7 -0
- package/dist/sync/requester/page-fetch.d.ts.map +1 -1
- package/dist/sync/requester/page-fetch.js +103 -49
- package/dist/sync/requester/page-fetch.js.map +1 -1
- package/dist/sync/requester/shared-memory-sync.d.ts +10 -5
- package/dist/sync/requester/shared-memory-sync.d.ts.map +1 -1
- package/dist/sync/requester/shared-memory-sync.js +149 -150
- package/dist/sync/requester/shared-memory-sync.js.map +1 -1
- package/dist/sync/requester/swm-materialization-memo.d.ts +12 -0
- package/dist/sync/requester/swm-materialization-memo.d.ts.map +1 -0
- package/dist/sync/requester/swm-materialization-memo.js +54 -0
- package/dist/sync/requester/swm-materialization-memo.js.map +1 -0
- package/dist/sync/requester/swm-recovery.d.ts +3 -29
- package/dist/sync/requester/swm-recovery.d.ts.map +1 -1
- package/dist/sync/requester/swm-recovery.js +11 -10
- package/dist/sync/requester/swm-recovery.js.map +1 -1
- package/dist/sync/requester/swm-snapshot-materializer.d.ts.map +1 -1
- package/dist/sync/requester/swm-snapshot-materializer.js +18 -0
- package/dist/sync/requester/swm-snapshot-materializer.js.map +1 -1
- package/dist/sync/requester/swm-target-executor.d.ts +6 -0
- package/dist/sync/requester/swm-target-executor.d.ts.map +1 -1
- package/dist/sync/requester/swm-target-executor.js +24 -3
- package/dist/sync/requester/swm-target-executor.js.map +1 -1
- package/dist/sync/requester/verified-asset-preparation.d.ts +9 -0
- package/dist/sync/requester/verified-asset-preparation.d.ts.map +1 -0
- package/dist/sync/requester/verified-asset-preparation.js +190 -0
- package/dist/sync/requester/verified-asset-preparation.js.map +1 -0
- package/dist/sync/responder/asset-metadata.d.ts +18 -0
- package/dist/sync/responder/asset-metadata.d.ts.map +1 -0
- package/dist/sync/responder/asset-metadata.js +27 -0
- package/dist/sync/responder/asset-metadata.js.map +1 -0
- package/dist/sync/responder/durable-data-request-policy.d.ts +2 -16
- package/dist/sync/responder/durable-data-request-policy.d.ts.map +1 -1
- package/dist/sync/responder/durable-data-request-policy.js +1 -31
- package/dist/sync/responder/durable-data-request-policy.js.map +1 -1
- package/dist/sync/responder/exact-asset-export-cache.d.ts +89 -0
- package/dist/sync/responder/exact-asset-export-cache.d.ts.map +1 -0
- package/dist/sync/responder/exact-asset-export-cache.js +550 -0
- package/dist/sync/responder/exact-asset-export-cache.js.map +1 -0
- package/dist/sync/responder/exact-batch-stream.d.ts +47 -0
- package/dist/sync/responder/exact-batch-stream.d.ts.map +1 -0
- package/dist/sync/responder/exact-batch-stream.js +151 -0
- package/dist/sync/responder/exact-batch-stream.js.map +1 -0
- package/dist/sync/responder/exact-data-session.d.ts +67 -0
- package/dist/sync/responder/exact-data-session.d.ts.map +1 -0
- package/dist/sync/responder/exact-data-session.js +130 -0
- package/dist/sync/responder/exact-data-session.js.map +1 -0
- package/dist/sync/responder/exact-graph-cursor-filter.d.ts +9 -0
- package/dist/sync/responder/exact-graph-cursor-filter.d.ts.map +1 -0
- package/dist/sync/responder/exact-graph-cursor-filter.js +101 -0
- package/dist/sync/responder/exact-graph-cursor-filter.js.map +1 -0
- package/dist/sync/responder/exact-graph-reader.d.ts +95 -0
- package/dist/sync/responder/exact-graph-reader.d.ts.map +1 -0
- package/dist/sync/responder/exact-graph-reader.js +623 -0
- package/dist/sync/responder/exact-graph-reader.js.map +1 -0
- package/dist/sync/responder/exact-page-reader.d.ts +37 -0
- package/dist/sync/responder/exact-page-reader.d.ts.map +1 -0
- package/dist/sync/responder/exact-page-reader.js +130 -0
- package/dist/sync/responder/exact-page-reader.js.map +1 -0
- package/dist/sync/responder/graph-plan.d.ts +19 -85
- package/dist/sync/responder/graph-plan.d.ts.map +1 -1
- package/dist/sync/responder/graph-plan.js +51 -988
- package/dist/sync/responder/graph-plan.js.map +1 -1
- package/dist/sync/responder/page-framing-policy.d.ts +31 -0
- package/dist/sync/responder/page-framing-policy.d.ts.map +1 -0
- package/dist/sync/responder/page-framing-policy.js +41 -0
- package/dist/sync/responder/page-framing-policy.js.map +1 -0
- package/dist/sync/responder/responder-row-page.d.ts +85 -0
- package/dist/sync/responder/responder-row-page.d.ts.map +1 -0
- package/dist/sync/responder/responder-row-page.js +181 -0
- package/dist/sync/responder/responder-row-page.js.map +1 -0
- package/dist/sync/responder/row-serialization.d.ts +34 -0
- package/dist/sync/responder/row-serialization.d.ts.map +1 -0
- package/dist/sync/responder/row-serialization.js +69 -0
- package/dist/sync/responder/row-serialization.js.map +1 -0
- package/dist/sync/responder/session-plan-memo.d.ts +20 -0
- package/dist/sync/responder/session-plan-memo.d.ts.map +1 -0
- package/dist/sync/responder/session-plan-memo.js +135 -0
- package/dist/sync/responder/session-plan-memo.js.map +1 -0
- package/dist/sync/responder/sync-handler.d.ts +18 -1
- package/dist/sync/responder/sync-handler.d.ts.map +1 -1
- package/dist/sync/responder/sync-handler.js +101 -57
- package/dist/sync/responder/sync-handler.js.map +1 -1
- package/dist/sync/shared-memory-completion.d.ts +30 -0
- package/dist/sync/shared-memory-completion.d.ts.map +1 -1
- package/dist/sync/shared-memory-completion.js +13 -0
- package/dist/sync/shared-memory-completion.js.map +1 -1
- package/dist/sync/wire-compression.d.ts +47 -0
- package/dist/sync/wire-compression.d.ts.map +1 -0
- package/dist/sync/wire-compression.js +111 -0
- package/dist/sync/wire-compression.js.map +1 -0
- package/dist/unscoped-query-consistency.d.ts +12 -0
- package/dist/unscoped-query-consistency.d.ts.map +1 -1
- package/dist/unscoped-query-consistency.js +17 -1
- package/dist/unscoped-query-consistency.js.map +1 -1
- package/dist/vm-promotion-audit.d.ts +2 -2
- package/dist/vm-promotion-audit.d.ts.map +1 -1
- package/dist/vm-promotion-audit.js +26 -9
- package/dist/vm-promotion-audit.js.map +1 -1
- package/dist/vm-reconcile-dispatcher.d.ts +42 -1
- package/dist/vm-reconcile-dispatcher.d.ts.map +1 -1
- package/dist/vm-reconcile-dispatcher.js +103 -4
- package/dist/vm-reconcile-dispatcher.js.map +1 -1
- package/dist/vm-reconcile-peer-topology.d.ts +7 -9
- package/dist/vm-reconcile-peer-topology.d.ts.map +1 -1
- package/dist/vm-reconcile-peer-topology.js +1 -37
- package/dist/vm-reconcile-peer-topology.js.map +1 -1
- package/dist/vm-recovery-candidate-order.d.ts +16 -0
- package/dist/vm-recovery-candidate-order.d.ts.map +1 -0
- package/dist/vm-recovery-candidate-order.js +24 -0
- package/dist/vm-recovery-candidate-order.js.map +1 -0
- package/dist/vm-recovery-core-transport-preference.d.ts +69 -0
- package/dist/vm-recovery-core-transport-preference.d.ts.map +1 -0
- package/dist/vm-recovery-core-transport-preference.js +131 -0
- package/dist/vm-recovery-core-transport-preference.js.map +1 -0
- package/dist/vm-recovery-experiment-policy.d.ts +24 -0
- package/dist/vm-recovery-experiment-policy.d.ts.map +1 -0
- package/dist/vm-recovery-experiment-policy.js +18 -0
- package/dist/vm-recovery-experiment-policy.js.map +1 -0
- package/dist/vm-recovery-footprint.d.ts +54 -0
- package/dist/vm-recovery-footprint.d.ts.map +1 -1
- package/dist/vm-recovery-footprint.js +88 -24
- package/dist/vm-recovery-footprint.js.map +1 -1
- package/dist/vm-recovery-microbatch.d.ts +2 -0
- package/dist/vm-recovery-microbatch.d.ts.map +1 -1
- package/dist/vm-recovery-microbatch.js +7 -3
- package/dist/vm-recovery-microbatch.js.map +1 -1
- package/dist/vm-recovery-pass-authority.d.ts +84 -0
- package/dist/vm-recovery-pass-authority.d.ts.map +1 -0
- package/dist/vm-recovery-pass-authority.js +126 -0
- package/dist/vm-recovery-pass-authority.js.map +1 -0
- package/dist/vm-recovery-phase-timing.d.ts +75 -0
- package/dist/vm-recovery-phase-timing.d.ts.map +1 -0
- package/dist/vm-recovery-phase-timing.js +236 -0
- package/dist/vm-recovery-phase-timing.js.map +1 -0
- package/dist/vm-recovery-preparation.d.ts +143 -0
- package/dist/vm-recovery-preparation.d.ts.map +1 -0
- package/dist/vm-recovery-preparation.js +433 -0
- package/dist/vm-recovery-preparation.js.map +1 -0
- package/dist/vm-recovery-provider-policy.d.ts +13 -1
- package/dist/vm-recovery-provider-policy.d.ts.map +1 -1
- package/dist/vm-recovery-provider-policy.js +37 -5
- package/dist/vm-recovery-provider-policy.js.map +1 -1
- package/dist/vm-recovery-stream-profile.d.ts +15 -0
- package/dist/vm-recovery-stream-profile.d.ts.map +1 -0
- package/dist/vm-recovery-stream-profile.js +53 -0
- package/dist/vm-recovery-stream-profile.js.map +1 -0
- package/dist/vm-recovery-stream-setback-policy.d.ts +54 -0
- package/dist/vm-recovery-stream-setback-policy.d.ts.map +1 -0
- package/dist/vm-recovery-stream-setback-policy.js +85 -0
- package/dist/vm-recovery-stream-setback-policy.js.map +1 -0
- package/dist/vm-recovery-timing-observer.d.ts +27 -0
- package/dist/vm-recovery-timing-observer.d.ts.map +1 -0
- package/dist/vm-recovery-timing-observer.js +49 -0
- package/dist/vm-recovery-timing-observer.js.map +1 -0
- package/dist/vm-recovery-transport-budget-policy.d.ts +32 -0
- package/dist/vm-recovery-transport-budget-policy.d.ts.map +1 -0
- package/dist/vm-recovery-transport-budget-policy.js +55 -0
- package/dist/vm-recovery-transport-budget-policy.js.map +1 -0
- package/dist/vm-recovery-transport-plan.d.ts +83 -0
- package/dist/vm-recovery-transport-plan.d.ts.map +1 -0
- package/dist/vm-recovery-transport-plan.js +117 -0
- package/dist/vm-recovery-transport-plan.js.map +1 -0
- package/package.json +17 -9
- package/dist/finalization-annotations.d.ts +0 -5
- package/dist/finalization-annotations.d.ts.map +0 -1
- package/dist/finalization-annotations.js +0 -6
- package/dist/finalization-annotations.js.map +0 -1
- package/dist/swm-ka-bound.d.ts +0 -21
- package/dist/swm-ka-bound.d.ts.map +0 -1
- package/dist/swm-ka-bound.js +0 -36
- package/dist/swm-ka-bound.js.map +0 -1
package/dist/dkg-agent-crypto.js
CHANGED
|
@@ -7,16 +7,17 @@
|
|
|
7
7
|
* 1:1 move; methods take `this: DKGAgent` so cross-calls resolve against the
|
|
8
8
|
* composed class.
|
|
9
9
|
*/
|
|
10
|
+
import { collectProjectedDelegatees } from './internal/workspace-projected-delegatees.js';
|
|
10
11
|
import { createHash, randomUUID } from 'node:crypto';
|
|
11
|
-
import { PROTOCOL_SWM_SENDER_KEY, encodeGossipEnvelope, computeGossipSigningPayload, GOSSIP_ENVELOPE_VERSION, GOSSIP_TYPE_WORKSPACE_PUBLISH, createOperationContext, logKaLifecycleEvent, WORKSPACE_AGENT_ENCRYPTION_KEY_ALGORITHM_X25519, WORKSPACE_RECIPIENT_ENCRYPTION_KEY_PURPOSE, decodeWorkspaceEncryptionKey, SWM_SENDER_KEY_PACKAGE_ACK_TYPE, SWM_SENDER_KEY_PACKAGE_ACK_RETRYABLE_REASON_CODES, SWM_SENDER_KEY_PACKAGE_VERSION, computeSwmSenderKeyMembershipHash, computeSwmSenderKeyPackageAAD, decodeWorkspacePublishRequest, decodeSwmSenderKeyPackage, decodeSwmSenderKeyPackageAck, decryptSwmSenderKeyMessage, decryptSwmSenderKeyPackage, encodeSwmSenderKeyMessage, encodeSwmSenderKeyPackage, encodeSwmSenderKeyPackageAck, encryptSwmSenderKeyMessage, encryptSwmSenderKeyPackage, generateEd25519Keypair, generateSwmSenderChainKey, generateSwmSenderEpochId, ratchetSwmSenderChainKey, uint64ForProto, SWM_SENDER_KEY_SKIPPED_MESSAGE_CACHE_LIMIT, } from '@origintrail-official/dkg-core';
|
|
12
|
+
import { PROTOCOL_SWM_SENDER_KEY, encodeGossipEnvelope, computeGossipSigningPayload, GOSSIP_ENVELOPE_VERSION, GOSSIP_TYPE_WORKSPACE_PUBLISH, createOperationContext, logKaLifecycleEvent, WORKSPACE_AGENT_ENCRYPTION_KEY_ALGORITHM_X25519, WORKSPACE_RECIPIENT_ENCRYPTION_KEY_PURPOSE, decodeWorkspaceEncryptionKey, SWM_SENDER_KEY_PACKAGE_ACK_TYPE, SWM_SENDER_KEY_PACKAGE_ACK_RETRYABLE_REASON_CODES, SWM_SENDER_KEY_PACKAGE_VERSION, computeSwmSenderKeyMembershipHash, computeSwmSenderKeyPackageAAD, decodeWorkspacePublishRequest, decodeSwmSenderKeyPackage, decodeSwmSenderKeyPackageAck, decryptSwmSenderKeyMessage, decryptSwmSenderKeyPackage, encodeSwmSenderKeyMessage, encodeSwmSenderKeyPackage, encodeSwmSenderKeyPackageAck, encryptSwmSenderKeyMessage, encryptSwmSenderKeyPackage, generateEd25519Keypair, generateSwmSenderChainKey, generateSwmSenderEpochId, ratchetSwmSenderChainKey, uint64ForProto, SWM_SENDER_KEY_SKIPPED_MESSAGE_CACHE_LIMIT, tryCanonicalPeerIdString, } from '@origintrail-official/dkg-core';
|
|
12
13
|
import { createRpcTimeoutError, withRpcRequestContext } from '@origintrail-official/dkg-chain';
|
|
13
14
|
import { resolveWorkspaceAgentRecipients, resolveWorkspaceAgentRecipientKeys, isWorkspaceAgentEncryptionKeyMissingError, WorkspaceAgentEncryptionKeyMissingError, } from '@origintrail-official/dkg-publisher';
|
|
14
15
|
import { ethers } from 'ethers';
|
|
15
16
|
import { resolveActivePublicContextGraphChainProof as resolveStrictActivePublicChainProof, } from './active-public-context-graph-chain-proof.js';
|
|
16
17
|
import { resolveContextGraphAgentGateAuthorityDecision, } from './internal/context-graph-authority/context-graph-agent-gate-authority.js';
|
|
17
18
|
import { resolveLiveOnChainAccessPolicyState as resolveLiveAccessPolicyState, } from './internal/context-graph-authority/context-graph-access-policy.js';
|
|
18
|
-
import { createContextGraphAuthorityError, isRetryableContextGraphAuthorityUnavailableReason, } from './internal/context-graph-authority/context-graph-authority.js';
|
|
19
|
-
import { resolveSwmRegisteredAuthorityDecision, resolveSwmTransportAuthorityDecision, } from './internal/context-graph-authority/swm-transport-authority.js';
|
|
19
|
+
import { createContextGraphAuthorityError, isContextGraphAuthorityUnavailableMarker, isRetryableContextGraphAuthorityUnavailableReason, } from './internal/context-graph-authority/context-graph-authority.js';
|
|
20
|
+
import { resolveSwmMemberRecoveryAuthorityDecision, resolveSwmRegisteredAuthorityDecision, resolveSwmTransportAuthorityDecision, } from './internal/context-graph-authority/swm-transport-authority.js';
|
|
20
21
|
import { FANOUT_RESPONSE_REJECTED, FANOUT_RESPONSE_RETRYABLE, } from './swm/substrate-fanout.js';
|
|
21
22
|
import { SyncTargetSupersededError } from './sync/error-tags.js';
|
|
22
23
|
import { resolveAssetUalFromKaIdentity } from './ka-identity.js';
|
|
@@ -24,43 +25,12 @@ import { SWM_RECIPIENT_KEY_FETCH_WAIT_MS, TIMEOUT_SENTINEL, CHAIN_POLICY_READ_TI
|
|
|
24
25
|
import { chainAuthorityReadBudgetsOf } from './chain-authority-read-budgets.js';
|
|
25
26
|
import { isBoundedOperationTimeoutError, runBoundedOperation, } from './bounded-operation.js';
|
|
26
27
|
import { StaleSenderKeyTargetError, SwmSenderKeySetupRejectionError, } from './dkg-agent-types.js';
|
|
27
|
-
import { swmSenderStateKey, swmReceiverStateKey, serializeSwmSenderSendState, serializeSwmSenderReceiveState, serializePendingSenderKeyEntry, deserializeSwmSenderSendState, deserializeSwmSenderReceiveState, deserializePendingSenderKeyEntry, } from './dkg-agent-swm-state.js';
|
|
28
|
+
import { computeSwmSenderKeyRecipientRouteHash, swmSenderStateKey, swmReceiverStateKey, serializeSwmSenderSendState, serializeSwmSenderReceiveState, serializePendingSenderKeyEntry, deserializeSwmSenderSendState, deserializeSwmSenderReceiveState, deserializePendingSenderKeyEntry, } from './dkg-agent-swm-state.js';
|
|
28
29
|
import { DKGAgentBase } from './dkg-agent-base.js';
|
|
29
30
|
import { isCanonicalPositiveContextGraphId, localContextGraphIdMatchesCommittedNameHash, } from './context-graph-binding-state.js';
|
|
30
31
|
import { CONTEXT_GRAPH_AUTHORITY_RPC_SITES as CG_AUTH_RPC_SITES, withRpcUsageSite, } from '@origintrail-official/dkg-chain';
|
|
31
32
|
const KA_LIFECYCLE_ASSET_UAL_RESOLVE_TIMEOUT_MS = 50;
|
|
32
|
-
|
|
33
|
-
if (expiresAtValues.length === 0)
|
|
34
|
-
return true;
|
|
35
|
-
return expiresAtValues.some((value) => {
|
|
36
|
-
const expiresAt = Number(value);
|
|
37
|
-
return !Number.isFinite(expiresAt) || expiresAt <= 0 || expiresAt >= nowMs;
|
|
38
|
-
});
|
|
39
|
-
}
|
|
40
|
-
function collectProjectedDelegatees(meta, field, normalizeValue) {
|
|
41
|
-
const members = new Set([...meta.allowedAgents, ...meta.participantAgents].map((agent) => agent.toLowerCase()));
|
|
42
|
-
const revoked = new Set(meta.revokedAgents.map((agent) => agent.toLowerCase()));
|
|
43
|
-
const out = new Map();
|
|
44
|
-
const nowMs = Date.now();
|
|
45
|
-
for (const delegation of meta.delegations) {
|
|
46
|
-
if (!delegationIsCurrentlyActive(delegation.expiresAtValues, nowMs))
|
|
47
|
-
continue;
|
|
48
|
-
for (const rawAgent of delegation.agents) {
|
|
49
|
-
const agent = rawAgent.toLowerCase();
|
|
50
|
-
if (!agent || !members.has(agent) || revoked.has(agent))
|
|
51
|
-
continue;
|
|
52
|
-
const values = out.get(agent) ?? [];
|
|
53
|
-
for (const rawValue of delegation[field]) {
|
|
54
|
-
const value = normalizeValue(rawValue);
|
|
55
|
-
if (value && !values.includes(value))
|
|
56
|
-
values.push(value);
|
|
57
|
-
}
|
|
58
|
-
if (values.length > 0)
|
|
59
|
-
out.set(agent, values);
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return out;
|
|
63
|
-
}
|
|
33
|
+
const SWM_RECIPIENT_AUTHORITY_STABILITY_ATTEMPTS = 3;
|
|
64
34
|
function mapContextGraphSlotBindingOutcome(outcome, mode) {
|
|
65
35
|
if (outcome.kind === 'match')
|
|
66
36
|
return true;
|
|
@@ -186,6 +156,49 @@ function withMemberKeyHint(error) {
|
|
|
186
156
|
+ (one ? 'its profile has' : 'their profiles have')
|
|
187
157
|
+ ' reached this node (profiles are re-published about every 20 minutes).');
|
|
188
158
|
}
|
|
159
|
+
function hasExactRecipientAgentRoster(resolution, participantAgents) {
|
|
160
|
+
try {
|
|
161
|
+
const resolvedAgents = new Set(resolution.recipients.map(({ agentAddress }) => ethers.getAddress(agentAddress).toLowerCase()));
|
|
162
|
+
const currentAgents = new Set(participantAgents.map((address) => ethers.getAddress(address).toLowerCase()));
|
|
163
|
+
return currentAgents.size === resolvedAgents.size
|
|
164
|
+
&& [...currentAgents].every((address) => resolvedAgents.has(address));
|
|
165
|
+
}
|
|
166
|
+
catch {
|
|
167
|
+
return false;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
function hasExactWorkspaceRecipientSet(left, right) {
|
|
171
|
+
const canonical = (recipient) => [
|
|
172
|
+
ethers.getAddress(recipient.agentAddress).toLowerCase(),
|
|
173
|
+
recipient.recipientKeyId,
|
|
174
|
+
recipient.peerId ?? '',
|
|
175
|
+
].join('\u0000');
|
|
176
|
+
try {
|
|
177
|
+
const leftRecipients = left.recipients.map(canonical).sort();
|
|
178
|
+
const rightRecipients = right.recipients.map(canonical).sort();
|
|
179
|
+
return leftRecipients.length === rightRecipients.length
|
|
180
|
+
&& leftRecipients.every((recipient, index) => recipient === rightRecipients[index]);
|
|
181
|
+
}
|
|
182
|
+
catch {
|
|
183
|
+
return false;
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
function sameStringSet(left, right) {
|
|
187
|
+
const leftSet = new Set(left);
|
|
188
|
+
const rightSet = new Set(right);
|
|
189
|
+
return leftSet.size === rightSet.size && [...leftSet].every((value) => rightSet.has(value));
|
|
190
|
+
}
|
|
191
|
+
function pendingSenderKeyEntryMatchesDrainAuthority(entry, authority) {
|
|
192
|
+
return entry.contextGraphId === authority.contextGraphId
|
|
193
|
+
&& (authority.allSubgraphs === true
|
|
194
|
+
|| (entry.subGraphName ?? undefined) === (authority.subGraphName ?? undefined))
|
|
195
|
+
&& entry.recipientAgentAddress === authority.recipientAgentAddress
|
|
196
|
+
&& entry.recipientKeyId === authority.recipientKeyId
|
|
197
|
+
&& entry.recipientPeerId === authority.recipientPeerId
|
|
198
|
+
&& (authority.senderAgentAddress === undefined
|
|
199
|
+
|| entry.senderAgentAddress === authority.senderAgentAddress)
|
|
200
|
+
&& (authority.epochId === undefined || entry.epochId === authority.epochId);
|
|
201
|
+
}
|
|
189
202
|
export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
190
203
|
getWorkspaceGossipSigningAgent() {
|
|
191
204
|
const defaultAddress = this.defaultAgentAddress?.toLowerCase();
|
|
@@ -241,8 +254,8 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
241
254
|
async resolveContextGraphAgentGateAuthority(contextGraphId, options = {}) {
|
|
242
255
|
return withRpcUsageSite(CG_AUTH_RPC_SITES.gate, () => resolveContextGraphAgentGateAuthorityDecision({
|
|
243
256
|
contextGraphId,
|
|
244
|
-
|
|
245
|
-
|
|
257
|
+
getTransportAuthority: () => this.resolveSwmTransportAuthority(contextGraphId, { signal: options.signal }),
|
|
258
|
+
readMetadataRevision: () => this.contextGraphMetaProjection.readAuthorityFactsRevision,
|
|
246
259
|
getLegacyMeta: () => this.getCgMeta(contextGraphId, { signal: options.signal }),
|
|
247
260
|
getSubscriptionAgents: () => (this.subscribedContextGraphs.get(contextGraphId)?.participantAgents ?? []),
|
|
248
261
|
}));
|
|
@@ -257,32 +270,89 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
257
270
|
return [];
|
|
258
271
|
}
|
|
259
272
|
/**
|
|
260
|
-
* R9 (SECURITY) —
|
|
273
|
+
* R9 (SECURITY) — authoritative member-recovery gate.
|
|
261
274
|
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
*
|
|
275
|
+
* Registered and active accepted-private graphs use their exact authority
|
|
276
|
+
* roster. Only a legacy unregistered graph (including accepted public, whose
|
|
277
|
+
* local gate controls recovery rather than reads) resolves the effective
|
|
278
|
+
* store-backed metadata projection of
|
|
279
|
+
* `allowedAgents ∪ participantAgents` minus `revokedAgents` from the CG
|
|
280
|
+
* metadata. The network-influenced `subscribedContextGraphs`
|
|
281
|
+
* subscription cache is DELIBERATELY OMITTED — that cache is poisonable, and
|
|
282
|
+
* folding it in is exactly what `member-recovery-auth.ts` forbids.
|
|
267
283
|
*
|
|
268
284
|
* Unlike {@link getContextGraphAgentGateAddresses} (which also feeds normal
|
|
269
285
|
* sync admission and may fold in the subscription cache only for graphs that
|
|
270
286
|
* are not registered on-chain), this read is used ONLY for `request.recovery`
|
|
271
287
|
* and is passed straight to
|
|
272
288
|
* `isMemberRecoveryAuthorized`, which hard-denies on null/empty. Returns
|
|
273
|
-
* `null` when the CG has no
|
|
289
|
+
* `null` when the CG has no projected agent gate at all (⇒ hard-deny).
|
|
274
290
|
*/
|
|
275
291
|
async getMemberRecoveryGate(contextGraphId, options = {}) {
|
|
276
|
-
const
|
|
277
|
-
if (
|
|
278
|
-
return
|
|
279
|
-
|
|
292
|
+
const recoveryAuthority = await withRpcUsageSite(CG_AUTH_RPC_SITES.recoveryGate, () => resolveSwmMemberRecoveryAuthorityDecision(this, contextGraphId, { signal: options.signal }));
|
|
293
|
+
if (recoveryAuthority.kind === 'private-roster') {
|
|
294
|
+
return [...recoveryAuthority.participantAgents];
|
|
295
|
+
}
|
|
296
|
+
if (recoveryAuthority.kind !== 'legacy-unregistered')
|
|
297
|
+
return null;
|
|
298
|
+
const metadataRevision = this.contextGraphMetaProjection
|
|
299
|
+
.readContextGraphAuthorityFactsRevision(contextGraphId);
|
|
300
|
+
const metadataGate = await this.getLocalMetadataMemberRecoveryGate(contextGraphId, options);
|
|
301
|
+
// Metadata is another async boundary. Re-resolve the authoritative state
|
|
302
|
+
// before returning it so a private policy activation/rotation or a chain
|
|
303
|
+
// registration that commits during the store read takes precedence over
|
|
304
|
+
// the now-stale local projection.
|
|
305
|
+
const currentRecoveryAuthority = await withRpcUsageSite(CG_AUTH_RPC_SITES.recoveryGate, () => resolveSwmMemberRecoveryAuthorityDecision(this, contextGraphId, { signal: options.signal }));
|
|
306
|
+
if (currentRecoveryAuthority.kind === 'private-roster') {
|
|
307
|
+
return [...currentRecoveryAuthority.participantAgents];
|
|
308
|
+
}
|
|
309
|
+
if (currentRecoveryAuthority.kind !== 'legacy-unregistered')
|
|
280
310
|
return null;
|
|
311
|
+
// A revocation or other authority-fact mutation during the metadata read
|
|
312
|
+
// invalidates the captured roster even if registration remained absent.
|
|
313
|
+
// Recovery is retryable, so fail closed instead of serving that snapshot.
|
|
314
|
+
return this.contextGraphMetaProjection
|
|
315
|
+
.readContextGraphAuthorityFactsRevision(contextGraphId) === metadataRevision
|
|
316
|
+
? metadataGate
|
|
317
|
+
: null;
|
|
318
|
+
}
|
|
319
|
+
/**
|
|
320
|
+
* Authority source used while constructing or mutating a private roster.
|
|
321
|
+
* Registered private graphs retain their finalized chain participant set;
|
|
322
|
+
* unregistered graphs use the effective store-backed metadata projection.
|
|
323
|
+
* This deliberately ignores an accepted-private overlay roster so rotation
|
|
324
|
+
* does not ask the roster it is replacing to define its own successor.
|
|
325
|
+
*/
|
|
326
|
+
async getMemberRecoveryRosterSource(contextGraphId, options = {}) {
|
|
327
|
+
const registered = await this.resolveSwmRegisteredAuthority(contextGraphId, { signal: options.signal });
|
|
328
|
+
if (registered.kind === 'private')
|
|
329
|
+
return [...registered.participantAgents];
|
|
330
|
+
if (registered.kind !== 'unregistered')
|
|
331
|
+
return null;
|
|
332
|
+
const metadataRevision = this.contextGraphMetaProjection
|
|
333
|
+
.readContextGraphAuthorityFactsRevision(contextGraphId);
|
|
334
|
+
const metadataGate = await this.getLocalMetadataMemberRecoveryGate(contextGraphId, options);
|
|
335
|
+
const currentRegistered = await this.resolveSwmRegisteredAuthority(contextGraphId, { signal: options.signal });
|
|
336
|
+
if (currentRegistered.kind === 'private') {
|
|
337
|
+
return [...currentRegistered.participantAgents];
|
|
338
|
+
}
|
|
339
|
+
if (currentRegistered.kind !== 'unregistered')
|
|
340
|
+
return null;
|
|
341
|
+
return this.contextGraphMetaProjection
|
|
342
|
+
.readContextGraphAuthorityFactsRevision(contextGraphId) === metadataRevision
|
|
343
|
+
? metadataGate
|
|
344
|
+
: null;
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Fresh effective store-backed metadata member set. This deliberately stays
|
|
348
|
+
* separate from the transport-authoritative recovery decision.
|
|
349
|
+
*/
|
|
350
|
+
async getLocalMetadataMemberRecoveryGate(contextGraphId, options = {}) {
|
|
281
351
|
const seen = new Set();
|
|
282
352
|
const agents = [];
|
|
283
353
|
const meta = await this.getCgMeta(contextGraphId, { signal: options.signal });
|
|
284
354
|
if (meta.allowedAgents.length === 0 && meta.participantAgents.length === 0) {
|
|
285
|
-
return null; // no
|
|
355
|
+
return null; // no projected agent gate ⇒ hard-deny at the recovery gate
|
|
286
356
|
}
|
|
287
357
|
const revoked = new Set(meta.revokedAgents.map((addr) => addr.toLowerCase()));
|
|
288
358
|
const add = (value) => {
|
|
@@ -600,6 +670,56 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
600
670
|
resolveSwmTransportAuthority(contextGraphId, options = {}) {
|
|
601
671
|
return resolveSwmTransportAuthorityDecision(this, contextGraphId, options);
|
|
602
672
|
}
|
|
673
|
+
/**
|
|
674
|
+
* Resolve the transport peer gate that applies to inbound SWM traffic.
|
|
675
|
+
*
|
|
676
|
+
* Ordinary graphs retain the legacy merged metadata projection. A graph
|
|
677
|
+
* carrying a durable local join approval must instead use the peer roster
|
|
678
|
+
* from the same source-qualified proof that authorized its private replica;
|
|
679
|
+
* falling back to the merged projection there would let an unrelated
|
|
680
|
+
* AGENTS/ONTOLOGY row widen the inbound gate. Registration (or a proven
|
|
681
|
+
* public policy) supersedes that participant proof and keeps the established
|
|
682
|
+
* registered/public behaviour.
|
|
683
|
+
*/
|
|
684
|
+
async resolveApprovedPrivateReplicaSwmAllowedPeersOverride(contextGraphId) {
|
|
685
|
+
if (!this.localApprovedAgentByCG.has(contextGraphId)) {
|
|
686
|
+
return undefined;
|
|
687
|
+
}
|
|
688
|
+
const transport = await this.resolveSwmTransportAuthority(contextGraphId, {
|
|
689
|
+
authorityReadMode: 'finalized-index-or-live',
|
|
690
|
+
});
|
|
691
|
+
switch (transport.kind) {
|
|
692
|
+
case 'approved-private-replica':
|
|
693
|
+
// An empty proved list is an authoritative open peer gate. Keep it
|
|
694
|
+
// distinct from `undefined` (this override does not apply) so the
|
|
695
|
+
// SharedMemoryHandler projection wrapper never falls back to a merged
|
|
696
|
+
// secondary-source row.
|
|
697
|
+
return [...new Set(transport.allowedPeers)];
|
|
698
|
+
case 'plaintext':
|
|
699
|
+
case 'private-roster':
|
|
700
|
+
return undefined;
|
|
701
|
+
case 'unavailable':
|
|
702
|
+
throw createContextGraphAuthorityError(`Context graph "${contextGraphId}" SWM peer gate authority is unavailable (${transport.reason})`, transport);
|
|
703
|
+
case 'legacy-unregistered':
|
|
704
|
+
// The approval may have been removed while the authority read was in
|
|
705
|
+
// flight. Only that transition restores ordinary legacy behaviour.
|
|
706
|
+
if (!this.localApprovedAgentByCG.has(contextGraphId)) {
|
|
707
|
+
return undefined;
|
|
708
|
+
}
|
|
709
|
+
throw createContextGraphAuthorityError(`Context graph "${contextGraphId}" still has a local join approval but no approved private replica authority`, {
|
|
710
|
+
reason: 'finalized-name-absence-unaccepted',
|
|
711
|
+
detail: 'approved private replica authority did not survive current SWM authority resolution',
|
|
712
|
+
});
|
|
713
|
+
}
|
|
714
|
+
}
|
|
715
|
+
/** List/null adapter for SWM consumers that do not consume meta records. */
|
|
716
|
+
async resolveSwmAllowedPeersForCurrentAuthority(contextGraphId) {
|
|
717
|
+
const approvedReplicaOverride = await this.resolveApprovedPrivateReplicaSwmAllowedPeersOverride(contextGraphId);
|
|
718
|
+
if (approvedReplicaOverride === undefined) {
|
|
719
|
+
return this.getContextGraphAllowedPeers(contextGraphId);
|
|
720
|
+
}
|
|
721
|
+
return approvedReplicaOverride.length > 0 ? approvedReplicaOverride : null;
|
|
722
|
+
}
|
|
603
723
|
/**
|
|
604
724
|
* Whether SWM on this graph is public-readable, so plaintext may be both
|
|
605
725
|
* sent and accepted. One predicate for both ends of the wire: the sender's
|
|
@@ -893,9 +1013,16 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
893
1013
|
* a public CG that never encrypts. Private graphs resolve recipients from
|
|
894
1014
|
* the live roster; unavailable registered authority fails closed. An
|
|
895
1015
|
* unregistered graph whose accepted owner-signed policy is public is
|
|
896
|
-
* treated like an on-chain public one.
|
|
1016
|
+
* treated like an on-chain public one. An approved private replica retains
|
|
1017
|
+
* the full effective metadata roster, while any explicit graph peer
|
|
1018
|
+
* allowlist restricts the recipient keys available to every roster member.
|
|
897
1019
|
*/
|
|
898
1020
|
async resolveWorkspaceRecipientsGated(input) {
|
|
1021
|
+
// Hydrate durable sender-key state before resolving recipients. The
|
|
1022
|
+
// publisher invokes the encryptor synchronously after this resolver, so
|
|
1023
|
+
// this keeps state I/O out of the authority-fenced interval between the
|
|
1024
|
+
// final recipient recheck and sender-key epoch selection/creation.
|
|
1025
|
+
await this.loadSwmSenderKeyState();
|
|
899
1026
|
return this.resolveWorkspaceAgentRecipientsForCurrentAuthority(input);
|
|
900
1027
|
}
|
|
901
1028
|
/**
|
|
@@ -924,9 +1051,53 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
924
1051
|
if (transport.kind === 'plaintext') {
|
|
925
1052
|
return { requiresEncryption: false, recipients: [] };
|
|
926
1053
|
}
|
|
1054
|
+
let recipientAuthorityRevision = null;
|
|
927
1055
|
let resolveKeys;
|
|
928
1056
|
if (transport.kind === 'legacy-unregistered') {
|
|
929
|
-
resolveKeys = () =>
|
|
1057
|
+
resolveKeys = async () => {
|
|
1058
|
+
const attemptRevision = this.contextGraphMetaProjection.readAuthorityFactsRevision;
|
|
1059
|
+
const resolution = await resolveWorkspaceAgentRecipients(this.store, input);
|
|
1060
|
+
recipientAuthorityRevision = attemptRevision;
|
|
1061
|
+
return resolution;
|
|
1062
|
+
};
|
|
1063
|
+
}
|
|
1064
|
+
else if (transport.kind === 'approved-private-replica') {
|
|
1065
|
+
// Resolve the complete effective metadata roster first. Participant
|
|
1066
|
+
// approval authorizes this receiver; it is not a replacement roster.
|
|
1067
|
+
// The peer gate comes from the same source-qualified, revision-fenced
|
|
1068
|
+
// proof as the transport classification, never the merged projection.
|
|
1069
|
+
resolveKeys = async () => {
|
|
1070
|
+
const attemptRevision = this.contextGraphMetaProjection.readAuthorityFactsRevision;
|
|
1071
|
+
const resolution = await resolveWorkspaceAgentRecipients(this.store, input);
|
|
1072
|
+
if (!resolution.requiresEncryption) {
|
|
1073
|
+
throw new Error(`Approved private replica "${input.contextGraphId}" resolved a plaintext SWM roster`);
|
|
1074
|
+
}
|
|
1075
|
+
if (transport.allowedPeers.length === 0) {
|
|
1076
|
+
recipientAuthorityRevision = attemptRevision;
|
|
1077
|
+
return resolution;
|
|
1078
|
+
}
|
|
1079
|
+
const allowedPeerSet = new Set(transport.allowedPeers);
|
|
1080
|
+
const effectiveAgentAddresses = new Set(resolution.recipients.map(({ agentAddress }) => agentAddress.toLowerCase()));
|
|
1081
|
+
const authorizedRecipients = resolution.recipients.filter((recipient) => (recipient.peerId !== undefined && allowedPeerSet.has(recipient.peerId)));
|
|
1082
|
+
const authorizedAgentAddresses = new Set(authorizedRecipients.map(({ agentAddress }) => agentAddress.toLowerCase()));
|
|
1083
|
+
for (const agentAddress of effectiveAgentAddresses) {
|
|
1084
|
+
if (!authorizedAgentAddresses.has(agentAddress)) {
|
|
1085
|
+
throw new Error(`Approved private replica "${input.contextGraphId}" requires encrypted SWM gossip but `
|
|
1086
|
+
+ `effective DKG agent ${ethers.getAddress(agentAddress)} has no recipient key `
|
|
1087
|
+
+ 'advertised by a peer in the context graph allowlist');
|
|
1088
|
+
}
|
|
1089
|
+
}
|
|
1090
|
+
const [firstRecipient, ...remainingRecipients] = authorizedRecipients;
|
|
1091
|
+
if (!firstRecipient) {
|
|
1092
|
+
throw new Error(`Approved private replica "${input.contextGraphId}" requires encrypted SWM gossip but has no allowed DKG agent recipients`);
|
|
1093
|
+
}
|
|
1094
|
+
const authorizedResolution = {
|
|
1095
|
+
requiresEncryption: true,
|
|
1096
|
+
recipients: [firstRecipient, ...remainingRecipients],
|
|
1097
|
+
};
|
|
1098
|
+
recipientAuthorityRevision = attemptRevision;
|
|
1099
|
+
return authorizedResolution;
|
|
1100
|
+
};
|
|
930
1101
|
}
|
|
931
1102
|
else {
|
|
932
1103
|
if (transport.kind === 'unavailable') {
|
|
@@ -937,14 +1108,20 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
937
1108
|
if (participantAgents.length === 0) {
|
|
938
1109
|
throw new Error(`Registered context graph "${input.contextGraphId}" requires encrypted SWM gossip but its authoritative chain roster is empty or unavailable`);
|
|
939
1110
|
}
|
|
940
|
-
const allowedPeers = await this.getContextGraphAllowedPeers(input.contextGraphId);
|
|
941
|
-
const allowedPeerSet = allowedPeers === null ? null : new Set(allowedPeers);
|
|
942
1111
|
// Resolve only the live chain-authorized addresses. Filtering a completed
|
|
943
1112
|
// local resolution afterward is too late: stale removed members can have
|
|
944
1113
|
// malformed/missing key metadata that makes the local resolver throw
|
|
945
1114
|
// before the chain intersection is reached, blocking every post-revoke
|
|
946
1115
|
// write until the local cleanup retry succeeds.
|
|
947
1116
|
resolveKeys = async () => {
|
|
1117
|
+
// The registered/accepted private roster, local peer gate, and verified
|
|
1118
|
+
// recipient-key routes are conjunctive authorities. Capture their shared
|
|
1119
|
+
// revision before every attempt so a phonebook-hydration retry owns a new
|
|
1120
|
+
// snapshot, while the final transport recheck cannot return a route
|
|
1121
|
+
// removed or revoked meanwhile.
|
|
1122
|
+
const attemptRevision = this.contextGraphMetaProjection.readAuthorityFactsRevision;
|
|
1123
|
+
const allowedPeers = await this.getContextGraphAllowedPeers(input.contextGraphId);
|
|
1124
|
+
const allowedPeerSet = allowedPeers === null ? null : new Set(allowedPeers);
|
|
948
1125
|
const recipients = [];
|
|
949
1126
|
// Every member without a key is named at once, so one phonebook fetch
|
|
950
1127
|
// can cover them all (#2849). Any other key failure still stops here.
|
|
@@ -976,6 +1153,10 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
976
1153
|
if (!firstRecipient) {
|
|
977
1154
|
throw new Error(`Registered context graph "${input.contextGraphId}" requires encrypted SWM gossip but has no chain-authorized DKG agent recipients`);
|
|
978
1155
|
}
|
|
1156
|
+
// Publish only the snapshot owned by the attempt that actually
|
|
1157
|
+
// completed. A failed pre-hydration attempt must not pin its revision
|
|
1158
|
+
// across the phonebook write and the successful retry.
|
|
1159
|
+
recipientAuthorityRevision = attemptRevision;
|
|
979
1160
|
return {
|
|
980
1161
|
requiresEncryption: true,
|
|
981
1162
|
recipients: [firstRecipient, ...remainingRecipients],
|
|
@@ -987,31 +1168,105 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
987
1168
|
// every member whose key is missing, then the keys are resolved once more.
|
|
988
1169
|
// Every member still needs a key, so a share that cannot get one stays
|
|
989
1170
|
// closed.
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1171
|
+
const resolveWithPhonebookHydration = async () => {
|
|
1172
|
+
try {
|
|
1173
|
+
return await resolveKeys();
|
|
1174
|
+
}
|
|
1175
|
+
catch (error) {
|
|
1176
|
+
if (!isWorkspaceAgentEncryptionKeyMissingError(error))
|
|
1177
|
+
throw error;
|
|
1178
|
+
if (typeof this.ensureAgentsInOnDemandPhonebook !== 'function') {
|
|
1179
|
+
throw withMemberKeyHint(error);
|
|
1180
|
+
}
|
|
1181
|
+
await this.ensureAgentsInOnDemandPhonebook(error.agentAddresses.map((address) => address.toLowerCase()), AbortSignal.timeout(SWM_RECIPIENT_KEY_FETCH_WAIT_MS));
|
|
1182
|
+
}
|
|
1183
|
+
try {
|
|
1184
|
+
return await resolveKeys();
|
|
1185
|
+
}
|
|
1186
|
+
catch (error) {
|
|
1187
|
+
throw isWorkspaceAgentEncryptionKeyMissingError(error) ? withMemberKeyHint(error) : error;
|
|
1188
|
+
}
|
|
1189
|
+
};
|
|
1190
|
+
let resolution = await resolveWithPhonebookHydration();
|
|
1191
|
+
if (transport.kind === 'private-roster'
|
|
1192
|
+
|| transport.kind === 'approved-private-replica'
|
|
1193
|
+
|| transport.kind === 'legacy-unregistered') {
|
|
1194
|
+
// Key and phonebook lookup are asynchronous. Re-read the paired
|
|
1195
|
+
// transport decision after them so an accepted-private (or registered)
|
|
1196
|
+
// roster rotation cannot return keys for a member removed meanwhile.
|
|
1197
|
+
// The store revision is deliberately conservative because recipient
|
|
1198
|
+
// facts can live in any named graph. Unrelated graph activity therefore
|
|
1199
|
+
// triggers a bounded optimistic retry, but a retry is accepted only when
|
|
1200
|
+
// both the exact (agent,key,peer) set and transport authority are stable.
|
|
1201
|
+
for (let attempt = 0; attempt < SWM_RECIPIENT_AUTHORITY_STABILITY_ATTEMPTS; attempt += 1) {
|
|
1202
|
+
const currentTransport = await withRpcUsageSite(CG_AUTH_RPC_SITES.recipients, () => this.resolveSwmTransportAuthority(input.contextGraphId, {
|
|
1203
|
+
authorityReadMode: 'finalized-index-or-live',
|
|
1204
|
+
requireLiveRosterForPrivate: true,
|
|
1205
|
+
}));
|
|
1206
|
+
if (currentTransport.kind === 'unavailable') {
|
|
1207
|
+
throw createContextGraphAuthorityError(`Context graph "${input.contextGraphId}" recipient authority is unavailable (${currentTransport.reason})`, currentTransport);
|
|
1208
|
+
}
|
|
1209
|
+
const transportStayedCurrent = transport.kind === 'private-roster'
|
|
1210
|
+
? currentTransport.kind === 'private-roster'
|
|
1211
|
+
&& resolution.requiresEncryption
|
|
1212
|
+
&& hasExactRecipientAgentRoster(resolution, currentTransport.participantAgents)
|
|
1213
|
+
: transport.kind === 'approved-private-replica'
|
|
1214
|
+
? currentTransport.kind === 'approved-private-replica'
|
|
1215
|
+
&& sameStringSet(transport.allowedPeers, currentTransport.allowedPeers)
|
|
1216
|
+
: currentTransport.kind === 'legacy-unregistered';
|
|
1217
|
+
const revisionStayedCurrent = recipientAuthorityRevision
|
|
1218
|
+
=== this.contextGraphMetaProjection.readAuthorityFactsRevision;
|
|
1219
|
+
if (transportStayedCurrent && revisionStayedCurrent)
|
|
1220
|
+
break;
|
|
1221
|
+
const hasAnotherAttempt = attempt + 1 < SWM_RECIPIENT_AUTHORITY_STABILITY_ATTEMPTS;
|
|
1222
|
+
if (!transportStayedCurrent || !hasAnotherAttempt) {
|
|
1223
|
+
throw createContextGraphAuthorityError(`Context graph "${input.contextGraphId}" private authority changed while recipient keys were resolving`, {
|
|
1224
|
+
reason: 'chain-participant-authority-unavailable',
|
|
1225
|
+
detail: 'retry recipient resolution against the current private authority',
|
|
1226
|
+
});
|
|
1227
|
+
}
|
|
1228
|
+
const retriedResolution = await resolveWithPhonebookHydration();
|
|
1229
|
+
const recipientSnapshotStayedCurrent = resolution.requiresEncryption
|
|
1230
|
+
? retriedResolution.requiresEncryption
|
|
1231
|
+
&& hasExactWorkspaceRecipientSet(resolution, retriedResolution)
|
|
1232
|
+
: !retriedResolution.requiresEncryption;
|
|
1233
|
+
if (!recipientSnapshotStayedCurrent) {
|
|
1234
|
+
throw createContextGraphAuthorityError(`Context graph "${input.contextGraphId}" private authority changed while recipient keys were resolving`, {
|
|
1235
|
+
reason: 'chain-participant-authority-unavailable',
|
|
1236
|
+
detail: 'recipient routes changed while retrying against current private authority',
|
|
1237
|
+
});
|
|
1238
|
+
}
|
|
1239
|
+
resolution = retriedResolution;
|
|
1240
|
+
}
|
|
1005
1241
|
}
|
|
1242
|
+
return resolution;
|
|
1006
1243
|
}
|
|
1007
1244
|
async encryptWorkspacePayloadWithSenderKey(input) {
|
|
1008
|
-
|
|
1245
|
+
let resolution = input.resolution;
|
|
1246
|
+
if (!this.swmSenderKeyStateLoaded) {
|
|
1247
|
+
await this.loadSwmSenderKeyState();
|
|
1248
|
+
// Recipient authority may have changed while durable state was loading.
|
|
1249
|
+
// Resolve once against the now-hydrated state and proceed only when the
|
|
1250
|
+
// exact agent/key/peer snapshot is unchanged. The production publisher
|
|
1251
|
+
// avoids this compatibility reread by hydrating in
|
|
1252
|
+
// resolveWorkspaceRecipientsGated above.
|
|
1253
|
+
const currentResolution = await this.resolveWorkspaceAgentRecipientsForCurrentAuthority({
|
|
1254
|
+
contextGraphId: input.contextGraphId,
|
|
1255
|
+
});
|
|
1256
|
+
if (!currentResolution.requiresEncryption
|
|
1257
|
+
|| !hasExactWorkspaceRecipientSet(resolution, currentResolution)) {
|
|
1258
|
+
throw createContextGraphAuthorityError(`Context graph "${input.contextGraphId}" authority changed while sender-key state was loading`, {
|
|
1259
|
+
reason: 'chain-participant-authority-unavailable',
|
|
1260
|
+
detail: 'retry recipient resolution before creating or reusing a sender-key epoch',
|
|
1261
|
+
});
|
|
1262
|
+
}
|
|
1263
|
+
resolution = currentResolution;
|
|
1264
|
+
}
|
|
1009
1265
|
const ctx = createOperationContext('share', input.operationId);
|
|
1010
1266
|
const sender = this.getLocalSigningAgentForAddress(input.senderAgentAddress);
|
|
1011
1267
|
if (!sender) {
|
|
1012
1268
|
throw new Error(`Cannot create SWM Sender Key epoch: no local custodial signing key for agent ${input.senderAgentAddress}`);
|
|
1013
1269
|
}
|
|
1014
|
-
const resolution = input.resolution;
|
|
1015
1270
|
const senderAddress = ethers.getAddress(sender.agentAddress);
|
|
1016
1271
|
const recipientSet = new Set(resolution.recipients.map((recipient) => recipient.agentAddress.toLowerCase()));
|
|
1017
1272
|
if (!recipientSet.has(senderAddress.toLowerCase())) {
|
|
@@ -1030,9 +1285,16 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
1030
1285
|
recipientKeyId: recipient.recipientKeyId,
|
|
1031
1286
|
})),
|
|
1032
1287
|
});
|
|
1288
|
+
const recipientRouteHash = computeSwmSenderKeyRecipientRouteHash({
|
|
1289
|
+
contextGraphId: input.contextGraphId,
|
|
1290
|
+
subGraphName: input.subGraphName,
|
|
1291
|
+
recipients: resolution.recipients,
|
|
1292
|
+
});
|
|
1033
1293
|
const stateKey = swmSenderStateKey(input.contextGraphId, input.subGraphName, senderAddress);
|
|
1034
1294
|
let state = this.swmSenderKeySendStates.get(stateKey);
|
|
1035
|
-
if (!state
|
|
1295
|
+
if (!state
|
|
1296
|
+
|| state.membershipHash !== membershipHash
|
|
1297
|
+
|| state.recipientRouteHash !== recipientRouteHash) {
|
|
1036
1298
|
const pruned = this.prunePendingSenderKeysForEpochRotation({
|
|
1037
1299
|
contextGraphId: input.contextGraphId,
|
|
1038
1300
|
subGraphName: input.subGraphName,
|
|
@@ -1055,7 +1317,12 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
1055
1317
|
await this.saveSwmSenderKeyState();
|
|
1056
1318
|
}
|
|
1057
1319
|
else {
|
|
1058
|
-
await this.drainPendingSenderKeyForRecipients(resolution.recipients, ctx
|
|
1320
|
+
await this.drainPendingSenderKeyForRecipients(resolution.recipients, ctx, {
|
|
1321
|
+
contextGraphId: input.contextGraphId,
|
|
1322
|
+
subGraphName: input.subGraphName,
|
|
1323
|
+
senderAgentAddress: state.senderAgentAddress,
|
|
1324
|
+
epochId: state.epochId,
|
|
1325
|
+
});
|
|
1059
1326
|
}
|
|
1060
1327
|
const encrypted = await encryptSwmSenderKeyMessage({
|
|
1061
1328
|
chainKey: state.chainKey,
|
|
@@ -1090,6 +1357,11 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
1090
1357
|
senderAgentAddress,
|
|
1091
1358
|
epochId,
|
|
1092
1359
|
membershipHash: input.membershipHash,
|
|
1360
|
+
recipientRouteHash: computeSwmSenderKeyRecipientRouteHash({
|
|
1361
|
+
contextGraphId: input.contextGraphId,
|
|
1362
|
+
subGraphName: input.subGraphName,
|
|
1363
|
+
recipients: input.recipients,
|
|
1364
|
+
}),
|
|
1093
1365
|
chainKey,
|
|
1094
1366
|
nextMessageIndex: 0,
|
|
1095
1367
|
senderSigningSecretKey: senderSigningKeypair.secretKey,
|
|
@@ -1105,7 +1377,18 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
1105
1377
|
senderPrivateKey: input.sender.privateKey,
|
|
1106
1378
|
});
|
|
1107
1379
|
const packageBytes = encodeSwmSenderKeyPackage(pkg);
|
|
1108
|
-
|
|
1380
|
+
const recipientIsLocal = this.hasLocalAgent(recipientAgentAddress);
|
|
1381
|
+
let targetsLocalPeer = recipient.peerId === undefined;
|
|
1382
|
+
if (recipientIsLocal && recipient.peerId !== undefined) {
|
|
1383
|
+
const localPeerId = this.node.peerId.toString();
|
|
1384
|
+
const canonicalRecipientPeerId = tryCanonicalPeerIdString(recipient.peerId);
|
|
1385
|
+
const canonicalLocalPeerId = tryCanonicalPeerIdString(localPeerId);
|
|
1386
|
+
targetsLocalPeer = recipient.peerId === localPeerId
|
|
1387
|
+
|| (canonicalRecipientPeerId !== null
|
|
1388
|
+
&& canonicalLocalPeerId !== null
|
|
1389
|
+
&& canonicalRecipientPeerId === canonicalLocalPeerId);
|
|
1390
|
+
}
|
|
1391
|
+
if (recipientIsLocal && targetsLocalPeer) {
|
|
1109
1392
|
try {
|
|
1110
1393
|
await this.acceptSwmSenderKeyPackage(pkg, this.node.peerId.toString(), input.ctx);
|
|
1111
1394
|
return { kind: 'success', agentAddress: recipientAgentAddress };
|
|
@@ -1142,7 +1425,7 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
1142
1425
|
});
|
|
1143
1426
|
pendingSenderKeyQueued = true;
|
|
1144
1427
|
this.log.warn(input.ctx, `SWM sender-key setup for ${recipientAgentAddress} keyId=${recipient.recipientKeyId} ` +
|
|
1145
|
-
`queued (no advertised peerId) — will deliver
|
|
1428
|
+
`queued (no advertised peerId) — will deliver after a verified peer route is learned`);
|
|
1146
1429
|
return { kind: 'success', agentAddress: recipientAgentAddress };
|
|
1147
1430
|
}
|
|
1148
1431
|
this.log.info(input.ctx, `SWM sender-key setup send: senderAgent=${senderAgentAddress} recipientAgent=${recipientAgentAddress} ` +
|
|
@@ -1183,6 +1466,7 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
1183
1466
|
senderAgentAddress: senderAgentAddress.toLowerCase(),
|
|
1184
1467
|
recipientAgentAddress: recipientAgentAddress.toLowerCase(),
|
|
1185
1468
|
recipientKeyId: recipient.recipientKeyId,
|
|
1469
|
+
recipientPeerId: recipient.peerId,
|
|
1186
1470
|
epochId: state.epochId,
|
|
1187
1471
|
contextGraphId: state.contextGraphId,
|
|
1188
1472
|
subGraphName: state.subGraphName,
|
|
@@ -1204,6 +1488,7 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
1204
1488
|
senderAgentAddress: senderAgentAddress.toLowerCase(),
|
|
1205
1489
|
recipientAgentAddress: recipientAgentAddress.toLowerCase(),
|
|
1206
1490
|
recipientKeyId: recipient.recipientKeyId,
|
|
1491
|
+
recipientPeerId: recipient.peerId,
|
|
1207
1492
|
epochId: state.epochId,
|
|
1208
1493
|
contextGraphId: state.contextGraphId,
|
|
1209
1494
|
subGraphName: state.subGraphName,
|
|
@@ -1222,6 +1507,7 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
1222
1507
|
senderAgentAddress: senderAgentAddress.toLowerCase(),
|
|
1223
1508
|
recipientAgentAddress: recipientAgentAddress.toLowerCase(),
|
|
1224
1509
|
recipientKeyId: recipient.recipientKeyId,
|
|
1510
|
+
recipientPeerId: recipient.peerId,
|
|
1225
1511
|
epochId: state.epochId,
|
|
1226
1512
|
contextGraphId: state.contextGraphId,
|
|
1227
1513
|
subGraphName: state.subGraphName,
|
|
@@ -1241,6 +1527,7 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
1241
1527
|
senderAgentAddress: senderAgentAddress.toLowerCase(),
|
|
1242
1528
|
recipientAgentAddress: recipientAgentAddress.toLowerCase(),
|
|
1243
1529
|
recipientKeyId: recipient.recipientKeyId,
|
|
1530
|
+
recipientPeerId: recipient.peerId,
|
|
1244
1531
|
epochId: state.epochId,
|
|
1245
1532
|
contextGraphId: state.contextGraphId,
|
|
1246
1533
|
subGraphName: state.subGraphName,
|
|
@@ -1349,26 +1636,34 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
1349
1636
|
/**
|
|
1350
1637
|
* PR-2 (SWM-fanout plan): enqueue a sender-key package whose recipient
|
|
1351
1638
|
* has no advertised `dkg:peerId` (so we can't even ask the messenger
|
|
1352
|
-
* to queue it). Older epochs for the same `(sender, recipient
|
|
1353
|
-
* are evicted — a newer epoch supersedes them by
|
|
1639
|
+
* to queue it). Older epochs for the same `(sender, recipient, context
|
|
1640
|
+
* graph, subgraph)` scope are evicted — a newer epoch supersedes them by
|
|
1641
|
+
* definition. Other graph/subgraph obligations remain independent.
|
|
1354
1642
|
*
|
|
1355
|
-
* Per-
|
|
1356
|
-
* matches an existing row, we replace it
|
|
1643
|
+
* Per-route dedup: `(senderAgentAddress, recipientKeyId,
|
|
1644
|
+
* recipientPeerId, epochId)` matches an existing row, we replace it
|
|
1645
|
+
* (idempotent re-enqueue). Distinct peers advertising the same key retain
|
|
1646
|
+
* independent delivery obligations.
|
|
1357
1647
|
*/
|
|
1358
1648
|
enqueuePendingSenderKey(entry) {
|
|
1359
1649
|
const recipientKey = entry.recipientAgentAddress.toLowerCase();
|
|
1360
1650
|
const existing = this.pendingSenderKeyByAgent.get(recipientKey) ?? [];
|
|
1361
|
-
// Drop older epochs
|
|
1362
|
-
// epoch's membership
|
|
1363
|
-
//
|
|
1651
|
+
// Drop older epochs only within the same sender + graph/subgraph scope;
|
|
1652
|
+
// the newer epoch's membership hash supersedes those rows. The map key
|
|
1653
|
+
// already scopes recipient agent. Keep every other scope unchanged.
|
|
1364
1654
|
const filtered = existing.filter((e) => {
|
|
1365
1655
|
if (e.senderAgentAddress !== entry.senderAgentAddress)
|
|
1366
1656
|
return true;
|
|
1657
|
+
if (e.contextGraphId !== entry.contextGraphId
|
|
1658
|
+
|| (e.subGraphName ?? undefined) !== (entry.subGraphName ?? undefined)) {
|
|
1659
|
+
return true;
|
|
1660
|
+
}
|
|
1367
1661
|
if (e.epochId === entry.epochId) {
|
|
1368
|
-
// Same epoch: dedupe
|
|
1369
|
-
//
|
|
1370
|
-
//
|
|
1371
|
-
return e.recipientKeyId !== entry.recipientKeyId
|
|
1662
|
+
// Same epoch: dedupe only the exact key + peer route. A successful
|
|
1663
|
+
// ACK from one peer must never overwrite a retry owed to another peer
|
|
1664
|
+
// that advertises the same agent/key pair.
|
|
1665
|
+
return e.recipientKeyId !== entry.recipientKeyId
|
|
1666
|
+
|| e.recipientPeerId !== entry.recipientPeerId;
|
|
1372
1667
|
}
|
|
1373
1668
|
return false;
|
|
1374
1669
|
});
|
|
@@ -1397,17 +1692,21 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
1397
1692
|
return removed;
|
|
1398
1693
|
}
|
|
1399
1694
|
async drainPendingSenderKeyQueueForPeer(input) {
|
|
1400
|
-
const
|
|
1695
|
+
const authority = {
|
|
1696
|
+
...input.authority,
|
|
1697
|
+
recipientAgentAddress: input.authority.recipientAgentAddress.toLowerCase(),
|
|
1698
|
+
senderAgentAddress: input.authority.senderAgentAddress?.toLowerCase(),
|
|
1699
|
+
};
|
|
1700
|
+
const recipientAgentAddress = authority.recipientAgentAddress;
|
|
1401
1701
|
const existingDrain = this.pendingSenderKeyDrainByAgent.get(recipientAgentAddress);
|
|
1402
1702
|
if (existingDrain) {
|
|
1403
1703
|
await existingDrain;
|
|
1404
1704
|
if (!this.pendingSenderKeyByAgent.has(recipientAgentAddress))
|
|
1405
1705
|
return 0;
|
|
1406
|
-
return this.drainPendingSenderKeyQueueForPeer(input);
|
|
1706
|
+
return this.drainPendingSenderKeyQueueForPeer({ authority, ctx: input.ctx });
|
|
1407
1707
|
}
|
|
1408
1708
|
const drain = this.drainPendingSenderKeyQueueForPeerLocked({
|
|
1409
|
-
|
|
1410
|
-
recipientAgentAddress,
|
|
1709
|
+
authority,
|
|
1411
1710
|
ctx: input.ctx,
|
|
1412
1711
|
}).finally(() => {
|
|
1413
1712
|
if (this.pendingSenderKeyDrainByAgent.get(recipientAgentAddress) === drain) {
|
|
@@ -1418,19 +1717,75 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
1418
1717
|
return drain;
|
|
1419
1718
|
}
|
|
1420
1719
|
async drainPendingSenderKeyQueueForPeerLocked(input) {
|
|
1421
|
-
const
|
|
1720
|
+
const { authority } = input;
|
|
1721
|
+
const recipientAgentAddress = authority.recipientAgentAddress;
|
|
1422
1722
|
const queue = this.pendingSenderKeyByAgent.get(recipientAgentAddress);
|
|
1423
1723
|
if (!queue || queue.length === 0)
|
|
1424
1724
|
return 0;
|
|
1425
1725
|
let drained = 0;
|
|
1426
|
-
|
|
1726
|
+
// `sendReliable` yields. Enqueue/epoch rotation can replace the live array
|
|
1727
|
+
// while this drain is in flight, so record outcomes against the exact
|
|
1728
|
+
// snapshot objects instead of later overwriting the whole map entry. At
|
|
1729
|
+
// commit, only original objects still present in the live queue are
|
|
1730
|
+
// transformed; concurrently inserted replacements/new scopes survive and
|
|
1731
|
+
// originals concurrently superseded by a new epoch are not resurrected.
|
|
1732
|
+
const outcomes = new Map();
|
|
1733
|
+
const commitOutcomes = () => {
|
|
1734
|
+
const live = this.pendingSenderKeyByAgent.get(recipientAgentAddress) ?? [];
|
|
1735
|
+
const reconciled = [];
|
|
1736
|
+
for (const liveEntry of live) {
|
|
1737
|
+
if (!outcomes.has(liveEntry)) {
|
|
1738
|
+
reconciled.push(liveEntry);
|
|
1739
|
+
continue;
|
|
1740
|
+
}
|
|
1741
|
+
const replacement = outcomes.get(liveEntry);
|
|
1742
|
+
if (replacement)
|
|
1743
|
+
reconciled.push(replacement);
|
|
1744
|
+
}
|
|
1745
|
+
if (reconciled.length === 0) {
|
|
1746
|
+
this.pendingSenderKeyByAgent.delete(recipientAgentAddress);
|
|
1747
|
+
}
|
|
1748
|
+
else {
|
|
1749
|
+
this.pendingSenderKeyByAgent.set(recipientAgentAddress, reconciled);
|
|
1750
|
+
}
|
|
1751
|
+
};
|
|
1427
1752
|
for (let i = 0; i < queue.length; i += 1) {
|
|
1428
1753
|
const entry = queue[i];
|
|
1754
|
+
// A row may only be consumed by the caller's exact current-authority
|
|
1755
|
+
// route and graph scope. Legacy/no-peer rows are expanded into explicit
|
|
1756
|
+
// peer-bound obligations before reaching this drain. Foreground callers
|
|
1757
|
+
// additionally bind the current sender + epoch; connection-open callers
|
|
1758
|
+
// explicitly authorize every subgraph for one resolved CG.
|
|
1759
|
+
if (!pendingSenderKeyEntryMatchesDrainAuthority(entry, authority)) {
|
|
1760
|
+
outcomes.set(entry, entry);
|
|
1761
|
+
continue;
|
|
1762
|
+
}
|
|
1763
|
+
// Authority can change while this route waits for the per-agent drain
|
|
1764
|
+
// lock, or while a preceding row is in flight. Re-resolve immediately
|
|
1765
|
+
// before every send so a stale caller snapshot can never consume a row.
|
|
1766
|
+
let routeStillCurrent = false;
|
|
1767
|
+
try {
|
|
1768
|
+
const resolution = await this.resolveWorkspaceAgentRecipientsForCurrentAuthority({
|
|
1769
|
+
contextGraphId: entry.contextGraphId,
|
|
1770
|
+
});
|
|
1771
|
+
routeStillCurrent = resolution.requiresEncryption
|
|
1772
|
+
&& resolution.recipients.some((recipient) => (recipient.agentAddress.toLowerCase() === entry.recipientAgentAddress
|
|
1773
|
+
&& recipient.recipientKeyId === entry.recipientKeyId
|
|
1774
|
+
&& recipient.peerId === entry.recipientPeerId));
|
|
1775
|
+
}
|
|
1776
|
+
catch {
|
|
1777
|
+
// Fail closed. The queued obligation remains available for a later
|
|
1778
|
+
// current-authority retry.
|
|
1779
|
+
}
|
|
1780
|
+
if (!routeStillCurrent) {
|
|
1781
|
+
outcomes.set(entry, entry);
|
|
1782
|
+
continue;
|
|
1783
|
+
}
|
|
1429
1784
|
try {
|
|
1430
|
-
const sendResult = await this.messenger.sendReliable(
|
|
1785
|
+
const sendResult = await this.messenger.sendReliable(authority.recipientPeerId, PROTOCOL_SWM_SENDER_KEY, entry.packageBytes, { messageId: this.swmSenderKeyPendingMessageId(entry) });
|
|
1431
1786
|
if (!sendResult.delivered) {
|
|
1432
1787
|
if (sendResult.queued || ('inFlight' in sendResult && sendResult.inFlight)) {
|
|
1433
|
-
|
|
1788
|
+
outcomes.set(entry, entry);
|
|
1434
1789
|
continue;
|
|
1435
1790
|
}
|
|
1436
1791
|
throw new Error(`Unexpected undelivered Sender Key retry result: ${sendResult.error}`);
|
|
@@ -1442,65 +1797,134 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
1442
1797
|
catch {
|
|
1443
1798
|
// Malformed/legacy ACK: no positive acceptance yet. Keep the
|
|
1444
1799
|
// row queued so a mixed-version rollout cannot strand the recipient.
|
|
1445
|
-
|
|
1800
|
+
outcomes.set(entry, this.rotateSwmSenderKeyPendingMessageId(entry));
|
|
1446
1801
|
continue;
|
|
1447
1802
|
}
|
|
1448
1803
|
if (ack.version !== SWM_SENDER_KEY_PACKAGE_VERSION ||
|
|
1449
1804
|
ack.type !== SWM_SENDER_KEY_PACKAGE_ACK_TYPE) {
|
|
1450
1805
|
// Malformed/legacy ACK: no positive acceptance yet. Keep the
|
|
1451
1806
|
// row queued so a mixed-version rollout cannot strand the recipient.
|
|
1452
|
-
|
|
1807
|
+
outcomes.set(entry, this.rotateSwmSenderKeyPendingMessageId(entry));
|
|
1453
1808
|
continue;
|
|
1454
1809
|
}
|
|
1455
1810
|
if (ack.accepted) {
|
|
1456
1811
|
drained += 1;
|
|
1812
|
+
outcomes.set(entry, null);
|
|
1457
1813
|
}
|
|
1458
1814
|
else if (this.isRetryableSwmSenderKeySetupAckReason(ack.reasonCode)) {
|
|
1459
|
-
|
|
1815
|
+
outcomes.set(entry, this.rotateSwmSenderKeyPendingMessageId(entry));
|
|
1460
1816
|
}
|
|
1461
1817
|
else {
|
|
1462
1818
|
const reason = ack.reason ?? 'unknown reason';
|
|
1463
1819
|
const reasonCode = ack.reasonCode ?? 'legacy-unknown';
|
|
1464
1820
|
this.log.warn(input.ctx ?? SWM_SENDER_KEY_PENDING_DRAIN_LOG_CTX, `SWM sender-key pending retry for ${entry.recipientAgentAddress} keyId=${entry.recipientKeyId} ` +
|
|
1465
|
-
`peerId=${
|
|
1821
|
+
`peerId=${authority.recipientPeerId} contextGraph=${entry.contextGraphId}${entry.subGraphName ? `/${entry.subGraphName}` : ''} ` +
|
|
1466
1822
|
`dropped after terminal rejection (${reasonCode}): ${reason}`);
|
|
1467
1823
|
// Terminal rejection: keep it out of the queue, but do not
|
|
1468
1824
|
// report it as a successful drain.
|
|
1825
|
+
outcomes.set(entry, null);
|
|
1469
1826
|
}
|
|
1470
1827
|
}
|
|
1471
1828
|
catch (err) {
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
}
|
|
1476
|
-
else {
|
|
1477
|
-
this.pendingSenderKeyByAgent.set(recipientAgentAddress, remaining);
|
|
1829
|
+
outcomes.set(entry, entry);
|
|
1830
|
+
for (const unprocessed of queue.slice(i + 1)) {
|
|
1831
|
+
outcomes.set(unprocessed, unprocessed);
|
|
1478
1832
|
}
|
|
1833
|
+
commitOutcomes();
|
|
1479
1834
|
await this.saveSwmSenderKeyState();
|
|
1480
1835
|
const message = err instanceof Error ? err.message : String(err);
|
|
1481
1836
|
this.log.warn(input.ctx ?? SWM_SENDER_KEY_PENDING_DRAIN_LOG_CTX, `SWM sender-key pending retry for ${entry.recipientAgentAddress} keyId=${entry.recipientKeyId} ` +
|
|
1482
|
-
`peerId=${
|
|
1837
|
+
`peerId=${authority.recipientPeerId} contextGraph=${entry.contextGraphId}${entry.subGraphName ? `/${entry.subGraphName}` : ''} ` +
|
|
1483
1838
|
`failed before the Messenger substrate queued a retry: ${message}`);
|
|
1484
1839
|
throw err;
|
|
1485
1840
|
}
|
|
1486
1841
|
}
|
|
1487
|
-
|
|
1488
|
-
this.pendingSenderKeyByAgent.delete(recipientAgentAddress);
|
|
1489
|
-
}
|
|
1490
|
-
else {
|
|
1491
|
-
this.pendingSenderKeyByAgent.set(recipientAgentAddress, remaining);
|
|
1492
|
-
}
|
|
1842
|
+
commitOutcomes();
|
|
1493
1843
|
await this.saveSwmSenderKeyState();
|
|
1494
1844
|
return drained;
|
|
1495
1845
|
}
|
|
1496
1846
|
/**
|
|
1497
|
-
*
|
|
1498
|
-
*
|
|
1499
|
-
*
|
|
1847
|
+
* Upgrade legacy/no-peer pending rows into one durable obligation per exact
|
|
1848
|
+
* `(agent, key, peer)` route in a current authority-fenced recipient
|
|
1849
|
+
* snapshot. The replacement is synchronous so drains can never observe a
|
|
1850
|
+
* partially expanded multi-peer obligation.
|
|
1851
|
+
*/
|
|
1852
|
+
expandUnboundPendingSenderKeyRoutes(input) {
|
|
1853
|
+
const peersByAgentAndKey = new Map();
|
|
1854
|
+
for (const recipient of input.recipients) {
|
|
1855
|
+
if (!recipient.peerId)
|
|
1856
|
+
continue;
|
|
1857
|
+
const routeKey = `${recipient.agentAddress.toLowerCase()}\0${recipient.recipientKeyId}`;
|
|
1858
|
+
const peers = peersByAgentAndKey.get(routeKey) ?? new Set();
|
|
1859
|
+
peers.add(recipient.peerId);
|
|
1860
|
+
peersByAgentAndKey.set(routeKey, peers);
|
|
1861
|
+
}
|
|
1862
|
+
if (peersByAgentAndKey.size === 0)
|
|
1863
|
+
return 0;
|
|
1864
|
+
const boundRouteIdentity = (entry, peerId) => JSON.stringify([
|
|
1865
|
+
entry.senderAgentAddress,
|
|
1866
|
+
entry.recipientKeyId,
|
|
1867
|
+
peerId,
|
|
1868
|
+
entry.epochId,
|
|
1869
|
+
entry.contextGraphId,
|
|
1870
|
+
entry.subGraphName ?? null,
|
|
1871
|
+
]);
|
|
1872
|
+
const senderAgentAddress = input.senderAgentAddress?.toLowerCase();
|
|
1873
|
+
let expanded = 0;
|
|
1874
|
+
for (const [recipientAgentAddress, queue] of this.pendingSenderKeyByAgent.entries()) {
|
|
1875
|
+
const existingBoundRoutes = new Set(queue
|
|
1876
|
+
.flatMap((entry) => entry.recipientPeerId === undefined
|
|
1877
|
+
? []
|
|
1878
|
+
: [boundRouteIdentity(entry, entry.recipientPeerId)]));
|
|
1879
|
+
let changed = false;
|
|
1880
|
+
const replacement = [];
|
|
1881
|
+
for (const entry of queue) {
|
|
1882
|
+
const matchesScope = entry.contextGraphId === input.contextGraphId
|
|
1883
|
+
&& (input.allSubgraphs
|
|
1884
|
+
|| (entry.subGraphName ?? undefined) === (input.subGraphName ?? undefined))
|
|
1885
|
+
&& (senderAgentAddress === undefined || entry.senderAgentAddress === senderAgentAddress)
|
|
1886
|
+
&& (input.epochId === undefined || entry.epochId === input.epochId);
|
|
1887
|
+
if (entry.recipientPeerId !== undefined || !matchesScope) {
|
|
1888
|
+
replacement.push(entry);
|
|
1889
|
+
continue;
|
|
1890
|
+
}
|
|
1891
|
+
const peers = peersByAgentAndKey.get(`${entry.recipientAgentAddress}\0${entry.recipientKeyId}`);
|
|
1892
|
+
if (!peers || peers.size === 0) {
|
|
1893
|
+
replacement.push(entry);
|
|
1894
|
+
continue;
|
|
1895
|
+
}
|
|
1896
|
+
changed = true;
|
|
1897
|
+
expanded += 1;
|
|
1898
|
+
for (const peerId of peers) {
|
|
1899
|
+
const identity = boundRouteIdentity(entry, peerId);
|
|
1900
|
+
if (existingBoundRoutes.has(identity))
|
|
1901
|
+
continue;
|
|
1902
|
+
replacement.push({ ...entry, recipientPeerId: peerId });
|
|
1903
|
+
existingBoundRoutes.add(identity);
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
if (changed) {
|
|
1907
|
+
if (replacement.length === 0) {
|
|
1908
|
+
this.pendingSenderKeyByAgent.delete(recipientAgentAddress);
|
|
1909
|
+
}
|
|
1910
|
+
else {
|
|
1911
|
+
this.pendingSenderKeyByAgent.set(recipientAgentAddress, replacement);
|
|
1912
|
+
}
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
return expanded;
|
|
1916
|
+
}
|
|
1917
|
+
/**
|
|
1918
|
+
* Drain queued sender-key packages whose exact current recipient route is
|
|
1919
|
+
* the newly connected `peerId`. Returns the number of rows successfully
|
|
1920
|
+
* delivered (acked) and removed.
|
|
1500
1921
|
*
|
|
1501
1922
|
* Fired from the `connection:open` listener — see line 2382 — so the
|
|
1502
1923
|
* cost lives on the cold path of "we just connected to a new peer",
|
|
1503
|
-
* not on every share.
|
|
1924
|
+
* not on every share. Every context graph represented by either a bound or
|
|
1925
|
+
* legacy row is resolved first; bound rows never bypass that authority read.
|
|
1926
|
+
* Legacy/no-peer rows are expanded only from that graph's current,
|
|
1927
|
+
* authority-fenced recipient snapshot. Each successful `sendReliable` with
|
|
1504
1928
|
* `delivered=true && ack.accepted=true` deletes the row and counts as
|
|
1505
1929
|
* drained; soft (`delivered=false`) and explicitly retryable delivered
|
|
1506
1930
|
* rejections leave it queued for the next attempt; terminal delivered
|
|
@@ -1511,21 +1935,80 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
1511
1935
|
if (this.pendingSenderKeyByAgent.size === 0)
|
|
1512
1936
|
return 0;
|
|
1513
1937
|
let drained = 0;
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1938
|
+
const pendingContextGraphIds = new Set([...this.pendingSenderKeyByAgent.values()]
|
|
1939
|
+
.flatMap((queue) => queue
|
|
1940
|
+
.filter((entry) => entry.recipientPeerId === undefined || entry.recipientPeerId === peerId)
|
|
1941
|
+
.map((entry) => entry.contextGraphId)));
|
|
1942
|
+
let expandedRows = 0;
|
|
1943
|
+
for (const contextGraphId of pendingContextGraphIds) {
|
|
1944
|
+
let resolution;
|
|
1945
|
+
try {
|
|
1946
|
+
resolution = await this.resolveWorkspaceAgentRecipientsForCurrentAuthority({
|
|
1947
|
+
contextGraphId,
|
|
1948
|
+
});
|
|
1949
|
+
}
|
|
1950
|
+
catch {
|
|
1951
|
+
// Authority/key resolution failure is benign on connection-open. No
|
|
1952
|
+
// row for this CG — including already bound rows — may bypass it.
|
|
1953
|
+
continue;
|
|
1954
|
+
}
|
|
1955
|
+
if (!resolution.requiresEncryption)
|
|
1956
|
+
continue;
|
|
1957
|
+
expandedRows += this.expandUnboundPendingSenderKeyRoutes({
|
|
1958
|
+
recipients: resolution.recipients,
|
|
1959
|
+
contextGraphId,
|
|
1960
|
+
allSubgraphs: true,
|
|
1961
|
+
});
|
|
1962
|
+
const routes = new Map();
|
|
1963
|
+
for (const recipient of resolution.recipients) {
|
|
1964
|
+
if (recipient.peerId !== peerId)
|
|
1965
|
+
continue;
|
|
1966
|
+
const recipientAgentAddress = recipient.agentAddress.toLowerCase();
|
|
1967
|
+
const authority = {
|
|
1968
|
+
contextGraphId,
|
|
1969
|
+
allSubgraphs: true,
|
|
1970
|
+
recipientAgentAddress,
|
|
1971
|
+
recipientKeyId: recipient.recipientKeyId,
|
|
1972
|
+
recipientPeerId: peerId,
|
|
1973
|
+
};
|
|
1974
|
+
const queue = this.pendingSenderKeyByAgent.get(recipientAgentAddress);
|
|
1975
|
+
if (!queue?.some((entry) => (pendingSenderKeyEntryMatchesDrainAuthority(entry, authority))))
|
|
1976
|
+
continue;
|
|
1977
|
+
routes.set(`${recipientAgentAddress}\0${recipient.recipientKeyId}\0${peerId}`, authority);
|
|
1978
|
+
}
|
|
1979
|
+
for (const authority of routes.values()) {
|
|
1980
|
+
drained += await this.drainPendingSenderKeyQueueForPeer({ authority, ctx });
|
|
1519
1981
|
}
|
|
1520
1982
|
}
|
|
1521
|
-
|
|
1522
|
-
//
|
|
1523
|
-
//
|
|
1983
|
+
if (expandedRows > 0) {
|
|
1984
|
+
// A connection can teach us routes for another peer. Persist that legacy
|
|
1985
|
+
// migration even when this peer had no exact authorized row to drain.
|
|
1986
|
+
await this.saveSwmSenderKeyState();
|
|
1524
1987
|
}
|
|
1525
|
-
|
|
1988
|
+
return drained;
|
|
1989
|
+
}
|
|
1990
|
+
/**
|
|
1991
|
+
* A delivered Sender Key setup can receive a retryable authority denial.
|
|
1992
|
+
* That leaves a durable pending row but does not create a Messenger outbox
|
|
1993
|
+
* retry, and an already-connected recipient may never produce another
|
|
1994
|
+
* connection:open event. Retry only peer-bound rows on the ordinary outbox
|
|
1995
|
+
* cadence. The drain below re-resolves current graph authority and the exact
|
|
1996
|
+
* recipient key/peer route before sending any package.
|
|
1997
|
+
*/
|
|
1998
|
+
async drainPendingSenderKeysForConnectedPeers() {
|
|
1999
|
+
await this.loadSwmSenderKeyState();
|
|
2000
|
+
if (this.pendingSenderKeyByAgent.size === 0 || !this.node.isStarted)
|
|
1526
2001
|
return 0;
|
|
1527
|
-
|
|
1528
|
-
|
|
2002
|
+
const pendingPeers = new Set([...this.pendingSenderKeyByAgent.values()]
|
|
2003
|
+
.flatMap((queue) => queue.flatMap((entry) => entry.recipientPeerId === undefined ? [] : [entry.recipientPeerId])));
|
|
2004
|
+
if (pendingPeers.size === 0)
|
|
2005
|
+
return 0;
|
|
2006
|
+
let drained = 0;
|
|
2007
|
+
for (const peer of this.node.libp2p.getPeers()) {
|
|
2008
|
+
const peerId = peer.toString();
|
|
2009
|
+
if (!pendingPeers.has(peerId))
|
|
2010
|
+
continue;
|
|
2011
|
+
drained += await this.drainPendingSenderKeyForPeer(peerId);
|
|
1529
2012
|
}
|
|
1530
2013
|
return drained;
|
|
1531
2014
|
}
|
|
@@ -1535,25 +2018,41 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
1535
2018
|
* connections where no fresh connection:open event will fire after the
|
|
1536
2019
|
* remote membership/key view converges.
|
|
1537
2020
|
*/
|
|
1538
|
-
async drainPendingSenderKeyForRecipients(recipients, ctx) {
|
|
2021
|
+
async drainPendingSenderKeyForRecipients(recipients, ctx, scope) {
|
|
1539
2022
|
if (this.pendingSenderKeyByAgent.size === 0)
|
|
1540
2023
|
return 0;
|
|
1541
|
-
const
|
|
2024
|
+
const senderAgentAddress = scope.senderAgentAddress.toLowerCase();
|
|
2025
|
+
this.expandUnboundPendingSenderKeyRoutes({
|
|
2026
|
+
recipients,
|
|
2027
|
+
contextGraphId: scope.contextGraphId,
|
|
2028
|
+
subGraphName: scope.subGraphName,
|
|
2029
|
+
senderAgentAddress,
|
|
2030
|
+
epochId: scope.epochId,
|
|
2031
|
+
});
|
|
2032
|
+
const routes = new Map();
|
|
1542
2033
|
for (const recipient of recipients) {
|
|
1543
2034
|
if (!recipient.peerId)
|
|
1544
2035
|
continue;
|
|
1545
2036
|
const recipientAgentAddress = recipient.agentAddress.toLowerCase();
|
|
1546
2037
|
if (!this.pendingSenderKeyByAgent.has(recipientAgentAddress))
|
|
1547
2038
|
continue;
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
2039
|
+
const authority = {
|
|
2040
|
+
contextGraphId: scope.contextGraphId,
|
|
2041
|
+
subGraphName: scope.subGraphName,
|
|
2042
|
+
senderAgentAddress,
|
|
2043
|
+
epochId: scope.epochId,
|
|
2044
|
+
recipientAgentAddress,
|
|
2045
|
+
recipientKeyId: recipient.recipientKeyId,
|
|
2046
|
+
recipientPeerId: recipient.peerId,
|
|
2047
|
+
};
|
|
2048
|
+
const routeKey = `${recipientAgentAddress}\0${recipient.recipientKeyId}\0${recipient.peerId}`;
|
|
2049
|
+
routes.set(routeKey, authority);
|
|
1551
2050
|
}
|
|
1552
|
-
if (
|
|
2051
|
+
if (routes.size === 0)
|
|
1553
2052
|
return 0;
|
|
1554
2053
|
let drained = 0;
|
|
1555
|
-
for (const
|
|
1556
|
-
drained += await this.drainPendingSenderKeyQueueForPeer({
|
|
2054
|
+
for (const authority of routes.values()) {
|
|
2055
|
+
drained += await this.drainPendingSenderKeyQueueForPeer({ authority, ctx });
|
|
1557
2056
|
}
|
|
1558
2057
|
if (drained > 0 && ctx) {
|
|
1559
2058
|
this.log.info(ctx, `SWM sender-key pending retry drained ${drained} queued package(s) during publish`);
|
|
@@ -1819,7 +2318,17 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
1819
2318
|
if (!agentGateSet.has(recipientAgentAddress.toLowerCase())) {
|
|
1820
2319
|
throw new SwmSenderKeySetupRejectionError('recipient-not-allowed', `Recipient agent ${recipientAgentAddress} is not allowed for context graph "${pkg.contextGraphId}"`);
|
|
1821
2320
|
}
|
|
1822
|
-
|
|
2321
|
+
let allowedPeers;
|
|
2322
|
+
try {
|
|
2323
|
+
allowedPeers = await this.resolveSwmAllowedPeersForCurrentAuthority(pkg.contextGraphId);
|
|
2324
|
+
}
|
|
2325
|
+
catch (error) {
|
|
2326
|
+
if (!isContextGraphAuthorityUnavailableMarker(error))
|
|
2327
|
+
throw error;
|
|
2328
|
+
throw new SwmSenderKeySetupRejectionError(isRetryableContextGraphAuthorityUnavailableReason(error.reason)
|
|
2329
|
+
? 'agent-gate-pending'
|
|
2330
|
+
: 'agent-gate-unavailable', `Context graph "${pkg.contextGraphId}" SWM peer gate authority is unavailable (${error.reason})`);
|
|
2331
|
+
}
|
|
1823
2332
|
if (allowedPeers !== null && !allowedPeers.includes(fromPeerId)) {
|
|
1824
2333
|
throw new SwmSenderKeySetupRejectionError('sender-not-allowed', `Sender peer ${fromPeerId} is not allowed for context graph "${pkg.contextGraphId}"`);
|
|
1825
2334
|
}
|
|
@@ -2148,23 +2657,32 @@ export class WorkspaceCryptoMethods extends DKGAgentBase {
|
|
|
2148
2657
|
const path = this.swmSenderKeyStatePath();
|
|
2149
2658
|
if (!path)
|
|
2150
2659
|
return;
|
|
2151
|
-
const
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2660
|
+
const save = this.swmSenderKeyStateSaveQueue.then(async () => {
|
|
2661
|
+
const { mkdir, writeFile, chmod } = await import('node:fs/promises');
|
|
2662
|
+
const { dirname } = await import('node:path');
|
|
2663
|
+
await mkdir(dirname(path), { recursive: true });
|
|
2664
|
+
// Capture inside the serialized section. A caller queued behind an
|
|
2665
|
+
// in-flight write must persist the state current when its turn begins,
|
|
2666
|
+
// not a stale snapshot captured while the older write was outstanding.
|
|
2667
|
+
const payload = {
|
|
2668
|
+
version: 1,
|
|
2669
|
+
send: [...this.swmSenderKeySendStates.values()].map(serializeSwmSenderSendState),
|
|
2670
|
+
receive: [...this.swmSenderKeyReceiveStates.values()].map(serializeSwmSenderReceiveState),
|
|
2671
|
+
pending: [...this.pendingSenderKeyByAgent.values()]
|
|
2672
|
+
.flatMap((queue) => queue.map(serializePendingSenderKeyEntry)),
|
|
2673
|
+
};
|
|
2674
|
+
await writeFile(path, JSON.stringify(payload, null, 2), { mode: 0o600 });
|
|
2675
|
+
try {
|
|
2676
|
+
await chmod(path, 0o600);
|
|
2677
|
+
}
|
|
2678
|
+
catch {
|
|
2679
|
+
// Best-effort on platforms/filesystems that do not support chmod.
|
|
2680
|
+
}
|
|
2681
|
+
});
|
|
2682
|
+
// A failed save is still reported to its caller, but must not poison the
|
|
2683
|
+
// queue and suppress every later persistence attempt.
|
|
2684
|
+
this.swmSenderKeyStateSaveQueue = save.catch(() => undefined);
|
|
2685
|
+
await save;
|
|
2168
2686
|
}
|
|
2169
2687
|
async resolveWorkspaceGossipSigningAgent(contextGraphId) {
|
|
2170
2688
|
const authority = await withRpcUsageSite(CG_AUTH_RPC_SITES.signer, () => this.resolveContextGraphAgentGateAuthority(contextGraphId));
|