Record Versioning

AT has always supported record updates: the clearest example is updating bsky profile records. And "strong refs" are a way to detect if a record has changed, by including both an AT URI and the hash (CID) of the exact version being referenced.

But these are different from proper record versioning, which would mean keeping multiple versions of a record around in the repo. This would let you do things like inspect changes or the history of a record.

The Proposal

Add another path segment in both AT URIs and repo record "keys" for version CIDs.

Eg, a full versioned AT URI would look like:

at://did:plc:44ybard66vv44zksje25o7dz/app.bsky.actor.profile/self/bafyreiarlrgo3wgrpetjottkvjepio7nt2x6yc4jtb3f56kif7r4nmm7q4

In the repo (MST), the current version of the record would still be stored at <collection>/<rkey> , pointing to the current CID. If the record is updated, the old version could be retained under a new repo key like <collection>/<rkey>/<cid>. These would be stored in the MST just like other keys. Fetching a record with a specific version (CID) which is the current primary version would "just work", even though the CID would not be part of the repo path.

To make all this work, the com.atproto.repo.* lexicons would need to be updated to work with versioned records. Eg, when updating or deleting a record, there would be a flag to indicate whether the old version should be retained, or fully removed. Maybe a default could be indicated in record lexicon declarations (eg, that records of a given type should be versioned by default, or not). Getters would need support for fetching a specific version, and listers would need the ability to list only current records, or also enumerate all versions. We'd probably also want a method to fetch all versions of a given record.

Thoughts

Regardless of whether we support record versioning in this way, I think it would be nice to support versioned AT URIs. It would remove the need for "strong refs" as a typed object. Perhaps we would want a new lexicon string format to clarify things: at-uri-versioned or something like that.

This system would still require some work from lexicon designers and app devs. The CIDs are just CIDs, with basically random ordering. If you wanted to track ordering of history or timestamps, those would need to be stored in the records themselves. They would also be user-controlled: a basic AT principle is that users have full control over their repositories, which means they could fake any ordering or timestamps in the data itself. Integrity in the network ends up being enforced by other mechanisms, such as strong refs from other accounts, or services that remember "indexed at" timestamps and flag anomalies.

It probably is worth considering alternative designs and syntax. Versions could instead be indicated by an incrementing integer, or a timestamp (TID).

This proposal would require changes to the repo specification, core XRPC methods, and client SDKs. Probably an increment of the repo version number (indicated in commits) and take some time. Are there simpler things that could be done sooner? Folks could encode versions in record keys. Eg, app.bsky.actor.profile/self.v3 or app.bsky.actor.profile/self-bafyreiarlrgo3wgrpetjottkvjepio7nt2x6yc4jtb3f56kif7r4nmm7q4. In many cases this would make the records "invalid" from a lexicon validation standpoint (because the record key would not match the declared key format), but that might actually be desirable, to prevent duplicate processing.