A community proposal for mapping web pages back to AT URIs.
28

Configure Feed

Select the types of activity you want to include in your feed.

TypeScript 100.0%
10 1 0

Clone this repository

https://tangled.org/chrisshank.com/at-tags https://tangled.org/did:plc:3qjugal4noyyfleptiojmwvy
git@tangled.org:chrisshank.com/at-tags git@tangled.org:did:plc:3qjugal4noyyfleptiojmwvy

For self-hosted knots, clone URLs may differ based on your setup.


README.md

AT Tags#

Note: This proposal is still a draft, but it's stabilized enough that folks are now implementing it.

Introduction#

There is no easy way to know if any given web page references an atproto record or identity and, if so, what those AT URIs are. This is important for the ecosystem where end-user's have preferred clients, where pasting links could turn into interactive embeds, where we might want to verify the relationship between an AT record and web page, or where search engines can take advantage of this metadata to provide richer search experiences. Currently the ecosystem is stuck at a local-maximum of parsing URL structures and scraping the DOM in any hope to extract such metadata. These approaches will not scale in our growing ecosystem and we need a shared language and communal practice to solve this.

The timing of this proposal is equally pressing as the use cases for this metadata continue increasing every day. For example, the most prominent example is standard.site using <link> tags to bi-directionally verify that a web page is rendering it's corresponding standard.site record. Unfortunately, AT URI's are invalid URLs, so we either have to make a breaking change to AT URIs, the web has to standardize AT URIs, or we change the way standard.site verification works. The latter provides us an opportunity to generalize these tags to work with any lexicon for any web page that builds on atproto.

Protocol#

This protocol consists of adding metadata to webpages that closely follow the web's <link> tag and Open Graph tags. We propose the at: namespace on <meta> tags and 4 standard properties: canonical, alternate, author, and me. These tags are defining semantic relationships between what a web page is displaying and the underlying atproto data/identity it's referencing.

at:canonical#

This property maps a web page to it's canonical AT record(s). The rule of thumb is if this AT record was deleted this web page would also cease to exist. The content attribute should be a valid AT URI that references a record.

<meta name="at:canonical" content="at://did:plc:abc123/site.standard.document/rkey" />

at:alternate#

This property defined the auxiliary AT record(s) that are being referenced on web page. The rule of thumb is that if these At record was deleted, the web page would still exist. The content attribute should be a valid AT URI that references a record.

For example, the publication of a standard.site document would be an auxiliary AT record if the canonical AT record is a a standard.site document:

<meta name="at:canonical" content="at://did:plc:xyz789/site.standard.document/rkey" />
<meta name="at:alternate" content="at://did:plc:abc123/site.standard.publication/rkey" />

Another example would be a self-hosted standard.site blog that is embedding a bluesky thread as a comment section:

<meta name="at:canonical" content="at://did:plc:xyz789/site.standard.document/rkey" />
<meta name="at:alternate" content="at://did:plc:abc123/site.standard.publication/rkey" />
<meta name="at:alternate" content="at://did:plc:abc123/app.bsky.feed.post/rkey" />

at:author#

This property defines the account(s) who authored the web page. The content attribute should be a valid AT URI that is DID. This property could be redundant if the at:canonical property is also included on the page, but there could be cases where the author(s) aren't just the DID in the canonical record.

<meta name="at:author" content="at://did:plc:author" />

at:me#

This property defines the account(s) associated with the overall website or section of website. This is mainly helpful for . The content attribute should be a valid AT URI that is a DID.

<meta name="at:me" content="at://did:plc:my-did" />

Custom Properties#

Custom at properties can be defined by using a namespace. A custom property thats the following structure at:{namespace}:{property}. Any at property that is not a standard property and not namespaced should be ignored. While it's possible to use a NSID as a namespace, we recommend against this since this information is already in the AT URI. Instead we recommend that property names contextualize the semantic relationship being described.

<meta name="at:canonical" content="at://did:plc:xyz789/site.standard.document/rkey" />
<meta name="at:standard.site:alternate" content="at://did:plc:abc123/site.standard.publication/rkey" />
<meta name="at:blog:comments" content="at://did:plc:abc123/site.standard.publication/rkey" />

Custom properties used by the community#

Below are some of the custom properties the community is already using. Feel free to file an issue to start a discussion about a new custom property.

  • at:blog:comments (draft) The AT record representing a blog posts comments. Discussion here.

Array Semantics#

Each property follows array semantics, which means that there can be multiple tags with the same property name and they will be interpreted as an array of values. For example, a web page can have multiple canonical AT records or multiple authors.

Reference Implementation#

The /src folder contains a minimal reference implementation in JavaScript of the intended semantics to parse these tags and /tests contains a test suite to confirm that.

Discussion#

Participate in the community discussion that formed this proposal.

Regarding this proposal, the biggest area of contestation is whether we should be using <link> or <meta> tags. In fact this proposal formed after Rich Harris noted that standard.site's usage of AT URIs in <link> tags was causing HMTL validation to fail. Bryan Newbold elaborated that this is because AT URIs are technically not valid URIs.

The July 2026 atp IEFT meeting would suggest that the URI spec will not be updated to accommodate this problem and it's likely a potential breaking change will need to be introduced to AT URI's to make them valid. At the time this proposal was drafted it is unclear what changes will happen and how long it will take. Since these changes are going through the IEFT we can probably expect it to be in the timeframe of years.

Even though <link> is the semantic tag this proposal should be using, we decided to take a pragmatic route to avoid the mess of AT URIs and <link> and went with <meta>. When AT URIs are fixed and a migration path is decided for the ecosystem we will update the proposal to use <link>.

There are related proposals that have been discussed by the community, and this proposal does not aim to solve all of challenges being brought up. For example, there has been much discourse about app lexicons (1, 2, 3) including a working group that recently formalized a lexicon for apps. It would be easy to imagine extending this lexicon to include metadata for an apps URL structures, that would allow us to parse out AT URIs for a URL.

While this work is valuable, especially for app stores, the cost of making every website or app in the ecosystem publish its URL structures to a PDS is too high compared to adding a couple of <meta> tags to a web page. This reduces to cost to participate and lets apps evolve at their own pace, while enable the same kinds of interop.

One challenge that this proposal doesn't address is how to translate AT URI's back to a given app's URL structure. That will require another protocol + community practice. For example, maybe apps should accept a AT URI query param on the base route that redirects to the corresponding route in the app.