Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/openlibrecommunity/olcrtc/llms.txt

Use this file to discover all available pages before exploring further.

Failover profiles let olcRTC automatically move between signaling providers and transports when the active tunnel becomes unavailable. Instead of crashing or stalling, the built-in supervisor cycles through an ordered list of profiles — each with its own provider, room, and transport — until a working path is found or the maximum number of cycles is exhausted. Use failover when you need resilience across multiple providers (e.g. WbStream as the primary, Jitsi as the fallback), or when a single provider’s availability is unpredictable.

How It Works

1

Apply top-level defaults

Fields defined at the top level of the YAML file (crypto, net.dns, liveness, data, etc.) become shared defaults for every profile.
2

Start the first profile

The supervisor applies the first entry in profiles[], overriding only the fields specified inside that profile. The session starts.
3

Detect failure

The liveness subsystem sends CONTROL_PING / CONTROL_PONG over the encrypted control stream. When liveness.failures consecutive pongs are missed, the current session is considered dead.
4

Retry delay

The supervisor waits failover.retry_delay before attempting the next profile.
5

Try the next profile

The supervisor applies the next profile in the list and starts a new session. If the last profile fails, the cycle count increments and the supervisor wraps back to the first profile.
6

Cycle limit

When failover.max_cycles full passes have completed the supervisor stops. Setting max_cycles: 0 makes cycling infinite.

Failover-specific YAML Fields

Profile fields (profiles[])

Each entry in profiles can override any of the following fields from the shared defaults:
FieldDescription
profiles[].nameHuman-readable label for the profile (used in logs)
profiles[].authProvider selection — overrides top-level auth
profiles[].roomRoom ID / channel — overrides top-level room
profiles[].netTransport and DNS — overrides top-level net
profiles[].engineDirect engine settings — overrides top-level engine
profiles[].cryptoEncryption key — overrides top-level crypto
profiles[].socksSOCKS5 settings — overrides top-level socks
profiles[].vp8VP8 transport tuning — overrides top-level vp8
profiles[].seiSEI transport tuning — overrides top-level sei
profiles[].videoVideo transport tuning — overrides top-level video
profiles[].livenessLiveness tuning for this profile specifically
profiles[].lifecycleLifecycle rotation for this profile specifically
profiles[].trafficTraffic shaping for this profile specifically

Supervisor fields (failover)

FieldDescription
failover.retry_delayPause between profile switch attempts, e.g. 2s
failover.max_cyclesMaximum full passes over the profile list; 0 = infinite

Full Failover Example

# failover.yaml
# Use the same profile order on both server and client.

mode: srv   # srv on the server side, cnc on the client side

# Shared defaults — applied to every profile unless overridden.
crypto:
  key_file: "./olcrtc.key"   # path to a file holding the 32-byte hex key

net:
  dns: "8.8.8.8:53"

liveness:
  interval: 10s   # how often to send liveness checks
  timeout: 5s     # wait time for a liveness reply
  failures: 3     # consecutive failures before the link is considered dead

# Optional scheduled rebuild for each active profile.
# lifecycle:
#   max_session_duration: 6h

# Optional limit / pacing for encrypted wire messages.
# traffic:
#   max_payload_size: 4096
#   min_delay: 5ms
#   max_delay: 30ms

data: data

profiles:
  - name: wb-vp8              # primary: WbStream + VP8
    auth:
      provider: wbstream
    room:
      id: "REPLACE_WITH_WB_ROOM_ID"
    net:
      transport: vp8channel

  - name: jitsi-datachannel   # fallback: Jitsi + datachannel
    auth:
      provider: jitsi
    room:
      id: "https://meet.example.org/REPLACE_WITH_ROOM_NAME"
    net:
      transport: datachannel

failover:
  retry_delay: 2s   # wait between profile switch attempts
  max_cycles: 0     # 0 = cycle indefinitely

Supervisor Behaviour

Profile order is decisive. The supervisor always starts at profile index 0. When the active profile’s session fails (liveness timeout exhausted), the supervisor waits retry_delay, then moves to the next profile by index. After the last profile fails, the full-cycle counter increments and the supervisor wraps back to profile 0. Infinite cycling. Setting failover.max_cycles: 0 means the supervisor never stops on its own. It keeps rotating through profiles until the process is terminated or a profile establishes a stable session. Liveness vs. profile switch. A single missed pong does not immediately trigger a profile switch. The supervisor only advances to the next profile after the liveness subsystem has exhausted its failures threshold and a reconnect attempt on the current profile also fails. Transient packet loss within the threshold is recovered on the same profile. Lifecycle rotation with failover. When lifecycle.max_session_duration is set inside a profile (or as a shared default), a planned session rebuild at the end of that duration still re-uses the same profile — it is not a profile switch. The supervisor advances to the next profile only on failure.
Active smux streams do not migrate between profiles. When the supervisor switches from one profile to another, all in-flight TCP connections multiplexed over the old smux session are dropped. Applications that hold open connections through the SOCKS5 proxy will need to reconnect. New connections will be established on the newly active profile.
The profile order must match on both server and client. The server and client independently run the same supervisor logic and must agree on which profile is active at any given time. If the profile lists differ between peers, the two sides will be in different provider/transport states and the session will not establish. Always deploy identical profiles[] lists to both sides, changing only mode: srv vs mode: cnc.

Liveness Interaction

Failover relies entirely on the liveness subsystem to detect dead tunnels — it does not use the WebRTC ICE or DTLS connection state. This means:
  • A PeerConnection that is formally connected but routing no data will be detected by CONTROL_PING / CONTROL_PONG timeouts.
  • Liveness checks run over the encrypted smux control stream, so only an end-to-end working path passes them.
  • Adjust liveness.failures and liveness.timeout per-profile if different providers have different latency characteristics.
profiles:
  - name: high-latency-provider
    auth:
      provider: telemost
    room:
      id: "<room-id>"
    net:
      transport: vp8channel
    liveness:
      interval: 15s     # give more time between checks
      timeout: 10s      # provider has higher RTT
      failures: 5       # tolerate more misses before giving up

Build docs developers (and LLMs) love