Skip to content

Latest commit

 

History

History
169 lines (121 loc) · 13.5 KB

File metadata and controls

169 lines (121 loc) · 13.5 KB

The Sendspin Protocol

Sendspin is a multi-room music experience protocol. The goal of the protocol is to orchestrate all devices that make up the music listening experience. This includes outputting audio on multiple speakers simultaneously, screens and lights visualizing the audio or album art, and wall tablets providing media controls.

Licensing and Trademarks

Sendspin is an open, royalty-free protocol that anyone may implement. This specification is licensed under the Community Specification License 1.0, which includes a royalty-free patent license from every contributor for implementations of the specification within its Scope. Contributions are accepted under the Contributor License Agreement.

Sendspin is a trademark of the Open Home Foundation. Implementing the protocol grants no right to use the Sendspin name or logo on commercial products; see TRADEMARKS.md.

THESE MATERIALS ARE PROVIDED “AS IS.” The Contributors and Licensees expressly disclaim any warranties (express, implied, or otherwise), including implied warranties of merchantability, non-infringement, fitness for a particular purpose, or title, related to the materials. The entire risk as to implementing or otherwise using the materials is assumed by the implementer and user. IN NO EVENT WILL THE CONTRIBUTORS OR LICENSEES BE LIABLE TO ANY OTHER PARTY FOR LOST PROFITS OR ANY FORM OF INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER FROM ANY CAUSES OF ACTION OF ANY KIND WITH RESPECT TO THIS DELIVERABLE OR ITS GOVERNING AGREEMENT, WHETHER BASED ON BREACH OF CONTRACT, TORT (INCLUDING NEGLIGENCE), OR OTHERWISE, AND WHETHER OR NOT THE OTHER MEMBER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

Normative Language

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in BCP 14 RFC 2119 RFC 8174 when, and only when, they appear in all capitals, as shown here.

Protocol overview

A typical session, from handshake through playback to disconnect:

sequenceDiagram
    participant Client
    participant Server

    Note over Client,Server: Noise handshake complete (see Communication)

    Server->>Client: server/hello (name)
    Client->>Server: client/hello (roles and capabilities)
    Server->>Client: server/activate (activities, active_roles)

    loop Continuous clock sync
        Client->>Server: client/time (client clock)
        Server->>Client: server/time (timing + offset info)
    end

    Note over Client,Server: Clock synchronization established
    Client->>Server: client/state (available: true, player: volume, muted)

    alt Stream starts
        Server->>Client: stream/start (codec, format details)
    end

    Server->>Client: group/update (playback_state, group_id, group_name)
    Server->>Client: server/state (metadata, controller, color)

    loop During playback
        alt Player role
            Server->>Client: binary Type 4 (audio chunks with timestamps)
        end
        alt Artwork role
            Server->>Client: binary Types 8-11 (artwork channels 0-3)
        end
        alt Visualizer role
            Server->>Client: binary Types 16-20 (loudness, beat, f_peak, spectrum, peak)
        end
    end

    alt Player changes preferred format
        Client->>Server: client/state (player: format)
        Server->>Client: stream/start (player: new format)
    end

    alt Seek operation
        Server->>Client: stream/clear (roles: [player, visualizer])
    end

    alt Track jump (skip to different track)
        Server->>Client: stream/clear (roles: [player, visualizer])
    end

    alt Controller role
        Client->>Server: client/command (controller: play/pause/seek/volume/switch/etc)
    end

    alt State changes
        Client->>Server: client/state (state and/or player changes)
    end

    alt Server commands player
        Server->>Client: server/command (player: volume, mute)
    end

    Server->>Client: stream/end (ends all role streams)

    alt Graceful disconnect
        Client->>Server: client/goodbye (reason)
        Note over Client,Server: Server initiates disconnect
    end
Loading

Definitions

  • Server - orchestrates all devices, generates audio streams, manages players and clients, provides metadata
  • Client - a device or application that can play audio, capture audio inputs, visualize audio, display metadata, display colors, or provide music controls. Has different possible roles (player, source, metadata, controller, artwork, visualizer, color). Every client has a unique identifier
    • Player - receives audio and plays it in sync. Has its own volume and mute state and preferred format settings
    • Source - captures audio from a local input and streams it to the server
    • Controller - controls the group this client is part of
    • Metadata - displays text metadata (title, artist, album, etc.)
    • Artwork - displays artwork images. Has preferred format for images
    • Visualizer - visualizes music. Has preferred format for audio features
    • Color - receives colors derived from the current audio
  • Group - a group of clients. Each client belongs to exactly one group, and every group has at least one client. Every group has a unique identifier. Each group has the following states: list of member clients, volume, mute, and playback state
  • Stream - client-specific details on how binary data is formatted and sent in either direction. Each role's stream is managed separately. For server-to-client streams, each client receives its own independently encoded stream based on its capabilities and preferences. For players, the server sends audio chunks as far ahead as the client's buffer capacity allows. For artwork clients, the server sends album artwork and other visual images through the stream
  • CSPRNG - a cryptographically secure pseudorandom number generator seeded with sufficient entropy (RFC 4086); a hardware RNG qualifies
  • Identity - a Curve25519 keypair used to identify a client or server in the Noise handshake. The base64url-encoded public key (43 characters, no padding) serves as the client_id or server_id. Persistent across reboots. The private key MUST be drawn from a CSPRNG.
  • long-term PSK - a 32-byte pre-shared symmetric secret established during pairing and mixed into the Noise handshake state for every subsequent connection. MUST be drawn from a CSPRNG.
  • pairing PSK - a 32-byte symmetric secret used as the PSK in the Pairing PSK method. It is always distributed alongside the client's static public key (client_id), which the server needs to verify the client identity. The operator enters it into the server as a pairing token, copied as text or scanned as a QR code. Distinct from the long-term PSK that pairing produces. MUST be drawn from a CSPRNG.
  • Pairing Code - a value used in code-based pairing methods. The static-pairing-code method uses a fixed 8-digit decimal value; the dynamic-pairing-code method uses a per-session generated value, emitted as a 6-digit decimal code or as a QR code (see Dynamic Pairing Code Flow).
  • Factory Reset - returns a device to its manufactured state: credentials and settings the manufacturer provisioned (identity keypair, pairing PSK, static pairing code, a calibrated output delay) are restored; everything accumulated since, pairing records included, is cleared.
  • Paired Session - a session keyed by a long-term PSK, meaning the client holds a pairing record for the server. Sessions keyed by the pairing PSK or the Sentinel PSK are unpaired, which limits the server to a pairing exchange, or to normal playback and control flows where unpaired access is enabled. Neither side sends this on the wire: both derive it from the PSK that matched during the Noise handshake.

Role Versioning

Roles define what capabilities and responsibilities a client has. All roles use explicit versioning with the @ character: <role>@<version> (e.g., player@v1, controller@v1).

This specification defines the following roles: player, source, controller, metadata, artwork, visualizer, color. All servers MUST implement all versions of these roles described in this specification.

All role names and versions not starting with _ are reserved for future revisions of this specification.

Priority and Activation

Clients list roles in supported_roles in priority order (most preferred first). If a client supports multiple versions of a role, all SHOULD be listed: ["player@v2", "player@v1"].

The server activates at most one version per role family (e.g., one player@vN, one controller@vN) - the first match it implements from the client's list, or none if server policy declines to activate that family. A server MUST NOT activate a role or version the client did not list in supported_roles. The server reports activated roles in active_roles; clients MUST consult the activation state established by the server/activate messages they have received and refrain from sending commands or state for roles that aren't active.

Message object keys (e.g., player?, controller?) use unversioned role names. The server determines the appropriate version from the client's active_roles.

Detecting Outdated Servers

Servers SHOULD track when clients request roles or role versions they don't implement (excluding those starting with _). This indicates the client supports newer role versions than the server and the server needs to be updated.

This mechanism only detects role-version skew, and only because roles are exchanged after the handshake. A newer core version, cipher suite, or handshake (a cipher or handshake change is itself a core version bump) makes the handshake abort before roles are exchanged, so that skew surfaces as a failed connection rather than through this role-request signal.

Application-Specific Roles

Custom roles outside the specification start with _ and MUST include an explicit version when advertised (e.g., _myapp_controller@v1, _custom_display@v1). To avoid collisions between independent vendors, custom role names SHOULD include a vendor-specific prefix (e.g., _vendorname_role).

Their binary message IDs come from the unmanaged 192-255 range: an application-specific role's own definition assigns its IDs, and a client MUST NOT advertise two roles with conflicting IDs.

Protocol evolution

This section describes how future revisions of this specification can add standard protocol features, roles, and role versions. It does not grant additional permissions to application-specific roles.

Core and role versions define behavior as well as message formats. If a new feature is not selected, existing behavior MUST remain unchanged. A change that breaks an existing role's contract requires a new role version. A core change that breaks existing behavior requires a new core version unless it can apply only when both peers explicitly opt in.

Future revisions of this specification MAY add optional information if older receivers can ignore it without changing the message's meaning. If a new feature needs the receiver to behave differently, the sender MUST confirm support through role activation or an explicitly defined capability negotiation before relying on that behavior. Matching core versions, the peer's software version, and the absence of an error do not confirm support. New features SHOULD use role support objects for role-specific capabilities.

A feature that changes connection-wide behavior, such as admission or ownership, MUST define how both peers opt in and what happens if they do not. It MUST NOT break the protocol rules that apply to peers on other connections that have not opted in. Support for a role MUST NOT be taken as support for a separate connection-wide feature unless that role version explicitly includes it.