Repository navigation
docs(actors): sync from rivet-dev/rivet #124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
136 changes: 136 additions & 0 deletions
136
vendor/actors/docs/content/guides/a-radically-simpler-architecture.mdx
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,136 @@ | ||
| --- | ||
| title: "A Radically Simpler Architecture" | ||
| description: "Why actors eliminate complexity instead of managing it, and how merging state and compute removes the biggest source of latency in modern applications." | ||
| --- | ||
|
|
||
| The typical backend architecture follows a familiar pattern. A web server connects to a database. Traffic grows, so you add Redis for caching. You need async processing, so you add Kafka. You need coordination, so you add distributed locks. | ||
|
|
||
| ## Every Solution Creates A Problem | ||
|
|
||
| As you add components to your architecture to solve problems as you scale, in turn you create new problems for yourself: | ||
|
|
||
| - **Caching**: brings cache invalidation bugs, stale data, and thundering herd problems. | ||
| - **Message queues**: bring message ordering issues, exactly-once delivery problems, and dead letter queue monitoring. | ||
| - **Pub/sub systems**: bring subscription management complexity, message replay challenges, and coordination overhead. | ||
| - **Distributed locks**: bring deadlocks, lock timeouts, and split-brain scenarios. | ||
| - **Multiple services**: bring distributed transactions, eventual consistency, and network partition handling. | ||
|
|
||
| Worse, these are bugs you can't unit test for. They're emergent behaviors that only appear under load when it matters most. | ||
|
|
||
| Yet it's accepted as _the way things must be_. Nobody got fired for adding Kafka, Redis, and RabbitMQ to the stack. The fact that each one brings its own failure modes is assumed to be the growing pains of any successful business. | ||
|
|
||
| <img src="https://assets.rivet.dev/website/learn/act-1/scene-1/old-arch.png" alt="Traditional backend architecture with separate layers for web server, cache, database, and message queue" className="theme-diagram-invert" /> | ||
|
|
||
| --- | ||
|
|
||
| ## How We Got Here | ||
|
|
||
| Looking back at the very first thing you did when starting your application: setting up a web server and a database. The way you've designed your app is through an **age-old practice of "separating state and compute."** | ||
|
|
||
| We've been doing it this way since the 1980s, when client-server architecture put databases on their own machines. | ||
|
|
||
| This came from the fact that computers were slow and had limited resources. Running application code and database operations on the same machine meant they'd fight over CPU and memory, making both perform poorly. Separating them protected databases from compute overhead. | ||
|
|
||
| This tradeoff made sense when CPU and memory were severely limited, but the pattern outlived its purpose. As traffic grew, we added caching layers, message queues, and distributed locks, each solving a problem from the last without questioning the original assumption of how we got here. | ||
|
|
||
| ## 40 Years Later | ||
|
|
||
| Those constraints from forty years ago no longer apply to today's servers. Modern CPUs are orders of magnitude faster, and memory is abundant and cheap. **Application bottlenecks have shifted from local compute to network latency and locks.** | ||
|
|
||
| This is best demonstrated with a simple comparison between a real-world Postgres query over the network versus a SQLite query on the same machine: A Postgres query over the network takes 1-10ms over LAN. The same query on a local SQLite database running in the same process as your application takes 0.01-0.1ms, **roughly 100x faster**. (These benchmarks are heavily dependent on the workload, this is a conservative performance number for SQLite.) | ||
|
|
||
| That 100x difference is not about switching to a marginally different database, it's about rethinking your architecture for modern computers by eliminating the centralized database completely in favor of databases colocated with your compute. **Combining compute and state removes the biggest sources of latency in modern applications.** | ||
|
|
||
| --- | ||
|
|
||
| ## The Actor Model: Combining Compute and State | ||
|
|
||
| Actors take the completely opposite approach to "separating compute and state:" they **merge state and compute together**. | ||
|
|
||
| Each actor's **state is isolated to itself** and cannot be read by any other actors. Instead, you communicate with actors over the network via actions. | ||
|
|
||
| They're like mini-servers: they can accept and respond to network requests and even send network requests themselves. They remain running as a long-lived process with in-memory state until they decide to go to sleep. | ||
|
|
||
| In addition to performance and complexity benefits, this architecture **eliminates entire categories of bugs by design.** No network to the database means no network partitions. No shared state means no race conditions. No locks means no deadlocks. | ||
|
|
||
| <img src="https://assets.rivet.dev/website/learn/act-1/scene-1/actor-arch.png" alt="Actor architecture showing compute and state combined in isolated actors" className="max-h-[500px] theme-diagram-invert" /> | ||
|
|
||
| ## The 4 Properties That Eliminate Complexity | ||
|
|
||
| By combining compute and state, actors present a few key properties that eliminate entire categories of problems. These properties are the core of the design patterns that we'll discuss in further articles. | ||
|
|
||
| ### Isolated State | ||
|
|
||
| Each actor **manages its own private state**. No other process can access an actor's state. | ||
|
|
||
| This eliminates race conditions (can't happen when only one process touches the data), deadlocks (no locks means no deadlocks), cache invalidation (no shared cache to invalidate), and read-after-write inconsistencies (your writes are immediately visible to you). | ||
|
|
||
| Debugging becomes straightforward: the actor's state is the single source of truth. There's no need to reconstruct state from multiple systems or reason about eventual consistency across caches, databases, and message queues. | ||
|
|
||
| As your app grows, new features affect a limited number of actors which have a limited scope. Changes don't ripple through shared state across services or risk breaking unrelated parts of your system. | ||
|
|
||
| <img src="https://assets.rivet.dev/website/learn/act-1/scene-1/isolated-state.png" alt="Diagram showing actors with isolated state that cannot be accessed by other processes" className="max-h-[500px]" /> | ||
|
|
||
| ### Message-Based Communication | ||
|
|
||
| Actors **talk through actions and events**, not direct state access. This makes it easier to scale actors since they can scale horizontally across multiple machines and still communicate efficiently. | ||
|
|
||
| Messages sent to actors are **automatically queued and processed sequentially**. This almost always eliminates the need for external message queues since backpressure, ordering, and delivery are handled by the actor runtime itself. | ||
|
|
||
| Crucially, **actors frequently talk to each other** to build larger systems that scale well. We'll be talking a lot about patterns like this in this course. | ||
|
|
||
| <img src="https://assets.rivet.dev/website/learn/act-1/scene-1/message-passing.png" alt="Diagram showing actors communicating through messages and events" className="max-h-[500px]" /> | ||
|
|
||
| ### Location Transparency | ||
|
|
||
| Actors can run on any machine in a cluster and still **send messages between actors regardless of the host machine**. Rivet automatically handles intelligent load balancing of actors and routing between actors. | ||
|
|
||
| The same code will run whether you have 1 or 1,000 machines without complex network configuration, DNS, or pub/sub systems. | ||
|
|
||
| <img src="https://assets.rivet.dev/website/learn/act-1/scene-1/location-transparency.png" alt="Diagram showing actors communicating across different machines in a cluster" /> | ||
|
|
||
| ### Horizontal Scaling | ||
|
|
||
| Actors are designed to transparently interact with other actors regardless of what machine they run on. This makes actors easy to scale by **just adding more machines** for actors to run on when you need it. | ||
|
|
||
| Load spreads naturally since actors are small, lightweight units. No complex sharding logic or coordination needed. | ||
|
|
||
| <img src="https://assets.rivet.dev/website/learn/act-1/scene-1/horizontal-scaling.png" alt="Diagram showing actors distributed across multiple machines for horizontal scaling" className="max-h-[500px]" /> | ||
|
|
||
| --- | ||
|
|
||
| ## Putting It All Together: A Radically Simpler Architecture | ||
|
|
||
| When you build your backend with actors, the four properties listed remove the need for: | ||
|
|
||
| - **Redis/Memcached**: Caching is built-in (state already lives in-memory with compute). | ||
| - **Kafka/RabbitMQ/SQS**: Message queueing, events, and async messaging are built-in to the actor runtime. | ||
| - **NATS/Redis Streams**: Pub/sub is built-in to actors through message passing and events. | ||
| - **Consul/etcd/ZooKeeper**: No distributed coordination needed, actors encapsulate their own state and the runtime handles discovery and routing automatically. | ||
| - **Istio/Linkerd**: Actors handle routing and discovery automatically. | ||
| - **Database sharding**: Actors distribute themselves automatically. No shard keys, no rebalancing logic, no cross-shard queries. | ||
|
|
||
| ## If Actors Are So Great, Why Aren't They Everywhere? | ||
|
|
||
| If you've reached this point and are unfamiliar with the actor model, you're probably asking this exact question. It all sounds a little _too_ rosy. | ||
|
|
||
| The truth is that actors _are_ used widely, just not visibly. Large enterprises with engineers who've spent years wrestling with traditional architectures have long since adopted them. The pattern has proven itself at massive scale: | ||
|
|
||
| - WhatsApp (notoriously acquired for $19B running Erlang/OTP with only 35 engineers) | ||
| - Discord | ||
| - X | ||
| - PayPal | ||
| - FoundationDB (powering Apple, Snowflake, DataDog) | ||
|
|
||
| So why hasn't the actor model spread to smaller teams and mainstream development? | ||
|
|
||
| This mirrors TypeScript's trajectory. It started as a niche tool for large codebases, and most developers dismissed it as unnecessary overhead with poor tooling. But as more developers felt the pain of loose typing at scale, adoption grew. Today, TypeScript is a non-negotiable for many teams because of that collective suffering. | ||
|
|
||
| Actors are on the same trajectory. The pain of distributed systems complexity is becoming impossible to ignore. | ||
|
|
||
| Other ecosystems have had mature actor frameworks for years. Erlang has OTP, Java has Akka, C# has Microsoft Orleans. But TypeScript has been the missing piece until recently with: | ||
|
|
||
| - **Rivet Actors**: Open-source actor infrastructure for TypeScript | ||
| - **Cloudflare Durable Objects**: Leverages Cloudflare's existing network & JavaScript runtime |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| --- | ||
| title: "Agent App Builders" | ||
| description: "Ship a backend per user the moment the agent generates one, on Rivet Actors." | ||
| --- | ||
|
|
||
| When your product's output is an app, the backend has to appear the moment the agent writes it. It needs one per user, isolated from every other user, live enough to click on in the same chat turn. Rivet gives each generated app its own Actor and its own persistence, so provisioning is a key lookup rather than a deploy pipeline. | ||
|
|
||
| ## Two shapes, depending on whose code it is | ||
|
|
||
| **Run the generated code in place.** A code agent Actor keeps the chat history and the current revision of the generated source in its own [SQLite database](/actors/docs/sqlite), so every key has an isolated transcript and an isolated codebase. A dynamic Actor fetches that source from the matching code agent and executes it in a [Secure Exec](/secure-exec) sandbox, so the user can call their app the moment it compiles and iterate on it without a redeploy. | ||
|
|
||
| **Deploy it into its own namespace.** For code that should outlive the chat, create a Rivet namespace per user, package the generated `registry.ts` and frontend into a project, deploy it to a serverless host such as [Freestyle](/docs/deploy/self-host/workers/freestyle), and configure that host as the namespace's worker. Each user's Actors then run under their own credentials, in their own namespace, with nothing shared but the control plane. | ||
|
|
||
| ## Start from the examples | ||
|
|
||
| <CardGroup> | ||
| <Card title="AI-Generated Actor" href="https://github.com/rivet-dev/rivet/tree/main/examples/ai-generated-actor" target="_blank"> | ||
| Chat, a Monaco editor, and a live test panel: generate Actor code, persist it in per-Actor SQLite, and invoke it in an isolated dynamic runtime. | ||
| </Card> | ||
| <Card title="User and AI Generated Actors with Freestyle" href="https://github.com/rivet-dev/rivet/tree/main/examples/ai-and-user-generated-actors-freestyle" target="_blank"> | ||
| Provision a namespace per user, package the generated source, and deploy it to Freestyle as that namespace's worker. | ||
| </Card> | ||
| </CardGroup> | ||
|
|
||
| ## Next steps | ||
|
|
||
| - [Dynamic Apps](/dynamic-apps/docs): the managed version of this pattern: deploy a generated app and backend per user, with SQLite, workflows, and multiplayer built in. | ||
| - [Actor Keys](/actors/docs/keys): how one key per user becomes one backend per user. | ||
| - [Deploy](/docs/deploy/): Rivet Cloud, your own cloud, or self-hosted. |
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,28 @@ | ||
| --- | ||
| title: "Coding Agents" | ||
| description: "Give each coding session a sandbox, a filesystem, and durable memory on Rivet Actors." | ||
| --- | ||
|
|
||
| A coding agent needs somewhere to run commands and somewhere to remember what it already did. On Rivet both belong to the same object: one Actor per coding session, holding the transcript and owning a sandbox whose filesystem survives between turns. | ||
|
|
||
| ## One Actor per session | ||
|
|
||
| Key the agent Actor by session id, so `agent.getOrCreate([sessionId])` always reaches the same conversation (see [Actor Keys](/actors/docs/keys)). The transcript, the run status, and the sandbox session id live in that Actor's persistent [state](/actors/docs/state), so a restart resumes the session instead of starting a new one. There is no separate session database to keep in sync. | ||
|
|
||
| ## The sandbox is an Actor too | ||
|
|
||
| The sandbox comes from `rivetkit/sandbox` and shares the agent's key, which makes the agent-to-sandbox mapping implicit in the key space. It runs the coding agent (Codex by default) inside Docker, Daytona, or E2B, and owns the filesystem and process state for that session. The agent Actor submits a prompt, awaits the sandbox round trip, and broadcasts the result to connected clients as an [event](/actors/docs/events). | ||
|
|
||
| ## Start from the example | ||
|
|
||
| <CardGroup> | ||
| <Card title="Sandbox Coding Agent" href="https://github.com/rivet-dev/rivet/tree/main/examples/sandbox-coding-agent" target="_blank"> | ||
| A React chat UI backed by a `rivetkit/sandbox` Actor: one sandbox, filesystem, and resumable session per coding agent. | ||
| </Card> | ||
| </CardGroup> | ||
|
|
||
| ## Next steps | ||
|
|
||
| - [AI Agent](/guides/ai-agent): memory, queued message handling, and streaming responses in depth. | ||
| - [Sandboxes](/agentos/docs/sandboxes): what a sandbox can run and how it is isolated. | ||
| - [Deploy](/docs/deploy/): run this on Rivet Cloud, in your own cloud, or self-hosted. |
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| --- | ||
| title: "Company-Specific Agents" | ||
| description: "Run agents against internal data, inside your own network, on Rivet Actors." | ||
| --- | ||
|
|
||
| An agent that works on internal data is constrained less by the model than by where the data is allowed to go. Rivet answers that by keeping each agent's memory in the agent itself and letting you run the whole control plane inside your own network. | ||
|
|
||
| ## Memory lives in the agent | ||
|
|
||
| Give every conversation its own Actor, keyed by agent or conversation id. The transcript and status live in the Actor's persistent [state](/actors/docs/state), and each model call rebuilds the prompt from that state plus a system prompt. Memory and inference input are the same data, with no vector store or session table beside it. Prompts arrive on the Actor's [queue](/actors/docs/queues) and the `run` hook consumes them serially, so one conversation never has two model calls in flight. Tokens stream back to clients as [events](/actors/docs/events). | ||
|
|
||
| Because the memory is the Actor, the blast radius of any single agent is one Actor: it can only read what its own tools hand it, and its transcript is never pooled with another tenant's. | ||
|
|
||
| ## It runs where your data is | ||
|
|
||
| Internal agents usually cannot call out to a vendor's control plane. Two deployments keep everything inside your perimeter: | ||
|
|
||
| - [Bring Your Own Cloud](/docs/deploy/byoc/): Rivet deploys and operates the control plane inside your VPC. No inbound management connection, and you choose whether it is reachable publicly or only privately. | ||
| - [Self-host](/docs/deploy/self-host/control-plane/): you run the control plane yourself on Kubernetes, ECS, Docker Compose, or a VM. | ||
|
|
||
| ## Start from the example | ||
|
|
||
| <CardGroup> | ||
| <Card title="AI Agent" href="https://github.com/rivet-dev/rivet/tree/main/examples/ai-agent" target="_blank"> | ||
| Queue-driven Actor agents with streaming Vercel AI SDK responses, one Actor per agent. | ||
| </Card> | ||
| </CardGroup> | ||
|
|
||
| ## Next steps | ||
|
|
||
| - [AI Agent](/guides/ai-agent): the memory, queue, and streaming patterns in depth. | ||
| - [Authentication](/docs/authentication): gate which users reach which agent. | ||
| - [BYOC quickstart](/docs/deploy/byoc/quickstart): stand up the control plane in your own VPC. |
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🔴 High · Update the website consumer for the renamed guides contract
This renames the sidebar section and content directory from
learntoguides, but the website still readsactors.learninsrc/sitemap/products.tsand only routes content IDs underactors/learninsrc/pages/guides/[...slug].astro. Consequently the migrated cookbook pages (including/guides/ai-agent) disappear from both static paths and navigation, while the four newly vendored guides are ignored in favor of stale duplicate files undersrc/content/guides. Update the routing/sidebar contract to consumeactors.guidesandactors/guides/*(and remove the duplicate website-owned entries), or preserve the old bundle shape until those consumers are migrated.