Skip to content

Allow actor dispatcher to return Tombstone (HTTP 410 Gone) #644

@dahlia

Description

@dahlia

Problem

The actor dispatcher currently returns Actor | null, which makes it impossible to represent a deleted or suspended account properly. When null is returned, Fedify passes the request to the next middleware without responding—so there is no way to return an HTTP 410 Gone with a Tombstone object, which is what ActivityPub §6.2 expects for deleted actors.

Applications that need to handle this case today are forced to work around Fedify by registering a separate route that catches requests Fedify declines and manually returns the 410 response. This is workable, but it means the application has to duplicate logic and manage two separate code paths for what should be a single concern.

The same problem applies to WebFinger: when a remote server looks up a deleted account, it should receive 410 Gone there too, not a 200 with a stale resource descriptor or a 404.

Proposed solution

Extend the actor dispatcher return type from Actor | null to Actor | Tombstone | null. When the dispatcher returns a Tombstone, Fedify should:

  • respond to the actor endpoint with HTTP 410 Gone and the serialized Tombstone as the body
  • respond to the corresponding WebFinger lookup with HTTP 410 Gone

Returning null continues to mean “not handled” (pass through to the next middleware), so there is no change to existing behavior.

API design

The only change is to the dispatcher callback signature:

federation.setActorDispatcher("/users/{identifier}", async (ctx, identifier) => {
  const account = await db.getAccount(identifier);
  if (account == null) return null;
  if (account.deletedAt != null) {
    return new Tombstone({
      id: ctx.getActorUri(identifier),
      deleted: account.deletedAt,
    });
  }
  return new Person({
    id: ctx.getActorUri(identifier),
    // ...
  });
});

No new methods or options are needed. The Tombstone type is already part of @fedify/vocab.

Response format

When the dispatcher returns a Tombstone, the actor endpoint should respond with:

HTTP/1.1 410 Gone
Content-Type: application/activity+json
{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "https://example.com/users/alice",
  "type": "Tombstone",
  "deleted": "2024-01-15T00:00:00Z"
}

The deleted field is not required by the spec, but it is widely expected by implementations like Mastodon for cache invalidation.

For WebFinger, a 410 Gone with an empty body (or no body) is appropriate.

Considerations

The change is purely additive. Existing dispatcher implementations that return Actor | null are unaffected, and Tombstone in the return type is opt-in.

Fedify already logs a warning when the returned actor's id does not match the expected actor URI, without blocking the response. The same behavior should apply to Tombstone: warn if the id is mismatched, but still respond with 410 Gone.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions