For a lot of things in ActivityPub, there are almost direct parallels in NodeBB. An as:Note object pairs well with a NodeBB post, an as:Person is a NodeBB user, etc.
One thing that didn't map 1:1 was the Delete activity, which at surface level, seems rather straightforward — just delete the object! However, once you dig in, there are some additional considerations:
in NodeBB, we have two separate states for content removal.
A delete, where the post is still present (but its content unavailable to non-privileged users), and a
A purge, where the post is scrubbed from the database entirely, and all references to it, removed
in ActivityPub, there is a single activity, as:Delete
Implementors may opt to replace the object representation with an as:Tombstone (how quaint!), but they may also just opt to use a 404
So there are some nuances that are left intentionally vague.
This is how NodeBB will interpret the protocol specification, and how we will align it with our own dual-state post deletion mechanic (delete & purge):
When a local post is deleted, we will federate out an Update(Tombstone) referencing the id
Afterwards, if the content is retrieved, an as:Tombstone will be served.
Deleted posts in NodeBB still maintain their place in the topic, so when the context is retrieved, the note will still be present in the collection.
If we receive an Update(Tombstone), we will delete the local representation of the post
When a local post is purged, we will federate out a Delete(Note)
Afterwards, if the content is retrieved, we will serve a 404
The note will no longer exist in the context collection
If we receive a Delete(Note) (or Article, or Question, etc.) we will not delete it immediately. Instead, as kaniini advises, we will attempt to retrieve the object from the origin:
If we see an as:Tombstone, we will delete the post (soft delete)
If we encounter a 404 or 410, we will purge the post (hard delete)
I'm writing this out less as a guideline for myself, but to solicit opinions and to give others a chance to point out if I've interpreted the spec incorrectly.
@julian I don't know how it works at ActivityPub level, but would it make sense to represent a soft delete as an update to the visibility of the object? Like as a Mastodon user, if I changed a post to private?
@blaue_Fledermaus@mstdn.io — interesting idea, but my gut feeling is no, because post visibility (which at present, NodeBB doesn't even support at all) and deletion are two separate properties in ActivityPub.
One is defined in the object itself (to, cc, etc.), whereas if a post is deleted, it simply ceases to exist or becomes a Tombstone.
Just my thought, but the whole Delete then Purge has always irritated me.
Delete should just be Delete.
If a Mod wants to temporarily hide something they could move post, or delete and keep a copy.
The only thing Delete then Purge does is add extra step to removing something!
@julian I don't know ActivityPub well enough to have a detailed set of comments but this seems sensible. In particular, using Tombstone would seem to enable good handling of cases like a deleted post with replies to it.
@julian I’m focusing on Deletion (both Notes and Actors) in Discourse at the moment and I’m thinking of essentially adopting the approach you’ve outlined here. Have there been any updates to your approach since you wrote this?
No, there have been no major concerns arising out of this.
A couple observations:
There is a mismatch between what other instances expect for deletions and what actually happens. A Mastodon user deleting post expects it to be gone for good, but on our end it will simply be soft deleted. This isn't a major issue, it just means admins get to see all the deleted stuff (useful when people say hurtful things, delete them, and pretend they never happened.) Even then it's only a small percentage, most of the deleted stuff I see are for fixing typos.
I am reasonably sure that nobody else besides NodeBB (and now Discourse) knows what to do with an Update(Tombstone), so nothing happens. It means soft deleted posts on our end are essentially ignored and still visible.
The latter may actually be a concern and warrant an admin-side option to explicitly federate out a Delete.
Yeah, I’m currently weighing this one. I’m wondering whether Update(Tombstone) really makes sense. I can see why you took that approach. I’m just mulling the implications, particularly
I’m currently thinking users may find this surprising and it may interfere with things like moderation. But I’m still mulling it.
tbh the more I think about this, the more it feels like federating Update(Tombstone) is never going to work very well.
We’d first have to win the argument that federating Tombstones make sense at all. I found the arguments to the contrary relatively persuasive on that front (I know you’re aware of this, but just giving context for other folks reading this):
I wonder if we can’t just do point 6 on your list:
If we receive a Delete(Note) (or Article, or Question, etc.) we will not delete it immediately. Instead, as kaniini advises, we will attempt to retrieve the object from the origin:
If we see an as:Tombstone, we will delete the post (soft delete)
If we encounter a 404 or 410, we will purge the post (hard delete)
Agreed. That makes sense since receiving a Delete should trigger a cache invalidation, so there's no reason it need be a different activity. I will make the appropriate changes soon.
I recognize that this makes the Delete activity work differently for S2S than it does C2S, which is to say that a user or client cannot distinguish between “soft delete” and “hard delete” this way. Otherwise, the “cache invalidation” idea is a sensible one.