1
0
mirror of https://github.com/moparisthebest/xeps synced 2025-02-16 07:10:15 -05:00

Add profiles / partial sync to entity versioning

This commit is contained in:
Sam Whited 2015-09-07 13:48:57 -05:00 committed by Matthew A. Miller
parent fb6a27747b
commit 61dd466023

View File

@ -8,10 +8,9 @@
<header> <header>
<title>Entity Versioning</title> <title>Entity Versioning</title>
<abstract> <abstract>
A method by which rosters and disco items may be versioned so that servers A method by which lists of items may be versioned so that servers will not
will not need to send the entire list if it has not been modified, saving need to send the entire list if it has not been modified, saving bandwidth
bandwidth and time during session initialization with minimal state being and time with minimal state being stored by the server and client.
stored by the server and client.
</abstract> </abstract>
&LEGALNOTICE; &LEGALNOTICE;
<number>xxxx</number> <number>xxxx</number>
@ -37,6 +36,12 @@
<surname>Keen</surname> <surname>Keen</surname>
<email>dkeen@atlassian.com</email> <email>dkeen@atlassian.com</email>
</author> </author>
<revision>
<version>0.0.2</version>
<date>2015-09-17</date>
<initials>ssw</initials>
<remark><p>Add profiles / parcial sync.</p></remark>
</revision>
<revision> <revision>
<version>0.0.1</version> <version>0.0.1</version>
<date>2015-08-25</date> <date>2015-08-25</date>
@ -48,17 +53,17 @@
<section1 topic='Introduction' anchor='intro'> <section1 topic='Introduction' anchor='intro'>
<p> <p>
This problem of "downloading the world" (downloading the entire roster This problem of "downloading the world" (downloading the entire roster
every time a session is initialized) was partially addressed by &xep0237; every time a session is initialized or receiving an entire disco items
which was later merged into &rfc6121; §2.6. While this solved the problem response every time a MUC list is queried, etc.) was partially addressed by
for the roster, it didn't account for other entities (eg. disco items). &xep0237; which was later merged into &rfc6121; §2.6. While this solved
the problem for the roster, it didn't account for other entities.
Furthermore, roster versioning requires that the server maintain a great Furthermore, roster versioning requires that the server maintain a great
deal of state (roster items which should be pushed for each entity on deal of state (roster items which should be pushed for each entity on
reconnect, or monotonically increasing counters, etc.) which can be reconnect, or monotonically increasing counters, etc.) which can be
difficult to store or synchronize in a large, distributed system. This XEP difficult to store or synchronize in a large, distributed system. This XEP
defines a method by which the roster and entities other than the roster can defines a method by which generic entity lists can be versioned and cached,
be versioned and cached, and which is optimized for distributed systems and which is optimized for distributed systems with large entity lists (but
with large enity lists (but works equally well on small, single server works equally well on small, single server deployments).
deployments).
</p> </p>
</section1> </section1>
@ -96,8 +101,8 @@
<dd>Any abstract object which may be versioned (eg. rooms, users).</dd> <dd>Any abstract object which may be versioned (eg. rooms, users).</dd>
<dt>Version Token</dt> <dt>Version Token</dt>
<dd> <dd>
A generally short, case sensitive string which represents an entity and A short, case sensitive string which represents an entity and changes if
changes if that entity changes. that entity changes.
</dd> </dd>
</dl> </dl>
</section1> </section1>
@ -144,55 +149,103 @@
</section2> </section2>
</section1> </section1>
<section1 topic='Entity Versioning Profiles' anchor='profiles'>
<p>
Because entity versioning is designed to be a generic system for syncing
any sort of list in XMPP, and the format and requirements of various entity
lists may vary greatly, no specific wire format is defined in this
specification. Instead, the specifics for various lists will be left up to
separate XEPs which will define entity versioning "profiles" which must be
registered with the XMPP registrar. These profiles will define exactly how
version tokens are represented in the specific list format for which they
wish to use entity versioning. The rest of this document will provide
details about entity versioning which will be common to all entity
versioning profiles and do not need to be redefined in EV profile XEPs. It
will also define an EV profile for fetching the roster.
</p>
<p>
The roster entity versioning profile which is used as an example throughout
this document will use the namespace 'urn:xmpp:entityver:profile:roster:0'
as described in the <link url="#registrar">XMPP Registrar
Considerations</link> section of this document.
</p>
</section1>
<section1 topic='Discovering Support' anchor='disco'> <section1 topic='Discovering Support' anchor='disco'>
<p> <p>
If a server supports entity versioning, it MUST inform the connecting If a server supports entity versioning, it MUST inform the connecting
client when returning stream features during the stream negotiation client when returning stream features during the stream negotiation
process. This is done by including a &lt;ver/&gt; element, qualified by process. This is done by including a &lt;ver/&gt; element, qualified by the
the 'urn:xmpp:entityver:0' namespace. At the latest, this SHOULD 'urn:xmpp:entityver:0' namespace with child &lt;profile&gt; nodes for each
be done when informing a client that resource binding is required. For supported entity versioning profile. At the latest, this SHOULD be done
example: when informing a client that resource binding is required. For example if
</p> the server only supports versioning of rosters it might return:
<example caption="Stream Features"><![CDATA[ </p>
<example caption="Stream Features"><![CDATA[
<stream:features> <stream:features>
<bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'> <bind xmlns='urn:ietf:params:xml:ns:xmpp-bind'>
<required/> <required/>
</bind> </bind>
<ver xmlns='urn:xmpp:entityver:0'/> <ver xmlns='urn:xmpp:entityver:0'>
<profile xmlns='urn:xmpp:entityver:profile:roster:0'/>
</ver>
</stream:features> </stream:features>
]]></example> ]]></example>
<p> <p>
The entity versioning stream feature is merely informative and therefore The entity versioning stream feature is merely informative and therefore
is never mandatory-to-negotiate. is never mandatory-to-negotiate.
</p> </p>
<p>
Clients, servers, and other entities that support &xep0030; and entity
versioning must respond to service discovery requests with a feature of
'urn:xmpp:entityver:0' and with a feature for each EV profile supported by
the responding entity as described in the relavant specifications. Eg. a
response from a server that supports roster versioning for the requesting
entity might look like the following:
<example caption="Service discovery information response"><![CDATA[
<iq from='shakespeare.lit'
id='ku6e51v3'
to='kingclaudius@shakespeare.lit/castle'
type='result'>
<query xmlns='http://jabber.org/protocol/disco#info'>
<feature var='urn:xmpp:entityver:0'/>
<feature var='urn:xmpp:entityver:profile:roster:0'/>
</query>
</iq>
]]></example>
</p>
</section1> </section1>
<section1 topic='Version Tokens' anchor='version_tokens'> <section1 topic='Entity Sync' anchor='entity_sync'>
<p> <section2 topic='Version Tokens' anchor='version_tokens'>
Version tokens are short case-sensitive strings which are generated by the <p>
server. Their format is not defined in this spec, but a recommendation may Version tokens are short case-sensitive strings which are generated by
be found in the <link url="#impl">Implementation Notes</link>. Version the server. Their format is not defined in this spec, but a
tokens are akin to a weakly-validated etag for the entity in question. recommendation may be found in the <link url="#impl">Implementation
</p> Notes</link>. Version tokens are akin to a weakly-validated etag for the
<p> entity in question.
Servers that implement this protocol must assign such a version token to </p>
each entity that is controlled by the server. The server MUST then update <p>
this version every time any mutable property of the entity changes (eg. Servers that implement this protocol must assign such a version token to
when the subscription status of a user changes). The server MAY choose to each entity that is controlled by the server. The server SHOULD then
update this token at any time (to force the clients to invalidate their update this version every time any mutable property of the entity changes
cached representation fo the object). This version token MUST then be (eg. when the subscription status of a user changes). The server MAY
included with every object representation of that entity sent down in the choose to update this token at any time (to force the clients to
stream. This is done by including a sub-node called "version" qualified invalidate their cached representation fo the object). This version token
by the entity versioning XML namespace defined in this document. MUST then be included with every object representation of that entity
Similarly, clients MAY also add version nodes for each version token they sent down in the stream. This is done by including a sub-node called
possess to the request for a list (not specifying a version token will "version" qualified by the entity versioning XML namespace defined in
force the server to send information on that entity to the client). If a this document. Similarly, clients MAY also add version nodes for each
server sends up a list of version tokens, the server MUST then check to version token they possess to the request for a list (not specifying a
see if those tokens correspond to any entity which it knows about, and version token will force the server to send information on that entity to
not send down any entities with matching version tokens in the response. the client). If a server sends up a list of version tokens, the server
</p> MUST then check to see if those tokens correspond to any entity which it
<p>For example, a roster request might look like:</p> knows about, and not send down any entities with matching version tokens
<example caption="Roster Request"><![CDATA[ in the response.
</p>
<p>
For example, a versioned roster request might look like this:
<example caption="Roster Request"><![CDATA[
<!-- Client --> <!-- Client -->
<iq from='romeo@montague.lit/home' id='56' to='romeo@montague.lit' type='get'> <iq from='romeo@montague.lit/home' id='56' to='romeo@montague.lit' type='get'>
<query xmlns='jabber:iq:roster'> <query xmlns='jabber:iq:roster'>
@ -213,64 +266,25 @@
</item> </item>
</query> </query>
</iq> </iq>
]]></example> ]]></example>
</p>
<p> <p>
Note that in this case there may be three roster items total (and the Note that in this case there may be three roster items total (and the
client only knows about two of them), or there may be two total roster client only knows about two of them), or there may be two total roster
items and the server is informing the client about a change to items and the server is informing the client about a change to
"bill@shakespeare.lit". Version tokens MUST also be present in roster "bill@shakespeare.lit". Version tokens MUST also be present in roster
pushes: pushes:
</p> <example caption="Roster Push"><![CDATA[
<example caption="Roster Push"><![CDATA[
<!-- Server -->
<iq from='romeo@montague.lit' id='ah382g678jka7' to='romeo@montague.lit/home' type='set'> <iq from='romeo@montague.lit' id='ah382g678jka7' to='romeo@montague.lit/home' type='set'>
<query xmlns='jabber:iq:roster' ver='ver34'> <query xmlns='jabber:iq:roster' ver='ver34'>
<item jid='tybalt@shakespeare.lit' subscription='remove'> <item jid='tybalt@shakespeare.lit' subscription='remove'>
<version xmlns='urn:xmpp:entityver:0'>XWE4MUUP</version> <version xmlns='urn:xmpp:entityver:0'>XWE4MUUP</version>
</item>
</query>
</iq>
]]></example>
<p>A disco request for rooms (as defined in &xep0045;) might look like:</p>
<example caption="MUC Disco"><![CDATA[
<!-- Client -->
<iq from='hag66@shakespeare.lit/phone'
id='zb8q41fas6yn4'
to='chat.shakespeare.lit'
type='get'>
<query xmlns='http://jabber.org/protocol/disco#items>
<item jid='coven@chat.shakespeare.lit'>
<version xmlns='urn:xmpp:entityver:0'>25P2A7H8</version>
</item>
<item jid='inverness@chat.shakespeare.lit'>
<version xmlns='urn:xmpp:entityver:0'>4OLGSVNY</version>
</item> </item>
</query> </query>
</iq> </iq>
]]></example>
<!-- Server --> </p>
<iq from='chat.shakespeare.lit' </section2>
id='zb8q41fas6yn4'
to='hag66@shakespeare.lit/phone'
type='result'>
<query xmlns='http://jabber.org/protocol/disco#items'>
<item jid='coven@chat.shakespeare.lit'
name='A Dark Cave'>
<version xmlns='urn:xmpp:entityver:0'>VIZSVF0D</version>
</item>
</query>
</iq>
]]></example>
<p>
In this example coven@chat.shakespeare.lit has been modified (eg. the
room name might have been changed), but inverness@chat.shakespeare.lit
has not changed, therefore no update is sent down.
</p>
<p>
Clients that implement this protocol SHOULD then cache the entity in
question when a version token is received.
</p>
<section2 topic='Cache Invalidation' anchor='deletes'> <section2 topic='Cache Invalidation' anchor='deletes'>
<p> <p>
When a client syncs with the server and indicates that it has a version When a client syncs with the server and indicates that it has a version
@ -278,127 +292,154 @@
the server wants to remove an entity from the clients cache for any other the server wants to remove an entity from the clients cache for any other
reason), the server MUST reply with an empty &lt;version/&gt; node. When reason), the server MUST reply with an empty &lt;version/&gt; node. When
the client receives such an empty version node it SHOULD purge the entity the client receives such an empty version node it SHOULD purge the entity
from its cache. For example, the following exchange would trigger the from its cache. For example, the following would remove the roster item
removal of 'inverness@chat.shakespeare.lit' from the cached MUC list: 'bill@shakespeare.lit' from the cache:
<example caption="Cache invalidation"><![CDATA[
<example caption="MUC deletion"><![CDATA[ <iq from='romeo@montague.lit' id='56' to='romeo@montague.lit/home' type='result>
<!-- Client --> <query xmlns='jabber:iq:roster'>
<iq from='hag66@shakespeare.lit/phone' <item subscription='both' jid='bill@shakespeare.lit'>
id='zb8q41fas6yn4' <version xmlns='urn:xmpp:entityver:0'/>
to='chat.shakespeare.lit' </item>
type='get'> </query>
<query xmlns='http://jabber.org/protocol/disco#items> </iq>
<item jid='coven@chat.shakespeare.lit'> ]]></example>
<version xmlns='urn:xmpp:entityver:0'>25P2A7H8</version>
</item>
<item jid='inverness@chat.shakespeare.lit'>
<version xmlns='urn:xmpp:entityver:0'>4OLGSVNY</version>
</item>
</query>
</iq>
<!-- Server -->
<iq from='chat.shakespeare.lit'
id='zb8q41fas6yn4'
to='hag66@shakespeare.lit/phone'
type='result'>
<query xmlns='http://jabber.org/protocol/disco#items'>
<item jid='inverness@chat.shakespeare.lit'>
<version xmlns='urn:xmpp:entityver:0'/>
</item>
</query>
</iq>
]]></example>
</p> </p>
<p> <p>
If the client receives an indication that it should delete an item from a Roster pushes that indicate a deleted item MUST also remove the version
list by any other means (eg. via a roster push), it SHOULD remove the from the cache (and need not contain an empty &lt;version/&gt; element).
version token associated with that entity from its cache.
</p> </p>
</section2> </section2>
</section1> <section2 topic='Partial sync'>
<section1 topic='Aggregate Tokens' anchor='agg_tokens'> <p>
<p> For very large groups fetching an entire list may not be practical or
While the version token approach to caching does not require a great deal necessary. For example, one might imagine a large corporation with a
of state to be stored on the client or the server, it does require a lot shared roster that is too large for its version tokens to be sent up to
more information to be sent by the client when requesting a list of the server on every sync, or even to download fully the first time. To
entities. For a very large list which is not likely to have changed, it may solve this, servers MAY choose to send down only a part of an entity list
be useful know in advance if the roster has changed or not (so that we can in response to a query (unless the individual EV profile forbids partial
avoid sending the large request entirely). To do this, we can request an list sync). How servers choose what items to return is an implementation
aggregate version token from the server. This aggregate token is calculated detail that is out of the scope of this document. Some suggestions may be
by constructing a string of comma separated "JID:version" pairs sorted in found in the <link url="#impl">Implementation Notes</link>. On subsequent
byte-wise order (because the JID:version pair is constructed before requests for the entity list, the server MAY choose to return more
sorting, if two items in the list have the same JID they can still be entities (eg. based on changes in its internal selection criteria),
sorted by the version token), and taking the MD5 hash of the constructed however it MUST NOT invalidate cached entities unless they have actually
string. For example, if the server is calculating the aggregate version been removed from the list.
token for a roster, it might end up with the following string: </p>
</p> <p>
<example caption="Aggregate token list"><![CDATA[ XEPs defining entity versioning profiles MUST include a section to
indicate if partial sync is supported, and if so, how it will be
indicated to the client (and how the client can request a full list). If
no mechanism is specified, this is done by adding a boolean "full_list"
attribute to the request, eg. a roster request for a partial list looks
like:
<example caption="Roster Request"><![CDATA[
<!-- Client -->
<iq from='romeo@montague.lit/home'
id='56'
to='romeo@montague.lit'
type='get'>
<query xmlns='jabber:iq:roster' full_list='false'>
<item jid='bill@shakespeare.lit'>
<version xmlns='urn:xmpp:entityver:0'>25P2A7H8</version>
</item>
<item jid='anne@shakespeare.lit'>
<version xmlns='urn:xmpp:entityver:0'>VIZSVF0D</version>
</item>
</query>
</iq>
<!-- Server -->
<iq from='romeo@montague.lit' id='56' to='romeo@montague.lit/home' type='result>
<query xmlns='jabber:iq:roster' full_list='false'>
<item subscription='both' jid='bill@shakespeare.lit'>
<version xmlns='urn:xmpp:entityver:0'>9ZFZXVP9</version>
</item>
</query>
</iq>
]]></example>
</p>
<p>
When making a request for a partial list, clients do not need to send up
every entity in their cache. Instead they MAY send up just those entities
for which they wish to check for updates. The server MUST then respond
with any updates for those entities, and MAY also add other entities to
the list if desired. If the client requests a partial list but does not
indicate that it has anything in its cache, what entities to return (if
any) is left up to the server implementation.
</p>
</section2>
<section2 topic='Aggregate Tokens' anchor='agg_tokens'>
<p>
While the version token approach to caching does not require a great deal
of state to be stored on the client or the server, it does require a lot
more information to be sent by the client when requesting a list of
entities. For a very large list which is not likely to have changed, it
may be useful know in advance if the roster has changed or not (so that
we can avoid sending the large request entirely). To do this, we can
request an aggregate version token from the server. This aggregate token
is calculated by constructing a string of comma separated "ID:version"
pairs sorted in byte-wise order (because the ID:version pair is
constructed before sorting, if two items in the list have the same ID
they can still be sorted by the version token), and taking the MD5 hash
of the constructed string. The ID in the pair is any ID or key that
identifies the entity as defined in its profile (eg. a JID for roster
items and most other entities). For example, if the server is calculating
the aggregate version token for a roster, it might end up with the
following string:
</p>
<example caption="Aggregate token list"><![CDATA[
anne@shakespeare.lit:VIZSVF0D,bill@shakespeare.lit:25P2A7H8 anne@shakespeare.lit:VIZSVF0D,bill@shakespeare.lit:25P2A7H8
]]></example> ]]></example>
<p> <p>
Which results in the aggregate token: Which results in the aggregate token:
</p> </p>
<example caption="Aggregate token"><![CDATA[ <example caption="Aggregate token"><![CDATA[
0514fc90e6c7981b06bbb2173bb8ef03 0514fc90e6c7981b06bbb2173bb8ef03
]]></example> ]]></example>
<p> <p>
The actual request is an IQ sent to the server, or entity handling the The actual request is an IQ sent to the server, or entity handling the
versioned list which contains a query that specifies the namespace of the versioned list which contains a query that specifies the namespace of the
list we want to fetch. Eg. to fetch the aggregate token for the roster one list we want to fetch. Eg. to fetch the aggregate token for the roster
would query the server with the type set to the `jabber:iq:roster` one would query the server with the query's XMLNS set to
namespace: 'urn:xmpp:entityver:profile:roster:0':
</p> </p>
<example caption="Roster aggregate token request"><![CDATA[ <example caption="Roster aggregate token request"><![CDATA[
<!-- Client --> <!-- Client -->
<iq to='bill@shakespeare.lit' type='get' id='bill1'> <iq to='bill@shakespeare.lit' type='get' id='bill1'>
<query xmlns='urn:xmpp:entityver:0' type='jabber:iq:roster' /> <query xmlns='urn:xmpp:entityver:profile:roster:0'/>
</iq> </iq>
<!-- Server --> <!-- Server -->
<iq to='bill@shakespeare.lit/home' type='result' id='bill1'> <iq to='bill@shakespeare.lit/home' type='result' id='bill1'>
<query xmlns='urn:xmpp:entityver:0' type='jabber:iq:roster'> <query xmlns='urn:xmpp:entityver:profile:roster:0'>
0514fc90e6c7981b06bbb2173bb8ef03 0514fc90e6c7981b06bbb2173bb8ef03
</query> </query>
</iq> </iq>
]]></example> ]]></example>
<p> <p>
Similarly, to fetch the aggregate token for a list of MUC rooms, one would Because aggregate tokens are OPTIONAL to implement, clients MUST fall
query the MUC component directly with the type set to the 'disco#items' back to making a normal list request if any error is returned in response
namespace: to an aggregate token IQ.
</p> </p>
<example caption="MUC rooms aggregate token request"><![CDATA[ <p>
<!-- Client --> If an aggregate token is requested for a list that may contain more than
<iq to='chat.shakespeare.lit' type='get' id='bill2'> one type of entity (eg. MUC rooms and pubsub nodes that live on the same
<query xmlns='urn:xmpp:entityver:0' type='http://jabber.org/protocol/disco#items' /> component), then the server MUST return the aggregate token constructed
</iq> with the entire list (rooms and pubsub nodes).
</p>
<!-- Server --> <p>
<iq to='bill@shakespeare.lit/home' type='result' id='bill2'> Because aggregate tokens are calculated for the entire list as seen by
<query xmlns='urn:xmpp:entityver:0' type='http://jabber.org/protocol/disco#items'> the client or server, they will never match if partial lists have been
32151d1d01440d5536a7f106afd3f4d8 downloaded by the client.
</query> </p>
</iq> <p>
]]></example> Clients are also NOT REQUIRED to check aggregate tokens. However, clients
<p> MAY wish to check aggregate tokens before making a roster or MUC request
Because aggregate tokens are OPTIONAL to implement, clients MUST fall back when the cached roster or MUC list is very large. When to check aggregate
to a normal request if any error is returned in response to an aggregate tokens (if at all) is left up to the implementation.
token IQ. </p>
</p> </section2>
<p>
If an aggregate token is requested for a list that may contain more than
one type of entity (eg. MUC rooms and pubsub nodes that live on the same
component), then the server MUST return the aggregate token constructed
with the entire list (rooms and pubsub nodes).
</p>
<p>
Clients are also NOT REQUIRED to check aggregate tokens. However, clients
MAY wish to check aggregate tokens before making a roster or MUC request
when the cached roster or MUC list is very large. When to check aggregate
tokens (if at all) is left up to the implementation.
</p>
</section1> </section1>
<section1 topic='Implementation Notes' anchor='impl'> <section1 topic='Implementation Notes' anchor='impl'>
<p> <p>
Version tokens may not provide enough collision resistance across versioned Version tokens may not provide enough collision resistance across versioned
@ -421,6 +462,22 @@ anne@shakespeare.lit:VIZSVF0D,bill@shakespeare.lit:25P2A7H8
version token MAY be used as a weakly validated ETag for any API requests version token MAY be used as a weakly validated ETag for any API requests
for that entity. for that entity.
</p> </p>
<p>
Servers following this specification may choose to send down partial entity
lists in response to queries. For the case of rosters one or more of the
following may be returned to the requesting entity during the initial
roster sync:
<ul>
<li>
Users that are grouped with the requester in some way. Eg. for a
company with a large shared roster which places the requesting client
in the "Marketing Department" group, the server may wish to return
roster items that also share that group.
</li>
<li>Users whom the requester has contacted recently or frequently.</li>
<li>Users that should always be returned as part of server policy.</li>
</ul>
</p>
</section1> </section1>
<section1 topic='Security Considerations' anchor='security'> <section1 topic='Security Considerations' anchor='security'>
@ -449,6 +506,53 @@ anne@shakespeare.lit:VIZSVF0D,bill@shakespeare.lit:25P2A7H8
&xep0053;. &xep0053;.
</p> </p>
</section2> </section2>
<section2 topic='Namespace Versioning' anchor='registrar-versioning'>
&NSVER;
</section2>
<section2 topic='Entity Versioning Profiles Registry' anchor='registrar-ev'>
<p>
The XMPP Registrar shall maintain a registry of entity versioning
profiles. All EV profile registrations shall be defined in separate
specifications (not in this document). Application types defined within
the XEP series MUST be registered with the XMPP Registrar, resulting in
protocol URNs of the form "urn:xmpp:entityver:profile:name:X" (where
"name" is the registered name of the profile and "X" is a non-negative
integer).
</p>
&REGPROCESS;
<code><![CDATA[
<profile>
<name>The name of the entity versioning profile.</name>
<desc>A natural-language summary of the profile.</desc>
<listdef>
The document in which the original list definition is specified.
</listdef>
<doc>
The document in which the EV profile for the list is specified (may be the
same as &lt;listdev/&gt;).
</doc>
</profile>
]]></code>
</section2>
<section2 topic='Entity Versioning Profiles' anchor='registrar-evprofile'>
<p>This specification defines the following entity versioning profile:</p>
<ul>
<li>urn:xmpp:entityver:profile:roster:0</li>
</ul>
<p>
Upon advancement of this specification from a status of Experimental to a
status of Draft, the &REGISTRAR; shall add the following definition to
the entity versioning profiles registry, as described in this document:
<code><![CDATA[
<profile>
<name>Roster entity versioning</name>
<desc>Allows versioning of entities in an XMPP roster.</desc>
<listdef>RFC 6121</listdef>
<doc>TODO: Insert this document once it is assigned a number</doc>
</profile>
]]></code>
</p>
</section2>
</section1> </section1>
<section1 topic='XML Schema' anchor='schema'> <section1 topic='XML Schema' anchor='schema'>