Identification on XMPP is done with the Jabber ID (JID), which is an email-like, human-friendly identifier. While handy, JID is usually tied to a Domain Name Server, which makes it difficult to change server (while Moved (XEP-0283) [1] helps, it needs the server to be available and cooperating), and which is not usable in a serverless environment.
XMPP Decentralized ID (XID) uses a simple mechanism based on cryptographic signature to have an entity identity independent of any central organization or component. It is designed to be simple to implement and use, and to stay compatible with normal JID.
XID can be used to implement the "cryptographic verification" mentioned in Moved (XEP-0283) [1] with the benefit that the old server doesn't need to be online or cooperating (thus it will work if the server is down or actively blocking migrations). It can also be used in a serverless environment. XID allows keeping track of entities, so if a user's contacts change server, or communicate in a serverless configuration, a client can use it to verify who it is talking to, and to keep all existing cached data (message history, vcard, avatars, etc.). XID could also be used to sign any external data to associate it with the XMPP entity, like a document or an image, or metadata (e.g., to associate a resource like a website with an XMPP entity).
The design goals of this XEP are:
A XID is derived from a cryptographic signature. It currently uses Ed25519 (which is already used for OMEMO Encryption (XEP-0384) [2] so most clients have an implementation already). The private key is shared between devices of the entity, and the public key is used to generate the ID (printed as a hex string in lowercase, with a prefix byte). The ID can then be used as a JID in the "<ID>@id.internal" form (this is this form that we call a "XID").
The XID is published on a well-known PEP node ('urn:xmpp:xid'), and private key synchronization is done automatically via end-to-end encrypted messages (via OMEMO Encryption (XEP-0384) [2] or OpenPGP for XMPP Instant Messaging (XEP-0374) [3]) or another means. XID can also be revoked via a PEP node.
To generate a XID, an entity must generate a public key and a private key. The former will be used to generate the ID, and the latter must be shared between devices of the entity, and kept secret otherwise (see below for private key synchronization).
The private key can be used to sign any kind of file or document, even outside of XMPP, and the corresponding public key can be used to verify those signatures. This allows an entity to associate external data with its XID identity independently of any XMPP communication.
To generate the ID, the public key is converted to a hex string (which MUST be lowercase), then the prefix byte "00" is prepended to indicate that Ed25519 is used. A JID is built by using this ID as node part, and "id.internal" as domain part.
Juliet's client generates her XID. To do so, it first generates a pair of private and public keys. It gets those values, converted to hex strings:
000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f03a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8The public key is used for the ID. As the key has been generated with Ed25519, the "00" byte is prepended to it, so it becomes 0003a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8. Finally, the "@id.internal" domain is used to generate the corresponding JID, which is 0003a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8@id.internal. The final JID is called Juliet's XID.
A XID can be published on an entity's Personal Eventing Protocol (XEP-0163) [4] to the well-known node 'urn:xmpp:xid'. By default, and according to XEP-0163, the access model MUST be "presence", but a user MAY choose to use another access model (e.g., "open") if they want to have their XID more or less accessible.
The item payload consists of a <xid/> element qualified by the 'urn:xmpp:xid:0' namespace which MUST have a 'created' attribute with a DateTime value as specified in XMPP Date and Time Profiles (XEP-0082) [5] to indicate when the XID has been generated. The <xid/> element content MUST be the generated JID as explained in XID Generation and JID Mapping.
The main XID MUST be published to an item with an itemID of "current" as explained in XEP-0060 §12.21 Singleton Nodes. Other XIDs can be added as backups with other IDs.
Entities SHOULD subscribe to the 'urn:xmpp:xid' PEP node with the +notify option to be informed of changes published by other devices. Before publishing a new XID, an entity SHOULD retrieve the items from the 'urn:xmpp:xid' PEP node to obtain the current state and check whether a XID is already published. If a XID with itemID "current" already exists, the entity SHOULD NOT overwrite it with a different XID.
<iq type='set'
from='juliet@capulet.lit/balcony'
to='juliet@capulet.lit'
id='xidpub1'>
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
<publish node='urn:xmpp:xid'>
<item id='current'>
<xid xmlns='urn:xmpp:xid:0'
created='2026-05-27T14:30:00Z'>
0003a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8@id.internal
</xid>
</item>
</publish>
</pubsub>
</iq>To revoke a XID (because it has been compromised, for rotation, or any other reason), it first MUST be removed from the 'urn:xmpp:xid' PEP node. Then a <revoked/> element MUST be published on the 'urn:xmpp:xid:revoked' well-known PEP node of the entity.
The item payload consists of a <revoked/> element qualified by the 'urn:xmpp:xid:0' namespace which MUST have 'created' and 'revoked' attributes with DateTime values as specified in XMPP Date and Time Profiles (XEP-0082) [5]; the 'created' attribute indicates the XID creation datetime (the same as in publishing), while the 'revoked' attribute indicates the datetime of the revocation. The <revoked/> element content MUST be the generated JID of the revoked XID as explained in XID Generation and JID Mapping.
The <revoked/> element MAY have a <reason/> child element whose content is the human-readable reason for the revocation.
If the main XID (the one with the "current" ID in 'urn:xmpp:xid' PEP node) is revoked, the revoking client MUST publish another XID with this ID, either by republishing an existing one with this ID (and removing the element with the old ID), or by generating a new one.
Juliet wants to revoke her old XID because she suspects it may have been compromised. She first removes it from the active node, then publishes the revocation on the revoked node.
<iq type='set'
from='juliet@capulet.lit/balcony'
to='juliet@capulet.lit'
id='xidrev1'>
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
<retract node='urn:xmpp:xid'>
<item id='current'/>
</retract>
</pubsub>
</iq>
<iq type='set'
from='juliet@capulet.lit/balcony'
to='juliet@capulet.lit'
id='xidrev2'>
<pubsub xmlns='http://jabber.org/protocol/pubsub'>
<publish node='urn:xmpp:xid:revoked'>
<item id='0003a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8'>
<revoked xmlns='urn:xmpp:xid:0'
created='2026-05-27T14:30:00Z'
revoked='2026-05-30T09:15:00Z'>
0003a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8@id.internal
</revoked>
</item>
</publish>
</pubsub>
</iq>To verify that an entity is allowed to use the XID, an XMPP entity can send a <message/> stanza with the default type ("chat") to the bare JID of the entity to verify. This stanza MUST have a <challenge/> element qualified by the 'urn:xmpp:xid:0' namespace. The <challenge/> MUST have a 'xid' attribute with the XID to verify, a 'timestamp' attribute with a DateTime value as specified in XMPP Date and Time Profiles (XEP-0082) [5] indicating when the challenge was issued, and MUST have a nonce for its content (i.e., a cryptographically strong random number used only once). The nonce MUST be encoded as a hex string (lowercase).
When the <challenge/> element is received by any of the devices having the private key corresponding to this XID, it MUST sign the requested nonce and send a message to the entity requesting the challenge with a <response/> element qualified by the 'urn:xmpp:xid:0' namespace. The <response/> element MUST have a 'xid' attribute with the XID being verified, a 'timestamp' attribute matching the 'timestamp' from the <challenge/> element, and MUST use the hex-encoded signature of the nonce as content. The entity verifier MUST then check that the signature is correct for this nonce and that the public key corresponds to the XID (i.e., the localpart of the JID from which the algorithm prefix byte has been stripped).
Notes:
Romeo wants to verify that Juliet is the legitimate owner of her XID. He sends a challenge with a random nonce, and Juliet's device signs it and returns the response.
<message type='chat'
from='romeo@montague.lit/orchard'
to='juliet@capulet.lit'>
<challenge xmlns='urn:xmpp:xid:0'
xid='0003a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8@id.internal'
timestamp='2026-05-30T10:15:30Z'>
a3f2c8b1e9d74560
</challenge>
</message>
<message type='chat'
from='juliet@capulet.lit/balcony'
to='romeo@montague.lit/orchard'>
<response xmlns='urn:xmpp:xid:0'
xid='0003a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8@id.internal'
timestamp='2026-05-30T10:15:30Z'>
7f2be0038e2f62b4ab6688440e07cd5939549feb810fc2514a26282d35056d3aea60c8c102dd3dbce678b520ca3622fbdb53b402cf7ca7f97d75ec23c29bc00d
</response>
</message>Devices may use several ways to share private keys. Two methods are described below: QR Code exchange and automatic synchronization.
The private key can be encoded in an xmpp: URI as follows:
xmpp:<public_key>@id.internal?;xid-private=<private_key>;xid-created=<timestamp>
Both keys are hex-encoded as specified in XID Generation. The 'xid-private' query parameter contains the private key corresponding to the XID. The 'xid-created' query parameter matches the 'created' attribute of the published XID item and uses a DateTime value as specified in XMPP Date and Time Profiles (XEP-0082) [5]. Before importing the private key, the scanning device MUST verify that the public key in the URI matches one of the XIDs published on its own entity's PEP node, and MUST verify that the public key derived from the private key matches the public key in the URI.
xmpp:0003a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8@id.internal?;xid-private=000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f;xid-created=2026-05-27T14:30:00Z
This method provides an automatic private key synchronization between known devices, and works asynchronously (devices don't need to be connected at the same time, or in the same location). It uses OMEMO Encryption (XEP-0384) [2] with Stanza Content Encryption (XEP-0420) [7].
When a device discovers a XID linked to its own identity (e.g., by checking its own 'urn:xmpp:xid' PEP node as explained in Publishing and Revoking XID) and it doesn't know the private key, it can request it by sending a <message/> stanza with the <private-key-request/> element qualified by the 'urn:xmpp:xid:0' namespace. This element MUST have a 'xid' attribute set to the XID it wants the private key for, and MUST be sent to its own bare JID. The request MUST use OMEMO Encryption (XEP-0384) [2] encryption and the element MUST be encrypted with Stanza Content Encryption (XEP-0420) [7] as explained in XEP-0384 §5.5 Sending a Message. This message will then be copied to all entity devices thanks to Message Carbons (XEP-0280) [6].
When a <private-key-request/> is received by a device, it MUST:
If all these conditions are respected, the entity SHOULD send a <message/> stanza with a <private-key/> element qualified by the 'urn:xmpp:xid:0' namespace. This element MUST have a 'xid' attribute with the XID corresponding to this private key. The content of the element MUST be the private key encoded as a hex string (lowercase). The message MUST be end-to-end encrypted with OMEMO Encryption (XEP-0384) [2] with Stanza Content Encryption (XEP-0420) [7] as explained in XEP-0384 §5.5 Sending a Message, and this message MUST be encrypted only for known devices of this entity.
Note: A SHOULD is used here and not a MUST because this feature is optional.
Juliet connects a new device (her tablet) and it discovers her XID but doesn't have the private key. It sends a request to her own bare JID, and her phone responds with the key.
<message type='chat'
from='juliet@capulet.lit/tablet'
to='juliet@capulet.lit'>
<encrypted xmlns='urn:xmpp:omemo:2'>
<header sid='12345'>
<keys jid='juliet@capulet.lit'>
<key rid='67890'><!-- base64 key --></key>
<key rid='09876'><!-- base64 key --></key>
</keys>
</header>
<payload>
<!-- The payload is normally encrypted and unreadable;
we show it here in plaintext to illustrate the protocol -->
<envelope xmlns='urn:xmpp:sce:1'>
<content>
<private-key-request xmlns='urn:xmpp:xid:0'
xid='0003a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8@id.internal'/>
</content>
<rpad>...</rpad>
<from jid='juliet@capulet.lit/tablet'/>
</envelope>
</payload>
</encrypted>
</message>
<message type='chat'
from='juliet@capulet.lit/phone'
to='juliet@capulet.lit'>
<encrypted xmlns='urn:xmpp:omemo:2'>
<header sid='67890'>
<keys jid='juliet@capulet.lit'>
<key rid='12345'><!-- base64 key --></key>
<key rid='09876'><!-- base64 key --></key>
</keys>
</header>
<payload>
<!-- The payload is normally encrypted and unreadable;
we show it here in plaintext to illustrate the protocol -->
<envelope xmlns='urn:xmpp:sce:1'>
<content>
<private-key xmlns='urn:xmpp:xid:0'
xid='0003a107bff3ce10be1d70dd18e74bc09967e4d6309ba50d5f1ddc8664125531b8@id.internal'>
000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f
</private-key>
</content>
<rpad>...</rpad>
<from jid='juliet@capulet.lit/phone'/>
</envelope>
</payload>
</encrypted>
</message>A server MAY support XID to and from JID mapping: if it has proven the identity and association between a JID and a XID (e.g., via the Identity Challenge mechanism), and an entity is using a XID in its 'from' attribute, it MUST convert any incoming stanza where the XID is used in the 'from' attribute to the corresponding JID, and it MUST convert any stanza sent to the corresponding entity by replacing the JID used in the 'to' attribute with the corresponding XID. A server that supports this feature MUST advertise it by including the 'urn:xmpp:xid:server-mapping:0' discovery feature in response to a Service Discovery (XEP-0030) [8] information request.
If a client supports XID, it MUST advertise it by including the 'urn:xmpp:xid:0' discovery feature in response to a Service Discovery (XEP-0030) [8] information request:
<iq type='get'
from='juliet@capulet.lit/balcony'
to='romeo@montague.lit/orchard'
id='disco1'>
<query xmlns='http://jabber.org/protocol/disco#info'/>
</iq>
<iq type='result'
from='romeo@montague.lit/orchard'
to='juliet@capulet.lit/balcony'
id='disco1'>
<query xmlns='http://jabber.org/protocol/disco#info'>
...
<feature var='urn:xmpp:xid:0'/>
...
</query>
</iq>A server that supports XID to and from JID mapping (see Server Mapping) MUST advertise the 'urn:xmpp:xid:server-mapping:0' discovery feature:
<iq type='result'
from='capulet.lit'
to='juliet@capulet.lit/balcony'
id='disco2'>
<query xmlns='http://jabber.org/protocol/disco#info'>
...
<feature var='urn:xmpp:xid:server-mapping:0'/>
...
</query>
</iq>This specification does not define any user interface elements and therefore has no accessibility considerations.
XID is designed to be independent of DNS-based identifiers, which can help reduce correlation by third parties. However, the XID itself is a stable identifier that can be used to track entities across different JIDs and servers. Entities should be aware that publishing their XID (especially with an "open" access model) makes this association publicly discoverable, and could be used for any kind of undesired tracking (advertisement, profiling, etc.).
Many OMEMO implementations use TOFU authentication scheme by default, or similar ones. If user devices have not been manually and correctly verified out of band, there is a risk that a malicious entity has set a compromised key (e.g., if the server is compromised). Among the problems involved (end-to-end encryption compromised), that would mean that Private Key Synchronization would potentially leak the private key to compromised devices. This is a problem of balance between UX and security.
Challenge/response pairs are protected against replay by the nonce mechanism. A verifying entity MUST NOT accept a response to the same nonce more than once.
This document requires no interaction with the Internet Assigned Numbers Authority (IANA).
The namespaces 'urn:xmpp:xid:0' and 'urn:xmpp:xid:server-mapping:0' should be included in the registry of protocol namespaces (see <https://xmpp.org/registrar/namespaces.html>).
The well-known PEP nodes 'urn:xmpp:xid' and 'urn:xmpp:xid:revoked' should be included in the registry of nodes for service discovery and publish-subscribe (see <https://xmpp.org/registrar/nodes.html>).
As authorized by XMPP URI Query Components (XEP-0147) [11], the XMPP Registrar maintains a registry of queries and key-value pairs for use in XMPP URIs (see <https://xmpp.org/registrar/querytypes.html>).
As described below, the registered querytypes for XID private key synchronization are "xid-private" and "xid-created".
The registered querytype "xid-private" contains the private key corresponding to the XID, encoded as a hex string (lowercase).
The registered querytype "xid-created" contains the creation datetime of the XID, using a DateTime value as specified in XMPP Date and Time Profiles (XEP-0082) [5].
This work has been done for the Serverless and Metadata Reduction for XMPP project. Many thanks to NLNet foundation and NGI Zero Core for funding the work on this specification.
TODO
This document in other formats: XML PDF
This XMPP Extension Protocol is copyright © 1999 – 2024 by the XMPP Standards Foundation (XSF).
Permission is hereby granted, free of charge, to any person obtaining a copy of this specification (the "Specification"), to make use of the Specification without restriction, including without limitation the rights to implement the Specification in a software program, deploy the Specification in a network service, and copy, modify, merge, publish, translate, distribute, sublicense, or sell copies of the Specification, and to permit persons to whom the Specification is furnished to do so, subject to the condition that the foregoing copyright notice and this permission notice shall be included in all copies or substantial portions of the Specification. Unless separate permission is granted, modified works that are redistributed shall not contain misleading information regarding the authors, title, number, or publisher of the Specification, and shall not claim endorsement of the modified works by the authors, any organization or project to which the authors belong, or the XMPP Standards Foundation.
## NOTE WELL: This Specification is provided on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. ##
In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall the XMPP Standards Foundation or any author of this Specification be liable for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising from, out of, or in connection with the Specification or the implementation, deployment, or other use of the Specification (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if the XMPP Standards Foundation or such author has been advised of the possibility of such damages.
This XMPP Extension Protocol has been contributed in full conformance with the XSF's Intellectual Property Rights Policy (a copy of which can be found at <https://xmpp.org/about/xsf/ipr-policy> or obtained by writing to XMPP Standards Foundation, P.O. Box 787, Parker, CO 80134 USA).
The HTML representation (you are looking at) is maintained by the XSF. It is based on the YAML CSS Framework, which is licensed under the terms of the CC-BY-SA 2.0 license.
The Extensible Messaging and Presence Protocol (XMPP) is defined in the XMPP Core (RFC 6120) and XMPP IM (RFC 6121) specifications contributed by the XMPP Standards Foundation to the Internet Standards Process, which is managed by the Internet Engineering Task Force in accordance with RFC 2026. Any protocol defined in this document has been developed outside the Internet Standards Process and is to be understood as an extension to XMPP rather than as an evolution, development, or modification of XMPP itself.
The primary venue for discussion of XMPP Extension Protocols is the <standards@xmpp.org> discussion list.
Discussion on other xmpp.org discussion lists might also be appropriate; see <https://xmpp.org/community/> for a complete list.
Errata can be sent to <editor@xmpp.org>.
The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all capitals, as shown here.
1. XEP-0283: Moved <https://xmpp.org/extensions/xep-0283.html>.
2. XEP-0384: OMEMO Encryption <https://xmpp.org/extensions/xep-0384.html>.
3. XEP-0374: OpenPGP for XMPP Instant Messaging <https://xmpp.org/extensions/xep-0374.html>.
4. XEP-0163: Personal Eventing Protocol <https://xmpp.org/extensions/xep-0163.html>.
5. XEP-0082: XMPP Date and Time Profiles <https://xmpp.org/extensions/xep-0082.html>.
6. XEP-0280: Message Carbons <https://xmpp.org/extensions/xep-0280.html>.
7. XEP-0420: Stanza Content Encryption <https://xmpp.org/extensions/xep-0420.html>.
8. XEP-0030: Service Discovery <https://xmpp.org/extensions/xep-0030.html>.
9. XEP-0395: Atomically Compare-And-Publish PubSub Items <https://xmpp.org/extensions/xep-0395.html>.
10. XEP-0060: Publish-Subscribe <https://xmpp.org/extensions/xep-0060.html>.
11. XEP-0147: XMPP URI Query Components <https://xmpp.org/extensions/xep-0147.html>.
Note: Older versions of this specification might be available at https://xmpp.org/extensions/attic/
First draft.
@report{poisson2026xid,
title = {XMPP Decentralized ID (XID)},
author = {Poisson, Jérôme},
type = {XEP},
number = {xxxx},
version = {0.0.1},
institution = {XMPP Standards Foundation},
url = {https://xmpp.org/extensions/xep-xxxx.html},
date = {2026-06-01/2026-06-01},
}END