server

package
v0.2.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 2, 2026 License: BSD-3-Clause Imports: 23 Imported by: 0

Documentation

Overview

Package server is a client for the Olvid server REST API (spec §42–50).

The wire format is uniform (Olvid ServerMethod): every call is an HTTP POST to `<serverURL><path>` with `Content-Type: application/bytes` and an `Olvid-API-Version` header. The request body is an encoded list of the input values; a 200 response body is an encoded list whose first element is a one-byte status and whose remaining elements are the method's output values.

Index

Constants

View Source
const (
	StatusOK                  = 0x00
	StatusInvalidSession      = 0x04 // token invalid — re-authenticate
	StatusDeletedFromServer   = 0x09
	StatusDeviceNotRegistered = 0x0b
	StatusListTruncated       = 0x17
	StatusPayloadTooLarge     = 0x18

	// Client-side (no valid server status was obtained).
	StatusConnectionError = -1
)

Return statuses (Olvid ServerMethod / per-endpoint). Values ≥ 0 come from the server; negative values are produced locally by the client.

View Source
const (
	UserAgentAndroid = "Dalvik/2.1.0 (Linux; U; Android 14; Pixel 7 Build/AP1A.240405.002)"
	UserAgentIOS     = "Olvid/1580 CFNetwork/1498.700.2 Darwin/23.6.0"
)

Well-known User-Agent strings that mimic the official Olvid apps' outbound requests (to both the Olvid servers and S3). The apps don't send a bespoke "Olvid" UA on the main API: Android sends the platform default http.agent (a generic Dalvik/Android string that blends in with ordinary Android traffic); iOS sends the URLSession default (Olvid/<build> CFNetwork/<ver> Darwin/<ver>, which does name the app). Neither the Olvid servers nor the AWS Lambdas validate the User-Agent, so this is purely about not standing out as a distinct third-party client. Override with WithUserAgent for a specific device/OS version.

View Source
const (
	StatusGroupUIDAlreadyUsed = 0x12 // /groupBlobCreate: group identifier already exists
	StatusGroupLocked         = 0x13 // group is (already) locked for an update
	StatusGroupNotLocked      = 0x15 // /groupBlobUpdate: group is not locked or lock expired
)

Additional group-specific return statuses (spec §47).

View Source
const (
	PushTypeAndroidFCM       = 0x01
	PushTypeIOSWithExt       = 0x05
	PushTypeWebSocketAndroid = 0x10
	PushTypeWindows          = 0x11
	PushTypeLinux            = 0x12
	PushTypeDaemon           = 0x13
)

Push-notification type identifiers (spec §46.1). The WebSocket-only types carry no extra info.

View Source
const (
	DeviceMgmtRename         = 0x00
	DeviceMgmtDeactivate     = 0x01
	DeviceMgmtSetNonExpiring = 0x02
)

Device-management request types (spec §46.4).

View Source
const (
	WSActionRegister            = "register"
	WSActionMessage             = "message"
	WSActionOwnedDevices        = "ownedDevices"
	WSActionKeycloak            = "keycloak"
	WSActionPushTopic           = "push topic"
	WSActionReturnReceipt       = "return receipt"
	WSActionDeleteReturnReceipt = "delete return receipt"
)

WebSocket actions.

View Source
const (
	WSErrInvalidToken = 0x04
	WSErrUnknown      = 0xff
)

WSError codes (spec §50.1).

View Source
const APIVersion = 21

SERVER_API_VERSION advertised by this client (Olvid Constants.SERVER_API_VERSION).

View Source
const StatusCallPermissionDenied = 0x0e

StatusCallPermissionDenied is returned by GetTurnCredentials (spec §49.2) when the licence does not permit initiating calls.

View Source
const StatusExtendedContentNotFound = 0x11

StatusExtendedContentNotFound is returned by DownloadExtendedPayload (spec §45.2) when the message has no extended payload on the server.

View Source
const StatusInvalidSignature = 0x14

StatusInvalidSignature is returned by /uploadPreKey (spec §49.3) when the pre-key signature is invalid, targets another device, or is stale.

View Source
const TransferServerURL = "wss://transfer.olvid.io"

TransferServerURL is the default relay endpoint.

Variables

This section is empty.

Functions

func DecodeRequest

func DecodeRequest(body []byte) ([][]byte, error)

DecodeRequest parses a server-style request body into its list of encoded inputs. Exported for test servers and mock implementations.

func DescribeAttachmentDescriptor

func DescribeAttachmentDescriptor(encoded []byte) string

DescribeAttachmentDescriptor decodes an attachment descriptor and renders each element's shape (int / list / bytes) for diagnosing a wire-format mismatch. Diagnostic only.

func EncodeResponse

func EncodeResponse(status byte, outputs ...[]byte) []byte

EncodeResponse builds a server-style response body: encodeList(status, outputs). It is exported for use by test servers and mock implementations.

Types

type AttachmentDownloadInfo

type AttachmentDownloadInfo struct {
	Number          int64
	EncryptedLength int64
	ChunkLength     int64
	DownloadURLs    []string // one signed S3 URL per chunk
}

AttachmentDownloadInfo describes one attachment listed for download (spec §45.1).

func ParseAttachmentDownloadInfo

func ParseAttachmentDownloadInfo(encoded []byte) (*AttachmentDownloadInfo, error)

ParseAttachmentDownloadInfo decodes an attachment descriptor from a downloaded message (spec §45.1). Two layouts are accepted (index of the length/chunk/URL fields shifts):

5 elements (real server): [number, metadataFlag(1 byte), encryptedLength, chunkLength, urls]
4 elements (legacy):      [number, encryptedLength, chunkLength, urls]

The extra element at index 1 in the 5-field form is a short per-attachment flag we don't need for downloading (only the lengths and the signed URLs matter).

type AttachmentUploadInfo

type AttachmentUploadInfo struct {
	EncryptedLength int64 // total encrypted attachment length
	ChunkLength     int64 // encrypted chunk length
}

AttachmentUploadInfo declares one attachment at message-upload time (spec §44.1, detail 4).

type Client

type Client struct {
	HTTP      *http.Client
	UserAgent string

	// AllowInsecureAttachmentURLs disables the SSRF guard (https + public-IP) on server-supplied
	// attachment chunk URLs. It exists ONLY for tests/dev against a loopback mock; leave it false in
	// production, where the URLs come from the untrusted Olvid server.
	AllowInsecureAttachmentURLs bool
	// contains filtered or unexported fields
}

Client talks to Olvid servers. The zero value is not usable; use New. serverURL arguments include the scheme (https:// in production; http:// works for tests against a local mock).

func New

func New(opts ...Option) *Client

New returns a Client with sensible defaults, applying any options.

func (*Client) Authenticate

func (c *Client) Authenticate(serverURL string, owned *engine.OwnedCryptoIdentity, rnd prng.PRNG) (token []byte, err error)

Authenticate performs the full server-authentication handshake (spec §42): request a challenge, solve it with the owned identity's authentication key (prefix "authentChallenge", reusing the vector-validated authentication scheme), and obtain a session token.

func (*Client) Call

func (c *Client) Call(serverURL, path string, inputs [][]byte) (status Status, outputs [][]byte, err error)

Call performs an arbitrary server method by path, POSTing the given already- encoded inputs and returning the status and the method's encoded outputs. Use this for endpoints not covered by a typed method (spec §43, §46–52).

func (*Client) CreateGroupBlob

func (c *Client) CreateGroupBlob(serverURL string, identity, token, groupUID, encodedAdminPublicKey, encryptedBlob []byte) (status Status, err error)

CreateGroupBlob uploads the first version of a group blob (spec §47.1): POST /groupBlobCreate [identity, token, groupUid, encodedAdminPublicKey, encryptedBlob]. StatusGroupUIDAlreadyUsed (0x12) means the groupUid is taken; StatusInvalidSession (0x04) means the token must be refreshed.

func (*Client) DeactivateDevice

func (c *Client) DeactivateDevice(serverURL string, identity, token, deviceUID []byte) (Status, error)

DeactivateDevice deactivates a device (spec §46.4, request type 0x01).

func (*Client) DeleteGroupBlob

func (c *Client) DeleteGroupBlob(serverURL string, groupUID, signature []byte) (status Status, err error)

DeleteGroupBlob removes a group and all its traces (spec §47.6): POST /groupBlobDelete [groupUid, signature]. The signature is engine.SignGroupDelete (over an empty challenge). A deleted group also returns StatusOK.

func (*Client) DeleteMessages

func (c *Client) DeleteMessages(serverURL string, identity, token, deviceUID []byte, uids []UIDToDelete) (status Status, err error)

DeleteMessages deletes (or marks as listed) messages on the server (spec §45.4): POST /deleteMessageAndAttachments [identity, token, deviceUid, [uid, bool, ...]].

func (*Client) DeviceDiscovery

func (c *Client) DeviceDiscovery(serverURL string, identity []byte) (status Status, devices []DiscoveredDevice, serverTimestamp int64, recentlyOnline bool, err error)

DeviceDiscovery lists a contact's active devices and their pre-keys (spec §46.2): POST /deviceDiscovery [identity] -> encoded dict {dev, st, ro}. This endpoint is unauthenticated. recentlyOnline is the "ro" flag.

func (*Client) DialTransfer

func (c *Client) DialTransfer(wsURL string) (*TransferClient, error)

DialTransfer opens a transfer-relay WebSocket (pass TransferServerURL, or an override for tests).

func (*Client) DialWebSocket

func (c *Client) DialWebSocket(wsURL string) (*WebSocket, error)

DialWebSocket opens a WebSocket to the given ws:// or wss:// URL.

func (*Client) DownloadExtendedPayload

func (c *Client) DownloadExtendedPayload(serverURL string, identity, token, messageUID []byte) (status Status, encrypted []byte, err error)

DownloadExtendedPayload fetches a message's encrypted extended payload (spec §45.2): POST /downloadMessageExtendedContent [identity, token, messageUid] -> encrypted extended content. Decrypt it with the key from engine.ExtendedPayloadKeyForOwner.

func (*Client) DownloadMessages

func (c *Client) DownloadMessages(serverURL string, identity, token, deviceUID []byte, listStartTimestamp int64) (status Status, serverTimestamp int64, msgs []DownloadedMessage, err error)

DownloadMessages lists the messages available for a device (spec §45.1): POST /downloadMessagesAndListAttachments [identity, token, deviceUid, listStartTimestamp] -> serverTimestamp, [messages...]. The returned status is StatusListTruncated when the server truncated the list.

func (*Client) GetAttachmentChunk

func (c *Client) GetAttachmentChunk(signedURL string) ([]byte, error)

GetAttachmentChunk downloads one encrypted chunk from its signed S3 URL (spec §45.1). It returns the raw ciphertext.

func (*Client) GetGroupBlob

func (c *Client) GetGroupBlob(serverURL string, groupUID []byte) (status Status, blob *GroupBlobDownload, err error)

GetGroupBlob downloads a group blob (spec §47.2): POST /groupBlobGet [groupUid] -> [encryptedBlob, logItems, adminPublicKey, updateTimestamp]. Unauthenticated. StatusDeletedFromServer (0x09) means the group is gone; StatusGroupLocked (0x13) means retry later.

func (*Client) GetToken

func (c *Client) GetToken(serverURL string, identity, response, nonce []byte) (status Status, token []byte, apiKeyStatus int64, err error)

GetToken exchanges a solved challenge for a session token (spec §42.2): POST /getToken [identity, response, nonce] -> token, serverNonce, apiKeyStatus, permissions, expiration.

func (*Client) GetTurnCredentials

func (c *Client) GetTurnCredentials(serverURL string, identity, token []byte, username1, username2 string) (status Status, creds *TurnCredentials, err error)

GetTurnCredentials retrieves TURN credentials to initiate a call (spec §49.2): POST /getTurnCredentials [identity, token, username1, username2] -> [timestampedUsername1, password1, timestampedUsername2, password2]. StatusCallPermissionDenied (0x0e) means the licence lacks the call permission.

func (*Client) LockGroupBlob

func (c *Client) LockGroupBlob(serverURL string, groupUID, lockNonce, signature []byte) (status Status, blob *GroupBlobDownload, err error)

LockGroupBlob acquires the update lock for 30 seconds (spec §47.3): POST /groupBlobLock [groupUid, lockNonce, signature] -> same payload as GetGroupBlob. The signature is engine.SignGroupLock(lockNonce, adminKey).

func (*Client) OwnedDeviceDiscovery

func (c *Client) OwnedDeviceDiscovery(serverURL string, owned *engine.OwnedCryptoIdentity) (status Status, encodedDict []byte, err error)

OwnedDeviceDiscovery lists one's own devices (spec §46.3): POST /ownedDeviceDiscovery [identity] -> ciphertext. The response is encrypted to the identity's KEM public key (so it needs no token); it is decrypted here with the owned KEM private key. Returns the raw decrypted encoded dictionary ({dev, st, multi}); callers can decode it further as needed.

func (*Client) OwnedDevices

func (c *Client) OwnedDevices(serverURL string, owned *engine.OwnedCryptoIdentity) (status Status, deviceUIDs [][]byte, serverTimestamp int64, err error)

OwnedDevices returns the UIDs of the identity's own devices (spec §46.3). It decrypts the OwnedDeviceDiscovery blob and decodes the same {st, dev:[{uid,…}]} dictionary the contact discovery uses (the reference's owned-device-discovery payload; exp/reg/name/prk per device are ignored here — only the UIDs are needed to establish own-device channels).

func (*Client) PutAttachmentChunk

func (c *Client) PutAttachmentChunk(signedURL string, encryptedChunk []byte) error

PutAttachmentChunk uploads one encrypted chunk to its signed S3 URL (spec §44.1, detail 6). This is a plain HTTP PUT of the raw ciphertext to AWS S3, not an Olvid server entry point.

func (*Client) PutGroupLog

func (c *Client) PutGroupLog(serverURL string, groupUID, signature []byte) (status Status, err error)

PutGroupLog uploads a leave-proof log item (spec §47.5): POST /groupLogPut [groupUid, signature]. Unauthenticated; the server only checks the length. The signature is engine.SignGroupLeaveLog.

func (*Client) RegisterPushNotification

func (c *Client) RegisterPushNotification(serverURL string, identity, token, deviceUID []byte, reg PushRegistration) (status Status, err error)

RegisterPushNotification registers a device so the server delivers it new-message notifications (spec §46.1). Registration is also what makes a device visible to device discovery (§46.2).

func (*Client) RenameDevice

func (c *Client) RenameDevice(serverURL string, identity, token, deviceUID, encryptedName []byte) (Status, error)

RenameDevice sets a device's encrypted name (spec §46.4, request type 0x00).

func (*Client) RequestChallenge

func (c *Client) RequestChallenge(serverURL string, identity, nonce []byte) (status Status, challenge, serverNonce []byte, err error)

RequestChallenge requests an authentication challenge (spec §42.1): POST /requestChallenge [identity, nonce] -> challenge, serverNonce.

func (*Client) SetNonExpiringDevice

func (c *Client) SetNonExpiringDevice(serverURL string, identity, token, deviceUID []byte) (Status, error)

SetNonExpiringDevice marks a device as non-expiring (spec §46.4, request type 0x02).

func (*Client) UpdateGroupBlob

func (c *Client) UpdateGroupBlob(serverURL string, groupUID, lockNonce, encryptedBlob, encodedAdminPublicKey, signature []byte) (status Status, err error)

UpdateGroupBlob overwrites a locked group blob (spec §47.4): POST /groupBlobUpdate [groupUid, lockNonce, encryptedBlob, encodedAdminPublicKey, signature]. The signature is engine.SignGroupUpdate over lockNonce||encryptedBlob||encodedAdminPublicKey.

func (*Client) UploadMessage

func (c *Client) UploadMessage(serverURL string, headers []Header, encryptedPayload, extendedPayload []byte, isApplication, isVoip bool) (status Status, res *UploadResult, err error)

UploadMessage uploads a message with its per-device headers (spec §44.1): POST /uploadMessageAndGetUids. This implements the no-attachment case, with an optional extended payload. Returns the server-assigned UID and nonce.

func (*Client) UploadMessageWithAttachments

func (c *Client) UploadMessageWithAttachments(serverURL string, headers []Header, encryptedPayload, extendedPayload []byte, isApplication, isVoip bool, attachments []AttachmentUploadInfo) (status Status, res *UploadResult, uploadURLs [][]string, err error)

UploadMessageWithAttachments uploads a message declaring its attachments (spec §44.1) and returns, alongside the message result, the per-attachment lists of signed S3 upload URLs (one URL per chunk). With no attachments it behaves like UploadMessage.

func (*Client) UploadPreKey

func (c *Client) UploadPreKey(serverURL string, identity, token, deviceUID, encodedSignedPreKey []byte) (status Status, err error)

UploadPreKey creates or updates the pre-key the server stores for a device (spec §49.3): POST /uploadPreKey [identity, token, deviceUid, encodedSignedPreKey]. Build encodedSignedPreKey with engine.GeneratePreKey. The server verifies the signature; StatusInvalidSignature (0x14) means the signature is invalid, targets another device, or is stale.

func (*Client) UploadReturnReceipts

func (c *Client) UploadReturnReceipts(serverURL string, receipts []ReturnReceiptToUpload) (status Status, err error)

UploadReturnReceipts uploads a batch of return receipts (spec §49.1): POST /uploadReturnReceipt with a list of [identity, [deviceUids], nonce, encryptedPayload] entries. Delivery to the sender happens over the WebSocket.

func (*Client) WellKnown

func (c *Client) WellKnown(serverURL string) (*WellKnown, error)

WellKnown fetches and parses the server configuration.

func (*Client) WithContext

func (c *Client) WithContext(ctx context.Context) *Client

WithContext returns a shallow copy of the client whose requests are bound to ctx — cancelling ctx aborts in-flight and future requests made through the returned client (like http.Request.WithContext). The underlying *http.Client is shared.

type DiscoveredDevice

type DiscoveredDevice struct {
	DeviceUID    []byte
	SignedPreKey []byte
}

DiscoveredDevice is one device returned by DeviceDiscovery (spec §46.2). SignedPreKey is the raw encoded signed pre-key (empty if the device published none); verify and parse it with engine.ParseSignedPreKey using the contact's authentication key.

type DownloadedMessage

type DownloadedMessage struct {
	MessageUID       []byte
	Timestamp        int64
	HeaderPayload    []byte // the wrapped message key for this device
	EncryptedPayload []byte
	HasExtended      bool
	Attachments      [][]byte // raw encoded attachment descriptors
}

DownloadedMessage is one message returned by DownloadMessages (spec §45.1). Attachment details are exposed as raw encoded lists (not decoded further).

type GroupBlobDownload

type GroupBlobDownload struct {
	EncryptedBlob   []byte
	LogItems        [][]byte
	AdminPublicKey  []byte
	UpdateTimestamp int64
}

GroupBlobDownload is the payload returned by GetGroupBlob and LockGroupBlob (spec §47.2/§47.3). AdminPublicKey is the raw encoded group administration public key (decode with engine.DecodeGroupAdminPublicKey). LogItems are the raw signed leave-proofs to consolidate into the blob after download.

type Header struct {
	DeviceUID         []byte
	WrappedKey        []byte // the channel-specific header payload (wrapped message key)
	RecipientIdentity []byte
}

Header is a per-recipient-device message header (spec §44.1): the wrapped message key destined to one device.

type Option

type Option func(*Client)

Option configures a Client (functional-options pattern).

func WithHTTPClient

func WithHTTPClient(h *http.Client) Option

WithHTTPClient sets the underlying *http.Client (e.g. custom transport/proxy).

func WithTimeout

func WithTimeout(d time.Duration) Option

WithTimeout sets the per-request timeout on the default HTTP client.

func WithUserAgent

func WithUserAgent(ua string) Option

WithUserAgent sets the User-Agent header.

type PushRegistration

type PushRegistration struct {
	PushType            byte
	ExtraInfo           [][]byte // encoded extra-info elements (empty for WebSocket-only)
	Reactivate          bool
	KeycloakTopics      []string
	EncryptedDeviceName []byte // for the first registration; may be empty
	DeviceUIDToReplace  []byte // optional
}

PushRegistration holds the parameters of a push-notification registration (spec §46.1). For a WebSocket-only device use PushTypeLinux/Daemon/… with an empty ExtraInfo.

type ReturnReceipt

type ReturnReceipt struct {
	ServerUID        []byte
	Nonce            []byte
	EncryptedPayload []byte
	Timestamp        int64
}

ReturnReceipt is a return-receipt notification (spec §50.6). The encrypted payload decrypts (with the AuthEncKey identified by Nonce) to an encoded list of [senderIdentity, status] — see engine.ParseReturnReceiptPayload.

type ReturnReceiptToUpload

type ReturnReceiptToUpload struct {
	RecipientIdentity []byte   // identity of the message sender (= return receipt recipient)
	DeviceUIDs        [][]byte // the sender's devices to notify
	Nonce             []byte   // 16-byte nonce from the original application message
	EncryptedPayload  []byte   // engine.EncryptReturnReceipt output
}

ReturnReceiptToUpload is one return receipt to upload (spec §49.1).

type Status

type Status int

Status is a server return status.

func (Status) String

func (s Status) String() string

type TransferClient

type TransferClient struct {
	// contains filtered or unexported fields
}

TransferClient is an open connection to the transfer relay.

func (*TransferClient) Close

func (t *TransferClient) Close() error

Close closes the transfer connection.

func (*TransferClient) JoinTarget

func (t *TransferClient) JoinTarget(sessionNumber string, firstPayload []byte) error

JoinTarget joins an existing session by its number, sending the first payload (the target's ephemeral identity). Subsequent messages arrive via Recv.

func (*TransferClient) OpenSource

func (t *TransferClient) OpenSource() (sessionNumber string, connID string, err error)

OpenSource starts a source session and returns the 8-digit session number the user reads to the joining device, plus this source's relay connection id.

func (*TransferClient) Recv

func (t *TransferClient) Recv() (otherConnID string, payload []byte, err error)

Recv reads the next relayed message, reassembling fragments, and returns the peer's connection id and the payload bytes. It blocks until a complete message arrives or the connection closes.

func (*TransferClient) Relay

func (t *TransferClient) Relay(otherConnID string, payload []byte, noResponseExpected bool) error

Relay sends a payload to the peer identified by otherConnID, fragmenting if it exceeds the relay size limit. noResponseExpected mirrors the reference flag (informational; the relay ignores it for delivery). The caller learns otherConnID from OpenSource's peer or from Recv.

type TurnCredentials

type TurnCredentials struct {
	Username1 string
	Password1 string
	Username2 string
	Password2 string
}

TurnCredentials are the two timestamped username/password pairs returned by GetTurnCredentials — pair 1 for the caller, pair 2 for the recipient.

type UIDToDelete

type UIDToDelete struct {
	UID          []byte
	MarkAsListed bool
}

UIDToDelete pairs a message UID with the mark-as-listed flag.

type UploadResult

type UploadResult struct {
	MessageUID []byte
	Nonce      []byte // needed to manage attachment uploads
	Timestamp  int64  // server timestamp (ms since epoch)
	SignedURLs [][]byte
}

UploadResult is the outcome of a successful message upload (spec §44.1).

type WSEvent

type WSEvent struct {
	Action        string
	Identity      []byte         // register/message/ownedDevices/keycloak/return receipt
	Message       []byte         // "message": the (attachment-less) message payload, may be nil
	Topic         string         // "push topic"
	ReturnReceipt *ReturnReceipt // "return receipt"
	Err           int            // "register": non-zero on failure (see WSErr*)
	HasErr        bool
}

WSEvent is a decoded server→device WebSocket message (spec §50.2–50.6).

type WebSocket

type WebSocket struct {
	// contains filtered or unexported fields
}

WebSocket is an established Olvid WebSocket connection.

func (*WebSocket) Close

func (w *WebSocket) Close() error

Close closes the WebSocket.

func (*WebSocket) DeleteReturnReceipt

func (w *WebSocket) DeleteReturnReceipt(serverUID []byte) error

DeleteReturnReceipt tells the server to delete a received return receipt (spec §50.7).

func (*WebSocket) Read

func (w *WebSocket) Read() (*WSEvent, error)

Read returns the next server→device event. It blocks until a message arrives or the connection closes (io.EOF).

func (*WebSocket) Register

func (w *WebSocket) Register(identity, token, deviceUID []byte) error

Register binds an (identity, deviceUid) to this WebSocket (spec §50.1). The server replies with a register WSEvent (Err set on failure).

type WellKnown

type WellKnown struct {
	WSServer       string   `json:"ws_server"`
	TurnServers    []string `json:"turn_servers"`
	AltTurnServers []string `json:"alt_turn_servers"`
	OSMServer      string   `json:"osm_server"`
	AddressServer  string   `json:"address_server"`
}

WellKnown is the server's published configuration, fetched from `<serverURL>/.well-known/server-config.json`. It tells clients where the WebSocket and TURN servers live (the WebSocket is on a different host than the REST API).

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL