Welcome to mirror list, hosted at ThFree Co, Russian Federation.

standalone-signaling-api-v1.md « docs - github.com/nextcloud/spreed.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 1ee91550d2e104edc09fe53c33e69a7fb4103b05 (plain)
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
# External signaling API

This document gives a rough overview on the API version 1.0 of the Spreed
signaling server. Clients can use the signaling server to send realtime
messages between different users / sessions.

The API describes the various messages that can be sent by a client or the
server to join rooms or distribute events between clients.

Depending on the server implementation, clients can use WebSockets (preferred)
or COMET (i.e. long-polling) requests to communicate with the signaling server.

For WebSockets, only the API described in this document is necessary. For COMET,
an extension to this API is required to identify a (virtual) connection between
multiple requests. The payload for COMET is the messages as described below.

See [Internal signaling API](internal-signaling.md) for the API of the regular PHP backend.


## Request

    {
      "id": "unique-request-id",
      "type": "the-request-type",
      "the-request-type": {
        ...object defining the request...
      }
    }

Example:

    {
      "id": "123-abc",
      "type": "samplemessage",
      "samplemessage": {
        "foo": "bar",
        "baz": 1234
      }
    }


## Response

    {
      "id": "unique-request-id-from-request-if-present",
      "type": "the-response-type",
      "the-response-type": {
        ...object defining the response...
      }
    }

Example:

    {
      "id": "123-abc",
      "type": "sampleresponse",
      "sampleresponse": {
        "hello": "world!"
      }
    }


## Errors

The server can send error messages as a response to any request the client has
sent.

Message format:

    {
      "id": "unique-request-id-from-request-if-present",
      "type": "error",
      "error": {
        "code": "the-internal-message-id",
        "message": "human-readable-error-message",
        "details": {
          ...optional additional details...
        }
      }
    }


## Backend requests

For some messages, the signaling server has to perform a request to the
Nextcloud backend (e.g. to validate the user authentication). The backend
must be able to verify the request to make sure it is coming from a valid
signaling server.

Also the Nextcloud backend can send requests to the signaling server to notify
about events related to a room or user (e.g. a user is no longer invited to
a room). Here the signaling server must be able to verify the request to check
if it is coming from a valid Nextcloud instance.

Therefore all backend requests, either from the signaling server or vice versa
must contain two additional HTTP headers:

- `Spreed-Signaling-Random`: Random string of at least 32 bytes.
- `Spreed-Signaling-Checksum`: SHA256-HMAC of the random string and the request
  body, calculated with a shared secret. The shared secret is configured on
  both sides, so the checksum can be verified.

### Example

- Request body: `{"type":"auth","auth":{"version":"1.0","params":{"hello":"world"}}}`
- Random: `afb6b872ab03e3376b31bf0af601067222ff7990335ca02d327071b73c0119c6`
- Shared secret: `MySecretValue`
- Calculated checksum: `3c4a69ff328299803ac2879614b707c807b4758cf19450755c60656cac46e3bc`


## Establish connection

This must be the first request by a newly connected client and is used to
authenticate the connection. No other messages can be sent without a successful
`hello` handshake.

Message format (Client -> Server):

    {
      "id": "unique-request-id",
      "type": "hello",
      "hello": {
        "version": "the-protocol-version-must-be-1.0",
        "auth": {
          "url": "the-url-to-the-auth-backend",
          "params": {
            ...object containing auth params...
          }
        }
      }
    }

Message format (Server -> Client):

    {
      "id": "unique-request-id-from-request",
      "type": "hello",
      "hello": {
        "sessionid": "the-unique-session-id",
        "resumeid": "the-unique-resume-id",
        "userid": "the-user-id-for-known-users",
        "version": "the-protocol-version-must-be-1.0",
        "server": {
          "features": ["optional", "list, "of", "feature", "ids"],
          ...additional information about the server...
        }
      }
    }


### Backend validation

The server validates the connection request against the passed auth backend
(needs to make sure the passed url / hostname is in a whitelist). It performs
a POST request and passes the provided `params` as JSON payload in the body
of the request.

Message format (Server -> Auth backend):

    {
      "type": "auth",
      "auth": {
        "version": "the-protocol-version-must-be-1.0",
        "params": {
          ...object containing auth params from hello request...
        }
      }
    }

If the auth params are valid, the backend returns information about the user
that is connecting (as JSON response).

Message format (Auth backend -> Server):

    {
      "type": "auth",
      "auth": {
        "version": "the-protocol-version-must-be-1.0",
        "userid": "the-user-id-for-known-users",
        "user": {
          ...additional data of the user...
        }
      }
    }

Anonymous connections that are not mapped to a user in Nextcloud will have an
empty or omitted `userid` field in the response. If the connection can not be
authorized, the backend returns an error and the hello request will be rejected.


### Error codes

- `unsupported-version`: The requested version is not supported.
- `auth-failed`: The session could not be authenticated.
- `too-many-sessions`: Too many sessions exist for this user id.
- `invalid_backend`: The requested backend URL is not supported.
- `invalid_client_type`: The [client type](#client-types) is not supported.
- `invalid_token`: The passed token is invalid (can happen for
  [client type `internal`](#client-type-internal)).


### Client types

In order to support clients with different functionality on the server, an
optional `type` can be specified in the `auth` struct when connecting to the
server. If no `type` is present, the default value `client` will be used and
a regular "user" client is created internally.

Message format (Client -> Server):

    {
      "id": "unique-request-id",
      "type": "hello",
      "hello": {
        "version": "the-protocol-version-must-be-1.0",
        "auth": {
          "type": "the-client-type",
          ...other attributes depending on the client type...
          "params": {
            ...object containing auth params...
          }
        }
      }
    }

The key `params` is required for all client types, other keys depend on the
`type` value.


#### Client type `client` (default)

For the client type `client` (which is the default if no `type` is given), the
URL to the backend server for this client must be given as described above.

This client type must be supported by all server implementations of the
signaling protocol.


#### Client type `internal`

"Internal" clients are used for connections from internal services where the
connection doesn't map to a user (or session) in Nextcloud.

These clients can skip some internal validations, e.g. they can join any room,
even if they have not been invited (which is not possible as the client doesn't
map to a user). This client type is not required to be supported by server
implementations of the signaling protocol, but some additional services might
not work without "internal" clients.

To authenticate the connection, the `params` struct must contain keys `random`
(containing any random string of at least 32 bytes) and `token` containing the
SHA-256 HMAC of `random` with a secret that is shared between the signaling
server and the service connecting to it.


## Resuming sessions

If a connection was interrupted for a client, the server may decide to keep the
session alive for a short time, so the client can reconnect and resume the
session.

In this case, no complete `hello` handshake is required and a client can use
a shorter `hello` request. On success, the session will resume as if no
interruption happened, i.e. the client will stay in his room and will get all
messages from the time the interruption happened.

Message format (Client -> Server):

    {
      "id": "unique-request-id",
      "type": "hello",
      "hello": {
        "version": "the-protocol-version-must-be-1.0",
        "resumeid": "the-resume-id-from-the-original-hello-response"
      }
    }

Message format (Server -> Client):

    {
      "id": "unique-request-id-from-request",
      "type": "hello",
      "hello": {
        "sessionid": "the-unique-session-id",
        "version": "the-protocol-version-must-be-1.0"
      }
    }

If the session is no longer valid (e.g. because the resume was too late), the
server will return an error and a normal `hello` handshake has to be performed.


### Error codes

- `no_such_session`: The session id is no longer valid.


## Releasing sessions

By default, the signaling server tries to maintain the session so clients can
resume it in case of intermittent connection problems.

To support cases where a client wants to close the connection and release all
session data, he can send a `bye` message so the server knows he doesn't need
to keep data for resuming.

Message format (Client -> Server):

    {
      "id": "unique-request-id",
      "type": "bye",
      "bye": {}
    }

Message format (Server -> Client):

    {
      "id": "unique-request-id-from-request",
      "type": "bye",
      "bye": {}
    }

After the `bye` has been confirmed, the session can no longer be used.


## Join room

After joining the room through the PHP backend, the room must be changed on the
signaling server, too.

Message format (Client -> Server):

    {
      "id": "unique-request-id",
      "type": "room",
      "room": {
        "roomid": "the-room-id",
        "sessionid": "the-nextcloud-session-id"
      }
    }

- The client can ask about joining a room using this request.
- The session id received from the PHP backend must be passed as `sessionid`.
- The `roomid` can be empty to leave the room.
- A session can only be connected to one room, i.e. joining a room will leave
  the room currently in.

Message format (Server -> Client):

    {
      "id": "unique-request-id-from-request",
      "type": "room",
      "room": {
        "roomid": "the-room-id",
        "properties": {
          ...additional room properties...
        }
      }
    }

- Sent to confirm a request from the client.
- The `roomid` will be empty if the client is no longer in a room.
- Can be sent without a request if the server moves a client to a room / out of
  the current room or the properties of a room change.


### Backend validation

Rooms are managed by the Nextcloud backend, so the signaling server has to
verify that a room exists and a user is allowed to join it.

Message format (Server -> Room backend):

    {
      "type": "room",
      "room": {
        "version": "the-protocol-version-must-be-1.0",
        "roomid": "the-room-id",
        "userid": "the-user-id-for-known-users",
        "sessionid": "the-nextcloud-session-id",
        "action": "join-or-leave"
      }
    }

The `userid` is empty or omitted for anonymous sessions that don't belong to a
user in Nextcloud.

Message format (Room backend -> Server):

    {
      "type": "room",
      "room": {
        "version": "the-protocol-version-must-be-1.0",
        "roomid": "the-room-id",
        "properties": {
          ...additional room properties...
        }
      }
    }

If the room does not exist or can not be joined by the given (or anonymous)
user, the backend returns an error and the room request will be rejected.


### Error codes

- `no_such_room`: The requested room does not exist or the user is not invited
  to the room.


## Leave room

To leave a room, a [join room](#join-room) message must be sent with an empty
`roomid` parameter.


## Room events

When users join or leave a room, the server generates events that are sent to
all sessions in that room. Such events are also sent to users joining a room
as initial list of users in the room. Multiple user joins/leaves can be batched
into one event to reduce the message overhead.

Message format (Server -> Client, user(s) joined):

    {
      "type": "event"
      "event": {
        "target": "room",
        "type": "join",
        "join": [
          ...list of session objects that joined the room...
        ]
      }
    }

Room event session object:

    {
      "sessionid": "the-unique-session-id",
      "userid": "the-user-id-for-known-users",
      "user": {
        ...additional data of the user as received from the auth backend...
      }
    }

Message format (Server -> Client, user(s) left):

    {
      "type": "event"
      "event": {
        "target": "room",
        "type": "leave",
        "leave": [
          ...list of session ids that left the room...
        ]
      }
    }

Message format (Server -> Client, user(s) changed):

    {
      "type": "event"
      "event": {
        "target": "room",
        "type": "change",
        "change": [
          ...list of sessions that have changed...
        ]
      }
    }


## Room list events

When users are invited to rooms or are disinvited from them, they get notified
so they can update the list of available rooms.

Message format (Server -> Client, invited to room):

    {
      "type": "event"
      "event": {
        "target": "roomlist",
        "type": "invite",
        "invite": [
          "roomid": "the-room-id",
          "properties": [
            ...additional room properties...
          ]
        ]
      }
    }

Message format (Server -> Client, disinvited from room):

    {
      "type": "event"
      "event": {
        "target": "roomlist",
        "type": "disinvite",
        "disinvite": [
          "roomid": "the-room-id"
        ]
      }
    }


Message format (Server -> Client, room updated):

    {
      "type": "event"
      "event": {
        "target": "roomlist",
        "type": "update",
        "update": [
          "roomid": "the-room-id",
          "properties": [
            ...additional room properties...
          ]
        ]
      }
    }


## Participants list events

When the list of participants or flags of a participant in a room changes, an
event is triggered by the server so clients can update their UI accordingly or
trigger actions like starting calls with other peers.

Message format (Server -> Client, participants change):

    {
      "type": "event"
      "event": {
        "target": "participants",
        "type": "update",
        "update": [
          "roomid": "the-room-id",
          "users": [
            ...list of changed participant objects...
          ]
        ]
      }
    }

If a participant has the `inCall` flag set, he has joined the call of the room
and a WebRTC peerconnection should be established if the local client is also
in the call.


## Room messages

The server can notify clients about events that happened in a room. Currently
such messages are only sent out when chat messages are posted to notify clients
they should load the new messages.

Message format (Server -> Client, chat messages available):

    {
      "type": "event"
      "event": {
        "target": "room",
        "type": "message",
        "message": {
          "roomid": "the-room-id",
          "data": {
            "type": "chat",
            "chat": {
              "refresh": true
            }
          }
        }
      }
    }


## Sending messages between clients

Messages between clients are sent realtime and not stored by the server, i.e.
they are only delivered if the recipient is currently connected. This also
applies to rooms, where only sessions currently in the room will receive the
messages, but not if they join at a later time.

Use this for establishing WebRTC connections between peers, i.e. sending offers,
answers and candidates.

Message format (Client -> Server, to other sessions):

    {
      "id": "unique-request-id",
      "type": "message",
      "message": {
        "recipient": {
          "type": "session",
          "sessionid": "the-session-id-to-send-to"
        },
        "data": {
          ...object containing the data to send...
        }
      }
    }

Message format (Client -> Server, to all sessions of a user):

    {
      "id": "unique-request-id",
      "type": "message",
      "message": {
        "recipient": {
          "type": "user",
          "userid": "the-user-id-to-send-to"
        },
        "data": {
          ...object containing the data to send...
        }
      }
    }

Message format (Client -> Server, to all sessions in the same room):

    {
      "id": "unique-request-id",
      "type": "message",
      "message": {
        "recipient": {
          "type": "room"
        },
        "data": {
          ...object containing the data to send...
        }
      }
    }

Message format (Server -> Client, receive message)

    {
      "type": "message",
      "message": {
        "sender": {
          "type": "the-type-when-sending",
          "sessionid": "the-session-id-of-the-sender",
          "userid": "the-user-id-of-the-sender"
        },
        "data": {
          ...object containing the data of the message...
        }
      }
    }

- The `userid` is omitted if a message was sent by an anonymous user.


# Internal signaling server API

The signaling server provides an internal API that can be called from Nextcloud
to trigger events from the server side.


## Rooms API

The base URL for the rooms API is `/api/vi/room/<roomid>`, all requests must be
sent as `POST` request with proper checksum headers as described above.


### New users invited to room

This can be used to notify users that they are now invited to a room.

Message format (Backend -> Server)

    {
      "type": "invite"
      "invite" {
        "userids": [
          ...list of user ids that are now invited to the room...
        ],
        "alluserids": [
          ...list of all user ids that invited to the room...
        ],
        "properties": [
          ...additional room properties...
        ]
      }
    }


### Users no longer invited to room

This can be used to notify users that they are no longer invited to a room.

Message format (Backend -> Server)

    {
      "type": "disinvite"
      "disinvite" {
        "userids": [
          ...list of user ids that are no longer invited to the room...
        ],
        "alluserids": [
          ...list of all user ids that still invited to the room...
        ]
      }
    }


### Room updated

This can be used to notify about changes to a room. The room properties are the
same as described in section "Join room" above.

Message format (Backend -> Server)

    {
      "type": "update"
      "update" {
        "userids": [
          ...list of user ids that are invited to the room...
        ],
        "properties": [
          ...additional room properties...
        ]
      }
    }


### Room deleted

This can be used to notify about a deleted room. All sessions currently
connected to the room will leave the room.

Message format (Backend -> Server)

    {
      "type": "delete"
      "delete" {
        "userids": [
          ...list of user ids that were invited to the room...
        ]
      }
    }


### Participants changed

This can be used to notify about changed participants.

Message format (Backend -> Server)

    {
      "type": "participants"
      "participants" {
        "changed": [
          ...list of users that were changed...
        ],
        "users": [
          ...list of users in the room...
        ]
      }
    }


### In call state of participants changed

This can be used to notify about participants that changed their `inCall` flag.

Message format (Backend -> Server)

    {
      "type": "incall"
      "incall" {
        "incall": new-incall-state,
        "changed": [
          ...list of users that were changed...
        ],
        "users": [
          ...list of users in the room...
        ]
      }
    }


### Send an arbitrary room message

This can be used to send arbitrary messages to participants in a room. It is
currently used to notify about new chat messages.

Message format (Backend -> Server)

    {
      "type": "message"
      "message" {
        "data": {
          ...arbitrary object to sent to clients...
        }
      }
    }