-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy pathindex.html
889 lines (854 loc) · 56.8 KB
/
index.html
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
<html>
<head>
<style>
html {
max-width: 800px;
margin-left: auto;
margin-right: auto;
padding: 80px 0;
line-height: 1.5em;
}
h2 {
margin-top: 70px;
}
h3 {
margin-top: 50px;
}
table {
border-collapse: collapse;
}
td, th {
padding: 0.5rem;
border: 1px #aaa solid;
}
a[href^="http"]::after {
content: "⧉";
}
a[href^="#"] {
text-decoration: none;
border-bottom: 1px blue dotted;
}
pre {
white-space: break-spaces;
}
pre, code {
background-color: #e8e8e8;
padding: 2px 4px;
border-radius: 3px;
}
</style>
</head>
<script crossorigin src="https://unpkg.com/mermaid@8.13.4/dist/mermaid.min.js"></script>
<body>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h1 id="ssb-rooms-20">SSB Rooms 2.0</h1>
<p><strong>Revision:</strong> <code>2022-10-10</code></p>
<p><strong>Author:</strong> Andre Medeiros <a href="mailto:contact@staltz.com">contact@staltz.com</a></p>
<p><strong>License:</strong> This work is licensed under a <a href="http://creativecommons.org/licenses/by/4.0/">Creative Commons Attribution 4.0 International License</a>.</p>
<h2 id="abstract">Abstract</h2>
<p>A room server is an SSB peer with privileged internet presence (for instance, not behind a NAT layer) which allows its clients to perform tunneled connections wich each other. For practical purposes, room clients seem to be connected to each other directly, but the room is an intermediary. Connections between server and client are end-to-end encrypted via secret-handshake, as well as in tunneled connections between room clients, so that the room server cannot eavesdrop on the payloads in the tunneled connections. This document describes new capabilities for rooms, such as user aliases, privacy modes, and tunneled authentication.</p>
<h2 id="terminology">Terminology</h2>
<p>The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in <a href="https://tools.ietf.org/html/rfc2119">RFC 2119</a>.</p>
<h2 id="table-of-contents">Table of contents</h2>
<ul>
<li><a href="#stakeholders">Stakeholders</a>
<ul>
<li><a href="#room-admin">Room admin</a></li>
<li><a href="#internal-user">Internal user</a></li>
<li><a href="#external-user">External user</a></li>
<li><a href="#moderator">Moderator</a></li>
</ul>
</li>
<li><a href="#setup">Setup</a>
<ul>
<li><a href="#components">Components</a></li>
<li><a href="#privacy-modes">Privacy modes</a></li>
<li><a href="#config-database">Config database</a></li>
<li><a href="#web-dashboard">Web Dashboard</a></li>
</ul>
</li>
<li><a href="#participation">Participation</a>
<ul>
<li><a href="#joining">Joining</a></li>
<li><a href="#internal-user-registry">Internal user registry</a></li>
<li><a href="#internal-user-authentication">Internal user authentication</a></li>
<li><a href="#invites">Invites</a></li>
<li><a href="#tunnel-addresses">Tunnel addresses</a></li>
<li><a href="#tunneled-connection">Tunneled connection</a></li>
<li><a href="#tunneled-authentication">Tunneled authentication</a></li>
<li><a href="#metadata-api">Metadata API</a></li>
<li><a href="#attendants-api">Attendants API</a></li>
</ul>
</li>
<li><a href="#alias">Alias</a>
<ul>
<li><a href="#alias-string">Alias string</a></li>
<li><a href="#alias-registration">Alias registration</a></li>
<li><a href="#alias-revocation">Alias revocation</a></li>
<li><a href="#alias-consumption">Alias consumption</a></li>
<li><a href="#web-endpoint">Web endpoint</a></li>
<li><a href="#alias-database">Alias database</a></li>
</ul>
</li>
<li><a href="#appendix">Appendix</a>
<ul>
<li><a href="#list-of-new-muxrpc-apis">List of new muxrpc APIs</a></li>
<li><a href="#list-of-new-ssb-uris">List of new SSB URIs</a></li>
</ul>
</li>
</ul>
<h2 id="stakeholders">Stakeholders</h2>
<p>Persons or organizations that are involved or engaged in or around room servers. They may hold responsibilities or powers, and may cause harm to other stakeholders when their responsibilities or powers are abused. They hold interest in engaging with other stakeholders while managing the risk for harm associated with engagement. Harm mitigation such as <a href="#privacy-modes">Privacy modes</a> is important when discussing stakeholders.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="room-admin">Room admin</h3>
<p>Person or organization responsible for operating the room server, and has full access rights over the server's resources such as logs, disk, memory, etc. In other words, this person or organization physically owns the room server or has SSH access to the remote server hosted in some PaaS cloud provider.</p>
<p>Typically, the admin possesses an SSB ID (it's very common, but not necessarily always the case), and is also a <a href="#moderator">moderator</a> in the room.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="internal-user">Internal user</h3>
<p>SSB user who accesses the room server and is considered <em>internal</em> because they have already <a href="#joining">joined</a> the room and may even have registered an <a href="#readme">alias</a> in the room.</p>
<h4 id="specification">Specification</h4>
<p><strong>Definition:</strong> an <em>internal user</em> of a room is any SSB ID for which the room grants a <a href="#tunnel-addresses">tunnel address</a>. In other words, if an SSB ID is <em>reachable</em> via a <a href="#tunneled-connection">tunneled connection</a> through a room server, then they are considered an internal user of that room.</p>
<p><strong>Becoming an internal user:</strong> read more about that in <a href="#joining">Joining a room</a>.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="external-user">External user</h3>
<p>Any SSB user who is not an <a href="#internal-user">internal user</a> of the room (i.e. do not have a usable <a href="#tunnel-addresses">tunnel address</a> referencing the room), but may still interact with the room server in meaningful ways, for instance with <a href="#tunneled-connection">tunneled connections</a>, <a href="#web-endpoint">alias endpoints</a> or <a href="#alias-consumption">alias consumption</a>.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="moderator">Moderator</h3>
<p>A moderator is an <a href="#internal-user">internal user</a> that has acquired special privileges in the <a href="#web-dashboard">web dashboard</a> and actions allowed by the dashboard.</p>
<p>Moderators can use sign-in to access the <a href="#web-dashboard">dashboard</a>.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h2 id="setup">Setup</h2>
<p>There are different ways a room server can be configured.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="components">Components</h3>
<p>A room server is defined by several components, which are systems that enable features, some of these are optional and some are required.</p>
<h4 id="required">Required</h4>
<ul>
<li><a href="#tunneled-connection">Tunneled connection</a></li>
<li><a href="#tunnel-addresses">Tunnel addresses</a></li>
<li><a href="#privacy-modes">Privacy modes</a> (at least the <em>Open</em> mode)</li>
<li><a href="#joining">Joining</a> (for at least the <em>Open</em> mode)</li>
</ul>
<h4 id="optional">Optional</h4>
<ul>
<li>Other <a href="#privacy-modes">Privacy modes</a> and respective ways of <a href="#joining">joining</a></li>
<li><a href="#internal-user-authentication">Internal user authentication</a></li>
<li><a href="#tunneled-authentication">Tunneled authentication</a></li>
<li><a href="#invites">Invites</a></li>
<li><a href="#web-dashboard">Web Dashboard</a></li>
<li><a href="#readme">Aliases</a></li>
</ul>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="privacy-modes">Privacy modes</h3>
<p>A room server should allow the <a href="#room-admin">room admin</a> or a <a href="#moderator">moderator</a> to configure which users can become <a href="#internal-user">internal user</a>.</p>
<h4 id="specification-1">Specification</h4>
<p>There are three strategies recommended as policies to <a href="#joining">join</a> the room, known as privacy modes:</p>
<ul>
<li><strong>Open</strong>: invite codes are openly known, similar to <a href="https://github.com/staltz/ssb-room">ssb-room v1</a></li>
<li><strong>Community</strong>: only <a href="#internal-user">internal users</a> can invite <a href="#external-user">external users</a> to become an internal users</li>
<li><strong>Restricted</strong>: only <a href="#room-admin">admins</a> and <a href="#moderator">moderators</a> can invite <a href="#external-user">external users</a> to become an internal users, and <a href="#readme">aliases</a> are not supported</li>
</ul>
<p><strong>Joining:</strong> To become a member of the room, peers need to <a href="#joining">join the room</a>.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="config-database">Config database</h3>
<p>The configuration database holds basic administrative data, readable only by <a href="#room-admin">admins</a> and (indirectly via the <a href="#web-dashboard">dashboard</a>) by <a href="#moderator">moderators</a>.</p>
<h4 id="specification-2">Specification</h4>
<p>The database should contain these data points:</p>
<ul>
<li>Which <a href="#privacy-modes">privacy mode</a> is selected</li>
<li>List of SSB IDs of <a href="#moderator">moderators</a></li>
<li>List of blocked SSB IDs</li>
<li>Name of the room (a short string)</li>
<li>Description for the room (not too long string)</li>
</ul>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="web-dashboard">Web Dashboard</h3>
<p>This is a WWW interface that allows <a href="#moderator">moderators</a> to sign-in and perform some privileged actions. The sign-in method <strong>SHOULD</strong> be <a href="https://github.com/ssb-ngi-pointer/ssb-http-auth-spec">SSB HTTP Authentication</a> but it <strong>MAY</strong> be username/password or other methods. <a href="#internal-user">Internal users</a> can also sign-in and perform basic actions such as <a href="#joining">create invites for other users to join</a>.</p>
<h4 id="specification-3">Specification</h4>
<p>The dashboard grants <a href="#moderator">moderators</a> with features and powers such as:</p>
<ul>
<li>Block SSB IDs from connecting with this room, meaning two things:
<ul>
<li>If they were an <a href="#internal-user">internal user</a>, they get demoted to <a href="#external-user">external user</a></li>
<li>Even if they were an <a href="#external-user">external user</a>, the room server will reject new attempts of secret-handshake connections</li>
</ul>
</li>
<li>Unblock SSB IDs that are blocked</li>
<li>Nominate other internal users to become moderators too</li>
<li>View the list of aliases according to the <a href="#alias-database">Alias database</a></li>
<li>Revoke aliases by removing an entry from the <a href="#alias-database">Alias database</a></li>
<li>Change the <a href="#privacy-modes">privacy mode</a> of the room</li>
<li>View other technical measurements such as bandwidth used, storage used by the databases, etc</li>
</ul>
<p>The dashboard grants <a href="#internal-user">internal users</a> basic features such as:</p>
<ul>
<li>Register an alias for themselves</li>
<li>Revoke an alias for themselves</li>
<li>Create an invite for <a href="#external-user">external users</a> to <a href="#joining">join the room</a> if the room is running in <a href="#privacy-modes">Community mode</a></li>
</ul>
<h4 id="security-considerations">Security considerations</h4>
<h5 id="https-vulnerabilities">HTTPS vulnerabilities</h5>
<p>Typically SSB has not relied on the certificate architecture underlying TLS, and has had no interoperability with HTTPS. Since rooms 2.0 rely on HTTPS, then the vulnerabilities inherent in TLS, such weak certificate authorities that can enable man-in-the-middle attacks. In such scenarios, with room servers there would be possibility for man-in-the-middle attacks when claiming invites (redirecting to another multiserver address), when <a href="#alias-consumption">resolving aliases</a> (impersonating the alias owner), or when performing sign-in with SSB identities.</p>
<p>To mitigate these types of attacks, implementations and deployments of rooms should make a conscious choice of a trustworthy certificate authority.</p>
<h5 id="malicious-moderator">Malicious <a href="#moderator">moderator</a></h5>
<p>Moderators obviously hold some power, and this power may be abused through unfair blocks, unfair revoking of aliases. In many cases, fairness is subjective, and is understood to be an essential compromise of having moderation to begin with. So in this section we will focus our attention on unusual security issues with moderation.</p>
<p>A moderator has the right to nominate other internal users to become moderators, and this could lead to a proliferation of moderators, which increases the possibility that one of these moderators abuses their powers. On the other hand, there has been many maintainers and npm owners in the <a href="https://github.com/ssbc/">SSBC</a> (e.g. 32 GitHub org members and 17 npm owners for the cornerstone <a href="https://www.npmjs.com/package/ssb-db"><code>ssb-db</code></a> package), we also know that the presence of many moderators may also help to <em>decrease</em> the possibility of abuse, because asymmetry of privilege is reduced.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h2 id="participation">Participation</h2>
<p>Before peers can connect to each other via a room server, they first need to become members, i.e. <a href="#internal-user">internal users</a>. This section describes the different protocols used for establishing internal user participation.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="joining">Joining</h3>
<p>"Joining a room" means the process where an <a href="#external-user">external user</a> becomes an <a href="#internal-user">internal user</a>.</p>
<h4 id="specification-4">Specification</h4>
<p>The joining process is different for each <a href="#privacy-modes">Privacy mode</a>:</p>
<ul>
<li><strong>Open:</strong>
<ol>
<li>An <a href="#external-user">external user</a>, Alice, acquires the open <em>invite code</em> either through the room's public website or via other means</li>
<li>Alice consumes the invite code in her SSB app that supports being a room client</li>
<li>The room accepts the connection from Alice and immediately grants her a <a href="#tunnel-addresses">tunnel address</a></li>
<li>Alice has become an <a href="#internal-user">internal user</a></li>
</ol>
</li>
<li><strong>Community:</strong>
<ol>
<li>An <a href="#internal-user">internal user</a>, Bob, signs into the room's <a href="#web-dashboard">web dashboard</a> where he creates a one-time invite code in the form of an <a href="#invites">invite link</a>, provided by the dashboard.</li>
<li>Bob informs an <a href="#external-user">external user</a>, Alice, of the invite link</li>
<li>Alice consumes the invite according to the <a href="#invites">invites specification</a></li>
<li>Alice has become an <a href="#internal-user">internal user</a></li>
</ol>
</li>
<li><strong>Restricted:</strong>
<ol>
<li>A <a href="#moderator">moderator</a>, Carla, signs into the room's <a href="#web-dashboard">web dashboard</a> where she creates a one-time invite code in the form of an <a href="#invites">invite link</a>, provided by the dashboard.</li>
<li>Bob informs an <a href="#external-user">external user</a>, Alice, of the invite link</li>
<li>Alice consumes the invite according to the <a href="#invites">invites specification</a></li>
<li>Alice has become an <a href="#internal-user">internal user</a></li>
</ol>
</li>
</ul>
<p>To summarize, in <strong>Community</strong> mode, all internal users can create invites while in <strong>Restricted</strong> mode only moderators can. <strong>Open</strong> mode means there always is an invite for all the users in the room.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="internal-user-registry">Internal user registry</h3>
<p>The <em>internal user registry</em> is a database the room manages, keeping records of which SSB users are <a href="#internal-user">internal users</a>. It is a simple list or table, where each entry refers to an internal user, and must contain at least the SSB ID for that user.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="internal-user-authentication">Internal user authentication</h3>
<p>In rooms where the <a href="#privacy-modes">privacy mode</a> is not <em>open</em>, not all SSB users who connect to the room are <a href="#internal-user">internal users</a>. The room thus needs a way to authenticate the user before granting them a <a href="#tunnel-addresses">tunnel address</a>.</p>
<h4 id="specification-5">Specification</h4>
<p>When the room receives an incoming secret-handshake connection from Alice, it checks the <a href="#internal-user-registry">internal user registry</a>, looking for the entry corresponding to Alice's ID. If there is an entry, Alice is recognized as an internal user, granting her a <a href="#tunnel-addresses">tunnel address</a>. Otherwise, the room recognizes Alice as an <a href="#external-user">external user</a> and does not grant Alice a tunnel address.</p>
<p>In either case, whether Alice is an internal or external user, the secret-handshake and muxrpc connection is allowed to remain up, because external users are allowed to <a href="#alias-consumption">consume aliases</a> and create <a href="#tunneled-connection">tunneled connections</a> with internal users. The exception to the above is when the room is in <a href="#privacy-modes"><em>Restricted</em> mode</a>, in which case only internal users are allowed to maintain a secret-handshake and muxrpc connection.</p>
<h4 id="security-considerations-1">Security considerations</h4>
<h5 id="malicious-room-admin">Malicious <a href="#room-admin">room admin</a></h5>
<p>The room software could be modified by the room admin to not authenticate some users as internal users.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="invites">Invites</h3>
<p>When <a href="#joining">joining</a> a <em>Community</em> room or <em>Restricted</em> room, <a href="#internal-user">internal users</a> create invites. An invite can be sent to anyone who is not yet an internal user, and who can then "claim" the invite in order to become a new internal user of the room.</p>
<p>A room server <strong>SHOULD</strong> employ <a href="https://github.com/ssbc/ssb-http-invite-spec">SSB HTTP Invites</a>.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="tunnel-addresses">Tunnel addresses</h3>
<p>To establish a <a href="#tunneled-connection">tunneled connection</a>, the peer initiating it must know the <em>tunnel address</em> of the peer at the other side of the tunnel.</p>
<h4 id="specification-6">Specification</h4>
<p>A tunnel address is a string conforming to the <a href="https://github.com/ssbc/multiserver-address">multiserver-address</a> grammar. We say that "room M <em>grants</em> peer A a tunnel address" when room M allows other peers to request and establish <a href="#tunneled-connection">tunneled connections</a> with peer A, using the tunnel address to identify peer A.</p>
<p>It consists of three parts and <code>:</code> as separators in between:</p>
<ul>
<li><code>tunnel</code> as a constant tag</li>
<li>SSB ID of the intermediary peer</li>
<li>SSB ID of the target peer</li>
</ul>
<h4 id="example">Example</h4>
<p>Without spaces nor newlines:</p>
<pre><code>tunnel:@7MG1hyfz8SsxlIgansud4LKM57IHIw2Okw
/hvOdeJWw=.ed25519:@1b9KP8znF7A4i8wnSevBSK
2ZabI/Re4bYF/Vh3hXasQ=.ed25519
</code></pre>
<p>The tunnel address, being a multiserver address, can also contain a <em>transform</em> section, such as the common <code>shs</code> transform (without spaces nor newlines):</p>
<pre><code>tunnel:@7MG1hyfz8SsxlIgansud4LKM57IHIw2Okw
/hvOdeJWw=.ed25519:@1b9KP8znF7A4i8wnSevBSK
2ZabI/Re4bYF/Vh3hXasQ=.ed25519~shs:1b9KP8z
nF7A4i8wnSevBSK2ZabI/Re4bYF/Vh3hXasQ=
</code></pre>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="tunneled-connection">Tunneled connection</h3>
<p>A tunneled connection is an indirect connection between two peers assisted by an intermediary peer. Ideally, two peers could always connect with each other directly, but they often have unstable IP addresses behind NATs and firewalls, making it difficult to consistently and reliably establish connections. The purpose of the intermediary peer is to improve connection reliability, because these intermediary peers can be privileged nodes with public IP addresses, such as from hosting services.</p>
<h4 id="specification-7">Specification</h4>
<p>Tunneled connections in SSB originated from the proof-of-concept <a href="https://github.com/ssbc/ssb-tunnel">ssb-tunnel</a> module. Suppose A and B are clients of a intermediary server M. Peer A creates a conventional <a href="https://ssbc.github.io/scuttlebutt-protocol-guide/#handshake">handshake</a> connection to M, and waits to receive tunnel connections. Peer B creates a conventional secret handshake connection to M, and then requests a tunneled connection to A through that conventional connection (B-M). Then, M calls A, creating a tunneled connection where one end is attached to A and the other end is attached to B's request. Finally, B uses the secret handshake to authenticate A.</p>
<p>Notice that for the intermediary M, peer A is the server and B is the client (client calls, server answers) but M is just the portal. The tunneled connection is inside the outer (conventional) connections, which means it is encrypted twice with <a href="https://ssbc.github.io/scuttlebutt-protocol-guide/#box-stream">box stream</a>. This means A and B can mutually authenticate each other, and M cannot see the content of their connection.</p>
<p>Diagram:</p>
<pre><code>,---, ,---, ,---,
| |----->| |<----| |
| A |<=====| M |<====| B |
| |----->| |<----| |
`---` `---` `---`
</code></pre>
<p>The arrows represent the direction of the connection – from the client, pointing to the server. Notice the M<=B connection is the same direction as the M<-B outer connection, but the A<=M connection is the opposite direction as the A->M outer connection.</p>
<h4 id="security-considerations-2">Security considerations</h4>
<h5 id="malicious-room-admin-1">Malicious <a href="#room-admin">room admin</a></h5>
<p>The room admin could log and track all connection sessions for every tunneled connection, thus tracking the <strong>IP addresses</strong>, <strong>timestamps</strong>, <strong>durations</strong>, and <strong>bandwidth</strong> of interactions between <a href="#internal-user">internal users</a>. The room admin could track which SSB users are interested in connecting with internal users, i.e. they can gather <strong>social interest metadata</strong>, which could be used to create a draft of a portion of the social graph.</p>
<p>That said, because of encrypted tunneled secret-handshake channels, the room admin could not know the contents of data transmitted between the internal users.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="tunneled-authentication">Tunneled authentication</h3>
<p>Tunneled authentication is about making sure that SSB peers on the opposite end of a <a href="#tunneled-connection">tunneled connection</a> only allow the connection to occur if they follow the peer on the other side. Thus we need a way for peers to know who wants to open a tunneled connection and we should facilitate mutual follows to occur so that peers only create tunneled connections imbued with mutual interest.</p>
<h4 id="specification-8">Specification</h4>
<p>Tunneled friend authentication is an algorithm or protocol that applies automatically without any user input from either end of the secret-handshake channel. This protocol should not apply for the intermediary peer, that is, the room server.</p>
<p>When Alice receives a tunneled secret-handshake incoming connection from Bob, she automatically allows it if Alice checks that she follows Bob, or automatically cancels the connection if Alice checks that she does not follow Bob (or blocks Bob). Same is true reciprocically: Bob applies this rule for incoming connections from Alice.</p>
<p>Thus tunneled authentication <strong>requires mutual follows</strong> ("friendship") before establishing a functioning <a href="#tunneled-connection">tunneled connection</a>.</p>
<p>When a denial of connection occurs, the peer that received the connection should be able to see (and thus locally log): (1) SSB ID of the intermediary peer (room) used, (2) SSB ID of the origin peer behind the intermediary, (3) (optionally) the address (<a href="#tunnel-addresses">tunnel address</a> or <a href="#web-endpoint">alias endpoint URL</a>) of the origin peer.</p>
<p>The user that received the denied connection can then see this fact in their SSB app, and then they can make a conscious choice to either (1) follow the origin peer, or (2) connect to the origin peer (if (3) from the previous paragraph existed), or both.</p>
<h4 id="implementation-notes">Implementation notes</h4>
<p>Note that in current room server implementation in JavaScript, <a href="https://github.com/staltz/ssb-room/blob/e78d54887682664def36d48ca9e648fc609478e9/tunnel/server.js#L100"><code>opts.origin</code></a> in the room is calculated from secret-handshake, so it can be trusted to be authentic.</p>
<p>For the next version of rooms, if we want <code>opts.origin</code> to also contain the origin peer's address (ssb-tunnel address or alias endpoint), then we need other means of verifying that the origin address is authentic. E.g. if it's an <a href="#web-endpoint">alias endpoint URL</a>, maybe the receiving peer visits the alias JSON endpoint then <a href="#alias-consumption">consumes the alias</a>, or maybe the receiving peer takes the ssb-tunnel address and verifies that the ID matches with the secret-handshake-given ID.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="metadata-api">Metadata API</h3>
<p>Clients, whether internal or external users, may need to know additional information about the room before interacting with it. For instance, they may need to know whether they are an internal user or not, and they may need to know what features the room has currently enabled.</p>
<h4 id="specification-9">Specification</h4>
<p>The muxrpc API <code>room.metadata</code> is an <code>async</code> method that returns a JSON object listing information about the room.</p>
<p><strong>Input</strong>: zero arguments required</p>
<p><strong>Output</strong>: JSON body type, with the following JSON schema</p>
<pre><code class="language-json">{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/ssb-ngi-pointer/rooms2#muxrpc-room-metadata",
"type": "object",
"properties": {
"name": {
"title": "Name of this room",
"description": "The domain or hostname or arbitrary name of the room server",
"type": "string"
},
"membership": {
"title": "Membership",
"description": "Whether or not the client calling this muxrpc method is recognized as an internal user",
"type": "boolean"
},
"features": {
"title": "Features",
"description": "A list of features supported by this room",
"type": "array",
"uniqueItems": true,
"items": {
"enum": ["tunnel", "room1", "room2", "alias", "httpAuth", "httpInvite"]
}
}
},
"required": ["name", "membership", "features"]
}
</code></pre>
<p>The <code>features</code> array is particularly important, as it flags which features clients can use on the room. The semantics of each value in the <em>enum</em> are listed below:</p>
<ul>
<li><code>"tunnel"</code>: <strong>MUST</strong> be included in <code>features</code> only if users can establish <a href="#tunneled-connection">tunneled connections</a> with other users</li>
<li><code>"room1"</code>: <strong>MUST</strong> be included only if the room server is fully compatible with Room 1.0, i.e. clients can interact with it in the same manner they interact with Room 1.0 servers. This means the server <strong>MUST</strong> operate with its <a href="#privacy-modes">Privacy mode</a> set to "Open"</li>
<li><code>"room2"</code>: <strong>MUST</strong> be included only if the room server supports muxrpc APIs under the namespace <code>room</code>, such as <code>room.metadata()</code> and <code>room.attendants</code></li>
<li><code>"alias"</code>: <strong>MUST</strong> be included only if the room server supports <a href="#readme">Aliases</a>, i.e. muxrpc APIs <code>room.registerAlias</code>, <code>room.revokeAlias</code> and alias consumption</li>
<li><code>"httpAuth"</code>: <strong>MUST</strong> be included only if the room server complies with the <a href="https://github.com/ssb-ngi-pointer/ssb-http-auth-spec">SSB HTTP Authentication</a> specification</li>
<li><code>"httpInvite"</code>: <strong>MUST</strong> be included only if the room server complies with the <a href="https://ssb-ngi-pointer.github.io/ssb-http-invite-spec/">SSB HTTP Invites</a> specification</li>
</ul>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="attendants-api">Attendants API</h3>
<p>Internal users can discover about the presence of other internal users currently online at the room. This gives them the opportunity to choose to establish a <a href="#tunneled-connection">tunneled connection</a>.</p>
<h4 id="specification-10">Specification</h4>
<p>The muxrpc API <code>room.attendants</code> is a <code>source</code> method that streams JSON objects of three different schemas: <strong>state</strong> objects, <strong>joined</strong> objects, and <strong>left</strong> objects.</p>
<p>There are no input arguments expected on this method.</p>
<p><strong>State objects</strong></p>
<p>When the user subscribes to the <code>room.attendants</code> stream, the first event <strong>MUST</strong> be of type "state", matching the JSON schema below:</p>
<pre><code class="language-json">{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/ssb-ngi-pointer/rooms2#muxrpc-room-attendants-state",
"type": "object",
"properties": {
"type": {
"title": "Event type",
"type": "string",
"pattern": "^(state)$"
},
"ids": {
"title": "SSB IDs of attendants",
"description": "A list of SSB IDs of all internal users currently online",
"type": "array",
"uniqueItems": true,
"items": {
"type": "string"
}
}
},
"required": ["type", "ids"]
}
</code></pre>
<p><strong>Joined objects</strong></p>
<p>Whenever an internal user becomes online in the room, an event matching the following JSON schema below <strong>MUST</strong> be sent through the stream:</p>
<pre><code class="language-json">{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/ssb-ngi-pointer/rooms2#muxrpc-room-attendants-joined",
"type": "object",
"properties": {
"type": {
"title": "Event type",
"type": "string",
"pattern": "^(joined)$"
},
"id": {
"title": "SSB ID",
"description": "SSB ID of the attendant who just joined",
"type": "string"
}
},
"required": ["type", "id"]
}
</code></pre>
<p><strong>Left objects</strong></p>
<p>Whenever an internal user ceases to be online in the room, an event matching the following JSON schema below <strong>MUST</strong> be sent through the stream:</p>
<pre><code class="language-json">{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/ssb-ngi-pointer/rooms2#muxrpc-room-attendants-left",
"type": "object",
"properties": {
"type": {
"title": "Event type",
"type": "string",
"pattern": "^(left)$"
},
"id": {
"title": "SSB ID",
"description": "SSB ID of the attendant who just left",
"type": "string"
}
},
"required": ["type", "id"]
}
</code></pre>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h2 id="alias">Alias</h2>
<p>An alias (also known as "room alias") is a string that identifies an <a href="#internal-user">internal user</a>, designed to be short and human-friendly, similar to <a href="https://en.wikipedia.org/wiki/Email_address">email addresses</a> and <a href="https://docs.joinmastodon.org/spec/webfinger/">Mastodon WebFinger addresses</a>. The purpose of aliases is to improve the user experience of accurately (1) <strong>identifying</strong> the internal user and (2) <strong>locating</strong> the internal user at a room server for the purpose of establishing a connection with them.</p>
<p>As an example, suppose Alice is an internal user of the room "Scuttlebutt EU". The room's domain is <code>scuttlebutt.eu</code> and Alice's <a href="#alias-string">alias</a> is <code>alice</code>. Alice's <a href="#web-endpoint">alias endpoint</a> is thus <code>alice.scuttlebutt.eu</code>.</p>
<p>In short,</p>
<ul>
<li>Anyone can access an alias <a href="#web-endpoint">web endpoint</a></li>
<li><a href="#internal-user">Internal users</a> can <a href="#registration">register</a> and <a href="#revocation">revoke</a> their aliases</li>
<li><a href="#internal-user">Internal users</a> and <a href="#external-user">external users</a> who visit a target user's <a href="#web-endpoint">alias endpoint</a> can <a href="#alias-consumption">consume it</a> in order to <a href="#tunneled-connection">connect</a> with the target internal user</li>
<li><a href="#room-admin">Room admins</a> have read/write access to the <a href="#alias-database">alias database</a> but can't add cryptographically valid entries since they are signed by the owner</li>
<li><a href="#moderator">Moderators</a> can remove alias entries from the <a href="#alias-database">alias database</a></li>
</ul>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="alias-string">Alias string</h3>
<p>An <a href="#internal-user">internal user</a>'s alias, also known as "alias string", is used to uniquely (unique within the room server only) identify that internal user. This string is useful only within the context of the room, i.e. not globally identifiable.</p>
<h4 id="example-1">Example</h4>
<p>Suppose Alice is an internal user of the room "Scuttlebutt EU". Alice's alias could be one of these strings (non-exhaustive list):</p>
<ul>
<li><code>alice</code></li>
<li><code>alice1994</code></li>
<li><code>alice94</code></li>
</ul>
<h4 id="specification-11">Specification</h4>
<p>The string should satisfy the same rules as domain "labels" as defined in <a href="https://tools.ietf.org/html/rfc1035">RFC 1035</a>.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="alias-registration">Alias registration</h3>
<p>An <a href="#internal-user">internal user</a> who does not have an alias in the current room server can choose to register an alias. Not all internal users need to have aliases, so the process described here is optional.</p>
<h4 id="specification-12">Specification</h4>
<ol>
<li>An internal user with SSB ID <code>feedId</code> and a room server with SSB ID <code>roomId</code> are connected to each other via secret-handshake</li>
<li>The internal user chooses a <code>alias</code> as a candidate <a href="#alias-string">alias string</a></li>
<li>The internal user calls a specific <a href="https://github.com/ssb-js/muxrpc/">muxrpc</a> <code>async</code> API <code>room.registerAlias(alias, signature)</code> where <code>signature</code> is a cryptographic signature of the string <code>=room-alias-registration:${roomId}:${feedId}:${alias}</code> using <code>feedId</code>'s cryptographic keypair, read more about it in the <a href="#alias-database">alias database</a> spec</li>
<li>The room, upon receiving the <code>room.registerAlias</code> muxrpc call, checks whether that <code>alias</code> is valid (see spec in <a href="#alias-string">Alias string</a>)
<ol>
<li>If it is invalid, respond <code>room.registerAlias</code> with an error</li>
<li>Else, proceed (below)</li>
</ol>
</li>
<li>The room checks whether there already exists an entry in the <a href="#alias-database">Alias database</a> with the <em>key</em> <code>alias</code>
<ol>
<li>If there is, respond <code>room.registerAlias</code> with an error</li>
<li>Else, proceed (below)</li>
</ol>
</li>
<li>The room adds an entry to its <a href="#alias-database">Alias database</a> for <code>key=alias</code> & <code>value=feedId+sig</code></li>
<li>The room responds <code>room.registerAlias</code> with a string containing the <a href="#web-endpoint">Alias endpoint URL</a> for the newly registered alias, indicating success</li>
<li>The internal user receives the room's response to <code>room.registerAlias</code>
<ol>
<li>If it is an error, then (optionally) display a user interface failure to register the alias</li>
<li>If it is a successful <code>url</code> string, then the internal user <strong>MAY</strong> publish an SSB msg of type <code>about</code> with a field listing all its aliases for various rooms, where this specific <code>url</code> is included. The specific schema of the message type is an application-level concern</li>
</ol>
</li>
</ol>
<p>The above algorithm is also provided below as a UML sequence diagram:</p>
<div class="mermaid">
sequenceDiagram
participant U as SSB peer
participant R as Room server
U->>R: (muxrpc async) `room.registerAlias(alias, signature)`
alt `alias` is an invalid alias string<br/>or already taken in the alias database<br/>or other errors
R-->>U: Respond room.registerAlias with an error
opt
U->>U: Display user interface error
end
else else
R->>R: Adds an entry to<br/>its alias database
R-->>U: Respond `room.registerAlias` with a `url` string
U->>U: Publishes an SSB<br/>msg of type<br/>`about`
end
</div>
<h4 id="security-considerations-3">Security considerations</h4>
<h5 id="malicious-internal-user">Malicious <a href="#internal-user">internal user</a></h5>
<p>A malicious internal user could take many or all possible aliases in case the room accidentally allows such malicious user to become an internal user. Arguably, some room implementations could choose to allow only one alias per internal user, and that would still be compliant with this spec.</p>
<h4 id="malicious-room-admin-2">Malicious <a href="#room-admin">room admin</a></h4>
<p>The room admin could reply with errors when technically the muxrpc should have succeeded, e.g. pretending that the <code>alias</code> candidate is invalid or pretending that it's already registered.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="alias-revocation">Alias revocation</h3>
<p>When an <a href="#internal-user">internal user</a> who has <a href="#registration">registered</a> no longer wishes to have that alias associated with them anymore, they can perform <em>alias revocation</em> to remove that alias from the <a href="#alias-database">alias database</a>.</p>
<h4 id="specification-13">Specification</h4>
<ol>
<li>An internal user with SSB ID <code>feedId</code> and a room server with SSB ID <code>roomId</code> are connected to each other via secret-handshake</li>
<li>The internal user calls a specific <a href="https://github.com/ssb-js/muxrpc/">muxrpc</a> <code>async</code> API <code>room.revokeAlias(alias, callback)</code></li>
<li>The room, upon receiving the <code>room.revokeAlias</code> muxrpc call, checks whether there exists an entry in the <a href="#alias-database">Alias database</a> for <code>alias</code>
<ol>
<li>If there is no entry, respond <code>room.revokeAlias</code> with an error</li>
<li>Else, if there exists an entry for <code>alias</code> but it is not owned by <code>feedId</code>, respond <code>room.revokeAlias</code> with an error</li>
<li>Else, proceed (below)</li>
</ol>
</li>
<li>The room removes the entry from the <a href="#alias-database">Alias database</a> associated with <code>feedId</code></li>
<li>The room responds <code>room.revokeAlias</code> with <code>true</code>, indicating success</li>
<li>The internal user receives the room's response to <code>room.revokeAlias</code>
<ol>
<li>If it is an error, then (optionally) display a user interface failure to revoke the alias</li>
<li>If it is <code>true</code>, then publish an SSB msg of type <code>about</code> with a field listing all its aliases for various rooms, where this specific <code>alias</code> is no longer listed. The specific schema of the message type is an application-level concern</li>
</ol>
</li>
</ol>
<p>The above algorithm is also provided below as a UML sequence diagram:</p>
<div class="mermaid">
sequenceDiagram
participant U as SSB peer
participant R as Room server
U->>R: (muxrpc async) `room.revokeAlias(alias)`
alt `alias` does not exist, or other errors
R-->>U: Respond room.revokeAlias with an error
opt
U->>U: Display user interface error
end
else else
R->>R: Remove the entry in<br/>the alias database
R-->>U: Respond room.revokeAlias with `true`
U->>U: Publishes an SSB<br/>msg of type<br/>`about`
end
</div>
<h4 id="security-considerations-4">Security considerations</h4>
<h5 id="malicious-room-admin-3">Malicious <a href="#room-admin">room admin</a></h5>
<p>The room admin could refuse to remove the database entry, or could delete the database entry at will (before the internal user performs revocation). In other words, the internal user does not ultimately have power over the deletion of the alias entry from the alias database, it must trust the room admin regarding deletion.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="alias-consumption">Alias consumption</h3>
<p>When an SSB user (<a href="#external-user">external</a> or <a href="#internal-user">internal</a>) is connected to the room, and knows of another <a href="#internal-user">internal user's</a> <a href="#alias-string">alias</a>, they can perform <em>alias consumption</em>. After consumption is completed successfully, they authentically obtain the target user's SSB ID and can use it to start a <a href="#tunneled-connection">tunneled connection</a>.</p>
<h4 id="specification-14">Specification</h4>
<p>The input for the consumption algorithm is the response from the <a href="#web-endpoint">web endpoint</a>, which is (either through JSON or SSB URI): the room's multiserver <code>address</code>, <code>roomId</code>, <code>userId</code>, <code>alias</code>, and <code>signature</code>.</p>
<ol>
<li>The SSB user verifies that the <code>signature</code> authentically matches <code>roomId</code>, <code>userId</code> and <code>alias</code>
<ol>
<li>If it is an invalid signature, interrupt alias consumption with a failure indicating that the alias association to the internal user <code>userId</code> was probably forged</li>
<li>Else, proceed (below)</li>
</ol>
</li>
<li>The SSB user acting as a client connects to the room's <code>address</code> and establishes a <a href="https://github.com/ssb-js/muxrpc/">muxrpc</a> connection</li>
<li>The client can now use <code>userId</code> to initiate a <a href="#tunneled-connection">tunneled connection</a> with them</li>
<li>(Optional and recommended) The client <em>follows</em> the <code>userId</code>, see <a href="#tunneled-authentication">tunneled authentication</a></li>
</ol>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="web-endpoint">Web endpoint</h3>
<p>Once an alias is <a href="#registration">registered</a>, it enables any web user to visit a web endpoint on the room server dedicated to that alias, for the purpose of telling the visitor what SSB ID does the alias resolve to, and with instructions on how to install an SSB app if the visitor doesn't have it yet.</p>
<p>The goal of this endpoint is to help any SSB user <em>locate and identify</em> the alias' owner by resolving the alias to: (1) the room's <a href="https://github.com/ssb-js/multiserver">multiserver address</a>, (2) the owner's SSB ID, and (3) a cryptographic signature that proves the owner associated themselves with that alias. This web endpoint is valuable to onboard new SSB users being invited by an <a href="#internal-user">internal user</a>.</p>
<p><strong>Prior art:</strong> This endpoint should be in many ways similar to the <a href="https://telegram.org/">Telegram</a> <code>https://t.me/example</code> service for the username <code>@example</code>, also capable of redirecting the web visitor to a scheme <code>tg</code> URI <code>tg://resolve?domain=example</code>, which Telegram apps know how to parse and open the target user's profile screen.</p>
<h4 id="specification-15">Specification</h4>
<p>This specification does not apply if the <a href="#privacy-modes">privacy mode</a> is <em>Restricted</em>. This web endpoint is available only if the privacy mode is <em>Open</em> or <em>Community</em>.</p>
<p>If the alias <code>${alias}</code> is registered at the room <code>${roomHost}</code> for a certain <code>${userId}</code>, then the room's HTTP endpoint reserved for the alias <strong>SHOULD</strong> be the wildcard subdomain URL <code>https://${alias}.${roomHost}</code> but it <strong>MAY</strong> be <code>https://${roomHost}/${alias}</code>.</p>
<p>The HTML response then:</p>
<ul>
<li><strong>MAY</strong> inform users how to install an SSB app that can correctly consume room aliases</li>
<li><strong>SHOULD</strong> render a "Connect with me" button linking to an SSB URI (see below)</li>
<li>The page <strong>MAY</strong> automatically redirect (when the browser supports it) to an SSB URI (see below)</li>
<li>The alias SSB URI <strong>MUST</strong> be <code>ssb:experimental?action=consume-alias&alias=${alias}&userId=${userId}&signature=${signature}&roomId=${roomId}&multiserverAddress=${roomMsAddr}</code>, in other words there are 6 query components:
<ul>
<li><code>action=consume-alias</code>, a constant string to identify the purpose of this URI</li>
<li><code>alias=${alias}</code>, the <a href="#alias-string">alias string</a></li>
<li><code>userId=${userId}</code>, the SSB ID of the alias's owner</li>
<li><code>roomId=${roomId}</code>, the room's SSB ID</li>
<li><code>signature=${signature}</code>, the alias's owner signature as described in the <a href="#alias-database">alias database</a></li>
<li><code>multiserverAddress=${roomMsAddr}</code>, the room's <a href="https://github.com/ssb-js/multiserver">multiserver address</a> using percent encoding for URIs</li>
</ul>
</li>
</ul>
<p>As an additional endpoint for programmatic purposes, if the query parameter <code>encoding=json</code> is added to the alias endpoint (for illustration: <code>https://${alias}.${roomHost}?encoding=json</code>), then, in successful responses, the JSON body <strong>MUST</strong> conform to the following schema:</p>
<pre><code class="language-json">{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/ssb-ngi-pointer/rooms2#alias-json-endpoint-success",
"type": "object",
"properties": {
"status": {
"title": "Response status tag",
"description": "Indicates the completion status of this response",
"type": "string",
"pattern": "^(successful)$"
},
"multiserverAddress": {
"title": "Multiserver address",
"description": "Should conform to https://github.com/ssbc/multiserver-address",
"type": "string"
},
"roomId": {
"title": "Room ID",
"description": "SSB ID for the room server",
"type": "string"
},
"userId": {
"title": "User ID",
"description": "SSB ID for the user owning the alias",
"type": "string"
},
"alias": {
"title": "Alias",
"description": "A domain 'label' as defined in RFC 1035",
"type": "string"
},
"signature": {
"title": "Signature",
"description": "Cryptographic signature covering the roomId, the userId, and the alias",
"type": "string"
}
},
"required": [
"status",
"multiserverAddress",
"roomId",
"userId",
"alias",
"signature"
]
}
</code></pre>
<p>In failed responses, the JSON body <strong>MUST</strong> conform to the following schema:</p>
<pre><code class="language-json">{
"$schema": "http://json-schema.org/draft-07/schema#",
"$id": "https://github.com/ssb-ngi-pointer/rooms2#alias-json-endpoint-error",
"type": "object",
"properties": {
"status": {
"title": "Response status tag",
"description": "Indicates the completion status of this response",
"type": "string"
},
"error": {
"title": "Response error",
"description": "Describes the specific error that occurred",
"type": "string"
}
},
"required": [
"status",
"error"
]
}
</code></pre>
<h4 id="example-2">Example</h4>
<p>Suppose the alias is <code>bob</code>, registered for the user ID <code>@yVQxFxzeRQ13DQ813hf8G20U5z5I/nkNDliKeSs/IpU=.ed25519</code> at the room with host name <code>scuttlebutt.eu</code>. Then the alias endpoint <code>https://bob.scuttlebutt.eu</code> responds with HTML containing the following SSB URI:</p>
<p><a href="ssb:experimental?action=consume-alias&multiserverAddress=net%3Ascuttlebutt.eu%3A8008~shs%3Azz%2Bn7zuFc4wofIgKeEpXgB%2B%2FXQZB43Xj2rrWyD0QM2M%3D&alias=bob&roomId=%40zz%2Bn7zuFc4wofIgKeEpXgB%2B%2FXQZB43Xj2rrWyD0QM2M%3D.ed25519&userId=%40yVQxFxzeRQ13DQ813hf8G20U5z5I%2FnkNDliKeSs%2FIpU%3D.ed25519&signature=EiEgn%2Fh2lKoaz28ggKBod6havJNKapRKCmXQ%2Ft%2F4KS1gY4T6zPXWhw6kTaglt8vDJZW%2BjJRJvfB4Rryhl0njCg%3D%3D.sig.ed25519">ssb:experimental?action=consume-alias&multiserverAddress=net%3Ascuttlebutt.eu%3A8008~shs%3Azz%2Bn7zuFc4wofIgKeEpXgB%2B%2FXQZB43Xj2rrWyD0QM2M%3D&alias=bob&roomId=%40zz%2Bn7zuFc4wofIgKeEpXgB%2B%2FXQZB43Xj2rrWyD0QM2M%3D.ed25519&userId=%40yVQxFxzeRQ13DQ813hf8G20U5z5I%2FnkNDliKeSs%2FIpU%3D.ed25519&signature=EiEgn%2Fh2lKoaz28ggKBod6havJNKapRKCmXQ%2Ft%2F4KS1gY4T6zPXWhw6kTaglt8vDJZW%2BjJRJvfB4Rryhl0njCg%3D%3D.sig.ed25519</a></p>
<p>The JSON endpoint <code>https://bob.scuttlebutt.eu/?encoding=json</code> would respond with the following JSON:</p>
<pre><code class="language-json">{
"status": "successful",
"multiserverAddress": "net:scuttlebutt.eu:8008~shs:zz+n7zuFc4wofIgKeEpXgB+/XQZB43Xj2rrWyD0QM2M=",
"roomId": "@zz+n7zuFc4wofIgKeEpXgB+/XQZB43Xj2rrWyD0QM2M=.ed25519",
"userId": "@yVQxFxzeRQ13DQ813hf8G20U5z5I/nkNDliKeSs/IpU=.ed25519",
"alias": "bob",
"signature": "EiEgn/h2lKoaz28ggKBod6havJNKapRKCmXQ/t/4KS1gY4T6zPXWhw6kTaglt8vDJZW+jJRJvfB4Rryhl0njCg==.sig.ed25519"
}
</code></pre>
<h4 id="security-considerations-5">Security considerations</h4>
<h5 id="malicious-web-visitor">Malicious web visitor</h5>
<p>A web visitor, either human or bot, could attempt brute force visiting all possible alias endpoints, in order to build a dataset of all SSB IDs and claimed aliases gathered at this room, potentially tracking profiles of these SSB IDs. Malicious web visitors can also attempt to connect with these target IDs as victims, and may use social engineering or impersonation tactics during <a href="#tunneled-authentication">tunneled authentication</a>.</p>
<h5 id="malicious-room-admin-4">Malicious <a href="#room-admin">room admin</a></h5>
<p>The room admin could tamper with the <a href="#alias-database">alias database</a> and provide fake information on this web endpoint, e.g. that a certain alias was claimed by a certain users. Although the <a href="#alias-database">database signature</a> exists to prevent this type of tampering, it is only verified when performing <a href="#alias-consumption">alias consumption</a>. For web visitors who only want to know which SSB ID corresponds to an alias, and only that, these visitors must trust the room administrator, who could provide inauthentic information.</p>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="alias-database">Alias database</h3>
<p>This is a database that stores all aliases that were <a href="#registration">registered</a> by internal users.</p>
<h4 id="example-3">Example</h4>
<p>The following is a mock up of a key-value store:</p>
<table>
<tr>
<th>Key</th>
<th>Value</th>
</tr>
<tbody>
<tr>
<td><code>alice</code></td>
<td><code>@FlieaFef19uJ6jhHwv2CSkFrDLYKJd/SuIS71A5Y2as=.ed25519</code> plus signature</td>
</tr>
<tr>
<td><code>bob</code></td>
<td><code>@25WfId3Vx/gyMAZqCyZzhtW4iPtUVXB/aOMYbq44P4c=.ed25519</code> plus signature</td>
</tr>
<tr>
<td><code>carla</code></td>
<td><code>@dRE+jzKo0VWX6JbcSVATyOvFlbjCNwPWNzQLkTGenac=.ed25519</code> plus signature</td>
</tr>
<tr>
<td><code>daniel</code></td>
<td><code>@SMMgb4bZAgRgtAPdMw4loQeZL9lQgsRDi+xin0ZDzAg=.ed25519</code> plus signature</td>
</tr>
</tbody>
</table>
<h4 id="specification-16">Specification</h4>
<p>This can be a simple persistent key-value store, such as Leveldb.</p>
<ul>
<li>Each <strong>Key</strong> is an <a href="#alias-string">alias string</a></li>
<li>Each <strong>Value</strong> is a string that encodes two things:
<ul>
<li><a href="https://ssbc.github.io/scuttlebutt-protocol-guide/#keys-and-identities">SSB identity</a> of the <a href="#internal-user">internal user</a></li>
<li>A cryptographic signature that covers <strong>all these</strong>
<ul>
<li>the room server's ID, i.e. <code>roomId</code></li>
<li>the SSB ID, i.e. <code>userId</code></li>
<li>alias string, i.e. <code>alias</code></li>
</ul>
</li>
</ul>
</li>
</ul>
<p>The signature is applied on the following string: <code>=room-alias-registration:${roomId}:${userId}:${alias}</code>, known as the <em>Alias confirmation</em>, see example (without spaces nor newlines):</p>
<pre><code>=room-alias-registration:@51w4nYL0k7mRzDGw20KQqCjt35
y8qLiBNtWk3MX7ppo=.ed25519:@FlieaFef19uJ6jhHwv2
CSkFrDLYKJd/SuIS71A5Y2as=.ed25519:alice
</code></pre>
<p>where</p>
<ul>
<li><code>roomId</code> is <code>@51w4nYL0k7mRzDGw20KQqCjt35y8qLiBNtWk3MX7ppo=.ed25519</code></li>
<li><code>userId</code> is <code>@FlieaFef19uJ6jhHwv2CSkFrDLYKJd/SuIS71A5Y2as=.ed25519</code></li>
<li><code>alias</code> is <code>alice</code></li>
</ul>
<p>The purpose of a cryptographic signature on the combined <code>roomId</code> & <code>userId</code> & <code>alias</code> is to make sure that the <a href="#room-admin">Room admin</a> cannot tamper with the database to delegitimize its contents. This means that each key-value pair is certainly authored by the declared SSB ID, that is, neither the key (the alias) nor the value (the SSB ID) was modified by the Room admin.</p>
<h4 id="security-considerations-6">Security considerations</h4>
<h5 id="malicious-room-admin-5">Malicious <a href="#room-admin">room admin</a></h5>
<p>The room admin can freely read or write to this database, they can create new entries, and so forth. If they modify an entry and thus break the validation of the signatures, other SSB users can detect this when verifying the signatures.</p>
<p>Thus the admin <strong>cannot</strong> effectively:</p>
<ul>
<li>Register a signed alias on behalf of an <a href="#internal-user">internal user</a></li>
<li>Modify a registered alias made by <a href="#internal-user">internal users</a></li>
</ul>
<p>But the admin <strong>can</strong>:</p>
<ul>
<li>Remove any registered key-value pairs from the database, essentially removing an alias</li>
<li>Register signed aliases for fake users it has created itself</li>
</ul>
<h5 id="malicious-moderator-1">Malicious <a href="#moderator">moderator</a></h5>
<p>Similar considerations as with the room admin, but less powers. The malicious moderator <em>cannot</em> do the actions that the room admin cannot do (otherwise moderators would have more power than admins), but the one thing moderators can do is:</p>
<ul>
<li>Remove any registered key-value pairs from the database, essentially removing an alias</li>
</ul>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h2 id="appendix">Appendix</h2>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="list-of-new-muxrpc-apis">List of new muxrpc APIs</h3>
<ul>
<li>async
<ul>
<li><code>room.metadata()</code></li>
<li><code>room.registerAlias(alias, signature)</code></li>
<li><code>room.revokeAlias(alias)</code></li>
</ul>
</li>
<li>source
<ul>
<li><code>room.attendants()</code></li>
</ul>
</li>
</ul>
<!--
SPDX-FileCopyrightText: 2021 Andre 'Staltz' Medeiros
SPDX-License-Identifier: CC-BY-4.0
-->
<h3 id="list-of-new-ssb-uris">List of new SSB URIs</h3>
<ul>
<li><code>ssb:experimental?action=consume-alias&alias=${alias}&userId=${userId}&signature=${signature}&roomId=${roomId}&multiserverAddress=${roomMsAddr}</code></li>
</ul>
</body>
</html>