Sitemap
Aarón Ayerdis Espinoza

Aarón Ayerdis Espinoza

Introducing the ActivityPub Account Portability Testbed

6 min readOct 22, 2026

--

A public server for testing how Fediverse accounts move between servers, built on LOLA draft specification.

On a traditional social platform there is, from the user’s point of view, one server. Your identity, your posts and your complete social graph live in one company’s system, under one set of rules. You can usually download an archive of your data, but you can’t take your account somewhere else and stay connected. Leaving the platform means losing your complete social relationships on the network.

The Fediverse turns that around. Thousands of independently run servers speak the same protocol, ActivityPub. This means that an account on one can follow, reply to and be followed by accounts on any other server that federates with it.

Your identity does not belong to one company’s database. It’s a URL on the server you chose, like https://old.example/users/alice, and every post, like, and follow you make carries an ID on that host. That design gives you a choice of server, but it also ties your history to it, and servers change their rules, defederate from each other, or shut down.

So when Alice moves to new.example, a Move activity can point her followers to the new address. Her history is another matter: her posts (followers-only and private ones included), the posts she liked, the accounts she follows and the accounts she blocks. ActivityPub defines collections for most of these, and says an outbox shows each reader only what they're permitted to see. But it leaves the permission itself open. The spec notes that at the time it was standardized, "there are no strongly agreed upon mechanisms for authentication." So it doesn't say how new.example proves Alice agreed, what credential it should carry, or how that credential is limited to her account and no one else's. And ActivityPub has no collection for block lists. LOLA, a draft specification for account portability, fills those gaps by building on OAuth: an endpoint the new server can discover, a scope meant only for portability, access limited to exactly one account, and a defined set of collections to copy.

We’re opening the ActivityPub Account Portability Testbed: a public server that plays old.example, so anyone building the new.example side can run the whole exchange against a real implementation.

ap-testbed.dtinit.org

What LOLA proposes

LOLA was created, written and edited by Lisa Dusseault, CTO of Data Transfer Initiative and submitted to the W3C’s SocialWeb Incubator Community Group, which has a task force on data portability. It’s a draft, not yet a standard. It builds on ActivityPub, the W3C Recommendation behind the fediverse.

In LOLA’s terms, the old server is the source and the new one is the destination. A move takes four steps:

  1. Discovery. The destination reads the account’s public profile (its Actor) on the source, and finds where to ask for permission: a field called oauthMigrationEndpoint.
  2. Permission. The user signs in on the source and approves a consent screen. This is ordinary OAuth, with a scope meant only for account portability.
  3. One account, nothing else. The source issues a token that works for that account alone, and tells the destination exactly which account was granted.
  4. Copying. With the token, the destination reads the account’s migration collections (its posts, the accounts it follows, the accounts it blocks) plus its likes. Followers aren’t copied: they’re accounts on other servers, and they follow the new account themselves.
Press enter or click to view image in full size
The four steps of a LOLA move, with the testbed playing old.example

What the testbed gives you

An account worth moving, the moment you sign up

Testing a move needs something to move. Every new signup gets an account that already has content:

  • 30 posts, dated over about four months, split evenly between public, followers-only and private;
  • 6 likes, 6 accounts it follows, 8 followers and 2 blocked accounts.

Every remote account lives on a reserved .example domain, so a destination that acts on them, for example by re-following, never reaches a real person.

Get Aarón Ayerdis Espinoza’s stories in your inbox

Join Medium for free to get updates from this writer.

The mix of visibilities is deliberate. Anonymously, the account’s outbox shows 19 activities. With the portability token, it shows 43. A destination can see for itself what the token unlocks, and check that it handles private content with care.

Every step of the protocol, live

Discovery works both ways LOLA describes. A destination can read the account’s Actor, where the portability endpoint is advertised under endpoints, or the standard OAuth server metadata (RFC 8414) at /.well-known/oauth-authorization-server. Both lead to the same place:

"endpoints": {
"oauthAuthorizationEndpoint": "https://ap-testbed.dtinit.org/oauth/authorize/",
"oauthMigrationEndpoint": "https://ap-testbed.dtinit.org/oauth/authorize/"
}

When Alice approves, the consent screen names exactly what she’s granting: her username, her Actor ID and the portability scope. The redirect back to the destination carries the authorization code and the destination's own state, plus activitypub_actor: the Actor ID of the account that was actually granted. LOLA requires the destination to use that value rather than whatever address the user started with, which matters when someone signs in to a different account than the one they typed.

The token the destination receives is bound to that one account. Presented on any other account, it’s refused, even for data that’s public anyway. And a private post belonging to someone else answers exactly as if it didn’t exist, so the token can’t even confirm that it’s there.

The collections follow LOLA too. The migration outbox carries only the Create activities of Alice's posts; her likes and follows travel through their own collections, liked and migration/following, so a destination never receives the same thing twice.

Tools to test with

You don’t need a finished destination to see any of this. The tester guide walks through what a destination has to do, step by step, with curl examples and the counts you should expect at the end. The Postman collection plays an outside destination from start to finish: 77 checks across five folders, from discovery to the refusals, runnable in Postman or from a terminal with newman. And if you only want to see what the token changes, the built-in demo opens the same account with the token and without it, side by side, in your browser. For the design behind all of it, the overview explains how the source works, with diagrams, and What Each Collection Holds shows every collection with real examples.

Trying it

Getting started takes a few minutes. Sign up at ap-testbed.dtinit.org, and your account is ready, content included. The dashboard shows your client credentials. Register your destination’s callback there (on the public server it must use https://). Then point your destination at your account's Actor, or import the Postman collection and its environment, approve the consent screen, exchange the code for a token, and read the migration collections. The guide lists the counts every new account produces, so you'll know straight away whether your destination read everything.

Press enter or click to view image in full size
Testbed dashboard after updating Redirect URL

How we tested it

Before opening it up, we walked the testbed end to end on the production server, the way a newcomer would: signing up, authorizing an outside client, reading the account, and then trying everything a token for a different account must not be able to read. Every step passed. In the Postman Runner, a second account’s token passed all 77 checks: it read its own account in full and was refused on every part of the first account’s data it tried. The full run, with every request and screenshot, is published here: Test Results (Mode A).

What’s next

Today the testbed implements the source side of LOLA v0.3: the server an account moves from. The next step is the other side. We’re building the destination, so you’ll be able to move an account into the testbed, and after that both sides together, so a move can run from one testbed account to another and be checked end to end. Paging through long collections, which real accounts with years of posts need, is on the list too.

I’ll share more about the destination work in the coming days, and I’ll be creating articles as each piece lands.

Get involved

If you’re building on ActivityPub, we’d love for you to try the testbed with your own server and let us know how it goes, especially anything that doesn’t work as expected. The code is open at github.com/dtinit/activity-pub-testbed. And if you have views on LOLA itself, the specification is discussed in its GitHub issues and on the public-swicg@w3.org mailing list.

Thank you

to Lisa Dusseault and the SWICG data portability task force, and to everyone who built ActivityPub and keeps bringing it to more people.

This is my very first experience with ActivityPub and it has been a thrilling and exciting experience so far.

--

--