So my idea was to have different versions of a post where you keep the interaction (likes, reposts, quoted posts, saves, replies) linked to a specific version.
For example you have post v1 which receives likes, comments and someone quotes the post. Then the person changes his post to v2. The post v2 won’t show the interactions from v1 and interactions on post v1 will be disabled. The post v1 can be found via the history, will have a ‘outdated’ badge (or another name) and will contain the disabled interactions (= read only). The quoted post of post v1 will still be visible and link to the post v1. People can also still interact with the quoted posts of post v1. You just won’t be able to create a new quoted post of post v1. The ‘outdated’ badge will also be visible in the quoted post on the display card of the post v1 and if people click on the ‘outdated’ badge they will be redirected to the latest version of the post.
For moderation I would expect that it works the same way. So a report will be linked to a specific version of a post/comment.
The only problem is that I don’t have enough experience with the technical design for this and how feasible this idea is.
I think it would be easy to over design this and it’s probably not needed.
Likes should be a real strong ref, if you edit your post you’re back to zero.
Reposts and quote posts can keep using strong ref but just follow the aturi component to the latest version. If the cid doesn’t match, I’d suggest rendering something similar to hidden tagged content “This Post Has Been Edited” and an expander needed to show the post.
Replies are more interesting. It would probably make sense to label a reply as “reply to a previous version of this post”. You could mix replies for different versions.
I think there’s a valid case for bluesky to cache the old post (by cid) and show it. The caching logic could use things like edit distance and dependents (replies, reposts, etc) to determine if it should keep an old version.
Edit history as a misinformation containment mechanism
Approaching this from a different angle than interaction attribution: visible edit history is a containment mechanism against propagating misinformation.
Today on Bluesky, a post with a factual error gets quoted and replied to. The author can only delete-and-repost, which severs the chain and leaves the error visible to everyone downstream. The correction never reaches them.
The v1-frozen / v2-current model with an outdated badge solves this — v1 stays viewable as a read-only historical record, and the badge on v1 (including inline on quote-post embeds) points readers to v2. Downstream readers see “this was revised, see latest” without clicking through, and fact-checkers can still verify what was originally said. That is the genuinely useful property of edits in a propagation-heavy network, more than author convenience.
One lexicon-level requirement follows: the pointer from v1 to a newer version must be discoverable from the v1 record alone, so every AppView surfaces the “revised” signal consistently. Interaction handling can vary by AppView policy, but the “this post has a successor” link should be binding network-wide.
I know a lot of lexicons and products that aren’t going to implement this and just edit in place. I’m generally against absolutes like this - that’s reserved for protocol requirements, everything else is product choices.
I think we’re giving a lot of importance to bsky posts because of scale?
And of course - we can edit today, it’s just bsky AppView rules that decide what happens.
Fair point — “binding network-wide” was too strong. You’re right that in atproto, lexicon-level bindingness is reserved for the protocol itself, and everything above that is product choice. Different AppViews editing in place is a legitimate design space.
Let me restate. The Japanese atproto community already has @holybea.blue running tomarigi (https://tomarigi.app/), an independent AppView with its own editing model, launched late April. So “AppViews will diverge on edit semantics” isn’t hypothetical — it’s already the case. That divergence is fine and probably healthy.
What I’d revise my proposal to: not a binding requirement, but a shared, opt-in convention for how a post record points to its successor. AppViews that want to surface “this post was revised, see latest” to their users can implement it; those that prefer in-place editing don’t have to. Bluesky publishing the convention as part of its edit release would let interested AppViews coordinate, without forcing anyone.
The misinformation-containment property only works among AppViews that opt in, which is fine. The point isn’t universal enforcement — it’s that the convention exists and is documented, so AppViews aren’t each inventing their own incompatible scheme. That’s a product choice on Bluesky’s part with downstream coordination value, not a protocol mandate.
And yes — agreed that bsky-scale shouldn’t drive lexicon decisions. This framing keeps the proposal scoped to Bluesky’s own release while leaving room for the wider atmosphere to do its own thing.
And deck blue and Skeets and another sky one (which actually puts an edit note in the body of the post) I forget the name of are all clients (not appviews) that do edits.
I think this is very interesting and you should do a long form post about it. I bet Alex & the team haven’t looked at in depth.
As per usual, the language barrier(s) are keeping us from learning about other approaches.
Of course, because of market power, the Bluesky AppView will have large impact - so it would be nice if we got more info about the technical approach sooner rather than later.
I’m going to put a bit of a UX-rundown here as a suggestion, also for other apps.
This post largely doesn’t apply to singleton records like profiles/self, as users already expect most of those to be mutable (though they should usually be referred to via non-strongRef anyway).
I’ll be using “really should” with footnotes for parts that are important to me.
Assumed goals are:
The “newest” version should be consistent in regards to backfilling,
the feature should be low-friction but abuse-resistant and
Edit history may be kept by apps, even if a record is deleted, but really should[2] be made inaccessible while the record is gone entirely (outside of moderation tools).
Versions may be considered identical if they only differ in non-content like interaction settings or "lang”, but really should[3] be considered distinct if anything more substantial like the "createdAt” changes.
Apps really should[1:1] consider recreated records fully identical if the CID matches.
Apps should treat new records with previously-used rkey as they would a record update, if they still have a version history for that rkey.
Apps should[4] expunge history of records that continuously haven’t existed for long enough.
Any “interaction-first” element in an app’s UI really should[3:1], if the interaction was with a non-current version, not show the mismatched current version of the interaction target. This includes reposts in feeds and e.g. likes on profiles, but also notifications.
If the version that was the interaction target, or one that is considered identical, is available, apps may choose to display that version instead. In this case, they really should with written-out notice indicate that the version is not current and prevent their own[1:2] users from creating new interactions targeting that version.
If the targeted version is not available, an app may indicate such via a tombstone element in the UI (e.g. in quotes, potentially distinct from a removal notice) or hide the interaction entirely (e.g. reposts and likes).
Any interactions shown alongside an edited item really should[3:2]:
Iff their target is considered identical, be shown as normal.
Iff their target is distinct but available, be shown with a clear written-out indication linking to that version. (E.g. “replied to [an older version]” for replies in threads.)
Iff their target version is unavailable, be shown with written-out notice that e.g. “replied to a deleted version”, unless the lexicon is documented to allow rkey reuse for unrelated records.
Apps may hide versions that aren’t known to have been interacted with.
Apps may show an edited-indicator where other versions are available, but it should be unobtrusive.
Apps really should[3:3]not include interactions with sufficiently distinct versions in counters that imply endorsement, like Like and Repost counts.
Neutral counters, like the reply count, may include interactions with distinct versions.
Apps may choose to show other-versions sums separately, for example as (+1234) after the current-version’s counter. This would help reduce the perceived friction of editing (since interactions wouldn’t be “lost”) and could clarify rankings in Feeds that don’t check for CID-match.
In compact interaction lists, like the /liked-by and /reposted-by pages on Bluesky, apps really should[3:4] group items by targeted version and really should[5] highlight the respective group header when navigating there from a specific version.
Apps may merge groups for versions they consider identical, even if they are not adjacent.
Apps may merge groups for unknown versions, but really should[5:1] indicate that the group may be aggregate, for example by labelling it “of unknown versions” or “of other versions” in plural.
Apps may discard attached blobs before discarding the associated record version.
That really should[5:2] be clearly indicated, for example as “deleted image”.
Apps should allow all user-controlled parts of the record to be edited, for example it should be possible to attach or detach media on posts. Apps really should[1:3]not ignore such edits when received through the firehose.
Apps should allow edits at any point in time. Apps really should[1:4]not ignore edits that are made “late” when received through the firehose.
Apps really should[5:3] include a "modifiedAt" field in the record where the lexicon includes it. In such cases, "createdAt" really should[5:4] be preserved unchanged.
However, apps really should[3:5]not trust these fields unconditionally and should indicate inconsistencies compared to when they first received the record or edit.
Whenever a record is edited to be distinct, apps really should[3:6] notify everyone who published an interaction with it or was notified of it previously.
Apps should collapse these notifications for the same record when they are adjacent, and (expandably[3:7]) for distinct record locations when adjacent edit notifications from the same account would clutter the notifications list.
Writing out certain abuse-resistance indications is important in my opinion, since screenshots happen. Having to manipulate a screenshot is likely considered much higher-risk for public perception for would-be platform manipulators.
It’s pretty likely that I missed something since this is mostly theoretical, so I’d love to read feedback/criticism on this design.
This is really an extension of the consistency goal. ↩︎↩︎↩︎↩︎↩︎
To avoid a regression on data control vs. the status quo. ↩︎
I’m very glad that they’re doing this and I believe handling edits is important for a thriving atproto ecosystem. I know there are many concerns expressed on bsky at this announcement, but those are all manageable by the AppViews. Many social media networks have not just survived supporting edits, but thrived with them. While I can respect the differences of opinion as to whether the post’s core content should be editable (that feels like a great opportunity for AppView differentiation), many other parts of lexicons may need to be edited, so the sooner we cross that bridge the better. As others have pointed out, edits are out there already, but as with all things ATProto, bsky adopting some version of this will help push it further.
I’m most interested to see how 2 parts unfold as this expands:
Where the line gets drawn between minor and major edits
How can users express that an edit means the original is no longer allowed to be used
On the first there’s obviously many technical and design choices (tamme.schichler.de already outlining some) that can be made when handling edits. Unless an AppView takes an absolute stance (one way or the other), it will eventually need to draw a line where they consider it an “in-place” edit or not (however they choose to handle that in their UX). Bsky’s proposed line seems a bit strict to me, but the best way to find the right balance is with experimentation, which is what this community is great for. I expect explicit rules will resonate with users early on, but I’m sure we’ll see some AppViews experiment with AI determination before too long, which could add some more nuance.
In the end we’re bound to end up with an edit showing differently between two separate AppViews. I’m sure for many users and many edits displaying the history is fine; my spelling mistakes may be a little embarrassing, but that’s part of being human. On the other side there will be edits where users will not want the originals to be shown any longer, even if that means some AppViews won’t show connected likes or comments. While there’s no technical solution that will guarantee this (public is public), it feels important that users are able to express this “control” towards well behaved AppViews. Perhaps that will end up as part of the manner of the edit convention (is that part of what you were suggesting moja.blue?) or perhaps it will be part of a wider mechanism.
To me it would make sense to codify “delete-then-recreate” as “hard” edit intent that cuts off history at cost of prior endorsement-like interactions, at least for non-singleton records. That’s mostly semantically compatible with Bluesky’s current glitchy bahaviour and allows AppViews to delete data eagerly when they see a record deletion.
It could be nice to have this atomically on-protocol as fourth record transition kind, but if that ever happens, it should in my eyes just require delete+create permissions for the collection and be semantically the same as the two-step.
I think it’s necessary for content parity of younger AppViews to allow backfills of the entire current “soft” edit chain for non-singleton records[1] though, which will likely need a protocol extension in the PDS API somewhere. (How do current AppViews behave when a PDS lists conflicting records during a backfill? cc @trezy.codes)
I also think that users should be able to reconnect with prior interactions when they create or update records to match their cid, for recovery, but apps probably need to use some discretion there to prevent abuse. Maybe they’d want to retain when they first saw an rkey/cid-pair pair somewhat longer than the content itself for that purpose.
Very fortunately, the lexicons already make them distinguishable iirc. ↩︎