{"id":"0cd874083590a76337cf360203dd1cc7c2d6165e3bff25f714a39f45f845c2f6","pubkey":"e5272de914bd301755c439b88e6959a43c9d2664831f093c51e9c799a16a102f","created_at":1778350223,"kind":30817,"tags":[["d","decentralized-lists"],["title","Decentralized Lists"],["k","9998"],["k","9999"],["k","39998"],["k","39999"],["client","nostrhub.io"]],"content":"Decentralized Lists\n=====\n\nThis NIP defines lists of things that users can create and that anyone can add to. It provides an alternative to NIP-51 for list management. The distinction is that for any given NIP-51 list, the list author has full control over list items, whereas for this NIP, list items are contributed by the community.\n\nWe introduce the following event kinds: `9998` and `39998` for _list header declaration_ (a.k.a. _list declaration_), and `9999` and `39999` for _list item declaration_. Whether to use kind `999x` or kind `3999x` depends on whether the declaration author wishes the list or list item to be editable (kind `3999x`) or not (kind `999x`).\n\n_This NIP is agnostic regarding the choice of method or methods for list curation and spam prevention_. (But see below for a brief discussion.)\n\n## Declarations of lists and list items\n\nThere are two actions: declaration of a list, and declaration of a specific item that belongs to a given list.\n\n### List declaration\n\nDeclaration of a list is a kind `9998` (or `39998`) event. It may be referred to as the _list header_.\n\nThe list header `must` have the `names` tag. The `names` tag must be followed by two strings: a singular form (\"widget\") and a plural form (\"widgets\"), as in the examples below.\n\nOptional tags: `titles`, `slugs`, which also must have a singular form and a plural form, as is the case for the `names` tag.\n\nThe list header `should` make use of `required` or `allowed` tags to indicate which child item types are expected. For example: `[\"required\", \"p\"]` indicates that child item declarations must make use of the `p` tag. Each required or allowed tag must be listed individually, not packed together into a single tag. For example, use `[\"required\", \"foo\"], [\"required\", \"bar\"]` rather than `[\"required\", \"foo\", \"bar\"]`.\n\nIf the list header is a kind `39998` replaceable event, it _must_ have a `d` tag as described in [NIP-01](https://github.com/nostr-protocol/nips/blob/master/01.md).\n\n### Item declaration \n\nThere are two methods to declare an item: the <i>standard</i> method and the <i>nonstandard</i> method. The standard method is recommended for routine use. The nonstandard method may be used for backwards compatibility with other NIPs. The only distinction between these two methods are the event kinds that are used for declaration of an item on a list. Standard declaration of an item on an existing list is a kind `9999` (or `39999`) event. Nonstandard declaration of an item on an existing list is some other kind that is selected for the sake of compatibility with one or more other chosen NIPs. For an example of nonstandard item declaration, see below.\n\nThe `z` tag is required by all list item events, whether standard (kind `9999` and `39999`) or nonstandard (some other event kind). It is used as a pointer to the parent list (the list header). If the list header is a kind `9998` event, the `z` tag is the _event id_ of the list header. If the list header is a kind `39998` event, which is a replaceable event and will not have a constant event id, the `z` tag follows the format of an `a` tag: `39998:<list header author pubkey>:<d-tag of the list header>`. Alternatively, if a list header has not been formally declared, the `z` tag value can be replaced with a human readable name of the list, e.g.  `[\"z\", \"country\"]` and `[\"t\", \"Switzerland\"]`could be used to add `Switzerland` to the list of countries. However, the _preferred_ practice will be to rely upon formal declaration of the list header.\n\nThe list item event `should` have at least one item tag, which is a `p`, `e`, `t`, or `a` tag (others), as indicated by `required` by the parent list declaration.\n\nOptional tags: `name`, `title`, `slug`, `description`, `comments`. Each of these tags is followed by a single string element, as in the examples below.\n\nA single kind `9999` or `39999` event can be used to declare multiple items for a single list (e.g. one z tag and multiple item tags), or a single item that belongs to multiple lists (multiple z tags and a single item tag), or multiple items that all belong to multiple lists (multiple z tags and multiple item tags). However, it is recommended to use a distinct event for each individual item (one z tag, one item tag).\n\nIf an item belongs to multiple lists, either multiple list item declaration events can be employed, or there can be a single list item declaration event with multiple `z` tags, one for each list to which it belongs.\n\nThe `p`, `e`, `t`, and `a` tags are used in list item declarations when declaring a pubkey, event id, string, or naddr as an item on a list. The parent list declaration event `should` specify which of these tags are expected to be present in their children using the `required`, `allowed`, `recommended` and/or `disallowed` tags.\n\n## Expressions of approval & disapproval of individual list items\n\nVarious methods exist for community members to express their approval or disapproval of individual list items. Existing methods include commonly used NIPs such as NIP-25: Reactions, zaps, NIP-56: Reports, etc. This NIP will therefore not introduce any new formalism for this purpose. It is up to personalized trust metric interpreters to decide what these data points mean and how to use them.\n\nAs a starting point, it is `recommended` that endorsements or objections to individual list items be handled with existing NIP-25: Reactions. Using this method, endorsement of a list item is a kind 7 event with an `e` tag pointing to the list item declaration and \"+\" in the content field. Objection is the same but with \"-\" in the content field.\n\n## Examples\n\n### Example 1: a list of nostr developers\n\nList creation:\n\n```json\n{\n  \"kind\": 9998,\n  \"tags\": [\n    [\"names\", \"nostr developer\", \"nostr developers\"],\n    [\"description\", \"This is a list of developers who build within the nostr ecosystem\"],\n    [\"required\", \"p\"],\n    [\"required\", \"name\"]\n  ],\n  \"id\": \"<id_list_of_developers>\"\n}\n```\n\nAdd a pubkey as an item on the list of developers:\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", \"<id_list_of_developers>\"],\n    [\"name\", \"Derek Ross\"],\n    [\"p\", \"3f770d65d3a764a9c5cb503ae123e62ec7598ad035d836e2a810f3877a745b24\"]\n  ]\n}\n```\n\n### Example 2: a list of long form articles on hyperinflation:\n\nList creation:\n\n```json\n{\n  \"kind\": 9998,\n  \"tags\": [\n    [\"names\", \"long form article on hyperinflation\", \"long form articles on hyperinflation\"],\n    [\"description\", \"This is a list of long form content events on the topic of hyperinflation.\"],\n    [\"required\", \"a\"],\n    [\"recommended\", \"title\"]\n  ],\n  \"id\": \"<id_hyperinflation>\"\n}\n```\n\nAdd an naddr address as an item to the above list:\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", \"<id_hyperinflation>\"],\n    [\"a\", \"naddr1qvzqqqr4gupzq4rqjpyzsnf2z5wgma397sxr382z8mg90l80jf7m3z2k628z9wsrqythwumn8ghj7cnfw33k76twv4ezuum0vd5kzmp0qythwumn8ghj7ct5d3shxtnwdaehgu3wd3skuep0qq3kv6tpwskkxatjwfjkucme946xsefdwd5kcetwwskhg6tdv5khg6rfv4nqnxv6fx\"],\n    [\"title\", \"Fiat Currency: The Silent Time Thief\"]\n  ]\n}\n```\n\n### Example 3: a list of dog names:\n\nList creation:\n\n```json\n{\n  \"kind\": 9998,\n  \"tags\": [\n    [\"names\", \"dog name\", \"dog names\"],\n    [\"description\", \"This is a list of commonly used dog names.\"],\n    [\"required\", \"t\"]\n  ],\n  \"id\": \"<id_dog_names>\"\n}\n```\n\nAdd a piece of text as an item to the above list:\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", \"<id_dog_names>\"],\n    [\"t\", \"Fido\"]\n  ]\n}\n```\n\n### Example 4: A single item on multiple lists\n\nCreate a list of dogs and a list of animals:\n\n```json\n{\n  \"kind\": 9998,\n  \"tags\": [\n    [\"names\", \"dog\", \"dogs\"],\n    [\"description\", \"This is a list (by name) of individual dogs.\"],\n    [\"required\", \"t\"]\n  ],\n  \"id\": \"<id_dogs>\"\n}\n```\n\n```json\n{\n  \"kind\": 9998,\n  \"tags\": [\n    [\"names\", \"animal\", \"animals\"],\n    [\"description\", \"This is a list (by name) of individual animals.\"],\n    [\"required\", \"t\"]\n  ],\n  \"id\": \"<id_animals>\"\n}\n```\n\nNow add Fido to both of the above lists.\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", \"<id_dogs>\"],\n    [\"z\", \"<id_animals>\"],\n    [\"t\", \"Fido\"]\n  ]\n}\n```\n\nIt is equally valid to split the above item declaration event into two events, one for each list:\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", \"<id_dogs>\"],\n    [\"t\", \"Fido\"]\n  ]\n}\n```\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", \"<id_animals>\"],\n    [\"t\", \"Fido\"]\n  ]\n}\n```\n\nAn alternate and equivalent way to add Fido to these two lists is to provide each list `name` (singular) in place of the list id. In this case, we replace `<id_dogs>` with `dog`, and `<id_animals>` with `animal`:\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", \"dog\"],\n    [\"z\", \"animal\"],\n    [\"t\", \"Fido\"]\n  ]\n}\n```\n\nThe above can be translated: \"Fido is a dog\" and \"Fido is an animal\".\n\nHowever, it is encouraged to use the list id if an appropriate one is known and available.\n\n### Example 5: A list of lists\n\nDeclare a list of movie lists, each one corresponding to a different genre.\n\n```json\n{\n  \"kind\": 9998,\n  \"tags\": [\n    [\"names\", \"list of good movies in a specific movie genre\", \"lists of good movies in a specific movie genre\"],\n    [\"description\", \"This is a list of lists of good movies in specific movie genres. It will be used to make a composite list of good movies of any genre.\"],\n    [\"required\", \"e\"]\n  ],\n  \"id\": \"<id_list_of_lists_of_movies>\"\n}\n```\n\nNow add two items to the above list: a list of comedies and a list of dramas.\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", \"<id_list_of_lists_of_movies>\"],\n    [\"e\", \"<id_list_of_comedies>\"],\n    [\"e\", \"<id_list_of_dramas>\"]\n  ],\n}\n```\n\n### Example 6: Objection to an item on a list\n\n```json\n{\n  \"kind\": 7,\n  \"content\": \"-\", // or \"+\" to upvote\n  \"tags\": [\n    [\"e\", \"<id_declaration_of_list_item>\"]\n  ]\n}\n```\n\n### Example 7: Declaration of the List Header using kind 39998 Replaceable Event\n\n```json\n{\n  \"kind\": 39998,\n  \"tags\": [\n    [\"d\", \"<d_tag_for_list_of_dogs>\"],\n    [\"names\", \"dog, \"dogs\"],\n    [\"required\", \"t\"]\n  ],\n  \"id\": \"<id_for_list_of_dogs>\",\n  \"author\": \"<pubkey_for_list_of_dogs>\"\n}\n```\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\":[\n    [\"z\", \"39998:<pubkey_for_list_of_dogs>:<d_tag_for_list_of_dogs>\"],\n    [\"t\", \"Fido\"]\n  ]\n}\n```\n\nIf the list item is also a replaceable event, it must also have a `d` tag:\n\n```json\n{\n  \"kind\": 39999,\n  \"tags\":[\n    [\"z\", \"39998:<pubkey_for_list_of_dogs>:<d_tag_for_list_of_dogs>\"],\n    [\"d\", \"<d_tag_for_Fido>\"]\n    [\"t\", \"Fido\"]\n  ]\n}\n```\n\n## Nonstandard methods to declare a list\n\nThe standard method to declare a list of widgets uses a kind `(3)9998` event as the list header:\n\n```json\n{\n  \"kind\": 9998,\n  \"tags\": [\n    [\"names\", \"widget\", \"widgets\"],\n    [\"description\", \"Lorem ipsum\"],\n    [\"required\", \"t\"]\n  ],\n  \"id\": \"<id_lists>\"\n}\n```\n\nThe nonstandard method to declare a list is to use a kind `(3)9999` event as the list header.\n\nTo see how this works, suppose we declare a list of all known lists:\n\n```json\n{\n  \"kind\": 9998,\n  \"tags\": [\n    [\"names\", \"list\", \"lists\"],\n    [\"description\", \"This is a list of all the lists that exist within any given datastore\"],\n    [\"required\", \"names\"]\n  ],\n  \"id\": \"<id_lists>\"\n}\n```\n\nFrom this point forward, we can avoid kind `(3)9998` events in favor of using `<id_lists>` in a kind `(3)9999` event to declare new lists:\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", \"<id_lists>\"],\n    [\"names\", \"dog\", \"dogs\"],\n    [\"description\", \"This is a list (by name) of individual dogs.\"],\n    [\"required\", \"t\"]\n  ],\n  \"id\": \"<id_dogs>\"\n}\n```\n\nAlternatively, we can eschew kind `9998` and `39998` events altogether by replacing `[\"z\", \"<id_lists>\"]` with `[\"z\", \"list\"]`, as follows:\n\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", \"list\"],\n    [\"names\", \"dog\", \"dogs\"],\n    [\"description\", \"This is a list (by name) of individual dogs.\"],\n    [\"required\", \"t\"]\n  ],\n  \"id\": \"<id_dogs>\"\n}\n```\n\nThere is a certain elegance in building this NIP entirely around only two event kinds: `9999` and `39999`. However, it is more in line with standard nostr practice to separate list headers from list items by kind. We suggest using `(3)9998` for list header declaration and then experiment with phasing kind `(3)9998` events out entirely if this proves to be a feasible course of action.\n\n## Retrieval of list headers and list items\n\nRetrieval of all canonically-formed lists:\n\n```json\n{\n  \"kinds\": [9998, 39998]\n}\n```\n\nRetrieval of all lists formed using the nonstandard method:\n\n```json\n{\n  \"kinds\": [9999, 39999],\n  \"#z\": [\"list\", \"<id1_lists>\", \"<id2_lists>\", \"<id3_lists>\", ... ]\n}\n```\n\nRetrieval of all items on the list of dogs:\n\n```json\n{\n  \"since\": 0,\n  \"kinds\": [9999, 39999],\n  \"#z\": [\"dog\", \"<id1_dogs>\", \"<id2_dogs>\",  \"<id3_dogs>\", ...]\n}\n```\n\nNote that we are searching for multiple `#z` tags to address the possibility of list redundancy, with multiple redundant list declarations used by different communities.\n\n## List curation and spam prevention\n\nList curation refers to questions like the following:\n- Which items should be accepted to a given list?\n- Which items should be excluded from a given list?\n- How can accepted list items be stratified?\n\nA wide variety of methods of varying degrees of sophistication can be imagined. For example, a _personalized trust metric_ could be used to exclude items based on the personalized trust metric of the item contributor. For simplicity, this NIP presumes these methods to be specified elsewhere.\n\n## Backwards Compatibility with Preexisting NIPs\n\n### Example: NIP-72 Moderated Communities\n\nSuppose we wish to create a Decentralized List of Communities. There are three methods we may consider:\n\n1. Incompatible with NIP-72\n\nStandard declaration of the list header for communities using kind `(3)9998`, with standard declaration of specific communities as list items using kind `(3)9999`:\n\nList Header declaration: \n\n```json\n{\n  \"kind\": 39998,\n  \"tags\": [\n    [\"d\", \"<d_tag_for_list_of_brainstorm_communities>\"],\n    [\"names\", \"Brainstorm Community\", \"Brainstorm Communities\"],\n    [\"description\", \"This is a list of Communities, members of which will be curated using Brainstorm (GrapeRank-style) methods.\"],\n    [\"required\", \"t\"]\n  ],\n  \"id\": \"<id_brainstorm_communities>\"\n}\n```\n\nList Item declaration: \n\n```json\n{\n  \"kind\": 39999,\n  \"tags\": [\n    [\"z\", \"39998:<pubkey_for_list_of_brainstorm_communities>:<d_tag_for_list_of_brainstorm_communities>\"],\n    [\"names\", \"Bitcoin Army\", \"dogs\"],\n    [\"d\", \"<d_tag_for_Bitcoin_Army>\"]\n    [\"t\", \"Bitcoin Army\"]\n  ]\n}\n```\n\n2. Compatibility with NIP-72 using standard list item declaration\n\nUsing this method, kind `(3)9999` list items are pointers to communities that are defined using kind `34550` events as per NIP-72.\n\n- Communities are created and defined using kind `34550`.\n- The kind `(3)9998` List Header for communities specifies the `a` tag as a required tag.\n- Each list item on the list of communities is a kind `(3)9999` event with an `a` tag that points to the kind `34550` community definition.\n\nList Header declaration: \n\n```json\n{\n  \"kind\": 39998,\n  \"tags\": [\n    [\"d\", \"<d_tag_for_list_of_brainstorm_communities>\"],\n    [\"names\", \"Brainstorm Community\", \"Brainstorm Communities\"],\n    [\"description\", \"This is a list of Communities, members of which will be curated using Brainstorm (GrapeRank-style) methods. It is intended to be compatible with NIP-72: Moderated Communities.\"],\n    [\"required\", \"e\"]\n  ],\n  \"id\": \"<id_brainstorm_communities>\"\n}\n```\n\nList Item declaration: \n\nto be completed\n\n3. Compatibility with NIP-72 using nonstandard list item declaration\n\nUsing this method, the kind 34550 community definition event exists exactly as specified by NIP-72, with the caveat that it contains an additional `z`-tag as per this Decentralized Lists NIP.\n\nThis method has the advantage of being more economical than method 2 in the sense that there is one less event required for specification of each list item. The primary disadvantage would happen if the external NIP(s) in question are already using the `z`-tag for some other purpose. However, this is an unusual occurrence. \n\nList Header declaration: \n\n```json\n{\n  \"kind\": 39998,\n  \"tags\": [\n    [\"d\", \"<d_tag_for_list_of_brainstorm_communities>\"],\n    [\"names\", \"Brainstorm Community\", \"Brainstorm Communities\"],\n    [\"item-kinds\", [34550]], // versus: [\"item-kinds\", [9999, 39999, 34550]],\n    [\"description\", \"This is a list of Communities, members of which will be curated using Brainstorm (GrapeRank-style) methods. It is intended to be compatible with NIP-72: Moderated Communities.\"],\n    // need to indicate required tags, as per NIP-72, as well as additional tags that we add to the NIP-72 spec\n  ],\n  \"id\": \"<id_brainstorm_communities>\"\n}\n```\n\nstill need to complete the above, with NIP-72 required tags. Also need to decide how to specify item-kinds and spell this out in the above spec.\n\nList Item declaration: \n\nto be completed","sig":"615f80692d2f96787afff979f3d735f79100615060b9620f3cd1ffba8364b76f130877571bf155d52cddea8db2302663e8738ec9da8ccd0f2c8696e341f11e7d"}