If there isn’t already a plan I’d like to propose a ‘deeplink’ lexicon. It’d declare supported other URI schemes equivalent to the indicated NSID / AT URI.
Please let me know what you think, any prior art, and if you’d find something like this useful!
Quick examples
For example, Bluesky might publish at://atproto-lexicons.bsky.social/com.atproto.lexicon.deeplink/app.bsky.feed.post:
{
"$type": "com.atproto.lexicon.deeplink",
"uris": [
"https://bsky.app/profile/{{did}}/post/{{rkey}}"
]
}
Which would declare that the above bsky.app URI — with {{did}} and {{rkey}} subbed in — is a great way to view AT URIs of the app.bsky.feed.post NSID.
Because it’s stored in the same repo where the app.bsky.feed.post NSID is defined (atproto-lexicons.bsky.social), this would also be considered the “canonical” https URI.
However a similar record may also exist at at://blackskyweb.xyz/com.atproto.lexicon.schema/app.bsky.feed.post, which would be the stable pointer to how blackskyweb.xyz recommends you view these NSIDs.
{
"$type": "com.atproto.lexicon.deeplink",
"uris": [
"https://blacksky.community/profile/{{did}}/post/{{rkey}}"
]
}
(I’m using an array here, with “use the first that matches your needs” semantics, as one repo may want to support multiple URLs, or URI schemes. Eg. a new site design, or… well I’m sure someone will build a gemini:// view on atproto data at some point; and it might even be me
)
Where it’s useful
This would be useful for AppViews supporting multiple external lexicons, and wanting to remain up-to-date as to how to link to them.
With the addition of another schema, an account could indicate a stable preference for where they like to view given NSIDs. eg. at://byjp.me/com.atproto.lexicon.deeplinkPreference/app.bsky.feed.post might look like this, to indicate that I want Atmospheric apps to open Bluesky posts in Bluepy:
{
"$type": "com.atproto.lexicon.deeplinkPreference",
"deeplinks": ["at://bluepy.social/com.atproto.lexicon.deeplink/app.bsky.feed.post"]
}
This approach also allows for site owners to change their URL scheme without affecting people’s preferences (as they own their deeplink record, they can just update).
Open questions
Having uris as an array is smart, but it has challenges.
- It supports multiple schemes while maintaining the fast lookup of using the
nsidas the record key - It allows for multiple equivalent URIs with an implicit preference, for example if the owner is switching to a new site design (with a “beta” URL)
- …but now it’s hard for
deeplinkPreferenceto indicate preference within a given adeeplink’s list (eg. I like Bluepy’s web interface but not their hypothetical Gemini interface and I prefer Example’s Gemini interface but not their HTTPS interface — this conflict is not easily resolvable with the schemas I’m proposing)- A picky user like this can create their own
deeplinkrecord and point to it as their preference, though they’d have to manually update their own record if Bluepy/Example’s URLs change.
- A picky user like this can create their own
Architecting these records on a per-NSID basis makes lookup easy, but does mean that for more complication NSID spaces (eg. Bluesky) you’d have to create/maintain bundles of deeplink & deeplinkPreference records in tandem (eg. one for posts, one for reposts, likes, lists etc.)
- Even complicated apps like Bluesky don’t have that many useful-to-link-to NSIDs
- You’d probably be using an app to manage your preferences anyway?
Would we want to make the distinction between {{did}} and {{handle}} in the templating language? I’ve opted for only did here (as it’s stable by definition) — but there are sites (eg. keytrace.dev) which don’t support dids in their URLs.
- It seems reasonable to ask a site that wants to offer support for deeplinks to support dids in their URLs
- It simplifies the logic for the apps creating links too; they only need to have the did ready when rendering a deeplink preference — rather than preparing a handle which they may not have ready, or need.
There is a security risk here; if example.com’s atproto repo is taken over, and lots of people depend on its deeplink record, then a devious bad actor could update the deeplink to point to an identical looking site with a similar URL and harvest credentials without people noticing.
- People already have to trust that the sites they use have good security & don’t leak sensitive info — this feels like an extension of that.
Lexicon sketches
com.atproto.lexicon.deeplink sketch
{
"id": "com.atproto.lexicon.deeplink",
"defs": {
"main": {
"key": "nsid",
"type": "record",
"record": {
"type": "object",
"required": ["uris"],
"properties": {
"uris": {
"type": "array",
"min": 1,
"items": {
"type": "string",
"format": "uri"
},
"description": "Template URIs which are equivalent to at-uris with this NSID. {{did}} and {{rkey}} will be replaced with their values, eg. https://example.com/profiles/{{did}}/things/{{rkey}}"
}
}
},
"description": "A declaration of URIs that support viewing at-uris with this NSID. If the repo hosting this record is the same as the one hosting the NSID's lexicon schema, the first of the listed URIs that is suitable will be considered the canonical alternate URI."
}
},
"$type": "com.atproto.lexicon.schema",
"lexicon": 1
}
com.atproto.lexicon.deeplinkPreference sketch
{
"id": "com.atproto.lexicon.deeplinkPreference",
"defs": {
"main": {
"key": "nsid",
"type": "record",
"record": {
"type": "object",
"required": ["deeplink"],
"properties": {
"deeplinks": {
"type": "array",
"items": {
"type": "string",
"format": "at-uri"
"description": "The com.atproto.lexicon.deeplink record for the preferred deeplink(s)."
},
"description": "References to deeplink definitions for this NSID, in order of preference."
}
}
},
"description": "A declared preference for the URIs this account likes to use to view the stated NSIDs."
}
},
"$type": "com.atproto.lexicon.schema",
"lexicon": 1
}