Documentation

Connection and Lifecycle

Fetch credentials, connect a channel, observe recovery, and release resources.

Before you start: add github.com/useceleris/sdk-go-client from the overview. Prepare an endpoint that signs credentials, as Authentication describes. A trusted backend can sign its own credentials instead. See Server-side usage.

1. Fetch credentials

The SDK has a built-in production endpoint. Supply a credential provider that calls your application's credential endpoint:

package main

import (
    "bytes"
    "context"
    "encoding/json"
    "fmt"
    "log"
    "net/http"

    celeris "github.com/useceleris/sdk-go-client"
)

func fetchCredentials(ctx context.Context, request celeris.CredentialRequest) (celeris.Credentials, error) {
    body, err := json.Marshal(map[string]any{
        "channelReference": request.ChannelReference,
        "replayLookbackMs": request.ReplayLookback.Milliseconds(),
    })

    if err != nil {
        return celeris.Credentials{}, err
    }

    httpRequest, err := http.NewRequestWithContext(ctx, http.MethodPost, "https://your-app.example/api/realtime-credentials", bytes.NewReader(body))

    if err != nil {
        return celeris.Credentials{}, err
    }

    httpRequest.Header.Set("Authorization", "Bearer ...")
    httpRequest.Header.Set("Content-Type", "application/json")
    response, err := http.DefaultClient.Do(httpRequest)

    if err != nil {
        return celeris.Credentials{}, err
    }

    defer response.Body.Close()

    if response.StatusCode != http.StatusOK {
        return celeris.Credentials{}, fmt.Errorf("credential endpoint answered %s", response.Status)
    }

    var credentials celeris.Credentials
    err = json.NewDecoder(response.Body).Decode(&credentials)

    return credentials, err
}

func main() {
    client, err := celeris.NewClient(celeris.ClientOptions{CredentialProvider: fetchCredentials})

    if err != nil {
        log.Fatal(err)
    }

    channel, err := client.Channel("demo-room")

    if err != nil {
        log.Fatal(err)
    }

    defer channel.Close()

    // The steps below continue here.
}

The provider receives a CredentialRequest with ChannelReference and Reconnect. Reconnect is false for the attempt that Connect starts and true for automatic recovery. On recovery, the request also carries DisconnectedAt, a time.Time value, and ReplayLookback, a time.Duration value. Both are zero on the first attempt. Honor ctx: the attempt's deadline and Close both cancel it. The SDK discards a result that arrives after that cancellation.

celeris.Credentials has the JSON tags payload and signature, so it decodes your endpoint's response directly. It prints as redacted through fmt and slog. Return both values exactly as your backend signed them. Do not generate, cache, or inspect them. If the provider returns an error or panics, the SDK reports a safe ErrTransport. The SDK does not forward the original failure details.

When you create a client, a channel, or a segment, the SDK does not open a connection. Use BaseURL only for local or self-hosted Celeris, not your application's credential endpoint. See the Go client API for the options.

2. Observe state, then connect

events := channel.Events()
stopState := events.OnStateChange(func(state celeris.ChannelState) {
    fmt.Println("Connection:", state)
})

stopErrors := events.OnError(func(err error) {
    fmt.Println("Error:", err)
})

ctx := context.Background()

if err := channel.Connect(ctx); err != nil {
    fmt.Println("Initial connection failed:", err)
}

The expected states are connecting, then connected. A failed initial attempt returns its error from Connect and sets failed. The SDK does not also report that failure to OnError, and it does not retry the attempt automatically.

Once connected, the channel is automatically a member of default. Use channel.DefaultSegment() to access that stream. Do not open another connection or call Subscribe() just to join it. See default-segment messaging.

StateWhat your program must do
StateIdleCall Connect when ready
StateConnectingWait. Do not make another Connect call
StateConnectedPublish, subscribe, and query
StateReconnectingShow the connection status. A publish waits in the queue
StateFailedExplain the failure. Call Connect again to retry
StateClosingWait for cleanup
StateClosedCreate a new channel if you need another connection

A Connect call in the connected, connecting, or reconnecting state returns ErrOperationInProgress.

To cancel a connection attempt, cancel its context or give it a deadline. Connect then returns ErrCancelled, or ErrTimeout for a deadline. The channel then sets failed:

attempt, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()

if err := channel.Connect(attempt); errors.Is(err, celeris.ErrTimeout) {
    fmt.Println(channel.State()) // failed
}

The context bounds only the attempt. An established connection outlives it. The ConnectTimeout option, 15 seconds by default, bounds the credentials and the handshake together. ReconnectTimeout bounds a reconnect attempt and defaults to ConnectTimeout. Each timeout option accepts from 1 ms to 15*time.Minute, and 0 means the default. For any other value, NewClient returns ErrConfiguration.

3. Respond to recovery

Register a recovery listener once when you set up the channel:

stopRecovery := events.OnRecovery(func(event celeris.RecoveryEvent) {
    fmt.Println("Recovered after retry", event.RetryIndex)
    // Reload your application's authoritative state here.
})

RetryIndex is the number of failed reconnect attempts already used in the current budget. It is 0 when recovery succeeds without an earlier failed attempt in that budget. PossibleGaps and PossibleDuplicates are always true. Reload the authoritative state from your own API. Then reconcile new messages with an application-owned version.

Recovery stops after MaximumReconnectAttempts failed attempts in one budget. The default is 10, and the option accepts from 1 to 100. A new budget starts when a connection drops after 60 seconds or more in the connected state. Only ErrTransport and ErrTimeout failures count. Any other failure stops recovery at once. When recovery stops, OnError gets the cause, and the channel sets failed. Call Connect to try again.

The SDK sends a WebSocket ping every 20 seconds, so the connection stays open while a listener runs. If a ping gets no answer within 15 seconds, the SDK treats the connection as dead and starts recovery. The SDK does not count the time that your listeners use.

Listeners survive reconnects. Do not register them again on every connected state. The SDK recovers a panic in a listener and reports it through OnError. The SDK does not recover a panic in a goroutine that your listener starts. Recover that panic yourself.

Replay sends at most the last 100 messages of each segment, for at most 2 minutes. default replays when the channel connects. Other segments replay when the connection joins them with Subscribe. A publish starts no replay. If the reconnect credentials have no replay claim, the missed messages are not available, and the recovery event reports the gap. See Reconcile application data.

See Reconnection and Recovery for triggers, timing, restored interests, and replay. The recovery event is not a replay-complete signal.

4. Release resources

Each registration returns a function that removes it. Subscription handles have Cancel(). The channel owns the socket.

channel.Close()
stopRecovery()
stopErrors()
stopState()

Close is idempotent and permanent. It returns within about five seconds, even when the server does not answer. It fails queued publishes and presence queries with ErrCancelled. It cancels recovery and ends the channel's segment handles too. Listeners can still receive events that the SDK queued before Close returned, such as the closed state change. Do not reuse a closed channel. Obtain a new one from the client.

Next: Messaging.

We use Google cookies for analytics and for advertising measurement, only if you allow them. Until you choose, Google sets no cookies. Refer to our Cookie Policy.