Skip to content

EPCIS: allow-list private payload propagation has no receiver-side pull #409

@zsculac

Description

@zsculac

Summary

When a publisher captures with accessPolicy: 'allowList' and allowedPeers: [<peerId>], the listed peer never actually receives the private payload. The publisher correctly authorizes the peer (server-side AccessHandler would respond if asked), but nothing on the receiver's side ever asks.

Surfaced empirically during the EPCIS async-private feature work (feat/epcis-async-private integration branch), where slice/04 and slice/06 had to demote the "allow-list peer query returns private payload" scenario to informational.

What works

  • Publisher (N1) captures with allowedPeers: [N2.peerId]
  • KC metadata records the accessPolicy + allowedPeers correctly (packages/publisher/src/dkg-publisher.ts:984-998, metadata.ts:103)
  • Public anchor lands on every CG subscriber via gossip ✓ (N1, N2, N3 all see the anchor)
  • N1's AccessHandler is wired up — when a peer asks for the private payload, it correctly enforces the allow-list (packages/publisher/src/access-handler.ts:98-110)

What's missing

There is no automatic mechanism on the receiver (N2) to fetch the private payload after the anchor arrives. Specifically:

  1. AccessClient class exists (packages/publisher/src/access-client.ts:26) and exposes requestAccess(publisherPeerId, kaUal), but nothing in packages/agent/ or packages/cli/ ever instantiates or calls it.
  2. There is no /api/access/request (or equivalent) HTTP endpoint that would let a node operator manually trigger the fetch.
  3. There is no listener on the receiver's event bus that watches for newly-arrived anchors carrying its peerId in allowedPeers and triggers a pull.

Net result: accessPolicy: 'allowList' grants permission that is currently impossible to exercise from the receiver side. N2's <cg>/_private partition stays empty after a finalized allow-list capture on N1, even though the peer was explicitly authorized.

Impact on the EPCIS PRD

The EPCIS async-private feature has a user story (PRD #21) that says: "As a node operator on an allowedPeers list, I want private events shared with my node to surface in my events response just like the publisher's, so that allow-list sharing gives me parity with the publisher for query."

That promise is currently structurally unmet. The privacy gate works correctly (non-allowed peers cannot fetch); the privacy delivery does not happen automatically.

Evidence

Suggested fix direction

Two reasonable options:

A. Receiver-side auto-pull. When a public anchor arrives on the gossip topic with allowedPeers matching the local peerId, queue an AccessClient.requestAccess call against the publisher peer. Likely lives in the agent's gossip-publish-handler or a new lightweight subscriber on the access-protocol topic.

B. Receiver-side manual pull API. Expose AccessClient via a new POST /api/access/request daemon route. Node operators trigger fetches explicitly. Smaller scope, but pushes the work onto callers.

A is closer to PRD intent; B is the smaller patch. A new ADR captures the choice.

Out of scope for this issue

  • Payment / cost handling on requestAccess (existing paymentProof argument left untouched)
  • Allow-list updates after publish (separate KC metadata mutation question)

Priority

Medium. Not blocking — the EPCIS feature lands without this, and the privacy gate is verifiably correct. But the delivery gap should be closed before users build production allow-list flows on top of EPCIS.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingenhancementNew feature or request

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions