Standardizing Bookmarks in the Atmosphere

Shared purpose, different styles ( source )

Since the beginning of Semble, we've been thinking about standardization and sharing lexicons. Some of the main actions in the app are common across many apps: saving and organizing links. However, I've hesitated to move forward on the standardization effort for two main reasons.

Firstly, that kind of coordination work is hard, it requires consistent effort and someone needs to be accountable for moving the needle. Personally, I'm much more interested in focusing my efforts on Semble as a product and continuing to improve the experience for our users (I'm not particularly keen to be the main driver of bookmark standardization). Let's be real, standard.site probably wouldn't have happened the way it did if it weren't for the leadership of @aka.dad (special props for driving the website and branding!), @brookie.blog & @awarm.space . Who's going to lead the way for bookmarks?

Secondly, despite bookmarking seeming like an easy thing to standardize, it's actually not so obvious. What a bookmark is (nice set of questions by @donohoe.dev ) depends on context, and context is often in tension with the generalizations needed for standardization.

With that said, the success of @standard.site has been inspiring. Additionally, Bluesky's willingness to directly support community initiatives like that (both in adopting it for their own docs as well as the link embeds in posts) means there is good reason to work towards shared lexicons.

The minimal bookmark lexicon

Standard.site did something clever and very pragmatic: they define a content property on the site.standard.document leaving the actual shape of the documents content up to the app that implements it. This is great because each long-form writing app will have different needs based on the kind of writing experience and feature they are going for.

There are only three properties that must be included for the record to be a valid site.standard.document record:

Why not do something similar for bookmarks: define the minimal set of required properties, and leave additional details up to specific apps? Some apps may have overlapping metadata needs, and they can agree on shared lexicons for properties of records, but only if desired.

Let's explore what those lexicons could look like. I'm going to use typescript as lexicon pseudo-code for readability.

interface RequiredBookmarkProperties {
url: string,
createdAt: Date
}

// standard properties usually included as URL metadata (to be finalized, this is just an example)
interface OptionalBookmarkProperties {
title: string,
description: string,
image: string, // url for the thumbnail image
siteName: string,
tags: string[],
canonicalUri: string, //e.g. the url with referal params stripped OR an aturi of the canonical atproto record (if there is one)
content: BookmarkContent
}

// this is how any app can define properties specific to their needs
interface BookmarkContent {
$type: string,
[key: string]: any,
}

export type Bookmark = RequiredBookmarkProperties & Partial<<OptionalBookmarkProperties>;

What this means is that everyone using this shared lexicon agrees what a basic bookmark is: a URL.

Important considerations

Now there is one caveat here: app views would need to implement a way to display/edit these bookmark records regardless of which optional properties or custom content is included. For example, a bookmarking app that expects every bookmark to have a title and description in the record would need to update to be able to handle bookmark records that don't include those properties.

I can think of three potential ways to do this:

(1) supplement the bookmark with the metadata at the AppView layer: the app view recognizes which properties aren't provided and fetches them directly, storing those values in the AppView database. This is effectively what Semble does for margin bookmarks (e.g. including the type property of the link). Editing here becomes tricky, and perhaps it's best to not allow editing of bookmarks and handle it similarly as option (2).

(2) treat as external link and display only (no editing): if an app view requires specific properties to fully display the bookmark, the app could instead show it as an external link. This would be similar to how @leaflet.pub shows standard site document with content not defined by pub.leaflet.content. The leaflet viewer treats these as external links, rather than rendering them in their own document viewer. Additionally, leaflet doesn't allow users to edit those documents within leaflet, the author would have to go to a different app that supports editing functionality for that specific content type.

(3) Ignore bookmarks from other AppViews: an app could render only bookmarks with certain properties and not render any others. IMO this is not ideal since it breaks the contract with the user who expects a bookmarking app to show them all of their bookmarks of the same record lexicon, regardless of the content.

The Outstanding UX Challenge

I think it's worth calling attention to the UX challenge around handling bookmarks with divergent custom properties. In standard.site readers, it's straightforward: treat them as external links. This is fine for most people, because people aren't writing long form posts from various standard.site apps, which means they won't be in a situation where they are looking at their @pckt.blog post from the leaflet reader wondering why they can't edit it directly in leaflet.

With bookmarking, it's more challenging because bookmark authoring is likely to originate in multiple apps because it's a common low-effort action. Someone might use @semble.so as their primary bookmarking app, but maybe they also use the @margin.at browser extension to bookmark links. Now they have two apps that display all their bookmarks, but some would have the content property with $type = network.cosmik.card and other with $type = at.margin.bookmark (assuming both apps adopt some version of the above proposed lexicon). Additionally, maybe this person also uses @sill.social and then their bookmarks from there end up with the content property of $type = community.lexicon.bookmarks.bookmark. Now they have three different bookmark content types to manage.

Displaying these different types isn't so difficult (see above 3 proposals), but there is a trade-off here for modifying the content of the bookmarks (e.g. editing the tags, changing the title, updating the thumbnail url, etc.): more cognitive overhead for the user or more maintenance burden for the developers. Either apps can direct the user to the appropriate app or the developers allow editing that is aware of the content type.

Of course, this problem would not exist if there was one canonical bookmark lexicon with no custom properties that everyone adopts, but I honestly don't see that happening nor do I think it should. The flexibility of custom content properties is a good thing: apps should be able to define data entities specific to their context. The protocol should not get in the way of building products tailored for specific use-cases. Interoperability is a means, not an end.

Next steps?

I hope this helps identify the key questions around standardizing bookmarks in the atmosphere. I'd love any feedback or comments.

In the meantime, which lexicon will you use to bookmark this post? 😜