Skip to content

Catch-up early stop needs a trusted curator-to-peer binding (D1 from #2006) #2018

Description

@Jurij89

Split out of #2006 / PR #2007, where the fan-out reduction was disabled rather than shipped on an unsound foundation.

Why it was disabled

PR #2007 let one peer — the Context Graph's curator — end a catch-up walk early, which is what removes the 5-6x amplification #2006 reported. Establishing which peer is the curator turned out to be the hard part, and three successive attempts were each unsound:

  1. The merged projection (getCgMeta()) unions _meta + AGENTS + _catalog + ONTOLOGY under first-wins precedence and discards which graph supplied each fact.
  2. Reading the graph's own _meta alone plus ONTOLOGY: ONTOLOGY is network-replicated, so a node can hold an injected DKG_CREATOR without holding the real one, and local uniqueness proves nothing about the network.
  3. Reading <cg>/_meta alone: source-qualifying by GRAPH establishes which graph HOLDS the rows, not which writer SUPPLIED them. Ordinary durable-meta catch-up admits IRI-subject descriptive metadata for the CG entity subject and inserts it verbatim into that exact graph, so a contacted peer can supply the rows the check reads. Three suffice — rdf:type, accessPolicy, and a curator DID naming itself.

Tightening the record's shape cannot fix (3); completeness and uniqueness only raise the number of rows the peer must send.

What is missing

A curator-to-peer binding from a source a peer cannot write. Surveyed, none is usable today:

Candidate Status
Signed join approval The join-approved payload is plain JSON with no curator key material. Only the JOINER's delegation is signed and verified.
On-chain binding Chain has an authoritative CG owner wallet and participantAgents, but nothing maps a wallet to a libp2p peer id. ProfileStorage.nodeId is documented display-only self-reported.
Curator-signed metadata snapshot context-graph-private-meta-proof / context-graph-public-meta-proof are pure quad-shape checks. No signature field, no key recovery. Sync signs the REQUEST only; responses are unsigned.
Locally persisted join-approval record urn:dkg:local:requester-join-state holds curator-peer-id, written pre-transport, never synced, not peer-writable — genuinely writer-qualified. But it is TOFU-from-invite, keyed by (cgId, agentAddress), and absent for public CGs, for the curator's own node, and for members admitted without a V10 invite.

The primitive for a real fix already exists and is simply unused for this: signAgentDelegation / verifyAgentDelegation can bind (scope, curator agent address, delegatee peer id, issuedAt, expiresAt). A curator never issues one for itself, it is not carried in the join-approved payload, and _meta strips signatures on promotion.

Scope

  1. Introduce a curator-signed binding artifact, or an on-chain curator→peer edge, and persist it where sync cannot write.
  2. Re-enable authority in resolveCuratorSyncPeer gated on that artifact only. The positive case belongs in the existing grants authority to NO route test in packages/agent/test/sync-policy.test.ts, not in a relaxation of the shapes it covers.
  3. Re-enable the wave walk. catchup-runner-worker-impl.ts currently collapses to a single bounded pass when no authority resolves, because waves without a possible early stop only add barrier latency.
  4. Add an end-to-end regression through the REAL admission → store → projection → resolver path. A hand-built resolver fixture cannot see this class of bug: every current CLI worker test stubs prepareCatchup and hands itself authoritativePeerId, so the CLI suite stays green over inert production code.

Still shipped in #2007

The correctness and observability half is unaffected and does not depend on authority: fail-closed readiness (unanimous-empty voids, incompleteResponders, the persistence split), the wall-clock backpressure budget, the bounded SyncAdmissionSource operation labels, and the worker-exit latch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions