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
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.
Join our Discord server, post your concern & someone from our team will help you out ✌️