Show HN: Durable Actors – OSS Durable Objects with configurable compute
🚀 Durable Actors: Open-Source Stateful Compute
Durable Actors is a powerful, open-source alternative to Cloudflare Durable Objects. It allows developers to deploy stateful serverless functions without the typical constraints of vendor lock-in, restrictive memory caps, or a lack of visibility.
By providing a foundational layer that handles persistence, coordination, and distributed infrastructure, Durable Actors enable the creation of complex, real-time systems—such as collaborative editors or AI-driven chat bots—with ease.
What is a Durable Actor? It is a class-based entity that combines durable state (data that persists across crashes and restarts) with serialized execution (guaranteeing that messages are processed one at a time to prevent race conditions).
🛠️ Core Architecture & Logic
At its heart, a Durable Actor manages state transitions. Mathematically, we can view the actor's state evolution as: where represents the persisted state and the process is executed sequentially.
Key Capabilities
- Persistence: Use the
@Persisteddecorator to ensure data survives interruptions. - Concurrency Control: Serialized execution ensures that if multiple users edit a document (like in Notion), the changes are coordinated perfectly.
- Efficiency: The
@Interleavedecorator allows the runtime to handle other requests while an actor is awaiting an asynchronous operation (e.g., an LLM API call).
Comparison: Traditional Serverless vs. Durable Actors
| Feature | Standard Serverless | Durable Actors |
|---|---|---|
| State | Stateless (requires external DB) | Built-in |
| Execution | Parallel/Concurrent | Serialized (per Actor ID) |
| Lock-in | High (Provider specific) | Low (OSS / Self-hostable) |
| Coordination | Complex (Distributed Locks) | Simple (Single-threaded logic) |
🚀 Getting Started
Follow this checklist to spin up your first actor project:
- Initialize the project
- Install dependencies
- Launch the local development server
- Define your actor logic
- Generate type-safe clients
1. Installation & Setup
Run the following commands in your terminal:
npx durable-actors init my-actors
cd my-actors
npm install
npx durable-actors dev # Starts the local runtime
2. Defining an Actor
Actors are defined in src/actors.ts. Here is an example of a ChatHistory actor that integrates with an AI SDK:
import { openai } from "@ai-sdk/openai";
import { streamText } from "ai";
import { Actor, type ActorSocket, Interleave, Persisted } from "durable-actors";
type Message = { role: "user" | "assistant"; content: string };
export class ChatHistory extends Actor<null, string, Message[]> {
@Persisted
messages: Message[] = [];
async onConnect(socket: ActorSocket<null, Message[]>) {
// Send existing history upon connection
socket.send(this.messages);
}
@Interleave
async onMessage(_socket: ActorSocket<null, Message[]>, _data: Message[], text: string) {
this.messages.push({ role: "user", content: text });
const result = streamText({
model: openai("gpt-5-mini"),
messages: [...this.messages],
});
const reply: Message = { role: "assistant", content: "" };
this.messages.push(reply);
// ... handle streaming logic
}
}
🌐 Integration Flow
Durable Actors use a generated SDK to ensure your backend and frontend remain in sync via type-safe contracts.
Backend Connection
First, generate your client:
npx durable-actors generate
Then, create an endpoint to handle WebSocket handshakes:
import express from "express";
import { actors } from "../generated/index.js";
export const app = express();
app.post("/api/chat/socket", async (_req, res) => {
const grant = await actors.prepareWebsocket({
actorId: "lobby",
metadata: null
});
res.json({ websocketUrl: grant.url });
});
Frontend Implementation
Connect your UI using a standard WebSocket:
import { useEffect, useRef, useState } from "react";
import { createRoot } from "react-dom/client";
import type { actors } from "../generated/index.js";
function Chat() {
const socket = useRef<WebSocket | null>(null);
const [messages, setMessages] = useState<actors.Outgoing[]>([]);
useEffect(() => {
async function connect() {
const response = await fetch("/api/chat/socket", { method: "POST" });
const { websocketUrl } = await response.json();
const ws = new WebSocket(websocketUrl);
ws.onmessage = (e) => setMessages(prev => [...prev, JSON.parse(e.data)]);
socket.current = ws;
}
connect();
return () => socket.current?.close();
}, []);
const send = (form: FormData) => {
const text = String(form.get("message"));
socket.current?.send(JSON.stringify(text));
};
return (
<main>
<div role="log">
{messages.map((m, i) => <p key={i}><strong>{m.content}</strong></p>)}
</div>
<form action={send}>
<input name="message" required />
<button>Send</button>
</form>
</main>
);
}
📈 Observability & Deployment
Durable Actors include built-in tools to monitor your agents in real-time, allowing you to debug state and execution flows without guessing.

Deployment Options:
- Local: Use
durable-actors devfor rapid iteration. - Cloud: Self-host the runtime on GCP for production workloads.
📚 Further Resources
- Examples: Explore the AI Chat, Collaborative Documents, and Chatroom samples.
- Community: Contributions, bug reports, and feature requests are welcome via the contributing guide.
- License: Distributed under the MIT License.
© 2026 Terse | www.useterse.ai