mirror of
https://codeberg.org/fediverse/fep.git
synced 2026-08-06 12:16:12 +00:00
FEP-1580: Move Actor Objects with a migration Collection (#692)
I'm very tired and just want to open this as a draft for now and will come back tomorrow to finish off starting the discussion, editing the proposal, and confirming the schema. For now, this is a draft FEP for migrating account objects after FEP-7628 account Moves Discussion: https://socialhub.activitypub.rocks/t/fep-1580-move-actor-objects-with-a-migration-collection/8111 Reviewed-on: https://codeberg.org/fediverse/fep/pulls/692 Co-authored-by: sneakers-the-rat <sneakers-the-rat@protonmail.com> Co-committed-by: sneakers-the-rat <sneakers-the-rat@protonmail.com>
This commit is contained in:
committed by
silverpill
parent
630bb4d94f
commit
9cfe0fb1a3
@@ -0,0 +1,29 @@
|
||||
{
|
||||
"@context": {
|
||||
"xsd": "http://www.w3.org/2001/XMLSchema#",
|
||||
"actors": {
|
||||
"@type": "@id",
|
||||
"@id": "https://w3id.org/fep/1580#actors"
|
||||
},
|
||||
"migratedAt": {
|
||||
"@type": "xsd:dateTime",
|
||||
"@id": "https://w3id.org/fep/1580#migratedAt"
|
||||
},
|
||||
"migratedFrom": {
|
||||
"@type": "@id",
|
||||
"@id": "https://w3id.org/fep/1580#migratedFrom"
|
||||
},
|
||||
"migration": {
|
||||
"@type": "@id",
|
||||
"@id": "https://w3id.org/fep/1580#migration"
|
||||
},
|
||||
"migrationComplete": {
|
||||
"@type": "@id",
|
||||
"@id": "https://w3id.org/fep/1580#migrationComplete"
|
||||
},
|
||||
"moves": {
|
||||
"@type": "@id",
|
||||
"@id": "https://w3id.org/fep/1580#moves"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,621 @@
|
||||
---
|
||||
slug: "1580"
|
||||
authors: Jonny Saunders <j@nny.fyi>
|
||||
status: DRAFT
|
||||
dateReceived: 2025-10-09
|
||||
discussionsTo: https://socialhub.activitypub.rocks/t/fep-1580-move-actor-objects-with-a-migration-collection/8111
|
||||
type: implementation
|
||||
relatedFeps: FEP-7628, FEP-73cd, FEP-e965, FEP-888d, FEP-8b32, FEP-fe34
|
||||
---
|
||||
# FEP-1580: Move Actor Objects with a `migration` Collection
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
*(This section is non-normative)*
|
||||
|
||||
Prior FEPs ([FEP-7628](https://codeberg.org/fediverse/fep/src/branch/main/fep/7628/fep-7628.md), [FEP-E965](https://codeberg.org/fediverse/fep/src/branch/main/fep/e965/fep-e965.md)) 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 owned[^ownership] by that actor.
|
||||
|
||||
This FEP describes a mechanism of migrating objects owned by a moved Actor to the target instance using two `OrderedCollection`s 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 migration[^push-pull] 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 `Object`s 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](https://codeberg.org/fediverse/fep/src/branch/main/fep/ef61/fep-ef61.md)).
|
||||
|
||||
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 equivalent[^actor-equivalence] [`as:alsoKnownAs`](https://www.w3.org/TR/did-1.0/#dfn-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 `Collection`s 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](https://tools.ietf.org/html/rfc2119.html) 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](https://codeberg.org/fediverse/fep/src/branch/main/fep/7628/fep-7628.md) and [FEP-E965](https://codeberg.org/fediverse/fep/src/branch/main/fep/e965/fep-e965.md) 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 `Object`s 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](https://codeberg.org/fediverse/fep/src/branch/main/fep/7628/fep-7628.md#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](https://www.w3.org/TR/2010/NOTE-curie-20101216/) prefixes used in this document:
|
||||
- `migration`: `https://w3id.org/fep/1580/` - the namespace created by this FEP ([FEP-888d](https://codeberg.org/fediverse/fep/src/branch/main/fep/888d/fep-888d.md))
|
||||
- `as`: `https://www.w3.org/ns/activitystreams` - the ActivityStreams vocabulary
|
||||
- `sec`: `https://w3id.org/security#` - The Data Integrity vocabulary used by [FEP-8b32](https://codeberg.org/fediverse/fep/src/branch/main/fep/8b32/fep-8b32.md) 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](https://codeberg.org/fediverse/fep/src/branch/main/fep/7628/fep-7628.md)
|
||||
- [FEP-8b32](https://codeberg.org/fediverse/fep/src/branch/main/fep/8b32/fep-8b32.md) - 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](https://codeberg.org/fediverse/fep/src/branch/main/fep/7628/fep-7628.md), [FEP-E965](https://codeberg.org/fediverse/fep/src/branch/main/fep/e965/fep-e965.md) 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](#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 `migration`[^migration-uri] `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 audience[^audience].
|
||||
- `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](#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 empty[^why-empty].
|
||||
|
||||
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**
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "https://example.com/chuckTargetson",
|
||||
"type": "Person",
|
||||
"...": "...",
|
||||
"migration": "https://example.com/chuckTargetson/migration",
|
||||
"moves": "https://example.com/chuckTargetson/moves"
|
||||
}
|
||||
```
|
||||
|
||||
**`migration` OrderedCollection**
|
||||
|
||||
```json
|
||||
{
|
||||
"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**
|
||||
|
||||
```json
|
||||
{
|
||||
"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](https://codeberg.org/fediverse/fep/src/branch/main/fep/8b32/fep-8b32.md) 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](https://www.rfc-editor.org/rfc/rfc8785), [RDF canonicalization](https://www.w3.org/TR/rdf-canon/)) 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**
|
||||
|
||||
```json
|
||||
{
|
||||
"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**
|
||||
|
||||
```json
|
||||
{
|
||||
"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](#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](#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](https://www.ietf.org/archive/id/draft-polli-ratelimit-headers-02.html) and [response codes](https://www.rfc-editor.org/rfc/rfc6585#section-4) 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 here[^target-ingest].
|
||||
|
||||
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](#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](https://codeberg.org/fediverse/fep/src/commit/fc3166af73591cdefbfcba19a17f0039d3760e27/fep/73cd/fep-73cd.md) 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](#migration-routine)) 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:Link`s. 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 `Update`s 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 `Move`s. 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](#moderation)) motivating this section and its design.
|
||||
|
||||
Recall that a valid actor `Move` having already been emitted and accepted is a [precondition](#preconditions) of this spec. This section[^this-section] 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](https://codeberg.org/fediverse/fep/src/branch/main/fep/7628/fep-7628.md#linked-actors))
|
||||
- `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](#ingest).
|
||||
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](https://www.w3.org/TR/activitypub/#update-activity-inbox), 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**
|
||||
```json
|
||||
{
|
||||
"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**
|
||||
|
||||
```json
|
||||
{
|
||||
"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)
|
||||
|
||||
```json
|
||||
{
|
||||
"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](https://codeberg.org/fediverse/fep/src/branch/main/fep/ef61/fep-ef61.md) 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](https://www.w3.org/TR/activitypub/#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](https://codeberg.org/fediverse/fep/src/branch/main/fep/6fcd/fep-6fcd.md)
|
||||
|
||||
### 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 moderators[^abusive-content].
|
||||
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](#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](#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`](./fep-1580.jsonld) - The JSON-LD context document to be included in the `@context` statement of actors on implementing instances.
|
||||
- [`migration.yaml`](./migration.yaml) - A [linkml](https://linkml.io/) schema that provides a concrete form of the ActivityPub/ActivityStreams extensions described in the text.
|
||||
- [`migration.owl.ttl`](./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`](https://codeberg.org/ansuz) - Advice from cryptography POV
|
||||
- [`apropos`](https://codeberg.org/apropos) - Clarifying questions
|
||||
- [`silverpill`](https://codeberg.org/silverpill) - Suggestions re: clarity and use of ActivityStreams
|
||||
- [`trwnh`](https://codeberg.org/trwnh) - Help with JSON-LD, critical feedback.
|
||||
|
||||
## References
|
||||
|
||||
- Christine Lemmer-Webber, Jessica Tallon, Erin Shepherd, Amy Guy, Evan Prodromou, [ActivityPub], 2018
|
||||
|
||||
[ActivityPub]: https://www.w3.org/TR/activitypub/
|
||||
|
||||
## Copyright
|
||||
|
||||
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
|
||||
|
||||
[^abusive-content]: 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.
|
||||
[^actor-equivalence]: 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.
|
||||
[^audience]: Members of `as:audience`, `as:to`, `as:bto`, `as:cc`, `as:bcc`, and any other properties used for scoping visibility or delivery.
|
||||
[^ownership]: "Ownership" in this document is defined according to [FEP-fe34: Ownership](https://codeberg.org/fediverse/fep/src/branch/main/fep/fe34/fep-fe34.md#ownership)
|
||||
[^migration-uri]: the CURIE for the collection is, maybe unhelpfully, `migration:migration` to refer to `https://w3id.org/fep/1580/migration`
|
||||
[^push-pull]: "Push" mode migration, as defined in [FEP-7628](https://codeberg.org/fediverse/fep/src/branch/main/fep/7628/fep-7628.md)
|
||||
[^target-ingest]: 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.
|
||||
[^this-section]: Until the next heading.
|
||||
[^why-empty]: 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.
|
||||
Executable
+3
@@ -0,0 +1,3 @@
|
||||
#!/bin/bash
|
||||
|
||||
linkml generate owl migration.yaml --useuris > migration.owl.ttl
|
||||
@@ -0,0 +1,439 @@
|
||||
@prefix as: <https://www.w3.org/ns/activitystreams#> .
|
||||
@prefix dcterms: <http://purl.org/dc/terms/> .
|
||||
@prefix migration: <https://w3id.org/fep/1580/fep/> .
|
||||
@prefix owl: <http://www.w3.org/2002/07/owl#> .
|
||||
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
|
||||
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
|
||||
@prefix sec: <https://w3id.org/security#> .
|
||||
@prefix skos: <http://www.w3.org/2004/02/skos/core#> .
|
||||
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .
|
||||
|
||||
<https://w3id.org/fep/1580#.owl.ttl> a owl:Ontology ;
|
||||
rdfs:label "migration" ;
|
||||
dcterms:license "https://creativecommons.org/publicdomain/zero/1.0/" ;
|
||||
skos:definition """Migrating account objects using an `OrderedCollection` of `Move` objects.
|
||||
|
||||
All subclasses of activitystreams objects should be considered "partial" extensions of the class they inherit from:
|
||||
e.g. though the Actor only describes the slots relevant for this FEP,
|
||||
it should be considered as having all the other properties of the Actor object.
|
||||
|
||||
Slots/Properties are overridden in order to refine them,
|
||||
e.g. the `ActorMove` class refines the `object` and `target` properties to specify that their range
|
||||
must be an Actor.
|
||||
""" .
|
||||
|
||||
migration:ProposedMove a owl:Class ;
|
||||
rdfs:label "ProposedMove" ;
|
||||
rdfs:subClassOf [ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:attachment ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 0 ;
|
||||
owl:onProperty migration:attachment ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:object ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 1 ;
|
||||
owl:onProperty migration:object ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:ProposedMigrationCollection ;
|
||||
owl:onProperty migration:attachment ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:ActorMove ;
|
||||
owl:onProperty migration:object ] ;
|
||||
skos:definition """A proposed move sent from the source instance and addressed to the target instance's shared inbox, and NOT as:Public.
|
||||
It may include an attached collection of object URIs that are to be considered the set of items that the actor proposes to move with.
|
||||
""" ;
|
||||
skos:exactMatch as:Offer ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:TentativeAcceptMove a owl:Class ;
|
||||
rdfs:label "TentativeAcceptMove" ;
|
||||
rdfs:subClassOf [ a owl:Restriction ;
|
||||
owl:minCardinality 1 ;
|
||||
owl:onProperty migration:object ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:object ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 1 ;
|
||||
owl:onProperty migration:attachment ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:TentativeAcceptCollection ;
|
||||
owl:onProperty migration:attachment ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:attachment ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:ActorMove ;
|
||||
owl:onProperty migration:object ] ;
|
||||
skos:definition """After moderation, a response to a ProposedMove that includes some attachment of modifications to make to the proposed migration items
|
||||
""" ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:ActorCollection a owl:Class ;
|
||||
rdfs:label "ActorCollection" ;
|
||||
rdfs:subClassOf [ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:Actor ;
|
||||
owl:onProperty migration:orderedItems ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:orderedItems ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 0 ;
|
||||
owl:onProperty migration:orderedItems ] ;
|
||||
skos:exactMatch as:OrderedCollection ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:ObjectMove a owl:Class ;
|
||||
rdfs:label "ObjectMove" ;
|
||||
rdfs:subClassOf [ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:Object ;
|
||||
owl:onProperty migration:object ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 1 ;
|
||||
owl:onProperty migration:target ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:target ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:object ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 1 ;
|
||||
owl:onProperty migration:object ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:Object ;
|
||||
owl:onProperty migration:target ] ;
|
||||
skos:definition """A mapping between objects on the source server and the target server.
|
||||
Each property should be a URI reference, rather than an inlined object.
|
||||
""" ;
|
||||
skos:exactMatch as:Move ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:ProposedMigrationCollection a owl:Class ;
|
||||
rdfs:label "ProposedMigrationCollection" ;
|
||||
rdfs:subClassOf [ a owl:Restriction ;
|
||||
owl:minCardinality 0 ;
|
||||
owl:onProperty migration:items ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:Object ;
|
||||
owl:onProperty migration:items ] ;
|
||||
skos:exactMatch as:Collection ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:Reject a owl:Class ;
|
||||
rdfs:label "Reject" ;
|
||||
skos:exactMatch as:Reject ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:TentativeAcceptCollection a owl:Class ;
|
||||
rdfs:label "TentativeAcceptCollection" ;
|
||||
rdfs:subClassOf [ a owl:Restriction ;
|
||||
owl:allValuesFrom [ owl:unionOf ( migration:Reject migration:Update ) ] ;
|
||||
owl:onProperty migration:items ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 0 ;
|
||||
owl:onProperty migration:items ] ;
|
||||
skos:exactMatch as:Collection ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:Update a owl:Class ;
|
||||
rdfs:label "Update" ;
|
||||
skos:exactMatch as:Update ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:DataIntegrityProof a owl:Class ;
|
||||
rdfs:label "DataIntegrityProof" ;
|
||||
skos:exactMatch sec:DataIntegrityProof ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:ActorMove a owl:Class ;
|
||||
rdfs:label "ActorMove" ;
|
||||
rdfs:subClassOf [ a owl:Restriction ;
|
||||
owl:minCardinality 1 ;
|
||||
owl:onProperty migration:object ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:Actor ;
|
||||
owl:onProperty migration:target ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:object ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:proof ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:target ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 1 ;
|
||||
owl:onProperty migration:actor ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 1 ;
|
||||
owl:onProperty migration:proof ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:Actor ;
|
||||
owl:onProperty migration:object ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:Actor ;
|
||||
owl:onProperty migration:actor ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 1 ;
|
||||
owl:onProperty migration:target ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:actor ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:DataIntegrityProof ;
|
||||
owl:onProperty migration:proof ] ;
|
||||
skos:definition """A refinement of the base ActivityPub Move action that specifies an actor moving to a new id,
|
||||
as described in [fep-7628](https://codeberg.org/fediverse/fep/src/branch/main/fep/7628/fep-7628.md),
|
||||
with an accompanying [fep-8b32](https://codeberg.org/fediverse/fep/src/branch/main/fep/8b32/fep-8b32.md)
|
||||
integrity proof signed by the source (object) actor.
|
||||
""" ;
|
||||
skos:exactMatch as:Move ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:MigrationCollection a owl:Class ;
|
||||
rdfs:label "MigrationCollection" ;
|
||||
rdfs:subClassOf [ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:moves ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:MigrationCollectionPage ;
|
||||
owl:onProperty migration:first ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 1 ;
|
||||
owl:onProperty migration:first ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom xsd:boolean ;
|
||||
owl:onProperty migration:migrationComplete ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 1 ;
|
||||
owl:onProperty migration:migrationComplete ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:migrationComplete ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:first ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 0 ;
|
||||
owl:onProperty migration:moves ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:MoveCollection ;
|
||||
owl:onProperty migration:moves ] ;
|
||||
skos:definition """An ordered collection of objects to move from the source actor to the target actor.
|
||||
The collection must reference a `moves` Collection of signed Move activities
|
||||
that can be used for validation in the case the origin server is inactive/uncooperative.
|
||||
Items within pages should be ordered in reverse chronological order by when the target server
|
||||
has imported them to facilitate gradual migration by 3rd-party instances.
|
||||
""" ;
|
||||
skos:exactMatch as:OrderedCollection ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:MigrationCollectionPage a owl:Class ;
|
||||
rdfs:label "MigrationCollectionPage" ;
|
||||
rdfs:subClassOf [ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:partOf ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 0 ;
|
||||
owl:onProperty migration:prev ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 1 ;
|
||||
owl:onProperty migration:orderedItems ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:ObjectMove ;
|
||||
owl:onProperty migration:orderedItems ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:MigrationCollectionPage ;
|
||||
owl:onProperty migration:prev ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:MigrationCollectionPage ;
|
||||
owl:onProperty migration:next ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 1 ;
|
||||
owl:onProperty migration:partOf ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 0 ;
|
||||
owl:onProperty migration:next ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:MigrationCollection ;
|
||||
owl:onProperty migration:partOf ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:prev ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:next ] ;
|
||||
skos:exactMatch as:OrderedCollectionPage ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:MoveCollection a owl:Class ;
|
||||
rdfs:label "MoveCollection" ;
|
||||
rdfs:subClassOf [ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:actors ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:DataIntegrityProof ;
|
||||
owl:onProperty migration:proof ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:ActorCollection ;
|
||||
owl:onProperty migration:actors ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:proof ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 0 ;
|
||||
owl:onProperty migration:orderedItems ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:ActorMove ;
|
||||
owl:onProperty migration:orderedItems ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 0 ;
|
||||
owl:onProperty migration:actors ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 0 ;
|
||||
owl:onProperty migration:proof ] ;
|
||||
skos:definition """A collection of Actor Move actions signed by the source Actor.
|
||||
Move actions MUST be inlined and MUST NOT be paginated.
|
||||
""" ;
|
||||
skos:exactMatch as:OrderedCollection ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:Object a owl:Class ;
|
||||
rdfs:label "Object" ;
|
||||
rdfs:subClassOf [ a owl:Restriction ;
|
||||
owl:allValuesFrom xsd:anyURI ;
|
||||
owl:onProperty migration:migratedFrom ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 0 ;
|
||||
owl:onProperty migration:migratedAt ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:migratedAt ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom xsd:dateTime ;
|
||||
owl:onProperty migration:migratedAt ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:migratedFrom ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 0 ;
|
||||
owl:onProperty migration:migratedFrom ] ;
|
||||
skos:exactMatch as:Object ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:actor a owl:ObjectProperty ;
|
||||
rdfs:label "actor" ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:actors a owl:ObjectProperty ;
|
||||
rdfs:label "actors" ;
|
||||
skos:definition """A collection of inlined Actor objects that include public key used for validating signed objects.
|
||||
Source Actors that are not inlined in the signed `Move` activity must be present.
|
||||
""" ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:first a owl:ObjectProperty ;
|
||||
rdfs:label "first" ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:migratedAt a owl:DatatypeProperty ;
|
||||
rdfs:label "migratedAt" ;
|
||||
rdfs:range xsd:dateTime ;
|
||||
skos:definition """A timestamp indicating when an object was migrated.
|
||||
""" ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:migratedFrom a owl:DatatypeProperty ;
|
||||
rdfs:label "migratedFrom" ;
|
||||
rdfs:range xsd:anyURI ;
|
||||
skos:definition """The URI of the object that this object was migrated migrated from.
|
||||
""" ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:migration a owl:ObjectProperty ;
|
||||
rdfs:label "migration" ;
|
||||
rdfs:range migration:MigrationCollection ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:migrationComplete a owl:DatatypeProperty ;
|
||||
rdfs:label "migrationComplete" ;
|
||||
rdfs:range xsd:boolean ;
|
||||
skos:definition """A boolean that indicates whether the target instance has completed its migration ingestion process.
|
||||
""" ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:next a owl:ObjectProperty ;
|
||||
rdfs:label "next" ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:partOf a owl:ObjectProperty ;
|
||||
rdfs:label "partOf" ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:prev a owl:ObjectProperty ;
|
||||
rdfs:label "prev" ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:Actor a owl:Class ;
|
||||
rdfs:label "Actor" ;
|
||||
rdfs:subClassOf [ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:moves ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 0 ;
|
||||
owl:onProperty migration:moves ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:MoveCollection ;
|
||||
owl:onProperty migration:moves ],
|
||||
[ a owl:Restriction ;
|
||||
owl:minCardinality 0 ;
|
||||
owl:onProperty migration:migration ],
|
||||
[ a owl:Restriction ;
|
||||
owl:allValuesFrom migration:MigrationCollection ;
|
||||
owl:onProperty migration:migration ],
|
||||
[ a owl:Restriction ;
|
||||
owl:maxCardinality 1 ;
|
||||
owl:onProperty migration:migration ] ;
|
||||
skos:definition """A generic form of the ActivityStreams [actor types](https://www.w3.org/TR/activitystreams-vocabulary/#actor-types)
|
||||
intended as a mixin to illustrate the added properties.
|
||||
""" ;
|
||||
skos:exactMatch as:Actor ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:items a owl:ObjectProperty .
|
||||
|
||||
migration:attachment a owl:ObjectProperty ;
|
||||
rdfs:label "attachment" ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:moves a owl:ObjectProperty ;
|
||||
rdfs:label "moves" ;
|
||||
rdfs:range migration:MoveCollection ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:proof a owl:ObjectProperty ;
|
||||
rdfs:label "proof" ;
|
||||
skos:definition """An object integrity proof signed by the actor on the target instance for the entire `OrderedCollection` object
|
||||
and its items.
|
||||
""" ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:target a owl:ObjectProperty ;
|
||||
rdfs:label "target" ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:orderedItems a owl:ObjectProperty ;
|
||||
rdfs:label "orderedItems" ;
|
||||
skos:definition "Inlined actor objects, ordered in lexicographic order according to their `id`." ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
migration:object a owl:ObjectProperty ;
|
||||
rdfs:label "object" ;
|
||||
skos:inScheme <https://w3id.org/fep/1580#> .
|
||||
|
||||
|
||||
@@ -0,0 +1,230 @@
|
||||
id: https://w3id.org/fep/1580#
|
||||
name: migration
|
||||
description: |
|
||||
Migrating account objects using an `OrderedCollection` of `Move` objects.
|
||||
|
||||
All subclasses of activitystreams objects should be considered "partial" extensions of the class they inherit from:
|
||||
e.g. though the Actor only describes the slots relevant for this FEP,
|
||||
it should be considered as having all the other properties of the Actor object.
|
||||
|
||||
Slots/Properties are overridden in order to refine them,
|
||||
e.g. the `ActorMove` class refines the `object` and `target` properties to specify that their range
|
||||
must be an Actor.
|
||||
license: https://creativecommons.org/publicdomain/zero/1.0/
|
||||
imports:
|
||||
- linkml:types
|
||||
prefixes:
|
||||
as: https://www.w3.org/ns/activitystreams#
|
||||
linkml: https://w3id.org/linkml/
|
||||
sec: https://w3id.org/security#
|
||||
migration: https://w3id.org/fep/1580/fep/
|
||||
default_prefix: migration
|
||||
|
||||
classes:
|
||||
Actor:
|
||||
description: |
|
||||
A generic form of the ActivityStreams [actor types](https://www.w3.org/TR/activitystreams-vocabulary/#actor-types)
|
||||
intended as a mixin to illustrate the added properties.
|
||||
class_uri: as:Actor
|
||||
slots:
|
||||
- migration
|
||||
- moves
|
||||
|
||||
Object:
|
||||
class_uri: as:Object
|
||||
slots:
|
||||
- migratedAt
|
||||
- migratedFrom
|
||||
|
||||
ActorMove:
|
||||
class_uri: as:Move
|
||||
description: |
|
||||
A refinement of the base ActivityPub Move action that specifies an actor moving to a new id,
|
||||
as described in [fep-7628](https://codeberg.org/fediverse/fep/src/branch/main/fep/7628/fep-7628.md),
|
||||
with an accompanying [fep-8b32](https://codeberg.org/fediverse/fep/src/branch/main/fep/8b32/fep-8b32.md)
|
||||
integrity proof signed by the source (object) actor.
|
||||
attributes:
|
||||
actor:
|
||||
slot_uri: as:actor
|
||||
range: Actor
|
||||
required: true
|
||||
object:
|
||||
slot_uri: as:object
|
||||
range: Actor
|
||||
required: true
|
||||
target:
|
||||
slot_uri: as:target
|
||||
range: Actor
|
||||
required: true
|
||||
proof:
|
||||
slot_uri: sec:proof
|
||||
range: DataIntegrityProof
|
||||
required: true
|
||||
|
||||
ObjectMove:
|
||||
class_uri: as:Move
|
||||
description: |
|
||||
A mapping between objects on the source server and the target server.
|
||||
Each property should be a URI reference, rather than an inlined object.
|
||||
attributes:
|
||||
object:
|
||||
slot_uri: as:object
|
||||
range: Object
|
||||
required: true
|
||||
inlined: false
|
||||
target:
|
||||
slot_uri: as:object
|
||||
range: Object
|
||||
required: true
|
||||
inlined: false
|
||||
|
||||
MigrationCollection:
|
||||
class_uri: as:OrderedCollection
|
||||
description: |
|
||||
An ordered collection of objects to move from the source actor to the target actor.
|
||||
The collection must reference a `moves` Collection of signed Move activities
|
||||
that can be used for validation in the case the origin server is inactive/uncooperative.
|
||||
Items within pages should be ordered in reverse chronological order by when the target server
|
||||
has imported them to facilitate gradual migration by 3rd-party instances.
|
||||
slots:
|
||||
- moves
|
||||
- migrationComplete
|
||||
attributes:
|
||||
first:
|
||||
slot_uri: as:first
|
||||
range: MigrationCollectionPage
|
||||
required: true
|
||||
|
||||
MigrationCollectionPage:
|
||||
class_uri: as:OrderedCollectionPage
|
||||
attributes:
|
||||
partOf:
|
||||
slot_uri: as:partOf
|
||||
range: MigrationCollection
|
||||
required: true
|
||||
next:
|
||||
slot_uri: as:next
|
||||
range: MigrationCollectionPage
|
||||
required: false
|
||||
prev:
|
||||
slot_uri: as:prev
|
||||
range: MigrationCollectionPage
|
||||
required: false
|
||||
orderedItems:
|
||||
slot_uri: as:orderedItems
|
||||
range: ObjectMove
|
||||
multivalued: true
|
||||
required: true
|
||||
|
||||
MoveCollection:
|
||||
class_uri: as:OrderedCollection
|
||||
description: |
|
||||
A collection of Actor Move actions signed by the source Actor.
|
||||
Move actions MUST be inlined and MUST NOT be paginated.
|
||||
attributes:
|
||||
actors:
|
||||
description: |
|
||||
A collection of inlined Actor objects that include public key used for validating signed objects.
|
||||
Source Actors that are not inlined in the signed `Move` activity must be present.
|
||||
range: ActorCollection
|
||||
proof:
|
||||
description: |
|
||||
An object integrity proof signed by the actor on the target instance for the entire `OrderedCollection` object
|
||||
and its items.
|
||||
range: DataIntegrityProof
|
||||
orderedItems:
|
||||
slot_uri: as:orderedItems
|
||||
range: ActorMove
|
||||
multivalued: true
|
||||
inlined_as_list: true
|
||||
|
||||
ActorCollection:
|
||||
class_uri: as:OrderedCollection
|
||||
attributes:
|
||||
orderedItems:
|
||||
description: Inlined actor objects, ordered in lexicographic order according to their `id`.
|
||||
slot_uri: as:items
|
||||
range: Actor
|
||||
inlined_as_list: true
|
||||
|
||||
ProposedMove:
|
||||
description: |
|
||||
A proposed move sent from the source instance and addressed to the target instance's shared inbox, and NOT as:Public.
|
||||
It may include an attached collection of object URIs that are to be considered the set of items that the actor proposes to move with.
|
||||
class_uri: as:Offer
|
||||
attributes:
|
||||
object:
|
||||
slot_uri: as:object
|
||||
range: ActorMove
|
||||
required: true
|
||||
attachment:
|
||||
slot_uri: as:attachment
|
||||
range: ProposedMigrationCollection
|
||||
required: false
|
||||
|
||||
ProposedMigrationCollection:
|
||||
class_uri: as:Collection
|
||||
attributes:
|
||||
items:
|
||||
slot_uri: as:items
|
||||
range: Object
|
||||
multivalued: true
|
||||
inlined: false
|
||||
|
||||
TentativeAcceptMove:
|
||||
description: |
|
||||
After moderation, a response to a ProposedMove that includes some attachment of modifications to make to the proposed migration items
|
||||
attributes:
|
||||
object:
|
||||
range: ActorMove
|
||||
required: true
|
||||
attachment:
|
||||
slot_uri: as:attachment
|
||||
range: TentativeAcceptCollection
|
||||
required: true
|
||||
|
||||
TentativeAcceptCollection:
|
||||
class_uri: as:Collection
|
||||
attributes:
|
||||
items:
|
||||
slot_uri: as:items
|
||||
multivalued: true
|
||||
any_of:
|
||||
- range: Reject
|
||||
- range: Update
|
||||
|
||||
# Unchanged aliases
|
||||
|
||||
Reject:
|
||||
class_uri: as:Reject
|
||||
|
||||
Update:
|
||||
class_uri: as:Update
|
||||
|
||||
DataIntegrityProof:
|
||||
class_uri: sec:DataIntegrityProof
|
||||
|
||||
|
||||
slots:
|
||||
migratedAt:
|
||||
description: |
|
||||
A timestamp indicating when an object was migrated.
|
||||
range: datetime
|
||||
required: false
|
||||
migrationComplete:
|
||||
description: |
|
||||
A boolean that indicates whether the target instance has completed its migration ingestion process.
|
||||
range: boolean
|
||||
ifabsent: false
|
||||
required: true
|
||||
migratedFrom:
|
||||
description: |
|
||||
The URI of the object that this object was migrated migrated from.
|
||||
range: uri
|
||||
required: false
|
||||
migration:
|
||||
range: MigrationCollection
|
||||
required: false
|
||||
multivalued: false
|
||||
moves:
|
||||
range: MoveCollection
|
||||
Reference in New Issue
Block a user