# Embedded VMs

Use the embedded API when you already have a Node.js application and want to
control agentOS VMs directly. You manage VM identity, persistence, and lifecycle
yourself instead of using Rivet Actors.

To create your first embedded VM, start with the
[embedded quickstart](/agentos/docs/quickstart-embedded).

This page covers only what is different about embedding. Each capability is
documented on its own page, which ends with a short `Embedded API` section.

## Embedded API vs actor

| | Embedded API | Actor |
|-|---|---|
| Persistence | In-memory unless you configure `database`, plus [mounts](/agentos/docs/filesystem#mounts) | Actor SQLite injected automatically |
| Distributed state | Manage yourself | Built-in |
| Stateful VMs | Complex to run yourself | Built into Rivet |
| Sleep/wake | Manual `dispose()` / `create()` | Automatic |
| Events | Direct in-process callbacks | Broadcast to every connected client |
| Preview URLs | Serve them from your own application | Built-in signed URL server |
| Multiplayer | Fan out from your own application | Multiple clients per actor |
| Orchestration | VM-local [cron](/agentos/docs/cron) while the VM is alive | Workflows, queues, and cron |
| Agent-to-agent | [Bindings](/agentos/docs/agent-to-agent) between VMs you own | Built into [Rivet Actors](/agentos/docs/agent-to-agent) |
| Authentication | Your application's own | [Docs](/agentos/docs/authentication) |

- Use [Rivet Actors](/actors/docs/) for persistence, networking, and
  orchestration.
- Use `AgentOs.create()` to embed VM control in a Node.js application.
- `agentOS()` returns an ordinary TypeScript Rivet actor definition: VM options
  plus normal actor state, actions, events, queues, connection types, and
  lifecycle hooks (`onBeforeConnect`).
- agentOS actions/events merge in automatically; their names are reserved.
- The VM is created lazily on the first agentOS action after wake, disposed on
  sleep, so a connection can subscribe before `vmBooted`.
- Creation input flows through `client.vm.create("key", { input })` and reaches
  `createState(c, input)` and `onCreate(c, input)`.

## Lifecycle

- `AgentOs.create()` resolves to a VM handle. There is no actor server, client,
  or connection, so your application owns VM identity: there is no actor key,
  no `getOrCreate`, and no routing.
- The VM stays alive until you call `dispose()`. Nothing sleeps it for you.
- Action timeouts and automatic sleep/wake are [`agentOS()` actor](/agentos/docs/quickstart)
  features. See [Persistence & Sleep](/agentos/docs/persistence) for the actor
  behavior the embedded API leaves to you.
- Every `on*` callback is registered on one handle in one process. Nothing is
  broadcast to other processes or clients.

## Configuration reference

All VM config is a single flat object passed to `AgentOs.create()`. The
[`agentOS()` actor](/agentos/docs/quickstart) accepts the same options and layers
persistence, sleep/wake, and preview URLs on top.

Durability is the one option you must supply yourself. Actors inject their own
SQLite database; an embedded VM keeps the root filesystem and session catalog
in memory until you pass `database`, and loses both on `dispose()`.

config-reference.ts:

```ts
import pi from "@agentos-software/pi";
import { AgentOs, nodeModulesMount } from "@rivet-dev/agentos-core";

// Common embedded AgentOs.create() configuration. The agentOS() actor accepts
// the same options and layers persistence, sleep/wake, and preview URLs on top.
const vm = await AgentOs.create({
	// Durable SQLite storage for the root filesystem and the session catalog.
	// Omit it for an in-memory VM that keeps nothing after dispose().
	database: { type: "sqlite_file", path: ".agentos/agentos.sqlite" },
	// Filesystems to mount at boot. Use nodeModulesMount() to expose a host
	// node_modules tree at /root/node_modules.
	mounts: [nodeModulesMount("/path/to/project/node_modules")],
	// Kernel permission policy (see /agentos/docs/permissions) and runtime caps
	// (see /agentos/docs/resource-limits) take the same values as the actor.
	// `bindings` does too (see /agentos/docs/bindings).
	permissions: { network: "allow" },
	limits: { jsRuntime: { v8HeapLimitMb: 128 } },
	// Software packages to install in the VM (see /agentos/docs/software).
	software: [pi],
	// Also install the default software bundle (sh + coreutils). Defaults to true;
	// set false for a bare VM with only the software you list.
	defaultSoftware: true,
	// Ports exempt from SSRF checks (for testing against host-side mock servers)
	loopbackExemptPorts: [3000],
	// Sidecar placement defaults to the shared `default` pool.
	sidecar: { kind: "shared" },
});

await vm.dispose();
```

See [Filesystem](/agentos/docs/filesystem#mounts) for mount plugins and
[Software](/agentos/docs/software) for the package list.

## Sidecar process

- Every VM runs inside a **shared sidecar process**, not its own process.
- All VMs default to a single process-global sidecar (the `default` pool); each
  extra VM adds only a V8 isolate + its kernel state.
- This keeps per-VM memory in the tens of MB and warm creation in single-digit
  ms (see [Performance](/agentos/docs/performance)).
- Automatic for `agentOS()`, `AgentOs.create()`, and Rivet Actors.
- Disposing a VM tears down only that VM; the sidecar is reused for the host
  process lifetime.
- Advanced: the embedded API exposes explicit sidecar handles to isolate a
  group of VMs in their own process.

advanced.ts:

```ts
import { AgentOs } from "@rivet-dev/agentos-core";

// An embedded application can use one dedicated sidecar process for multiple VMs.
const sidecar = await AgentOs.createSidecar();
const a = await AgentOs.create({
	sidecar: { kind: "explicit", handle: sidecar },
});
const b = await AgentOs.create({
	sidecar: { kind: "explicit", handle: sidecar },
});

await a.dispose(); // tears down VM a only
await b.dispose();
await sidecar.dispose(); // tears down the shared process
```
