Skip to content

FEP-1580: Move Actor Objects with a migration Collection

Authors Status Type Date received Tracking issue Discussions Repository
Jonny Saunders j@nny.fyi DRAFT implementation 2025-10-09 #702 Discussions codeberg

Summary

(This section is non-normative)

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 are owned4 by that actor.

This FEP describes a mechanism of migrating objects owned by 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. This FEP describes a "Push"-style migration6 initiated by a source instance followed by a "pull" of objects by a target instance, as well as a "Pull"-style migration initiated by a target instance given a prior export of actor data. 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.

Problem Statement

(This section is non-normative)

Following an Actor migration, the new, "target" Actor is considered equivalent to and the successor of the prior "source" Actor. Despite being declared as equivalent2 as:alsoKnownAs, activities and objects on a cooperative source instance continue to exist under the ownership of the now-inactive Actor, effectively becoming "zombie objects" that the new primary Actor has no direct control of; on an uncooperative or unavailable instance, these objects are lost even with a valid migration activity. Inability to migrate the objects owned by an Actor means that they are catastrophically lost in the case of instance shutdown, and has far-ranging impacts on the social and political structure of the fediverse by strongly dis-incentivizing movement between instances, and makes new user entry into the fediverse difficult and confusing, where a choice of instance made before having the chance to gain familiarity with the network becomes indefinite destiny.

Moving objects is hard: - The combinatorics of every third-party instance needing to update every object after a move make object migration potentially forbiddingly costly. This is particularly true when considering ActivityPub in "pub/sub-only" mode where actions must be processed immediately upon receipt: a target server emitting 100,000 Move activities, one for each post, that all recipients need to keep up with would drown the fediverse. - The mapping from old to new URIs is not knowable in advance, as the local IDs used by one instance software need not map onto the IDs used by another, so a single batch update action is impossible. - There is no guarantee that a given Object on the source instance will be fully supported by the target instance, and some non-uri properties like proof signatures by definition must change after a migration, so 3rd-party instances must query the target instance to retrieve the updated versions of the migrated objects. - Objects are embedded within a larger graph that includes other collections like replies that must also be updated to reflect the migration. - Not all objects should be migrated, e.g. someone may want to make a "fresh start" on a new instance, keeping their followers/followed accounts, but only bringing some subset of objects (or none) along with them. - The source instance may not be cooperative or even online, so object migration may require the use of external backups which may be lossy or out of date. - Instance software that does not support object migration may be left in an inconsistent state with the rest of the fediverse that does.

However ActivityPub has a dual push and pull operation, where activities and objects can be stored in Collections for at-will consumption by 3rd-party instances. By storing a mapping between prior object URIs/IDs on the source instance and new URIs/IDs on the target instance, object migration can be done gradually, partially, safely, and selectively. This approach uses standard ActivityPub semantics and common collection enumeration operations, easing implementation.

Specification

MUST, MAY, and SHOULD used in the RFC-2119 sense where they appear in CAPITAL LETTERS.

Supporting instances MUST indicate their support of this FEP by including its namespace in the @context of affected Actor objects.

Terminology

As used in this document:

  • An "Actor move" or "Actor migration" refer to the Move specifications in FEP-7628 and FEP-E965 where the primary actor of a set of linked identities is changed. Specifically, a "irreversible" migration of the movedTo variety, rather than the reversible, partial copiedTo. The "Actor move" activity is addressed to as:Public.
  • A "source instance" is an ActivityPub server instance from which the Actor is moving from
  • A "target instance" is an ActivityPub server instance which the Actor is moving to. A target instance MAY be the same as the source instance.
  • A "3rd-party instance" is an ActivityPub server instance which is neither a source or target instance, but stores Objects that are affected by the object migration.
  • A "proposed move" is an Offer[Move] activity emitted by the source instance addressed to the target instance's shared inbox (and not as:Public) after the source instance has validated the source and target actors are FEP-7628 linked actors in order to solicit an Accept[Move] from the target instance confirming the move. It is distinct from the "actor move" Move activity that is publicly emitted following acceptance of the move proposal.

CURIE prefixes used in this document: - migration: https://w3id.org/fep/1580/ - the namespace created by this FEP (FEP-888d) - as: https://www.w3.org/ns/activitystreams - the ActivityStreams vocabulary - sec: https://w3id.org/security# - The Data Integrity vocabulary used by FEP-8b32 proofs.

The prefixes are assumed to be declared in the @context of any JSON-LD examples. Prefixes are included on first mention of a non-activitystreams term, but are otherwise omitted for concision.

Preconditions

Supporting software MUST support the following behavior specified in other FEPs - FEP-7628 - FEP-8b32 - specifically the software must be capable of managing keypairs per actor rather than use one private key per instance.

All behavior described in this FEP MUST follow receipt of a valid Move activity or its side effects as defined by FEP-7628, FEP-E965 and this FEP that inactivates the source account: i.e. the source account is either no longer available or has a movedTo property declared.

Target Instance

Ingest

After an Actor Move activity is completed and verified, the target instance MUST perform an "ingest routine" that - enumerates all supported actor collections present in the actor object OR the set of objects approved during the moderation handshake, as described in Pre-Move Migration - ingests their contents, and - creates new Activities and Objects that preserve as much of their content as is supported.

"Supported actor collections" is left purposefully undefined and is left as an implementation detail to supporting software, however supporting software SHOULD attempt to support all collections and objects that it has a representation for. If unknown object types are encountered, the target instance MAY store a full copy of those objects in case a future software update supports them.

If applicable, objects that control the visibility of other objects (in particular: Block activities) MUST be ingested first so they may be used to control the visibility of objects within the migration collection. Migrated items MUST be subject to the same visibility constraints as on the source instance, including blocks, whenever possible.

During collection enumeration, requests MUST be signed as being on behalf of the target Actor if signing a request as being on behalf of an actor is supported by the source and target instances.

Created/updated timestamps MUST be preserved from the original object, but supporting instances MAY add and MAY display additional properties to indicate an object has been migrated: - a migration:migratedAt timestamp to display the time an object was migrated, and - a migration:migratedFrom URI to link to the prior object.

The target instance MUST insert the newly migrated objects in relevant collections and perform any side effects that normally accompany object creation, however it SHOULD NOT emit Activities indicating the creation of these migrated objects.

Instead, the target instance MUST create two collections on the Actor object to support gradual migration:

migration Collection

The target instance MUST create a migration5 OrderedCollection that maps the object URIs from the source instance to their counterparts on the target instance. The migration collection SHOULD remain present on the actor object for the lifetime of the actor.

The migration collection MUST be ordered in reverse chronological order by the timestamp that the migrated counterpart of the original object is created on the target instance (NOT according to the date the original object was created or updated on the source instance).

The migration collection MUST contain a migration:moves URI reference to the accompanying moves collection (below).

The items in the migration collection SHOULD use URI references in their object and target fields rather than inlining the materialized objects, and SHOULD paginate the collection as OrderedCollectionPages to reduce resources spent serializing and constructing the collection.

Items in the migration collection MUST match the item visibility of the source objects, when possible. In particular: - Move activities for objects that are not addressed to as:Public MUST not be publicly displayed. - Move activities for non-public objects MAY be displayed when the request is authenticated as having been made by an Actor in the object's audience3. - Move activities MUST respect actor-level visibility controls like blocks.

The migration collection MAY be created incrementally, inserting migrated objects as they are ingested, or it may only populate the collection upon completion of the ingest routine.

Implementing software MAY provide appropriate options and interfaces to allow the migrating actor to select which, if any objects they want to migrate (see Pre-Move Moderation). If the actor elects to not migrate any objects, the migration collection MUST still be published and its items field MUST be empty9.

Upon completion of the ingest routine, the migration collection's migration:migrationComplete property must be set to true, indicating for consuming instances that for the given state of the moves collection (below) there will be no changes to the migration collection. To "reopen" the migration collection for consumption by third party instances, signaling new changes from e.g. an upload of an exported collection (below), the target instance MUST emit another (potentially duplicate) Move activity and set the migrationComplete property to false until the changes are completed. The target instance MAY delay setting migrationComplete to true in order to provide an interface to prompt the migrating actor to upload a previously exported collection of objects.

A target instance MUST NOT begin another actor move to another instance until the migration is complete in order to prevent loss of objects still in the process of being migrated from the source instance.

Examples

(This section is non-normative)

("...": "..." is used to indicate "the rest of the object" in the absence of JSON comments)

Actor on target instance

{
  "id": "https://example.com/chuckTargetson",
  "type": "Person",
  "...": "...",
  "migration": "https://example.com/chuckTargetson/migration",
  "moves": "https://example.com/chuckTargetson/moves"
}

migration OrderedCollection

{
  "id": "https://example.com/chuckTargetson/migration",
  "type": "OrderedCollection",
  "attributedTo": "https://example.com/chuckTargetson",
  "totalItems": 100,
  "first": "https://example.com/chuckTargetson/migration/page/0",
  "migrationComplete": false,
  "moves": "https://example.com/chuckTargetson/moves"
}

migration OrderedCollectionPage

{
  "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"
    },
    {
      "type": "Move",
      "actor": "https://source.example.com/barbaraSourceworth",
      "origin": "https://source.example.com/barbaraSourceworth/posts/abcde",
      "target": "https://example.com/chuckTargetson/items/zyxwp"
    }
    {"...": "..."}
  ]
}

moves Collection

The moves collection serves two purposes: - As a content-hashable indicator of the state of an account's migrations - As a durable source of authentication for confirming the validity of the items in the migration collection.

The moves collection contains the deduplicated Move activities emitted when an Actor is migrated. Move activities MUST be signed by the source actor using an FEP-8b32 proof. The move collection MUST make a copy of the source Actor object that contains the means of validating object integrity proofs available either inlined in the items of the Move collection or in the actors collection (described below). Source instances SHOULD inline the source Actor object in actor Move activities.

If the actor is not inlined in a signed Move activity, the target instance MUST create an inlined copy of the source Actor object in its actors collection. The actors collection is an OrderedCollection of inlined Actor objects sorted in lexicographic order according to their id (example below).

The moves collection MUST itself be signed with an FEP-8b32 proof. By including the inlined Actor objects and Move activities signed by the source Actor(s), and having the collection be signed by the target Actor, the moves collection serves as a self-contained, verifiable, bidirectional proof of the validity of the items in the migration collection.

Consuming instances MAY consider a hash of the canonicalized form of the collection (e.g. RFC 8785, RDF canonicalization) as a content hash for the purposes of change detection, but the details of content addressing activitypub objects are outside the scope of this FEP.

The moves collection MAY include a full chain of account movement, including moves from prior instances to the source account where the target is not the current target account. Inclusion of account move activities MAY be used by consuming instances to migrate objects across several moves, but specifying chains of migration is out of scope for this FEP.

Examples

(This section is non-normative)

moves collection with Actors inlined in Move activities

{
  "id": "https://example.com/chuckTargetson/moves",
  "type": "OrderedCollection",
  "orderedItems": [
    {
      "id": "https://source.example.com/activities/12345",
      "type": "Move",
      "origin": "https://source.example.com/barbaraSourceworth",
      "target": "https://example.com/chuckTargetson",
      "actor": {
        "id": "https://source.example.com/barbaraSourceworth",
        "type": "Person",
        "...": "...",
        "publicKey": {
          "id": "https://source.example.com/barbaraSourceworth#main-key",
          "owner": "https://source.example.com/barbaraSourceworth",
          "publicKeyPem": "-----BEGIN PUBLIC KEY-----\n..."
        }
      },
      "proof": {
          "type": "DataIntegrityProof",
          "cryptosuite": "eddsa-jcs-2022",
          "verificationMethod": "https://source.example.com/barbaraSourceworth#main-key",
          "proofPurpose": "assertionMethod",
          "proofValue": "...",
          "created": "2023-02-24T23:36:38Z"
      }
    }
  ],
  "proof": {
    "type": "DataIntegrityProof",
    "cryptosuite": "eddsa-jcs-2022",
    "verificationMethod": "https://example.com/chuckTargetson#main-key",
    "proofPurpose": "assertionMethod",
    "proofValue": "...",
    "created": "2023-02-24T23:36:38Z"
  }
}

moves collection with Actors inlined in the actors collection

{
  "id": "https://example.com/chuckTargetson/moves",
  "type": "OrderedCollection",
  "orderedItems": [
    {
      "id": "https://source.example.com/activities/12345",
      "type": "Move",
      "origin": "https://source.example.com/barbaraSourceworth",
      "target": "https://example.com/chuckTargetson",
      "actor": "https://source.example.com/barbaraSourceworth",
      "proof": {
          "type": "DataIntegrityProof",
          "cryptosuite": "eddsa-jcs-2022",
          "verificationMethod": "https://source.example.com/barbaraSourceworth#main-key",
          "proofPurpose": "assertionMethod",
          "proofValue": "...",
          "created": "2023-02-24T23:36:38Z"
      }
    }
  ],
  "actors": {
    "id": "https://example.com/chuckTargetson/moves#actors",
    "type": "OrderedCollection",
    "orderedItems": [
      {
        "id": "https://source.example.com/barbaraSourceworth",
        "type": "Person",
        "...": "...",
        "publicKey": {
          "id": "https://source.example.com/barbaraSourceworth#main-key",
          "owner": "https://source.example.com/barbaraSourceworth",
          "publicKeyPem": "-----BEGIN PUBLIC KEY-----\n..."
        }
      }
    ]
  },
  "proof": {
    "type": "DataIntegrityProof",
    "cryptosuite": "eddsa-jcs-2022",
    "verificationMethod": "https://example.com/chuckTargetson#main-key",
    "proofPurpose": "assertionMethod",
    "proofValue": "...",
    "created": "2023-02-24T23:36:38Z"
  }
}

Migrations from Exported Collections

In circumstances where the original objects are not available at the source instance, importing objects from some external backup of the relevant collections is no different than enumerating them from the source instance. If a valid Move activity was emitted, supporting instances SHOULD provide a means of uploading and ingesting exported collections, and if they do, they SHOULD ingest and otherwise handle objects from an export format of a collection identically as those that were retrieved from the source instance, ignoring any duplicates.

Recall this FEP assumes that a valid, signed Move activity was emitted by the source instance - the case where an account Move activity was not emitted and the source instance is unavailable or hostile is outside the scope of this FEP. However, if the actor export contains the private key that accompanies the public key used for object proofs, and a Move activity can be created and validated independently of the source instance, the actor Move activity SHOULD be considered equivalent to one emitted by the source instance (FEP-7628 "Pull" mode migrations). Supporting software MAY provide interfaces and mechanisms for import and export of private actor keys and creation of Move activities from those exports.

Target instances MAY treat imports from exported collections as subject to the same moderation flow they would apply to objects imported from a source instance (see Pre-Move Moderation) and choose objects to remove or modify from an import. If moderation is performed, the target instance MUST notify the target actor of removed or modified objects.

See notes on security below re: source object forgery.

Rate Limiting

The target instance is the only instance that can know the rate at which it can service requests. Accordingly, the target instance SHOULD use standard HTTP rate limit headers and response codes to coordinate gradual migration requests by 3rd-party instances.

The target instance SHOULD service migration-related requests as quickly as possible without otherwise degrading normal service so that time spent with third-party instances in an inconsistent state is minimized.

Source Instance

Comparatively little needs to be implemented on the source server.

If authentication and signing requests as being made on behalf of an actor is supported by the source and target instances, the source server MUST consider requests made on behalf of the of the target Actor as if they were made by the source Actor for purposes of visibility and access.

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.

Source instances SHOULD: - continue to host original copies of the migrated objects until the target instance completes its ingestion. - prompt source actors to export any objects that were rejected or changed during moderation (see Pre-Move Moderation), and attempt to store them for some reasonable period to allow an export to be made. - consume the migration collection from the target instance and store a mapping from the source to target objects. - present the migrated objects with a FEP-7628 movedTo property containing the new target object URI while the object remains present - if the source object is removed, return a 301 redirect to the target object URI.

However, source instances MAY elect not to preserve the original objects for one of several reasons e.g. in FEP-73cd re: moderation, legal, or other concerns that make continued hosting of the source objects intolerable to the source instance. This FEP is written to minimize the responsibility of the source instance, purposefully avoiding binding it into longterm service requirements by making a self-contained record of moves and migration on the target instance.

3rd-Party Instances

After observing and validating an account Move activity, 3rd-party instances MUST initiate a "migration routine" (below) that consumes the migration collection of the target Actor.

If the Move activity was not observed by a 3rd-party instance (e.g. because the instance was offline or unreachable), a 3rd-party instance MUST initiate the "migration routine" upon observing either: - a FEP-7628 movedTo property in the source Actor object AND reciprocal alsoKnownAs property in the target Actor, or - the presence of an unprocessed Move action in the target Actor's moves collection.

If the migration collection is not marked as complete (via migrationComplete == true), 3rd-party instances MUST periodically check for new items, with some long timeout in case the target instance is malfunctioning or incorrectly implements migrationComplete.

3rd-Party instances MUST validate the object integrity proof for actor Move activities, but are NOT REQUIRED to re-validate each of the migrated items.

If after observing a valid Move activity, 3rd-party instance observes a mismatch in migration data (e.g. mapping to different URIs, differences in content) between the source and target instances, it MUST prefer the target instance's data, as the Move activity has designated the target Actor and target instance as the primary Actor in custody of the objects.

Migration Routine

For an actor Move activity, 3rd-party instances MUST update all references to the source Actor in its internal representations of objects to refer to the target Actor - e.g. in "mentions" or as:Links. They MAY preserve representations of both the source and target actor for the purposes of representing and displaying the Move.

For each entry in the migration collection that the 3rd-party instance has a local representation of, 3rd-party instances MUST - Update the item's ID/URI from the origin to the target. - Request any updated fields in the target object from the target instance. - Update its internal object representations so it is indistinguishable from the case where source object was created by the target actor and instance (except for migration-specific fields like migratedFrom).

3rd-party instances MUST NOT emit additional Update or other activities while updating migrated objects.

3rd-party instances SHOULD NOT fetch items referred to in the migration mapping that they do not already have a local representation of: object migration is not backfilling.

3rd-party instances SHOULD implement migration routine operations as optimistic: they should perform predictable operations using their local representations of objects without waiting for full data from the target instance when possible. For example, after consuming the migration collection, which is effectively a mapping from source to target URIs, the instance should update the object's URI without waiting for a full refresh of the object. The target instance MAY update the content of the objects during migration, but 3rd-party instances SHOULD continue to treat them as unchanged until told otherwise by the target instance.

3rd-party instances SHOULD treat interactions (replies, likes, etc.) from other 3rd-party instances as if they have been, or will be migrated as well. 3rd-party instances SHOULD store the relevant migratedFrom and other information needed to reconcile activities and objects from other 3rd-party instances that do not implement object migration: e.g. if actor xena migrated from instance a to instance b, and instance c has a receives an activity from instance d that is inReplyTo object zzz from @xena@a, but instance c knows @xena@a is now @xerxes@b and zzz is now yyy, it should transform the inReplyTo field while processing that activity.

Migration routine operations SHOULD also be patient: requests for migration pages and refreshes of migrated objects should be given ample inter-request delays. The target instance is responsible for communicating rate limits, but 3rd-party instances should also be polite by default and aim to complete, for example, a migration of an Actor with 10,000 objects on the order of ~days to a week.

Migration of extended objects that are referred to by a migrated object SHOULD be performed on a "best effort" basis - when there is an unambiguous change to be made (e.g. updating the target for activities in the object's Like collection to refer to the new URI), it should be made, but it is the responsibility of the target instance to include any objects that should be migrated in the migration collection.

The target instance MAY provide some batched or condensed collection of Updates that contain only the changed fields for migrated objects, but specification of such a change collection is outside the scope of this FEP.

Passive Updates

If 3rd-party instances passively or periodically refreshes objects from the source or target instance and observe that they have been migrated (e.g. the source instance responds with a 301 redirect to the target object, a movedTo property is present on an object), the 3rd-party instance must verify the existence of a relevant actor Move activity, and if found, perform the migration routine on that object.

Retroactive Migration

As this FEP is implemented and deployed by various fedi server apps, different instances will "become aware of" migrations at different times. This FEP supports incremental migration as each different role becomes object migration aware:

  • Source instances: should require very few changes. They should periodically check in on instances to which former Actors have migrated to to check for migration collections, and when present they should create redirects and populated movedTo fields accordingly.
  • Target instances: should attempt to enumerate any collections from source instances for accounts that have migrated to them, ingest whatever objects still are available there, and publish their migration and moves collections accordingly. In the initial window of implementation, there will be many previously-migrated accounts that no longer have access to a signed account Move activity to prove a given set of entries in the migration collection. However they should still publish those collections however possible, and allow 3rd-party instances to decide how to handle previously-accepted account Moves. This transition path will be the subject of further expansion and revision as lessons are learned during implementation. In cases where the source instance is no longer available and the moved Actor has an export copy of their prior posts, this FEP should support recovering those posts to the target instance.
  • 3rd-party instances: unprocessed migration collections encountered while refreshing Actors is a formally spec'd initiation route for migration, so no special consideration should be needed. The gradual deployment of object migration across instances should soften some of the resource burden of needing to migrate a decade of objects.

Pre-Move Moderation

See discussion below (Discussion: Moderation) motivating this section and its design.

Recall that a valid actor Move having already been emitted and accepted is a precondition of this spec. This section8 specifies behaviors before the actor Move activity has been emitted and accepted.

Prior to emitting the publicly-addressed actor Move activity, source and target instances MUST complete the following Proposed Move handshake:

  • verification: The source instance verifies the source and target actors are Linked Actors (as per FEP-7628)
  • subsetting: OPTIONAL - The source instance allows the source actor to subset the objects they wish to migrate
  • syn: The source instance sends a Offer[Move] "Proposed Move" activity addressed to the target instance's shared inbox.
  • moderation: OPTIONAL - The target instance may perform a "moderation routine" to remove or update objects within the set of objects to be migrated
  • syn-ack:
  • If no moderation was performed or no changes were made to the proposed collection of objects to migrate, the target instance returns an Accept[Move] activity addressed to the source actor indicating it accepts the Proposed Move with no changes.
  • If after moderation the target instance accepts the move with some changes to or removals from the proposed collection of objects to migrate, the target instance returns a TentativeAccept[Move] activity addressed to the source actor with an attached collection of Remove and Update activities.
  • If after moderation the target instance chooses to reject the actor migration, the target instance returns a Reject[Move] activity addressed to the source actor, and the source actor is notified that their request to move was rejected.
  • review: If a TentativeAccept[Move] response was received, the source instance provides a means of the source actor reviewing the changes to the migrated items required by moderation and indicating whether they accept them or wish to cancel the actor move operation.
  • ack:
  • If an Accept[Move] response was received, the public Actor Move activity is emitted.
  • If a TentativeAccept[Move] was received and the source Actor indicated that they accept the changes required by moderation in the review phase, the public Actor Move activity is emitted
  • If a TentativeAccept[Move] was received and the source actor indicated that did not accept the changes from moderation and wish to cancel the actor move, a Reject[TentativeAccept[Move]] activity is emitted addressed to the target instance's shared inbox.

subsetting: The source instance MAY provide the source actor some means of selecting which objects they choose to migrate. If such a subsetting feature is provided, the resulting subset of objects to migrate MUST be included as an attachment to the Proposed Move. If a collection of objects to migrate is attached to a proposed move, the target instance MUST consider only these objects in the subsequent moderation stage and replace the set of objects that would otherwise be discovered by enumerating the source actor's collections in the ingest routine. An empty attachment collection MUST be interpreted as the intent to migrate only the actor with none of its objects. If an attachment of objects to migrate is absent, the target instance MUST consider the set of objects to migrate as the set of importable objects in the source actor's collections.

moderation: The target instance MAY provide some means of moderating the collection of objects to be migrated. The target instance MUST return a TentativeAccept[Move] activity with an attached collection of Update[Object] and Reject[Object] activities that represents the objects that would be changed or removed in a migration, relative to the collection of proposed objects to migrate. Following the ActivityPub specification of server-to-server Update actions, the complete object with modifications made must be included in Update[Object] responses, rather than a set of partial updates (only those fields that have changed). To avoid specious updates that are trivially implied by an Actor Move (e.g. the proof field changing) or would require the target instance to have already imported the object (e.g. the id field changing), the updated object MUST NOT contain changes to fields that can only be determined after an object is migrated. The source instance MUST NOT validate the updated objects against the proof field - as they will by definition fail the integrity proof.

Collections or OrderedCollections referenced within the attachments of the Proposed Move and TentativeAccept[Move] activities MUST be treated as if the contained objects were included in the attachments directly - proposing to migrate or moderate a Collection means that one is proposing to migrate or moderating all the items in that Collection.

If a valid Actor Move activity is received without having previously accepted a Proposed Move, e.g. from a source instance that does not support this FEP: - If a target instance chooses not to moderate migrated objects, it MUST accept the Actor move activity as otherwise described in this FEP, migrating all objects it is capable of migrating. - If a target instance chooses to moderate migrated objects, it MUST consider all objects it is capable of migrating as being subject to moderation, but MAY choose not to immediately ingest all objects, instead gradually ingest and add them to the migration collection as they are approved. The target instance MUST immediately notify the target actor of the pending moderation and advise them to make a backup of the objects from the source instance to preserve any objects that may be removed or modified by moderation.

If the target instance refuses the initial Proposed Move request, e.g. if it does not support this FEP, the source instance MUST notify the source actor that their objects may not be migrated and allow them to approve or cancel emission of the public actor Move activity.

Examples

(This section is non-normative)

Proposed Move with a set of proposed object to migrate

{
  "id": "https://source.example.com/activities/23456",
  "type": "Move",
  "to": "https://example.com/sharedInbox",
  "origin": "https://source.example.com/barbaraSourceworth",
  "target": "https://example.com/chuckTargetson",
  "attachment": {
      "type": "Collection",
      "items": [
        "https://source.example.com/barbaraSourceworth/posts/111",
        "https://source.example.com/barbaraSourceworth/posts/222",
        "https://source.example.com/barbaraSourceworth/followers"
        "..."
      ]
  } 
} 

Proposed Move specifying that no objects should be migrated with an empty collection

{
  "id": "https://source.example.com/activities/34567",
  "type": "Move",
  "to": "https://example.com/sharedInbox",
  "origin": "https://source.example.com/barbaraSourceworth",
  "target": "https://example.com/chuckTargetson",
  "attachment": {
      "type": "Collection",
      "items": []
  } 
} 

TentativeAccept[Move] specifying a set of removals and updates from moderation

The original proposed collection of objects is modified such that - post 111 would not be migrated - post 222 would have its attachment removed (but otherwise remain unchanged, assuming the "..." expands to the original object's remaining fields)

{
  "id": "https://example.com/activities/45678",
  "to": "https://source.example.com/barbaraSourceworth/inbox",
  "type": "TentativeAccept",
  "object": "https://source.example.com/activities/23456",
  "attachment": {
      "type": "Collection",
      "items": [
        {
          "type": "Reject",
          "object": "https://source.example.com/barbaraSourceworth/posts/111"
        },
        {
          "type": "Update",
          "object": {
            "id": "https://source.example.com/barbaraSourceworth/posts/222",
            "attachment": null,
            "content": "The thing I originally posted, since server-to-server Updates aren't partial updates",
            "...": "..."
          }
        }
      ]
  } 
} 

Discussion

(This section is non-normative)

Feedback and edits are welcome for each of these sections to include the inevitable caveats and consequences unforeseen by the author.

Out of Scope

This FEP is focused exclusively on the circumstance where a valid Move activity exists or can be created, and thus does not consider

  • Adversarial/uncooperative instances that refuse to create a Move activity or export a private key to sign one created by the target instance.
  • Content-addressed/portable objects: FEP-ef61 and other non-HTTP/non-URL-based id schemes typically don't require object migration (indeed that is their primary purpose), and thus this FEP does not apply to them. Implementing object migration for HTTP/URL objects should not be construed as competing with or trading off with instance-independent objects or alternative id or resolution schemes, and is intended to resolve a need in existing systems rather than preventing new systems from being created.
  • "Splitting" actors and partial migration: Account Redirects (e.g. copiedTo-style, non-final redirects) are not considered in this FEP, and it assumes that there is a single target instance and actor which becomes the primary Actor in custody of the source actor's objects. It is not possible in this formulation for the source Actor to indicate which objects might be owned by different targets or to resolve conflicts over object ownership between multiple target actors, though future amendments could allow the actor Move activity to contain scoping information that could accomplish that. Merging actors (multiple source actors moving to a single target actor) is explicitly supported by the moves collection, however.
  • Chained movement: Though a linear, unbranching chain of actor movements should be resolvable, this FEP only considers pairwise source-to-target moves.
  • Deep perf optimization: This FEP attempts to avoid being overprescriptive and minimize implementation burden - create two collections, enumerate them, and otherwise apply normal update operations - which trades off with a perfectly performance-optimized spec. It would, for example, probably be ideal to update objects using a compressed, batched changeset with partial updates rather than refreshing full objects - such a follow-on FEP would be welcome - but it would require several new mechanisms without wide implementation.
  • Export format/private key export: which is the subject of FEP-6fcd

Moderation

Migrating objects from one instance to another involves changing their moderation context: the source and target instances are likely to have differences in rules governing content, behavior, and connections between actors. If migrating objects is subject to different rate limits and moderation control than normal object creation, it may serve as an abuse vector, where content that is not allowed on the target instance can be created at faster rates or out of the eye of moderators1. As a result, target instances may want some means of moderating incoming content.

The potential implementations of moderation for migrated content might vary considerably, and this FEP is not intended to overly constrain them in advance. However since the needs of the migrating actor and the needs of the instance may conflict during moderation, some guidance is provided above (Pre-Move Moderation) to balance them.

Some examples of conflicting needs between involved parties: - The migrating actor has an interest in maintaining whatever subset of their previously-created objects they elect to migrate, but the target instance has an interest in maintaining the norms and rules that govern instance content. There is a risk of permanent loss if the target instance chooses to not ingest some objects during a migration and the source instance chooses to remove them following a migration. - Reviewing a large number of objects might create a heavy moderation burden on target instances, but a migrating actor should not be left in a prolonged state of uncertainty after their account has already been moved regarding whether their content will be allowed to migrate with them. - The migrating actor may wish to revoke their move to a target instance after seeing the results of moderation, but repeated moves create a large amount of traffic on the network, and the target instance should not have to serve as an intermediate host to content it views as against its rules in order to facilitate a second-order move. - Current implementations of FEP-7628 account movements may not be subject to moderation, as they only take into account the actor, not any of its objects. An overly-complex moderation spec in this FEP may prolong its implementation, prolonging the broader harms from the absence of object migration.

The above spec language reflects a strategy for balancing these conflicting needs: - Moderation should take place between the proposal of the Move and its acceptance - Target instances should make modifications from moderation to the collection of migrated objects transparent to the migrating actor. - Actors must have some means of changing their mind about a move if the terms of moderation are unacceptable to them after the target instance reviews any imported content. - If subsetting and moderation are both waived, or the source or target instances do not implement this FEP, the handshake can fallback to behavior equivalent to emitting a public actor move activity without the handshake. This can allow for staged implementation, handling the core migration routines first, and the frontend work needed for moderation afterwards.

The pre-move moderation spec is intended to be very general to accommodate different strategies for managing bulk moderation. For (non-exhaustive) example:

  • Accept all: e.g. when a source instance is known to share moderation practices or the source actor is otherwise trusted). This strategy may also be useful when an instance performs moderation at the time an Actor is created - e.g. if an instance supports a "create from account on another instance" feature that specifically ties a request for Actor creation with a request for migration from another Actor.
  • Strip media: e.g. return Update for all objects with attached media with that attachment removed, e.g. if storage costs would be prohibitive or an instance doesn't support the content type.
  • Strip DMs: e.g. either by the source actor by omitting them in the Proposed Move or moderation on the target instance by returning Remove for all objects not addressed to as:Public or the "followers" collection.
  • Custom selection: manually reviewing objects to import and exclude by both the source actor or target instance moderation.
  • Reject Migrations: The target instance indicates they refuse migrating objects altogether
  • Reject Moves: The target instance indicates they refuse to migrate both actors and objects

The above specification regarding pre-move moderation is compatible with FEP-e965, as it only requires that actors be publicly declared as linked in order for a Move to be considered valid - this spec adds additional optional behavior prior to the Move activity being broadcast to 3rd-party instances without modifying that requirement.

Performance

Migrating tens of thousands of objects across hundreds or thousands of instances is an intrinsically costly operation. The strategy here attempts to mitigate extreme resource requirements by allowing migrations to happen gradually and partially. Rather than emitting a flood of thousands of activities, 3rd-party instances can accomplish a majority of the important changes needed by consuming a collection of small Move activities that contain pairs of URIs, and then slowly backfill remaining migration changes over days or weeks. The need for object migration is so high that the resource cost is well worth it, and after the initial period after deployment where all the migrations until the present need to be processed, account migrations are relatively rare (i.e. Mastodon has a 30-day cooldown).

Compatibility

Instances that support object migration should remain interoperable with instances that do not, and that should be achievable by keeping a map from prior to current object IDs and transforming references to migrated objects when processing activities from non-migratory instances. Some inconsistency in network state is inevitable when implementing new behaviors, but this FEP shouldn't introduce new baffling complexities to network state: the current behavior of objects on accounts that have migrated and are no longer active is that they are inert - so additional replies can be created but they won't be received, and so on. Any change that introduces even partial object migration is a net-positive for network coherence.

Security

The heavy-lifting of secure object migrations is already handled by the existing account Move specs, which establish a trusted means of declaring that one actor is now another actor. This FEP should not introduce new risks for account takeover that are not already inherent in the existing account Move specs. Account Move activities must always be validated by an integrity proof by the source actor, accordingly attempts at account hijacking or spurious attribution must be rejected. However, since there is no mechanism for forcing key expiration or rotation, and thus a compromised secret key may be used to initiate a hostile account migration using an actor export, the Migrations from Exported Collections section is left as an optional recommendation subject to more formal specification in the future.

Importing an external backup introduces the possibility of object forgery. Assuming a valid account Move and proper signing and validation, this is actually not a problem: uploading an export where the author edits all their posts or adds new ones is effectively the same as them batch-posting a bunch of back-dated posts. This FEP does not provide for modifying objects which the target actor does not own, and the target actor may do whatever they please with their objects. 3rd-party instances should be responsible for notifying 3rd-party accounts of changes to objects that they have interacted with, as is done with updates.

Ingesting objects from collections hosted by a hostile source instance poses a larger problem: implementations may want to consider some interface that displays changes made to objects during collection ingestion and allows the target actor to reject them. Uploading some prior export of objects should override the versions of objects served by the source instance, as those should reflect the will of the actor more directly. This is another implementation detail that will require expansion and revision from experiences during implementation.

The author notes they are not a security expert and this FEP and all implementations should be independently audited.

Privacy

No new access routes should need to be established, as all operations use existing Collection mechanisms which must support filtering for object access already. the migration collection should not be an information leak (e.g. by indicating that some private objects exist by referring to their id, which may have more information beyond mere existence, as is the case with the snowflake id scheme used by Mastodon) and only contain information that is already publicly enumerable via public collections on the source actor.

Supplementary Information

  • fep-1580.jsonld - The JSON-LD context document to be included in the @context statement of actors on implementing instances.
  • migration.yaml - A linkml schema that provides a concrete form of the ActivityPub/ActivityStreams extensions described in the text.
  • migration.owl.ttl - An OWL schema generated from the LinkML schema

Acknowledgements

Thank you to those that helped provide feedback and recommendations for this FEP:

(in alphabetical order by handle, please feel free to make a PR to add yourself if you helped!) - ansuz - Advice from cryptography POV - apropos - Clarifying questions - silverpill - Suggestions re: clarity and use of ActivityStreams - trwnh - Help with JSON-LD, critical feedback.

References

  • Christine Lemmer-Webber, Jessica Tallon, Erin Shepherd, Amy Guy, Evan Prodromou, ActivityPub, 2018

CC0 1.0 Universal (CC0 1.0) Public Domain Dedication

To the extent possible under law, the authors of this Fediverse Enhancement Proposal have waived all copyright and related or neighboring rights to this work.

Footnotes


  1. The problem of abusive content being created on an instance is not unique to migrations, and most software implementing ActivityPub has means of creating abusive content rapidly (through API requests and automation) and outside the normal visibility of moderation (with direct messages or other scoped visibility settings). While the problem is not unique, this section attempts to address some new potentials for abuse vectors that may exist given prior implementations of actor moves that did not consider migrating objects along with the actor. 

  2. The description of alsoKnownAs cautions about treatment as an equivalence relationship, however the combination of a movedTo from the source Actor and an inverse alsoKnownAs property from the target Actor does indicate an equivalence relationship where the source Actor is inactive and has become the target Actor. copiedTo does not necessarily indicate an equivalence, as according to the DID spec, linked Actors that are still in use can be used for different purposes. Accordingly we do not consider copiedTo Actor redirects in this FEP. 

  3. Members of as:audience, as:to, as:bto, as:cc, as:bcc, and any other properties used for scoping visibility or delivery. 

  4. "Ownership" in this document is defined according to FEP-fe34: Ownership 

  5. the CURIE for the collection is, maybe unhelpfully, migration:migration to refer to https://w3id.org/fep/1580/migration 

  6. "Push" mode migration, as defined in FEP-7628 

  7. This is possible since the target instance must enumerate all collections linked from an actor document if a specific collection of objects to migrate is not provided by the Offer[Move] handshake. 

  8. Until the next heading. 

  9. The inclusion of an empty migration collection is to differentiate "a migration that migrated no objects" from "an instance that doesn't implement this FEP," which would be ambiguous if migration was omitted.