You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

6.2 KiB

NIP-02

Follow List

final optional

A special event with kind 3, meaning "follow list" is defined as having a list of p tags, one for each of the followed/known profiles one is following.

Each tag entry should contain the key for the profile, a relay URL where events from that key can be found (can be set to an empty string if not needed), and a local name (or "petname") for that profile (can also be set to an empty string or not provided), i.e., ["p", <32-bytes hex key>, <main relay URL>, <petname>].

The .content is not used.

For example:

{
  "kind": 3,
  "tags": [
    ["p", "91cf9..4e5ca", "wss://alicerelay.com/", "alice"],
    ["p", "14aeb..8dad4", "wss://bobrelay.com/nostr", "bob"],
    ["p", "612ae..e610f", "ws://carolrelay.com/ws", "carol"]
  ],
  "content": "",
  // other fields...
}

Every new following list that gets published overwrites the past ones, so it should contain all entries. Relays and clients SHOULD delete past following lists as soon as they receive a new one.

Whenever new follows are added to an existing list, clients SHOULD append them to the end of the list, so they are stored in chronological order.

Uses

Follow list backup

If one believes a relay will store their events for sufficient time, they can use this kind-3 event to backup their following list and recover on a different device.

Profile discovery and context augmentation

A client may rely on the kind-3 event to display a list of followed people by profiles one is browsing; make lists of suggestions on who to follow based on the follow lists of other people one might be following or browsing; or show the data in other contexts.

Relay sharing

A client may publish a follow list with good relays for each of their follows so other clients may use these to update their internal relay lists if needed, increasing censorship-resistance.

Petname scheme

The data from these follow lists can be used by clients to construct local "petname" tables derived from other people's follow lists. This alleviates the need for global human-readable names. For example:

A user has an internal follow list that says

[
  ["p", "21df6d143fb96c2ec9d63726bf9edc71", "", "erin"]
]

And receives two follow lists, one from 21df6d143fb96c2ec9d63726bf9edc71 that says

[
  ["p", "a8bb3d884d5d90b413d9891fe4c4e46d", "", "david"]
]

and another from a8bb3d884d5d90b413d9891fe4c4e46d that says

[
  ["p", "f57f54057d2a7af0efecc8b0b66f5708", "", "frank"]
]

When the user sees 21df6d143fb96c2ec9d63726bf9edc71 the client can show erin instead; When the user sees a8bb3d884d5d90b413d9891fe4c4e46d the client can show david.erin instead; When the user sees f57f54057d2a7af0efecc8b0b66f5708 the client can show frank.david.erin instead.

GitRepublic Usage

GitRepublic uses KIND 3 (Contact List) events to enable repository filtering based on social connections.

Repository Filtering

When a user enables the "Show only my repos and those of my contacts" filter on the landing page, GitRepublic:

  1. Fetches the user's contact list: Retrieves the latest KIND 3 event published by the logged-in user
  2. Extracts contact pubkeys: Parses all p tags from the contact list event to build a set of followed pubkeys
  3. Includes the user's own pubkey: Automatically adds the logged-in user's pubkey to the filter set
  4. Filters repositories: Shows only repositories where:
    • The repository owner (event pubkey) is in the contact set, OR
    • Any maintainer (from maintainers tags) is in the contact set

Implementation Details

  • Pubkey normalization: GitRepublic handles both hex-encoded pubkeys and bech32-encoded npubs in contact lists
  • Maintainer matching: The filter checks all maintainers listed in repository announcement maintainers tags
  • Real-time updates: The contact list is fetched when the user logs in and can be refreshed by reloading the page

Example Use Case

A user follows several developers on Nostr. By enabling the contact filter:

  • They see repositories owned by people they follow
  • They see repositories where their contacts are maintainers
  • They see their own repositories
  • They don't see repositories from people they don't follow

This creates a personalized, social discovery experience for finding relevant code repositories based on trust relationships established through Nostr's follow mechanism.

Technical Implementation

// Fetch user's kind 3 contact list
const contactEvents = await nostrClient.fetchEvents([
  {
    kinds: [KIND.CONTACT_LIST], // KIND 3
    authors: [userPubkey],
    limit: 1
  }
]);

// Extract pubkeys from 'p' tags
const contactPubkeys = new Set<string>();
contactPubkeys.add(userPubkey); // Include user's own repos

if (contactEvents.length > 0) {
  const contactEvent = contactEvents[0];
  for (const tag of contactEvent.tags) {
    if (tag[0] === 'p' && tag[1]) {
      let pubkey = tag[1];
      // Decode npub if needed
      try {
        const decoded = nip19.decode(pubkey);
        if (decoded.type === 'npub') {
          pubkey = decoded.data as string;
        }
      } catch {
        // Assume it's already a hex pubkey
      }
      contactPubkeys.add(pubkey);
    }
  }
}

// Filter repositories
const filteredRepos = allRepos.filter(event => {
  // Check if owner is in contacts
  if (contactPubkeys.has(event.pubkey)) return true;
  
  // Check if any maintainer is in contacts
  const maintainerTags = event.tags.filter(t => t[0] === 'maintainers');
  for (const tag of maintainerTags) {
    for (let i = 1; i < tag.length; i++) {
      let maintainerPubkey = tag[i];
      // Decode npub if needed
      try {
        const decoded = nip19.decode(maintainerPubkey);
        if (decoded.type === 'npub') {
          maintainerPubkey = decoded.data as string;
        }
      } catch {
        // Assume it's already a hex pubkey
      }
      if (contactPubkeys.has(maintainerPubkey)) return true;
    }
  }
  return false;
});

This implementation provides a seamless way for users to discover repositories from their social network while maintaining the decentralized, trustless nature of Nostr.