Skip to content

Add "Followed by" / Followers view — surface who follows the user #92

Description

@asherp

Summary

Add a "Followed by" / Followers view that surfaces who follows the user on Nostr. Today nostr-mail only shows the outbound direction — the user's own follow list — and has no way to see who has added the user as a contact.

Current state in the codebase

The backend (tauri-app/backend/src/nostr.rs) only resolves the following direction by reading the user's own kind 3 (ContactList) event:

  • fetch_following_profiles(...) — fetches the user's kind 3 event and resolves profiles for the p-tagged pubkeys they follow.
  • fetch_following_pubkeys(...) — returns the bech32 npubs the user follows.

There is no function that resolves the reverse (followers). The Contacts page is populated from the follow list only.

Why this is non-trivial on Nostr (research)

Nostr follow lists (NIP-02, kind 3) are one-directional: an event lists who you follow, with no inverse index. To compute followers, a client must find every kind 3 event anywhere that contains the user's pubkey in a p tag — which a single relay generally cannot answer fully, and reverse-querying/counting all kind-3 events referring to a pubkey is prohibitively expensive to do naively. (NIP-02, Kind 3: Follow List)

This is fundamentally a social-graph indexing problem, which is exactly what a graph service is built to answer.

How other apps have handled it

  • Indexer/aggregator services — clients historically relied on indexing services (notably nostr.band) that crawled relays and maintained a reverse follow index, exposing follower lists/counts via API. ⚠️ Update: nostr.band is no longer available, which removes the de-facto third-party follower index much of the ecosystem depended on, and is a strong argument for using our own graph infrastructure rather than a third party.
  • NIP-45 COUNT — relays that support the COUNT verb can return an aggregate follower count ({"count": N}) without streaming every event, though support is inconsistent and the NIP has been contested. (NIP-45)
  • Best-effort relay query — simpler clients subscribe for kind 3 events with a #p filter for the user's pubkey across configured relays and show whatever they find, accepting that it's incomplete.
  • Caveat widely noted: for popular accounts a large share of "followers" are bots/spam, so raw counts are noisy.

Preferred approach: use our own Graph Name Service (asherp/GNS)

Rather than depend on a third-party indexer (now that nostr.band is gone), this is a good driver to lean on our own GNS (Graph Name System) project. GNS is a decentralized naming/discovery layer built on the Nostr social graph: it already runs Nostr relay clients with relay attribution, does BFS graph traversal over follow lists, and exposes an HTTP API with a TTL-based cache. Computing followers is the reverse-edge case of the graph it already builds.

Proposed direction:

  1. Extend GNS to expose a reverse-follow / followers query (list + count) for a given pubkey, leveraging its existing graph traversal, caching, and relay-attribution layers. → tracked in Reverse-edge query: followers index (pubkey → who follows it) GNS#9.
  2. nostr-mail backend (nostr.rs): add a fetch_followers(...) (+ optional fetch_followers_count(...)) that primarily queries GNS via its HTTP API, with a fallback to a best-effort relay #p kind-3 query (and optionally NIP-45 COUNT) when GNS is unavailable. Resolve profiles for the resulting pubkeys (reuse the path from fetch_following_profiles).
  3. Frontend: surface results — e.g. a "Followers" / "Followed by" tab or sub-tab on the Contacts page, and/or a follower count on the Profile page. Indicate mutual relationships ("follows you" / "you follow") by cross-referencing the existing following list. GNS additionally enables provenance / graph-relative naming (e.g. how you're connected to a follower), which is a richer UX than a flat count.
  4. Caching: cache follower results in the local SQLite store (like contacts are cached) since the query is expensive and incomplete in real time.

Acceptance criteria

  • Backend can resolve followers (pubkeys + profiles) for the current user, primarily via GNS, with a graceful relay-query fallback and handling of incompleteness.
  • UI surfaces followers (list and/or count), e.g. on Contacts and/or Profile.
  • Mutual-follow state is indicated where known (cross-referenced with the existing following list).
  • Results are cached locally to avoid repeated expensive queries.
  • Clear UX messaging that follower data is best-effort/approximate on Nostr.

Open questions

  • Data source priority: GNS as primary (preferred) with relay #p fallback — confirm. Do we still want NIP-45 COUNT as a fast-count option where supported?
  • GNS deployment: which GNS endpoint does nostr-mail talk to (self-hosted vs. a hosted instance), and is it configurable in Settings?
  • Where to surface it: a Contacts sub-tab, the Profile page, or both? Show provenance/paths from GNS, or just a flat follower list to start?
  • Spam/bot filtering — apply any trust-rank/heuristics, or show raw results?
  • Privacy: querying an external GNS instance still reveals interest in a pubkey — default-on or opt-in?

Related

Sources

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    Status
    Todo

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions