Actors combine compute and storage into unified entities for simplified architecture. Actors seamlessly integrate with your existing infrastructure or can serve as a complete standalone solution.

Quickstart

Run this to get started:

npx create-actor@latest

What are actors good for?

Actors in ActorCore are ideal for applications requiring:

  • Stateful Services: Applications where maintaining state across interactions is critical. For example, Collaborative Apps with shared editing and automatic persistence.
  • Realtime Systems: Applications requiring fast, in-memory state modifications or push updates to connected clients. For example, Multiplayer Games with game rooms and player state.
  • Long-Running Processes: Tasks that execute over extended periods or in multiple steps. For example, AI Agents with ongoing conversations and stateful tool calls.
  • Durability: Processes that must survive crashes and restarts without data loss. For example, Durable Execution workflows that continue after system restarts.
  • Horizontal Scalability: Systems that need to scale by distributing load across many instances. For example, Realtime Stream Processing for stateful event handling.
  • Local-First Architecture: Systems that synchronize state between offline clients. For example, Local-First Sync between devices.

Core Concepts

In ActorCore, each actor has these key characteristics:

  • State Is Automatically Persisted: State automatically persists between restarts, upgrades, & crashes
  • State Is Stored In-Memory: State is stored in memory for high-performance reads/writes while also automatically persisted
  • Isolated State Ownership: Actors only manage their own state, which can only be modified by the actor itself
  • Communicates via Actions: How clients and other actors interact with an actor
  • Actions Are Low-Latency: Actions provide WebSocket-like performance for time-sensitive operations
  • Broadcast Updates With Events: Actors can publish real-time updates to connected clients

Code Example

Here’s a complete chat room actor that maintains state and handles messages. We’ll explore each component in depth throughout this document:

chat_room.ts
import { actor } from "actor-core";

// Define a chat room actor
const chatRoom = actor({
  // Initialize state when the actor is first created
  createState: () => ({
    messages: []
  }),

  // Define actions clients can call
  actions: {
    // Action to send a message
    sendMessage: (c, sender, text) => {
      // Update state
      c.state.messages.push({ sender, text });
      
      // Broadcast to all connected clients
      c.broadcast("newMessage", { sender, text });
    },
    
    // Action to get chat history
    getHistory: (c) => {
      return c.state.messages;
    }
  }
});

export default chatRoom;

Using the App

To start using your actor, create an app and serve it:

app.ts
import { setup, serve } from "actor-core";
import chatRoom from "./chat_room";

// Create the application
const app = setup({
  actors: { chatRoom }
});

// Start serving on default port
serve(app);

// Export the app type for client usage
export type App = typeof app;

Key Actor Components

State

Actors maintain state that’s stored in memory and automatically persisted. State is defined either as a constant or via a createState function:

import { actor } from "actor-core";

// Method 1: State constant
const counter1 = actor({
  state: { count: 0 },
  actions: {
    // ...
  }
});

// Method 2: CreateState function
const counter2 = actor({
  createState: () => ({ count: 0 }),
  actions: {
    // ...
  }
});

Update state by modifying c.state in your actions:

import { actor } from "actor-core";

const counter = actor({
  state: { count: 0 },
  actions: {
    // Example of state update in an action
    increment: (c) => {
      c.state.count += 1;
      return c.state.count;
    }
  }
});

These changes are durable and are automatically persisted across updates, restarts, and crashes.

Learn more about state management.

Actions

Actions are functions defined in your actor configuration that clients & other actors can call:

import { actor } from "actor-core";

const mathUtils = actor({
  state: {},
  actions: {
    multiplyByTwo: (c, x) => {
      return x * 2;
    }
  }
});

Each action receives a context object (commonly named c) as its first parameter, which provides access to state, connections, and other utilities.

Learn more about actions.

Events

Actors can broadcast events to connected clients:

import { actor } from "actor-core";

const inventory = actor({
  createState: () => ({ 
    items: [] 
  }),
  
  actions: {
    addItem: (c, item) => {
      // Add to state
      c.state.items.push(item);
      
      // Notify all clients about the new item
      c.broadcast("itemAdded", { item });
    }
  }
});

You can also send events to specific clients:

import { actor } from "actor-core";

const messageService = actor({
  state: {},
  actions: {
    sendPrivateMessage: (c, userId, text) => {
      // Send to a specific connection
      const conn = c.conns.find(conn => conn.params.userId === userId);
      if (conn) {
        conn.send("privateMessage", { text });
      }
    }
  }
});

Learn more about events.

Actor Tags

Tags are key-value pairs attached to actors that serve two purposes:

  1. Actor Discovery: Find specific actors using client.get(tags)
  2. Organization: Group related actors for management purposes

For example, you can query chat rooms by tag like:

client.ts
await client.chatRoom.get({ channel: "random" });

Common Tag Patterns

import { createClient } from "actor-core/client";
import type { App } from "./src/index";

const client = createClient<App>("http://localhost:6420");

// Game room with ID parameter
const gameRoom = await client.gameRoom.get({ roomId: "ABC123" });

// User profile with ID
const userProfile = await client.userProfile.get({ profileId: "1234" });

// Document with multiple parameters
const document = await client.document.get({
  workspaceId: "team-alpha",
  documentId: "budget-2024"
});

Actor Lifecycle

Actors are created automatically when needed and persist until explicitly shutdown.

To shut down an actor, use c.shutdown() from within an action:

import { actor } from "actor-core";

const chatRoom = actor({
  createState: () => ({
    messages: []
  }),
  actions: {
    closeRoom: (c) => {
      // Do any cleanup needed
      c.broadcast("roomClosed");
      
      // Shutdown the actor
      c.shutdown();
    }
  }
});

Learn more about the actor lifecycle.

Next Steps