More

AI and User-Generated Rivet Actors

This guide shows you how to programmatically create sandboxed Rivet environments and deploy custom actor code to them.

Use Cases

Deploying AI and user-generated Rivet Actors to sandboxed namespaces is useful for:

  • AI-generated code deployments: Deploy code generated by LLMs in sandboxed environments
  • User sandbox environments: Give users their own sandboxed Rivet namespace to experiment
  • Preview deployments: Create ephemeral environments for testing pull requests
  • Multi-tenant applications: Isolate each customer in their own sandboxed namespace

Rivet Actors For AI-Generated Backends

Traditional architectures require AI agents to coordinate across multiple disconnected systems: a database schemas, API logic, and synchronizing schemas & APIs.

With Rivet Actors, state and logic live together in a single actor definition. This consolidation means:

  • Less LLM context required: No need to understand multiple systems or keep them in sync
  • Fewer errors: State and behavior can't drift apart when they're defined together
  • More powerful generation: AI agents can focus on business logic instead of infrastructure plumbing

How It Works

The deployment process involves four key steps:

  1. Create sandboxed Rivet namespace: Programmatically create a sandboxed Rivet namespace using the Cloud API or self-hosted Rivet API
  2. Generate tokens: Create the necessary tokens for authentication:
    • Runner token: Authenticates the serverless runner to execute actors
    • Publishable token: Used by frontend clients to connect to actors
    • Access token: Provides API access for configuring the namespace
  3. Deploy AI or user-generated code: Deploy the actor code and frontend programmatically to your serverless platform of choice (such as Vercel, Netlify, AWS Lambda, or any other provider). We'll be using Freestyle for this example since it's built for this use case.
  4. Connect Rivet to your deployed code: Configure Rivet to run actors on your deployment in your sandboxed namespace

Setup

Prerequisites

Before you begin, ensure you have:

Create Cloud API Token

  1. Visit your project on Rivet Cloud
  2. Click on "Tokens" in the sidebar
  3. Under "Cloud API Tokens" click "Create Token"
  4. Copy the token for use in your deployment script

Install Dependencies

Install the required dependencies:

npm install @rivetkit/engine-api-full@^25.7.2 freestyle-sandboxes@^0.0.95
Command Line

Write Deployment Code

Write deployment code that handles namespace creation, token generation, Freestyle deployment, and runner configuration. This can be called from your backend to deploy actor and frontend code to an isolated Rivet namespace.

import { execSync } from "child_process";
import { RivetClient } from "@rivetkit/engine-api-full";
import { FreestyleSandboxes } from "freestyle-sandboxes";
import { prepareDirForDeploymentSync } from "freestyle-sandboxes/utils";

const CLOUD_API_TOKEN = "your-cloud-api-token";
const FREESTYLE_DOMAIN = "your-app.style.dev";
const FREESTYLE_API_KEY = "your-freestyle-api-key";

async function deploy(projectDir: string) {
	// Step 1: Inspect API token to get project and organization
	const { project, organization } = await cloudRequest("GET", "/tokens/api/inspect");

	// Step 2: Create sandboxed namespace with a unique name
	const namespaceName = `ns-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;

	const { namespace } = await cloudRequest(
		"POST",
		`/projects/${project}/namespaces?org=${organization}`,
		{ displayName: namespaceName.substring(0, 16) },
	);
	const engineNamespaceName = namespace.access.engineNamespaceName;  // NOTE: Intentionally different than namespace.name

	// Step 3: Generate tokens
	// - Runner token: authenticates the serverless runner to execute actors
	// - Publishable token: used by frontend clients to connect to actors
	// - Access token: provides API access for configuring the namespace
	const { token: runnerToken } = await cloudRequest(
		"POST",
		`/projects/${project}/namespaces/${namespace.name}/tokens/secret?org=${organization}`,
	);

	const { token: publishableToken } = await cloudRequest(
		"POST",
		`/projects/${project}/namespaces/${namespace.name}/tokens/publishable?org=${organization}`,
	);

	const { token: accessToken } = await cloudRequest(
		"POST",
		`/projects/${project}/namespaces/${namespace.name}/tokens/access?org=${organization}`,
	);

	// Step 4: Build the frontend with public environment variables.
	execSync("npm run build", {
		cwd: projectDir,
		env: {
			...process.env,
			VITE_RIVET_ENDPOINT: "https://api.rivet.dev",
			VITE_RIVET_NAMESPACE: engineNamespaceName,
			VITE_RIVET_TOKEN: publishableToken,
		},
		stdio: "inherit",
	});

	// Step 5: Deploy actor code and frontend to Freestyle with backend
	// environment variables.
	const freestyle = new FreestyleSandboxes({ apiKey: FREESTYLE_API_KEY });
	const deploymentSource = prepareDirForDeploymentSync(projectDir);

	const { deploymentId } = await freestyle.deployWeb(deploymentSource, {
		envVars: {
			RIVET_ENDPOINT: "https://api.rivet.dev",
			RIVET_NAMESPACE: engineNamespaceName,
			RIVET_TOKEN: runnerToken,
		},
		entrypoint: "src/backend/server.ts",
		domains: [FREESTYLE_DOMAIN],
		build: false,
	});

	// Step 6: Configure Rivet to run actors on the Freestyle deployment.
	const rivet = new RivetClient({
		environment: "https://api.rivet.dev",
		token: accessToken,
	});

	await rivet.runnerConfigsUpsert("default", {
		datacenters: {
			"us-west-1": { // Freestyle datacenter is on west coast
				serverless: {
					url: `https://${FREESTYLE_DOMAIN}/api/rivet`,
					headers: {},
					runnersMargin: 0,
					minRunners: 0,
					maxRunners: 1000,
					slotsPerRunner: 1,
					requestLifespan: 60 * 5,
				},
			},
		},
		namespace: engineNamespaceName,
	});

	console.log("Deployment complete!");
	console.log("Frontend:", `https://${FREESTYLE_DOMAIN}`);
	console.log("Rivet Dashboard:", `https://dashboard.rivet.dev/orgs/${organization}/projects/${project}/ns/${namespace.name}`);
	console.log("Freestyle Dashboard:", `https://admin.freestyle.sh/dashboard/deployments/${deploymentId}`);
}

async function cloudRequest(method: string, path: string, body?: any) {
	const res = await fetch(`https://api-cloud.rivet.dev${path}`, {
		method,
		headers: {
			Authorization: `Bearer ${CLOUD_API_TOKEN}`,
			...(body && { "Content-Type": "application/json" }),
		},
		...(body && { body: JSON.stringify(body) }),
	});
	return res.json();
}
TypeScript

See the example repository for the complete project structure including the template directory and build process.

For more information on Freestyle deployment, see the Freestyle documentation.

Suggest changes to this page