Add AgentOS daemon control plane and maintenance tooling

This commit is contained in:
-Puter
2026-07-19 06:29:51 +05:30
parent a37e0cc3c9
commit ec84c52155
621 changed files with 97578 additions and 15 deletions

View File

@@ -0,0 +1,64 @@
# Node.js & Bun Quickstart
> Source: `src/content/docs/actors/quickstart/backend.mdx`
> Canonical URL: https://rivet.dev/docs/actors/quickstart/backend
> Description: Get started with Rivet Actors in Node.js and Bun
---
Prefer to start from a complete project? See the runnable [`hello-world`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world) example.
## Steps
### Add Rivet Skill to Coding Agent (Optional)
If you're using an AI coding assistant (like Claude Code, Cursor, Windsurf, etc.), add Rivet skills for enhanced development assistance:
```sh
npx skills add rivet-dev/skills
```
### Install Rivet
```sh
npm install rivetkit
```
### Create Actors and Start Server
Create a file with your actors, set up the registry, and start the server:
### Run Server
```sh Node.js
npx tsx --watch index.ts
```
```sh Bun
bun --watch index.ts
```
```sh Deno
deno run --allow-net --allow-read --allow-env --watch index.ts
```
Your server is now running on `http://localhost:6420`. Clients connect directly to the Rivet Engine on this port.
Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live.
### Connect To The Rivet Actor
This code can run either in your frontend or within your backend:
### TypeScript
See the [JavaScript client documentation](/docs/clients/javascript) for more information.
### React
See the [React documentation](/docs/clients/react) for more information.
### Deploy
## Configuration Options
_Source doc path: /docs/actors/quickstart/backend_

View File

@@ -0,0 +1,165 @@
# Cloudflare Workers Quickstart
> Source: `src/content/docs/actors/quickstart/cloudflare.mdx`
> Canonical URL: https://rivet.dev/docs/actors/quickstart/cloudflare
> Description: Set up a Rivet project locally targeting Cloudflare Workers.
---
Set up a Rivet project locally that runs on Cloudflare Workers. The `@rivetkit/cloudflare-workers` package wires the WebAssembly runtime for you.
Prefer to start from a complete project? See the runnable [`hello-world-cloudflare-workers`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-cloudflare-workers) example (with [Hono](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-cloudflare-workers-hono) and [raw router](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-cloudflare-workers-raw) variants).
## Steps
### Install Packages
```sh
npm install rivetkit @rivetkit/cloudflare-workers
npm install --save-dev wrangler
```
### Configure Wrangler
```toml wrangler.toml
name = "rivetkit-cloudflare"
main = "src/index.ts"
compatibility_date = "2025-04-01"
compatibility_flags = ["nodejs_compat"]
```
The `nodejs_compat` flag is required so the runtime can read its connection config from `process.env`.
### Create the Worker
`createHandler` serves the Rivet manager API on `/api/rivet`. Pick how you want to handle your own routes:
- **Default**: Rivet handles everything.
- **Hono**: Mount a [Hono](https://hono.dev) app for your routes (`npm install hono`).
- **Raw**: Provide a `fetch` and route requests yourself.
```ts Default @nocheck
import { actor } from "rivetkit";
import { createHandler } from "@rivetkit/cloudflare-workers";
const counter = actor({
state: { count: 0 },
actions: {
increment: (c, amount = 1) => {
c.state.count += amount;
return c.state.count;
},
},
});
export default createHandler({ use: { counter } });
```
```ts Hono @nocheck
import { actor } from "rivetkit";
import { createClient } from "rivetkit/client";
import { createHandler, setup } from "@rivetkit/cloudflare-workers";
import { Hono } from "hono";
const counter = actor({
state: { count: 0 },
actions: {
increment: (c, amount = 1) => {
c.state.count += amount;
return c.state.count;
},
},
});
// `setup` returns a typed registry, so the client below is fully typed.
const registry = setup({ use: { counter } });
const app = new Hono();
app.get("/", (c) => c.text("Hello from Hono + Rivet Actors!"));
app.post("/increment/:name", async (c) => {
// `createClient` reads RIVET_ENDPOINT from the environment (passed by `rivet
// dev`, or set as a Worker secret in production).
const client = createClient<typeof registry>();
const count = await client.counter.getOrCreate(c.req.param("name")).increment(1);
return c.json({ count });
});
// Rivet keeps `/api/rivet`; Hono handles everything else.
export default createHandler(registry, { fetch: app.fetch });
```
```ts Raw @nocheck
import { actor } from "rivetkit";
import { createClient } from "rivetkit/client";
import { createHandler, setup } from "@rivetkit/cloudflare-workers";
const counter = actor({
state: { count: 0 },
actions: {
increment: (c, amount = 1) => {
c.state.count += amount;
return c.state.count;
},
},
});
// `setup` returns a typed registry, so the client below is fully typed.
const registry = setup({ use: { counter } });
// Rivet keeps `/api/rivet`; your `fetch` handles everything else.
export default createHandler(registry, {
fetch: async (request: Request) => {
const url = new URL(request.url);
if (url.pathname === "/") {
return new Response("Hello from a raw Rivet Worker router!");
}
const increment = url.pathname.match(/^\/increment\/(.+)$/);
if (request.method === "POST" && increment) {
// `createClient` reads RIVET_ENDPOINT from the environment (passed by
// `rivet dev`, or set as a Worker secret in production).
const client = createClient<typeof registry>();
const count = await client.counter.getOrCreate(increment[1]).increment(1);
return Response.json({ count });
}
return new Response("Not found", { status: 404 });
},
});
```
### Run Locally
Start Rivet. The CLI runs the local engine and spawns `wrangler dev` for you:
```sh
npx @rivetkit/cli dev --provider cloudflare
```
Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live.
### Connect To The Rivet Actor
This code can run either in your frontend or within your backend. It connects directly to the local engine on `http://localhost:6420`:
### TypeScript
See the [JavaScript client documentation](/docs/clients/javascript) for more information.
### React
See the [React documentation](/docs/clients/react) for more information.
### Deploy
Ready to ship? See [Deploying to Cloudflare Workers](/docs/deploy/cloudflare).
## Related
- [Quickstart](/docs/actors/quickstart)
- [Deploying to Cloudflare Workers](/docs/deploy/cloudflare)
- [SQLite](/docs/actors/sqlite)
_Source doc path: /docs/actors/quickstart/cloudflare_

View File

@@ -0,0 +1,239 @@
# Effect.ts Quickstart (Beta)
> Source: `src/content/docs/actors/quickstart/effect.mdx`
> Canonical URL: https://rivet.dev/docs/actors/quickstart/effect
> Description: Build a Rivet Actor with the Effect SDK
---
Effect support is in beta. The `@rivetkit/effect` API may change between releases. See the [`hello-world-effect`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-effect) and [`chat-room-effect`](https://github.com/rivet-dev/rivet/tree/main/examples/chat-room-effect) examples for complete runnable projects.
## Steps
### Install Rivet
Add `rivetkit`, the Effect SDK, and its Effect peers:
```sh
npm install rivetkit @rivetkit/effect effect @effect/platform-node
```
### Define Your Actor
Split each actor into a public contract and a server-only implementation so the contract can be imported from client code without leaking server details.
The contract declares the actor and its actions. Actions are standalone values with explicit [`effect/Schema`](https://effect.website/docs/schema/introduction/) payloads and successes, validated end to end:
```ts src/actors/counter/api.ts @nocheck
import { Action, Actor } from "@rivetkit/effect";
import { Schema } from "effect";
export const Increment = Action.make("Increment", {
payload: { amount: Schema.Number },
success: Schema.Number,
});
export const GetCount = Action.make("GetCount", {
success: Schema.Number,
});
export const Counter = Actor.make("Counter", {
actions: [Increment, GetCount],
});
```
The implementation registers the actor with `.toLayer`. The wake function runs once when the actor awakes and returns the action handlers. Persisted state is accessed through a `SubscriptionRef`-like `State` API:
```ts src/actors/counter/live.ts @nocheck
import { Actor, State } from "@rivetkit/effect";
import { Effect, Schema } from "effect";
import { Counter } from "./api.ts";
export const CounterLive = Counter.toLayer(
Effect.fnUntraced(function* ({ rawRivetkitContext, state }) {
return Counter.of({
Increment: Effect.fnUntraced(function* ({ payload }) {
const next = yield* State.updateAndGet(state, (current) => ({
count: current.count + payload.amount,
})).pipe(Effect.orDie);
// Broadcast the new value to every connected client.
rawRivetkitContext.broadcast("newCount", next.count);
return next.count;
}),
GetCount: () =>
State.get(state).pipe(
Effect.map((current) => current.count),
Effect.orDie,
),
});
}),
{
state: {
schema: Schema.Struct({ count: Schema.Number }),
initialValue: () => ({ count: 0 }),
},
name: "Counter",
icon: "calculator",
},
);
```
### Serve The Registry
Compose the actor layers and serve them with `Registry.serve`. `Registry.layer()` reads engine config from the environment, and the actor layer is provided a `Client` so actors can call other actors:
```ts src/main.ts @nocheck
import { NodeRuntime } from "@effect/platform-node";
import { Client, Registry } from "@rivetkit/effect";
import { Layer } from "effect";
import { CounterLive } from "./actors/counter/live.ts";
const endpoint = process.env.RIVET_ENDPOINT ?? "http://127.0.0.1:6420";
const ActorsLayer = CounterLive.pipe(Layer.provide(Client.layer({ endpoint })));
const MainLayer = Registry.serve(ActorsLayer).pipe(Layer.provide(Registry.layer()));
// Keeps the layer alive. Tears down on SIGINT/SIGTERM.
Layer.launch(MainLayer).pipe(NodeRuntime.runMain);
```
### Run The Server
Set `RIVET_RUN_ENGINE=1` to spawn a local Rivet Engine alongside the server. The engine binary is downloaded and cached the first time you run, so there is nothing else to install:
```sh
RIVET_RUN_ENGINE=1 npx tsx --watch src/main.ts
```
Your server now connects to the Rivet Engine on `http://localhost:6420`. Clients connect directly to the engine on this port.
Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live.
To point at a remote engine instead, set `RIVET_ENDPOINT=https://...` and omit `RIVET_RUN_ENGINE`.
### Connect To The Rivet Actor
This code can run either in your frontend or within your backend:
### Effect
The Effect client imports the same actor contract from your registry. `Counter.client` yields a typed accessor backed by the client layer:
```ts src/client.ts @nocheck
import { NodeRuntime } from "@effect/platform-node";
import { Client } from "@rivetkit/effect";
import { Effect } from "effect";
import { Counter } from "./actors/counter/api.ts";
const program = Effect.gen(function* () {
const counter = (yield* Counter.client).getOrCreate("my-counter");
const count = yield* counter.Increment({ amount: 3 });
yield* Effect.log(`New count: ${count}`);
const total = yield* counter.GetCount();
yield* Effect.log(`Total: ${total}`);
});
const ClientLayer = Client.layer({ endpoint: "http://localhost:6420" });
program.pipe(Effect.provide(ClientLayer), NodeRuntime.runMain);
```
With the server still running, start the client in another terminal:
```sh
npx tsx src/client.ts
```
See the [`chat-room-effect`](https://github.com/rivet-dev/rivet/tree/main/examples/chat-room-effect) example for a larger project with typed errors and actor-to-actor calls.
### TypeScript
A plain RivetKit client can call your Effect actor by name through the same engine. Actor and action names are resolved at runtime, so the client is untyped here:
```ts client.ts @nocheck
import { createClient } from "rivetkit/client";
const client = createClient("http://localhost:6420");
const counter = client.Counter.getOrCreate(["my-counter"]);
const count = await counter.Increment({ amount: 3 });
console.log("New count:", count);
```
See the [JavaScript client documentation](/docs/clients/javascript) for more information.
### Deploy
## Feature Support
The Effect SDK wraps the most common actor features with typed, schema-validated APIs. Everything else is still fully usable through the raw RivetKit context (see [Raw Escape Hatch](#raw-escape-hatch) below), so no feature is off limits, it just isn't typed yet.
| Feature | Effect-native API | Access |
| --- | --- | --- |
| Actor contract & actions | `Actor.make`, `Action.make` | Typed |
| Persisted state | `State.get` / `set` / `update` / `updateAndGet` / `changes` | Typed |
| Typed client | `Actor.client`, `Client.layer` | Typed |
| Typed errors | `RivetError` | Typed |
| Logging | `RivetLogger` | Typed |
| Sleep request | `Actor.Sleep` | Typed |
| Actor address (`actorId` / `name` / `key`) | `Actor.CurrentAddress` | Typed |
| Registry serve / test / web handler | `Registry` | Typed |
| [Events & broadcast](/docs/actors/events) | Not yet wrapped | `rawRivetkitContext.broadcast(...)` |
| [Schedule](/docs/actors/schedule) | Not yet wrapped | `rawRivetkitContext.schedule.*` |
| [Embedded SQLite](/docs/actors/sqlite) | Not yet wrapped | `rawRivetkitContext.db.execute(...)` |
| [Destroy](/docs/actors/lifecycle) | Not yet wrapped | `rawRivetkitContext.destroy()` |
| Queues, connections, vars, alarms | Not yet wrapped | `rawRivetkitContext.*` |
| Lifecycle hooks (`onSleep` / `onDestroy`) | Not yet wrapped | `rawRivetkitContext.*` |
| Raw HTTP / WebSocket handlers | Not yet wrapped | `rawRivetkitContext.*` |
### Raw Escape Hatch
Every wake function receives `rawRivetkitContext`, the underlying RivetKit [actor context](/docs/actors). Reach for it to use any feature that does not have a typed wrapper yet. The typed `state` argument and the raw context point at the same actor, so you can mix both:
```ts src/actors/counter/live.ts @nocheck
export const CounterLive = Counter.toLayer(
Effect.fnUntraced(function* ({ rawRivetkitContext, state }) {
return Counter.of({
Increment: Effect.fnUntraced(function* ({ payload }) {
// Typed state wrapper
const next = yield* State.updateAndGet(state, (current) => ({
count: current.count + payload.amount,
})).pipe(Effect.orDie);
// Untyped features run through the raw context
rawRivetkitContext.broadcast("newCount", next.count);
rawRivetkitContext.schedule.after(1_000, "tick", {});
return next.count;
}),
});
}),
{
state: {
schema: Schema.Struct({ count: Schema.Number }),
initialValue: () => ({ count: 0 }),
},
name: "Counter",
},
);
```
Calls through `rawRivetkitContext` are not validated by `effect/Schema` and their payloads are typed as they are in the base RivetKit API.
## Next Steps
- [Actions](/docs/actors/actions) — Define the RPC surface clients call on your actor.
- [State](/docs/actors/state) — Persist and load actor state across sleeps and restarts.
- [Events](/docs/actors/events) — Broadcast realtime updates to connected clients.
_Source doc path: /docs/actors/quickstart/effect_

View File

@@ -0,0 +1,117 @@
# Next.js Quickstart
> Source: `src/content/docs/actors/quickstart/next-js.mdx`
> Canonical URL: https://rivet.dev/docs/actors/quickstart/next-js
> Description: Get started with Rivet Actors in Next.js
---
### Add Rivet Skill to Coding Agent (Optional)
If you're using an AI coding assistant (like Claude Code, Cursor, Windsurf, etc.), add Rivet skills for enhanced development assistance:
```sh
npx skills add rivet-dev/skills
```
### Create a Next.js App
```sh
npx create-next-app@latest my-app
cd my-app
```
### Install RivetKit
### Create an Actor
Create a file at `src/rivet/registry.ts` with a simple counter actor:
### Setup Rivet API route
Create a file at `src/app/api/rivet/[...all]/route.ts` to setup the API routes:
```ts src/app/api/rivet/[...all]/route.ts @nocheck
import { toNextHandler } from "@rivetkit/next-js";
import { registry } from "@/rivet/registry";
export const maxDuration = 300;
export const { GET, POST, PUT, DELETE, PATCH, HEAD, OPTIONS } = toNextHandler(registry);
```
### Use the Actor in a component
Create a Counter component and add it to your page:
```tsx src/components/Counter.tsx @nocheck
"use client";
import { createRivetKit } from "@rivetkit/next-js/client";
import type { registry } from "@/rivet/registry";
import { useState } from "react";
export const { useActor } = createRivetKit<typeof registry>({
endpoint:
process.env.NEXT_PUBLIC_RIVET_ENDPOINT ?? "http://localhost:3000/api/rivet",
namespace: process.env.NEXT_PUBLIC_RIVET_NAMESPACE,
token: process.env.NEXT_PUBLIC_RIVET_TOKEN,
});
export function Counter() {
const [count, setCount] = useState(0);
// Get or create a counter actor for the key "my-counter"
const counter = useActor({
name: "counter",
key: ["my-counter"]
});
// Listen to realtime events
counter.useEvent("newCount", (x: number) => setCount(x));
const increment = async () => {
// Call actions
await counter.connection?.increment(1);
};
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
```
```tsx src/app/page.tsx @nocheck
import { Counter } from "@/components/Counter";
export default function Home() {
return (
<main>
<h1>My App</h1>
<Counter />
</main>
);
}
```
For information about the Next.js client API, see the [React Client API Reference](/docs/clients/react).
### Run Locally
Start the Next.js dev server. Rivet auto-starts a local engine alongside it:
```sh
npm run dev
```
Open `http://localhost:3000` in your browser to use the app.
Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live.
### Deploy to Vercel
See the [Vercel deployment guide](/docs/deploy/vercel) for detailed instructions on deploying your Rivet app to Vercel.
_Source doc path: /docs/actors/quickstart/next-js_

View File

@@ -0,0 +1,82 @@
# React Quickstart
> Source: `src/content/docs/actors/quickstart/react.mdx`
> Canonical URL: https://rivet.dev/docs/actors/quickstart/react
> Description: Build realtime React applications with Rivet Actors
---
## Steps
### Add Rivet Skill to Coding Agent (Optional)
If you're using an AI coding assistant (like Claude Code, Cursor, Windsurf, etc.), add Rivet skills for enhanced development assistance:
```sh
npx skills add rivet-dev/skills
```
### Install Dependencies
```sh
npm install rivetkit @rivetkit/react
```
### Create Backend Actor and Start Server
Create your actor registry on the backend and start the server:
### Create React Frontend
Set up your React application:
For detailed information about the React client API, see the [React Client API Reference](/docs/clients/react).
### Setup Vite Configuration
Configure Vite for development:
```ts vite.config.ts @nocheck
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
server: {
port: 5173,
},
})
```
### Run Your Application
Start both the backend and frontend:
**Terminal 1**: Start the backend
```sh Node.js
npx tsx --watch backend/index.ts
```
```sh Bun
bun --watch backend/index.ts
```
```sh Deno
deno run --allow-net --allow-read --allow-env --watch backend/index.ts
```
**Terminal 2**: Start the frontend
```sh Frontend
npx vite
```
Open `http://localhost:5173` in your browser. Try opening multiple tabs to see realtime sync in action.
Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live.
### Deploy
## Configuration Options
_Source doc path: /docs/actors/quickstart/react_

View File

@@ -0,0 +1,249 @@
# Rust Quickstart (Beta)
> Source: `src/content/docs/actors/quickstart/rust.mdx`
> Canonical URL: https://rivet.dev/docs/actors/quickstart/rust
> Description: Build a Rivet Actor in Rust
---
Rust support is in beta. The supported public Rust API is `rivetkit` and `rivetkit-client`; lower-level crates are internal implementation details and do not carry a stability guarantee. See the full API reference on [docs.rs/rivetkit](https://docs.rs/rivetkit), or the runnable [`hello-world-rust`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-rust) example.
## Steps
### Add Rivet
Add the `rivetkit` crate and its companions:
```sh
cargo add rivetkit anyhow async-trait
cargo add serde --features derive
cargo add tokio --features full
```
### Define Your Actor
Put the actor in `src/lib.rs` so both your server and your client can share the same types. An actor is a type that implements `Actor`, plus one `Handles` implementation for each action. Persisted state lives in `type State`; ephemeral runtime state is just fields on your actor struct.
```rust src/lib.rs
use std::{future::Future, pin::Pin, sync::Arc};
use async_trait::async_trait;
use rivetkit::prelude::*;
use serde::{Deserialize, Serialize};
type BoxFuture<T> = Pin<Box<dyn Future<Output = Result<T>> + Send>>;
pub struct Counter;
#[derive(Default, Serialize, Deserialize)]
pub struct CounterState {
pub count: i64,
}
#[derive(Serialize, Deserialize)]
pub struct Increment {
pub amount: i64,
}
impl Action for Increment {
type Output = i64;
const NAME: &'static str = "increment";
}
#[derive(Serialize, Deserialize)]
pub struct NewCount {
pub count: i64,
}
impl Event for NewCount {
const NAME: &'static str = "newCount";
}
#[async_trait]
impl Actor for Counter {
type State = CounterState;
type Input = ();
type Actions = (Increment,);
type Events = (NewCount,);
type Queue = ();
type ConnParams = ();
type ConnState = ();
type Action = action::Raw;
async fn create_state(_ctx: &Ctx<Self>, _input: Self::Input) -> Result<Self::State> {
Ok(CounterState::default())
}
async fn create(_ctx: &Ctx<Self>) -> Result<Self> {
Ok(Self)
}
}
impl Handles<Increment> for Counter {
type Future = BoxFuture<i64>;
fn handle(self: Arc<Self>, ctx: Ctx<Self>, action: Increment) -> Self::Future {
Box::pin(async move {
let count = {
let mut state = ctx.state_mut();
state.count += action.amount;
state.count
};
ctx.emit(NewCount { count })?;
Ok(count)
})
}
}
pub fn registry() -> Registry {
let mut registry = Registry::new();
registry.register_actor::<Counter>("counter");
registry
}
```
### Serve The Registry
Your `src/main.rs` just starts the registry from the library:
```rust src/main.rs
#[tokio::main]
async fn main() -> anyhow::Result<()> {
counter::registry().start().await
}
```
Replace `counter` with your crate name (the package `name` in `Cargo.toml`, with dashes as underscores).
### Run The Server
The Rust runtime connects to the Rivet Engine. Setting `RIVETKIT_ENGINE_AUTO_DOWNLOAD=1` lets the runtime download and cache a matching engine binary the first time you run, so there is nothing else to install:
```sh
RIVETKIT_ENGINE_AUTO_DOWNLOAD=1 cargo run
```
Your server now connects to the Rivet Engine on `http://localhost:6420`. Clients connect directly to the engine on this port.
Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live.
Already have an engine binary? Set `RIVET_ENGINE_BINARY_PATH=/path/to/rivet-engine` to point at it instead. If you are working inside the [Rivet monorepo](https://github.com/rivet-dev/rivet), a local `cargo build -p rivet-engine` is discovered automatically from `target/debug`.
### Connect To The Rivet Actor
This code can run either in your frontend or within your backend:
### Rust
Add a `src/bin/client.rs` that imports the same actor types from your library. There is no need to redefine the actor on the client.
```rust src/bin/client.rs
use counter::{Counter, Increment, NewCount};
use rivetkit::{
client::{Client, ClientConfig},
prelude::*,
TypedClientExt,
};
#[tokio::main]
async fn main() -> Result<()> {
let client = Client::new(ClientConfig::new("http://localhost:6420").namespace("default"));
let counter = client.get_or_create_typed_default::<Counter>("counter", ["my-counter"])?;
let count = counter.send(Increment { amount: 3 }).await?;
println!("New count: {count}");
let connection = counter.connect();
connection
.on::<NewCount>(|event| println!("Count changed: {}", event.count))
.await;
connection.send(Increment { amount: 1 }).await?;
Ok(())
}
```
With the server still running, start the client in another terminal:
```sh
cargo run --bin client
```
See the [`hello-world-rust`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-rust) example for a complete runnable counter.
### TypeScript
A TypeScript client can call your Rust actor by name through the same engine. Actor and action names are resolved at runtime, so the client is untyped here:
```ts client.ts @nocheck
import { createClient } from "rivetkit/client";
const client = createClient("http://localhost:6420");
const counter = client.counter.getOrCreate(["my-counter"]);
const counterConnection = counter.connect();
counterConnection.on("newCount", (event) => {
console.log("Event count:", event.count);
});
const count = await counterConnection.increment(3);
console.log("New count:", count);
await counterConnection.increment(1);
```
See the [JavaScript client documentation](/docs/clients/javascript) for more information.
### React
```tsx Counter.tsx @nocheck
import { createRivetKit } from "@rivetkit/react";
import { useState } from "react";
const { useActor } = createRivetKit("http://localhost:6420");
function Counter() {
const [count, setCount] = useState(0);
const counter = useActor({
name: "counter",
key: ["my-counter"],
});
const increment = async () => {
await counter.connection?.increment(1);
};
counter.useEvent("newCount", (event) => {
setCount(event.count);
});
return (
<div>
<p>Count: {count}</p>
<button onClick={increment}>Increment</button>
</div>
);
}
```
See the [React documentation](/docs/clients/react) for more information.
### Deploy
## Next Steps
- [API Reference](https://docs.rs/rivetkit) — Full `rivetkit` crate documentation on docs.rs.
- [Actions](/docs/actors/actions) — Define the RPC surface clients call on your actor.
- [State](/docs/actors/state) — Persist and load actor state across sleeps and restarts.
- [Events](/docs/actors/events) — Broadcast realtime updates to connected clients.
_Source doc path: /docs/actors/quickstart/rust_

View File

@@ -0,0 +1,109 @@
# Supabase Functions Quickstart
> Source: `src/content/docs/actors/quickstart/supabase.mdx`
> Canonical URL: https://rivet.dev/docs/actors/quickstart/supabase
> Description: Set up a Rivet project locally targeting Supabase Edge Functions.
---
Set up a Rivet project locally that runs on Supabase Edge Functions. The `@rivetkit/supabase` package wires the WebAssembly runtime for you.
Prefer to start from a complete project? See the runnable [`hello-world-supabase-functions`](https://github.com/rivet-dev/rivet/tree/main/examples/hello-world-supabase-functions) example.
## Steps
### Prerequisites
- [Node.js](https://nodejs.org/)
- [Supabase CLI](https://supabase.com/docs/guides/cli)
- Docker, for Supabase's local Edge Runtime
The CLI runs the local Rivet engine as a bundled native binary, so Docker is only needed for Supabase itself. A Supabase project is only needed to deploy.
### Create the Function
```sh
npx supabase functions new rivet
```
Add the packages used by the function:
```sh
npm install rivetkit @rivetkit/supabase
```
### Configure the Function
Call `serve` from `@rivetkit/supabase`. It loads the WebAssembly runtime and serves the Rivet handler.
```ts supabase/functions/rivet/index.ts @nocheck
import { actor } from "rivetkit";
import { serve, setup } from "@rivetkit/supabase";
const counter = actor({
state: { count: 0 },
actions: {
increment: (c, amount = 1) => {
c.state.count += amount;
return c.state.count;
},
},
});
// `setup` returns a typed registry, so a client can type itself with
// `typeof registry`.
export const registry = setup({ use: { counter } });
await serve(registry);
```
Add a `deno.json` next to the function so the deploy bundles only the WebAssembly runtime. It points `rivetkit` at the pre-bundled `@rivetkit/supabase`, keeping the deploy small. Without it, the deploy pulls Rivet's native engine and 413s.
```json supabase/functions/rivet/deno.json
{
"imports": {
"rivetkit": "npm:@rivetkit/supabase",
"@rivetkit/supabase": "npm:@rivetkit/supabase"
}
}
```
Your function code keeps importing from `rivetkit` as usual. The import map only changes how Deno resolves it at bundle time.
### Run Locally
Start Rivet. The CLI runs the local engine, spawns `supabase functions serve` for you, and populates the connection values:
```sh
npx @rivetkit/cli dev --provider supabase
```
Visit [http://localhost:6420](http://localhost:6420) in your browser (or point your AI agent at it) to open the Rivet developer tools and inspect your actors live.
### Call the Actor
Connect to your actor from a client. This connects directly to the local engine on `http://localhost:6420`:
```ts client.ts @nocheck
import { createClient } from "rivetkit/client";
import type { registry } from "./supabase/functions/rivet/index";
const client = createClient<typeof registry>("http://localhost:6420");
const counter = client.counter.getOrCreate(["my-counter"]);
const count = await counter.increment(3);
console.log("New count:", count);
```
See the [JavaScript client documentation](/docs/clients/javascript) for more information.
### Deploy
Ready to ship? See [Deploying to Supabase Functions](/docs/deploy/supabase).
## Related
- [Quickstart](/docs/actors/quickstart)
- [Deploying to Supabase Functions](/docs/deploy/supabase)
- [SQLite](/docs/actors/sqlite)
_Source doc path: /docs/actors/quickstart/supabase_