Connections
Manage Rivet Actor client connections, authenticate callers, store connection-specific state, and respond to connect or disconnect lifecycle events.
For documentation on connecting to actors from clients, see the Clients documentation.
Parameters
When clients connect to an actor, they can pass connection parameters that are handled during the connection process. Use params for static values, or getParams when you need fresh connection data each time a connection opens.
For example:
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";
interface ConnParams {
authToken: string;
}
const gameRoom = actor({
onBeforeConnect: (c, params: ConnParams) => {},
actions: {}
});
const registry = setup({ use: { gameRoom } });
const client = createClient<typeof registry>("http://localhost:6420");
async function getAuthToken(): Promise<string> {
return "supersekure";
}
const gameRoomHandle = client.gameRoom.getOrCreate(["room-123"], {
getParams: async () => ({
authToken: await getAuthToken(),
})
});
import { actor } from "rivetkit";
interface ConnParams {
authToken: string;
}
interface ConnInfo {
userId: string;
role: string;
}
// Example validation functions
function validateToken(token: string): boolean {
return token.length > 0;
}
function getUserIdFromToken(token: string): string {
return "user-" + token.slice(0, 8);
}
const gameRoom = actor({
// Per-connection data, keyed by connection ID
createVars: () => ({ players: new Map<string, ConnInfo>() }),
// Validate the authentication token before accepting the connection
onBeforeConnect: (c, params: ConnParams) => {
if (!params.authToken || !validateToken(params.authToken)) {
throw new Error("Invalid auth token");
}
},
// Record who this connection belongs to
onConnect: (c, conn) => {
c.vars.players.set(conn.id, {
userId: getUserIdFromToken(conn.params.authToken),
role: "player"
});
},
onDisconnect: (c, conn) => {
c.vars.players.delete(conn.id);
},
actions: {}
});
Connection State
Track per-connection data in c.vars, keyed by conn.id. Add it in onConnect and remove it in onDisconnect. For data that must outlive the actor’s memory, such as on a hibernatable WebSocket, store it in SQLite instead.
Connection Lifecycle
Each client connection goes through a series of lifecycle hooks that allow you to validate, initialize, and clean up connection-specific resources.
On Connect (per client)
onBeforeConnectonConnect
Pending connections are not visible in c.conns while onBeforeConnect is running. RivetKit adds the connection to c.conns after it succeeds and before onConnect runs.
On Disconnect (per client)
onDisconnect
onBeforeConnect
The onBeforeConnect hook is called whenever a new client connects to the actor. Can be async. Clients can pass parameters when connecting, accessible via params. This hook is used for connection validation and can throw errors to reject connections.
The onBeforeConnect hook is used solely for validation. To keep per-connection data, store it in c.vars from onConnect.
Connections are not visible in c.conns while onBeforeConnect is running.
import { actor } from "rivetkit";
interface ConnParams {
authToken?: string;
userId?: string;
role?: string;
}
interface ConnInfo {
userId: string;
role: string;
joinTime: number;
}
function validateToken(token: string): boolean {
return token.length > 0;
}
const chatRoom = actor({
// Per-connection data, keyed by connection ID
createVars: () => ({ connections: new Map<string, ConnInfo>() }),
// Validate connections before accepting them
onBeforeConnect: (c, params: ConnParams) => {
// Validate authentication
const authToken = params.authToken;
if (!authToken || !validateToken(authToken)) {
throw new Error("Invalid authentication");
}
// Authentication is valid, so the connection will proceed to onConnect
},
// Record per-connection data once the connection is accepted
onConnect: (c, conn) => {
c.vars.connections.set(conn.id, {
userId: conn.params.userId || "anonymous",
role: conn.params.role || "guest",
joinTime: Date.now()
});
},
onDisconnect: (c, conn) => {
c.vars.connections.delete(conn.id);
},
actions: {}
});
Connections cannot interact with the actor until this method completes successfully. Throwing an error will abort the connection. This can be used for authentication, see Permissions for details.
onConnect
Executed after the client has successfully connected. Can be async. Receives the connection object as a second parameter.
By the time onConnect runs, the connection is visible in c.conns.
import { actor } from "rivetkit";
interface UserStatus {
userId: string;
online: boolean;
lastSeen: number;
}
const chatRoom = actor({
// Track users in memory, keyed by connection ID
createVars: () => ({ users: new Map<string, UserStatus>() }),
onConnect: (c, conn) => {
// Add the user to the room's user list
const userId = "user-" + Math.random().toString(36).slice(2, 11);
c.vars.users.set(conn.id, {
userId,
online: true,
lastSeen: Date.now()
});
// Broadcast that a user joined
c.broadcast("userJoined", { userId, timestamp: Date.now() });
console.log(`User ${userId} connected`);
},
actions: {}
});
Messages will not be processed for this actor until this hook succeeds. Errors thrown from this hook will cause the client to disconnect.
onDisconnect
Called when a client disconnects from the actor. Can be async. Receives the connection object as a second parameter. Use this to clean up any connection-specific resources.
import { actor } from "rivetkit";
interface UserStatus {
userId: string;
online: boolean;
lastSeen: number;
}
const chatRoom = actor({
// Track users in memory, keyed by connection ID
createVars: () => ({ users: new Map<string, UserStatus>() }),
onConnect: (c, conn) => {
const userId = "user-" + Math.random().toString(36).slice(2, 11);
c.vars.users.set(conn.id, { userId, online: true, lastSeen: Date.now() });
},
onDisconnect: (c, conn) => {
// Clean up the user's entry when they disconnect
const user = c.vars.users.get(conn.id);
if (!user) return;
c.vars.users.delete(conn.id);
// Broadcast that a user left
c.broadcast("userLeft", { userId: user.userId, timestamp: Date.now() });
console.log(`User ${user.userId} disconnected`);
},
actions: {}
});
Connection List
All active connections can be accessed through the context object’s conns property. This is a Map<string, Conn> of all current connections, keyed by connection ID.
This is frequently used with conn.send(name, event) to send messages directly to clients. To send an event to all connections at once, use c.broadcast() instead. See Events for more details on broadcasting.
For example:
import { actor } from "rivetkit";
const chatRoom = actor({
// Map each connection ID to its user ID
createVars: () => ({ userIds: new Map<string, string>() }),
onConnect: (c, conn) => {
c.vars.userIds.set(conn.id, "user-" + Math.random().toString(36).slice(2, 11));
},
onDisconnect: (c, conn) => {
c.vars.userIds.delete(conn.id);
},
actions: {
sendDirectMessage: (c, recipientId: string, message: string) => {
// Find the recipient's connection by iterating over the Map
let recipientConn = null;
for (const conn of c.conns.values()) {
if (c.vars.userIds.get(conn.id) === recipientId) {
recipientConn = conn;
break;
}
}
if (recipientConn) {
// Send a private message to just that client
recipientConn.send("directMessage", {
from: c.vars.userIds.get(c.conn.id),
message: message
});
}
}
}
});
conn.send() has no effect on low-level WebSocket connections. For low-level WebSockets, use the WebSocket API directly (e.g., websocket.send()).
Disconnecting clients
Connections can be disconnected from within an action:
import { actor } from "rivetkit";
const secureRoom = actor({
// Map each connection ID to its user ID
createVars: () => ({ userIds: new Map<string, string>() }),
onConnect: (c, conn) => {
c.vars.userIds.set(conn.id, "user-" + Math.random().toString(36).slice(2, 11));
},
onDisconnect: (c, conn) => {
c.vars.userIds.delete(conn.id);
},
actions: {
kickUser: (c, targetUserId: string, reason?: string) => {
// Find the connection to kick by iterating over the Map
for (const conn of c.conns.values()) {
if (c.vars.userIds.get(conn.id) === targetUserId) {
// Disconnect with a reason
conn.disconnect(reason || "Kicked by admin");
break;
}
}
}
}
});
If you need to wait for the disconnection to complete, you can use await:
import { actor } from "rivetkit";
const myActor = actor({
actions: {
disconnect: async (c) => {
await c.conn.disconnect("Too many requests");
}
}
});
This ensures the underlying network connections close cleanly before continuing.