Networking & Previews
Proxy HTTP requests into agentOS VMs and create shareable preview URLs.
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
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
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");
// Fetch 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));
import { createClient } from "@rivet-dev/agentos/client";
import type { registry } from "./server";
const client = createClient<typeof registry>({ endpoint: "http://localhost:6420" });
const response = await client.vm.getOrCreate("my-agent").vmFetch(3000, "/api/data", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ key: "value" }),
});
console.log("Status:", response.status, response.statusText);
console.log("Headers:", response.headers);
console.log("Body:", new TextDecoder().decode(response.body));
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
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();
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");
// Start a web app in the VM
await agent.spawn("node", ["/home/agentos/app.js"]);
// Create a preview URL for port 3000, valid for 1 hour
const preview = await agent.createSignedPreviewUrl(3000, 3600);
console.log("Preview URL:", preview.url);
console.log("Token:", preview.token);
console.log("Expires at:", new Date(preview.expiresAt));
// Create a preview URL with a shorter expiration
const shortPreview = await agent.createSignedPreviewUrl(3000, 300); // 5 minutes
console.log("Short-lived preview:", shortPreview.url);
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 minutes
console.log("Preview URL:", preview.url);
console.log("Expires at:", new Date(preview.expiresAt));
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.