Skip to content

@helia/ipns: fetch cold IPNS-over-pubsub records from rendezvous-CID providers instead of waiting for subscription-change? #1082

Description

@Rinse12

Package

@helia/ipns (packages/ipns), current npm 9.2.1

Context

Since #906, PubSubRouting can fetch an IPNS record over the libp2p/fetch protocol instead of only waiting for a gossipsub push. That's great. But get() today only fetches from peers gossipsub already reports as subscribers:

for (const peerId of this.libp2p.services.pubsub.getSubscribers(topic)) {
  promises.push(this.#fetchFromPeer(topic, routingKey, peerId, ...))
}
if (promises.length > 0) { /* Promise.any */ }
throw new NotFoundError('Pubsub routing does not actively query peers')

On a cold resolve, getSubscribers(topic) is empty, so get() throws immediately. The record only becomes fetchable after the topic's peers re-announce via a subscription-change event (the #fetchFromPeer triggered from the subscription-change listener). In practice that adds a real latency floor before the first fetch can happen, even though the publisher (kubo's go-libp2p-pubsub-router, or another Helia via registerLookupFunction) will answer a libp2p/fetch request for the record regardless of the subscription handshake.

Idea

IPNS-over-pubsub publishers announce themselves as providers of the topic's rendezvous CID — the standard go-libp2p-pubsub discovery key, CID(sha256("floodsub:" + topic)). So on a cold get(), in parallel with the existing subscriber-fetch, PubSubRouting could:

  1. contentRouting.findProviders(rendezvousCidForTopic(topic))
  2. dial each discovered provider
  3. fetch(peerId, routingKey) the moment the dial completes

First signature-valid record wins; losers get aborted. This skips the wait for subscription-change entirely and reuses the exact libp2p/fetch path #906 added — it just sources candidate peers from content routing instead of waiting for gossipsub to confirm them.

We've implemented exactly this downstream (fetch in parallel from both current subscribers and freshly-discovered providers of the rendezvous CID, first valid record wins) and it reliably resolves cold IPNS names well under the subscription-wait floor, then falls back to the existing path on a miss.

Question

Does this belong in @helia/ipns PubSubRouting itself, or is it better kept as downstream orchestration around the router?

Reasons it might fit upstream:

  • The rendezvous CID (floodsub:<topic> → sha256 → CID) is a standard libp2p-pubsub discovery key, not app-specific.
  • It reuses the fetch path from feat: ipns libp2p fetch #906 verbatim; only the peer-sourcing changes.
  • Every Helia-over-pubsub consumer would get faster cold resolves.

Reasons it might not:

  • get() currently never actively queries the network ("Pubsub routing does not actively query peers") — provider discovery + dialing is a deliberate behavior/cost change and may warrant an opt-in (e.g. an init flag).
  • It introduces a contentRouting dependency and dial amplification into a router that today only listens.

If you'd welcome it upstream I'm happy to open a PR (likely gated behind an init option, defaulting off). Wanted to check direction before writing it.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions