# Actor Keys

> Source: `src/content/docs/actors/keys.mdx`
> Canonical URL: https://rivet.dev/docs/actors/keys
> Description: 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:

```typescript
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"]);
```

### 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:

```typescript
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]);
```

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:

```typescript
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();
```

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](/docs/actors/scaling) for more information.

### Key Uniqueness

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

```typescript
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"]);
```

## Accessing Keys in Metadata

Access the actor's key within the actor using the [metadata](/docs/actors/metadata) API:

```typescript actors.ts
import { actor, setup } from "rivetkit";

const chatRoom = actor({
  state: { messages: [] as string[] },
  actions: {
    getRoomName: c => {
      // Access the key from metadata
      const key = c.key;
      return key[1]; // Get "general" from ["room", "general"]
    },
  },
});

export const registry = setup({
  use: { chatRoom },
});
```

```typescript client.ts
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

const chatRoom = actor({
  state: { messages: [] as string[] },
  actions: { getRoomName: c => c.key[1] },
});

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

async function connectToRoom(roomName: string) {
  // Connect to a chat room
  const chatRoomHandle = client.chatRoom.getOrCreate(["room", roomName]);

  // Get the room name from the key
  const retrievedRoomName = await chatRoomHandle.getRoomName();
  console.log("Room name:", retrievedRoomName); // e.g., "general"

  return chatRoomHandle;
}

// Usage example
const generalRoom = await connectToRoom("general");
```

## Configuration Examples

### Simple Configuration with Keys

Use keys to provide basic actor configuration:

```typescript actors.ts
import { actor, setup } from "rivetkit";

interface UserSessionState {
  userId: string;
  loginTime: number;
  preferences: Record<string, unknown>;
}

const userSession = actor({
  state: { userId: "", loginTime: 0, preferences: {} } as UserSessionState,
  createState: (c): UserSessionState => ({
    userId: c.key[0], // Extract user ID from key
    loginTime: Date.now(),
    preferences: {},
  }),

  actions: {
    getUserId: c => c.state.userId,
  },
});

export const registry = setup({
  use: { userSession },
});
```

```typescript client.ts
import { actor, setup } from "rivetkit";
import { createClient } from "rivetkit/client";

const userSession = actor({
  state: { userId: "", loginTime: 0, preferences: {} },
  actions: { getUserId: c => c.state.userId },
});

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

// Pass user ID in the key for user-specific actors
const userId = "user-123";
const userSessionHandle = client.userSession.getOrCreate([userId]);
```

### Complex Configuration with Input

For more complex configuration, use [input parameters](/docs/actors/input):

```typescript client.ts
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,
    },
  },
});
```

## API Reference

- [`ActorKey`](/typedoc/types/rivetkit.mod.ActorKey.html) - Key type for actors
- [`ActorQuery`](/typedoc/types/rivetkit.mod.ActorQuery.html) - Query type using keys
- [`GetOptions`](/typedoc/interfaces/rivetkit.client_mod.GetOptions.html) - Options for getting by key
- [`QueryOptions`](/typedoc/interfaces/rivetkit.client_mod.QueryOptions.html) - Options for querying

_Source doc path: /docs/actors/keys_
