Back to Rivet

AI and User-Generated Rivet Actors

website/src/content/docs/actors/ai-and-user-generated-actors.mdx

2.2.111.2 KB
Original Source

import { faGithub } from "@rivet-gg/icons";

<CardGroup> <Card title="View Example on GitHub" href="https://github.com/rivet-dev/rivet/tree/main/examples/ai-and-user-generated-actors-freestyle" target="_blank" icon={faGithub}> Complete example showing how to deploy user-generated Rivet Actor code. </Card> </CardGroup>

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

<Tabs> <Tab title="Rivet Cloud"> <Steps> <Step title="Prerequisites"> Before you begin, ensure you have: - Node.js 18+ installed - A [Freestyle](https://freestyle.sh) account and API token - A [Rivet Cloud](https://dashboard.rivet.dev/) account </Step>
		<Step title="Create Cloud API Token">
			1. Visit your project on [Rivet Cloud](https://dashboard.rivet.dev/)
			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
		</Step>

		<Step title="Install Dependencies">
			Install the required dependencies:

			```bash
			npm install @rivetkit/engine-api-full@^25.7.2 freestyle-sandboxes@^0.0.95
			```
		</Step>

		<Step title="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.

			```typescript
			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();
			}
			```

			See the [example repository](https://github.com/rivet-dev/rivet/tree/main/examples/ai-and-user-generated-actors-freestyle) for the complete project structure including the template directory and build process.

			For more information on Freestyle deployment, see the [Freestyle documentation](https://docs.freestyle.sh/web/overview).
		</Step>
	</Steps>
</Tab>

<Tab title="Rivet Self-Hosted">
	<Steps>
		<Step title="Prerequisites">
			Before you begin, ensure you have:
			- Node.js 18+ installed
			- A [Freestyle](https://freestyle.sh) account and API key
			- A [self-hosted Rivet instance](/docs/self-hosting) with endpoint and API token
		</Step>

		<Step title="Install Dependencies">
			Install the required dependencies:

			```bash
			npm install @rivetkit/engine-api-full@^25.7.2 freestyle-sandboxes@^0.0.95
			```
		</Step>

		<Step title="Write Deployment Code">
			Write deployment code that handles namespace creation, Freestyle deployment, and runner configuration. This can be called from your backend to deploy actor and frontend code to an isolated Rivet namespace.

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

			// Configuration
			const RIVET_ENDPOINT = "http://your-rivet-instance:6420";
			const RIVET_TOKEN = "your-rivet-token";
			const FREESTYLE_DOMAIN = "your-app.style.dev";
			const FREESTYLE_API_KEY = "your-freestyle-api-key";

			async function deploy(projectDir: string) {
				// Step 1: Create sandboxed namespace using the self-hosted Rivet API
				const rivet = new RivetClient({
					environment: RIVET_ENDPOINT,
					token: RIVET_TOKEN,
				});

				const namespaceName = `ns-${Date.now()}-${Math.random().toString(36).substring(2, 8)}`;

				const { namespace } = await rivet.namespaces.create({
					displayName: namespaceName,
					name: namespaceName,
				});

				// Step 2: Build the frontend with public environment variables.
				execSync("npm run build", {
					cwd: projectDir,
					env: {
						...process.env,
						VITE_RIVET_ENDPOINT: RIVET_ENDPOINT,
						VITE_RIVET_NAMESPACE: namespace.name,
						VITE_RIVET_TOKEN: RIVET_TOKEN,
					},
					stdio: "inherit",
				});

				// Step 3: Deploy actor 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,
						RIVET_NAMESPACE: namespace.name,
						RIVET_TOKEN,
					},
					entrypoint: "src/backend/server.ts",
					domains: [FREESTYLE_DOMAIN],
					build: false,
				});

				// Step 4: Configure your self-hosted Rivet to run actors on the Freestyle
				// deployment
				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: namespace.name,
				});

				console.log("Deployment complete!");
				console.log("Frontend:", `https://${FREESTYLE_DOMAIN}`);
				console.log("Freestyle Dashboard:", `https://admin.freestyle.sh/dashboard/deployments/${deploymentId}`);
			}
			```

			See the [example repository](https://github.com/rivet-dev/rivet/tree/main/examples/ai-and-user-generated-actors-freestyle) for the complete project structure including the template directory and build process.
		</Step>
	</Steps>
</Tab>
</Tabs>