Lifecycle & Config

Actor Keys

Actor keys uniquely identify actor instances within each actor type. Keys are used for addressing which specific actor to communicate with.

Key Format

Actor keys can be either a string or an array of strings:

import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

const counter = actor({
  state: { count: 0 },
  actions: { increment: (c) => c.state.count++ }
});

const chatRoom = actor({
  state: { messages: [] as string[] },
  actions: {}
});

const registry = setup({ use: { counter, chatRoom } });
const client = createClient<typeof registry>();

// String key
const counterHandle = client.counter.getOrCreate(["my-counter"]);

// Array key (compound key)
const chatRoomHandle = client.chatRoom.getOrCreate(["room", "general"]);
TypeScript

Compound Keys & User Data

Array keys are useful when you need compound keys with user-provided data. Using arrays makes adding user data safe by preventing key injection attacks:

import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

const chatRoom = actor({ state: { messages: [] as string[] }, actions: {} });
const gameRoom = actor({ state: { players: [] as string[] }, actions: {} });
const workspace = actor({ state: { data: {} }, actions: {} });

const registry = setup({ use: { chatRoom, gameRoom, workspace } });
const client = createClient<typeof registry>();

// Example user data
const userId = "user-123";
const gameId = "game-456";
const tenantId = "tenant-789";
const workspaceId = "workspace-abc";

// User-specific chat rooms
const userRoomHandle = client.chatRoom.getOrCreate(["user", userId, "private"]);

// Game rooms by region and difficulty
const gameRoomHandle = client.gameRoom.getOrCreate(["us-west", "hard", gameId]);

// Multi-tenant resources
const workspaceHandle = client.workspace.getOrCreate(["tenant", tenantId, workspaceId]);
TypeScript

This allows you to create hierarchical addressing schemes and organize actors by multiple dimensions.

Don’t build keys using string interpolation like "foo:${userId}:bar" when userId contains user data. If a user provides a value containing the delimiter (: in this example), it can break your key structure and cause key injection attacks.

Omitting Keys

You can create actors without specifying a key in situations where there is a singleton actor (i.e. only one actor of a given type). For example:

import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

const globalActor = actor({
  state: { config: {} },
  actions: {}
});

const registry = setup({ use: { globalActor } });
const client = createClient<typeof registry>();

// Get the singleton session
const globalActorHandle = client.globalActor.getOrCreate();
TypeScript

This pattern should be avoided, since a singleton actor usually means you have a single actor serving all traffic & your application will not scale. See scaling documentation for more information.

Key Uniqueness

Keys are unique within each actor name. Different actor types can use the same key:

import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

const chatRoom = actor({ state: { messages: [] as string[] }, actions: {} });
const userProfile = actor({ state: { name: "" }, actions: {} });

const registry = setup({ use: { chatRoom, userProfile } });
const client = createClient<typeof registry>();

// These are different actors, same key is fine
const userChat = client.chatRoom.getOrCreate(["user-123"]);
const userProfileHandle = client.userProfile.getOrCreate(["user-123"]);
TypeScript

Accessing Keys in Metadata

Access the actor’s key within the actor using the metadata API:

Configuration Examples

Simple Configuration with Keys

Use keys to provide basic actor configuration:

Complex Configuration with Input

For more complex configuration, use input parameters:

import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

interface ChatRoomInput {
  maxUsers: number;
  isPrivate: boolean;
  moderators: string[];
  settings: { allowImages: boolean; slowMode: boolean };
}

const chatRoom = actor({
  state: { maxUsers: 0, isPrivate: false, moderators: [] as string[], settings: { allowImages: true, slowMode: false } },
  createState: (c, input: ChatRoomInput) => ({
    maxUsers: input.maxUsers,
    isPrivate: input.isPrivate,
    moderators: input.moderators,
    settings: input.settings,
  }),
  actions: {}
});

const registry = setup({ use: { chatRoom } });
const client = createClient<typeof registry>("http://localhost:8080");
const roomName = "general";

// Create with both key and input
const chatRoomHandle = await client.chatRoom.create(["room", roomName], {
  input: {
    maxUsers: 100,
    isPrivate: false,
    moderators: ["admin1", "admin2"],
    settings: {
      allowImages: true,
      slowMode: false
    }
  }
});
client.ts

API Reference