FEP-1580: Move Actor Objects with a `migration` Collection

Hey everyone, i see there is a FEP category, but i’m not allowed to make a new post there.

Actual draft needs some work, editing and a few open topics have already been raised, but opening this now since the shape of it is there.

let’s make full-on migration a reality, posts and all baby

Prior FEPs (FEP-7628, FEP-E965) describe an ability for an Actor to move to a new id, often hosted on a different server instance,
however they do not describe a mechanism for moving objects that belong[^belong] to that actor.

This FEP describes a mechanism of migrating objects belonging to a moved Actor to the target instance using two OrderedCollections created by the target instance:

  • a migration collection that contains a mapping from source object URIs to new URIs on the target instance, and
  • a moves collection that contains the actor Move activities that prove a migration has taken place and allows verification of object signatures in the case the source Actor is no longer available.

This FEP attempts to balance effectiveness, performance, security, and ease of implementation by allowing 3rd-party instances to gradually update their local copies of the affected Objects. “Push”-style migration where the source instance is still active and cooperative, “Pull”-style migration where the source instance is uncooperative or unavailable, and given appropriate proofs, migration from an exported collection of objects and activities are all described. The migration operation is agnostic to the type of the Objects being migrated, supporting protocol evolution to unanticipated Object types across instances with varying support for them. Collection-based object migration is orthogonal to, and compatible with content-addressed or other portable object schemes (e.g. FEP-ef61).

tl;dr: to migrate objects, create a mapping from the old to new objects on the target instance, and let 3rd-party instances gradually migrate their local representations using that map.

edit: shoutout to that moves collection design and how i’ve been totally bittorrent-brained for like 9 months and now think everything needs to have an infohash and be self-validating

1 Like

one thing that i think warrants some discussion is the handling of object updates during migration - i’m thinking of proof being one property that is almost certain to mutate when migrated, and other things like the uris for likes and reposts and etc. also should change. as written currently this would all be handled by asking the target instance for an updated version of the object after the URI is remapped, but it could also be done in the migration collection directly with an attached update activity. This would I think be the better implementation if server-to-server updates were partial updates, however server-to-server updates are always complete objects and partial updates are only spec’d for client-to-server interactions.

it is probably too much to also add an additional PartialUpdate activity to support server-to-server partial updates in this one FEP, but if we had something like that then it could look something like this:

{
  "id": "https://example.com/chuckTargetson/migration/page/0",
  "type": "OrderedCollectionPage",
  "partOf": "https://example.com/chuckTargetson/migration",
  "next": "https://example.com/chuckTargetson/migration/page/1",
  "items": [
    {
      "type": "Move",
      "actor": "https://source.example.com/barbaraSourceworth",
      "origin": "https://source.example.com/barbaraSourceworth/posts/12345",
      "target": "https://example.com/chuckTargetson/items/98765",
      "attachment": {
        "type": "PartialUpdate",
        "proof": {"...", "..."},
        "likes": "https://example.com/chuckTargetson/items/98765/likes",
        "...": "..."
      }
    },
  ]
}

Serializing the entire updated object in the migration collection would make that collection much more expensive to serialize, and part of the goal of the design with keeping it as just source->target mappings of URIs is to make that step very cheap so that the “main” part of migration could be done quickly (instances remapping objects to a new URI with a new owner) and then fully refreshing the object could be spaced out over a much longer period of time. so it’s sort of an empirical question of whether everyone hitting one bottleneck of enumerating a gigantic collection is faster or slower than everyone hitting a very cheap collection and coming back later to sip updates over a week.

The semantics of “move with update” are a little awkward but i think they’re correct (?) - activitypub specs Update as “update its copy of the object of the same id” and so explicitly forbids “moving” the object. Move is unspec’d by activitypub, and activitystreams has no detail aside from the description in the notes, which is very general, but the use here conforms to prior use from actor moves.

I think the proposed mechanism might work, although it is quite difficult to implement.

Could be used to move from HTTP(S) URIs to FEP-ef61 identifiers. That's nice.

Could be used to move from HTTP(S) URIs to FEP-ef61 identifiers. That’s nice.

Yes this was part of the (unstated) goals! I am implementing a fedi overlay that uses FEP-ef61 identifiers and I want it to be possible to move general objects (not necessarily just Notes) to it.

Any recs on simplifying implementation would be welcome - this is the simplest I could think of that did the job

1 Like

i also want to invite discussion of names, i am not totally sold on migration. current list of defined terms: Making sure you're not a bot!

  • actors : name for a collection of actors, intended to only be used within the moves collection, don’t want it to be confused with other potential collections that might be named actors. not sure if there’s a way to express this in the json-ld context, seems like there should be? @trwnh could you spare another drop of json-ld knowing?
  • migratedAt: optional timestamp for when an object was (most recently) migrated
  • migratedFrom: optional former (most recent) URI
  • migration: collection of objects to be moved
  • migrationComplete: bool to signal a migration collection will not change until the next Actor Move is emitted
  • moves: collection of Move activities with the given actor as the target. this name i am pretty sold on.

the reason i used a different word migration from moves even if both are a collection of Move activities is, maybe obviously, because they have two different meanings, purposes, and uses, but migration is not a type, i’m sure there must be other collections whose predicate is not the type name, but it’s not exactly immediately clear what the difference would be. another option for the structure of the collection was for moves to be a collection of collections, where you would have the moves/{type} collection, of which person/actor moves could be one type. this would also group both under a single key in the top-level actor object. i opted for two flat collections out of simplicity, but again inviting comment on the structure or on the choice of names (for any)

There’s a lot in this FEP, and I’ll need to read it a few more times to have confident opinions about it. But, some super-hot takes:

  1. This seems to dovetail pretty well with LOLA. It fleshes out many areas where LOLA is under-specified, and LOLA does the same in some places that this FEP leaves unidentified. So, we should definitely set up a “big ‘ole chat” to merge these somehow

  2. One this I really like about LOLA is that it enumerates the collections available for migration, including specifying a collection/namespace for “content” — all of the attachments and other data that needs to come along with a post

  3. The ActivityPub actions are very clear in this FEP, but I’m not sure what the USER interactions would be. It reads as though I might need to bounce between my source and target accounts A LOT to make this work. For this, LOLA uses an OAuth authentication to retrieve permission to start the migration, which feels really quick and easy for users could you help me understand how users would navigate this process?

  4. So much here feels very thorough and well thought through. Again, I’ve only had a short time with it (reading on my phone, no less) so I’m looking forward to digging into this in detail.

Thank you for sharing this (obviously tremendous) amount of work!

thanks for the comments, i’ve actually not heard of LOLA and can’t find it with a search, can you link me? definitely agreed it would be great to sync efforts.

  1. One this I really like about LOLA is that it enumerates the collections available for migration,

A few things here - there are two strategies for the source instance: put all the objects to fetch in one collection, or use the existing collections on the Actor (and add new collections for migration-only objects) where there is (usually) one collection per object type.

this FEP has basically two mechanisms for enumerating what should be imported - the “handshake” route where an explicit collection of objects to import are defined before the move operation (in the case the target instance wants to moderate or the source actor wants to subset what they want to move), or the “enumerate collections on the actor” route.

I opted for using the collection-per-type strategy in the latter case for a few reasons:

  • instances already serialize the actor with a bunch of linked collections, and adding a single-typed collection is likely to be easier for instances to implement than create a multi-typed collection, where paginating over a heterogeneous collection might be a bit of work
  • specific objects (those that control the visibility of other objects) need to be imported first, so having a specific collection for them makes that easy to do. otherwise the target instance needs to load a bunch of objects and hold them in some cache until it completes enumerating the full collection → sort → apply blocks/mutes/etc. → import the rest
  • objects usually have a graph of other objects around them, like their “likes” collections and attachments and whatnot. attachments are typically serialized as nested within an object in JSON-LD, but in the expanded RDF form there’s no difference between those things. in the “handshake” import route, this should all be explicitly defined, but in the “enumerate collections” route, the spec is to import objects as if they had been created by the target instance (“The target instance MUST insert the newly migrated objects in relevant collections and perform any side effects that normally accompany object creation”, and generally fetch as much as is possible) to give some implementation room so common graph expansions like grabbing collections can happen without too much special handling by the source instance and any existing import mechanisms can be used.
  • wanted to encourage the use of collections more generally, so rather than having a single-use/single-purpose collection for source objects, wanted to encourage instances to make general mechanisms for serializing collections for different object types so they can be reused

i think the source->target migration is reasonably well handled by collections within the actor object or a negotiated object uri move map, but i do think the 3rd-party instance updates could be made more efficient by having a collection of object diffs, only problem there being that server-to-server Update activities are always full objects per the ActivityStreams spec, so there wouldn’t be much of a gain, and imo being able to space out individual requests over a ~week may be less expensive than serializing whole pages at a time, but that’s an empirical question. For 3rd party instances the structure of migration being URIs only is on purpose: that should be as fast as possible so they can use locally-cached object representations and just change the URI and fill in and updates to the objects later.

I’m not sure what the USER interactions would be. It reads as though I might need to bounce between my source and target accounts A LOT to make this work.

the UI is also purposely unspec’d - this FEP describes the protocol-level mechanisms which are general enough to cover lots of different kinds of UI implementations. It should be basically the same as current move with the added optional steps of subsetting content and approving a TentativeAccept:

new steps are in bold

  • create reverse alias from target account
  • initiate move from source account
  • optionally subset my posts (this i was imagining as something that had a handful of ‘bulk’ checkboxes for different object types, visibility settings, etc. and then for those detail-oriented ones among us, a per-item checklist, but the generality of the design is to allow different software to make different choices, some might implement some very detailed interface, others might only have dev resources to implement an option or two, etc.)
  • (wait for other instance response)
  • if other instance TentativeAccepted, review any changes they request approve

and then the move is performed or not, depending on whether i accepted.

LOLA uses an OAuth authentication to retrieve permission to start the migration

this spec is agnostic to how the reverse actor link is established, it could be through OAUTH, it could be through some action on the target instance, the thing that matters at an activityPub level is that there is a FEP-7628actor link”, most commonly an alsoKnownAs in the target actor.

So much here feels very thorough and well thought through

Most of this just falls out of the ActivityPub/Streams spec! thank you, and it certainly still needs work, but i’ve thought about this a bit (for the microblogging case, but also for bulk data, which is part of what i’m spec’ing this for)

Lots here. Still reading. For now, here’s the link I should have included: LOLA Portability for ActivityPub (0.2)

this one seems to be accidentally marked @type: @id in the linked context document

i’m not sure i understand the question here – what is the potential confusion? part of the point of json-ld context declarations is to map ambiguous shorthand terms to unambiguous full identifiers. so what you refer to as actors within the context of this FEP is actually https://w3id.org/fep/1580#actors as you have currently defined it.

if you are talking about the case where a separate FEP defines actors differently, then you would disambiguate by the full identifier and not by the shorthand term. there’s two approaches for this:

  1. you understand JSON-LD (or enough JSON-LD) to use the explicit @context, which allows you to expand any/all terms (properties, vocab-relative values like types) into full identifiers. so the canonical form you will be working with is https://w3id.org/fep/1580#actors and https://some-other-vocab.example/actors.
  • you can take the unambiguous expanded form and work with it directly, or (re)compact it against whichever @context you understand, or flatten it, or whatever you need to do to make it easier for you to work with.
  • if you’re retransmitting the object, you should store its original @context because you’ll need that to squeeze all the data back into the shape that other LD-unaware peers expect.
  1. you do not understand JSON-LD and do not use explicit @context, which means that you instead have to agree ahead-of-time with any/all peers on the canonical term definitions (aka the implicit @context) to be used within a document. if you use actors to mean https://w3id.org/fep/1580#actors and someone else uses it to mean something else, you will have a misunderstanding. if someone else uses actors differently and instead aliases https://w3id.org/fep/1580#actors to something like fep-1580_actors_collection, then you will probably not recognize this. you either accept this as a failure to communicate (because you assumed shared definitions instead of actually negotiating them), or you implement at minimum the JSON-LD term expansion algorithm (i.e. enough JSON-LD) and you are now in case 1.
  • a slightly different subvariant of this case is to agree ahead-of-time with any/all peers to always use fully expanded identifiers, which removes ambiguity at the cost of readability. you still have to agree with your peers ahead of time, and you cannot communicate with peers who are context-aware unless they pre-expand all of their statements for you.

if i understand correctly, i think that you could pick clearer names probably? even reading the FEP a few times over i’m not sure i have them disambiguated in my head. looking at the examples it seems like you have something kinda like:

<A1> a <MigrationActor>.
<C1> a as:Collection, <MigrationCollection>.
<C2> a as:Collection, <MovesCollection>.

<migration>
  label "has a collection of objects to be moved".

<moves>
  label "has a collection of Move activities".

<A1> <migration> <C1>.
<A1> <moves> <C2>.
<C1> <moves> <C2>.

is that accurate to the current state of the FEP? the actor points to the migration collection, but both the actor and the migration collection point to the moves collection? if so, this seems like ambiguous data modeling, because the relation between the actor and a collection is almost certainly not the same as the relation between a collection and another collection.

i almost kind of wonder if you wouldn’t be better off trying to model this as a discrete reified Migration object of some sort, that then references two Collections?

also, in which cases would you have more than one item in a moves collection? all the examples look like they only ever need to describe a singular actor migration.

i’m at a loss to provide further feedback because i’m having trouble grasping the relations between all these things. probably an entity relation diagram would help.

Yes, I agree with you that the “collection-per-type” strategy is the way to go. We talked about this a bit in the data portability discussion and settled on making a separate property in the actor for “migrate-able” objects.

Here’s where that discussion ended up:

{
  // Standard ActivityPub actor here
  "id":"https://muh-server.social/@me"
  "type":"Person",
  "inbox":"https://muh-server.social/@me/inbox",
  "outbox":"https://muh-server.social/@me/outbox",
  "liked":"https://muh-server.social/@me/likes",
  "following":"https://muh-server.social/@me/following",
  "followers":"https://muh-server.social/@me/followers",
  "blocked":"https://muh-server.social/@me/blocked",
  "shared":"https://muh-server.social/@me/shared",

  // A new object contains migration-specific collections
  "migration": { 
    "outbox": "https://muh-server.social/@me/outbox-but-for-migration",
    "following":"https://muh-server.social/@me/following-but-for-migration",
    "liked":"https://muh-server.social/@me/liked-but-for-migration",
    "shared":"https://muh-server.social/@me/shared-but-for-migration",
    "blocked":"https://muh-server.social/@me/blocked-but-for-migration",
    "content": "https://muh-server.social/@me/content",

    "emissary:privileges":"https://muh-server.social/@me/portable/privileges",
    "emissary:circles":"https://muh-server.social/@me/portable/uploads",
    "mastodon:other": "https://we-could-even-export-schemas-for-other-apps.social/@me/other"
    } 
}

This addresses several important requirements:

  1. This separate property also gives us a clean catalog of everything the source has that can be exported. This gives the target server the responsibility of figuring out what data it can (or can’t) use.

  2. This is a super-set of the publicly available data. Users will likely need to migrate lots of un-published information too, so these collections are only accessible with some enhanced set of privileges - hence the OAuth handshake at the beginning to get an access key with permission to export this data.

For instance, if you have Blocked an account but do not publish this in your blocked collection, this fact might be missed if we just use the standard collection. We solve the problem by making this available in a privileged, “migration only” collection.

  1. This also allows us to expand the set of “migrate-able” data outside of the regular ActivityPub collections. For instance, my particular use case requires that I’m able to migrate a lot of extra stuff that doesn’t fit into ActivityPub. (for instance, purchase history, memberships, etc). These additional collections show up (as examples only) as properties like: emissary:privileges and emissary:circles. This is a very important requirement that any migration spec must handle.

What do you see as the best way to being these two efforts together?

1 Like

This is a part I don’t understand very well. It seems like a malicious target actor could just add this “actor link” without any authentication or authorization. No?

I touched on this in my last comment, but I’m starting with the assumption that there’s likely private information in these export collections, so we must have some way to guarantee that the target is allowed to retrieve this data.

And, it seems like an API key (retrieved via OAuth) would be the simplest way to do this. Yes, we could probably use HTTP signatures to sign requests, then verify that the signature comes from an approved actor, but this feels like a lot more work (with more potential errors) than just validating an JWT token / API key.

Yes, and I’m certainly not suggesting that the spec describe UI layouts, or buttons and colors. But I think the spec should absolutely focus on the user experience (UX, not UI). So, we should define the overall user workflow. Otherwise, apps will define multiple, incompatible workflows and nothing will interoperate.

Would you be open to adding OAuth handshake into this part of the FEP? Here’s the process makes sense to me:

  1. I start with an existing account on my “source” server.

  2. I create a new account on my “target” server.

  3. I initiate the migration on the target server, which starts an OAuth handshake with the source.

  4. I approve the OAuth “migration” privileges on the source server, and deliver that API key to the target

  5. The target server uses the API key to locate all of the “migrate-able” data available on the source

  6. The target pulls over all of the data it can use, and lets me know when it’s done

  7. I double check my account on the target to verify that it looks good.

  8. I go to the source server to finalize the migration, which sends Move notifications to the network.

This (or something like it) reduces the number of times that a user would need to bounce between servers, which is a critical part of the UX – which is perfectly reasonable for this FEP to address.

@benpate @jonny Lisa Dussault's threat models that preceeded the LOLA spec might also be useful - Threat Model Part 3 - Access, Content, and Spoofing links back to the first two posts in the series.

2 Likes

this one seems to be accidentally marked @type: @id in the linked context document

ah i must have misunderstood your comment about coercion when it used to be an xsd bool as saying that should also be an @id, i’ll fix.

if you are talking about the case where a separate FEP defines actors differently, then you would disambiguate by the full identifier and not by the shorthand term

I think i figured it out, what i wanted is to have the actors term only defined when used within the moves collection, because i wanted to avoid the need for using an absolute URI in the JSON and also the potential for another vocabulary to define actors (which seems pretty high likelihood).

I knew this was possible, just didn’t remember offhand, if i use scopes contexts this works

{
  "@context": {
    "moves": {
      "@type": "@id",
      "@id": "https://w3id.org/fep/1580#moves",
      "@context": {
        "actors": "https://w3id.org/fep/1580#actors"
      }
    }
  }
}

where when i apply it to this data

{
  "moves": {   
    "actors": {}
  },
  "actors": {}
}

then the actors within the moves object is expanded to the full URI, but the root actors is not.

i think that you could pick clearer names probably?

suggestions welcome

is that accurate to the current state of the FEP?

yes, except for OrderedCollections, but yes that is the structure.

this seems like ambiguous data modeling, because the relation between the actor and a collection is almost certainly not the same as the relation between a collection and another collection.

It’s maybe duplicative but i don’t think it’s ambiguous. The reason a reference to the moves collection is that a migration collection is only defined with respect to a moves collection: a migration collection contains the object movements that correspond to a set of actor moves, and the moves collection is a proof document that proves that the migrations are valid. so on its own migration is not to be trusted, it always needs a moves collection, and so to avoid the situation where the link was implicit in the case of odd implementations or future drift, include a reference to it in the collection. the reverse is not necessarily true, as there may be actor moves without object migrations, and actor moves are meaningful without the context of the migrations collection.

i almost kind of wonder if you wouldn’t be better off trying to model this as a discrete reified Migration object of some sort, that then references two Collections?

After skimming LORA i think this is probably the direction that we’ll go - i don’t have strong opinions for why there should be two root-level objects as opposed to a single object that contains both except for the idea that moves can be used independently of migration, but it can still be used independently if nested within another object.

in which cases would you have more than one item in a moves collection? all the examples look like they only ever need to describe a singular actor migration.

e.g.

  • when doing a key rotation on the target actor, a Move (or some other activity) will need to be emitted with the new key. that needs to be signed by the previous key, so you need to have both the prior Move (from the source instance to the target) and the key rotation move (actor id unchanged, but key changed) to be able to prove that the key rotation is valid
  • wanted to leave open the possibility for a full chain history of movements
  • wanted to leave open the possibility for merging accounts: move multiple actors into a single actor

i’m having trouble grasping the relations between all these things. probably an entity relation diagram would help.

lmk what’s confusing, it’s

  • a collection of actor Move activities that prove a move was initiated by the source and confirmed by the target actors
  • a collection of object Move activities that map the URI on the source instance to the URI on the target instance. it refers to the actor moves collection which is needed to prove that the objects should indeed be moved.

and that’s pretty much it for the collections as they stand right now.

oh! i must have misunderstood you before, and couldn’t find LORA (since found) - this looks good, i like that, although I don’t immediately think that the collections should be different collections, i’ll keep reading LORA and see if i find the rationale there.

Looking at LORA, they make a migration collection on the source actor, so that’s one point where we’ll need to converge: if i were to pick names offhand i would probably do migrate (as in “these are the things to migrate”) on the source instance, and migration (as in “this is the content of a migration”) or migrated (as in “these are the things that have been migrated”) on the target instance - the name makes sense in LORA because they don’t have an equivalent to what this FEP does on the target instance.

This separate property also gives us a clean catalog of everything the source has that can be exported. This gives the target server the responsibility of figuring out what data it can (or can’t) use.

cleaner, yes, than just enumerating all collections on the actor - the responsibility is with the target actor to discover and ingest in both cases. The problem here is that it doesn’t really do backwards compatibility well: the (current) ingest flow in this FEP allows for migrations from instances that don’t support the FEP because it doesn’t require a special collection on the source instance, you just grab whatever you can see.

This is a super-set of the publicly available data. Users will likely need to migrate lots of un-published information too,

yep that’s in this fep too:

“Source instances MUST make objects that control object visibility (e.g., blocks) available to target instances during migration in order to preserve Actor visibility preferences. If any such visibility control objects are not typically present in an Actor object’s linked Collections, the source instance MUST include them in Collections linked from the Actor object, and they SHOULD use keys that can be predicted from the object type, e.g. blocks for Block activities, though the specific terms and Collection structure are left unspecified here7.”

although i only made the minimal spec there, just object control visibility things, but in general it’s like “when there is stuff you don’t normally put in the actor object, put it in there when you get a request on behalf of the target actor”

I was trying to be very general with the spec, assuming there might be other objects that control visibility than Block activities, so i didn’t want to enumerate the required collections or assign terms to them.

This also allows us to expand the set of “migrate-able” data outside of the regular ActivityPub collections. For instance, my particular use case requires that I’m able to migrate a lot of extra stuff that doesn’t fit into ActivityPub.

yep this spec is that general too - if there are collections of objects on the source actor when the target actor goes looking, and the target instance understands them, it should ingest them. (and if it doesn’t understand them it might just make a copy of the json in case it wants to offer an export or it supports them later)

i see another comment below about OAUTH things so i’ll answer the auth question there

What do you see as the best way to being these two efforts together?

let me finish reading LORA, but i think with only minor adjustment they can be made compatible. there are some differences i am finding that i think are done better here, and some that are done better there, so i will do a PR with an update after finishing reading and thinking about it.

This is a part I don’t understand very well. It seems like a malicious target actor could just add this “actor link” without any authentication or authorization. No?

nope! an actor link needs to be reciprocal. both actors need to have reciprocal links in order to be valid. the way it currently works on mastodon is that after a move there is a forward movedTo link and a reverse alsoKnownAs link. before the Move activity is emitted it checks for the reverse alsoKnownAs link. a malicious target actor could add one of those (anyone can always declare any property!), but it doesn’t do anything without the source actor initiating a move.

so we must have some way to guarantee that the target is allowed to retrieve this data. And, it seems like an API key (retrieved via OAuth) would be the simplest way to do this. Yes, we could probably use HTTP signatures to sign requests, then verify that the signature comes from an approved actor, but this feels like a lot more work (with more potential errors) than just validating an JWT token / API key.

Again this FEP is agnostic to mechanism, it just specifies that if there is some mechanism for authenticating requests as being on behalf of the target actor understood by the source actor, it must be used. So that can be OAuth or HTTP signatures. I am wary about requiring an OAuth flow because it can be a reasonably big development burden to implement right, and i want it to be possible for extremely lightweight AP instances to implement.

This FEP does require that the instances support object proofs and signatures already, and to me moving towards signed objects is a more important development target.

it’s also the case that ActivityPub already (implicitly) specs HTTP signatures: https://www.w3.org/wiki/ActivityPub/Primer/Authentication_Authorization so it’s likely to already be implemented on AP instances

again this is a backwards compatibility concern - for a migration from an instance that doesn’t support object migration, it probably already supports HTTP signatures for basic operation, but if it doesn’t support OAuth then the migration can’t take place. I think that allowing both (and also allowing for future expansion in the case of other auth standards) is probably worth the need to do an if/else to check if something supports OAuth or HTTP signatures only.

there’s also a subtle distinction of semantics here: this FEP is written with the perspective that the target actor should be treated the same as the source actor, the new version where the source actor is now deactivated. there is a (again very subtle) distinction between “asking permission from the source actor” and “treating the target actor as if it is the source actor” that may have some impact on how things are implemented, and that may be relevant in the course of hostile instances.

I am not really sure how this differs from what i described above, except it doesn’t include the moderation steps.

this FEP is intended for the UX of the process to be different on different software, and imo that’s a good thing.

so as specified, migration could look like

  • one click: from the target instance, set a reverse actor link and use some auth like OAuth to tell the source instance to begin migration
  • two clicks: set a reverse link on the target account, then initiate move from source instance
  • three clicks: {two clicks} and then review requested changes from the target instance
  • four clicks: {two clicks}, subset the things i want to move, and then review requested changes from the target instance

depending on the moderation needs of the target and the control desired by the migrating actor.

so this FEP does indeed address UX (and backwards compatibility, and consent, and moderation, and etc.) by allowing implementing software to have a full moderation flow or not depending on what is appropriate.

json-ld scoping

this only works if you embed the moves collection and not if you serve it directly as a top-level document. that might be okay if you don’t expect the collection to ever be served as a top-level document. there might also be weirdness if the term moves is ever used with a different definition.


clarifying the information modeling

i don’t think i understand the proposal well enough yet to suggest alternative names. i’m still not entirely clear on what the data model looks like or how it will be used, but if what i mapped out above

is correct as you say, then it still seems like moves is being used ambiguously. the arc between <A1> and <C2> is not the same relation as the arc between <C1> and <C2>. the part that i’d like to understand better is:

how would moves be used independently of migration?


misc comments

i don’t think this is true? you can update the key at any time, in what is known as “blind key rotation”, and this is valid because the authoritative identifier doesn’t depend on the key at all.

Hey, sorry I’ve been silent. I really do want to keep this conversation moving forward, because this is probably the #1 issue for moving the whole Fediverse forward. It’s just been hard to collect my thoughts on this.

I think you’re talking about the moderation steps in your FEP, which are not really discussed in LOLA. Yes. This is one area where the FEP shines. It’s a great idea to put this into the spec so implementors can see the whole process holistically.

I believe it’s also important that the spec includes a single way to start a migration. If that is left up to implementors, then we’re bound to have multiple/incompatible ways of getting started, which could derail this entire effort. If my software expects an OAuth handshake, and yours expects a manual process, users might not be able to move between our two apps. And if the standard is split, then it will be dead in the water, so we must work out any incompatibilities before they happen.

So, what’s the best way to hammer out the holes in the FEP? Maybe we can schedule a video conference with Lisa and the LOLA task force?