LOLA is a proposal for live online account portability between two ActivityPub servers at the request of a user. The goal is to allow the user to pursue the following workflow:

The approach taken in this document is a set of normatively-described resources, along with a non-normatively described over-arching process. The descriptions of user flows in this document are thus not normative - they are both a way of breaking down the content in this doc and a way of guiding implementors towards a process that should work for moving accounts. No shared state or explicit signaling between servers is implied by the references to "phases" as perceived by the end-user.

Some of the pieces required to complete this workflow do not need to described as protocol or schema in order to be interoperable. For example, the user can interact with an ActivityPub server using its Web pages for account management; for these tasks we need to describe what the server ought to be able to do, not how the user can ask it what to do. This specification uses the language of a user communicating intent to a server, and whether this intent is communicated via a Web page or some other client<>server protocol is out of scope of this document.

The server-to-server communications involved in this workflow do need to be reliable (URLs, messages and schemas) in order to be interoperable, so those sections are normative.

This document is an individual submission to the SocialWeb Incubator Community Group, which has formed a task force to focus on the work of data portability. Interested parties should join the public-swicg@w3.org mailing list to discuss.

This document is compatible with the ActivityPub Data Portability document, which describes a way to perform an offline account migration.

Approach

The technical approach proposed for LOLA is, at a high level:

Use cases

The primary user need to solve with LOLA is to allow Fediverse users to move their accounts in response to moderation and defederation decisions. The connections between servers and the communities on servers form a structure that has close connections, looser connections, and explicit blocks, allowing many different types of communities to co-exist. That does put a lot of pressure on users to be able to move their accounts (due to moderation decisions they disagree with, because of server defederation, or just to move closer to an affinity group). A user making such an account move will sometimes want to copy their previously posted content to their new location, sometimes follow the same people, and sometimes notify their followers so their followers can resubscribe in a new location. Servers implementing LOLA can provide these choices to users.

Several secondary use cases are enabled by this approach. It allows accounts to be copied and NOT moved which is useful for testing of all kinds. The mechanisms are independent of each other (beyond assuming the account migration authorization token), thus allowing reuse. For instance, the mechanisms for notify/redirect can also be used after a user moves to a new location using a different approach.

Some use cases are not covered by this approach. An offline server cannot participate in server-to-server portability mechanisms. Servers may also be unwilling or unable to communicate directly in any way. If the unwillingness to communicate is part of ‘defederating’ another server, we recommend that account portability be an exception to the defederation logic, as moving accounts is an important part of helping defederation work well for more people.

The Migration User Stories FEP has a number of use cases that can be solved with LOLA, as well as some that can't. Those intended to be addressed:

Overview

This overview covers high-level flow, trust decisions, and some architectural decision discussion.

Account migration interactions, high-level

The interactions described in this document can be separated into three informal phases.

In the first phase, discovery and authorization features allow the destination server to find out where to guide the user to authorize the destination server to the source server for an account migration.

  1. A user typically forms a desire to move to a specific ActivityPub server, the destination. They set up an account there. The name or identity (e.g email address or login) of the account may have no relationship to their old account.
  2. The destination server offers some way (out of scope) for the user to communicate intent for data to be copied in from another account.
  3. The user communicates the source for this data to be copied by providing a ActivityPub Actor ID or server domain.
  4. The destination server performs interoperability discovery to checks whether this specification is supported and what URL to use to initiate authorization. If successful, it finds an OAuth endpoint that has been advertised to have the appropriate scope for account migration.
  5. The destination server redirects the user’s browser to this OAuth endpoint, with information to tell the source server which access scope it will need.
  6. The user, assuming approval, shows their own authorization and authorizes the destination server to the source server.
  7. The source server redirects the user to the destination server with an access token.

Note that the mechanism for the user communicating intent to the destination server is not specified in detail in this document. It could be Web forms that do not need to be standardized in protocol documents, or client/server protocol mechanisms that are out of scope of this specification.

In the second phase, fetching and saving content, the destination uses its new access token in requests to the source server to authenticate its requests. The destination makes requests to existing and new data endpoints to fetch data and content. Because it is using an access token, the destination server is able to access private posts and metadata that are not normally accessible to 3rd party requesters

The final phase, notifications and redirects followup, happens asynchronously.

In this document, the interoperability of redirects and notifications are considered, but the way the user communicates their intent to setup a redirect is out of scope.

Trust decisions

The interactions above assumed a number of trust decisions happening invisibly. Let's expose those and show how they can be made explicitly, even though decisions are probably enacted via automated moderation, manual moderation, or account management mechanisms, rather than via standardized protocol. We encourage implementations to consider these trust decision-making points and build in appropriate notifications and confirmations to users or admins.

Even when a trust decision results in a server blocking or canceling a data transfer request, this does not necessarily mean that the server is failing to be compliant with interoperability standards. Interoperability can be consistent with intentional failures or refusals. This specification will attempt to give some guidance about when and how such trust decisions can be made for better interoperability and transparency, leading to better user experiences. A transparently explained trust decision is a better user experience, even if it blocks a desired account transfer, than an inexplicably failing transfer.

Destination approves copy from source

The destination server’s administrators may need to make a trust decision before allowing the bulk content to be copied over.

Receiving a large amount of content can introduce risks of harmful content. An administrator may need to approve a request to migrate an account. This administrative choice can be made based on trusting the source server (reputations are nuanced in the Fediverse) or based on trusting the user requesting the account migration. Some approvals might be made automatically (e.g. from any server already in an allow list) and others might involve notifications and review.

Pre-approval could also be built into account approval flow - for example, an implementation could add a field to an account application form to list the account(s) that a new account would migrate older content from. If the user’s new account is approved, the user would have to return to the new server to complete the authorization flow to the source server.

Copied content may undergo moderation.

This specification emphatically allows automated or manual moderation processes. A server receiving data may put some or all posts into quarantine, flag or hide some content, or reject the new account data once it has been reviewed asynchronously.

There are a few “good-citizen”-type implementation considerations when moderating content. Since the user may delete/disable their original account in communications directly with the source server, it would surely be useful for them to know whether all of their content had been accepted on the destination before they do that. Timeliness and communication are important. We don’t make any recommendations for implementations how to do this, as many options are available.

Source approves sending to destination

Sometimes the server sending information has a trust decision to make. ActivityPub might be used for syndicating private and even sensitive content as well as public content. When a server holding content receives the OAuth messages requesting access to trnasfer that content, the source server will have to decide whether and how to trust the destination to receive the private and sensitive content.

Content can be filtered at the source

Our default assumption is that authorizing access account portability means that the source server will allow an entire account to be copied for account migration, but this will not always be the case. A source server MAY provide filters or additional scoping as an advanced feature to users. A server might offer to filter or restrict based on permissions, tags, collections or types. We accept this reality, because limits can be applied in ways that aid interoperability, privacy, or other user goals. Ideally, the decision to filter or limit data transfer is a decision that the user makes, not one that the source server imposes on the user, because overprotective filtering can result in content lock-in.

Illustrative examples

Approaches for moving followers and following

The approaches suggested here for moving followers and following lists are entirely different.

Copying the list of Actors the user wishes to follow

When a user moves their account to a new location, they can choose to follow all the same Actors in the new location, although there’s no guarantee that all those Follow activities will be successful. This specification provides the format for how the source server lists all the Actors followed, but not what the destination server does with that information. New Follow activities from the new Actor are optional and not linked to old Follow activities. New Follow activities should not have older timestamps or breadcrumbs.

Other accounts following the moved Actor

Followers of an account that moves might get a notification, via a Move activity, that they have an opportunity to follow a new Actor and stop following the old. They might not get the notification, might ignore it, or might fail to follow the new account. In any case, any new successful Follow activities by previous followers are brand-new and have no visible or necessary relationship to old Follow activities.

Timing for copying content, copying Follows, and issuing Move

Timing and ordering of operations here is important to get right for a good user experience. A user will likely want to create a new account, check out the new account and its features, copy content over, and confirm that the content is copied before making any public moves. After trying this so far, it's possible for the user to be unsatisfied with the new location or the way the content is copied, and decide to drop the new account and never issue the Move.

If the user is satisfied with the new account and content copied over, the next step is likely to follow the same accounts they used to. Only after that step (and poking around to make sure that's working well) will a cautious user wish to finally advertise the move publicly and disable the old account.

To support this user journey and the time periods needed for the user to verify the results of their choices, destination servers that choose to implement this entire specification would do so in two stages. For example, a destination server that offers a Web page for moving content from another server would have both a button to copy content from another account, and another button to follow all the same accounts. (The option to issue the Move activity is already asynchronous as it must be done from the source server). In addition to supporting a good user journey for the complete account move, this separation also allows users to choose just to copy content, or just to copy a following list, which are both interesting use cases.

Approaches for moving shares and likes

Moving shares and likes can be done with a number of approaches that differ greatly in who does the work and what the impact is. This section explores the decision-making that led to the proposals made in this specification. When this specification is closer to being finalized, it might be better to limit this section to an illustrative example, cutting some of the decision-making tradeoffs and justifications.

In the ActivityPub specification, there is a user story with examples, summarized as follows (users renamed to Aurora, Brock and Cherry):

After the like/likes is propagated this way, there are several things that can happen.

Presently, in these cases, what should happen is not standardized [I think]. If a Person update happens for Aurora with a “movedTo” value, receiving servers could go looking for any reference including that Actor, and update their follows.

  1. Knowing that Aurora’s Activity also moved, Cherry's server could rewrite his “Like” activity to point to the new location. Presumably if this path is chosen, Cherry's server would do this with all users and all Likes that pointed to any moved thing.
  2. Aurora’s Activity could move, and Cherry's server could learn about the move and know that it has Like activities referencing the original article, and nevertheless leave them alone. They already propagated, their purpose is served.

Existing servers seem to follow the “leave them alone” model rather than the “rewrite” model. The way items propagate initially doesn’t have to define the way they are maintained. In any case, trying to follow the rewrite model would sure involve some misses, failing back to the leave them alone model.

For ease of implementation, we lean towards assuming the “leave them alone” model. Some of the reasons why:

In addition to assuming that 3rd party servers leave their likes alone in many cases, this specification recommends that the destination server add breadcrumbs to Activity and Like objects to record their prior identities. This is beneficial in showing a nuanced view of older data. If necessary, a server interested in validating a collection of “Likes” submitted to it, can mostly do so (except for accounts that have disappeared) if the object that the collection of “Likes” it is on maintains a history of past object IDs. Note that this still ALLOWS other servers to fix up their Likes but they can do so immediately, later, or never.

Data Degradation

Any account migration bears a risk of data loss or degradation. A server cannot reasonably accept content it does not know how to store or present. A roundtrip can result in data that is not in all ways, or even in important ways, identical to the data before it was migrated and migrated back. This specification is intended to facilitate best-effort account migration rather than perfect duplication, with the philosophy that the perfect can be the enemy of the good.

Some consequences of this philosophy:

Discovery

The goal of discovery is that once a user communciates their intent to a destination server to copy content from a source server, the destination server can discover how to get account access authorization and what features are available.

While communicating their intent to copy account data over, the user may provide a source server domain or a full Actor ID on the source server. Either a domain or Actor ID will be enough to contact the source server. If only the domain is provided, the Actor ID will be learned via the OAuth interaction. If the Actor ID is provided, OAuth is still required to authorize access.

OAuth Endpoint in Authorization Server Metadata (.well-known)

ActivityPub servers supporting this specification SHOULD include the URL of their portability authorization endpoint in their authorization server metadata document [RFC8414] using the activitypub_account_portability parameter.

This parameter name will need to be registered in the IANA "OAuth Authorization Server Metadata" registry established by [RFC8414].

OAuth Endpoint Discovery via Actor object

ActivityPub servers supporting this specification MUST provide the URL for their portability authorization endpoint in Actor objects, using the "accountPortabilityOauth" field.

Example Actor object

  {
    "@context": [
        "https://www.w3.org/ns/activitystreams",
        "https://purl.archive.org/socialweb/blocked"
    ],
    "id": "https://oakfrost.example.com/brock",
    "type": "Person",
    "name": "Brock Oakfrost",
    "accountPortabilityOauth": "https://example.com/oauth2/porting-access-endpoint",
    "inbox": "https://oakfrost.example.com/brock/inbox"
  }
      

Role of destination server in discovery

If the destination server finds the account portability OAuth endpoint via the [RFC8414] ".well-known" approach, it MUST gain authorization and use its account migration authorization token when doing feature discovery.

If the destination server requests the Actor object before gaining authorization, it MUST request the Actor object again using its account migration authorization token to do feature discovery.

Feature Discovery

Feature discovery is bootstrapped by OAuth authorization, in case the source server does not want to advertise all features to all requestors of the Actor object.

The destination server MUST use the account migration authorization token when requesting the Actor object to find out which URLs the source server offers to access the data that will be copied during an account migration. The source server MAY EITHER verify the authorization token and return the discovery URLs, OR skip verifying the authorization token and always return the discovery URLs.

Example Actor object in response with account migration authorization token included in request

{
    "@context": [
        "https://www.w3.org/ns/activitystreams",
        "https://purl.archive.org/socialweb/blocked"
    ],
    "id": "https://oakfrost.example.com/brock",
    "type": "Person",
    "name": "Brock Oakfrost",
    "accountPortabilityOauth": "https://example.com/oauth2/porting-access-endpoint",
    "inbox": "https://oakfrost.example.com/brock/inbox",
    "outbox": "https://oakfrost.example.com/brock/outbox",
    "content": "https://oakfrost.example.com/brock/content",
    "following": "https://oakfrost.example.com/brock/following",
    "followers": "https://oakfrost.example.com/brock/followers",
    "liked": "https://oakfrost.example.com/brock/liked",
    "blocked": "https://oakfrost.example.com/brock/blocked",
    "migration": "https://oakfrost.example.com/brock/outbox"
}

Notes

Authorization

This specification is compatible with and independent of the OAuth 2.0 Profile for the ActivityPub API specification. A server would be able to implement this specification and yet not use OAuth for user authentication, although the author thinks it would be best to support OAuth for both.

Servers supporting this specification MUST support this OAuth mechanism to grant authorization. However, a destination server could acquire an account migration authorization token using some mechanism not defined in this specification, and it might be possible use that in the requests involved in fetching data (next phase).

The OAuth interaction defined in this specification takes place over HTTP, with messages passing between the destination server and a user's Web browser, between the user's Web browser and the source server, and finally between the destination server and source server. The interaction can involve multiple back-and-forths between the user and the source server to re-authenticate, or to make choices relative to the access that could be granted.

The Oauth endpoint advertised using the two discovery mechanisms above MUST support the 'activitypub_account_portability' scope. This scope MUST be limited to an account - if there is more than one account on the source server, the source server MUST NOT allow access to any other accounts than the one granted, by requestors using the account migration authorization token.

Additional scopes for related use cases (e.g. backup, mirroring) can be defined in separate specifications.

Destination directs to source server

The destination server constructs a URL to direct the user's Web browser with the following parameters:

Source server verifies

Upon receiving the Oauth authorization request, the source server has a number of things to verify or decide.

Source server response

If authorization is approved, the source server redirects the user back to the destination server with an authorization code with parameters:

It is possible for the user to provide one Actor ID to the destination server, and then for the destination server to receive a different Actor ID in this response. The destination server MAY confirm this with the user but MUST use the Actor ID provided with the authorization code rather than the original one the user communicated.

Obtaining access token

An access token is finally obtained by destination server asking the source server as per 4.1.3 and 4.1.4 in [RFC6749]. This is the token used in the HTTP Authorization header as described in section 7.1 of RFC6749. This header MUST be included in feature discovery and data access requests.

Fetching Data

This section makes requirements for how the source server makes data and content avaialble to be fetched, and requirements for how the destination server makes requests.

Authorization

The destination MUST fetch data using HTTPS, not HTTP. It MUST provide the account migration authorization token in requests even when it believes the requests are for public content, in the following way:

Authorization: Bearer <access_token>

The source server SHOULD enable fetching data even if the account has been suspended in some way. For example, even if the source server is not responding to general requests for posts for a suspended account, it should treat account migration requests with the account migration authorization token as bypassing the suspension.

Migration Outbox

The destination server MUST use the migration outbox in lieu of the regular outbox if the migration outbox URL is different. This allows the source server some flexibility that may be important, in how it fetches and presents data for account migration differently than for regular use.

Content Collection

The source server SHOULD include these object types in the content collection:

The source server MUST NOT include content wrapper or content modification activities in the content collection, including Create, Update and Delete activities. The end result of creates, updates and deletes is what we're aiming to see here.

The source server should include for each object type as appropriate:

If the source server has has ‘likes’ information on objects, it should include that collection in each object as per https://www.w3.org/TR/activitypub/#likes. If the source server has ‘shares’ information, that is included as per https://www.w3.org/TR/activitypub/#shares. The source server MAY limit the size of particularly large likes and shares collections, which would mean that only some likes and shares would be copied if the destination tries to copy them at all; however, this behavior is NOT RECOMMENDED until there is some specified way to show that results are limited or a way to get further results.

Open Issues: Is there a way to negotiate what extended features are recognized and whether the source should downgrade structured info? Is there already a way to request posts in one format or another?

Actor Information

The Following collection as per https://www.w3.org/TR/activitypub/#following SHOULD be provided on the Actor object when accessed with the account migration authorization token.

If the source server does blocking, the personal block list SHOULD be fetchable at the URL advertised on the Actor object, as per https://codeberg.org/fediverse/fep/src/branch/main/fep/c648/fep-c648.md

Destinations are not required to fetch and reconstruct Following and Blocked data, as it may not be relevant depending on the use case. If the destination does fetch and use this data, it’s probably good to offer it separately from copying content.

The destination SHOULD copy the “Liked” collection on the Actor, which the source SHOULD provide if there is an account migration authorization token. No changes should need to be made and dates and references SHOULD not be made.

A destination server MAY copy additional Actor information if it’s not overwriting choices already made by the user. For example, if a new account on a destination does not have an avatar, and the user then chooses to migrate content or follows from another location that does have an avatar, it would be great to copy the avatar too.

Other Collections

Generally, other collections MAY be made available to destination servers presenting an account migration authorization token, and SHOULD be made available in a similar way, with the collection URL available in the Actor object. The destination server then gets to decide if it knows what to do with a given collection. For example, a specification for group membership (which would be great...) can define a collection with a URL listed on the Actor object, and state what the behavior should be during account migration. Without knowing exactly what the data is in advance, it's hard to make assumptions about behavior.

Not Fetched

Because we assume that a new account was setup on the destination server (and approved, if necessary) before copying content over, the following items should not be saved as copies by the destination:

Like, Follow and Block Activities

Because the destination can simply copy the “Liked”, “Following” and “Blocked” collections on the Actor, it need not copy all these activities. The source server MAY omit these types of activity when providing a migration outbox. The destination server can copy Like activity along with content, and Follow and Block activity in a separate operation when the user is ready.

Change Type Activities

Add, Update, Undo, Remove, Tombstone and Delete activities should not be copied. Rather than propagate these, the results of changes SHOULD be provided by the source server.

For example, if a server supports first a “Like” Activity then an “Undo” activity which reverse the Like, the server SHOULD NOT provide either of these in the migration outbox. The source server can filter all Likes out anyway as this information is easier to process from the Actor’s “Likes” collection, so it can automatically filter out all Undo events linked to Like events as those should not be represented in the current “Likes” collection.

Another example, if a server posts a Note then replaces that with a Tombstone activity, the Tombstone activity SHOULD be omitted by the source and SHOULD NOT be copied by the destination.

This choice is made because the purpose of account migration is not to provide a perfect replayable copy of all activity, but to provide a similar end result, and shooting for greater detail and replayability risks making the effort too effortful to be undertaken. This is an open issue and we need broader conversation on the implications of this intent.

Other Activity Types

The following activity types from Activity Vocabulary may be copied or ignored individually or en masse.

Flag activities should be ignored (by the destination) or omitted (by the source). Flag activities are more relevant when the content that was flagged was originally posted, and not so relevant later.

Ignore activities SHOULD be copied if the server is capable of honoring an Ignore activity as it appears. If the server does not handle Ignores, it SHOULD NOT copy it.

Join and Leave activity migration are not yet specified as Group collection migration is not yet specified and they seem to interact.

Question activities SHOULD be copied but the destination MAY close them when copying if they are not closed.

Load Management During Fetdching of Content

The source server MAY rate limit requests by sending a 429 Too Many Requests response as defined in RFC6585, with a Retry-After header. If the destination receives a `429` response status code, it SHOULD respect the `Retry-After` header and resume its requests after the chosen delay.

The rest of this section is informative.

Copying all the data from an account can be time consuming and consume network bandwidth and server resources. A source server that announces it is going to shut down is likely to get many transfer requests under way. Administrators are advised to consider the possibility that other servers might then have excess traffic.

Source servers have a number of tools and mechanisms to use to keep load manageable.

The destination server has more state to maintain to keep track of what has been already copied and what remains to be fetched, but it is also in full control of the pace of requests. A destination server could risk becoming overwhelmed by many users requesting that it transfer content, and thus the number of data-transfer jobs that the destination could have going at once. To throttle this, he destination server always has the power to reject user requests for new data transfer jobs by refusing the user when the user or client interaction makes the request, and explaining the reason for not allowing the data transfer to be started at this time.

Saving Content

This section makes requirements of the destionation server for how to save the content that it has fetched, for a good experience for the user moving their account as well as for other stakeholders, both followers and other content consumers.

Saving Objects

Object IDs

As the destination server copies Activity and Like objects, it MUST create or choose a new context-appropriate Object ID for each object. Destination servers that use numeric IDs or GUIDs can generate them in a way that seems appropriate. Destination servers that use dates or slugs in object IDs can pull information necessary to create those object IDs out of object metadata. Although the destination server CAN use part of the object's prior ID it doesn't have to, and since it is making a copy of an object that still exists elsewhere it can't use the exact same object ID.

Actor ID

The actor ID of an object MAY change or be kept the same. The decision to change the actor ID of the object can depend on context.

Type, and adding to Outbox

A destination server MAY post some or all migrated content to the outbox. This specification defines a new Activity type, "Copy", to help 3rd parties make decisions about whether to notify or take other action based on seeing the activity. For 3rd party backward compatibility, the "Create" Activity SHOULD be used along with the "Copy" activity:

  "type": ["Create", "Copy"]
      

Note objects MAY be presented directly in the destination outbox after migration. TBD - we need to confirm if this avoids triggering UX notifications on 3rd party servers.

Content and Type values MAY change. Some supporting examples:

“Likes” of an Object

If the destination is offering an account migration affordance and not some other use case, the destination SHOULD copy the “Likes” collection on the object. No changes should need to be made.

Object “replies” and “inReplyTo”

The server SHOULD copy ‘replies’ collection and ‘inReplyTo’ values (specified in Activity Vocabulary).

To

The destination SHOULD keep the ‘to’ value of an object as-is. Although the ‘to’ list may no longer be in existence, it is an accurate name for where it was originally distributed. This ought to be consistent with the way current servers maintain the ‘to’ list: even when an Activity is not moved, after time its ‘to’ list may reference accounts or collections that no longer exist, and that’s OK.

Published

The destination SHOULD keep the ‘published’ timestamp rather than rewrite it. It helps consistency to understand that at the time it was published originally, the ‘to’ list had a certain meaning.

Adding Breadcrumbs

As the destination server copies Activity and Like objects, it SHOULD add breadcrumbs. It SHOULD preserve existing breadcrumbs because it’s nice to do so. Breadcrumbs should be pushed tothe top (or beginning) of a most-recently-first list in the ‘previously’ property. The 'previously' attribute is defined in this specification as a multi-valued property, as follows:

URI:TBD
Notes:One or more actor/ID pairs
Domain:Activity
Range:Object | Link

Example Article moved from "lemongrove" to "newsite" to "thirdhome":

"type": "Article",
"id": "https://thirdhome.example/meadowvine/posts/xyz",
"previously": [
  { 
    "actor": "https://newsite.example.org/aurora/",
    "id": "https://newsite.example.org/aurora/items/02751cab-..."
  },
  { 
    "actor": "https://lemongrove.example.co.uk",
    "id": "https://lemongrove.example.co.uk/2016/05/minimal-activitypub"
  }
]
      

The `actor` value SHOULD be included in each breadcrumb, because there is no guarantee that a 3rd party can derive the `actor` value from an `id` value. The `actor` value is needed in order to validate the previous `id` value before trusting it. Because the destination server simply attests to the `previously` value, it cannot be entirely trusted without cryptographic and/or third-party verification. See the Third-Party section for requirements for that verification.

Motivation of breadcrumbs

This section was added to help readers consider the tradeoffs we must make in early design decisions for this specification. This section will be deleted in some future version of the specification.

Breadcrumbs are intended to "solve" the problem of an object that has been liked moving to a new location, while balancing tradeoffs such as not overwhelming a network through massive amounts of activity triggered by an Actor's move. To expand on the problem, a few use cases follow:

If Aurora's original Actor's `movedTo` value can be verified, breadcrumbs on the destination can solve the first three use cases. Good caching of moved Actor information could solve the fourth use case much of the time, but would not be guaranteed. Other solutions could be designed, such as client-side signing or witness services. In the fourth use case, when an ActivityPub agent wants to use information in breadcrumbs but cannot validate them, safe behavior may depend on the reason they want to validate the breadcrumb.

NOTE: Showing prior IDs of objects seems like a useful feature in general. Servers implementing this spec or just this feature MAY utilize breadcrumbs to show object ID changes due to domain name changes, Actor name changes, and other ways an object may change after creation, as well as due to account moves.

Keeping unrecognized attributes

Unrecognized attributes in objects should probably not be kept.

Example Copied Object

An activity originating from a moved account with a new Object ID and breadcrumbs may look like this when fetched from a new content collection location (without an outbox Activity wrapper). Note the ID is changed and is dereferencable at the new site, but the ‘to’ and ‘published’ values are historical.


{
  "@context": ["https://www.w3.org/ns/activitystreams",
               {"@language": "en"}],
  "id": "https://newsite.example.org/aurora/items/02751cab-7dd7-416b-905b-...",
  "previously": [
    { "actor": "https://lemongrove.example.co.uk",
      "id": "https://lemongrove.example.co.uk/2016/05/minimal-activitypub"
    }
  ],
  "attributedTo": "https://newsite.example.org/aurora/"
  "to": ["https://lemongrove.example.co.uk/followers"],
  "type": "Article",
  "published": "2014-12-12T12:12:12Z",
  "content": "<div>Article is in here... </div>",
}
      

Example Copied Object in Outbox

If the destination server chooses to place Create activities in the new outbox for some or all content items:

{
  "@context": ["https://www.w3.org/ns/activitystreams",
  "actor": "https://newsite.example.org/aurora",
  "id": "https://newsite.example.org/aurora/items/e2495916-877c-4428-8ada-c199508ad855",
  "to": [...],
  "type": ["Copy", "Create"]

  {
    "@context": ["https://www.w3.org/ns/activitystreams",
                 {"@language": "en"}],
    "id": "https://newsite.example.org/aurora/items/02751cab-7dd7-416b-905b-...",
    "previously": ["https://lemongrove.example.co.uk/2016/05/minimal-activitypub"],
    "attributedTo": "https://newsite.example.org/aurora/"
    "to": ["https://lemongrove.example.co.uk/followers"],
    "type": "Article",
    "published": "2014-12-12T12:12:12Z",
    "content": "<div>Article is in here... </div>",
  }
}
      

A destination server might also reference the copied article rather than wrap it:

{
  "@context": ["https://www.w3.org/ns/activitystreams",
  "actor": "https://newsite.example.org/aurora",
  "id": "https://newsite.example.org/aurora/items/e2495916-877c-4428-8ada-c199508ad855",
  "to": [...],
  "type": ["Copy", "Create"]
  "object": "https://newsite.example.org/aurora/items/02751cab-7dd7-416b-905b-...",
  }
}
      

Example Copied Like

A like originating from a moved account may look like this when fetched - note the actor and ID are both dereferenceable for the new server, and the ‘previously’ value shows older IDs. Note also that the Like has some old and possibly obsolete 'to' addresses. The Like was originally distributed to Cherry's followers when Cherry was on mistywing.example.org, not to Cherry's followers at newserver.example.org, and the 'to' addresses do not have to be rewritten to try to translate to different follower lists at different locations.


{
  "@context": ["https://www.w3.org/ns/activitystreams",
               {"@language": "en"}],
  "type": "Like",
  "actor": "https://newserver.example.org/cherry/",
  "id": "https://newserver.example.org/cherry/like/1972"
  "previously": ["https://mistywing.example.org/cherry/like/228"]
  "name": "Cherry liked 'Minimal ActivityPub update client'",
  "object": "https://lemongrove.example.co.uk/2016/05/minimal-activitypub",
  "published": "2014-12-14T14:14:14Z",
  "to": ["https://lemongrove.example.co.uk/#aurora",
         "https://mistywing.example.org/followers",
         "https://lemongrove.example.co.uk/followers/"],
  "cc": "https://oakfrost.example.com/brock"
}

      

It is NOT strictly required of servers to add or maintain “previously” lists. A user may wish to move content without associating with an old address for many reasons.

Sending Notifications While Saving

Destination servers SHOULD NOT normally send notifications to followers, as they save these objects with copied content. Our assumed model for account migrations is that most likely the account’s original followers already saw those posts and messages, and will not be best accommodated by receiving additional unnecessary notifications. It’s also possible at this stage that the user will copy content over and then change their mind about the whole account migration and stay with their old server.

That said, there may be reasons that the destination chooses to send some notifications to some other actors. For example, there may be moderation processes that require some notifications, or the user may have explicitly selected to notify some actors, a group or all followers if that affordance was offered.

Inferring and Setting Permissions

It would be really great to have interoperable schemas for permissions. In the meantime, even without explicit permissions, a destination can make some pretty good choices.

First, a follower list is often a guide to what content should be readable by whom. It seems like it would fit most users’ intended use cases if content addressed to their followers on their old server is now readable by their followers on a new server, even if the follower lists are not identical.

A destination may allow the user to communicate intent for permissions before or after an account migration. If getting the user intent happens afterward, it probably is best to default to private and then change the content to being more available on user confirmation.

Load Management During Copying

Copying all the data from an account can be time consuming and consume network bandwidth and server resources. A source server that announces it is going to shut down is likely to get many transfer requests under way. Administrators are advised to consider the possibility that other servers might then have excess traffic. With the exception of the 429 status code and the need for the destination to understand that status code, this section is informative.

Source servers have a number of tools and mechanisms to use to keep load manageable.

If the destination receives a `429` response status code, it SHOULD respect the `Retry-After` header and resume its requests after such delay.

The destination server has more state to maintain to keep track of what has been already copied and what remains to be fetched, but it is also in full control of the pace of requests. A destination server could risk becoming overwhelmed by many users requesting that it transfer content, and thus the number of data-transfer jobs that the destination could have going at once, so the destination server always has the power to reject user requests for new data transfer jobs by refusing the user when the user or client interaction makes the request.

Followup Work

Post-Copy Destination Jobs

After attempting to copy a user’s account it is the destination’s responsibility to use its own UI and notification channels to notify the user of success/failure and provide error/warning detail if any.

Objects where the source format could not be understood and was dropped, keeping only the final content, may be less editable in the future, so, while allowed, this may merit warning the user.

If a Following list was copied, the destination may issue Follow activities. The destination may also provide an option to NOT do this in case the user wishes to test out the account and content before committing to issuing Follow activities from this new location.

Reporting failed attempts back to the user is also up to the destination server using its own UI and notification channels. A destination MAY fail an account transfer for a number of reasons (e.g. quota reached, access lost) and SHOULD NOT keep an account transfer job going forever, although appropriate timeouts are not specified here.

Followup Requirements on Source Server

Notifying Followers

The source server SHOULD offer UI for the user to choose to notify followers of a new location. This is done via the Move Activity type, defined in ActivityStreams Vocabulary and described more for use to move Actors in the Move Actor FEP.

Load management: it is possible that the source server issuing Move activities could cause many follower agents to issue a great deal of data fetching requests to the destination. A malicious source server could issue false Move Activities in an attempt to overload the alleged destination server with follow requests and other requests. More discussion of this threat is welcome, but so far this seems self-limiting because a malicious source server would have to have followers to send to a false destination, and would presumably lose its followers and run out of them during this process.

Open Issue: Can the destination server send out Move activity if necessary? This document proposes that the answer should be NO. It cannot easily be trusted, because a malicious actor could issue a Move activity with the 'object' value an Actor ID they have no access to. If we can't count on the source server sending the Move Activity because it is not cooperating or not able to, then we also can't count on the source server verifying a destination-issued Move Activity. Until a more robust verification mechanism is designed (e.g. user-signed Move Activity with a previously known or proven key) then we might as well not issue Move Activities on the destination. See the Third-Party section for consequences for other agents.

Hosting Moved Actors

The source server SHOULD offer UI for the user to choose to mark their Actor as moved to the new server. This is accomplished via the `movedTo` property as explained and described in Data Portability in ActivityPub The source server MUST offer an affordance (such as a dedicated UI exposed to the user) to delete their content while still hosting just the Actor with the `movedTo` property. The source server SHOULD attempt to maintain this information for at least a year, even if the account is otherwise shut down. The source server might not maintain the `movedTo` property on the correct Actor URL for a number of legitimate reasons (including change of domain which changes the URL, a request from the account holder to stop maintaining the `movedTo` value, or a request from the account holder to completely delete the Actor).

The movedTo property is defined in FEP 7628 and its associated JSON-LD definition.

Example:


{
  "@context": [
      "https://www.w3.org/ns/activitystreams",
  ],
  "id": ""https://lemongrove.example.co.uk"",
  "type": "Person",
  "name": "Aurora Lemongrove",
  "movedTo": "https://newsite.example.org/aurora/"
}
      

Hosting Redirects for Objects

After an Actor has been configured with a `movedTo` value, the source server might still receive third-party GET requests for Objects that don't exist any more, but the source server knows that the requested object once belonged with the Actor. How the source server knows this will be implementation dependent, but here are some possibilities:

Once the source server has figured out which Actor and what the appropriate ‘movedTo’ value is, it can now issue a HTTP 301 Moved Permanently response with a URL to the destination. This SHOULD be a URL the destination's Actor ID with an additional parameter "redirect_ap_obj", and the value of that parameter is the requested object ID in percent-encoding per [RFC3986] section 2.1:


<actor URL>?redirect_ap_obj=<percent-encoded source object ID>

      

When the 3rd-party follows the URL containing the destination and the object ID it requested, the destination MAY respond directly to this URL with the object or it MAY provide another Redirect response to point to the real new URL (or some other appropriate response such as an error). Since the destination server SHOULD be keeping breadcrumbs, it SHOULD be able to look up the object ID. If the destination server cannot identify the new object ID, it SHOULD return 404 Not Found.

Note that this solution is very friendly to 3rd parties that are not only unaware of this specification but also 3rd parties that are browsers or other HTTP agents.

Note that a user may move their accounts AGAIN within the timeframe that their first server is still serving redirects. It may be possible to fix up the ‘movedTo’ value on the first server, but it is also possible that the first server shows a ‘movedTo’ to a second server which shows a ‘movedTo’ to a third server. No 3rd-party is obliged to follow these links for any longer than they are inclined before giving up.

Third-Party ActivityPub Server Behavior

A server not involved in an account migration either as a source or destination is a third-party. Third-parties can be aware of this specification and can implement this section, in which case the conformance langage in this section becomes normative for that implementation.

Relying on 'previously' values

The value of the 'previously' field can be used for a number of use cases, including to find out if the current third-party Actor has 'liked' a moved object, if they have replied to it, or if it was previously muted/ignored. If a third-party makes use of the 'previously' value, which it is not obliged to do, this specification RECOMMENDS validating the information.

Validating the 'previously' field is necessary not for all URLs in it, but for any URL that would result in a change of behavior for the third-party (such as hiding a Object based on a URL in its 'previously' field). That URL is validated by the third-party checking the original Actor to make sure that it does have a 'movedTo' value pointing to the next Actor. If the next Actor is not the current provider of the 'previously' value, then either the chain must continue to be validated, or the third-party can give up and decide the URL is not validated.

These validation checks can fail not because they are wrong but because one of the Actors needed to validate is no longer online. If that is the case, a cache or oracle mapping MAY be used instead. See cache considerations below in "Handling Move Activity and 'movedTo' property".

If a `previously` value cannot be trusted, the expected behavior (though not REQUIRED) is that it be ignored but retained for future consumers. Ignoring a `previously` value reduces the situation to an existing common situation, where ActivityPub agents cannot be sure whether or not an Object has been copied from somewhere else or not.

Some sample decision factors to consider (informative) when deciding whether to make use of an unvalidated `previously` breadcrumb:

Following up a ‘Like’ of moved object

When a third-party server has a ‘Like’ activity pointing to a moved Activity, it need not take any action to fix up ‘Like’ links. However sometimes a user wants to dereference a Like and find the original article. When the user is browsing their Likes and the ActivityPub server is aware of it, the server can attempt to use the original location and object ID of the Liked activity to find the activity’s new location.

Open Issue: will the server even detect that the Like reference has moved? It may only present a link to the remote object and not ever try to dereference it.

When a user is browsing Likes via ActivityPub client or using another C2S protocol, a smart client may find itself in the same situation - the user just clicked on a Like and the thing liked has moved.

The agent requesting the Liked item may receive a generic redirect to an Actor, not an Object (this won’t be obvious until the referenced resource is fetched). If the agent finds an Actor, it may fetch a number of objects hoping to find one with a breadcrumb matching the original Like reference.

Open Issue: should the Like activity then be rewritten with the new location? Can a client do this - is there an affordance to update a Like?

Handling Move Activity and 'movedTo' property

Move Activity objects, if processed at all, MUST be checked to be sure that the sender is responsible for the Actor listed in the 'object' field, to prevent malicious unauthorized Move Activity objects from being issued by other parties. If the sender does not appear to be responsible, a third party MAY check the Actor object listed in the 'object' field to see if there is a matching 'movedTo' property. Move Activity objects that cannot be verified SHOULD be ignored.

Mappings of old Actor URLs to new locations listed in Move Activity objects or `movedTo` properties MAY be cached to make it more efficient to validate `previously` values on objects. A hypothetical cache might attempt to refresh the mapping if it is over 24 hours old, but, if it can't be refreshed, to maintain the cached value for much longer. This design would allow mistakes (such as a `movedTo` value that simply has the wrong URL, or a change of mind has occurred) to be corrected while still allowing longer-term validation of `previously` values even when the Actor's original server has stopped serving the Actor object.

Not In Scope

Interoperable formats for export/import are not in scope. The collections defined to provide migration data can lead smoothly to an interoperable export/import format that can share much code with support for this specification; however many of the recommendations for this specification are tied to its use for re-homing content in a best-effort server-to-server move. As an example, the destination server can skip over content it doesn't handle in an account transfer, whereas export/import use cases could have a different approach due to many differences in context.

Signatures are out of scope.

Nomadic identities (in particular, how to transfer the identity itself) are out of scope, though some recommendations accounting for the existence of nomadic identities have been made.

This is required for specifications that contain normative material.