Skip to main content

Authentication & Connection

Overview

This page explains how authentication and connection work in the RelayX Kotlin SDK.

RelayX uses API key and secret–based authentication. Both values are required for a client to connect to the Relay Network and perform any operation such as publishing messages or subscribing to topics.

Connection management is handled entirely by the SDK. The SDK establishes the connection, monitors its health, retries on failures, and recovers messages automatically. Application code is not expected to manage sockets, retries, or resubscription logic manually.

This page focuses only on how authentication and connection behave in kotlin, and what you should expect at runtime.


How This Works

Authentication

info

Tutorial to generate API key & Secret

Authentication is performed using an API key and secret pair generated from the RelayX Developer Console.

The SDK does not store your credentials on RelayX servers. Authentication is stateless and verified on each connection attempt.

Important behavior to understand:

  • Both API key and secret are required.
  • Lost API keys & secrets cannot be recovered and must be regenerated.
  • There are permissions associated with each key that allow clients to publish, subscribe and read / write from the KV Store. More information here

Authentication happens as part of establishing a connection to the Relay Network.


Connection Lifecycle

When a RelayX client is created, it is ready to connect but may not yet be connected.

Once a connection attempt is made:

  • The SDK opens a connection to the Relay Network.
  • Authentication is validated using the provided key and secret.
  • On success, the client enters the CONNECTED state.

If the network connection drops:

  • The SDK automatically enters a reconnecting state.
  • Reconnection attempts continue for up to 2 minutes.
  • If reconnection succeeds, the client resumes normal operation.
  • If reconnection fails, the client disconnects permanently.

API Usage

Initializing the Client

The kotlin SDK requires both an API key and secret.

It is strongly recommended to load credentials from environment variables.

Client Initialization

import relay.Realtime
import relay.models.RealtimeConfig
import kotlinx.coroutines.runBlocking
import java.io.File

val realtime = Realtime()
realtime.apiKey = "<API KEY>"
realtime.secretKey = "<SECRET KEY>"

// Normal Kotlin
realtime.filesDir = File.createTempFile("test", "dir").parentFile!!

// Android
realtime.filesDir = context.filesDir

val config = RealtimeConfig()
realtime.init(config)

// Application Code

runBlocking {
realtime.connect()
}

Important Notes:

  • The connect() method is a suspend function and must be called from a coroutine scope (e.g., runBlocking, launch, or within another suspend function).
  • Client initialization (init()) is not a suspend function and does not establish a connection.
  • The actual connection is established when connect() is called.

Connection Listeners

The Client SDK provides a set of connection event listeners that allow your application to respond dynamically to changes in connection state. Leveraging these listeners helps you build a more resilient and user-friendly experience by reacting promptly to connectivity events.

Note that on() is a suspend function and must be called from a coroutine scope.

  • CONNECTED Fired when the client successfully establishes a connection to the server. Use this event to trigger any initialization logic, enable UI elements, or start sending messages.

    realtime.on(Realtime.CONNECTED) { status ->
    val connected = status as Boolean
    if(connected){
    println("Connected to Relay!")
    }else{
    println("Authentication failure :(")
    }
    }
  • RECONNECT This event covers the lifecycle of reconnection attempts and has three states:

    • RECONNECTING: Fired when the client begins attempting to restore a lost connection. You can use this to inform users that the app is trying to reconnect.
    • RECONNECTED: Fired immediately after the client successfully re-establishes the connection. At this point, queued messages are sent, and subscriptions resume normally.
    • RECONN_FAIL: Fired when the client fails to reconnect within the allowed 2-minute window. This signals a permanent disconnection until manual intervention or a new connection attempt.
    realtime.on(Realtime.RECONNECT) { status ->
    val state = status as String
    if(state == "RECONNECTING"){
    println("Looks like we're not connected, attempting to reconnect to Relay...")
    }else if(state == "RECONNECTED"){
    println("Reconnected to Relay! :D")
    }else if(state == "RECONN_FAIL"){
    println("Failed to reconnect to Relay :(")
    }
    }
  • DISCONNECTED Emitted when the connection is closed intentionally (e.g., client calls close()) or after failed reconnection attempts. This event can be used to disable message sending, alert users, or trigger cleanup routines.

    realtime.on(Realtime.DISCONNECTED) {
    println("Connection to Relay closed")
    }

By hooking into these listeners, you gain full visibility and control over the connection lifecycle, enabling responsive UI updates and robust handling of network conditions.

Closing a Connection

The code snippet demonstrates how to close the connection between the client & the Relay Network. Note that close() is a suspend function and must be called from a coroutine scope.

runBlocking {
realtime.close()
}

Code Example

import relay.Realtime
import relay.models.RealtimeConfig
import kotlinx.coroutines.runBlocking
import java.io.File

fun main() = runBlocking {
val realtime = Realtime()
realtime.apiKey = "<API KEY>"
realtime.secretKey = "<SECRET KEY>"

// Normal Kotlin
realtime.filesDir = File.createTempFile("test", "dir").parentFile!!

val config = RealtimeConfig()
realtime.init(config)

realtime.on(Realtime.CONNECTED) { status ->
val connected = status as Boolean
if(connected){
println("Connected to Relay!")
}else{
println("Authentication failure :(")
}
}

realtime.on(Realtime.RECONNECT) { status ->
val state = status as String
if(state == "RECONNECTING"){
println("Looks like we're not connected, attempting to reconnect to Relay...")
}else if(state == "RECONNECTED"){
println("Reconnected to Relay! :D")
}else if(state == "RECONN_FAIL"){
println("Failed to reconnect to Relay :(")
}
}

realtime.on(Realtime.DISCONNECTED) {
println("Connection to Relay closed")
}

realtime.connect()
}

This example shows explicit connection handling and how to observe connection state changes.


Failure & Edge Cases

Network Failures

If the network becomes unavailable:

  • The SDK automatically attempts to reconnect.
  • Reconnection retries continue for up to 2 minutes.
  • Messages published during this period are queued locally.
  • Queued messages are sent automatically once reconnection succeeds.

If reconnection fails:

  • The connection is closed.
  • Any queued but unsent messages are discarded.

Subscriber Message Recovery

For subscribers:

  • Messages missed during a temporary disconnection are delivered after reconnection.
  • Subscriptions are restored automatically.
  • Message ordering per topic is preserved.

No manual resubscription is required.


Process Crashes

If the kotlin process crashes or exits:

  • The connection is closed immediately.
  • Queued messages are lost.
  • In-flight operations may not complete.

After restart, a new client must be created and connected again.


Common Mistakes

Publishing Without Monitoring Connection State

Publishing while disconnected queues messages temporarily.

If the connection ultimately fails, queued messages are dropped.

Do not assume messages are persisted indefinitely.


Ignoring Connection Events

Connection events provide critical signals for application behavior.

Ignoring them makes debugging network and auth issues significantly harder.


Notes & Limitations

  • Secrets cannot be recovered after creation. Store them safely.
  • Reconnection behavior is fixed and not configurable.
  • Message queuing during reconnect is best-effort and bounded by the reconnect window.


Need Help?

Join our Discord server, post your concern & someone from our team will help you out ✌️