Skip to main content

Rivet

Run durable Actors, workflows, sandboxes, and generated apps on one control plane.

  1. 1
    Install
    npm install rivetkit
  2. 2
    Define an Actor index.ts
    import { actor, setup } from "rivetkit";
    import { db } from "rivetkit/db";
    
    // A chat room Actor. Every room key gets its own instance with its own SQLite
    // database, which survives restarts and redeploys.
    export const chatRoom = actor({
    	// Create the messages table the first time a room starts.
    	db: db({
    		onMigrate: async (db) => {
    			await db.execute(`
    				CREATE TABLE IF NOT EXISTS messages (
    					id INTEGER PRIMARY KEY AUTOINCREMENT,
    					sender TEXT NOT NULL,
    					text TEXT NOT NULL
    				)
    			`);
    		},
    	}),
    
    	actions: {
    		// Save a message, then push it to everyone connected to this room.
    		sendMessage: async (c, sender: string, text: string) => {
    			await c.db.execute(
    				"INSERT INTO messages (sender, text) VALUES (?, ?)",
    				sender,
    				text,
    			);
    
    			c.broadcast("newMessage", { sender, text });
    		},
    
    		// Read the room's history, oldest first.
    		getHistory: async (c) => {
    			return (await c.db.execute(
    				"SELECT sender, text FROM messages ORDER BY id",
    			)) as { sender: string; text: string }[];
    		},
    	},
    });
    
    // Register the Actor and start serving it on http://localhost:6420.
    export const registry = setup({ use: { chatRoom } });
    registry.start();
  3. 3
    Talk to it from a client client.ts
    import { createClient } from "rivetkit/client";
    import type { registry } from "./index";
    
    // Connect to the server from index.ts. The registry type checks every call.
    const client = createClient<typeof registry>("http://localhost:6420");
    
    // Get the room keyed "general", creating it on first use.
    const room = client.chatRoom.getOrCreate(["general"]);
    
    // Open a realtime connection and print each message as it arrives.
    const connection = room.connect();
    connection.on("newMessage", (message: { sender: string; text: string }) => {
    	console.log(`${message.sender}: ${message.text}`);
    });
    
    // Send a message. The Actor saves it to SQLite before broadcasting it.
    await connection.sendMessage("alice", "Hello, Rivet!");
    
    // The history is still there after the Actor sleeps or restarts.
    console.log(await room.getHistory());
  1. 1
    Install
    npm install @rivet-dev/workflows rivetkit
  2. 2
    Define a workflow index.ts
    import {
    	setup,
    	type WorkflowStepContextOf,
    	workflow,
    } from "@rivet-dev/workflows";
    import { db } from "rivetkit/db";
    
    // An order workflow, one instance per order. Each step runs exactly once:
    // after a crash or redeploy, the workflow replays and skips finished steps.
    export const order = workflow({
    	// Each order gets its own SQLite database to record its progress.
    	db: db({
    		onMigrate: async (db) => {
    			await db.execute(`
    				CREATE TABLE IF NOT EXISTS progress (
    					id INTEGER PRIMARY KEY AUTOINCREMENT,
    					step TEXT NOT NULL
    				)
    			`);
    		},
    	}),
    
    	run: async (ctx) => {
    		// Reserve stock, then record that the step finished.
    		await ctx.step("reserve", (step) => record(step, "reserve"));
    
    		// Wait durably. The workflow keeps its place across restarts.
    		await ctx.sleep("wait-for-payment", 1_000);
    
    		// Charge the card and send the receipt, recording each step.
    		await ctx.step("charge", (step) => record(step, "charge"));
    		await ctx.step("receipt", (step) => record(step, "receipt"));
    	},
    
    	actions: {
    		// Read which steps have finished so far.
    		getProgress: async (c) => {
    			return (await c.db.execute(
    				"SELECT step FROM progress ORDER BY id",
    			)) as { step: string }[];
    		},
    	},
    });
    
    // Write one progress row to this order's database.
    async function record(
    	step: WorkflowStepContextOf<typeof order>,
    	name: string,
    ) {
    	await step.db.execute("INSERT INTO progress (step) VALUES (?)", name);
    }
    
    // Register the workflow and start serving it on http://localhost:6420.
    export const registry = setup({ use: { order } });
    registry.start();
  3. 3
    Start it from a client client.ts
    import { createClient } from "rivetkit/client";
    import type { registry } from "./index";
    
    // Connect to the server from index.ts. The registry type checks every call.
    const client = createClient<typeof registry>("http://localhost:6420");
    
    // Get the order keyed "order-123". Creating it starts its workflow.
    const order = client.order.getOrCreate(["order-123"]);
    
    // Give the workflow a moment to run, then read the steps it has finished.
    await new Promise((resolve) => setTimeout(resolve, 2_000));
    console.log(await order.getProgress());
  1. 1
    Install
    npm install @rivet-dev/agentos
  2. 2
    Create a hosted VM and use it quickstart.ts
    import { createAgentOsClient } from "@rivet-dev/agentos";
    
    // Connect to Rivet Cloud. Set RIVET_NAMESPACE and RIVET_TOKEN from your
    // project's dashboard before running this.
    const client = createAgentOsClient({ endpoint: "https://api.rivet.dev" });
    
    // Get the VM keyed "my-vm", creating it on first use. Same key, same VM.
    const vm = client.agentOS.getOrCreate(["my-vm"]);
    
    // Write a file. The VM's filesystem persists while the VM sleeps.
    await vm.v1.filesystem.writeFile({
    	path: "/home/agentos/hello.txt",
    	content: "Hello from agentOS!",
    });
    
    // Run a shell command and capture its output.
    const result = await vm.v1.process.run({
    	command: "cat",
    	args: ["/home/agentos/hello.txt"],
    	options: { captureStdio: true },
    });
    console.log(result.stdout);
    
    // Start a web server inside the VM.
    await vm.v1.filesystem.writeFile({
    	path: "/home/agentos/server.js",
    	content: `require("http")
    	.createServer((req, res) => res.end("Hello from the VM!"))
    	.listen(3000);`,
    });
    await vm.v1.process.spawn({
    	command: "node",
    	args: ["/home/agentos/server.js"],
    });
    
    // Expose the server through a preview URL that expires in one hour.
    const preview = await vm.v1.network.preview.create({
    	port: 3000,
    	ttlMs: 60 * 60 * 1_000,
    });
    console.log(preview.path);
  1. 1
    Start the server
    npx @rivet-dev/services dev
  2. 2
    Install the client
    npm install @durable-streams/client
  3. 3
    Write to a stream and read it back client.ts
    import { DurableStream } from "@durable-streams/client";
    
    const stream = await DurableStream.create({
    	url: "http://127.0.0.1:8642/durable-streams/v1/stream/demo",
    	contentType: "application/json",
    });
    
    await stream.append(JSON.stringify({ message: "hello" }));
    
    // Read from the beginning, then keep receiving new messages live
    const res = await stream.stream<{ message: string }>();
    res.subscribeJson(async (batch) => {
    	for (const item of batch.items) console.log(item.message);
    });

Once it runs

Guides

Agents

Coding Agents

Workflows

Realtime

Databases

Games

Concepts