<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE rfc [
  <!ENTITY nbsp    "&#160;">
  <!ENTITY zwsp   "&#8203;">
  <!ENTITY nbhy   "&#8209;">
  <!ENTITY wj     "&#8288;">
]>
<!-- name="GENERATOR" content="github.com/mmarkdown/mmark Mmark Markdown Processor - mmark.miek.nl" -->
<rfc xmlns:xi="http://www.w3.org/2001/XInclude" version="3" ipr="trust200902" docName="draft-ietf-mimi-content-08" submissionType="IETF" category="info" xml:lang="en" indexInclude="true">

<front>
<title abbrev="MIMI Content">More Instant Messaging Interoperability (MIMI) message content</title><seriesInfo value="draft-ietf-mimi-content-08" stream="IETF" status="informational" name="Internet-Draft"/>
<author initials="R." surname="Mahy" fullname="Rohan Mahy"><organization>Rohan Mahy Consulting Services</organization><address><postal><street/>
</postal><email>rohan.ietf@gmail.com</email>
</address></author><date/>
<area>art</area>
<workgroup>MIMI</workgroup>
<keyword>mimi</keyword>
<keyword>content</keyword>
<keyword>mls</keyword>
<keyword>mime</keyword>

<abstract>
<t>This document describes content semantics common in Instant Messaging (IM)
systems and describes a profile suitable for instant messaging
interoperability of messages end-to-end encrypted inside the MLS
(Message Layer Security) Protocol.</t>
</abstract>

</front>

<middle>

<section anchor="terminology"><name>Terminology</name>
<t>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 <xref target="RFC2119"/> <xref target="RFC8174"/> when,
and only when, they appear in all capitals, as shown here.</t>
<t>The terms MLS client, MLS group, and KeyPackage have the same meanings as in
the MLS protocol <xref target="RFC9420"/>. Other relevant terminology can be
found in <xref target="I-D.ietf-mimi-arch"/>.</t>
</section>

<section anchor="introduction"><name>Introduction</name>
<t>RFC EDITOR: PLEASE REMOVE THE FOLLOWING PARAGRAPH. The source for
this draft is maintained in GitHub. Suggested changes should be
submitted as pull requests at <eref target="https://github.com/ietf-wg-mimi/draft-ietf-mimi-content">https://github.com/ietf-wg-mimi/draft-ietf-mimi-content</eref>.
Editorial changes can be managed in GitHub, but any substantive
change should be discussed on the MIMI mailing list (mimi@ietf.org).</t>
<t>MLS <xref target="RFC9420"/> is a group key establishment protocol
motivated by the desire for group chat with efficient end-to-end encryption.
While one of the motivations of MLS is interoperable standards-based secure
messaging, the MLS protocol does not define or prescribe any format for the
encrypted "application messages" encoded by MLS.  The development of MLS
was strongly motivated by the needs of a number of Instant Messaging (IM)
systems, which encrypt messages end-to-end using variations of the
Double Ratchet protocol <xref target="DoubleRatchet"/>.</t>
<t>End-to-end encrypted instant messaging was also a motivator for the Common
Protocol for Instant Messaging (CPIM) <xref target="RFC3862"/>, however the model used at the time
assumed standalone encryption of each message using a protocol such as S/MIME
<xref target="RFC8551"/> or PGP <xref target="RFC3156"/> to interoperate between IM protocols such as
SIP <xref target="RFC3261"/> and XMPP <xref target="RFC6120"/>.  For a variety of practical reasons, interoperable
end-to-end encryption between IM systems was never deployed commercially.</t>
<t>There are now several instant messaging vendors implementing MLS, and the
MIMI (More Instant Messaging Interoperability) Working Group is chartered
to standardize an extensible interoperable messaging format for common
features to be conveyed "inside" MLS application messages.</t>
<t>This document assumes that MLS clients advertise media types they support
and can determine what media types are required to join a
specific MLS group using the content advertisement extensions in Section 2.3 of
<xref target="I-D.ietf-mls-extensions"/>. It allows implementations to define MLS groups
with different media type requirements and allows MLS clients to send
extended or proprietary messages that would be interpreted by some members
of the group while assuring that an interoperable end-to-end encrypted
baseline is available to all members, even when the group spans multiple
systems or vendors.</t>
<t>Below is a list of some features commonly found in IM group chat systems:</t>

<ul spacing="compact">
<li>plain text and rich text messaging</li>
<li>mentions</li>
<li>replies</li>
<li>reactions</li>
<li>edit or delete previously sent messages</li>
<li>expiring messages</li>
<li>delivery notifications</li>
<li>read receipts</li>
<li>shared files/audio/videos</li>
<li>calling / conferencing</li>
<li>message threading</li>
</ul>
<t>Delivery notifications and read receipts are addressed in
{{?I-D.mahy-mimi-message-status}}. Calling and conferencing will also be
addressed in another document.</t>
</section>

<section anchor="overview"><name>Overview</name>

<section anchor="binary-encoding"><name>Binary encoding</name>
<t>The MIMI Content format is encoded in Concise Binary Object Representation
(CBOR) <xref target="RFC8949"/>. The Working Group chose a binary format in part because:</t>

<ul spacing="compact">
<li>we do not want to scan body parts to check for boundary marker
collisions. This rules out using multipart MIME types.</li>
<li>we do not want to base64 encode body parts with binary media
types (ex: images). This rules out using JSON to carry the binary data.</li>
</ul>
<t>All examples start with an instance document annotated in the CBOR
Extended Diagnostic Notation (described in [Appendix G of @!RFC8610] and
more rigorously specified in <xref target="I-D.ietf-cbor-edn-literals"/>), and then
include a hex dump of the CBOR data in the pretty printed format popularized
by the CBOR playground website (<eref target="https://cbor.me">https://cbor.me</eref>) with some minor whitespace
and comment reformatting. Finally, a message ID for the message is included
for most messages.</t>
<t>All the instance documents validate using the CDDL schemas in Appendix B and
are included in the examples directory in the github repo for this document.</t>
</section>

<section anchor="naming-schemes"><name>Naming schemes</name>
<t>IM systems have a number of types of identifiers. These are described in detail
in <xref target="I-D.mahy-mimi-identity"/>. A few of these used in this document are:</t>

<ul spacing="compact">
<li>handle identifier (external, friendly representation). This is the type
of identifier described later as the senderUserUrl in the examples, which
is analogous to the From header in email.</li>
<li>client/device identifier (internal representation). This is the type
of identifier described as the senderClientUrl in the examples.</li>
<li>group or room or conversation or channel name (either internal or external representation).
This is the type of identifier described as the MLS group URL in the examples.</li>
</ul>
<t>This proposal relies on URIs for naming and identifiers. All the example use
the <tt>im:</tt> URI scheme (defined in <xref target="RFC3862"/>), but any instant messaging scheme
could be used.</t>
</section>

<section anchor="message-id"><name>Message ID</name>
<t>The MIMI content format relies heavily on message IDs to refer to other
messages, to reply, react, edit, delete, and report on the status of
messages. Every MIMI content message contains a 16-octet per-message
cryptographically random salt, and has a 32-octet message ID which is calculated
from the hash of the message (including the salt), the sender URI, and the
room URI.</t>
<t>Calculation of the message ID works as follows. The first octet of the MessageID
is the hash function ID from the
<eref target="https://www.iana.org/assignments/named-information/named-information.xhtml#hash-alg">IANA hash algorithm registry</eref>.
The senderUriLength and roomUriLength are big-endian unsigned 16-bit integers
representing the length of the sender URI, and the room URI, respectively.
The senderUriLength, sender URI, roomUriLength, room URI, the entire MIMI
message content (including the salt), and the salt (again) are all concatenated,
and then hashed with the algorithm identified in the first octet. The first 31
octets of the hash_output is appended to the hash function ID.</t>

<artwork><![CDATA[hash_output = hash( senderUriLength || senderUri ||
                    roomUriLength   || roomUri   ||
                    message         || salt )
messageId = hashAlg || hash_output[0..30]
]]></artwork>
<t>The MIMI content format uses the SHA-256 hash algorithm (identifier 0x01) by
default, regardless of the hash algorithm of the cipher suite of a room's MLS
group. The initial octet allows the MIMI protocol to deprecate SHA-256 and
specify a new default algorithm in the future (for example if a practical
birthday attack on SHA_256 becomes feasible).</t>
<blockquote><t>The salt is duplicated in the input to the hash to avoid a SHA-256 length
extension attack.</t>
</blockquote></section>

<section anchor="accepted-timestamp"><name>Accepted Timestamp</name>
<t>As described in the MIMI architecture <xref target="I-D.ietf-mimi-arch"/>, one
provider, called the hub, is responsible for ordering messages. The hub is
also responsible for recording the time that any application message is
accepted, and conveying it to any "follower" providers which receive messages
from the group. It is represented as the whole number of milliseconds since
the start of the UNIX epoch (01-Jan-1970 00:00:00 UTC). The accepted timestamp
MUST be available to each receiving MIMI client. The client can use it
for fine grain sorting of messages into a consistent order.</t>
</section>

<section anchor="message-container"><name>Message Container</name>
<t>Most common instant messaging features are expressed as individual messages.
A plain or rich text message is obviously a message, but a reaction (ex: like),
a reply, editing a previous message, deleting an earlier message, and read
receipts are all typically modeled as another message with different properties.</t>
<t>This document describes the semantics of a message container, which
can represent most of these previously mentioned message types.
The container typically carries one or more body parts with the actual message
content (for example, an emoji used in a reaction, a plain text or rich text
message or reply, a link, or an inline image).</t>
</section>
</section>

<section anchor="mimi-content-container-message-semantics"><name>MIMI Content Container Message Semantics</name>
<t>Each MIMI Content message is a container format with two categories
of information:</t>

<ul spacing="compact">
<li>the message behavior fields (which can have default or empty values), and</li>
<li>the body part(s) and associated parameters</li>
</ul>
<t>The object fields in the structure defined below are numbered in
curly braces for reference in the text.</t>
<t>The subsections that follow contain snippets of Concise Data Definition
Language (CDDL) <xref target="RFC8610"/> schemas for the MIMI Content Container. The complete collected CDDL schema for MIMI Content Container is available in
<xref target="full-schema"/>.</t>

<section anchor="message-behavior-fields"><name>Message Behavior Fields</name>

<sourcecode type="cddl"><![CDATA[mimiContent = [
  salt: bstr .size 16,
  replaces: null / MessageId,       ; {1}
  topicId: bstr,                    ; {2}
  expires: null / Expiration,       ; {3}
  inReplyTo: null / MessageId,      ; {4}
  mimiExtensions: extensions,       ; {6}
  nestedPart: NestedPart            ; {7}
]

MessageId = bstr .size 32
]]></sourcecode>
<t>The first data field is the per-message unique salt which MUST be
cryptographically random. An example algorithm for generating the salt
is described in <xref target="salt-generation"/>.</t>
<t>The <tt>replaces</tt> {1} data field indicates that the current message
is a replacement or update to a previous message whose message ID
is in the <tt>replaces</tt> data field. It is used to edit previously-sent
messages, delete previously-sent messages, and adjust reactions to
messages to which the client previously reacted.
If the <tt>replaces</tt> field is null, the receiver
assumes that the current message has not identified any special
relationship with another previous message.</t>
<t>The <tt>topicId</tt> {2} data field indicates that the current message is
part of a logical grouping of messages which all share the same
value in the <tt>topicId</tt> data field. If the <tt>topicId</tt> is zero length,
there is no such grouping.</t>
<t>The <tt>expires</tt> {3} data field is a hint from the sender to the receiver
that the message should be locally deleted and disregarded at either a specific
timestamp in the future, or a relative amount of time after the receiving client
reads the message. Indicate a message with no specific expiration
time with the value null. If non-null, the data field is an array of two items.</t>

<sourcecode type="cddl"><![CDATA[Expiration = [
    relative: bool,
    time: uint .size 4
]
]]></sourcecode>
<t>The first is a boolean indicating if the time is relative (true) or absolute
(false). The second is an unsigned integer. If relative, it is the whole number
of seconds the message should be visible before it is deleted. If absolute, it
is the number of seconds after the start of the UNIX epoch, at which point the
message should be deleted. Using an 32-bit unsigned integer allows expiration
dates until the year 2106. Note that specifying an expiration time provides no
assurance that the client actually honors or can honor the expiration time, nor
that the end user didn't otherwise save the expiring message (ex: via a
screenshot).</t>
<t>The <tt>inReplyTo</tt> {4} data field indicates that the current message is
a related continuation of another message sent in the same MLS group.
If present, it contains the message ID of the referenced message. Otherwise,
the receiver assumes that the current message has not identified any special
reply relationship with another previous message. The message id of the
referenced message is also used to make sure that a MIMI
message cannot refer to a sequence of referred messages which refers
back to itself. When replying, a client MUST NOT knowingly create a sequence
of replies which create a loop.</t>
<t>When receiving a message with inReplyTo message, the client checks if the
referenced message is itself inReplyTo another message. If so, it continues
following the referenced messages, checking that the message ID of none of the
referenced messages "loop" back to a message later in the inReplyTo chain.</t>
<t>Note that a <tt>inReplyTo</tt>
always references a specific message ID. Even if the original message
was edited several times, a reply always refers to a specific version
of that message, and SHOULD refer to the most current version at the
time the reply is sent.</t>
</section>

<section anchor="message-ordering"><name>Message Ordering</name>
<t>Message ordering is provided by the Hub in the form of the accepted
timestamp. A malicious hub may be able to manipulate the order of
messages.</t>
<t>Imagine however that two users (Bob and Cathy) see a message from Alice
offering free Hawaiian pizza, and reply at the same time. Bob and Cathy both send
messages somehow referencing (Alice's)
message about pizza.  Their messages don't need to be replies or reactions.
Bob might just send a message saying he doesn't like pineapple on pizza.</t>
<t>If clients want to detect messages sent out of order by the hub, they
require notification of message delivery at the MLS level (ex: the AppAck
mechanism provided in <xref target="I-D.ietf-mls-extensions"/>) or at the MIMI level,
such as the format defined in {{?I-D.mahy-mimi-message-status}}.</t>
</section>

<section anchor="extension-fields"><name>Extension Fields</name>
<t>In order to add additional functionality to MIMI, senders can include
extension fields in the message format {6}. Each extension has a CBOR map key
which is a positive integer, negative integer, or text string containing between
1 and 255 octets of UTF-8. The value can be any CBOR (including combinations of
maps and arrays).
The message content <tt>mimiExtensions</tt> field MUST NOT include more than one
extension field with the same map key.</t>

<sourcecode type="cddl"><![CDATA[extensions = {
  ? &(senderUri: 1) ^ => tstr,
  ? &(roomUri: 2) ^ => tstr,
  * $$otherKnownExtensions,
  * unknownExtension
}

unknownExtension = (
   name => value
)
name = int / tstr .size (1..255)
value = any .size (0..4095)
]]></sourcecode>
<t>An IANA registry <xref target="keys-registry"/> is defined for
positive integer keys. Negative integer and text string keys are only for
private use.</t>
</section>

<section anchor="message-bodies"><name>Message Bodies</name>
<t>Every MIMI content message has a body {7} which can have multiple,
possibly nested parts. A body with zero parts is permitted when
deleting or unliking. External body parts <xref target="external"/> are also supported.
When there is a single (inline) part or a (single) externally reference
part, its IANA media type, subtype, and parameters are included in the
contentType field {8}.</t>

<sourcecode type="cddl"><![CDATA[NestedPart = [
  disposition: baseDispos / $extDispos / unknownDispos,  ; {10}
  language: tstr,                                        ; {11}
  ( NullPart // SinglePart // ExternalPart // MultiPart)
]

NullPart = ( cardinality: nullpart )

SinglePart = (
    cardinality: single,
    contentType: tstr,        ; {8}
    content: bstr
)

ExternalPart = (
    cardinality: external,
    contentType: tstr,
    url: tstr,
    expires: uint .size 4,
    size: uint .size 8,
    encAlg: uint .size 2,
    key: bstr,
    nonce: bstr,
    aad: bstr,
    hashAlg: uint .size 1,
    contentHash: bstr,
    description: tstr,
    filename: tstr
)

MultiPart = (
    cardinality: multi,
    partSemantics: chooseOne / singleUnit / processAll,
    parts: [2* NestedPart]
)

; cardinality
nullpart = 0
single   = 1
external = 2
multi    = 3

; part semantics {9}
chooseOne  = 0  ; receiver picks exactly one part to process
singleUnit = 1  ; receiver processes all parts as single unit
processAll = 2  ; receiver processes all parts individually
]]></sourcecode>
<t>With some types of message content, there are multiple media types
associated with the same message which need to be rendered together,
for example a rich-text message with an inline image. With other
messages, there are multiple choices available for the same content,
for example a choice among multiple languages, or between two
different image formats. The relationship semantics among the parts
is specified as an enumeration {9}.</t>
<t>The <tt>chooseOne</tt> part semantic is roughly analogous to the semantics of the
<tt>multipart/alternative</tt> media type, except that the ordering of the
nested body parts is merely a preference of the sender. The receiver
can choose the body part among those provided according to its own
policy.</t>
<t>The <tt>singleUnit</tt> part semantic is roughly analogous to the semantics
of the <tt>multipart/related</tt> media type, in that all the nested body
parts at this level are part of a single entity (for example, a
rich text message with an inline image). If the receiver does not
understand even one of the nested parts at this level, the receiver
should not process any of them.</t>
<t>The <tt>processAll</tt> part semantic is roughly analogous to the semantics
of the <tt>multipart/mixed</tt> media type. The receiver should process as
many of the nested parts at this level as possible. For example, a
rich text document with a link, and a preview image of the link target
could be expressed using this semantic. Processing the preview image
is not strictly necessary for the correct rendering of the rich text
part.</t>
<t>The disposition {10} and language {11} of each part can be specified
for any part, including for nested parts. The disposition represents
the intended semantics of the body part or a set of nested parts.
It is inspired by the values in the Content-Disposition MIME header
<xref target="RFC2183"/>. Disposition values are defined in <xref target="dispo-registry"/>.</t>

<sourcecode type="cddl"><![CDATA[baseDispos = &(
    unspecified: 0,
    render: 1,
    reaction: 2,
    profile: 3,
    inline: 4,
    icon: 5,
    attachment: 6,
    session: 7,
    preview: 8
)
; Note: any ext_dispos take precedence
unknownDispos = &( unknown: 9..255 )
]]></sourcecode>
<t>The <tt>render</tt> disposition means that the content should be rendered
according to local policy. The  <tt>inline</tt> dispositions means that the
content should be rendered "inline" directly in the chat interface.
The <tt>attachment</tt> disposition means that the content is intended to
be downloaded by the receiver instead of being rendered immediately.
The <tt>reaction</tt> disposition means that the content is a single
reaction to another message, typically an emoji, but which could be
an image, sound, or video. The <tt>reaction</tt> disposition was originally published
in <xref target="RFC9078"/>, but was incorrectly placed in the Content Disposition
Parameters IANA registry instead of in the Content Disposition Values
registry.
The <tt>session</tt> disposition means that the content is a description of
a multimedia session, or a URI used to join one.
The <tt>preview</tt> disposition means that the content is a sender-generated
preview of something, such as the contents of a link.</t>
<t>The value of the language data field is an empty string or a
comma-separated list of one or more <tt>Language-tag</tt>s as defined
in <xref target="RFC5646"/>.</t>
<t>Each part also has an implied part index, which is a zero-indexed,
depth-first integer. It is used to efficiently refer to a specific
body part (for example, an inline image) within another part. See
<xref target="nesting-example"/> for an example of how the part index is
calculated.</t>
<t>The partIndex can be used inside a content ID URI <xref target="RFC2392"/> in a "container"
part (for example HTML, Markdown, vCard <xref target="RFC6350"/>, or iCal <xref target="RFC5545"/>) to
reference another part inside the same MIMI message. In a MIMI message it has
the form <tt>cid:</tt><em>partIndex</em><tt>@local.invalid</tt>.
This format of the content ID URI in MIMI MUST only reference the <tt>partIndex</tt>
of a SinglePart or ExternalPart.</t>
<t>When processing a MultiPart nested structure, the client can start from the
<tt>body</tt> in the MIMI content (the "top-level" or "root") and evaluate any
<tt>chooseOne</tt> semantics MultiPart elements, effectively discarding non-chosen
Parts. The remaining Parts might still reference each other in content ID URI.
To prevent processing a Part more than once, the client can handle the remaining
Parts in order, skipping any Parts already referenced by a previously handled
Part. This process of skipping already processed Parts is respected regardless
of the disposition of the Part.</t>
</section>

<section anchor="external"><name>External content</name>
<t>It is common in Instant Messaging systems to reference external
content via URI that will be processed automatically, either to
store bulky content (ex: videos, images, recorded sounds) outside
the messaging infrastructure, or to access a specific service URI,
for example, a media forwarding service for conferencing.</t>
<t>An <tt>ExternalPart</tt> is a convenient way to reference this content. It
provides a similar function to the <tt>message/external-body</tt> media type.
It optionally includes the size of the data in octets (or zero if
the length is not provided). It also includes an optional timestamp
after which the external content is invalid, expressed as seconds
since the start of the UNIX epoch (01-Jan-1970), or zero if the
content does not expire.</t>

<sourcecode type="cddl"><![CDATA[ExternalPart = (
    cardinality: external,
    contentType: tstr,     ; An IANA media type {8}
    url: uri,              ; A URL where the content can be fetched
    expires: uint .size 4, ; expiration in seconds since UNIX epoch
    size: uint .size 8,    ; size of content in octets
    encAlg: uint .size 2,  ; An IANA AEAD Algorithm number, or zero
    key: bstr,             ; AEAD key
    nonce: bstr,           ; AEAD nonce
    aad: bstr,             ; AEAD additional authentiation data
    hashAlg: uint .size 1, ; An IANA Named Information Hash Algorithm
    contentHash: bstr,     ; hash of the content at the target url
    description: tstr,     ; an optional text description
    filename: tstr         ; an optional suggested filename
)
]]></sourcecode>
<t>Typically, external content is encrypted with an ephemeral symmetric
key before it is uploaded, and whatever is necessary for decryption
is shared over the message channel.</t>
<t>It is a matter of local policy to where the content is uploaded. Often
in federated messaging systems, the sender of the content stores the
external content in their own domain, but in some systems the content
is stored in the "owning" or "hub" domain of the MLS group.</t>
<t>Before being uploaded, private external content is encrypted with an
IANA-registered Authenticated Encryption with Additional Data (AEAD)
algorithm as described in <xref target="RFC5116"/>. The key, nonce, and additional
authenticated data (aad) values are set to the values used during the
encryption. Unless modified by an extension, the default value of the
<tt>aad</tt> is empty.</t>
<t>If the external URL is a service, or the external content is not considered
private, the <tt>encAlg</tt> is set to zero, and the <tt>key</tt>, <tt>nonce</tt>, and <tt>aad</tt>
fields are zero length.</t>
<t>Implementations of this specification MUST implement the AES-128-GCM
algorithm.</t>
</section>

<section anchor="derived-data-values"><name>Derived Data Values</name>
<t>In addition to fields which are contained in a MIMI content message,
there is the hub accepted timestamp, which can be represented either
as milliseconds since the start of the UNIX epoch, or for future
extensibility as a CBOR extended time tag as defined in {{Section 3 of
!RFC9581}}. There are also two fields the implementation can definitely derive:
(the MLS group ID {12}, and the leaf index of the sender {13}). Many
implementations could also determine one or more of: the sender's client
identifier URL {14}, the user identifier URL of the credential associated
with the sender {15}, and the identifier URL for the MIMI room {16}.</t>

<sourcecode type="cddl"><![CDATA[MessageDerivedValues = [
    messageId: MessageId,
    hubAcceptedTimestamp: Timestamp,
    mlsGroupId: bstr,                  ; value always available {12}
    senderLeafIndex: uint .size 4,     ; value always available {13}
    senderClientUrl: uri               ; {14},
    senderUserUrl: uri,                ; "From" {15}
    roomUrl: uri                       ; "To"   {16}
]

MessageId = bstr .size 32
Timestamp = MsecsSinceEpoch / ExtendedTime
; milliseconds since start of UNIX epoch
MsecsSinceEpoch = uint .size 8
; extended time from RFC9581
ExtendedTime = #6.1001({* name => value })
]]></sourcecode>
</section>
</section>

<section anchor="examples"><name>Examples</name>
<t>In the following examples, we assume that an MLS group is already established and
that either out-of-band or using the MLS protocol or MLS extensions, or
their client to provider protocol that the following is known to every
member of the group:</t>

<ul spacing="compact">
<li>The membership of the group (via MLS).</li>
<li>The identity of any MLS client which sends an application message (via MLS).</li>
<li>The MLS group ID (via MLS)</li>
<li>The human-readable name(s) of the MIMI room, if any (out-of-band or extension).</li>
<li>Which media types are mandatory to implement (MLS content advertisement extensions).</li>
<li>For each member, the media types each supports (MLS content advertisement extensions).</li>
</ul>
<t>Messages sent to an MLS group are delivered to every member of the group
active during the epoch in which the message was sent.</t>
<t>All the examples start with a CBOR instance document annotated in the
Extended Diagnostic Format (described in [Appendix G of @!RFC8610] and more
rigorously specified in <xref target="I-D.ietf-cbor-edn-literals"/>), and then include a
hex dump of the CBOR data in the pretty printed format popularized by the
CBOR playground website (<eref target="https://cbor.me">https://cbor.me</eref>) with some minor whitespace and
comment reformatting. Finally, a message ID for the message is included for
most messages.</t>
<t>All the instance documents validate using the CDDL schemas in Appendix B and
are included in the examples directory in the github repo for this document.</t>

<section anchor="original-message"><name>Original Message</name>
<t>In this example, Alice Smith sends a rich-text (Markdown) <xref target="RFC7763"/>
message to the Engineering Team room. The following values are
derived from the client, except for the hub received timestamp, which
needs to be made available to the client by its provider:</t>

<ul spacing="compact">
<li>Sender MLS leaf index: 4</li>
<li>Sender MLS client ID URL:
mimi://example.com/d/3b52249d-68f9-45ce-8bf5-c799f3cad7ec/0003</li>
<li>MLS group ID:
7u4NEqe1tbeBFa0aHdsTgRyD_XOHxD5meZpZS-7aJr8</li>
<li>The MIMI room name: "Engineering Team"</li>
<li>The Hub received timestamp:
1644387225019 = 2022-02-09T06:13:45.019Z</li>
</ul>
<t>Below is the message in annotated Extended Diagnostic Notation, and pretty
printed CBOR.</t>

<sourcecode type="edn"><![CDATA[# ORIGINAL
# message ID = h'017ce54837404c3696e0c747b985cb17
#                2716d0ed0a3d249ca63ace7d82a096f4'
# timestamp  = 1644387225019 = 2022-02-09T06:13:45.019Z
[
  h'5eed9406c2545547ab6f09f20a18b003', # salt
  null,                                # replaces
  h'',                                 # topicId
  null,                                # expires = never
  null,                                # inReplyTo
  {                                    # extensions
    1: "mimi://example.com/u/alice-smith",
    2: "mimi://example.com/r/engineering_team"
  },
  [                                    # body (NestedPart)
    1,                                   # disposition = render
    "",                                  # language
    1,                                   # cardinality = single part
    "text/markdown;variant=GFM-MIMI",    # contentType
                                         # content
    'Hi everyone, we just shipped release 2.0. __Good  work__!'
  ]
]
]]></sourcecode>

<artwork><![CDATA[87                                      # array(7)
   50                                   # bytes(16)
      5eed9406c2545547ab6f09f20a18b003
   f6                                   # primitive(22)
   40                                   # bytes(0)
   f6                                   # primitive(22)
   f6                                   # primitive(22)
   a2                                   # map(2)
      01                                # unsigned(1)
      78 20                             # text(32)
         6d696d693a2f2f6578616d706c652e63
         6f6d2f752f616c6963652d736d697468
         # "mimi://example.com/u/alice-smith"
      02                                # unsigned(2)
      78 25                             # text(37)
         6d696d693a2f2f6578616d706c652e63
         6f6d2f722f656e67696e656572696e67
         5f7465616d
         # "mimi://example.com/r/engineering_team"
   85                                   # array(5)
      01                                # unsigned(1)
      60                                # text(0)
                                        # ""
      01                                # unsigned(1)
      78 1e                             # text(30)
         746578742f6d61726b646f776e3b7661
         7269616e743d47464d2d4d494d49
         # "text/markdown;variant=GFM-MIMI"
      58 39                             # bytes(57)
         48692065766572796f6e652c20776520
         6a75737420736869707065642072656c
         6561736520322e302e205f5f476f6f64
         2020776f726b5f5f21
         # "Hi everyone, we just shipped release 2.0. __Good  work__!"
]]></artwork>
<t>Below are the rest of the implied values for this message:</t>

<sourcecode type="edn"><![CDATA[[
  / messageId = Original message /
  h'017ce54837404c3696e0c747b985cb17
    2716d0ed0a3d249ca63ace7d82a096f4',
  
  / hubAcceptedTimestamp = 2022-02-08T22:13:45.019Z /
  1644387225019,

  / mlsGroupId /
  h'eeee0d12a7b5b5b78115ad1a1ddb1381
    1c83fd7387c43e66799a594beeda26bf',
  
  / senderLeafIndex /
  4,
  
  / senderClientUrl /
  "mimi://example.com/d/3b52249d-68f9-45ce-8bf5-c799f3cad7ec/0003",

  / senderUserUrl /
  "mimi://example.com/u/alice-smith",
  
  / roomUrl /
  "mimi://example.com/r/engineering_team"
]
]]></sourcecode>
</section>

<section anchor="reply"><name>Reply</name>
<t>A reply message looks similar, but contains the message ID of the
original message in the <tt>inReplyTo</tt> data field. The derived MLS
group ID, URL, and name do not change in this example. The derived
senderClientId and senderLeafIndex are not especially relevant so
only the user handle URL, message ID, and hub received timestamp
are provided (in comments at the beginning of the annotated EDN).
The annotated EDN follows, then the pretty printed CBOR.</t>

<sourcecode type="edn"><![CDATA[# REPLY
# message ID = h'015354973c2b65ca937bf1e035ae53a5
#                ab80e947afa43d46920d4202e5cc0b27'
# timestamp  = 1644387237492 = 2022-02-09T06:13:57.492Z
[
  h'11a458c73b8dd2cf404db4b378b8fe4d', # salt
  null,                                # replaces
  h'',                                 # topicId
  null,                                # expires
  h'017ce54837404c3696e0c747b985cb17   # inReplyTo =
    2716d0ed0a3d249ca63ace7d82a096f4', #   Original
  {                                    # extensions
    1: "mimi://example.com/u/bob-jones",
    2: "mimi://example.com/r/engineering_team"
  },
  [                                    # body (NestedPart)
    1,                                   # disposition = render
    "",                                  # language
    1,                                   # cardinality = single part
    "text/markdown;variant=GFM-MIMI",    # contentType
                                         # content
    'Right on! _Congratulations_ \'all!'
  ]
]
]]></sourcecode>

<artwork><![CDATA[87                                      # array(7)
   50                                   # bytes(16)
      11a458c73b8dd2cf404db4b378b8fe4d
   f6                                   # primitive(22)
   40                                   # bytes(0)
   f6                                   # primitive(22)
   58 20                                # bytes(32)
      01b0084467273cc43d6f0ebeac13eb84
      229c4fffe8f6c3594c905f47779e5a79
   a2                                   # map(2)
      01                                # unsigned(1)
      78 1e                             # text(30)
         6d696d693a2f2f6578616d706c652e63
         6f6d2f752f626f622d6a6f6e6573
         # "mimi://example.com/u/bob-jones"
      02                                # unsigned(2)
      78 25                             # text(37)
         6d696d693a2f2f6578616d706c652e63
         6f6d2f722f656e67696e656572696e67
         5f7465616d
         # "mimi://example.com/r/engineering_team"
   85                                   # array(5)
      01                                # unsigned(1)
      60                                # text(0)
                                        # ""
      01                                # unsigned(1)
      78 1e                             # text(30)
         746578742f6d61726b646f776e3b7661
         7269616e743d47464d2d4d494d49
         # "text/markdown;variant=GFM-MIMI"
      58 21                             # bytes(33)
         5269676874206f6e21205f436f6e6772
         6174756c6174696f6e735f2027616c6c
         21
         # "Right on! _Congratulations_ 'all!"
]]></artwork>
</section>

<section anchor="reaction"><name>Reaction</name>
<t>A reaction looks like a reply, but uses the Disposition token of reaction. It is
modeled on the reaction Content-Disposition token defined in <xref target="RFC9078"/>.
Both indicate that the intended disposition of the
contents of the message is a reaction.</t>
<t>The content in the sample message is a single Unicode heart character (U+2665)
which is expressed in UTF-8 as the three octet sequence 0xe299a5.
Often a single text reaction consists of multiple Unicode characters. For
example, the five Unicode characters for a medium skin-toned, female health
worker (U+1F469 U+1F3FD U+200D U+2695 U+FE0F: woman, medium skin tone, combined
with, staff of Aesculapius, present as image) are typically rendered as a single
glyph. This sequence is expressed in UTF-8 as the 17-octet sequence
0xf09f91a9f09f8fbde2808de29a95efb88f.</t>
<t>Discovering the range of characters each implementation could render as a
reaction can occur out-of-band and is not within the scope of this proposal.
However, an implementation which receives a reaction character string it
does not recognize could render the reaction as a reply, possibly prefixing
with a localized string such as "Reaction: ".  Note that a reaction could
be another media type (ex: image, audio, or video), although
not as universally implemented in instant messaging systems.</t>
<t>Note that many systems allow multiple independent reactions per sender.
When multiple reactions are supported, each reaction could be represented
either as a separate part inside a MultiPart with <tt>processAll</tt> semantics, or as
a separate message. The ensemble of multiple text reactions MUST NOT be
represented as a single text part. Due to the semantics of Unicode characters,
concatenating reactions could change their semantics, resulting in unexpected or
misleading rendering, or even avenues for attack.</t>
<t>Below is the annotated message in EDN and pretty printed CBOR:</t>

<sourcecode type="edn"><![CDATA[# REACTION
# message ID = h'0158c4288911e50a8f6be3f47746b668
#                2f10fd91bc8c05557aa589a3157aff68'
# timestamp  = 1644387237728 = 2022-02-08T22:13:57.728Z
[
  h'd37bc0e6a8b4f04e9e6382375f587bf6', # salt
  null,                                # replaces
  h'',                                 # topicId
  null,                                # expires
  h'017ce54837404c3696e0c747b985cb17   # inReplyTo =
    2716d0ed0a3d249ca63ace7d82a096f4', #   Original
  {                                    # extensions
    1: "mimi://example.com/u/cathy-washington",
    2: "mimi://example.com/r/engineering_team"
  },
  [                                    # body (NestedPart)
    2,                                   # disposition = reaction
    "",                                  # language
    1,                                   # cardinality = single
    "text/plain;charset=utf-8",          # contentType
    '❤'                                  # content = U+2665 (heart)
  ]
]
]]></sourcecode>

<artwork><![CDATA[87                                      # array(7)
   50                                   # bytes(16)
      d37bc0e6a8b4f04e9e6382375f587bf6
   f6                                   # primitive(22)
   40                                   # bytes(0)
   f6                                   # primitive(22)
   58 20                                # bytes(32)
      01b0084467273cc43d6f0ebeac13eb84
      229c4fffe8f6c3594c905f47779e5a79
   a2                                   # map(2)
      01                                # unsigned(1)
      78 25                             # text(37)
         6d696d693a2f2f6578616d706c652e63
         6f6d2f752f63617468792d7761736869
         6e67746f6e
         # "mimi://example.com/u/cathy-washington"
      02                                # unsigned(2)
      78 25                             # text(37)
         6d696d693a2f2f6578616d706c652e63
         6f6d2f722f656e67696e656572696e67
         5f7465616d
         # "mimi://example.com/r/engineering_team"
   85                                   # array(5)
      02                                # unsigned(2)
      60                                # text(0)
                                        # ""
      01                                # unsigned(1)
      78 18                             # text(24)
         746578742f706c61696e3b6368617273
         65743d7574662d38
         # "text/plain;charset=utf-8"
      43                                # bytes(3)
         e29da4                         # "❤"
]]></artwork>
</section>

<section anchor="mentions"><name>Mentions</name>
<t>In instant messaging systems and social media, a mention allows special
formatting and behavior when a name, handle, or tag associated with a
known group is encountered, often when prefixed with a commercial-at "@"
character for mentions of users or a hash "#" character for groups or tags.
A message which contains a mention may trigger distinct notifications on
the IM client.</t>
<t>We can convey a mention by linking the user handle URI, or group URI in Markdown
or HTML rich content. For example, a mention using Markdown is indicated below.</t>

<sourcecode type="edn"><![CDATA[# MENTION
# message ID = h'018d825adf9f6be00dcafc5704c4102f
#                5022e74219d0b603e4ba7622654042af'
# timestamp  = 1644387243008 = 2022-02-08T22:14:03.008Z
[
  h'04f290e215d0f82d1750bfa8b7dc089d', # salt
  null,                                # replaces
  h'',                                 # topicId
  null,                                # expires
  h'017ce54837404c3696e0c747b985cb17   # inReplyTo =
    2716d0ed0a3d249ca63ace7d82a096f4', #   Original
  {                                    # extensions
    1: "mimi://example.com/u/cathy-washington",
    2: "mimi://example.com/r/engineering_team"
  },
  [                                    # body (NestedPart)
    1,                                   # disposition = render
    "",                                  # language
    1,                                   # cardinality = single
    "text/markdown;variant=GFM-MIMI",    # contentType
                                         # content
    'Kudos to [@Alice Smith](mimi://example.com/u/alice-smith)' +
    ' for making the release happen!'
  ]
]
]]></sourcecode>
<t>The same mention using HTML <xref target="W3C.CR-html52-20170808"/> would instead
replace in the EDN the contentType and content indicated below.</t>

<sourcecode type="edn"><![CDATA[  / ... /
  "text/html;charset=utf-8",
  '<p>Kudos to <a href="mimi://example.com/u/alice-smith">' +
  '@Alice Smith</a> for making the release happen!</p>'
]]></sourcecode>
</section>

<section anchor="edit"><name>Edit</name>
<t>Unlike with email messages, it is common in IM systems to allow the sender of
a message to edit or delete the message after the fact. Typically, the message
is replaced in the user interface of the receivers (even after the original
message is read) but shows a visual indication that it has been edited.</t>
<t>The <tt>replaces</tt> data field includes the message ID of the message to
edit/replace. The message included in the body is a replacement for the message
with the replaced message ID.
If the same message is placed more than once, the <tt>replaces</tt> data field refers
to the message ID of the first instance of the message. This allows maximum
correlation of different version of the same message, even if the receiver was
not privy to all of the original versions.</t>
<t>Here Bob Jones corrects a typo in his original message:</t>

<sourcecode type="edn"><![CDATA[# EDIT
# message ID = h'014028c0deddbdea56bec26172f6ede9
#                53d11024cb82b8192b5e2aea62d7fb47'
# timestamp  = 1644387248621 = 2022-02-08T22:14:08.621Z
[
  h'b8c2e6d8800ecf45df39be6c45f4c042', # salt
  h'015354973c2b65ca937bf1e035ae53a5   # replaces =
    ab80e947afa43d46920d4202e5cc0b27', #   Reply
  h'',                                 # topicId
  null,                                # expires
  h'017ce54837404c3696e0c747b985cb17   # inReplyTo =
    2716d0ed0a3d249ca63ace7d82a096f4', #   Original
  {                                    # extensions
    1: "mimi://example.com/u/bob-jones",
    2: "mimi://example.com/r/engineering_team"
  },
  [                                    # body (NestedPart)
    1,                                   # disposition = render
    "",                                  # language
    1,                                   # cardinality = single part
    "text/markdown;variant=GFM-MIMI",    # contentType
                                         # content
    'Right on! _Congratulations_ y\'all!'
  ]
]
]]></sourcecode>
<t>Note that replies and reactions always refer to a specific message id,
and therefore a specific "version" of a message, which could have been
edited before and/or after the message id referenced in the reply or reaction.
It is a matter of local policy how to render (if at all) a reaction to
a subsequently edited message.</t>
</section>

<section anchor="delete"><name>Delete</name>
<t>In IM systems, a delete means that the author of a specific message has
retracted the message, regardless if other users have read the message
or not. Typically, a placeholder remains in the user interface showing
that a message was deleted. Replies which reference a deleted message
typically hide the quoted portion and reflect that the original message
was deleted. To delete a message which was previously edited, the <tt>replaces</tt>
header field refers to the message ID of the first instance of the message to
be deleted.</t>
<t>If Bob deleted his message instead of modifying it, we would represent it
using the <tt>replaces</tt> data field, and using an empty body (NullPart),
as shown below.</t>

<sourcecode type="edn"><![CDATA[# DELETE
# message ID = h'011d9efc78d04d4dcf4d82b07d5199bb
#                ef37011c1f0c7e004b6111c6dda504b4'
# timestamp  = 1644387248621 = 2022-02-08T22:14:08.621Z
[
  h'0a590d73b2c7761c39168be5ebf7f2e6', # salt
  h'015354973c2b65ca937bf1e035ae53a5   # replaces =
    ab80e947afa43d46920d4202e5cc0b27', #   Reply
  h'',                                 # topicId
  null,                                # expires
  h'017ce54837404c3696e0c747b985cb17   # inReplyTo =
    2716d0ed0a3d249ca63ace7d82a096f4', #   Original
  {                                    # extensions
    1: "mimi://example.com/u/bob-jones",
    2: "mimi://example.com/r/engineering_team"
  },
  [                                    # body (NestedPart)
    1,                                   # disposition = render
    "",                                  # language
    0,                                   # cardinality = null part
  ]
]
]]></sourcecode>
</section>

<section anchor="unlike"><name>Unlike</name>
<t>In most IM systems, not only is it possible to react to a message ("Like"),
but it is possible to remove a previous reaction ("Unlike"). This can be
accomplished by deleting the message which creates the original reaction</t>
<t>If Cathy removes her reaction, we would represent the removal using a
<tt>replaces</tt> data field with an empty body, referring to the message which
created the reaction, as shown below.</t>

<sourcecode type="edn"><![CDATA[# UNLIKE
# message ID = h'013aadbb8f313253c8930f4e93c6ca54
#                b2ed06d258185bdcec3870534c8a4ec4'
# timestamp  = 1644387250389 = 2022-02-08T22:14:10.389Z
[
  h'c5ba86dc9fd272e58ca52ec805b79199', # salt
  h'0158c4288911e50a8f6be3f47746b668   # replaces =
    2f10fd91bc8c05557aa589a3157aff68', #   Reaction
  h'',                                 # topicId
  null,                                # expires
  h'017ce54837404c3696e0c747b985cb17   # inReplyTo =
    2716d0ed0a3d249ca63ace7d82a096f4', #   Original
  {                                    # extensions
    1: "mimi://example.com/u/cathy-washington",
    2: "mimi://example.com/r/engineering_team"
  },
  [                                    # body (NestedPart)
    2,                                   # disposition = reaction
    "",                                  # language
    0                                    # cardinality = null part
  ]
]
]]></sourcecode>
</section>

<section anchor="expiring"><name>Expiring</name>
<t>There are two types of expiring messages in instant messaging systems. In the
typical implementation, messages are deleted a specific amount of time relative
to (after) when the receiving client reads the message. We will refer to this as
relative expiration.</t>
<t>Absolute expiring messages are designed to be deleted automatically by the
receiving client at a certain time whether they have been read or not.</t>
<t>As with manually deleted messages, there is no guarantee that an uncooperative
client or a determined user will not save the content of the message. The goal
instead is to allow cooperating client that respect the convention to signal
expiration times clearly.</t>
<t>The <tt>expires</tt> data field contains the absolute timestamp when, or relative
amount of time after reading, after which the message can be deleted.
The semantics of the header are that the message is automatically deleted
by the receiving clients at the indicated time without user interaction or
network connectivity necessary.</t>

<sourcecode type="edn"><![CDATA[# EXPIRING
# message ID = h'01e59db8173939facc2c8a4a0f0ae8d0
#                c7a11a81239626630c9464a8d6717a03'
# timestamp  = 1644389403227 = 2022-02-08T22:49:06.227Z
[
  h'33be993eb39f418f9295afc2ae160d2d', # salt
  null,                                # replaces
  h'',                                 # topicId
  [                                    # expires
    false,                               # absolute, not relative
    1644390004           # expires = 10 minutes after it was sent
  ],
  null,                                # inReplyTo
  {                                    # extensions
    1: "mimi://example.com/u/alice-smith",
    2: "mimi://example.com/r/engineering_team"
  },
  [                                    # body (NestedPart)
    1,                                   # disposition = render
    "",                                  # language
    1,                                   # cardinality = single part
    "text/markdown;variant=GFM-MIMI",    # contentType
                                         # content
    '__*VPN GOING DOWN*__ I\'m rebooting the VPN in ten minutes' +
    ' unless anyone objects.'
  ]
]
]]></sourcecode>

<artwork><![CDATA[87                                      # array(7)
   50                                   # bytes(16)
      33be993eb39f418f9295afc2ae160d2d
   f6                                   # primitive(22)
   40                                   # bytes(0)
                                        # ""
   82                                   # array(2)
      f4                                # primitive(20)
      1a 62036674                       # unsigned(1644390004)
   f6                                   # primitive(22)
   a2                                   # map(2)
      01                                # unsigned(1)
      78 20                             # text(32)
         6d696d693a2f2f6578616d706c652e63
         6f6d2f752f616c6963652d736d697468
         # "mimi://example.com/u/alice-smith"
      02                                # unsigned(2)
      78 25                             # text(37)
         6d696d693a2f2f6578616d706c652e63
         6f6d2f722f656e67696e656572696e67
         5f7465616d
         # "mimi://example.com/r/engineering_team"
   85                                   # array(5)
      01                                # unsigned(1)
      60                                # text(0)
                                        # ""
      01                                # unsigned(1)
      78 1e                             # text(30)
         746578742f6d61726b646f776e3b7661
         7269616e743d47464d2d4d494d49
         # "text/markdown;variant=GFM-MIMI"
      58 50                             # bytes(80)
         5f5f2a56504e20474f494e4720444f57
         4e2a5f5f2049276d207265626f6f7469
         6e67207468652056504e20696e207465
         6e206d696e7574657320756e6c657373
         20616e796f6e65206f626a656374732e
         # "__*VPN GOING DOWN*__ I'm rebooting the VPN in" +
         # " ten minutes unless anyone objects."
]]></artwork>
</section>

<section anchor="attachments"><name>Attachments</name>
<t>An ExternalPart is a convenient way to present both "attachments" and
(possibly inline rendered) content which is too large to be included
in an MLS application message. The disposition data
field is set to inline if the sender recommends inline rendering, or
attachment if the sender intends the content to be downloaded or
rendered separately.</t>

<sourcecode type="edn"><![CDATA[# ATTACHMENT
# message ID = h'0176180c7d19a925021fe446d241134d
#                05c38e0d999cdc0f39c391d2377ed9d1'
# timestamp  = not specified in example
[
  h'18fac6371e4e53f1aeaf8a013155c166', # salt
  null,                                # replaces
  h'',                                 # topicId
  null,                                # expires = never
  null,                                # inReplyTo
  {                                    # extensions
    1: "mimi://example.com/u/bob-jones",
    2: "mimi://example.com/r/engineering_team"
  },
  [                                    # body (NestedPart)
    6,                                   # disposition = attachment
    "en",                                # language
    2,                                   # cardinality = external
    "video/mp4",                         # contentType
    # url
    "https://example.com/storage/8ksB4bSrrRE.mp4",
    0,                                   # expires
    708234961,                           # size
    1,                                   # encAlg = AES-128-GCM
    h'21399320958a6f4c745dde670d95e0d8', # key
    h'c86cf2c33f21527d1dd76f5b',         # nonce
    h'',                                 # aad
    1,                                   # hashAlg = sha256
    h'9ab17a8cf0890baaae7ee016c7312fcc   # content hash
      080ba46498389458ee44f0276e783163',
    "2 hours of key signing video",      # description
    "bigfile.mp4"                        # filename
  ]
]
]]></sourcecode>

<artwork><![CDATA[87                                      # array(7)
   50                                   # bytes(16)
      18fac6371e4e53f1aeaf8a013155c166
   f6                                   # primitive(22)
   40                                   # bytes(0)
                                        # ""
   f6                                   # primitive(22)
   f6                                   # primitive(22)
   a2                                   # map(2)
      01                                # unsigned(1)
      78 1e                             # text(30)
         6d696d693a2f2f6578616d706c652e63
         6f6d2f752f626f622d6a6f6e6573
         # "mimi://example.com/u/bob-jones"
      02                                # unsigned(2)
      78 25                             # text(37)
         6d696d693a2f2f6578616d706c652e63
         6f6d2f722f656e67696e656572696e67
         5f7465616d
         # "mimi://example.com/r/engineering_team"
   8f                                   # array(15)
      06                                # unsigned(6)
      62                                # text(2)
         656e                           # "en"
      02                                # unsigned(2)
      69                                # text(9)
         766964656f2f6d7034             # "video/mp4"
      78 2b                             # text(43)
         68747470733a2f2f6578616d706c652e
         636f6d2f73746f726167652f386b7342
         346253727252452e6d7034
         # "https://example.com/storage/8ksB4bSrrRE.mp4"
      00                                # unsigned(0)
      1a 2a36ced1                       # unsigned(708234961)
      01                                # unsigned(1)
      50                                # bytes(16)
         21399320958a6f4c745dde670d95e0d8
      4c                                # bytes(12)
         c86cf2c33f21527d1dd76f5b
      40                                # bytes(0)
      01                                # unsigned(1)
      58 20                             # bytes(32)
         9ab17a8cf0890baaae7ee016c7312fcc
         080ba46498389458ee44f0276e783163
      78 1c                             # text(28)
         3220686f757273206f66206b65792073
         69676e696e6720766964656f
         # "2 hours of key signing video"
      6b                                # text(11)
         62696766696c652e6d7034         # "bigfile.mp4"
]]></artwork>
<t>Other dispositions of external content are also possible, for example
an external GIF animation of a rocket ship could be used with a
reaction disposition.</t>
</section>

<section anchor="conferencing"><name>Conferencing</name>
<t>Joining a conference via an external URL is possible. The link could be
rendered to the user, requiring a click. Alternatively the URL could be
in an ExternalPart with a disposition of <tt>session</tt>, which could be processed
differently by the client (for example, alerting the user or presenting
a dialog box).
Further discussion of calling and conferencing functionality is out-of-scope
of this document.</t>

<sourcecode type="edn"><![CDATA[# CONFERENCING
# message ID = h'01496d15a8dba28d7397f9868b70768e
#                4a67f765d5b5b1ae9e03848c5fdeb0ba'
# timestamp  = not specified in example
[
  h'678ac6cd54de049c3e9665cd212470fa', # salt
  null,                                # replaces
  'Foo 118',                           # topicId
  null,                                # expires
  null,                                # inReplyTo
  {                                    # extensions
    1: "mimi://example.com/u/alice-smith",
    2: "mimi://example.com/r/engineering_team"
  },
  [                                    # body (NestedPart)
    7,                                   # disposition = session
    "",                                  # language
    2,                                   # cardinality = external
    "",                                  # contentType
    "https://example.com/join/12345",    # url
    0,                                   # expires
    0,                                   # size
    0,                                   # encAlg = none
    h'',                                 # key
    h'',                                 # nonce
    h'',                                 # aad
    0,                                   # hashAlg = none
    h'',                                 # content hash
    "Join the Foo 118 conference",       # description
    ""                                   # filename
  ]
]
]]></sourcecode>

<artwork><![CDATA[87                                      # array(7)
   50                                   # bytes(16)
      678ac6cd54de049c3e9665cd212470fa
   f6                                   # primitive(22)
   47                                   # bytes(7)
      466f6f20313138                    # "Foo 118"
   f6                                   # primitive(22)
   f6                                   # primitive(22)
   a2                                   # map(2)
      01                                # unsigned(1)
      78 20                             # text(32)
         6d696d693a2f2f6578616d706c652e63
         6f6d2f752f616c6963652d736d697468
         # "mimi://example.com/u/alice-smith"
      02                                # unsigned(2)
      78 25                             # text(37)
         6d696d693a2f2f6578616d706c652e63
         6f6d2f722f656e67696e656572696e67
         5f7465616d
         # "mimi://example.com/r/engineering_team"
   8f                                   # array(15)
      07                                # unsigned(7)
      60                                # text(0)
                                        # ""
      02                                # unsigned(2)
      60                                # text(0)
                                        # ""
      78 1e                             # text(30)
         68747470733a2f2f6578616d706c652e
         636f6d2f6a6f696e2f3132333435
         # "https://example.com/join/12345"
      00                                # unsigned(0)
      00                                # unsigned(0)
      00                                # unsigned(0)
      40                                # bytes(0)
                                        # ""
      40                                # bytes(0)
                                        # ""
      40                                # bytes(0)
                                        # ""
      00                                # unsigned(0)
      40                                # bytes(0)
                                        # ""
      78 1b                             # text(27)
         4a6f696e2074686520466f6f20313138
         20636f6e666572656e6365
         # "Join the Foo 118 conference"
      60                                # text(0)
]]></artwork>
</section>

<section anchor="topics-threading"><name>Topics / Threading</name>
<t>As popularized by the messaging application Slack, some messaging
applications have a notion of a Topic or message Thread
(not to be confused with message threading as used in email).
Clients beginning a new "topic" populate the <tt>topicId</tt> with a unique
opaque octet string. This could be the message ID of the first message
sent related to the topic. Subsequent messages may include the same
<tt>topicId</tt> for those messages to be associated with the same topic. The sort order
for messages within a thread uses the timestamp field. If more than
one message has the same timestamp, the lexically lowest message ID
sorts earlier.</t>
</section>
</section>

<section anchor="cbor-data-model-and-encoding-restrictions"><name>CBOR Data Model and Encoding Restrictions</name>

<section anchor="encoding-restrictions"><name>Encoding restrictions</name>
<t>MIMI content documents MUST be encoded using CBOR "deterministic" serialization as defined in Section 4.2.1 of <xref target="RFC8949"/>.</t>
<t>In summary, this consists of:</t>

<ul spacing="compact">
<li>using the shortest CBOR encoding of integers, tags, and floats, and the shortest encoding of the length of byte strings, text strings, arrays, and maps.</li>
<li>representing bigints as regular integers when they fit</li>
<li>never using indefinite length encoding of byte strings, text strings, arrays, and maps</li>
<li>sorting map keys according to the third bullet point of that section</li>
</ul>
<t>Most CBOR libraries automatically use the shortest form and use definite length. Support for bigints is optional and is not required by any if the values built into the MIMI Content specification. Most implementations of CBOR which support bigints can automatically reduce to regular ints.</t>
<t>The only map inside the MIMI content format is the extensions map. Many popular implementations of CBOR do not yet support map sorting in 4.2.1, but preserve map order. Therefore it should be straightforward for MIMI content extensions to present their maps in the correct order. Implementations MUST NOT send MIMI content in RFC7094 "canonical" order.</t>
</section>

<section anchor="data-model-restrictions"><name>Data model restrictions</name>

<ul spacing="compact">
<li>map keys for MIMI content extensions MUST be either integers, or text strings up to 255 octets long</li>
<li>map keys for any maps at any level of nesting inside a MIMI content extension MUST only be integers, text strings, or byte strings.</li>
<li>integer keys MUST be representable as a double float without loss of precision: uints 0..9007199254740991 (2^53 - 1), and negints (-(2^53) + 1) -9007199254740991..-1</li>
<li>the only floating point NaN values permitted are the half-width quiet NaN (CBOR encoding 0xF97E00), or floating point values inside tags 80-87 (representing arrays of various fixed-sized IEEE floating point types)</li>
<li>text strings used anywhere inside MIMI content MUST be valid UTF-8 sequences.</li>
<li>use of floating point values inside MIMI content is strongly discouraged</li>
</ul>
</section>

<section anchor="depth-restrictions"><name>Depth restrictions</name>
<t><tt>NestedParts</tt> MUST NOT be nested more than 4-levels deep.</t>
<t>MIMI content extensions MUST NOT contain any combination of tags, maps, or arrays at more than 4 levels of depth.
Specifically the MIMI content extensions map shown below has 4 levels of depth.
The extensions map is at level 1, the array inside map key 256 is at level 2, the array inside that array is at level 3, the URI inside tag 32 is at level 4.</t>

<artwork><![CDATA[{
    /sender URI/ 1: "mimi://a.example/u/alice",
    256: [
        [h'1234', 32("http://example.com")],
        [h'3456', 5771]
    ]
}
]]></artwork>
</section>
</section>

<section anchor="support-for-specific-media-types"><name>Support for Specific Media Types</name>

<section anchor="mimi-required-and-recommended-media-types"><name>MIMI Required and Recommended media types</name>
<t>As the MIMI Content container is just a container, the plain text or rich
text messages sent inside, and any image or other formats needs to be specified.
Clients compliant with MIMI MUST be able to receive the following media types:</t>

<ul spacing="compact">
<li>application/mimi-content -- the MIMI Content container format (described in this document)</li>
<li>text/plain;charset=utf-8</li>
<li>text/markdown;variant=GFM-MIMI -- Github Flavored Markdown for MIMI, defined in <xref target="gfm-mimi"/></li>
</ul>
<t>Note that it is acceptable to render the contents of a received markdown
document as plain text.</t>
<t>The following MIME types are RECOMMENDED:</t>

<ul spacing="compact">
<li>text/markdown;variant=CommonMark -- <eref target="https://spec.commonmark.org/0.30">CommonMark</eref></li>
<li>text/html</li>
<li>image/jpeg</li>
<li>image/png</li>
</ul>
<t>Clients compliant with this specification must be able to download
ExternalParts with <tt>http</tt> and <tt>https</tt> URLs, and decrypt downloaded content
encrypted with the AES-128-GCM AEAD algorithm.</t>

<section anchor="gfm-mimi"><name>Specifics of Github Flavored Markdown in MIMI</name>
<t>A MIMI content client supports GitHub Flavored Markdown as defined in <xref target="GFM"/>,
with two changes: the Autolink extension is not supported; and instead of the
Disallowed Raw HTML extension (<tt>tagfilter</tt>), the No HTML extension (<tt>nohtml</tt>),
which is defined here, is MANDATORY. (For clarity, a fixed list of supported
extensions, is further described in the bullet points below.)</t>
<t>To implement the No HTML extension to GFM, the opening angle bracket (<tt>&lt;</tt>) of
any <tt>HTML tag</tt> (as defined in Section 6.10 of <xref target="GFM"/>) is replaced with <tt>&amp;lt;</tt>
before sending. Any <tt>HTML tag</tt> in a received message is rendered as plain text.
Note that <tt>HTML tag</tt> includes open tags, closing tags, HTML comments, processing
instructions, declarations, and CDATA sections.</t>
<t>The following GitHub Flavored Markdown extensions are supported. No other extensions are allowed:</t>

<ul spacing="compact">
<li>Tables</li>
<li>Task list items</li>
<li>Strikethrough</li>
<li>No HTML</li>
</ul>
<t>This document specifies what can be sent inside a MIMI content message; it does
not restrict or prescribe in any way how input from a user is interpreted by an
Instant Messaging client that support MIMI, before any message resulting from
that input is sent.</t>
<t>Note that rendering Markdown as plain text is an acceptable form of "support".</t>
</section>
</section>

<section anchor="use-of-proprietary-media-types"><name>Use of proprietary media types</name>
<t>As most messaging systems are proprietary, standalone systems, it is useful to allow
clients to send and receive proprietary formats among themselves. Using the
functionality in the MIMI Content container, clients can send a message using the basic
functionality described in this document AND a proprietary format for
same-vendor clients simultaneously over the same group with end-to-end
encryption. An example is given in the Appendix.</t>
</section>
</section>

<section anchor="iana-considerations"><name>IANA Considerations</name>
<t>RFC EDITOR: Please replace XXXX throughout with the RFC number assigned to this document.</t>

<section anchor="mime-subtype-registration-of-application-mimi-content"><name>MIME subtype registration of application/mimi-content</name>
<t>This document proposes registration of a media subtype with IANA.</t>

<artwork><![CDATA[Type name: application

Subtype name: mimi-content

Required parameters: none

Optional parameters: none

Encoding considerations:
   This message type should be encoded as binary data

Security considerations:
   See Section A of RFC XXXX

Interoperability considerations:
   See Section Y.Z of RFC XXXX

Published specification: RFC XXXX

Applications that use this media type:
   Instant Messaging Applications

Fragment identifier considerations: N/A

Additional information:

   Deprecated alias names for this type: N/A
   Magic number(s): N/A
   File extension(s): N/A
   Macintosh file type code(s): N/A

Person & email address to contact for further information:
   IETF MIMI Working Group mimi@ietf.org

Author:
   See the Authors' Addresses section of RFC XXXX.

Change controller:
   Internet Engineering Task Force (iesg@ietf.org).
]]></artwork>
</section>

<section anchor="more-instant-messaging-interoperability-mimi-group-heading"><name>"More Instant Messaging Interoperability (MIMI)" Group Heading</name>

<section anchor="keys-registry"><name>MIMI Content Extension Keys registry</name>
<t>This document requests the creation of a new MIMI Content Extension Keys
registry.
The registry should be under the group heading of "More Instant Messaging Interoperability (MIMI)".</t>
<t>The MIMI Content format defined in this document, contains an extensions map in
each message. The keys in the extensions map can be (positive or negative)
integers, or text strings. Text strings and negative integer keys are reserved
for private use. Positive integer keys are assigned in the registry under the
Expert Review policy <xref target="RFC8126"/>. Integer keys between 1 and 255 are restricted
to IETF Stream RFCs, as specified by the IETF Review registration procedure
defined in <xref target="RFC8126"/>.</t>
<t>The columns in the registry are as follows:</t>

<ul spacing="compact">
<li>Key: The extension map key positive integer assigned to the MIMI content extension.</li>
<li>Name: a short descriptive name for the MIMI content extension.</li>
<li>Type: The CBOR data type of the value corresponding to the key. Applications
with multiple, related data items are encouraged to register a map type that
contains all the related fields.</li>
<li><t>Recommended: Whether support for this MIMI content extension is recommended by
the IETF. Valid values are "Y", "N", and "D", as described below. The default
value of the "Recommended" column is "N". Setting the Recommended item to "Y" or
"D", or changing an item whose current value is "Y" or "D", requires Standards
Action <xref target="RFC8126"/>.</t>

<ul spacing="compact">
<li>Y: Indicates that the IETF has consensus, and that the item is
RECOMMENDED. This only means that the associated mechanism is fit for the
purpose for which it was defined. Careful reading of the documentation for the
mechanism is necessary to understand the applicability of that mechanism. The
IETF could recommend mechanisms that have limited applicability, but will
provide applicability statements that describe any limitations of the mechanism
or necessary constraints on its use.</li>
<li>N: Indicates that the item has not been evaluated by the IETF and that the
IETF has made no statement about the suitability of the associated mechanism.
This does not necessarily mean that the mechanism is flawed, only that no
consensus exists. The IETF might have consensus to leave an item marked as "N"
on the basis of it having limited applicability or usage constraints.</li>
<li>D: Indicates that the item is discouraged and SHOULD NOT or MUST NOT be
used. This marking could be used to identify mechanisms that might result in
problems if they are used, such as a weak cryptographic algorithm or a mechanism
that might cause interoperability problems in deployment.</li>
</ul></li>
<li>Reference: The document where this MIMI content extension is defined</li>
</ul>
<t>Initial Contents:</t>
<table>
<thead>
<tr>
<th align="left">Key</th>
<th align="left">Name</th>
<th align="left">Description</th>
<th align="left">Type</th>
<th align="left">R</th>
<th align="left">Reference</th>
</tr>
</thead>

<tbody>
<tr>
<td align="left">0</td>
<td align="left">(reserved)</td>
<td align="left">N/A</td>
<td align="left">-</td>
<td align="left">-</td>
<td align="left">RFCXXXX</td>
</tr>

<tr>
<td align="left">1</td>
<td align="left">senderUri</td>
<td align="left">the sender as a MIMI participant</td>
<td align="left">tstr</td>
<td align="left">Y</td>
<td align="left">RFCXXXX</td>
</tr>

<tr>
<td align="left">2</td>
<td align="left">roomUri</td>
<td align="left">the MIMI room URI</td>
<td align="left">tstr</td>
<td align="left">Y</td>
<td align="left">RFCXXXX</td>
</tr>
</tbody>
</table></section>

<section anchor="dispo-registry"><name>MIMI Content Disposition registry</name>
<t>This document requests the creation of a new MIMI Disposition
registry.
The registry should be under the group heading of "More Instant Messaging Interoperability (MIMI)".</t>
<t>The MIMI Content format defined in this document, contains a disposition field
for each message part, represented by an integer in the range of 0 to 255.
These values are assigned in the registry under the Expert Review policy
<xref target="RFC8126"/>.</t>
<t>The columns in the registry are as follows:</t>

<ul spacing="compact">
<li>Name: a short descriptive name for the MIMI disposition.</li>
<li>Value: The positive integer assigned to the MIMI disposition.</li>
<li>Semantics: The semantics of the MIMI disposition.</li>
<li>Reference: The document where this MIMI disposition is defined</li>
</ul>
<t>The initial contents of the registry are below:</t>
<table>
<thead>
<tr>
<th align="left">Name</th>
<th align="left">Value</th>
<th align="left">Semantics</th>
<th align="left">Reference</th>
</tr>
</thead>

<tbody>
<tr>
<td align="left">unspecified</td>
<td align="left">0</td>
<td align="left">no disposition hint provided</td>
<td align="left">RFCXXXX</td>
</tr>

<tr>
<td align="left">render</td>
<td align="left">1</td>
<td align="left">render to the user according to local policy</td>
<td align="left">RFCXXXX</td>
</tr>

<tr>
<td align="left">reaction</td>
<td align="left">2</td>
<td align="left">a single reaction to another message</td>
<td align="left">RFCXXXX</td>
</tr>

<tr>
<td align="left">profile</td>
<td align="left">3</td>
<td align="left">representing a profile of a participant or room</td>
<td align="left">RFCXXXX</td>
</tr>

<tr>
<td align="left">inline</td>
<td align="left">4</td>
<td align="left">rendered "inline" directly in the chat interface</td>
<td align="left">RFCXXXX</td>
</tr>

<tr>
<td align="left">icon</td>
<td align="left">5</td>
<td align="left">an icon such as an avatar image</td>
<td align="left">RFCXXXX</td>
</tr>

<tr>
<td align="left">attachment</td>
<td align="left">6</td>
<td align="left">intended to be downloaded instead of rendered immediately</td>
<td align="left">RFCXXXX</td>
</tr>

<tr>
<td align="left">session</td>
<td align="left">7</td>
<td align="left">a description of a multimedia session, or URI to join one</td>
<td align="left">RFCXXXX</td>
</tr>

<tr>
<td align="left">preview</td>
<td align="left">8</td>
<td align="left">a sender-generated preview, ex: image or link contents</td>
<td align="left">RFCXXXX</td>
</tr>
</tbody>
</table></section>

<section anchor="expert-review"><name>Expert Review</name>
<t>Expert Review <xref target="RFC8126"/> registry requests are registered
after a three-week review period on the MIMI Designated Expert (DE) mailing list
<eref target="mailto:mimi-reg-review@ietf.org">mimi-reg-review@ietf.org</eref> on the advice of one or more of the MIMI DEs.</t>
<t>Registration requests sent to the MIMI DEs' mailing list for review
SHOULD use an appropriate subject (e.g., "Request to register value
in MIMI Content Extensions Keys registry").</t>
<t>Within the review period, the MIMI DEs will either approve or deny
the registration request, communicating this decision to the MIMI DEs'
mailing list and IANA. Denials SHOULD include an explanation and, if
applicable, suggestions as to how to make the request successful.
Registration requests that are undetermined for a period longer than
21 days can be brought to the IESG's attention for resolution using
the <eref target="mailto:iesg@ietf.org">iesg@ietf.org</eref> mailing list.</t>
<t>Criteria that SHOULD be applied by the MIMI DEs includes determining
whether the proposed registration duplicates existing functionality,
whether it is likely to be of general applicability or useful only
for a single application, and whether the registration description
is clear.</t>
<t>IANA MUST only accept registry updates from the MIMI DEs and SHOULD
direct all requests for registration to the MIMI DEs' mailing list.</t>
<t>In cases where a registration decision could be perceived as creating a conflict
of interest for a particular MIMI DE, that MIMI DE SHOULD defer to the judgment
of the other MIMI DEs.</t>
</section>
</section>

<section anchor="gfm-mimi-markdown-variant"><name>GFM-MIMI Markdown variant</name>
<t>This document registers a new Markdown variant in the IANA Markdown Variants
registry. The registration template below conforms with <xref target="RFC7763"/>.</t>

<artwork><![CDATA[Identifier: GFM-MIMI

Name: GitHub Flavored Markdown Subset for MIMI

Description:
   GitHub Flavored Markdown, without Autolinks and with no embedded HTML

References: RFCXXXX

Contact Information:
   IETF MIMI Working Group <mimi@ietf.org>
]]></artwork>
</section>
</section>

<section anchor="security-considerations"><name>Security Considerations</name>

<section anchor="general-handling"><name>General handling</name>
<t>The following cases are examples of nonsensical values that most likely
represent malicious messages. These should be logged and discarded.</t>

<ul spacing="compact">
<li><t>sender of the message</t>

<ul spacing="compact">
<li>where the apparent sender is not a member of the target MLS group</li>
</ul></li>
<li><t>message IDs</t>

<ul spacing="compact">
<li>which duplicate another message ID already encountered</li>
<li>where the first octet of the message ID is an unknown hash algorithm.</li>
</ul></li>
<li><t>timestamps</t>

<ul spacing="compact">
<li>received more than a few minutes in the future, or</li>
<li>before the first concrete syntax of this document is published</li>
<li>before the room containing them was created</li>
</ul></li>
<li><t>topicId</t>

<ul spacing="compact">
<li>the <tt>topicId</tt> is very long (greater than 4096 octets)</li>
</ul></li>
<li><t>expires</t>

<ul spacing="compact">
<li>refers to a date more than a year in the past (only possible with absolute
expiration)</li>
<li>refers to a date more than a year in the future (possible with both
relative and absolute expiration)</li>
</ul></li>
<li><t>body</t>

<ul spacing="compact">
<li>has too many body parts (more than 1024)</li>
<li>is nested too deeply (more than 4 levels deep)</li>
<li>is too large (according to local policy)</li>
<li>has an unknown PartSemantics value</li>
</ul></li>
</ul>
<t>For the avoidance of doubt, the following cases may be examples of legitimate use
cases, and should not be considered the result of a malicious sender.</t>

<ul spacing="compact">
<li><t>message IDs</t>

<ul spacing="compact">
<li>where <tt>inReplyTo</tt> or <tt>replaces</tt> refer to an unknown message.
Such a message could have been sent before the local client joined.</li>
</ul></li>
<li><t>body</t>

<ul spacing="compact">
<li>where a body part contains an unrecognized Disposition value. The
unknown value should be treated as if it where <tt>render</tt>.</li>
<li>where a contentType is unrecognized or unsupported.</li>
<li>where a language tag is unrecognized or unsupported.</li>
</ul></li>
</ul>
</section>

<section anchor="salt-generation"><name>Generating the random salt</name>
<t>To ensure a strong source of entropy for the per-message unique salt required in
each message, the client can export a secret from the MLS key schedule, for
example with the label <tt>salt_base_secret</tt> and calculate the salt as the first
16-octets of the HMAC of a locally generated nonce and the franking_base_secret.</t>

<artwork><![CDATA[hash_output = HMAC_SHA256( salt_base_secret, nonce )
salt = hash_output[0..15]
]]></artwork>
</section>

<section anchor="rendering-and-authorization-of-edits-and-deletes"><name>Rendering and authorization of edits and deletes</name>
<t>This content format allows clients to send new versions of previously sent
messages, effectively replacing ("editing") or retracting ("deleting") another
referenced message. The rendering of these "edits" and "deletes" are important
from a security perspective.</t>
<t>For example, if Alice writes "Bob, could I borow a pen?", and Bob reacts with a
thumbs up emoji, Alice might edit this message with no change in meaning
(correcting the spelling of "borrow"), or a dramatic change in meaning ("Bob,
could I borrow your Ferrari this month?"). The receiver SHOULD indicate clearly
that a received message has been edited or retracted. The receiver might:</t>

<ul spacing="compact">
<li>offer an option to view previous versions of a message,</li>
<li>show a summarized or thumbnail version of a message referenced in a reply,</li>
<li>indicate clearly that a reply was to a previous version of a message than the
most recent one.</li>
<li>show a detailed view of reaction indicating that some reactions referred to a
previous version of the message.</li>
</ul>
<t>In addition, some groups may have special policies or permissions allowing
specific types of edits or deletes. For example, a moderator in one room might
be allowed to edit the topic of a message, but not modify the rest of the
content. An administrator might be allowed to delete messages which violate the
policy of the group. Receiving clients MUST NOT allow parties other than the
original sender of a message to edit or delete that message, unless there is a
specific, concrete authorization policy which allows it. Likewise, even the
original sender of a message MUST NOT be able to change the semantics of any
other portion of the message except for the contents of the NestedPart, without
specific authorization.</t>
</section>

<section anchor="validation-of-timestamp"><name>Validation of timestamp</name>
<t>The timestamp is the time a message is accepted by the hub provider. As such,
the hub provider can manipulate the timestamp, and the sending provider
can delay sending messages selectively to cause the timestamp on a hub to
be later.
Note that the optional franking mechanism discussed in Section 5.4.1.2 of
<xref target="I-D.ietf-mimi-protocol"/> prevents follower servers from modifying the
timestamp.</t>
<blockquote><t><strong>TODO</strong>: Discuss how to sanity check lastSeen, timestamp and the MLS
epoch and generation, and the limitations of this approach.</t>
</blockquote></section>

<section anchor="alternate-content-rendering"><name>Alternate content rendering</name>
<t>This document includes a mechanism where the sender can offer alternate
versions of content in a single message. For example, the sender could
send:</t>

<ul spacing="compact">
<li>an plain text and an HTML version of a text message</li>
<li>a thumbnail preview and link to a high-resolution image or video</li>
<li>versions of the same message in multiple languages</li>
<li>an PNG image and a scalable vector graphics version of a line drawing</li>
</ul>
<t>A malicious client could use this mechanism to send content that will appear
different to a subset of the members of a group and possibly elicit an
incorrect or misleading response.</t>

<artwork><![CDATA[Message as seen by Alice (manager)
Xavier: Do you want me to reserve a room for the review meeting?

Message as seen by Bob (Alice's assistant)
Xavier: @Bob I need to pickup Alice's Ferarri keys. She'll confirm

Reply sent by Alice
Alice: Yes please.
]]></artwork>
</section>

<section anchor="link-and-mention-handling"><name>Link and Mention handling</name>
<t>Both Markdown and HTML support links. Using the example of an <tt>https</tt> link,
if the rendered text and the link target match exactly or are canonically
equivalent, there is no need for confirmation if the end user selects the link.</t>

<artwork><![CDATA[[example.com/foobar](https://example.com/foobar)
[https://example.com/foobar](https://example.com/foobar)
[https://example.com:443/foobar](https://example.com/foobar)
]]></artwork>
<t>However, if the link text is different, or the scheme is downgraded
from https to http, the user should be presented with
an alert warning that the text is not the same.</t>

<artwork><![CDATA[[https://example.com/foobar](https://spearphishers.example/foobar)
[https://example.com/foobar](http://example.com/foobar)
]]></artwork>
<t>An IM URI link to a user who has a member client in the MLS
group in which the message was sent is considered a mention. Clients may
support special rendering of mentions instead of treating them like any
other type of link. In Markdown and HTML, the display text portion of a
link is considered a rendering hint from the sender to the receiver of
the message. The receiver should use local policy to decide if the hint
is an acceptable local representation of the user represented by the link
itself. If the hint is not an acceptable representation, the client should
instead display its canonical representation for the user.</t>
<t>For example, in the first example, the sender expresses no preference
about how to render the mention. In the second example, the sender requests
that the mention is rendered as the literal URI. In the third example, the
sender requests the canonical handle for Alice. In the fourth example, the
sender requests Alice's first name.</t>

<artwork><![CDATA[<mimi://example.com/u/alice-smith>
[mimi://example.com/u/alice-smith](mimi://example.com/u/alice-smith)
[@AliceSmith](mimi://example.com/u/alice-smith)
[Alice](mimi://example.com/u/alice-smith)
]]></artwork>
<t>Note that in some clients, presence of a mention for the local user may
result in a different notification policy.</t>
<t>If the client does not support special rendering of mentions, the
application, should render the text like any other link.</t>
</section>
</section>

</middle>

<back>
<references><name>References</name>
<references><name>Normative References</name>
<reference anchor="GFM" target="https://github.github.com/gfm/">
  <front>
    <title>GitHub Flavored Markdown Spec, Version 0.29-gfm</title>
    <author>
      <organization>GitHub</organization>
    </author>
    <date year="2019" month="March" day="6"/>
  </front>
</reference>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2119.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2392.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.3862.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.5116.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.5646.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8126.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8174.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8610.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8949.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9420.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml4/reference.W3C.CR-html52-20170808.xml"/>
</references>
<references><name>Informative References</name>
<reference anchor="DoubleRatchet" target="https://signal.org/docs/specifications/doubleratchet/">
  <front>
    <title>The Double Ratchet Algorithm</title>
    <author fullname="Trevor Perrin">
      <organization>Signal</organization>
    </author>
    <author fullname="Moxie Marlinspike">
      <organization>Signal</organization>
    </author>
    <date year="2016" month="November" day="20"/>
  </front>
</reference>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.ietf-cbor-edn-literals.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.ietf-mimi-arch.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.ietf-mimi-protocol.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.ietf-mls-extensions.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml3/reference.I-D.mahy-mimi-identity.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2046.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.2183.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.3156.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.3261.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.5545.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.6120.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.6350.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.7763.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.8551.xml"/>
<xi:include href="https://bib.ietf.org/public/rfc/bibxml/reference.RFC.9078.xml"/>
</references>
</references>

<section anchor="cddl-schemas"><name>CDDL Schemas</name>
<t>Below are Concise Data Definition Language (CDDL) <xref target="RFC8610"/> schemas for
the formats described in the body of the document.</t>

<section anchor="full-schema"><name>Complete Message Format Schema</name>

<sourcecode type="cddl"><![CDATA[mimiContent = [
  salt: bstr .size 16,
  replaces: null / MessageId,
  topicId: bstr,
  expires: null / Expiration,
  inReplyTo: null / MessageId,
  mimiExtensions: extensions,
  nestedPart: NestedPart
]

NestedPart = [
  disposition: baseDispos / $extDispos / unknownDispos,
  language: tstr,
  ( NullPart // SinglePart // ExternalPart // MultiPart)
]

NullPart = ( cardinality: nullpart )

SinglePart = (
    cardinality: single,
    contentType: tstr,
    content: bstr
)

ExternalPart = (
    cardinality: external,
    contentType: tstr,
    url: tstr,
    expires: uint .size 4,
    size: uint .size 8,
    encAlg: uint .size 2,
    key: bstr,
    nonce: bstr,
    aad: bstr,
    hashAlg: uint .size 1,
    contentHash: bstr,
    description: tstr,
    filename: tstr
)

MultiPart = (
    cardinality: multi,
    partSemantics: chooseOne / singleUnit / processAll,
    parts: [2* NestedPart]
)

Expiration = [
    relative: bool,
    time: uint .size 4
]

baseDispos = &(
    unspecified: 0,
    render: 1,
    reaction: 2,
    profile: 3,
    inline: 4,
    icon: 5,
    attachment: 6,
    session: 7,
    preview: 8
)
; Note: any ext_dispos take precedence
unknownDispos = &( unknown: 9..255 )

; MessageId is derived from SHA256 hash
MessageId = bstr .size 32

extensions = {
  ? &(senderUri: 1) ^ => tstr,
  ? &(roomUri: 2) ^ => tstr,
  * $$otherKnownExtensions,
  * unknownExtension
}

unknownExtension = (
   name => value
)
name = int / tstr .size (1..255)
value = any

nullpart = 0
single   = 1
external = 2
multi    = 3

chooseOne  = 0
singleUnit = 1
processAll = 2
]]></sourcecode>
</section>

<section anchor="implied-message-fields"><name>Implied Message Fields</name>
<t>Below is a CDDL schema for the implied message fields.</t>

<sourcecode type="cddl"><![CDATA[MessageDerivedValues = [
    messageId: MessageId,
    hubAcceptedTimestamp: Timestamp,
    mlsGroupId: bstr,
    senderLeafIndex: uint .size 4,
    senderClientUrl: MsgUri,
    senderUserUrl: MsgUri,
    roomUrl: MsgUri
]

MsgUri = tstr
MessageId = bstr .size 32
Timestamp = MsecsSinceEpoch / ExtendedTime
; milliseconds since start of UNIX epoch
MsecsSinceEpoch = uint .size 8
; extended time from RFC9581
ExtendedTime = #6.1001({* name => value })
name = int / tstr .size (1..255)
value = any .size (0..4095)
]]></sourcecode>
</section>
</section>

<section anchor="multipart-examples"><name>Multipart examples</name>
<t>In a heterogeneous group of IM clients, it is often desirable to send more than one
media type as alternatives, such that IM clients have a choice of which media
type to render. For example, imagine an IM group containing a set of clients
which support a common video format and a subset which only support animated GIFs.
The sender could use a <tt>MultiPart</tt> NestablePart with <tt>chooseOne</tt> semantics
containing both media types. Every client in the group chat could render something
resembling the media sent. This is analogous to the <tt>multipart/alternative</tt> <xref target="RFC2046"/>
media type.</t>
<t>Likewise, it is often desirable to send more than one media type intended to be
rendered together as in (for example a rich text document with embedded images),
which can be represented using a <tt>MultiPart</tt> NestablePart with <tt>processAll</tt>
semantics. This is analogous to the <tt>multipart/mixed</tt> <xref target="RFC2046"/> media type.</t>
<t>Note that there is a minor semantic difference between multipart/alternative and
<tt>MultiPart</tt> with <tt>chooseOne</tt> semantics. In multipart/alternative, the parts are
presented in preference order by the sender. With <tt>MultiPart</tt> the receiver
chooses its "best" format to render according to its own preferences.</t>

<section anchor="proprietary-and-common-formats-sent-as-alternatives"><name>Proprietary and Common formats sent as alternatives</name>
<t>This shows sending a message containing both this profile and a proprietary
messaging format simultaneously.</t>

<sourcecode type="edn"><![CDATA[# MULTIPART-1
# message ID = h'01da5a515ec5db42cc4dcc19b90c3c31
#                245d8a1cfcce11318f24eb11dce0990e'
# timestamp  = not specified in example
[
  h'261c953e178af653fe3d42641b91d814', # salt
  null,                                # replaces
  h'',                                 # topicId
  null,                                # expires = never
  null,                                # inReplyTo
  {                                    # extensions
    1: "mimi://example.com/u/alice-smith",
    2: "mimi://example.com/r/engineering_team"
  },
  [                                    # body (NestedPart)
                                         # partIndex = 0 (1st part)
    1,                                   # disposition = render
    "",                                  # language
    3,                                   # cardinality = multi
    0,                                   # partSemantics = chooseOne
    [
      [                                  # partIndex = 1
        1,                               # disposition = render
        "",                              # language
        1,                               # cardinality = single
        "text/markdown;variant=GFM-MIMI",# contentType
        '# Welcome!'                     # content
      ],
      [                                  # partIndex = 2
        1,                               # disposition = render
        "",                              # language
        1,                               # cardinality = single
        # contentType
        "application/vnd.examplevendor-fancy-im-message",
        # content
        h'dc861ebaa718fd7c3ca159f71a2001'
      ]
    ]
  ]
]
]]></sourcecode>
</section>

<section anchor="multiple-reactions-example"><name>Multiple Reactions Example</name>
<t>This example shows sending a reaction with multiple separate emojis.</t>

<sourcecode type="edn"><![CDATA[# MULTIPART-2
# message ID = h'01d65918c6c51c8e76546337276ae6f4
#                bfd873d867d5cb57c76bcdca3d999dd7'
# timestamp  = not specified in example
[
  h'8528dc2d92e4f1944d62042907ab94d0', # salt
  null,                                # replaces
  h'',                                 # topicId
  null,                                # expires = never
  null,                                # inReplyTo
  {                                    # extensions
    1: "mimi://example.com/u/alice-smith",
    2: "mimi://example.com/r/engineering_team"
  },
  [                                    # body (NestedPart)
                                         # partIndex = 0 (1st part)
    2,                                   # disposition = reaction
    "",                                  # language
    3,                                   # cardinality = multi
    2,                                   # partSemantics = processAll
    [
      [                                  # partIndex = 1
        2,                               # disposition = reaction
        "",                              # language
        1,                               # cardinality = single
        "text/plain;charset=utf-8",      # contentType
        h'E2 9D A4'           # content = Unicode "heart"
      ],
      [                                  # partIndex = 2
        2,                               # disposition = reaction
        "",                              # language
        1,                               # cardinality = single
        "text/plain;charset=utf-8",      # contentType
        h'F0 9F A5 B3'        # content = Unicode "party face"
      ],
      [                                  # partIndex = 3
        2,                               # disposition = reaction
        "",                              # language
        1,                               # cardinality = single
        "text/plain;charset=utf-8",      # contentType
        h'F0 9F A4 9E'        # content = Unicode "fingers crossed"
      ]
    ]
  ]
]
]]></sourcecode>
</section>

<section anchor="nesting-example"><name>Complicated Nested Example</name>
<t>This example shows separate GIF and PNG inline images with English and
French versions of an HTML message. A summary of the 11 parts are shown below.</t>

<artwork><![CDATA[Part  Description
 0    choose either GIF or PNG
 1      (with GIF) process all
 2        choose either English or French
 3          English
 4          French
 5        GIF
 6      (with PNG) process all
 7        choose either English or French
 8          English
 9          French
10        PNG
]]></artwork>

<sourcecode type="edn"><![CDATA[# MULTIPART-3
# message ID = h'01cfebeadbdb83c1eefb6403ba4852da
#                f8bbbf9cd53bf5035a74d5d741950c9f'
# timestamp  = not specified in example
[
  h'b8362793168d18c049b882d4642a2274', # salt
  null,                                # replaces
  h'',                                 # topicId
  null,                                # expires = never
  null,                                # inReplyTo
  {                                    # extensions
    1: "mimi://example.com/u/alice-smith",
    2: "mimi://example.com/r/engineering_team"
  },
  [                                    # body (NestedPart)
                                         # partIndex = 0 (1st part)
    1,                                   # disposition = render
    "",                                  # language
    3,                                   # cardinality = multi
    0,                                   # partSemantics = chooseOne
    [
      [                                  # partIndex = 1
        1,                               # disposition = render
        "",                              # language
        3,                               # cardinality = multi
        2,                               # partSemantics = processAll
        [
          [                              # partIndex = 2
            1,                           # disposition = render
            "",                          # language
            3,                           # cardinality = multi
            0,                           # partSemantics = chooseOne
            [
              [                          # partIndex = 3
                1,                       # disposition = render
                "en",                    # language
                1,                       # cardinality = single
                # content type
                "text/html;charset=utf-8",
                # content
                '<html><body><h1>Welcome!</h1>\n' +
                '<img src="cid:5@local.invalid" ' +
                'alt="Welcome image"/>\n</body></html>'
              ],       # English HTML
              [                          # partIndex = 4
                1,                       # disposition = render
                "fr",                    # language
                1,                       # cardinality = single
                # content type
                "text/html;charset=utf-8",
                # content
                '<html><body><h1>Bienvenue!</h1>\n' +
                '<img src="cid:5@local.invalid" ' +
                'alt="Image bienvenue"/>\n</body></html>'
              ]        # French HTML
            ]
          ],         # English or French HTML (refers to GIF)
          [                              # partIndex = 5
            4,                           # disposition = inline
            "",                          # language
            1,                           # cardinality = single
            "image/gif",                 # content type
            # content
            h'dc861ebaa718fd7c3ca159f71a2001a7'
          ]          # GIF
        ]
      ],           # GIF version with English or French HTML
      [                                  # partIndex = 6
        1,                               # disposition = render
        "",                              # language
        3,                               # cardinality = multi
        2,                               # partSemantics = processAll
        [
          [                              # partIndex = 7
            1,                           # disposition = render
            "",                          # language
            3,                           # cardinality = multi
            0                            # partSemantics = chooseOne
            [
              [                          # partIndex = 8
                1,                       # disposition = render
                "en",                    # language
                1,                       # cardinality = single
                # content type
                "text/html;charset=utf-8",
                # content
                '<html><body><h1>Welcome!</h1>\n' +
                '<img src="cid:10@local.invalid" ' +
                'alt="Welcome image"/>\n</body></html>'
              ],       # English HTML
              [                         # partIndex = 9
                1,                      # disposition = render
                "fr",                   # language
                1,                      # cardinality = single
                # content type
                "text/html;charset=utf-8",
                # content
                '<html><body><h1>Bienvenue!</h1>\n' +
                '<img src="cid:10@local.invalid" ' +
                'alt="Image bienvenue"/>\n</body></html>'
              ]        # French HTML
            ]
          ],         # English or French HTML (refers to PNG)
          [                              # partIndex = 10
            4,                           # disposition = inline
            "",                          # language
            1,                           # cardinality = single
            "image/png",                 # content type
            # content
            h'fa444237451a05a72bb0f67037cc1669'
          ]          # PNG
        ]
      ]            # PNG version with English or French HTML
    ]            # GIF or PNG (with English or French HTML)
  ]
]
]]></sourcecode>
</section>
</section>

<section anchor="changelog"><name>Changelog</name>
<t>RFC Editor, please remove this entire section.</t>

<section anchor="changes-between-draft-mahy-mimi-content-01-and-draft-mahy-mimi-content-02"><name>Changes between draft-mahy-mimi-content-01 and draft-mahy-mimi-content-02</name>

<ul spacing="compact">
<li>made semantics abstract (C++ structs) instead of using CPIM or MIME headers</li>
</ul>
</section>

<section anchor="changes-between-draft-mahy-mimi-content-02-and-draft-ietf-mimi-content-00"><name>Changes between draft-mahy-mimi-content-02 and draft-ietf-mimi-content-00</name>

<ul spacing="compact">
<li>replaced threadId with topicId</li>
<li>inReplyTo now has a hash of the referenced message</li>
<li>clarified that replies are always to a specific version of a modified message</li>
<li>changed timestamp to a whole number of milliseconds since the epoch
to avoid confusion</li>
<li>added Security Considerations section</li>
<li>added IANA Considerations section</li>
<li>added change log</li>
</ul>
</section>

<section anchor="changes-between-draft-ietf-mimi-content-00-and-draft-ietf-mimi-content-01"><name>Changes between draft-ietf-mimi-content-00 and draft-ietf-mimi-content-01</name>

<ul spacing="compact">
<li>created new abstract format for attachment information, instead of using
message/external-body</li>
<li>added discussion of encrypting external content</li>
<li>clarified the difference between <tt>render</tt> and <tt>inline</tt> dispositions</li>
<li>created a way for the messageId and timestamp to be shared in the MLS
additional authenticated data field</li>
<li>expanded discussion of what can and should be rendered when a mention is
encountered; discussed how to prevent confusion attacks with mentions.</li>
<li>added a lastSeen field used to ensure a more consistent sort order of
messages in a room.</li>
</ul>
</section>

<section anchor="changes-between-draft-ietf-mimi-content-01-and-draft-ietf-mimi-content-02"><name>Changes between draft-ietf-mimi-content-01 and draft-ietf-mimi-content-02</name>

<ul spacing="compact">
<li>consensus at IETF 118 was to use a hash of the ciphertext in lieu of the
message ID</li>
<li>consensus at IETF 118 was to use the hub accepted timestamp for protocol
actions like sorting</li>
<li>Updated author's address</li>
</ul>
</section>

<section anchor="changes-between-draft-ietf-mimi-content-02-and-draft-ietf-mimi-content-03"><name>Changes between draft-ietf-mimi-content-02 and draft-ietf-mimi-content-03</name>

<ul spacing="compact">
<li>added hash of content to external content</li>
<li>replaced abstract syntax with concrete TLS Presentation Language and CBOR syntaxes</li>
</ul>
</section>

<section anchor="changes-between-draft-ietf-mimi-content-03-and-draft-ietf-mimi-content-04"><name>Changes between draft-ietf-mimi-content-03 and draft-ietf-mimi-content-04</name>

<ul spacing="compact">
<li>use CBOR as the binary encoding</li>
<li>add multipart examples</li>
</ul>
</section>

<section anchor="changes-between-draft-ietf-mimi-content-04-and-draft-ietf-mimi-content-05"><name>Changes between draft-ietf-mimi-content-04 and draft-ietf-mimi-content-05</name>

<ul spacing="compact">
<li>change message ID construction, and add salt</li>
<li>remove partIndex and make it implied</li>
<li>mention Content ID URI (cid:) and describe using it with the implicit
partIndex</li>
<li>discuss rendering and authorization issues for edit/delete in the security
considerations</li>
<li>include both absolute and relative expiration times</li>
<li>add specificity about markdown support / create GFM-MIMI Markdown variant</li>
<li>remove tag from URLs in ExternalPart and implied headers</li>
</ul>
</section>

<section anchor="changes-between-draft-ietf-mimi-content-05-and-draft-ietf-mimi-content-06"><name>Changes between draft-ietf-mimi-content-05 and draft-ietf-mimi-content-06</name>

<ul spacing="compact">
<li>remove lastSeen field</li>
<li>improve guidance on processing MultiPart with content ID URIs</li>
<li>add extra copy of the salt to Message ID construction to prevent SHA256 length extension attack</li>
<li>future-proof timestamp format hub timestamp</li>
<li>IANA register the MIMI extension keys used in the draft</li>
<li>lots of changes for internal consistency</li>
<li>rebuild all the examples from a script. make sure the EDN and CBOR correspond
and the CDDL validates the CBOR.</li>
</ul>
</section>

<section anchor="changes-between-draft-ietf-mimi-content-06-and-draft-ietf-mimi-content-07"><name>Changes between draft-ietf-mimi-content-06 and draft-ietf-mimi-content-07</name>

<ul spacing="compact">
<li>fixed a bug in the generation of message IDs; regenerated them correctly.</li>
<li>moved MIMI message status format into draft-mahy-mimi-message-status</li>
<li>clarified that the salt is always 16 octets</li>
<li>make examples uses the correct CBOR map key for room</li>
</ul>
</section>

<section anchor="changes-between-draft-ietf-mimi-content-07-and-draft-ietf-mimi-content-08"><name>Changes between draft-ietf-mimi-content-07 and draft-ietf-mimi-content-08</name>

<ul spacing="compact">
<li>add an explicit length before the sender URI and room URI in the input to the message ID hash input (Issue #61/PR#70)</li>
<li>make the inline CDDL examples consistent with the complete CDDL (PR#69)</li>
<li>add IANA registry for disposition values (Issue #64/PR#71)</li>
<li>add CBOR encoding, data model, and depth restrictions section (Issue #76/PR#79)</li>
<li>removed the maximum length of MIMI content extensions</li>
</ul>
</section>
</section>

</back>

</rfc>
