Skip to content
GitHub Get Started
Operating System

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.

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.

client.ts
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/agentos/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/agentos/server.js"]);
console.log("server pid:", pid);

See Full Example

With the HTTP server running in the VM (above), send requests to it with vmFetch, including custom methods, headers, and body.

client.ts
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));

See Full Example

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.

Token lifetimes are configured under the preview key:

server.ts
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();

See Full Example

Mint short-lived preview tokens so access expires automatically; the lifetime is capped by preview.maxExpiresInSeconds.

client.ts
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 minutes
console.log("Preview path:", preview.path);
console.log("Expires at:", new Date(preview.expiresAt));

See Full Example

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.