Networking & Previews
Proxy HTTP requests into VM services with vmFetch and create time-limited, token-based preview URLs (with configurable expiration, revocation, and CORS), all carried over one transport (the kernel socket table) that is loopback-only by default under a three-layer confinement model.
Run an HTTP server in the VM
Section titled “Run an HTTP server in the VM”Guest code runs a normal Node HTTP server: it binds a loopback port inside the VM exactly like any Node process. Write the server file and spawn it.
import { createClient } from "@rivet-dev/agentos/client";import type { registry } from "./server";
const client = createClient<typeof registry>({ endpoint: "http://localhost:6420" });const agent = client.vm.getOrCreate("my-agent");
// Write a simple Node HTTP server and run it inside the VM. It binds a loopback// port (3000) exactly like any normal Node process.await agent.writeFile( "/home/user/server.js", `const http = require("http");http.createServer((req, res) => { res.writeHead(200, { "Content-Type": "text/plain" }); res.end("Hello from inside the VM");}).listen(3000, () => console.log("listening on http://127.0.0.1:3000"));`,);const { pid } = await agent.spawn("node", ["/home/user/server.js"]);console.log("server pid:", pid);Fetch from a VM service
Section titled “Fetch from a VM service”With the HTTP server running in the VM (above), send requests to it with vmFetch, including custom methods, headers, and body.
import { createClient } from "@rivet-dev/agentos/client";import type { registry } from "./server";
const client = createClient<typeof registry>({ endpoint: "http://localhost:6420" });const agent = client.vm.getOrCreate("my-agent");
// Simple GET from the VM service started above.const response = await agent.vmFetch(3000, "/");console.log("Status:", response.status);console.log("Body:", new TextDecoder().decode(response.body));
// With a custom method, headers, and body.const posted = await agent.vmFetch(3000, "/api/data", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ key: "value" }),});console.log("Status:", posted.status, posted.statusText);console.log("Headers:", posted.headers);console.log("Body:", new TextDecoder().decode(posted.body));Preview URLs
Section titled “Preview URLs”Preview URLs are port forwarding for VM services: a time-limited, public URL that proxies HTTP to a port inside the VM, for browser or external access (use vmFetch for server-to-server). Tokens survive sleep/wake and CORS is enabled; see Security for details.
Create a preview URL
Section titled “Create a preview URL”Token lifetimes are configured under the preview key:
import { agentOS, setup } from "@rivet-dev/agentos";
const vm = agentOS({ software: [], preview: { defaultExpiresInSeconds: 3600, // 1 hour default maxExpiresInSeconds: 86400, // 24 hour maximum },});
export const registry = setup({ use: { vm } });registry.start();Revoke a preview URL
Section titled “Revoke a preview URL”Mint short-lived preview tokens so access expires automatically; the lifetime is capped by preview.maxExpiresInSeconds.
import { createClient } from "@rivet-dev/agentos/client";import type { registry } from "./server";
const client = createClient<typeof registry>({ endpoint: "http://localhost:6420" });
// Mint a short-lived preview token so access expires automatically.const preview = await client.vm.getOrCreate("my-agent").createSignedPreviewUrl(3000, 300); // 5 minutesconsole.log("Preview path:", preview.path);console.log("Expires at:", new Date(preview.expiresAt));Permissions
Section titled “Permissions”Network access is governed by the VM permission policy. By default the guest cannot reach the network; grant it, or allow only specific destinations:
const vm = agentOS({ permissions: { network: { default: "deny", rules: [{ mode: "allow", operations: ["*"], patterns: ["api.example.com"] }], }, },});See Permissions for the full configuration.