ENSIP: Text Record Attestations

Text records are commonly used to publish profile information, such as a user’s name or social media handle. A long-standing problem with this setup is that this information is often displayed as part of the user’s “profile” without any way to verify if the information is actually correct.

Some of this information can be verified on-chain or by using cryptographic proofs or other methods. For some types of information, however, using a trusted authority to attest to the truthiness of a statement is the best and/or only option. A good example is ownership of a Twitter/X account. Only X can say who owns an account, so this information can not be proven cryptographically and relies on X as the authority.

If you trusted someone to talk to X on your behalf and verify ownership of an account, you might consider them an acceptable authority as well.

Since starting work on ENSIP-27 and attaching metadata to ENS names, we have been working on a solution for how to best publish these attestations to ENS names for quick and easy verification of text record data. Earlier this year we launched a proof of concept implementation, specifically for proving ownership of social media accounts, detailed at atst.me.

We have now submitted a draft ENSIP to formalize this method: ENSIP: Text Record Attestations by jmacwhyte · Pull Request #85 · ensdomains/ensips · GitHub

This ENSIP is focused solely on allowing entities to attest to the veracity of data published as a text record under a specific ENS name. Work on solutions to verify other aspects of ENS names are still ongoing (such as Vedant’s post here)

5 Likes

We created a small playground to explore how it works. playground.atst.me lets you create, verify, and inspect attestations, so you can see exactly how the scheme works.

The problem

Nothing stops vitalik.eth from listing com.twitter = elonmusk, and nothing tells the viewer it’s wrong. For a system built around identity, that’s a gap that impersonators are happy to use.

Some records can be verified on-chain or with cryptographic proofs. Social handles can’t. Only X knows who controls an X account, so any proof has to route through X, or through someone you trust to check with X on your behalf.

The solution

Text Record Attestations let an entity sign a statement saying “this ENS name’s record k really has value v.” The attestation is published alongside the record. Any client can fetch it, check the signature against the attester’s ENS name, and show a verified mark. If the record changes, the attestation no longer matches and the mark disappears.

An attestation contains five things:

  • n — the user’s ENS name
  • a — the user’s address
  • k — the text record key (e.g. com.twitter)
  • v — the attested value
  • the attester’s ENS name

The attester can be anyone with an ENS name and a signing key. atst.me is one attester, for X accounts. A DAO could attest to membership, an employer to staff, a conference to speakers. Clients decide which attesters they trust, the same way they decide which resolvers or gateways to trust.

See it work in 30 seconds

Open the playground:

  1. Attest — generate a key and sign an attestation for your own name.
  2. Verify — paste it in and watch it pass.
  3. Change one character of the value and verify again. It fails.

That’s the whole security property, and it’s the reason a verified badge is worth something.

Visual explainers




Where this sits

This grew out of ENSIP-27 and our work on attaching metadata to ENS names. We shipped the atst.me proof of concept earlier this year, and it’s been running since. The draft ENSIP formalises the format so any attester and any client can interoperate:

ENSIP: Text Record Attestations (PR #85)

What we’d like from you

  • Poke at the playground. If you’re building a client, the Verify tab is the reference for what you need to implement.
  • Review the ENSIP. Comments on the PR or in this thread are equally welcome.
1 Like

Why not use binary data fields (ENSIP-24) for storing attestations rather than hex- or base64-encoded text?

1 Like

That would be more efficient and is preferable! The only question is accessibility (how many resolver contracts have ENSIP-24 support, and are there many existing users who will need to switch resolver just to add an attestation?)

I would be grateful for any data on this, because I haven’t been able to figure out how to decide if it should be text() or data() (or either, based on which the resolver supports?)

One part I’m still trying to understand is the trust model on the client side.

Is the expectation that each client maintains its own list of trusted attesters? If so, it seems possible for the same ENS profile to appear as “verified” in one client and “unverified” in another.

Would it make sense to have an optional, community-maintained registry of recommended attesters — somewhat analogous to a browser root store — while still allowing individual clients to override or extend it?

It may also make sense for trust to be scoped by claim type. For example, an attester trusted to verify ownership of an X account may not necessarily be trusted to verify KYC, employment, or other claims.

1 Like

Yes, exactly. Anyone can issue an attestation, but that doesn’t make every attester equally authoritative. Clients decide which attesters they trust, and that trust can be scoped to particular claims.

A KYC provider might be trusted to attest that it has verified someone’s passport, for example, while an attestation from the issuing government would carry greater authority. Likewise, an attester that is reliable for social-account ownership need not be trusted for identity or employment claims.

The idea is to enable an ecosystem of specialised attesters contributing evidence about different facts, while leaving each client free to decide whose claims it accepts and how much weight to give them.

A registry would aid clients discovering well known attesters.

1 Like

the registry idea is where this gets sharp, and i think it needs one more constraint before it’s safe.

trust here is pinned to an attester’s ens name, and validation resolves that name live - step 6 reads the attester’s current address. that’s the same property that makes key rotation work as revocation: yesterday’s compromised key stops validating today, with no revocation infrastructure. but it cuts both ways. an ens name is a lease. if atst.example.eth lapses and someone else registers it, they point the resolver at a key they hold, and every consumer carrying atst.example.eth in its trusted list is now trusting them. they can’t forge the old attestations - those still fail, correctly - but everything they sign from that point validates cleanly under a name the ecosystem already agreed to trust.

a registry concentrates that rather than fixing it. one lapsed registration and every client pulling the list inherits the new owner silently. there’s no event for a consumer to notice, either: the name resolves, the signature recovers, the badge is green. the failure is indistinguishable from correct operation from inside the validation algorithm as written.

the security considerations cover an attester signing without the authority to do so, and the mutable-username gap, but not this one. i’d name it, and i’d have any registry pin (name, address) pairs rather than names alone — so a rotation becomes an explicit change a maintainer ratifies, and a client can tell “the attester rotated their key” apart from “someone else owns the attester now.” those two look identical today and they want opposite handling: a legitimate rotation should invalidate every old attestation, which is exactly what happens now and is right. a lapsed-and-reregistered name should distrust the attester going forward, which nothing currently does.

This is an issue for registry design, unrelated to the attestations themselves. Like you said, the solution is to have the registry store ENS name/address pairs and stop suggesting an attester who has had their name expire or changed their address unexpectedly.