XIP-66: AT Protocol Identity

authors: @boscolo (cboscolo (Chris Boscolo) · GitHub)

Abstract

XIP-66 defines a way to add an AT Protocol identity as an associated identity for an XMTP Inbox.

Motivation

Bluesky has popularized the AT Protocol with over 36M sign-ups and an estimated 2 million daily active users. The AT protocol does not have a broadly adopted messaging protocol for either direct messages or group messages. Supporting AT Protocols identities in XMTP would enable apps built on the AT Protocol to use XMTP for decentralized messaging.

The goal of this XIP is to enable apps built with AT Protocol, including Bluesky, to adopt XMTP for messaging by associating an AT Protocol account DID as a valid identity connected to an XMTP Inbox ID.

Specification

Challenges

There are two challenges with AT Protocol identities the make it different than either a Passkey or an Ethereum Wallet.

  1. An ATProto DID (account) is an W3C DID (eg. did:plc:bq5ygkxmmbae7n6z34neuaih). The DID itself may not be self-certifying the way a wallet EOA or public key is. Instead, the DID Document contains the public key designated by the user to authenticate writes to the Personal Data Store (PDS). It’s assumed that there is secure way to query the DID Document for the DID. In order to prove control over the AT Protocol identity, client software needs to lookup the DID Document associated with the ATProto DID and read the #atproto public key from the verificationMethod element, then use this key to verify signatures of data commited to user’s PDS. Similar to Smart Contract Wallet address, verification of signatures cannot be performed offline. One ramification of this, is that an ATProto DID would not be suitable as an MemberIdentifier that can add installations (key pairs) as children. An ATProto DID would only serve as an “also known ss” identifier for the XMTP Inbox ID.

  2. Due to the nature of the AT Protocol architecture, this public key stored in the verificationMethod is not available for adhoc signing of arbitrary content. Instead, it is typically only available within the Personal Data Store (PDS) for signing commits in the PDS. This means that signature verification logic will need to be performed over this AT Protocol commit record.

Typical scenarios for AT Protocol identity associations

Due to the challenges identified above, AT Protocol Identities can only be added to XMTP Inbox IDs that have already been created via one of the supported key types. (currently either Passkey or Wallet address.) The associated ATProto DID can be thought of as an “also known as” record for this XMTP Inbox ID instead of a MemberIdentity that can manage the XID itself.

The typical scenario for a native AT Protocol app will be to create an XMTP Inbox ID using a Passkey, add this Inbox ID to the ATProto PDS resulting in a signed commit that is then used to add the AT Protocol DID as a valid MemberIdentifier using the Passkey.

Attesting to an association with the a particular Inbox

To associate an AT Protocol Identity with an XMTP Inbox ID:

  1. Write a record to the PDS using com.atproto.repo.putRecord with collection = org.xmtp.inboxid and rkey as the string self. This step can only be performed by the user that controls the ATProto DID and associated PDS.
  2. Read the signed commit data from the PDS using com.atproto.sync.getRecord. Extract the signature from this commit for use in a call to AddAssociation. The AddAssociation call must verify that the signature matches the public key in the ATProto DID, and must verify that Inbox ID written with com.atproto.repo.putRecord matches this XMTP InboxID being updated.

Other considerations

Looking up the AT Protocol ID

[TBD] Need help filling in the DevX for how to know when to show the AT Protocol ID, and how to look up an ATProto identity to initiate messages to them.

Human readable names

In AT Protocol, the human readable name for an AT Protocol identity is a DNS name. It can be retrieved from the ATProto DID Document alsoKnownAs element.

  "alsoKnownAs": [
    "at://chaosmokey.bsky.social"
  ],

Rationale and alternatives

One alternative not described in this spec is adding the XMTP Inbox ID to the DID Document itself rather than adding it to the ATProto PDS referenced in the ATProto DID. The reason for this is that the developer tooling required to update a user’s DID does not exist and even if it existed for did:plc, updating a did:web DID will probably never exist.

Backward compatibility

Compatibility with old versions of LibXMTP

Adding an ATProto MemberIdentifier should not break compatibility with older version LibXMTP releases due to new MemberIdentifiers not being able to sign identity updates, and the graceful handling of new unknown MemberIdentifiers.

Reference implementation

High-level technical implementation

Adding ATProto Indentity support (non-breaking)

  • Add a new MemberIdentifier::ATProto variant that has restricted permissions. It will not be able to perform any identity updates, nor will it be allowed as the recovery identity.
  • [Confirm with Nick]Add a new UnverifiedSignature::ATProto variant
    • Implement the associated to_verified branch logic
  • [Confirm with Nick] Add a new SignatureKind::ATProto variant

This means that the MemberIdentifier in the AddAssociation proto may come through as None in old versions that haven’t received the AT Proto update. It must be verified that this behavior is acceptable.

Changes to AddAssociation proto

[TBD] Is this needed in the XIP?

message Association {
  oneof kind {
    :RecoverableEcdsaSignature erc_191 = 1;
    Erc1271Signature erc_1271 = 2;
    LegacyCreateIdentityAssociation legacy_xmtp_identity = 3;
    ATProtoAssociation atproto_identity - 4;
  }
}

Verify the DID to XMTP Mailbox ID mapping

AT Protocol DIDs are represented as a string in the format did:method:value. The two methods currently supported are did:plc and did:web.

Steps to verify that the DID has authorized the association with the XMTP Inbox ID.

  1. Ensure at://DID/org.xmtp.inbox/self record exists in the PDS for DID.
  2. Ensure the inboxId content of the record matches the XMTP Inbox being updated.
  3. Compute the CID for this record.
  4. Retrieve the commit record for at://DID/org.xmtp.inbox/self and verify that the commit contains the record CIS and that the signature is valid for public key in DID.

Example code for performing these steps can be found at verify-atrecord.ts

Adding the XMTP Inbox ID to the AT Protocol PDS

[TBD] chrisb will provide sample typescript code for how this is done.

Security considerations

Threat model

Insecure association types

Because these new association types can be looked up through the API, any flaws in the implementation of signature verification could allow one user to impersonate another. New association types must be vetted with the same level of security review as any other protocol change.

Copyright

Copyright and related rights waived via CC0.

1 Like

This XIP is well-written, thank you! I couldn’t find this in the ATProto docs, but is this public key (as well as the alsoKnownAs field) mutable?

  • If immutable, there may be some optimizations such as caching that could also be explored (later).
  • If mutable, it might be helpful to flesh out some details of how older signatures are handled when the public key changes. We had to work through a similar issue with smart contract signatures. This should be easier to resolve though, if we are not using it as a MemberIdentifier.

I’m assuming it’s the latter, seeing as it’s hosted on a webserver?

1 Like

Your assumption is correct, it is mutable. The main idea of an W3C DID is that the DID (uri) itself is immutable, and uses the following format: did:method:foobar. How you update and query the contents of the DID Document for a DID depends on the method. So, as you said, did:web:foobar.com would mean you could update the public key by updating a web page, but did:plc:foobar requires following the protocol specified by plc.

I chatted with Nick briefly about this. He said:

AtProto identifiers would not add devices as children. The passkey that added the AtProto identifier would add the device as a sibling to the AtProto identifier. So, maybe we could loosen the rules a bit to accommodate something like AtProto. If you rotate their AT DID public key, the association would simply appear as invalid for anyone checking, and the association would be hidden. The user could add a new association using the new public key to fix things

Is this what you mean by:

This should be easier to resolve though, if we are not using it as a MemberIdentifier

If so, I tried to capture that sentiment in with:

One ramification of this, is that an ATProto DID would not be suitable as an MemberIdentifier that can add installations (key pairs) as children.

But I can also make that point more clear and add Nick’s expanded description.

Also, related to this, I’m wondering if I am violating the definition of MemberIdentifier by adding the qualifier: “that can add installations (key pairs) as children”

Should it be called something other than a MemberIdentifier given it has this limitation?

1 Like

If you rotate their AT DID public key, the association would simply appear as invalid for anyone checking, and the association would be hidden

Yep that’s what I was verifying, and this makes perfect sense!

Should it be called something other than a MemberIdentifier given it has this limitation?

No you’re right, and I misspoke when I didn’t call it a MemberIdentifier - there’s nothing preventing different types of MemberIdentifiers having different permission levels, and I was confusing the concept of an identifier with the concept of a permission level.

You bring up a good point around terminology though - in XIP-46, we define the permission levels Associated Address, Installation Key and Recovery Address. With passkeys and this new “also known as” permission level that could also be extended for other identities like Solana or Bitcoin, it could be a good time for us to add/consolidate levels and give them good names.

Ip updated the XIP to capture this feedback. I didn’t try to answer the question about updating XIP-46 terminology, but highlighted the issue.

In section " ATProto DID as a Member Identifiers"