diff --git a/docs/concepts/overview.mdx b/docs/concepts/overview.mdx
index 901720629..a5e8686d5 100644
--- a/docs/concepts/overview.mdx
+++ b/docs/concepts/overview.mdx
@@ -1,5 +1,5 @@
---
-title: Overview
+title: Introduction
icon: square-info
---
@@ -13,28 +13,6 @@ Run this to get started:
-## 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:
diff --git a/docs/docs.json b/docs/docs.json
index 37e7b1944..2b68b699b 100644
--- a/docs/docs.json
+++ b/docs/docs.json
@@ -1,7 +1,7 @@
{
"$schema": "https://mintlify.com/docs.json",
"name": "ActorCore",
- "description": "Stateful, scalable, realtime backend framework. The modern way to build multiplayer, realtime, or AI agent backends. Supports Rivet, Cloudflare Workers, Bun, and Node.js.",
+ "description": "Stateful Serverless that runs anywhere. The easiest way to build stateful, AI agent, collaborative, or local-first applications.",
"theme": "palm",
"logo": {
"dark": "/logo/dark.svg",
diff --git a/docs/images/clients/nextjs.svg b/docs/images/clients/nextjs.svg
new file mode 100644
index 000000000..e2da0adf9
--- /dev/null
+++ b/docs/images/clients/nextjs.svg
@@ -0,0 +1,20 @@
+
diff --git a/docs/images/clients/python.svg b/docs/images/clients/python.svg
new file mode 100644
index 000000000..84dd1f953
--- /dev/null
+++ b/docs/images/clients/python.svg
@@ -0,0 +1,54 @@
+
+
+
\ No newline at end of file
diff --git a/docs/images/integrations/better-auth.svg b/docs/images/integrations/better-auth.svg
new file mode 100644
index 000000000..45cb774df
--- /dev/null
+++ b/docs/images/integrations/better-auth.svg
@@ -0,0 +1,8 @@
+
diff --git a/docs/images/platforms/hono.svg b/docs/images/integrations/hono.svg
similarity index 100%
rename from docs/images/platforms/hono.svg
rename to docs/images/integrations/hono.svg
diff --git a/docs/images/integrations/livestore.svg b/docs/images/integrations/livestore.svg
new file mode 100644
index 000000000..2592d779d
--- /dev/null
+++ b/docs/images/integrations/livestore.svg
@@ -0,0 +1,13 @@
+
diff --git a/docs/images/integrations/resend.svg b/docs/images/integrations/resend.svg
new file mode 100644
index 000000000..3f1491556
--- /dev/null
+++ b/docs/images/integrations/resend.svg
@@ -0,0 +1,3 @@
+
diff --git a/docs/images/integrations/tinybase.svg b/docs/images/integrations/tinybase.svg
new file mode 100644
index 000000000..af127bf91
--- /dev/null
+++ b/docs/images/integrations/tinybase.svg
@@ -0,0 +1,5 @@
+
diff --git a/docs/images/integrations/yjs.svg b/docs/images/integrations/yjs.svg
new file mode 100644
index 000000000..5a1147276
--- /dev/null
+++ b/docs/images/integrations/yjs.svg
@@ -0,0 +1,24 @@
+
+
+
\ No newline at end of file
diff --git a/docs/images/integrations/zerosync.svg b/docs/images/integrations/zerosync.svg
new file mode 100644
index 000000000..0d9727c9d
--- /dev/null
+++ b/docs/images/integrations/zerosync.svg
@@ -0,0 +1,11 @@
+
diff --git a/docs/images/platforms/postgres.svg b/docs/images/platforms/postgres.svg
new file mode 100644
index 000000000..8666f75c1
--- /dev/null
+++ b/docs/images/platforms/postgres.svg
@@ -0,0 +1,20 @@
+
+
+
\ No newline at end of file
diff --git a/docs/images/platforms/supabase.svg b/docs/images/platforms/supabase.svg
new file mode 100644
index 000000000..ad802ac16
--- /dev/null
+++ b/docs/images/platforms/supabase.svg
@@ -0,0 +1,15 @@
+
diff --git a/docs/images/platforms/vercel.svg b/docs/images/platforms/vercel.svg
new file mode 100644
index 000000000..4e3b0e933
--- /dev/null
+++ b/docs/images/platforms/vercel.svg
@@ -0,0 +1,3 @@
+
diff --git a/docs/images/quotes/posts/1902835527977439591.jpg b/docs/images/quotes/posts/1902835527977439591.jpg
new file mode 100644
index 000000000..e4403665a
Binary files /dev/null and b/docs/images/quotes/posts/1902835527977439591.jpg differ
diff --git a/docs/images/quotes/posts/1909278348812952007.png b/docs/images/quotes/posts/1909278348812952007.png
new file mode 100644
index 000000000..f799ec742
Binary files /dev/null and b/docs/images/quotes/posts/1909278348812952007.png differ
diff --git a/docs/images/quotes/users/Chinoman10_.jpg b/docs/images/quotes/users/Chinoman10_.jpg
new file mode 100644
index 000000000..f37c750a2
Binary files /dev/null and b/docs/images/quotes/users/Chinoman10_.jpg differ
diff --git a/docs/images/quotes/users/Social_Quotient.jpg b/docs/images/quotes/users/Social_Quotient.jpg
new file mode 100644
index 000000000..ea6fcddbd
Binary files /dev/null and b/docs/images/quotes/users/Social_Quotient.jpg differ
diff --git a/docs/images/quotes/users/alistaiir.jpg b/docs/images/quotes/users/alistaiir.jpg
new file mode 100644
index 000000000..67f2eaedc
Binary files /dev/null and b/docs/images/quotes/users/alistaiir.jpg differ
diff --git a/docs/images/quotes/users/devgerred.jpg b/docs/images/quotes/users/devgerred.jpg
new file mode 100644
index 000000000..4a5a86e49
Binary files /dev/null and b/docs/images/quotes/users/devgerred.jpg differ
diff --git a/docs/images/quotes/users/j0g1t.jpg b/docs/images/quotes/users/j0g1t.jpg
new file mode 100644
index 000000000..fb881848e
Binary files /dev/null and b/docs/images/quotes/users/j0g1t.jpg differ
diff --git a/docs/images/quotes/users/localfirstnews.jpg b/docs/images/quotes/users/localfirstnews.jpg
new file mode 100644
index 000000000..55bb2081b
Binary files /dev/null and b/docs/images/quotes/users/localfirstnews.jpg differ
diff --git a/docs/images/quotes/users/samgoodwin89.jpg b/docs/images/quotes/users/samgoodwin89.jpg
new file mode 100644
index 000000000..7d3dbf7a1
Binary files /dev/null and b/docs/images/quotes/users/samgoodwin89.jpg differ
diff --git a/docs/images/quotes/users/samk0_com.jpg b/docs/images/quotes/users/samk0_com.jpg
new file mode 100644
index 000000000..4ae81a0bb
Binary files /dev/null and b/docs/images/quotes/users/samk0_com.jpg differ
diff --git a/docs/images/quotes/users/uripont_.jpg b/docs/images/quotes/users/uripont_.jpg
new file mode 100644
index 000000000..8cd28fc03
Binary files /dev/null and b/docs/images/quotes/users/uripont_.jpg differ
diff --git a/docs/introduction.mdx b/docs/introduction.mdx
index ad54ef142..9100e8ba1 100644
--- a/docs/introduction.mdx
+++ b/docs/introduction.mdx
@@ -1,610 +1,231 @@
---
title: ActorCore
-description: Stateful, Scalable, Realtime Backend Framework
+description: Stateful Serverless That Runs Anywhere
sidebarTitle: Introduction
mode: custom
---
+import ComparisonTable from "/snippets/landing-comparison-table.mdx";
+import Snippets from "/snippets/landing-snippets.mdx";
+import Tech from "/snippets/landing-tech.mdx";
+import Quotes from "/snippets/landing-quotes.mdx";
+import Manifesto from "/snippets/landing-manifesto.mdx";
+import FAQ from "/snippets/landing-faq.mdx";
+
+
+
+
+
+ Stateful Serverless That Runs Anywhere
+
+
+
+ The easiest way to build{" "}
+ stateful,{" "}
+ AI agent,{" "}
+ collaborative, or{" "}
+ local-first{" "}
+ applications.
+ Deploy to Rivet, Cloudflare, Bun, Node.js, and more.
+
Each unit of compute is like a tiny server that remembers things between requests – no need to reload data or worry about timeouts. Like AWS Lambda, but with memory and no timeouts.
+
+
+
+
+
Durable State Without a Database
+
+
Your code's state is saved automatically—no database, ORM, or config needed. Just use regular JavaScript objects or SQLite (available in April).
+
+
+
+
+
Blazing-Fast Reads & Writes
+
+
State is stored on the same machine as your compute, so reads and writes are ultra-fast. No database round trips, no latency spikes.
+
+
+
+
+
Realtime, Made Simple
+
+
Update state and broadcast changes in realtime. No external pub/sub systems, no polling – just built-in low-latency events.
+
+
+
+
+
Store Data Near Your Users
+
+
Your state lives close to your users on the edge – not in a faraway data center – so every interaction feels instant.
+
+
+
+
+
Serverless & Scalable
+
+
No servers to manage. Your code runs on-demand and scales automatically with usage.
- ```typescript
- import { actor } from "actor-core";
-
- const chatRoom = actor({
- state: { messages: [] },
- actions: {
- // receive an action call from the client
- sendMessage: (c, username, message) => {
- // save message to persistent storage
- c.state.messages.push({ username, message });
-
- // broadcast message to all clients
- c.broadcast("newMessage", username, message);
- },
- // allow client to request message history
- getMessages: (c) => c.state.messages
- }
- });
- ```
-
-
-
-
-
-
-
- Fast in-memory access with built-in durability — no external databases or
- caches needed.
-
-
- Real-time state updates with ultra-low latency, powered by co-locating
- compute and data.
-
-
- Integrated support for state, actions, events, scheduling, and multiplayer — no
- extra boilerplate code needed.
-
-
- Effortless scaling, scale-to-zero, and easy deployments on any serverless
- runtime.
-
-
-
-
-
-
- Features
-
-
-
-
-
-
-
-
-
-
-
-
-
-
Everything you need to build realtime, stateful backends
-
ActorCore provides a solid foundation with the features you'd expect for modern apps.
-
-
-
-
-
-
Feature
-
-
- ActorCore
-
-
-
- Durable Objects
-
-
-
- Socket.io
-
-
-
- Redis
-
-
-
- AWS Lambda
-
-
-
-
-
-
-
-
-
-
- In-Memory State
-
-
-
Fast access to in-memory data without complex caching
-
-
-
-
-
-
-
-
-
-
-
- ActorCore
-
-
-
- Durable Objects
-
-
-
- Socket.io
-
-
-
- Redis
-
-
-
- AWS Lambda
-
-
-
-
-
-
-
-
-
-
- Persisted State
-
-
-
Built-in persistence that survives crashes and restarts
-
-
-
-
-
-
-
-
-
-
-
- ActorCore
-
-
-
- Durable Objects
-
-
-
- Socket.io
-
-
-
- Redis
-
-
-
- AWS Lambda
-
-
-
-
-
-
-
-
-
-
- Actions
-
-
-
Define and call functions that interact with your actors
-
-
-
-
-
-
-
-
-
-
-
- ActorCore
-
-
-
- Durable Objects
-
-
-
- Socket.io
-
-
-
- Redis
-
-
-
- AWS Lambda
-
-
-
-
-
-
-
-
-
-
- Events (Pub/Sub)
-
-
-
Real-time messaging with publish/subscribe patterns
-
-
-
-
-
-
-
-
-
-
-
- ActorCore
-
-
-
- Durable Objects
-
-
-
- Socket.io
-
-
-
- Redis
-
-
-
- AWS Lambda
-
-
-
-
-
-
-
-
-
-
- Scheduling
-
-
-
Run tasks in the future without external schedulers
-
-
-
-
-
-
-
-
-
-
-
- ActorCore
-
-
-
- Durable Objects
-
-
-
- Socket.io
-
-
-
- Redis
-
-
-
- AWS Lambda
-
-
-
-
-
-
-
-
-
-
- Edge Computing
-
-
-
Deploy globally for low-latency access from anywhere
-
-
-
¹
-
-
-
-
-
-
-
-
- ActorCore ¹
-
-
-
- Durable Objects
-
-
-
- Socket.io
-
-
-
- Redis
-
-
-
- AWS Lambda
-
-
-
-
-
-
-
-
-
-
- No Vendor Lock
-
-
-
Run on multiple platforms without rewriting your app
-
-
-
-
-
-
-
-
-
-
-
- ActorCore
-
-
-
- Durable Objects
-
-
-
- Socket.io
-
-
-
- Redis
-
-
-
- AWS Lambda
-
-
-
-
-
-
-
-
-
= requires significant boilerplate code or external service
-
¹ = on supported platforms
-
+
+
+
+
+
+
+
+
+
+
Join the Community
+
Help make ActorCore the universal way to build & scale stateful serverless applications.
-
-
- ```typescript Actor
- import { actor, setup } from "actor-core";
-
- const chatRoom = actor({
- state: { messages: [] },
- actions: {
- // receive an action call from the client
- sendMessage: (c, username: string, message: string) => {
- // save message to persistent storage
- c.state.messages.push({ username, message });
-
- // broadcast message to all clients
- c.broadcast("newMessage", username, message);
- },
- // allow client to request message history
- getMessages: (c) => c.state.messages
- },
- });
-
- export const app = setup({
- actors: { chatRoom },
- cors: { origin: "http://localhost:8080" }
- });
-
- export type App = typeof app;
- ```
-
-
-
-
- ```typescript Browser (TypeScript)
- import { createClient } from "actor-core/client";
- import type { App } from "../src/index";
-
- const client = createClient(/* manager endpoint */);
-
- // connect to chat room
- const chatRoom = await client.chatRoom.get({ channel: "random" });
-
- // listen for new messages
- chatRoom.on("newMessage", (username: string, message: string) =>
- console.log(`Message from ${username}: ${message}`),
- );
-
- // send message to room
- await chatRoom.sendMessage("william", "All the world's a stage.");
- ```
-
- ```javascript Browser (JavaScript)
- import { createClient } from "actor-core/client";
-
- const client = createClient(/* manager endpoint */);
-
- // connect to chat room
- const chatRoom = await client.chatRoom.get();
-
- // listen for new messages
- chatRoom.on("newMessage", (username, message) =>
- console.log(`Message from ${username}: ${message}`),
- );
-
- // send message to room
- await chatRoom.sendMessage("william", "All the world's a stage.");
- ```
-
-
-
-
-
-
-{/*
-
-
- Overview
-
-
-Resources to help you use LLMs with ActorCore to build AI agents and tools. The following guides provide information on how to use ActorCore with your vibe coding workflow of choice.
-
-
-
-
-
-
-
-
-
-
-*/}
-
-
diff --git a/docs/overview.mdx b/docs/overview.mdx
index 5a8112f95..57b6bcc10 100644
--- a/docs/overview.mdx
+++ b/docs/overview.mdx
@@ -5,6 +5,29 @@ sidebarTitle: "Overview"
ActorCore is a framework for building stateful, scalable, realtime backend applications. Whether you're building multiplayer games, collaborative apps, AI agent backends, or any stateful service, ActorCore provides the tools and patterns to simplify your architecture.
+{/*## 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
+
+
## Get Started
Integrate ActorCore with your project:
diff --git a/docs/scripts/faq.js b/docs/scripts/faq.js
new file mode 100644
index 000000000..e0b413890
--- /dev/null
+++ b/docs/scripts/faq.js
@@ -0,0 +1,75 @@
+function initializeFAQ(faqSection) {
+ console.log("[Initialize] FAQ", faqSection?.id || "all");
+
+ // If no section provided, fall back to querying all accordions
+ const accordions = faqSection ?
+ faqSection.querySelectorAll('.faq-accordion') :
+ document.querySelectorAll('.faq-accordion');
+
+ if (!accordions.length) return;
+
+ accordions.forEach(accordion => {
+ const button = accordion.querySelector('.faq-question');
+ const answer = accordion.querySelector('.faq-answer');
+
+ if (!button || !answer) return;
+
+ button.addEventListener('click', () => {
+ const isOpen = accordion.getAttribute('data-state') === 'open';
+
+ // Close all other accordions
+ accordions.forEach(otherAccordion => {
+ if (otherAccordion !== accordion) {
+ otherAccordion.setAttribute('data-state', 'closed');
+ }
+ });
+
+ // Toggle current accordion
+ accordion.setAttribute('data-state', isOpen ? 'closed' : 'open');
+ });
+ });
+}
+
+// Create an observer instance
+const observer = new MutationObserver((mutations) => {
+ for (const mutation of mutations) {
+ if (mutation.type !== 'childList') continue;
+
+ for (const node of mutation.addedNodes) {
+ // Quick check for element nodes only
+ if (node.nodeType !== 1) continue;
+
+ // Direct class check is faster than matches()
+ if (node.classList?.contains('faq-section')) {
+ initializeFAQ(node);
+ continue;
+ }
+
+ // Only query children if needed
+ const nestedSection = node.getElementsByClassName('faq-section')[0];
+ if (nestedSection) {
+ initializeFAQ(nestedSection);
+ continue;
+ }
+ }
+ }
+});
+
+// Start observing with optimized configuration
+observer.observe(document.body, {
+ childList: true,
+ subtree: true,
+ attributes: false,
+ characterData: false
+});
+
+// Initialize any existing FAQ sections
+document.querySelectorAll('.faq-section').forEach(section => initializeFAQ(section));
+
+// Cleanup when needed
+function cleanup() {
+ observer.disconnect();
+}
+
+// Optional: Add cleanup on page unload
+window.addEventListener('unload', cleanup);
\ No newline at end of file
diff --git a/docs/snippets/examples/ai-agent-js.mdx b/docs/snippets/examples/ai-agent-js.mdx
new file mode 100644
index 000000000..ad96fc931
--- /dev/null
+++ b/docs/snippets/examples/ai-agent-js.mdx
@@ -0,0 +1,67 @@
+```typescript
+import { actor } from "actor-core";
+import { generateText, tool } from "ai";
+import { openai } from "@ai-sdk/openai";
+import { getWeather } from "./my-utils";
+
+export type Message = { role: "user" | "assistant"; content: string; timestamp: number; }
+
+const aiAgent = actor({
+ // State is automatically persisted
+ state: {
+ messages: [] as Message[]
+ },
+
+ actions: {
+ // Get conversation history
+ getMessages: (c) => c.state.messages,
+
+ // Send a message to the AI and get a response
+ sendMessage: async (c, userMessage: string) => {
+ // Add user message to conversation
+ const userMsg: Message = {
+ role: "user",
+ content: userMessage,
+ timestamp: Date.now()
+ };
+ c.state.messages.push(userMsg);
+
+ // Generate AI response using Vercel AI SDK with tools
+ const { text } = await generateText({
+ model: openai("o3-mini"),
+ prompt: userMessage,
+ messages: c.state.messages,
+ tools: {
+ weather: tool({
+ description: 'Get the weather in a location',
+ parameters: {
+ location: {
+ type: 'string',
+ description: 'The location to get the weather for',
+ },
+ },
+ execute: async ({ location }) => {
+ return await getWeather(location);
+ },
+ }),
+ },
+ });
+
+ // Add AI response to conversation
+ const assistantMsg: Message = {
+ role: "assistant",
+ content: text,
+ timestamp: Date.now()
+ };
+ c.state.messages.push(assistantMsg);
+
+ // Broadcast the new message to all connected clients
+ c.broadcast("messageReceived", assistantMsg);
+
+ return assistantMsg;
+ },
+ }
+});
+
+export default aiAgent;
+```
diff --git a/docs/snippets/examples/ai-agent-react.mdx b/docs/snippets/examples/ai-agent-react.mdx
new file mode 100644
index 000000000..1adffc908
--- /dev/null
+++ b/docs/snippets/examples/ai-agent-react.mdx
@@ -0,0 +1,87 @@
+```typescript
+import { createClient } from "actor-core/client";
+import { createReactActorCore } from "@actor-core/react";
+import { useState, useEffect } from "react";
+import type { App } from "../actors/app";
+import type { Message } from "./actor";
+
+const client = createClient("http://localhost:6420");
+const { useActor, useActorEvent } = createReactActorCore(client);
+
+export function AIAssistant() {
+ const [{ actor }] = useActor("aiAgent", { tags: { conversationId: "default" } });
+ const [messages, setMessages] = useState([]);
+ const [input, setInput] = useState("");
+ const [isLoading, setIsLoading] = useState(false);
+
+ // Load initial messages
+ useEffect(() => {
+ if (actor) {
+ actor.getMessages().then(setMessages);
+ }
+ }, [actor]);
+
+ // Listen for real-time messages
+ useActorEvent({ actor, event: "messageReceived" }, (message) => {
+ setMessages(prev => [...prev, message as Message]);
+ setIsLoading(false);
+ });
+
+ const handleSendMessage = async () => {
+ if (actor && input.trim()) {
+ setIsLoading(true);
+
+ // Add user message to UI immediately
+ const userMessage = { role: "user", content: input } as Message;
+ setMessages(prev => [...prev, userMessage]);
+
+ // Send to actor (AI response will come through the event)
+ await actor.sendMessage(input);
+ setInput("");
+ }
+ };
+
+ return (
+
+
+ {messages.length === 0 ? (
+
+ Ask the AI assistant a question to get started
+
+ );
+}
+```
diff --git a/docs/snippets/examples/sync-sqlite.mdx b/docs/snippets/examples/sync-sqlite.mdx
new file mode 100644
index 000000000..13d96824d
--- /dev/null
+++ b/docs/snippets/examples/sync-sqlite.mdx
@@ -0,0 +1,68 @@
+```typescript
+import { actor } from "actor-core";
+import { drizzle } from "@actor-core/drizzle";
+import { contacts } from "./schema";
+
+export type Contact = { id: string; name: string; email: string; phone: string; updatedAt: number; }
+
+const contactSync = actor({
+ sql: drizzle(),
+
+ actions: {
+ // Gets changes after the last timestamp (when coming back online)
+ getChanges: async (c, after: number = 0) => {
+ const changes = await c.db
+ .select()
+ .from(contacts)
+ .where(contacts.updatedAt.gt(after));
+
+ return {
+ changes,
+ timestamp: Date.now()
+ };
+ },
+
+ // Pushes new changes from the client & handles conflicts
+ pushChanges: async (c, contactList: Contact[]) => {
+ let changed = false;
+
+ for (const contact of contactList) {
+ // Check if contact exists with a newer timestamp
+ const existing = await c.db
+ .select()
+ .from(contacts)
+ .where(contacts.id.equals(contact.id))
+ .get();
+
+ if (!existing || existing.updatedAt < contact.updatedAt) {
+ // Insert or update the contact
+ await c.db
+ .insert(contacts)
+ .values(contact)
+ .onConflictDoUpdate({
+ target: contacts.id,
+ set: contact
+ });
+
+ changed = true;
+ }
+ }
+
+ if (changed) {
+ // Get all contacts to broadcast
+ const allContacts = await c.db
+ .select()
+ .from(contacts);
+
+ c.broadcast("contactsChanged", {
+ contacts: allContacts
+ });
+ }
+
+ return { timestamp: Date.now() };
+ }
+ }
+});
+
+export default contactSync;
+```
\ No newline at end of file
diff --git a/docs/snippets/examples/tenant-js.mdx b/docs/snippets/examples/tenant-js.mdx
new file mode 100644
index 000000000..c9ddab5f9
--- /dev/null
+++ b/docs/snippets/examples/tenant-js.mdx
@@ -0,0 +1,49 @@
+```typescript
+import { actor } from "actor-core";
+import { authenticate } from "./my-utils";
+
+// Simple tenant organization actor
+const tenant = actor({
+ // Example initial state
+ state: {
+ members: [
+ { id: "user-1", name: "Alice", email: "alice@example.com", role: "admin" },
+ { id: "user-2", name: "Bob", email: "bob@example.com", role: "member" }
+ ],
+ invoices: [
+ { id: "inv-1", amount: 100, date: Date.now(), paid: true },
+ { id: "inv-2", amount: 200, date: Date.now(), paid: false }
+ ]
+ },
+
+ // Authentication
+ createConnState: async (c, { params }) => {
+ const token = params.token;
+ const userId = await authenticate(token);
+ return { userId };
+ },
+
+ actions: {
+ // Get all members
+ getMembers: (c) => {
+ return c.state.members;
+ },
+
+ // Get all invoices (only admin can access)
+ getInvoices: (c) => {
+ // Find the user's role by their userId
+ const userId = c.conn.userId;
+ const user = c.state.members.find(m => m.id === userId);
+
+ // Only allow admins to see invoices
+ if (!user || user.role !== "admin") {
+ throw new UserError("Permission denied: requires admin role");
+ }
+
+ return c.state.invoices;
+ }
+ }
+});
+
+export default tenant;
+```
diff --git a/docs/snippets/examples/tenant-react.mdx b/docs/snippets/examples/tenant-react.mdx
new file mode 100644
index 000000000..b403ec7d5
--- /dev/null
+++ b/docs/snippets/examples/tenant-react.mdx
@@ -0,0 +1,132 @@
+```typescript
+import { createClient } from "actor-core/client";
+import { createReactActorCore } from "@actor-core/react";
+import { useState, useEffect } from "react";
+import type { App } from "../actors/app";
+
+// Create client and hooks
+const client = createClient("http://localhost:6420");
+const { useActor } = createReactActorCore(client);
+
+export function OrgDashboard({ orgId }: { orgId: string }) {
+ // State for data
+ const [members, setMembers] = useState([]);
+ const [invoices, setInvoices] = useState([]);
+ const [error, setError] = useState("");
+
+ // Login as admin or regular user
+ const loginAsAdmin = () => {
+ setToken("auth:user-1"); // Alice is admin
+ };
+
+ const loginAsMember = () => {
+ setToken("auth:user-2"); // Bob is member
+ };
+
+ // Authentication token
+ const [token, setToken] = useState("");
+
+ // Connect to tenant actor with authentication token
+ const [{ actor }] = useActor("tenant", {
+ params: { token },
+ tags: { orgId }
+ });
+
+ // Load data when actor is available
+ useEffect(() => {
+ if (!actor || !token) return;
+
+ const loadData = async () => {
+ try {
+ // Get members (available to all users)
+ const membersList = await actor.getMembers();
+ setMembers(membersList);
+
+ // Try to get invoices (only available to admins)
+ try {
+ const invoicesList = await actor.getInvoices();
+ setInvoices(invoicesList);
+ setError("");
+ } catch (err: any) {
+ setError(err.message);
+ }
+ } catch (err) {
+ console.error("Failed to load data");
+ }
+ };
+
+ loadData();
+ }, [actor, token]);
+
+ // Login screen when not authenticated
+ if (!token) {
+ return (
+
+
Organization Dashboard
+
Choose a login:
+
+
+
+ );
+ }
+
+ return (
+
+
Organization Dashboard
+
Logged in as: {token.split(":")[1]}
+
+ {/* Members Section - available to all users */}
+
+
Members
+
+
+
+
Name
+
Email
+
Role
+
+
+
+ {members.map(member => (
+
+
{member.name}
+
{member.email}
+
{member.role}
+
+ ))}
+
+
+
+
+ {/* Invoices Section - only displayed to admins */}
+
+
Invoices
+ {error ? (
+
{error}
+ ) : (
+
+
+
+
Invoice #
+
Date
+
Amount
+
Status
+
+
+
+ {invoices.map(invoice => (
+
+
{invoice.id}
+
{new Date(invoice.date).toLocaleDateString()}
+
${invoice.amount}
+
{invoice.paid ? "Paid" : "Unpaid"}
+
+ ))}
+
+
+ )}
+
+
+ );
+}
+```
diff --git a/docs/snippets/examples/tenant-sqlite.mdx b/docs/snippets/examples/tenant-sqlite.mdx
new file mode 100644
index 000000000..c5f3700bb
--- /dev/null
+++ b/docs/snippets/examples/tenant-sqlite.mdx
@@ -0,0 +1,53 @@
+```typescript
+import { actor } from "actor-core";
+import { drizzle } from "@actor-core/drizzle";
+import { members, invoices } from "./schema";
+import { authenticate } from "./my-utils";
+
+// Simple tenant organization actor
+const tenant = actor({
+ sql: drizzle(),
+
+ // Authentication
+ createConnState: async (c, { params }) => {
+ const token = params.token;
+ const userId = await authenticate(token);
+ return { userId };
+ },
+
+ actions: {
+ // Get all members
+ getMembers: async (c) => {
+ const result = await c.db
+ .select()
+ .from(members);
+
+ return result;
+ },
+
+ // Get all invoices (only admin can access)
+ getInvoices: async (c) => {
+ // Find the user's role by their userId
+ const userId = c.conn.userId;
+ const user = await c.db
+ .select()
+ .from(members)
+ .where(members.id.equals(userId))
+ .get();
+
+ // Only allow admins to see invoices
+ if (!user || user.role !== "admin") {
+ throw new Error("Permission denied: requires admin role");
+ }
+
+ const result = await c.db
+ .select()
+ .from(invoices);
+
+ return result;
+ }
+ }
+});
+
+export default tenant;
+```
\ No newline at end of file
diff --git a/docs/snippets/landing-comparison-table.mdx b/docs/snippets/landing-comparison-table.mdx
new file mode 100644
index 000000000..dd95b9dca
--- /dev/null
+++ b/docs/snippets/landing-comparison-table.mdx
@@ -0,0 +1,561 @@
+
+
+
+
Feature
+
+
+ ActorCore
+
+
+
+ Durable Objects
+
+
+
+ Socket.io
+
+
+
+ Redis
+
+
+
+ AWS Lambda
+
+
+
+
+
+
+
+
+
+
+ In-Memory State
+
+
+
+ Fast access to in-memory data without complex caching
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ActorCore
+
+
+
+
+
+ Durable Objects
+
+
+
+
+
+ Socket.io
+
+
+
+
+
+ Redis
+
+
+
+ AWS Lambda
+
+
+
+
+
+
+
+
+
+
+ Persisted State
+
+
+
+ Built-in persistence that survives crashes and restarts
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ActorCore
+
+
+
+
+
+ Durable Objects
+
+
+
+ Socket.io
+
+
+
+ Redis
+
+
+
+ AWS Lambda
+
+
+
+
+
+
+
+
+
+
+ Actions
+
+
+
+ Define and call functions that interact with your actors
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ActorCore
+
+
+
+
+
+ Durable Objects
+
+
+
+
+
+ Socket.io
+
+
+
+ Redis
+
+
+
+
+
+ AWS Lambda
+
+
+
+
+
+
+
+
+
+
+ Events (Pub/Sub)
+
+
+
+ Real-time messaging with publish/subscribe patterns
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ActorCore
+
+
+
+
+
+ Durable Objects
+
+
+
+
+
+ Socket.io
+
+
+
+
+
+ Redis
+
+
+
+ AWS Lambda
+
+
+
+
+
+
+
+
+
+
+ Scheduling
+
+
+
+ Run tasks in the future without external schedulers
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ActorCore
+
+
+
+
+
+ Durable Objects
+
+
+
+ Socket.io
+
+
+
+ Redis
+
+
+
+
+
+ AWS Lambda
+
+
+
+
+
+
+
+
+
+
+ Edge Computing
+
+
+
+ Deploy globally for low-latency access from anywhere
+
+
+
+
+
+
+
+ ¹
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ActorCore ¹
+
+
+
+
+
+ Durable Objects
+
+
+
+ Socket.io
+
+
+
+ Redis
+
+
+
+
+
+ AWS Lambda
+
+
+
+
+
+
+
+
+
+
+ No Vendor Lock
+
+
+
+ Run on multiple platforms without rewriting your app
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ActorCore
+
+
+
+ Durable Objects
+
+
+
+
+
+ Socket.io
+
+
+
+
+
+ Redis
+
+
+
+ AWS Lambda
+
+
+
+
+
+
+
+
+
+ =
+ requires significant boilerplate code or external service
+
+
¹ = on supported platforms
+
diff --git a/docs/snippets/landing-faq.mdx b/docs/snippets/landing-faq.mdx
new file mode 100644
index 000000000..363619854
--- /dev/null
+++ b/docs/snippets/landing-faq.mdx
@@ -0,0 +1,103 @@
+import { Icon } from "@/components/Icon";
+
+
+
Frequently Asked Questions
+
Common questions about stateful serverless and ActorCore.
+
+
+
+
+
+
+
+
ActorCore is a framework written in TypeScript that provides high-level functionality. Rivet is an open-source serverless platform written in Rust with features tailored for stateful serverless.
+
You can think of it as ActorCore is to Rivet as Next.js is to Vercel.
+
While Rivet is the primary maintainer of ActorCore, we intend for this to be community driven.
+
+
+
+
+
+
+
Stateful serverless is very similar to actors: it's essentially actors with persistence, and usually doesn't have as rigid constraints on message handling. This makes it more flexible while maintaining the core benefits of the actor model.
+
+
+
+
+
+
+
Stateless serverless works well when you have an external resource that maintains state. Stateful serverless, on the other hand, is almost like a mini-database.
+
Sometimes it makes sense to use stateless serverless to make requests to multiple stateful serverless instances, orchestrating complex operations across multiple state boundaries.
+
+
+
+
+
+
+
By storing state in memory and flushing to a persistence layer, we can serve requests instantly instead of waiting for a round trip to the database. There are additional optimizations that can be made around your state to tune the durability of it.
+
Additionally, data is stored near your users at the edge, ensuring round-trip times of less than 50ms when they request it. This edge-first approach eliminates the latency typically associated with centralized databases.
+
+
+
+
+
+
+
Some software makes sense to separate – e.g., for data lakes or highly relational data. But at the end of the day, data has to be partitioned somewhere at some point.
+
Usually "faster" databases like Cassandra, DynamoDB, or Vitess make consistency tradeoffs to get better performance. Stateful serverless forces you to think about how your data is sharded for better performance, better scalability, and less consistency footguns.
+
+
+
+
+
+
+
OLAP, data lakes, graph databases, and highly relational data are currently not ideal use cases for stateful serverless, though it will get better at handling these use cases over time.
+
+
+
+
+
+
+
Yes, but only as much as storing data in a single database row does. We're working on building out read replicas to allow you to perform read-only actions on actors.
\ No newline at end of file
diff --git a/docs/snippets/landing-manifesto.mdx b/docs/snippets/landing-manifesto.mdx
new file mode 100644
index 000000000..368fbcb96
--- /dev/null
+++ b/docs/snippets/landing-manifesto.mdx
@@ -0,0 +1,21 @@
+
+
Why We Created ActorCore
+
+ Stateful serverless is the future of how applications will be architected.
+
+
+ Startups increasingly build on stateful serverless to ship faster, achieve better performance, and outscale databases like Postgres. The actor model – closely related to stateful serverless – has an established history in frameworks like Elixir, Orleans, and Akka, though these typically involve steep learning curves and complex infrastructure. Cloudflare demonstrates the power of this approach, having built their entire infrastructure – including R2, Workflows, and Queues – on their stateful serverless engine called Durable Objects.
+
+
+ With years of experience in gaming infrastructure, we've seen firsthand how the stateful serverless model excels. After building numerous systems like matchmaking, chat, presence, and social networks using stateful serverless, we're convinced it's hands down the best way to build applications. However, the ecosystem lacks accessibility and resources.
+
+
+ To popularize stateful serverless, we decided to build something that works for everyone. No vendor lock-in, no steep learning curve, and a community-driven approach that brings the best ideas from different ecosystems together.
+
+
+ At Rivet, we maintain an open-source runtime to run stateful serverless workloads – including ActorCore. We see maintaining ActorCore as a rising tide: more people will build applications this way, and we hope to provide the best deployment, monitoring, and collaboration solution for this architecture.
+
\ No newline at end of file
diff --git a/docs/snippets/landing-quotes.mdx b/docs/snippets/landing-quotes.mdx
new file mode 100644
index 000000000..1c74dcb75
--- /dev/null
+++ b/docs/snippets/landing-quotes.mdx
@@ -0,0 +1,132 @@
+{/* Quotes Layout */}
+
\ No newline at end of file
diff --git a/docs/snippets/landing-snippets.mdx b/docs/snippets/landing-snippets.mdx
new file mode 100644
index 000000000..af4ea71ea
--- /dev/null
+++ b/docs/snippets/landing-snippets.mdx
@@ -0,0 +1,594 @@
+import ChatRoomJs from "/snippets/examples/chat-room-js.mdx";
+import ChatRoomSqlite from "/snippets/examples/chat-room-sqlite.mdx";
+import ChatRoomReact from "/snippets/examples/chat-room-react.mdx";
+import AiAgentJs from "/snippets/examples/ai-agent-js.mdx";
+import AiAgentSqlite from "/snippets/examples/ai-agent-sqlite.mdx";
+import AiAgentReact from "/snippets/examples/ai-agent-react.mdx";
+import SyncJs from "/snippets/examples/sync-js.mdx";
+import SyncSqlite from "/snippets/examples/sync-sqlite.mdx";
+import SyncReact from "/snippets/examples/sync-react.mdx";
+import TenantJs from "/snippets/examples/tenant-js.mdx";
+import TenantSqlite from "/snippets/examples/tenant-sqlite.mdx";
+import TenantReact from "/snippets/examples/tenant-react.mdx";
+import DatabaseJs from "/snippets/examples/database-js.mdx";
+import DatabaseSqlite from "/snippets/examples/database-sqlite.mdx";
+import DatabaseReact from "/snippets/examples/database-react.mdx";
+import CrdtJs from "/snippets/examples/crdt-js.mdx";
+import CrdtSqlite from "/snippets/examples/crdt-sqlite.mdx";
+import CrdtReact from "/snippets/examples/crdt-react.mdx";
+import DocumentJs from "/snippets/examples/document-js.mdx";
+import DocumentSqlite from "/snippets/examples/document-sqlite.mdx";
+import DocumentReact from "/snippets/examples/document-react.mdx";
+import StreamJs from "/snippets/examples/stream-js.mdx";
+import StreamSqlite from "/snippets/examples/stream-sqlite.mdx";
+import StreamReact from "/snippets/examples/stream-react.mdx";
+import GameJs from "/snippets/examples/game-js.mdx";
+import GameSqlite from "/snippets/examples/game-sqlite.mdx";
+import GameReact from "/snippets/examples/game-react.mdx";
+import RateJs from "/snippets/examples/rate-js.mdx";
+import RateSqlite from "/snippets/examples/rate-sqlite.mdx";
+import RateReact from "/snippets/examples/rate-react.mdx";
+
+
+
+
+ {/* Scroll Bars */}
+
+
+
+
+
+
+
+
+
+ {/* Examples */}
+
+
Example
+
+
+ Chat Room
+
+
+
+ AI Agent
+
+
+
+ Local-First Sync
+
+
+
+ Per-Tenant Saas
+
+
+
+ Per-User Databases
+
+
+
+ Yjs CRDT
+
+
+
+ Collaborative Document
+
+
+
+ Stream Processing
+
+
+
+ Multiplayer Game
+
+
+
+ Rate Limiter
+
+
+
+ {/* State */}
+
+
State
+
JavaScript
+
SQLiteAvailable In April
+
+
+
+ {/* Code */}
+
+ {/* Chat Room */}
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+ {/* AI Agent */}
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+ {/* Local-First Sync */}
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+ {/* Per-Tenant SaaS */}
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+ {/* Per-User Databases */}
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+ {/* Yjs CRDT */}
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+ {/* Collaborative Document */}
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+ {/* Stream Processing */}
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+ {/* Multiplayer Game */}
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+ {/* Rate Limiter */}
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+
+
+
+
+ actor.ts
+ Runs on the server
+
+
+
+
+
+
+
+
+ App.tsx
+ Runs in the browser
+
+
+
+
+
+
+
+
+
+
+ We're working on publishing full examples related to these snippets. If you find an error, please create an issue.
+
+
diff --git a/docs/snippets/landing-tech.mdx b/docs/snippets/landing-tech.mdx
new file mode 100644
index 000000000..fdd524af5
--- /dev/null
+++ b/docs/snippets/landing-tech.mdx
@@ -0,0 +1,260 @@
+
+
+
+
Runs On Your Stack
+
Deploy ActorCore anywhere - from serverless platforms to your own infrastructure with our flexible runtime options.