{"id":"5b7eef79c9ef163aa613dd456f5995228e888fea92aab7db76ca3b613d9f5faa","pubkey":"e5272de914bd301755c439b88e6959a43c9d2664831f093c51e9c799a16a102f","created_at":1766168521,"kind":30817,"tags":[["d","properties-for-decentralized-lists"],["title","Properties for Decentralized Lists"],["client","nostrhub.io"]],"content":"Properties for Decentralized Lists\n=====\n\nWe augment the [Decentralized Lists NIP](https://nostrhub.io/naddr1qvzqqqrcvypzpef89h53f0fsza2ugwdc3e54nfpun5nxfqclpy79r6w8nxsk5yp0qyt8wumn8ghj7un9d3shjtnswf5k6ctv9ehx2aqqzdjx2cm9de68yctvd9ax2epdd35hxarnwrn9hx) with a method to curate the properties that individual list items are expected to have. The list of properties will itself be a decentralized list.\n\n# Background and motivation\n\nAccording to the Decentralized Lists NIP, declaration of a list may include specification of one or more tags that are required (or optional) in list items. For example, the declaration of the list of _dogs_ might specify that _name_ (\"Spot\") and _breed_ (\"Irish Setter\") are required tags when adding items to this list:\n\n```json\n{\n  \"kind\": 9998,\n  \"tags\": [\n    [\"names\", \"dog\", \"dogs\"],\n    [\"required\", \"name\"],\n    [\"required\", \"breed\"]\n  ],\n  \"id\": <id_for_list_of_digs>\n}\n```\n\nThese two tags, name and breed, are expected to be strings. This method has several disadvantages:\n- Tag values can only be strings; there is no clear method to specify when to use non-string data types (number, boolean, object, array, null)\n- The list of tags is fixed, not able to grow over time\n- There is no method for the web of trust to curate the list of tags for a given list\n- Tags are not reusable\n\nThis NIP addresses these disadvantages through the use of items on the list of _properties_ in place of the kind 9998 _required_ tag. A relationship using the specialized relationship type, _is a property of_, is used to associate specific properties with specific lists.\n\n# The list of _properties_ and the relationship type: _is a property of_\n\nDeclare the list of Properties:\n\n```json\n{\n  \"kind\": 9998,\n  \"tags\": [\n    [\"names\", \"property\", \"properties\"],\n    [\"description\", \"type must be one of the standard json types: string, number, boolean, null, object, array.\"],\n    [\"required\", \"name\"],\n    [\"required\", \"type\"],\n    [\"optional\", \"allowed_value\"] // specifies allowed values for the name tag\n  ],\n  \"id\": <id_for_list_of_properties>\n}\n```\n\nCreate a relationship type called _is a property of_\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", <id_for_list_of_relationship_types>],\n    [\"name\", \"is a property of\"],\n    [\"description\", \"lorem ipsum\"],\n    [\"direction\", \"forward\"],\n    [\"required\", \"nodeFrom\"],\n    [\"required\", \"nodeTo\"]\n  ],\n  \"id\": <id_for_is_a_property_of>\n}\n```\n\n# Example: Add _breed_ as a property to the list of _dogs_\n\nSuppose the list of _dogs_ is declared with name and breed as the only required tags, as in the example above.\n\nAs alternative to including breed as a required tag, as in the above example, is to declare breed as a property, as in the example below:\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", <id_for_list_of_properties>],\n    [\"type\", \"string\"],\n    [\"name\", \"breed\"],\n    [\"allowed_value\", \"Irish Setter\"],\n    [\"allowed_value\", \"Golden Retriever\"],\n    [\"allowed_value\", \"poodle\"]\n  ],\n  \"id\": <id_for_breed_property>\n}\n```\n\nAttach the _breed_ property to the list of _dogs_\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", <id_for_list_of_relationships>],\n    [\"relationship_type\", <id_for_is_a_property_of>],\n    [\"description\", \"lorem ipsum\"],\n    [\"nodeFrom\", <id_for_breed_property>],\n    [\"nodeTo\", <id_for_dogs>]\n  ]\n}\n```\n\n# Enumeration\n\nIn the above example, the list of breed is declared with three allowed values: Irish Setter, Golden Retriever, and poodle. What if we want allowed values to be a list?\n\nDeclaration of the list of _dog breeds_:\n\n```json\n{\n  \"kind\": 9998,\n  \"tags\": [\n    [\"names\", \"dog breed\", \"dog breeds\"],\n    [\"description\", \"lorem ipsum\"],\n    [\"required\", \"name\"]\n  ],\n  \"id\": <id_for_list_of_dog_breeds>\n}\n```\n\nInstead of enumerating allowed values when delaring the proerty: breed, we enumerate them using the tag: _list_of_allowed_values_for_name_:\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", <id_for_properties>],\n    [\"type\", \"string\"],\n    [\"name\", \"dog breed\"],\n    [\"list_of_allowed_values_for_name\", <id_for_list_of_dog_breeds>]\n  ],\n  \"id\": <id_for_dog_breed_property>\n}\n```\n\nThis process creates a relationship between two lists in a process known as _horizontal integration_. In the above example, the list of _dog breeds_ is horizonally integrated with the list of _dogs_.\n\nAn alternative method of horizontal integration is using a special relationship type: _enumerates_. We eliminate the tag: `[\"list_of_allowed_values_for_name\", <id_for_list_of_dog_breeds>]` and in its place we have the following relationship:\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", <id_for_list_of_relationships>],\n    [\"relationship_type\", <id_for_enumerates>],\n    [\"description\", \"lorem ipsum\"],\n    [\"nodeFrom\", <id_for_list_of_dog_breeds>],\n    [\"nodeTo\", <id_for_dog_breed_property>]\n  ]\n}\n```\n\n# Properties of type: Object\n\nThe defining characteristic of an object is that it is itself composed of properties. If property A is of type object, we specify that property B is a property of property A using the relationship:\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", <id_for_list_of_relationships>],\n    [\"relationship_type\", <id_for_is_a_property_of>],\n    [\"description\", \"lorem ipsum\"],\n    [\"nodeFrom\", <id_for_property_A>],\n    [\"nodeTo\", <id_for_property_B>]\n  ]\n}\n```\n\nWe expect a property of type: object to be stringified as in the example below.\n\nIn the below example, we create a property of type: object called \"pedigree\" to bundle one boolean and three string properties together:\n\n![Properties of Dog](https://i.nostr.build/U3gWNauEnoylpTDv.png)\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", <id_for_list_of_dogs>],\n    [\"name\", \"Spot\"],\n    [\"pedigree\", '{ \"pureblood\": true, \"breed\": \"Irish Setter\", \"mother\": <p-tag_for_spots_mother>, \"father\": <p-tag_for_spots_father>}']\n  ]\n}\n```\n\nA property tree can be of arbitrary depth. Effectively, the property tree defines the [JSON Schema](https://json-schema.org/) for an object, which we will refer to as a Kind 9999 Object (K9O). At some point it will make more sense to construct the data as a K9O that validates against this JSON Schema, then stringify the K9O and put that into the `content` field, like this:\n\nThe Kind 9999 Object for Spot:\n\n```json\n\"dogData\": { // alternate: <id_for_list_of_dogs>\n  \"name\": \"Spot\",\n  \"pedigree\": {\n    \"pureblood\": true,\n    \"breed\": \"Irish Setter\", // alternate: <id_for_Irish_Setter>\n    \"mother\": <p-tag_for_spots_mother>,\n    \"father\": <p-tag_for_spots_father>\n  }\n}\n```\n\n```json\n{\n  \"kind\": 9999,\n  \"tags\": [\n    [\"z\", <id_for_list_of_dogs>],\n    [\"name\", \"Spot\"],\n    [\"pedigree\", '{ \"pureblood\": true, \"breed\": \"Irish Setter\", \"mother\": <p-tag_for_spots_mother>, \"father\": <p-tag_for_spots_father>}']\n  ],\n  \"content\": <stingified K9O>\n}\n```\n\nWe can declare the list of JSON Schemas, such that there should be a 1-to-1 correspondence between a property tree and a JSON schema. Given a property tree, we can determine the JSON schema, add it to the knowledge graph so that for any given list item, it will be a simple matter to validate any given list item against its corresponding JSON schema(s).\n\n```json\n{\n  \"kind\": 9998,\n  \"tags\": [\n    [\"names\", \"JSON schema\", \"JSON schemas\"],\n    [\"description\", \"See json-schema.org\"]\n  ],\n  \"id\": <id_for_list_of_json_schemas>\n}\n```\n\nThe corresponding knowledge graph can be organized as follows:\n\n![JSON Schema for Dog](https://i.nostr.build/Orh6WgETmtnqKnBW.png)\n\nGiven the above Knowledge Graph, we introduce the notion of a `class thread` as the specialized path that connects any given list item (kind 9999 event -- the green node in the above figure) to its JSON Schema (the yellow node in the figure). The `Class Thread Principle` is the constraint that within any knowledge graph, for every given class thread, the kind 9999 event at the end of the thread must validate against the JSON Schema at the start of the thread. The completeness criterion is the requirement that within the KG, each kind 9999 event must be connected via class thread(s) to at least one JSON Schema. In other words, for every list item in the KG, we can find its list header(s) and JSON Schema(s) by tracing its class threads. (Reminder that the Decentralized Lists NIP allows any given kind 9999 event to be the item on more than one list; this is achieved by using more than one z-tag to the 9999 event.)\n\nThese ideas are described in more detail in [this article](https://prettygoodproject.substack.com/p/for-web-of-trust-to-succeed-it-must), [this article](https://prettygoodproject.substack.com/p/the-tapestry-model) and [this other article](https://prettygoodproject.substack.com/p/short-pitch-for-the-concept-graph). All 3 of these articles are somewhat out of date, but get across the basic idea of class threads.","sig":"e0d7c76e35ddf82513d8827fd292d358ca806f98ec94fa5e51e7484e75676ad6d2978286c365678201a914064763ed164e72029c46db35d0f421e8c0cad2123f"}