Networking & Previews
Proxy HTTP requests into VM services with vmFetch and create time-limited, token-based preview URLs (with per-call expiration and revocation), 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/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);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; see Security for details.
Create a preview URL
Section titled “Create a preview URL”Token lifetime is controlled per call via the second argument to createSignedPreviewUrl(port, ttlSeconds), defaulting to 1 hour when omitted or 0:
import { agentOS, setup } from "@rivet-dev/agentos";
const vm = agentOS({ software: [] });
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 exactly the ttlSeconds you pass (no server-side maximum is currently enforced — omit it for the 1-hour default).
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.
Listen port policy
Section titled “Listen port policy”Constrain which ports guest code may bind with the listen field on the VM config. It bounds the allowed listen range and gates privileged (below 1024) ports:
const vm = agentOS({ listen: { portMin: 3000, // lowest bindable port (validated 1–65535) portMax: 9000, // highest bindable port (portMin <= portMax) allowPrivileged: false, // default false; gates ports < 1024 },});portMin/portMaxbound which ports the guest may bind and where automatic port allocation searches. Both are validated to be between1and65535, withportMin <= portMax.allowPrivilegeddefaults tofalseand gates ports below1024. With it leftfalse, binding a privileged port fails withEACCES.- A bind to a port outside
[portMin, portMax], or to a privileged port withoutallowPrivileged: true, fails withEACCES.