Properties for Decentralized Lists

Properties for Decentralized Lists

We augment the Decentralized Lists NIP 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.

Background and motivation

According 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:

{
  "kind": 9998,
  "tags": [
    ["names", "dog", "dogs"],
    ["required", "name"],
    ["required", "breed"]
  ],
  "id": <id_for_list_of_digs>
}

These two tags, name and breed, are expected to be strings. This method has several disadvantages:

  • 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)
  • The list of tags is fixed, not able to grow over time
  • There is no method for the web of trust to curate the list of tags for a given list
  • Tags are not reusable

This 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.

The list of properties and the relationship type: is a property of

Declare the list of Properties:

{
  "kind": 9998,
  "tags": [
    ["names", "property", "properties"],
    ["description", "type must be one of the standard json types: string, number, boolean, null, object, array."],
    ["required", "name"],
    ["required", "type"],
    ["optional", "allowed_value"] // specifies allowed values for the name tag
  ],
  "id": <id_for_list_of_properties>
}

Create a relationship type called is a property of

{
  "kind": 9999,
  "tags": [
    ["z", <id_for_list_of_relationship_types>],
    ["name", "is a property of"],
    ["description", "lorem ipsum"],
    ["direction", "forward"],
    ["required", "nodeFrom"],
    ["required", "nodeTo"]
  ],
  "id": <id_for_is_a_property_of>
}

Example: Add breed as a property to the list of dogs

Suppose the list of dogs is declared with name and breed as the only required tags, as in the example above.

As 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:

{
  "kind": 9999,
  "tags": [
    ["z", <id_for_list_of_properties>],
    ["type", "string"],
    ["name", "breed"],
    ["allowed_value", "Irish Setter"],
    ["allowed_value", "Golden Retriever"],
    ["allowed_value", "poodle"]
  ],
  "id": <id_for_breed_property>
}

Attach the breed property to the list of dogs

{
  "kind": 9999,
  "tags": [
    ["z", <id_for_list_of_relationships>],
    ["relationship_type", <id_for_is_a_property_of>],
    ["description", "lorem ipsum"],
    ["nodeFrom", <id_for_breed_property>],
    ["nodeTo", <id_for_dogs>]
  ]
}

Enumeration

In 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?

Declaration of the list of dog breeds:

{
  "kind": 9998,
  "tags": [
    ["names", "dog breed", "dog breeds"],
    ["description", "lorem ipsum"],
    ["required", "name"]
  ],
  "id": <id_for_list_of_dog_breeds>
}

Instead of enumerating allowed values when delaring the proerty: breed, we enumerate them using the tag: list_of_allowed_values_for_name:

{
  "kind": 9999,
  "tags": [
    ["z", <id_for_properties>],
    ["type", "string"],
    ["name", "dog breed"],
    ["list_of_allowed_values_for_name", <id_for_list_of_dog_breeds>]
  ],
  "id": <id_for_dog_breed_property>
}

This 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.

An 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:

{
  "kind": 9999,
  "tags": [
    ["z", <id_for_list_of_relationships>],
    ["relationship_type", <id_for_enumerates>],
    ["description", "lorem ipsum"],
    ["nodeFrom", <id_for_list_of_dog_breeds>],
    ["nodeTo", <id_for_dog_breed_property>]
  ]
}

Properties of type: Object

The 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:

{
  "kind": 9999,
  "tags": [
    ["z", <id_for_list_of_relationships>],
    ["relationship_type", <id_for_is_a_property_of>],
    ["description", "lorem ipsum"],
    ["nodeFrom", <id_for_property_A>],
    ["nodeTo", <id_for_property_B>]
  ]
}

We expect a property of type: object to be stringified as in the example below.

In the below example, we create a property of type: object called “pedigree” to bundle one boolean and three string properties together:

Properties of Dog

{
  "kind": 9999,
  "tags": [
    ["z", <id_for_list_of_dogs>],
    ["name", "Spot"],
    ["pedigree", '{ "pureblood": true, "breed": "Irish Setter", "mother": <p-tag_for_spots_mother>, "father": <p-tag_for_spots_father>}']
  ]
}

A property tree can be of arbitrary depth. Effectively, the property tree defines the JSON Schema 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:

The Kind 9999 Object for Spot:

"dogData": { // alternate: <id_for_list_of_dogs>
  "name": "Spot",
  "pedigree": {
    "pureblood": true,
    "breed": "Irish Setter", // alternate: <id_for_Irish_Setter>
    "mother": <p-tag_for_spots_mother>,
    "father": <p-tag_for_spots_father>
  }
}
{
  "kind": 9999,
  "tags": [
    ["z", <id_for_list_of_dogs>],
    ["name", "Spot"],
    ["pedigree", '{ "pureblood": true, "breed": "Irish Setter", "mother": <p-tag_for_spots_mother>, "father": <p-tag_for_spots_father>}']
  ],
  "content": <stingified K9O>
}

We 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).

{
  "kind": 9998,
  "tags": [
    ["names", "JSON schema", "JSON schemas"],
    ["description", "See json-schema.org"]
  ],
  "id": <id_for_list_of_json_schemas>
}

The corresponding knowledge graph can be organized as follows:

JSON Schema for Dog

Given 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.)

These ideas are described in more detail in this article, this article and this other article. All 3 of these articles are somewhat out of date, but get across the basic idea of class threads.


Looking for comments…

Searching Nostr relays. This may take a moment the first time this article is opened.