-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathdraft-rosenberg-dispatch-ripp-chat.xml
More file actions
1416 lines (1151 loc) · 51.8 KB
/
Copy pathdraft-rosenberg-dispatch-ripp-chat.xml
File metadata and controls
1416 lines (1151 loc) · 51.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="US-ASCII"?>
<!DOCTYPE rfc SYSTEM "rfc2629.dtd">
<?rfc toc='yes'?>
<?rfc tocdepth='5'?>
<rfc category="std" submissionType="IETF" ipr="trust200902" docName="draft-rosenberg-dispatch-ripp-chat-00">
<front>
<title abbrev="RIPP Chat">Web API for Persistent Messaging Apps</title>
<author fullname="Jonathan Rosenberg" initials="J.R." role="editor"
surname="Rosenberg">
<organization abbrev="Five9">Five9</organization>
<address>
<postal>
<street>4000 Executive Parkway #400</street>
<city>San Ramon</city>
<region>CA</region>
<code>94583</code>
<country>US</country>
</postal>
<email>jdrosen@jdrosen.net</email>
</address>
</author>
<date month="February" year="2020" day="7"/>
<area>Applications</area>
<abstract>
<t>This specification defines a web-based application protocol
that enables interoperability for modern enterprise and consumer
applications that provide persistent group chat
capabilities. It outlines the protocol capabilities needed for a
union of the features provided across iMessage, Slack, and Webex
Teams. The specification enables many use cases in which
interoperability is desirable, including enterprises that have
deployed a multiplicity of these applications, contact center
clients seeking to integrate with a large number of enteprise
telephony systems, inter-company chat, and interop between
consumer chat apps like iMessage and WeChat. </t>
</abstract>
</front>
<middle>
<section title="Terminology">
<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>
</section>
<section title="Introduction">
<t>Chat and presence applications have existed for many years on the
Internet, and have been the subject of much standardization
activity. Most recently, the SIMPLE suite of specifications were
produced, targeting SIP-based systems <xref target="RFC6914"/>, along
with XMPP <xref target="RFC6120"/>. These specifications were produced
many years ago, and since then, chat applications have evolved. In
enterprise, persistent chat (also known as team chat) has become
popular, commercialized with applications such as Slack, Webex Teams,
Microsoft Teams, Zoom, and others. For consumers, iMessage, Facebook
Messenger, WhatsApp, and WeChat are widely deployed and used.
</t>
<t>None of these products utilize standards-based protocols. This has
not prevented any of them from being widely successful. However, it
has introduced some use cases that are problematic.
</t>
<t>Within enterprises, it has become commonplace for multiple
enterprise chat applications to exist and be deployed. This happens
because some applications are focused on groups of users (such as
developers), while others are more general purpose. It is also not
uncommon for a department to choose one application, and for the
company to choose a different. This forces users to migrate, a painful
process which often never happens. Another similar case is when a
company wishes to migrate from the product of one vendor, to another
vendor. The transition period requires both applications to co-exist,
creating a painful process.
</t>
<t>Another common problem is integration. Almost all of the vendors
today offer web-based APIs for integrating into their
platforms. Though they share some similarities, each are different,
increasing the costs of integrations which support all of them. One
common use case is for contact center agents. These agents typically
utilize their own desktop applications, which are highly specialized
and provided by different vendors. Yet, agents are required to chat
with users throughout the rest of the company. Today, this is
problematic, requiring custom-built integrations, cross-launches, or
multiple conflicting applications on the desktop.
</t>
<t>For consumers, the lack of interoperability between chat apps -
such as iMessage and WhatsApp - is well known. Though there are
reasons for this which are not technical in nature, it is also the
case that there is no specification which would easily facilitate such
interoperation.
</t>
<t>This document is - to a large degree - a thought experiment on what
a standardized protocol would look like, in order to rememdy these
deficiencies. Generally speaking, modern web applications are largely
not the subject of standardization, and thus it is unclear if the
industry really wants or needs a standardized protocol here. Thus,
this document is a strawman and an attempt to determine if in fact
there is inerest in standards activity in this space.</t>
<t>This document is more of an outline of the protocol than an actual
interoperable specification. It focuses on examples and use cases, and
lacks any formalized syntax or normative behaviors. Those would follow
on should this document move forward. </t>
<t>As a target for this thought experiment, the protocol provides the
functionality that is sufficient to enable the union of the features
provided in three applications - Webex Teams, Slack, and
iMessage. This is only because the author is a regular user of all
three, facilitating an easy analysis. The analysis is solely done
based on visual observation of the features of these products, and
consequently, only reflects those visible to the author of this
document. The functionality provided
includes instances (Teams in Slack parlance), persistent chat, text,
image, HTML, GIF, note, and video messages, threading, delivery and
read-receipts, unread markers, unread message counts, chat moderation
and locking, chat group membership management, content
pinning, favorites, deep-links, presence, directory, do-not-disturb,
and user profile capabilities. A basic guest construct is
introduced. That, combined with common techniques for user invitation
and authentication, enable inter-domain and intra-domain federation as
well, without further standardization work.
</t>
<t>Since this is nothing more than a web application, it is fully
specified through its definition of web resources and the affects of
GET, PUT, POST, and DELETE on those resources, along with JSON-based
object models for those resources. The protocol is not focused on
optimization, and without a doubt faster protocols could be
defined. Rather, this protocol focuses on consistency, simplicity and
minimum functionality, at the expense of requiring clients to perhaps
make more queries than a more optimized protocol. </t>
</section>
<section title="Instances">
<t>An instance refers to a unique instance of a chat application. Each
instance has its own set of users, chats, and so on. In Slack, this is
referred to as a "Team". The name team means something different in
Webex Teams, so we use a more descriptive term that is vendor
agnostic.
</t>
<t>The primary functions needed are are for a user to enumerate the
teams they have access to, and for each, get the name and icon for the
team. To do this, a "/instances" resource would exist on the well-known
root URI for the resources defined here. A POST would create a new
instance. GET would enumerate all of them with a list structure. For
example, to get a list of instances:
</t>
<figure>
<artwork><![CDATA[
GET example.com/.well-known/ietf-chat/instances
]]>
</artwork>
</figure>
<t>This would return:</t>
<figure>
<artwork><![CDATA[
{
"instances" : [ {
"name" : "marketing",
"uri" : "https://example.com/.well-known/ietf-chat/instances/23",
"img" : "https://example.com/files/marketing-example-image.jpg"
},{
"name" : "company",
"uri" : "https://example.com/.well-known/ietf-chat/instances/38",
} ]
}
]]>
</artwork>
</figure>
<t>If a service didnt support the notion of instances, it would simply
not implement this resource, or return a single value rather than a
list. </t>
<t>If an instance existed, all subsequent operations in this
specification would live underneath the URI for that instance.
</t>
<t>For the "/instances" resource - and indeed for all resources in
this document - a sub-resource called "events" exists. A client can
perform a long lived GET against this sub-resource. The server will
return a streaming JSON object which includes events against the
resource. These events convey only the URI for the sub-resource which
has changed. In the case of "/instances", if a user had been added to
a new instance, it would receive an event with the URI of the new
instance. The client could then query this URI and obtain the
information about the new instance. Similarly, if an instance were
deleted, the user would receive an event with the URI of the instance
that was deleted. A query against it would return a 404, informing the
client that the instance has been deleted. Alternatively the client
could simply re-query "/instances" with a GET for the complete
list.
</t>
<t>Performing a GET against an instance URI provides the client with
the key information and services across the instance. For example,
with the list of instances above in hand, a client can fetch
information about one of them:
</t>
<figure>
<artwork><![CDATA[
GET https://example.com/.well-known/ietf-chat/instances/23,
]]>
</artwork>
</figure>
<t>and the response might be:</t>
<figure>
<artwork><![CDATA[
{
"uri" : "https://example.com/.well-known/ietf-chat/instances/23",
"img" : "https://example.com/files/marketing-example-image.jpg"
"chats" : "https://example.com/.well-known/ietf-chat/chats/23",
"users": "https://example.com/.well-known/ietf-chat/profs/23",
"presence":"https://example.com/.well-known/ietf-chat/pres/23",
"lists":"https://example.com/.well-known/ietf-chat/lists/23",
}
]]>
</artwork>
</figure>
<t>In this case, the instance supports chat, user and directory
services, presence services, and list services. Each includes the URI
for accessing those serices. In general, this specification avoids
defining the structure of URIs, and instead returns URIs as
values. This follows the HATEOAS principle. </t>
</section>
<section title="The Chat Service">
<section title="Chats">
<t>The chats resource represents all of the chats available to
the user. A chat is used to model 1-1 conversations (of which there
is often just one instance between a pair of users), multi-user
chats, as well as named channels. </t>
<t>A GET to chats would return the list of chats. For each chat,
there is sufficient meta-data to render the chat in a list of
chats. These include "name", and "img". To facilitate rendering if
chats in order of most recent post, there is also a "last-updated"
parameter which specifies the time it was last updated. To
facilitate clients performing catch-up queries to retrieve the
changed chats since last time it had been synchronized, a client can
include a "since" and "until" parameter that is used to filter the
response based on the value of the "last-updated"
parameter. Omitting both "since" and "until" would perform a full
refresh (which of course would be paginated). Omitting "since" but
including "until", and using a value for "until" which matches the
value of "last-updated" in the most recent chat, would return all
chats which had some kind of new message since the last one on the
client. Providing "since" and "until" allows for time-ranged based
queries. </t>
<t>The result of the GET to chats in all cases is a paginated
list of chats. For example:</t>
<figure>
<artwork><![CDATA[
{
"chats" : [ {
"name" : "sales-kickoff",
"uri" : "https://example.com/.well-known/ietf-chat/chats/abbx",
"last-updated" : "2020-02-01T23:02:26+00:00"
},{
"name" : "all-company",
"uri" : "https://example.com/.well-known/ietf-chat/chats/299ak",
"last-updated" : "2020-02-01T22:01:18+00:00"
} ]
}
]]>
</artwork>
</figure>
<t>A chat itself has many attributes. These include "name", "uri",
"img" and "last-updated" - same as returned in the list of chats. Its
core sub-resources are "messages" which is the ordered list of
messages in the chat, "participants" which specifies the participants,
"policy" which specifies the policy for the chat, "files" for the
files sent in the chat, "links" for the links in the chat, "threads"
for the threads and "favorites" for the pinned objects. As with all
objects, it can be extended with proprietary features unique to the
vendor - i.e., "whiteboards" for Webex Teams.
</t>
<t>The JSON object returned in a response to a GET against the chat
resource might look something like this:
</t>
<figure>
<artwork><![CDATA[
{
"name" : "sales-kickoff",
"uri" : "https://example.com/.well-known/ietf-chat/chats/1",
"policy" : {
"participants" : "all-participants",
"messages-create" : "all-participants",
"messages-modify" : "creator",
"messages-delete" : "admin"
},
"messages":"https://example.com/.well-known/ietf-chat/chats/1/m",
"participants":"https://example.com/.well-known/ietf-chat/chats/1/p1",
"read-receipts":"https://example.com/.well-known/ietf-chat/chats/1/rr",
"delivery-receipts":"https://example.com/.well-known/ietf-chat/chats/1/dr",
"typing-indicators":"https://example.com/.well-known/ietf-chat/chats/1/ti",
"files":"https://example.com/.well-known/ietf-chat/chats/1/fi",
"last-updated" : "2020-02-01T23:02:26+00:00"
}
]]>
</artwork>
</figure>
<t>Note how the JSON contains references to the URIs for messages,
and participants, rather than embedding them. This is one example
where simplicity is favored over optimization. This is done in part in
expectation of HTTP2.0 deployment, which removes much of the overhead
that is normally suffered in dereferencing lists. It also facilitates
interoperability in cases different services provide different
features. If one vendor has the notion of "links" but another does
not, the "links" URI would simply be absent from the chat object. This
interoperability works the other way as well; a client which doesnt
understand the notion of "links" would just ignore this object in the
result.
</t>
<t>
In a similar way, optional features like read receipts, delivery
receipts, typing indicators and so on, are indicated by URIs which
can be used by clients to use those features.
</t>
<t>Policy is one area in which existing products have differing
capabilities. For example, Webex Teams has the notion of a locked (or
moderated) space, wherein only the moderators and add and remove
participants, or delete messages. On the other hand, Slack has the
notion of instance-wide channels which can be joined by anyone, a
concept which doesnt exist in Webex Teams. To model these variations,
the "policy" object describes the rules of the chat based on a series
of permissions. These permissions are rooted on the core objects of
the chat - participants, messages, files, links, favorites. For each
of those, permissions can be sub-typed by create, update or
delete. For each permission, it can be granted to everyone in the chat
"all-participants", everyone in the instance "all-instance", the
admins of the instance "admins", no one "none" or a list of users, by
URI (see below). </t>
<t>To retrieve the list of messages, participants, links and so on - a
GET to the associated URI is performed. This query also supports the
"since" and "until" parameters, allowing for catch ups wherein the
client can obtain only changes since the last sync. This is
particularly useful for "messages" but can also come in handy for
"participants" in chats with many, many members.
</t>
<t>As with "/instances", the chats resource supports a sub-resource
called "/events" which generates events for adds, removes, and changes
to any message, particpant, link, etc. in the chat. </t>
<t>Creating a new chat is accomplished by POSTing to chats, which
returns a body including the chat object in JSON (including of course
its URI). The response would be a 201 if a new chat is created, 200 if
its actually an existing one. When creating a chat, URI
parameters can be present to specify participants and/or a name. In a
product like Slack, which has the notion of a single chat for named
set of participants, a POST to chats with a list of names that
corresponds to an existing chat with those users, would return the URI
for that chat. In the case of Webex Teams, it would create a new chat
and thus return a new URI for a new chat. This allows server-side
decision making on how the service works. To create a channel in
slack, wherein only a channel name is needed, only the "name" URI
parameter would be present.
</t>
<t>Deleting a chat is accomplished with DELETE against the chat
object. As with this request and all others, the server would
authorize whether the client is permitted to perform the action.
</t>
</section>
<section title="Retrieving Messages">
<t>A GET to the messages URI learned from the chat object returns
the list of messages in the chat. The uri parameters "from" and
"since" permit time-ranged based queries. A message object is, like
everything else, identified by a URI. Each message has a creator and
a timestamp. It then has a list of one or more components, which can
be text, links, images (either inline or links), videos, and so
on. The usage of a list enables (for example) posting an image with
accompanying text, a common feature in modern chat apps.
</t>
<t>As an example:</t>
<figure>
<artwork><![CDATA[
GET https://example.com/.well-known/ietf-chat/chats/1/m
]]>
</artwork>
</figure>
<t>would return:</t>
<figure>
<artwork><![CDATA[
[{
"timestamp":"2020-02-01T23:02:26+00:00",
"creator":"https://example.com/.well-known/ietf-chat/users/8as",
"uri":""https://example.com/.well-known/ietf-chat/chats/1/m23",
"components" [
{
"type":"text/plain",
"embed":"Check out ths cat!"
},
{
"type":"image/jpeg",
"link":"https://example.com/.well-known/ietf-chat/files/h98s"
} ]
}]
]]>
</artwork>
</figure>
<t>In this case, the chat has one message. For this message, the
image is referenced by hyperlink, whereas the text is embedded. </t>
<t>It is common for users to attach files (such as an Adobe PDF file
or Microsoft Powerpoint file). Thumbnails (pre-rendered images) for the
files can be provided through the thumnail parameter:
</t>
<figure>
<artwork><![CDATA[
[{
"timestamp":"2020-02-01T23:02:26+00:00",
"creator":"https://example.com/.well-known/ietf-chat/users/8as",
"uri":""https://example.com/.well-known/ietf-chat/chats/1/m23",
"components" [
{
"type":"application/vns.ms-powerpoint",
"link":"https://example.com/.well-known/ietf-chat/files/aa9w",
"thumbnail": [
{
"uri":"https://example.com/.well-known/ietf-chat/files/9a9kk",
"page": 1,
"pages": 2,
"type": "image/jpeg"
},
{
"uri":"https://example.com/.well-known/ietf-chat/files/-99Hn",
"page": 2,
"pages": 2,
"type": "image/jpeg"
},
} ]
}]
]]>
</artwork>
</figure>
<t>To learn about new messages in a chat, the client performs a GET
to the /events sub-resource of the messages URI or chat URI (the
latter also performing notifications of participant changes, policy
changes and so on). As with others above, this returns a stream of
JSON objects which contain only the URI for
the message which has been added, removed or changed. </t>
<t>This design means that, if a user is in 20 chats, and they wish
to receive updates of new messages in all of them, they would
require 20 long-lived GET transactions. THis provides flexibility in
client design - allowing one client to receive only messages for a
subset of chats at its discretion. Note that a client will laern
about which chats have any new messages via a GET on /events on the
chats URI, even for ones that it is not performing a GET against
for its events. This provides a baseline client design wherein a
client only performs a GET against /events for the chat which is
visible in the UI.</t>
</section>
<section title="Participant Management">
<section title="Adding, Removing and Listing Participants">
<t>A user can add and remove users from a chat using the
participants URI, learned from the chat object. To retrieve the
current list of participants, the user performs a GET against the
participants URI. To add a user, the client performs a POST against
this URI, and includes in the body the URI of the user:
</t>
<figure>
<artwork><![CDATA[
POST https://example.com/.well-known/ietf-chat/chats/1/p1
{
"user" : "https://example.com/.well-known/ietf-chat/users/8as",
}
]]>
</artwork>
</figure>
<t>The response is a 201 Created response, with a Location header
field that includes the URI of the participant just created. To remove
this participant from the chat, the client can perform a DELETE
against this URI.
</t>
<t>The server will typically perform authorization to determine if
the membership management operations are permitted. A chat where
membership additions is limited to a list of moderators, for example,
would be rejected if the user was not a moderator. </t>
</section>
<section title="Guest Access">
<t>A client can also add a participant that is not part of the system,
by including their email address or E164 number rather than a user URI:
</t>
<figure>
<artwork><![CDATA[
POST https://example.com/.well-known/ietf-chat/chats/1/p1
{
"email" : "jdrosen@example.com"
}
]]>
</artwork>
</figure>
<t>This request will also create a participant object, but have the
side effect of sending that user an invitation to the service as a
guest. The email (or SMS if a phone number was provided) will
contain a well-known guest URI that looks like this:
</t>
<figure>
<artwork><![CDATA[
https://example.com/.well-known/ietf-chat-invitations/9aks00g8a6s/index.html
]]>
</artwork>
</figure>
<t>The usage of a dedicated well-known URI for these invitations is
essential to many interoperability use cases, as described below. A
GET to this URI - invoked only if the end user actually clicks on
the link - would take the user to an HTML page for a guest login
process, download of the client, and so on.
</t>
<t>However, to automate guest access, if this URI is understood by
the email or SMS application, the email or SMS application would
strip the index.html part and perform a GET query against the rest
of it. The server would respond with a JSON object that
provides the client with a JWT token that it can use
to access that service. Using the token, it would query the
"/instances" resource against the well-known chat service URI for
the authority in the URI above. It would then automatically create a
profile in each instance, using the profile data stored in the
client (or retrieved from another instance). It would then be able
to retrieve its list of chats, which would incldue the one it was
just added to.
</t>
</section>
</section>
<section title="Posting Messages and Files">
<t>Adding a message to a chat is accomplished via a POST to the
messages URI. The body can be any MIME type. If no body is present,
it can be specified by reference using the Location header field in
the request.
</t>
<t>In a similar way, a client can add a file to the messaging
service by performing a POST to the files resource of the
instance. In this case, the file isnt posted into any chat or
otherwise become available for others to download. It merely
provides a way for clients to then add the associated content to a
chat by reference, rather than by value. Adding a message to a chat
by reference would then create any of the permissions needed for
other clients in the chat to download that file (by virtue of being
in the chat).
</t>
<t>As an example, to upload the cat image with a caption, the client
would first add the file: </t>
<figure>
<artwork><![CDATA[
POST example.com/.well-known/ietf-chat/files
Content-Type: image/jpeg
{image content}
]]>
</artwork>
</figure>
<t>The response would then indicate the URI which points to the file
that was just uploaded: </t>
<figure>
<artwork><![CDATA[
201 Created
Location: https://example.com/.well-known/ietf-chat/files/h98s
]]>
</artwork>
</figure>
<t>Then, the client can send the cat image with the caption
thusly:</t>
<figure>
<artwork><![CDATA[
POST https://example.com/.well-known/ietf-chat/chats/0e87
{
"components" : [
{
"type":"text/plain",
"embed":"Check out ths cat!"
},
{
"type":"image/jpeg",
"link":"https://example.com/.well-known/ietf-chat/files/h98s"
} ]
}
]]>
</artwork>
</figure>
<t>The response to this provides the client with the reference to this
message, in the 201 Location header field of the response, and
duplicated in the body. The server assigns the creator, timestamp and
URI fields, removed by the server if they were present in the request.
</t>
<t>This basic mechanism also allows for server side thumbnail
creation. The client would add the powerpoint file to the "/files"
resource. The server would perform the thumnail creation behind the
scenes. When the client then posts the message with the reference to
the powerpoint file, the server recognizes this, and adds the
thumbnail links in the JSON in the response. </t>
</section>
<section title="Read and Delivery Receipts">
<t>When a client receives a message, it can post a delivery
receipt. And when it reads one, it can post a read receipt. These
are simple objects which refer to the message which has been
delivered or read. For example, to add a read receipt for that cat
message: </t>
<figure>
<artwork><![CDATA[
POST https://example.com/.well-known/ietf-chat/chats/1/rr
{
"message":"https://example.com/.well-known/ietf-chat/chats/1/messages/3"
"timestamp":"2020-02-01T23:02:26+00:00",
}
]]>
</artwork>
</figure>
<t>Note that the URI against which the post has been made is learned
from the "read-receipts" attribute of the chat object. </t>
<t>Other clients can learn of read receipts in two ways. The first is
to perform a long running GET to /events sub-resource of the delivery
receipts or read receipts resources on the chat URI. These deliver
only delivery and read receipts respectively. The /events resource of
the messages or chat URI however does not provide notifications of
read receipts. This provides clients a choice in how much notification
traffic to receive.
</t>
<t>The other way is by performing a GET against the messages
resource. The server will not actually include read-receipts as
standalone messages, but rather, add the "read-receipts" or
"delivery-receipts" attributes to each message which has had delivery
or read receipts. Each attribute includes the list of users which have
been delivered or read the messages.
</t>
<t>For example - a fetch of a message once there have been some
read-receipts might look like:
</t>
<figure>
<artwork><![CDATA[
[{
"timestamp":"2020-02-01T23:02:26+00:00",
"creator":"https://example.com/.well-known/ietf-chat/users/8as",
"uri":"https://example.com/.well-known/ietf-chat/chats/1/m103",
"read-receipts": {[
"https://example.com/.well-known/ietf-chat/users/j7t",
"https://example.com/.well-known/ietf-chat/users/0Y6"]},
"components" [
{
"type":"application/vns.ms-powerpoint",
"link":"https://example.com/.well-known/ietf-chat/files/aa9w",
"thumbnail": [
{
"uri":"https://example.com/.well-known/ietf-chat/files/9a9kk",
"page": 1,
"pages": 2,
"type": "image/jpeg"
},
{
"uri":"https://example.com/.well-known/ietf-chat/files/-99Hn",
"page": 2,
"pages": 2,
"type": "image/jpeg"
},
} ]
}]
]]>
</artwork>
</figure>
</section>
<section title="Unread Message Counters and Read Markers">
<t>ANother common feature of chat apps is the abiliyt to see the
number of unread messages in a chat. Similarly, when a user enters
this chat, there is frequently a visual indicator showing the
boundary between the oldest read message, and the oldest unread
message. We call this the unread marker.
</t>
<t>This feature is largely driven by the usage of read-receipts
generated by the client.
</t>
<t>If a chat supports read indicators, the chat object will include
an additional parameter called "unread-messages". This represents
the number of messages in the chat which are beyond the most recent
read-receipt issued by the user for that chat. When a client starts
up, and it fetches the chat object, this parameter will be present
and the client can render it, even without fetching the actual
messages in the chat.
</t>
<t>The server can also elect to update this value, and then inform
the client of the change. To do that, a client would be running a
long-running GET to the "chats" resource in the instance. When the
server wishest to update the value for one of the unread indicators,
it sends the client an event for that particular chat. The client
will fetch the object and see the new unread counter. This will
happen even if the client isnt receiving the messages themself. This
approach gives the server a great deal of latitude in how
frequently, and for which chats, it provides updated unread message
counts.
</t>
<t>If the client enters a chat (i.e., it is rendered in the UI), it
will fetch the messages since the last time it synced, via a GET to
the messages URI. The server knows the most recently read message
from read receipts (which may have been read on another client), and
so indicates - in the message object - a "last-read" indicator for that
message which generated the last read receipt. This provides the
unread marker capability.
</t>
</section>
<section title="Typing Indicators">
<t>Typing indicators are similar to read-receipts. However, they
have an expiration which indicates when its
typing indicator should be removed. To post a typing indicator to a
chat, the client posts to the typing indicator resource learned from
the chat object itself. </t>
<figure>
<artwork><![CDATA[
POST https://example.com/.well-known/ietf-chat/chats/0e87/ti
{
"expires":"2020-02-01T23:02:26+00:00",
}
]]>
</artwork>
</figure>
<t>The server would respond with a 201 and the Location header field
contains a pointer to this read-receipt. That read-receipt resource
exists for a short while, only until the expiration is passed.</t>
<t>Clients can receive real-time notifications of typing indicators by
performing a GET against the "/events" subresource of the typing
indicators URI. This will provide the client with the typing
indicators sent by other clients into the chat. A client can retrieve
the current list of non-expired read receipts by performing a GET
against the typing indicators resource. Typing indicators are not
returned in response to a fetch of the current messages in the chat.
</t>
</section>
<section title="Files in a Chat">
<t>When the "files" attribute is present in the chat object, it
means that the client can post and retrieve the files uploaded to
the chat. </t>
<t>
Files can be uploaded to the chat by posting a message to the chat
resource that contains a file (either embedded or uploaded to the
separate files repository of the instance itself), or by
performing a POST directly to the files URI. The latter is
isomorphic to posting a message to the chat containing just a
file.
</t>
<t>Performing a GET against the files URI returns the files that are
available in the chat. The GET can contain the optional "since" and
"until" in order to provide re-synchronization. Results are
paginated as needed. The result is a list of file URI.
</t>
</section>
<section title="Message Edit and Delete">
<t>A user can delete a message by performing a DELETE against the
message URI. This will generate an event to any client performing an
event against the /events subresource on the messages URI,
containing the URI of the message that was deleted. A client
receiving this notification would perform a GET against this URI and
get back a 404, indicating it was a deletion. The message can then
be removed from the UI.
</t>
<t>When a client retrieves messages by performing a GET against the
messages URI, any messages which have been deleted are, of course,
not provided in the response.
</t>
<t>Modifying a message works similarly - the client performs a PUT
against the message URI. Other users find out about the change via
the /events subresource on the messages URI, which provides other
clients with the URI of the changed message. The clients would
perform a GET. This time, the message is returned but it will be
marked with an "edited" attribute which indicates by whome and when
the message was edited, along with the new message content. Of
course, when a client performs a GET against the messages URI, it
receives only the edited version of the message, along with the
"edited" attribute.
</t>
</section>
<section title="Threads">
<t>Threads are modeled as a chat within a chat. The capability for
threading in a chat is discovered because the chats JSON object will
have a "threads" attribute containing the URI to use for retrieving
threads. </t>
<t>To create a thread from a message, the client performs a POST
against the threads URI, and provides a reference to the message:
</t>
<figure>
<artwork><![CDATA[
POST https://example.com/.well-known/ietf-chat/chats/0e87/th
{
"message":"https://example.com/.well-known/ietf-chat/chats/1/m103"
}
]]>
</artwork>
</figure>
<t>The server would create the thread, and return a URI. This URI has
semantics identical to a chat. A GET against it will return a
description of the "chat", including URIs for posting messages,
read-receipts, and so on. If the object includes a "threads" URI, it
means the service supports threads within a thread. The server can
control the depth of the threading by deciding when to return a
"threads" URI and when to not.
</t>
<t>A client can learn about new messages in just the thread by
performing a GET on the thread's URI. A GET against the top-level
messages URI will inform the client about messages in the main chat
and all threads and subthreads.
</t>
</section>
<section title="Pinned Content">
<t>Pinned content in a chat is just a list. The chat object would
include a URI, called "pins" in its JSON, which represents this
list. Using the list processes defined below, a user can add, remove
or retrieve all of the objects in the list.
</t>
</section>
</section>
<section title="Profile, Users and Directory">
<t>A user is represented by a URI, of course, which uniquely
identifies the user. An actual human user with accounts on multiple
instances in multiple domains would have multiple URI, one in each
instance and domain it is on. </t>
<t>A user can define their own profile, through the "users" URI on
the instance itself. To do this, a user would POST to the users
URI, and include a body which describes their key attributes. These
include firstname, lastname, city, image, email, phone number, and so
on. Server policy can decide which of these fields the user can set,
vs. which ones the server obtains through other sources (i.e., email
generally should not be settable by the client). The server can
ignore any attributes that the client cannot set. The response to
this POST includes a URI identifying the user themself. If a system
only allows the user to have a single profile, the URI returned will
not change if the user does a subsequent POST. </t>
<figure>
<artwork><![CDATA[
POST https://example.com/.well-known/ietf-chat/profs/23
{
"firstname":"Jonathan",
"lastname":"Rosenberg",
"email":"fake@example.com"
}
]]>
</artwork>
</figure>
<t>And the server would reply:</t>