Reuse of Identity Channel Addresses & Revocation/Reissue of Keys

If there’s a unique identifier that is not bound to a specific domain and a cryptographic key, then what is the difference between nomadic identities in Zot and other kinds of decentralized identities?
DID (as a described in W3C standard) is also a unique identifier for which some kind of verification method is defined:

verification method
A set of parameters that can be used together with a process to independently verify a proof. For example, a cryptographic public key can be used as a verification method with respect to a digital signature; in such usage, it verifies that the signer possessed the associated cryptographic private key.

They certainly are similar and it would depend on the implementation. I think the best way to answer that would be to compare Bluesky AT Protocol and Zot. ATP and Zot handle identities very differently, yet both are considered decentralized.

  1. ATP uses DIDs, and Zot does not. So there is a different way of provisioning, discovering, resolving, and authenticating an identity, even though both use cryptographic keys to verify identity.

  2. ATP allows migration of identities to other servers, while Zot allows you to use your identity on and from multiple servers at the same time.

  3. ATP tells you which server they are currently on; Zot tells you the primary server, and the location of all of the user’s clones.

I think point #2 is the main difference from a functional standpoint. ATP seems to allow only one server at a time, whereas Zot allows multiple servers to host the same identity simultaneously. ATP allows migration; Zot allows nomadic identity.

This is why I suggested allowing multiple entries in the alsoKnownAs field. You can’t have nomadic identity if you only allow one server at a time.

In the end, it comes down to how it is implemented. As you can see, there are different approaches.

Probably a simpler way of putting it is that the Zot protocol do not tie your identity to one domain.

Other protocols like ATP still tie your identity to one domain; they just allow you to change it by migrating to a new domain.

So the difference is, most other protocols allow migration (one domain at a time), not nomadic identity (multiple domains at a time).

TOFU the service actor, not the regular users

currently if you assign an https id then nothing else you do will matter. you’re already bound to a domain.

validation should be done between service actors, as servers wholly own their users’ resources on that domain. users are delegating their identity entirely to the service. there is no self-sovereign identity for actors unless we start supporting DIDs and then people would probably also have to do their own key management

or alternatively you clone your keys across multiple servers (like nomadic identity) and hope no one compromises any of those servers and steals your keys (as you are increasing risk for every additional server you trust). validating then requires you trust that any given server is authoritative for that actor

Yes, that is the risk, but not as much as you think.

  1. People who clone accounts are more likely to be running their own hubs.
  2. People who want to simply log into another hub will use OpenWebAuth instead.

With OpenWebAuth, you can interact on another server without cloning your account. And Hubzilla’s default interface currently favors small or single person instances.

So, as long as we warn people of the risks, I don’t think this is much of an issue.

I mentioned this earlier. With nomadic identity, the server is the identity provider, and you are verifying with the identity provider whether the information is valid or not.

The difference is that with nomadic identity, the server gives you a list of valid clones. The server is the one telling you that, not the user.

So if a server tells you that an identity is primarily located at user42@example.social and the following domains are authorized to authenticate this identity using handles user42@example.rocks and user@example.casa, then, as you said, trust what the server is telling you, and trust all of the domains/handles the server is telling you to trust for that identity.

Since all of the clones are synced with the primary, you can think of the clones as authorized relays. The server is authorizing additional servers to act as relays for a specific identity.

It’s like an email server in a way. You can set up your SPF records to say that specific other servers are allows to send email for this domain, With nomadic identity, the server tells you which handles@domains are allowed to send messages on behalf of the primary account.

at risk of getting offtopic

the only reason nomadic identity works at all in zot is because zot uses 256-bit guids which don’t directly depend on DNS. this won’t work for activitypub because the activitypub id must be dereferenceable and in practice this means https for now. which depends on dns. in other words you need a “canonical” id that is derived from some authority other than dns (while still being dereferenceable)

so the problem isn’t with “who is providing the identity” as much as it is “what even is the identity”. in case of your primary going down you need a way to failover without losing authority over all prior activities and objects. and more importantly, you need to do so without breaking all existing links!

1 Like

Since I am designing themes and addons for Hubzilla, I wind up looking at the database structure since I need to get data out of it.

Internally, a Hubzilla/Zot identity is stored as a hash, which is somehow derived from the public/private key and maybe some other stuff. Identities from ActivityPub, Diaspora*, and other protocols also have hashes, and I am not sure how they are created.

All of the clones are, well, clones, so they all produce the same hash.

If an actor produces the same hash, it is considered to be the same actor.

So, internally, Hubzilla stores the handle and URI for reference, but uses the hash as the unique identifier.

The reason why each clone will tell you which one is the primary is so that Hubzilla shows information about the primary to the viewer/recipient. If the user changes primary, Hubzilla displays the information about the new primary to the viewer/recipient.

For example, if I changed my primary from scott@scottstolz.com to scott@completehostingguide.com then the next time a hub/instance saw my hash (on a post or whatever), it would contact the server and see what the primary is. Since it has changed, everywhere it used to say scott@scottstolz.com will be magically changed to scott@completehostingguide.com (i.e. on my profile and all of my posts).

If ActivityPub supported nomadic identity, and Streams & Hubzilla supported ActivityPub’s implementation, it would look at the public/private key it is given and create a hash internally so it can identify clones. It will have to do more than that since not all ActivityPub identities will have an identity proof, but you get the idea.

I think the way platforms with nomadic identities and platforms without nomadic identities could work together is by treating clones as relays or backup servers. In FEP-c390, you could list all of the authorized handles@domains that are allowed to speak for an identity.

A platform that does not understand nomadic identities would simply display the handle and URI of whatever handle@domain sent the message. Not ideal, but it does not require any coding changes on that platform for it to work.

Any system that uses the public/private key for authentication can use any of the listed clones/relays to authenticate the identity. They could be considered backup servers for the identity, so to speak.

My recommendation is that information about clones is added to FEP-c390 messages, and each system could decide what to do with that information based on its feature-set.

An interesting side effect of this is that platforms without nomadic identities could use the list to declare backup servers. If the main server is down, you can send messages to one of the authorized backups.

To nudge this back on topic, and to summarize what has been discussed so far:

  • Servers should be able to revoke/reissue keys for an actor, and have a way to tell others they did so.
  • If a server issues a revoke/reissue order, the old key and new key are considered the same actor, but only the new key is accepted for all future communications.
  • If the server does not issue a revoke/reissue order, a new key for a handle is considered a different actor for authentication purposes.
  • A server should be able to list all of the clones/relays/backups of an actor, so that other servers can decide how they want to handle that.

If I missed something, feel free to mention it.

I still don’t quite understand the difference between nomadic identities and ATP / FEP-c390 identities. On a high level they look exactly the same: you have one key-based identity that is linked to many domain-based identities. The rest is implementation details.

DIDs are generic and can be used to represent all kinds of identities (even domain-bound ones). You wrote:

So this hash is a decentralized identifier: it is globally unique and not bound to a particular domain. Technically it’s not a DID because it can’t be resolved into a DID document, but this is not big problem. If you want to make associated public keys and metadata discoverable via identity provider (server), perhaps did:web method is what you need?

Migration is very similar to changing the primary clone. I don’t know exactly how migrations works in ATP but in ActivityPub world actors continue to exist after migration. These actors are not treated as belonging to the same person but they could be (if they carry FEP-c390 identity proofs).

ActivityPub actors have alsoKnownAs property that lists “aliases”. This field is already used during simple migrations (where identities are bound to a domain). We don’t call them “clones” because the data is not replicated. However, the servers can send Sync() activities to each other to synchronize actors linked to the same decentralized identity - it’s not impossible.

I suppose the main difference is that with nomadic identities, you can send a message to ANY of the clones*, and the recipient will get it since they are all synced. With portable identity, you must send the messages to the current domain, or else they won’t be received.

From an ActivityPub perspective, ActivityPub doesn’t actually care how identities are managed on each platform. They could use DID documents as specified in the DID standards, or they can use something like Zot to manage the identities. ActivityPub wouldn’t actually care about how a platform does it. ActivityPub just wants you to send the correct information as defined in FEP-c390.

If ActivityPub (or more accurately, the platforms that accept FEP-c390 messages) consider different actors with the same key to be the same person, then that is an implementation of nomadic identity. If they do not consider them to be the same person, then they haven’t implemented nomadic identity.

i.e. user43@example.social and user42@example.org are the same person since they have the same key or unique identifier.


The reason why I mentioned adding multiple aliases is that there are two use cases that would benefit from this:
  1. Nomadic identity / any clone can accept the message*; (primary or sending to all is preferred).
  2. Backup servers / other servers can accept the message if the primary is down.

It basically works like an MX record for email. Here are a list of servers that can accept messages for this email address. Basically, this is the same exact thing, except with fediverse messages and it allows multiple handles for the same identity.

*The Zot specification says you should send the message to all of the clones, but realistically, since they are all synced, the message will still propagate to all clones if you send the message to one clone, it’ll just be slower.


But, you are right. They are very similar, and there are only subtle differences, as long as you accept messages from any domain for a specific key.

A problem only arises if a platform refuses to accept the key (or messages with that key) because it comes from the “wrong” domain.

1 Like

I think it’s better to add information about existing aliases to the root of actor object using alsoKnownAs property. The server can fetch FEP-c390 identity proof for each actor in alsoKnownAs array and verify that they are indeed clones/aliases. But FEP-c390 identity proof itself is meant to be a 1-to-1 verifiable link between the public key and the actor ID, so its alsoKnownAs property should contain only one value.

1 Like

That would probably work better then. As long as there is somewhere in the spec where the information can be conveyed and acted upon.

1 Like

So, I am guessing it would work something like this? But I am not sure where an instance would notify another instance of a revoke/reissue order.

1 Like

Okay, but I need some ID first

Is this a step where server (re)fetches actor object?

Okay, here’s my key and my handle/URI

If this is an actor object, then it should already contain identity proofs and a list of aliases. Therefore,

Is this ID legit

…is not necessary. Another server only needs to verify the signature (offline).

However, if we’re doing a re-fetch and fetched object contains a key that doesn’t match the known key, then additional check is necessary:

Is this ID legit

The exact implementation would depend on who is authority for a given key. If Actor’s Server is the authority, then your diagram is correct.
But the authority could be some other entity.

1 Like

Okay, so the identity proof and the request for content is in the same request. That makes sense and is more efficient.

Where would a server tell another server that there is a revoke/reissue order in effect (i.e. a key change for a given identity)?

If Actor’s Server is the authority, then Another Server can look at identity proof and compare the proof value to a known one. If the proof is different (proof.created attribute value has changed), then Another Server should resolve the DID again and grab the new key (by making another request to Actor’s Server if DID document is hosted there).
If Actor’s Server is not the authority, then Another Server should not trust it because it may be presenting an old identity proof even if the key was revoked by user. In that case, we need to resolve DID regularly or rely on some other notification mechanism. Here’s what DID spec says about change notifications:

I think the major difference here is that Zot does not use DIDs and the server verifies the credentials directly. Even though the identities are nomadic, it behaves similarly to an authoritative identity provider as far as verification goes.

  1. someone@example.com contacts another server and says “I am someone@example.com and here is my identity proof.”
  2. The other server contacts example.com and verifies that someone@example.com exists and the identity proof is valid. (It also gives a list of known aliases/clones.)

If the same person is contacting you from a different server, say example.social, then they would be identified as someone@example.social and the other server would contact example.social to verify the identity. It would have the same identity proof though, so you would know they are the same person.

So it works very differently than DIDs.

How identity proof (or its equivalent) looks like in Zot?
A public key (or a hash of public key) can be represented as DID. There should be a way to cleanly map concepts from Zot to ActivityPub+FEP-c390, because there are so many similarities.