ENSIP: Organizational Metadata (Node Classification Metadata)

Hey @jkm.eth and the Lighthouse team,

Great to see the Org Metadata ENSIP coming together! We’ve been following this since the temp check and as Conor mentioned early on, we at Enscribe have been working on a related problem: storing standardized metadata for smart contracts on ENS (PR #50).

We’ve shipped and iterated on this quite a bit since then, and wanted to share some feedback and ideas from what we’ve learned building it out. Two things specifically:


1. Hierarchical Inheritance — the big one

ENS names are already hierarchical — subnames are children of parent names. This maps really well to how orgs actually work: ensdao.ethtreasury.ensdao.ethmultisig.treasury.ensdao.eth or ensdao.ethgovernor.ensdao.eth. Your ENSIP uses this structure for organizing nodes, which is great. But we think the spec should go a step further and formalize metadata inheritance across the name hierarchy.

Why this matters

Right now, without inheritance, every node needs to store all its metadata independently — even when most of it is the same as the parent. If a DAO has 50 contract subnames, each one would redundantly store the org’s avatar, description, social links, docs URL, etc.

This causes a few real problems:

  • Wasted gas - you’re paying to write the same text records over and over across subnames
  • Inconsistency - org updates its avatar? Now you need to update it on every single subname
  • Maintenance headache - more records = more things that go stale

We’ve built this and it works

We actually have this inheritance model live in Enscribe today. Here’s how it looks across our own name hierarchy:

enscribe.eth (2LD org level) stores org-wide metadata:

  • avatar, header, description, url, com.twitter, com.github, org.telegram
  • View metadata

app.enscribe.eth (3LD - app level) stores app-specific metadata, inherits the rest from parent:

v2.app.enscribe.eth (4LD - contract level) doesn’t set any of its own records currently, but could add contract-specific keys like audits in the future. Everything it surfaces comes from inheritance:

When you query the 4LD through our API, you get the full resolved metadata even though this name itself doesn’t store any records directly:

GET https://app.enscribe.xyz/api/v1/contractMetadata/1/v2.app.enscribe.eth
{
  "avatar": "https://euc.li/enscribe.eth",
  "description": "Fixing Ethereum UX one smart contract at a time.",
  "url": "https://www.enscribe.xyz/",
  "category": "Security",
  "license": "MIT",
  "docs": "https://enscribe.xyz/docs",
  "com.github": "enscribexyz/enscribe-contracts",
  "com.twitter": "enscribe_",
  "org.telegram": "enscribers"
}

Some of this comes from enscribe.eth, some from app.enscribe.eth, but the client gets one clean merged result. The server just walks up the hierarchy and fills in what’s missing. Here’s how it looks for v2.app.enscribe.eth

What we’d change in the spec

Add an inherit boolean to schema properties where it makes sense, with default as false. This tells resolvers which fields should fall back to parent nodes when not set at the current level — basically a fallback mechanism up the name tree. Only fields marked "inherit": true trigger the parent lookup; everything else stays node-specific by default.

Using the Person schema from the current draft as an example:

{
  "$id": "v1.0",
  "title": "Person",
  "description": "This node represents an individual human",
  "type": "object",
  "properties": {
    "firstName": {
      "type": "string",
      "description": "The person's first name."
    },
    "lastName": {
      "type": "string",
      "description": "The person's last name."
    },
    "proofOfHumanity": {
      "type": "string",
      "description": "A signed attestation of proof of humanity.",
      "recordType": "data"
    },
    "avatar": {
      "inherit": true
    }
  }
}

Here avatar gets inherited from the parent node (at any level up), while firstName and lastName don’t have inherit so they stay node-specific. Simple, removes redundancy, and makes the whole thing easier to reason about.


2. Contract Metadata Schema Template

Separate from inheritance, based on our work with contract metadata, we’d like to share a schema template for contract-type nodes that plugs into the Org Metadata framework.

This isn’t a new ENSIP or a separate proposal. It’s a community schema that fits into your existing design. We loved your schema approach - the whole point of the schema approach is that people can create and distribute these without needing a new proposal each time. Here’s what we’ve landed on:

{
  "$id": "ens-schema:contract-metadata/v1.0",
  "title": "Contract",
  "description": "This node represents a smart contract deployed on-chain",
  "type": "object",
  "properties": {
    "name": {
      "type": "string",
      "description": "Human-readable name of the contract or application"
    },
    "description": {
      "type": "string",
      "description": "Brief description of what the contract does",
      "inherit": true
    },
    "avatar": {
      "type": "string",
      "description": "Avatar image URI",
      "inherit": true
    },
    "url": {
      "type": "string",
      "description": "Official project website",
      "inherit": true
    },
    "category": {
      "type": "string",
      "description": "Contract type (defi, dao, nft, gaming, utility, security, etc.)"
    },
    "license": {
      "type": "string",
      "description": "Software license (SPDX format, e.g. MIT, Apache-2.0)",
      "inherit": true
    },
    "docs": {
      "type": "string",
      "description": "Documentation URL",
      "inherit": true
    },
    "audits": {
      "type": "array",
      "description": "Security audit reports",
      "items": {
        "type": "object",
        "properties": {
          "auditor": { "type": "string" },
          "report": { "type": "string" }
        }
      }
    },
    "com.github": {
      "type": "string",
      "description": "GitHub repository (e.g. org/repo)"
    },
    "com.twitter": {
      "type": "string",
      "description": "X/Twitter handle",
      "inherit": true
    },
    "org.telegram": {
      "type": "string",
      "description": "Telegram handle",
      "inherit": true
    }
  }
}

Fields like description, avatar, url, license, docs, and socials have "inherit": true — they flow down from the org level. Things like name, category, audits, and com.github are contract-specific so they don’t inherit. This would sit alongside schemas for “Person”, “DAO”, “WorkingGroup”, etc.


We’ve been heads down on contract metadata for a few months now and would love to bring those learnings into this standard directly. Happy to co-author the inheritance section, contribute the contract schema as a reference, or share implementation details from our live deployment.

Let us know how we can help!

3 Likes