Nomadic identity for the fediverse?

Yes, you’re right. Actually, I just published a FEP about that: #33 - Add FEP-c390: Identity Proofs - fep - Codeberg.org

It’s like ATP identity but fully backwards-compatible with existing Fediverse software, and without any vendor lock-in.

4 Likes

Turns out FEP-8b32 is not required for migrations, and FEP-c390 should be enough to implement them. Here’s an example of Move activity:

{
  "@context": [
    "https://www.w3.org/ns/activitystreams"
  ],
  "actor": "https://server2.com/users/alice",
  "id": "https://server2.com/activities/00000000-0000-0000-0000-000000000001",
  "object": "https://server1.com/users/alice",
  "target": "https://server2.com/users/alice",
  "to": [
    "https://example.com/users/bob"
  ],
  "type": "Move"
}
  • object is the old account
  • target is the new account (which initiates the migration)

This activity could be sent by the server even without FEP-8b32 integrity proof. Actors identified by object and target properties must have at least one FEP-c390 identity key in common to be considered aliases.

4 Likes

Yeah, this is looking promising. There’s a huge gulf between linking identities and having actual nomadic identities you can switch to at any time with all your content intact. I’m really not looking forward to implementing that again (it took a number of years of mind-numbing effort to make it all work smoothly under Zot/Nomad ), but the journey of a thousand miles always begins with a single step.

2 Likes

How it works in Zot/Nomad? (and Streams?)
Perhaps there’s a way to adapt it to ActivityPub without a complete re-write?

What we do here is send ‘sync packets’ to our clones when we change data locally on one instance. Currently this is pretty platform specific. I’ll probably create a ‘nomad:Sync’ activity type on the short term. It’s also too bad ActivityPub doesn’t provide any decent payload encryption because some of this data is sensitive, but that comes with the territory. To be fair I suppose we can just change the mediaType and send opaque encrypted blobs like we do for E2EE. So it’s still platform specific, but it would provide a way to use ActivityPub as a transport.

1 Like

I might be interested in creating interoperable implementation. The need to create clone in advance is not good from a user experience perspective (most people won’t bother until it’s too late), but I’m not sure if there’s a better way. I thought about automatically backing up content to some external storage system like IPFS: when you create a post, the server encrypts it with your public key and adds it to your personal repository which is automatically synced to your local IPFS node. Here you have full ownership of the data, but this is even more complicated than setting up a clone.

Sounds good. How nomadic identities are represented at the protocol level? Are they based on public keys?

Is TLS not enough? Is there a chance that sync activity would go through an untrusted server?

3 Likes

We are facing the same issue here in our project, and one idea we have is to store user data with IPNS hashes. The server can update it easily on behave of the user, and the user can migrate it to other servers easily as well.

2 Likes

It might be time to start giving fediverse users their own public/private key pair where they can control their private key as part of a nomadic identity

In the nostr project this is starting to work really well

It’s possible to add nomadic identity to fediverse profiles (or even solid profiles) using the following vocab:

https://w3id.org/user

To date I’ve added two identies

But it would be possible to add more.

The magic sauce here is the owl InverseFunctionalProperty. What it means is that anyone with a certain public key has the same identity (owl sameAs) as any other profile. So you can have two different instances and the model knows you are the same. You move from one place to another and the underlying model knows it’s the same person. Further more with a nostr key you can sign something to prove that.

Aside: A side effect of using this would be that it could bring certain projects together and allow the user more functionality. e.g. fediverse for micro blogging etc., nostr for social communication and zaps etc., solid for cloud storage etc. matrix for chat etc. with an intersection of tooling where people from different platforms can converse

There would need to be a bit of integration into different fediverse software to make it a good UX. If at some point there’s a bit of demand for this I could write up an FEP

3 Likes

This was discussed earlier in this thread. The FEP describing key-based identities is here: fep/fep-c390.md at main - fep - Codeberg.org

This proposal describes a mechanism of linking cryptographic keys to ActivityPub actor profiles.

I’m developing an ActivityPub server Mitra where these ideas are currently being tested.

1 Like

What your project is about? Is it open-source?
I’ve also considered IPNS/IPFS but it is not suitable for private data like follower-only posts and direct messages. On the other hand, public data is more valuable, so using IPFS to create permanent URIs for your public posts might be a good idea after all.

you could in theory strongly encrypt your private posts and hand out decryption keys to your trusted audience. if you did that, it wouldn’t really matter if you used something like IPFS. anyone with the link/hash can get the encrypted data, but it will take a lot of time and effort to crack the encryption.

1 Like

That’s a good idea. I’m trying to find a way to create permanent URIs for content. If there’s a blog post hosted on server 1, how it can be moved to server 2 without breaking any links (let’s assume the post has been shared in many places across the web, inside and outside Fediverse)?

Today this is not possible. However, IPFS project came close to solving this problem. I can put my blog post into IPFS and generate a permanent URI ipfs://bafyabcxyz. My blog platform also hosts IPFS gateway, so the post can be accessed by URL https://myblog.net/ipfs/bafyabcxyz. If I move to another server, the same post will be available at https://anotherblog.net/ipfs/bafyabcxyz. The URL is different, but visitors who have IPFS-enabled browser still can access https://myblog.net/ipfs/bafyabcxyz which is automatically translated into ipfs://bafyabcxyz by the browser.

If IPFS will be integrated into mainstream browsers, the experience will be seamless. They are still very far from achieving this goal but to my knowledge they are closer than everyone else.

There’s a catch though. If https://myblog.net/ipfs/bafyabcxyz resolves to ActivityPub object, users of IPFS-enabled browsers see plain JSON instead of HTML page. Also, the post can’t be updated because that will change its content ID. In theory IPNS can help but I don’t know how to keep IPNS key under the user’s control.

There are other decentralized storage networks that provide IDs that don’t depend on a particular domain or hash. In the end what matters is adoption. We can even create our own URI scheme or ID encoding, and write a browser extension, but most likely nobody will use it. It’s better to piggyback on existing technologies.

1 Like

uri persistence is really a question of persistent authority as it pertains to a particular resolution method. the reason activitypub “works” is because it piggybacks off of https+dns. the authority is within the domain namespace. “this is /resource, as determined by the HTTP server running on domain.tld.” to break out of that in any meaningful, we need an alternative source of authority that allows minting identifiers while avoiding the double-spend problem.

some days ago, i was researching the history of XRIs and XRI resolution (among other “persistent identifier” strategies and schemes throughout history). essentially, you could have a registry of “i-numbers” that are unchanging machine-friendly numbers, while “i-names” would be the reassignable human-friendly names. basically you go to a registry of some sort and register a name like in DNS as well as a number like in DOI. authority is derived from the authoritative resolution server, and this authority can cascade or be forwarded like in DNS name resolution.

the abstraction that makes this all possible is the idea of the URN and the URN resolver or resolution service, as a DNS-like abstraction on top of HTTP. the way XRI resolution worked was to query some server for a resource by any of its XRIs, and you would get back the XML “resource descriptor” or XRD, containing some properties about the resource and linking to certain endpoints that might be relevant. kind of like getting a plaintext response from a DNS query that contains available records for that query, but more well-structured. if this sounds familiar, it’s because it should be familiar – this is how webfinger works! we just swapped the XRI resolution endpoint for the well-known webfinger path, we swapped XRIs for any URI, and we swapped XRDs for JRDs – the “JSON Resource Descriptor”. a JRD works basically the same as an XRD: subject, aliases, properties, links. and you can forward any webfinger queries you don’t understand.

so we have a mechanism that works and isn’t too alien, we can just pass every activitypub id through webfinger if we wanted to. or we could have some extension property containing some other URI as a sort of “network identifier” for some network that isn’t the World Wide Web. but the problem is still authority. who mints these identifiers? where do you forward your webfinger queries? we effectively have to centralize on some “superservice” that handles only identifier minting and record-keeping, then defers to the current location for actually resolving and fetching that resource.

taking a step back we can sort of see how wasteful this all is, we effectively have a network-within-a-network, and maybe it even starts to make sense to forego HTTP and roll our own TCP/UDP protocol, combined with some DNS records that map a human-friendly dns name to this new network. i think this is what bluesky went with? DNS records point you to a “placeholder” DID method (did:plc:), which is described here: DID Placeholder (did:plc) | AT Protocol

the remaining challenge is how to map this to the existing HTTPS network, the Web-based servers. and here’s something i just thought of: what if we used both @id and alsoKnownAs? essentially, the strategy would be something like this:

  1. try to resolve @id
  2. if that fails, see if there is an alsoKnownAs we can resolve

so the document currently might look something like so:

id: https://trwnh.com/activities/1
actor: https://trwnh.com/actors/1
type: Create
object: https://trwnh.com/objects/1

and it would instead look something like this:

id: did:example:activity
alsoKnownAs: https://trwnh.com/activities/1
actor: {
  id: did:example:actor
  alsoKnownAs: https://trwnh.com/actors/1
}
type: Create
object: {
  id: did:example:object
  alsoKnownAs: https://trwnh.com/objects/1
}

basically use a partial representation? this would break some software that assumes a partial representation is a full representation, though.

one last remaining nitpick: bit of a bikeshed really but i wish the alsoKnownAs was called aliases or something like that, as this really does seem like the subject-alias pattern all over again, we’re just calling it something different, id-alsoKnownAs. and there may be reason to not use alsoKnownAs for this, also… “alias” implies the same document or resource, whereas “alsoKnownAs” might not carry this assumption (and instead implies that multiple different resources share the same controller? this might be a misuse though)

1 Like

Are partial representations valid in AP/AS? Accept(Follow) often contains a partial representation of Follow activity but I always thought that it is just a mastodonism and something that should be avoided.
It would certainly break some software. Actually I find the webfinger idea more appealing. What if we add the authority property like this:

type: Create
authority: https://trwnh.com
actor: did:example:actor

If the recipient doesn’t know how to resolve did:example, it can make a webfinger query to authority for did:example:actor. This can be gradually introduced to existing implementations, and similarly will provide some space for experimenting with different DID methods and URI schemes.

this isn’t about AS2 per se, this would probably defer to JSON-LD? and i’m not entirely sure, it could go either way. we can definitively rule out making @id into an array, since @id MUST be a string, and JSON-LD parsers will actually fail if they encounter a non-string @id. So the @id has to be the canonical identifier of the document (or “subject” of the resource). so i reached for alsoKnownAs instead.

it is of course “valid” to only represent some properties, and i see this as dependent on app logic on whether to expand the node or take it as-is. the JSON-LD spec describes section 4.5 Embedding: JSON-LD 1.1 but there is no guidance on whether partial embedding is allowed, recommended, etc.

there’s two interpretations here. either a Follow activity is given a persistent identifier and can then be the object of an Accept or Undo without embedding it… or you consider Follow to be a transient activity and throw away the activity once you’re done processing it, in which case the id is irrelevant and actually could be null or not provided. i think mastodon technically handles both – it first checks for an existing follow request with the activitypub id matching the Accept.object, and then if not found, it checks that the Accept.object.type == Follow. as to whether that should be avoided, that’s open to interpretation. you might consider always inlining/embedding the Follow inside the Accept/Reject, but include the id in the object node if you have it.

if we do it like that, we might want to look into marking it up as a “service endpoint” instead? (unsure) Decentralized Identifiers (DIDs) v1.0

maybe see also: B.8 Assigning DIDs to existing web resources: Decentralized Identifiers (DIDs) v1.0

but whatever the property ends up being and however it is encoded, it is basically “take the @id and plug it into this authoritative resolver”. this has problems when the authority or endpoint changes. really, the fundamental issue is, how do we establish authority across multiple origins? the goal is that you should be able to plug the @id into any resolver downstream of the authoritative one, in the same way you can get your DNS records from any nameserver or resolution service that pulls the authoritative record from your registrar. but ideally we should be able to do it without a registrar.

actually, why encode an authority into the JSON-LD document at all? why not just always use the webfinger endpoint of the server that sent you the activity? perhaps we signal this in an HTTP header, something like X-Webfinger-Domain? i’d have to give more thought to the security implications of this, though – i’m a bit tired currently, so maybe something something reverse DNS. not sure.

1 Like

I would love for this to be a standard supported across multiple social protocols.

Related HN thread with several fediverse comparisons: Domain Names as Handles in Bluesky | Hacker News

I recently rediscovered the notion of Account Aliases:

It feels to me like a necessary precursor to wholly nomadic (decentralized, probably DID-based) identity.

…then again, there’s an argument to be made for doing it all at once, which seems to be where this related discussion is headed:

1 Like

We provide ActivityPub representations of our existing Nomadic Identity using alsoKnownAs. The only other bit that’s needed is that we have a “copiedTo” to complement Mastodon’s “movedTo” - with the only real difference being that when you add the new identity, you don’t delete the original.

3 Likes

From @aumetra :

3 Likes