FEP-d1cb: Same-origin actor re-identification

I ran into this while working on the ActivityPub plugin for WordPress, and I could not find a FEP that covers it.

Our actor ids used to be built from the permalink, so https://example.com/@handle. The id contains the handle, which means changing the handle changes the id. We are moving to an id that does not contain the handle. The origin stays the same, the WebFinger address stays the same, only the id changes.

FEP-7628 gives me the right signal for this, the Move activity. But the receiving behaviour it describes does not fit. It says a follower should Undo the follow of the old actor and Follow the new one. In my case both ids are the same actor, on the same origin, with the same inbox and the same followers collection. Unfollowing and following again is noise at best, and for an actor that approves followers manually the new follow arrives as a request, so people can quietly lose their followers.

The other migration FEPs do not cover it either, as far as I can see:

  • FEP-e965 describes the actor object after a migration between personas.
  • FEP-1580 does allow the target instance to be the same as the source, but only for migrating objects after an actor move, and it requires FEP-8b32 proofs. Here the origin never changes, so the authority over both ids never changes, and there is nothing to prove.
  • FEP-a427 is a domain move, and its mapping rules (replace the origin, replace a path prefix) cannot express a change from /@handle to ?author=5.

So I would like to write a small FEP that relies on FEP-7628 and describes only this one case. Roughly:

  • the old id has to stay dereferenceable and carries movedTo
  • the new actor lists the old id in alsoKnownAs, so the Move can be verified
  • WebFinger for the unchanged address returns the new id
  • a receiver updates the id it has stored and keeps the follow, instead of undoing and following again
  • no object integrity proof is needed, because the origin did not change

What I am not sure about is whether this should be its own FEP or a section in FEP-e965. There are a few migration FEPs already and I do not want to add one just to add one. But the receiver behaviour here contradicts FEP-7628 rather than clarifying it, and that felt wrong to put inside somebody else’s document. Happy to be told otherwise.

It also does not solve the object side. Object ids change on the same origin too, for example when the permalink structure changes, and I have no answer for that yet.

1 Like

This is indeed a limitation of the “unfollow and refollow” behavior, but that behavior is only necessary because changing the id fundamentally changes the identity. That’s what id is.

I think I’d suggest that “unfollow and refollow” could possibly be handled differently, such as with a rewrite of any URIs, but that would be conceptually different than a Move activity, and also practically different from how actor migrations have ended up in the fediverse.

WebFinger subject staying the same is a red herring; it should not have any bearing on the situation unless the identifier was based on WebFinger.

1 Like

FEP-7628 gives me the right signal for this, the Move activity. But the receiving behaviour it describes does not fit. It says a follower should Undo the follow of the old actor and Follow the new one.

It's MAY, not SHOULD:

Upon receiving valid Move activity, all actors following object MAY un-follow it by sending Undo(Follow) activity and MUST either send Follow activity to the target or otherwise notify the user that their contact has moved.

It seems to me that your use case fits FEP-7628.

I was going to finalize this FEP, but can postpone that if you'd like to add something to it

@silverpill @trwnh thanks for your feedback!

The biggest concerns I have with extending FEP-7628 are:

  1. The title does not accurately reflect the use case. This is not primarily about “Moving Actors”; it is more about changing IDs. Even if these are technically the same operation, developers may not find the FEP based on its current title.
  2. The visibility: Modifying an existing FEP may not provide sufficient visibility. Developers who have already implemented the FEP may not revisit it to check whether there have been changes. As a result, important updates could easily go unnoticed.
1 Like

@trwnh I generally like the idea of the rewrite, but I think it could break for sites that use the ID both as the ID and the URL and can’t forward all traffic to the new ID. That’s why I like the general idea of a Move!

I definitely get the use case here! Especially for projects that started out with usernames in AP ids, and then later realized that they should use other ids instead so that users can change their usernames.

Having said that, I’m with @trwnh and @silverpill here: id really does define identity in AP, full stop. If we try to design a way to let the same actor (or object) have multiple ids, even as part of a transition, we’re going to have a bad time.

Also, one minor nit:

You probably still do need/want object integrity proofs, at least if you were providing them before. Server-custodial keys are common in AP, but not required. There are AP implementations where the server isn’t quite as trusted, and users retain their private keys and sign their own activities/objects.

1 Like

Thanks for all the feedback!

I created a first draft, I tried to address all mentioned points: Annotate file.

FEP-7628 requires receivers of a valid Move to either send a Follow to target or to notify the user. This document requires receivers to do neither when object and target have the same origin.

Same origin isn’t enough to classify a “reidentification” because someone can Move to a different account on the same origin. Those are still 2 separate accounts and they were never the same:

{
  "type": "Move",
  "object": "https://mastodon.social/users/trwnh",
  "target": "https://mastodon.social/users/a_trwnh"
}

In this case you can say that trwnh and a_trwnh exist simultaneously as separate users and I am simply migrating from one to the other (and may not accept the same followers on the new account as I did on the old account). What your FEP draft is describing is something different than that – more akin to an HTTP redirect (3xx) than moving to a different account. Instead of having two accounts, you have two identifiers for the same account (with one becoming the new “canonical” identifier, and one serving as what you call the “alias”).

It sounds like you are trying to have the detection criteria be when you fetch an actor and its id is different from what you fetched, which could be fine. I would add an example of an HTTP request to make this more explicit. Something like:

-> GET /users/foo

<- 308 Permanent Redirect
Location: /users/bar

-> GET /users/bar

<- 200 OK
Content-Type: application/activity+json

{
  "id": "/users/bar",
  "alsoKnownAs": "/users/foo"
}

But this isn’t actually limited to same origin! You can just do this regardless of origin, because the same-origin check doesn’t get you anything here.

So I think the strongest argument you can make here is that FEP-7628 should be handled differently, with the algorithm you describe as an optional optimization to avoid having to resend Follow activities. Although… I think that you might be better off sending them anyway, since the rewrite is quite expensive once you consider past data, especially if that past data was signed. For example:

{
  "id": "/follows/1",
  "type": "Follow",
  "object": "/users/foo",
  "proof": ...
}

Rewriting this to be a Follow of /users/bar is going to invalidate the proof. Software that stores activities will have to 1) detect that this activity is now stale, 2) re-obtain the latest information (perhaps via HTTP GET). The bigger problem is 1, because there isn’t a good way to search for all activities that use a given identifier anywhere. Really, you end up having to do this sort of detection when handling any activity, including prior activities. So it’s sort of a cache-buster in that regard.

I’d be inclined to think this was a better idea if there was an explicit network-wide notion of actors having a “base id” and then you could update that “base id” while using relative links everywhere else. But that’s not the world we currently live in, so…

Keep its relationships with the actor: follows in both directions, pending follow requests, blocks, mutes, list memberships and similar state

^ this is the bit that is problematic, basically. It only works if you have an internal “database id” that you treat as more canonical than the actual canonical https: id. If you wanted to model it with https: then you really should be assigning local identifiers to remote users:

-> GET /users/14715
Host: mastodon.social

<- 200 OK
Content-Type: application/activity+json

{
  "id": "https://trwnh.com/@a",
  "alsoKnownAs": "https://mastodon.social/users/14715"
}

It’s a really gnarly can of worms, but doing this also generalizes better than you’d think… as long as everyone else understands it, which right now they won’t. :frowning: But what we just came up with here in this example is basically just a user record, made explicit.

(If you get into the data modeling there, then maybe as:alsoKnownAs isn’t the best fit here, but that’s really firmly out of scope of this conversation for now…)

1 Like

I agree - URL rewrite is only feasible if you store data in a certain way. If there is a "users" table with an "actor_id" field, then updating this field is not difficult... But ActivityPub implementations are quite diverse, some store normalized activities as JSON, some even store raw activities.

There are advanced features that are simply not possible to implement without storing raw activities.

@trwnh you are right, semantically a Move is the wrong thing here. The actor does not move anywhere, only its id changes, and a rewrite describes that much better. I tried to reflect that in a new draft: Annotate file.

There is no Move anymore. The publisher sends a normal Update, and a receiver detects the change when the alias resolves to a different id. Your trwnh / a_trwnh case is not affected by that, and nothing conflicts with FEP-7628 anymore.

The same-origin rule is only a scope limit, not part of the verification. You are right that the check itself works across origins, I just did not want to touch migrations.

Stored data does not need a rewrite. The alias keeps resolving and is listed in alsoKnownAs, so a stored Follow of the alias is still a follow of the actor. Only the actor record has to learn the new id. I also added HTTP examples for both variants, redirect and serving the document at the alias.