|
| 1 | +--- |
| 2 | +title: Node Classification Metadata |
| 3 | +description: Optional metadata to describe the organizational role of an ENS node |
| 4 | +contributors: |
| 5 | + - jkm.eth |
| 6 | + - 1a35e1.eth |
| 7 | +ensip: |
| 8 | + created: "2025-12-15" |
| 9 | + status: draft |
| 10 | +--- |
| 11 | + |
| 12 | +# ENSIP-X: Node Classification Metadata |
| 13 | + |
| 14 | +## Abstract |
| 15 | + |
| 16 | +This ENSIP proposes a standard method for using text records to declare the role of an ENS name/subname (node). Two types of standardized text records are introduced, which provide for labeling the role of node against a larger organizational structure, and for defining the structure of additional, context-dependent attributes that may be appended to that node. |
| 17 | + |
| 18 | +## Motivation |
| 19 | + |
| 20 | +In practice, ENS subnames are often used to represent organizational structures. For example, an ENS name belonging to an individual might have a subname which points to the individual's cold wallet, and another which points to a hot wallet that they use for voting in on-chain governance. DAOs or other on-chain organizations might have a top-level ENS name that represents the group as a whole, with subnames created to represent treasuries, smart contracts, working groups, committees, and other entities within the organization. This ENSIP offers a way to add metadata to each of those nodes to declare its role in the larger organizational structure, and to append context-specific metadata which is machine-readable and standardized, allowing dynamic discovery and recognition of the role each subname is meant to fulfill. |
| 21 | + |
| 22 | +## Specification |
| 23 | + |
| 24 | +The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119 and RFC 8174. |
| 25 | + |
| 26 | +### Metadata Records |
| 27 | + |
| 28 | +This ENSIP builds on ENSIP-5 and ENSIP-24, and uses text records stored in each node's resolver to provide additional metadata for that node. We define two new unqualified, global key names (`class` and `schema`) which are hereby reserved for use in accordance with this ENSIP. Additional metadata attributes can also be added to a node and stored as text or data records, with their key names declared by the `schema` entry (explained below). |
| 29 | + |
| 30 | +It is expected that each node will be configured to resolve to one or more on-chain addresses, and the metadata attributes attached to a node are understood to apply to the entity or entities represented by those addresses. |
| 31 | + |
| 32 | +### `class` Text Record |
| 33 | + |
| 34 | +Nodes can specify a text record with the key name of `class`, the value of which serves to label the general role or purpose of that node. These values are largely intended to be useful to humans who wish to understand what importance to give to each node, but can also be used programmatically in filtering and recognizing categories of node for automated systems. |
| 35 | + |
| 36 | +The value of this record MUST be pascal case, using only alphabet characters. Values SHOULD be limited to those outlined in the following table to maximize compatability across the ecosystem, however other values MAY be used for specialized use cases. |
| 37 | + |
| 38 | +| `class` Value | Meaning | |
| 39 | +|---|---| |
| 40 | +| Agent | This node represents an autonomous software-controlled entity | |
| 41 | +| Application | This node represents a software application, service, or product | |
| 42 | +| Committee | This node represents a formal group with delegated authority to make decisions or provide oversight within a defined scope | |
| 43 | +| Contract | This node represents, and resolves to, a smart contract | |
| 44 | +| Council | This node represents a high-level governance body with broad strategic authority and stewardship responsibilities | |
| 45 | +| Delegate | This node represents a voter in on-chain governance, who may or may not have been delegated voting power from others | |
| 46 | +| Group | This node represents a logical grouping of multiple child nodes | |
| 47 | +| Org | This node represents an organization or sub-organization within a larger entity | |
| 48 | +| Person | This node represents an individual human | |
| 49 | +| Treasury | This node represents an account for funds that are collectively owned and/or managed | |
| 50 | +| Wallet | This node's main purpose is to send, receive, and/or store funds | |
| 51 | +| Workgroup | This node represents an operational team focused on executing tasks or work within a specific domain | |
| 52 | + |
| 53 | +It is understood that all on-chain addresses could have the ability to perform a base set of actions, including: |
| 54 | +* sending and receiving funds |
| 55 | +* calling smart contracts |
| 56 | +* publishing new smart contracts |
| 57 | +* providing signatures |
| 58 | + |
| 59 | +Therefore, `class` designations like `contract` and `wallet` should only be interpreted as declaring that node's main purpose in the context of the organizational structure; nodes that do not use one of these classes can still point to addresses which are smart contracts and/or hold funds. The same address can be pointed to by multiple nodes, each with a different `class` designation and metadata describing a different aspect of that address/account. |
| 60 | + |
| 61 | +### `schema` Text Record |
| 62 | + |
| 63 | +Nodes can specify a text record with the key name of `schema`, the value of which points to a JSON schema which declares which metadata attributes can be added to the node as text or data records, in addition to the global text records already specified in existing ENSIPs. The value of `schema` MUST start with one of the following prefixes, followed by the appropriate value: |
| 64 | + |
| 65 | +* `ipfs://` - followed by the ipfs URI pointing to the JSON payload |
| 66 | +* `https://` - followed by the http(s) URI pointing to the JSON payload |
| 67 | +* `cbor:` - followed by the schema encoded in CBOR format |
| 68 | + |
| 69 | +### Schema Payload |
| 70 | + |
| 71 | +If a schema is provided for a node, it specifies which additional metadata attributes are expected to be provided for that node, stored as text or data records. Schemas MUST follow the JSON Schema specification, [version 2020-12](https://json-schema.org/draft/2020-12/json-schema-core), and describe a single-level object in which property names match the text or data record key names. Attribute key names MUST use camel case. |
| 72 | + |
| 73 | +Clients that facilitate storing metadata records SHOULD reject values that fail validation according to the provided schema. |
| 74 | + |
| 75 | +Clients that facilitate retrieving metadata records MAY ignore values that fail validation according to the provided schema. |
| 76 | + |
| 77 | +#### Additional details |
| 78 | + |
| 79 | +* The schema's `$id` field SHOULD be used to identify the schema's creator and/or version. |
| 80 | +* The schema's `title` field identifies what entity is described in the data structure. If the schema is intended to be used with a specific `class`, the value of `title` SHOULD be the same as the class it is meant to represent. |
| 81 | +* If a node has a `schema` present but no `class` record set, the value of the schema's `title` SHOULD be used as the class identifier for the node. |
| 82 | +* Schema authors are encouraged to populate the `description` field with an explanation of the organizational role fulfilled by nodes which use this schema, in line with the `class` descriptions listed above. |
| 83 | +* Schemas MAY include definitions for key names which are declared as global in other ENSIPs, however if present, the descriptions of these keys MUST NOT be in direct conflict of their original definition provided by existing ENSIPs. |
| 84 | +* Attributes can include an optional `recordType` ("text" | "data") keyword which indicates if the record is stored as `text` (ENSIP-5) or `data` (ENSIP-24) (default: `text`) |
| 85 | + |
| 86 | +#### Basic schema example: |
| 87 | + |
| 88 | +``` |
| 89 | +{ |
| 90 | + "$id": "v1.0", |
| 91 | + "title": "Person", |
| 92 | + "description": "This node represents an individual human", |
| 93 | + "type": "object", |
| 94 | + "properties": { |
| 95 | + "firstName": { |
| 96 | + "type": "string", |
| 97 | + "description": "The person's first name.", |
| 98 | + }, |
| 99 | + "lastName": { |
| 100 | + "type": "string", |
| 101 | + "description": "The person's last name." |
| 102 | + }, |
| 103 | + "proofOfHumanity": { |
| 104 | + "type": "string", |
| 105 | + "description": "A signed attestation of proof of humanity.", |
| 106 | + "recordType": "data" |
| 107 | + }, |
| 108 | + "avatar": { |
| 109 | + "type": "string", |
| 110 | + "description": "a URL to an image used as an avatar or logo" |
| 111 | + } |
| 112 | + } |
| 113 | +} |
| 114 | +``` |
| 115 | + |
| 116 | +#### Parameterized Key Names |
| 117 | + |
| 118 | +Schemas can support parameterized properties, which allow a single property to have multiple variant-specific values. Parameters are specified using bracket notation appended to the property when used as a key name: |
| 119 | + |
| 120 | +``` |
| 121 | +keyName[parameter] |
| 122 | +``` |
| 123 | + |
| 124 | +Schemas MAY simultaneously support both the base form (`keyName`) and parameterized form (`keyName[parameter]`). The parameterized form with empty brackets (`keyName[]`) SHALL NOT be allowed. |
| 125 | + |
| 126 | +When both base and parameterized forms exist, clients SHOULD treat them as independent records, with the base form serving as a default when no specific parameter is requested. |
| 127 | + |
| 128 | +To add a parameterized key to a JSON schema, add a regex pattern which enforces the use of brackets to the `patternProperties` object. The following example regex value will accept either the base form or the parameterized form, while rejected empty brackets: `^keyName(\[[^\]]+\])?$` |
| 129 | + |
| 130 | +When parsing key names, the following regex can be used to isolate the base form (group 1) and the parameter (group 3, if provided): `^(keyName)(\[([^\]]+)\])?$` |
| 131 | + |
| 132 | +**Note:** Defining which values are allowed to be passed inside of the brackets when setting and retrieving records is up to schema publishers and is outside the scope of this ENSIP. |
| 133 | + |
| 134 | +#### Basic schema example including parameterized properties: |
| 135 | + |
| 136 | +``` |
| 137 | +{ |
| 138 | + "$id": "v1.0", |
| 139 | + "title": "Person", |
| 140 | + "description": "This node represents an individual human", |
| 141 | + "type": "object", |
| 142 | + "properties": { |
| 143 | + "firstName": { |
| 144 | + "type": "string", |
| 145 | + "description": "The person's first name.", |
| 146 | + }, |
| 147 | + "lastName": { |
| 148 | + "type": "string", |
| 149 | + "description": "The person's last name." |
| 150 | + }, |
| 151 | + "avatar": { |
| 152 | + "type": "string", |
| 153 | + "description": "a URL to an image used as an avatar or logo" |
| 154 | + } |
| 155 | + }, |
| 156 | + "patternProperties": { |
| 157 | + "^proofOfHumanity(\[[^\]]+\])?$": { |
| 158 | + "type": "string", |
| 159 | + "description": "A signed proof of humanity attestation. The name of a specific provider can be passed as a parameter.", |
| 160 | + "recordType": "data" |
| 161 | + } |
| 162 | + } |
| 163 | +} |
| 164 | +``` |
| 165 | + |
| 166 | +In this example, the owner of the node could use the key `proofOfHumanity[provider]` to store a proof of humanity attestation from a specific provider, and they could use `proofOfHumanity` to publish a default attestation to be retrieved if no provider is specified. |
| 167 | + |
| 168 | +## Backwards Compatibility |
| 169 | + |
| 170 | +This proposal is built upon existing ENSIPs and does not affect existing ENS functionality. It introduces no breaking changes. |
| 171 | + |
| 172 | +## Security Considerations |
| 173 | + |
| 174 | +None. |
| 175 | + |
| 176 | +## Copyright |
| 177 | + |
| 178 | +Copyright and related rights waived via [CC0](https://creativecommons.org/publicdomain/zero/1.0/). |
0 commit comments