# Architecture

**Dynamic Apps is a library, not a hosted app deployment platform.** You own
the routing server, its authentication, and the URL on which applications are
mounted.

The request lifecycle looks like:

1. Your routing server receives a request.
2. Your middleware handles it: authentication, rate limits, whatever else you run.
3. The request is passed to `appsRouter`.
4. `appsRouter` executes it in the agentOS VM for that app, starting the VM if it isn't already running.

src/server.ts:

```ts
import { serve } from "@hono/node-server";
import { appsRouter } from "@rivet-dev/dynamic-apps";
import { Hono } from "hono";

const server = new Hono();

// This is how the Rivet control plane communicates with your backend.
server.all("/api/rivet/*", (c) => appsRouter.fetch(c.req.raw));

// Mount every deployed application at /apps/:appId.
server.route("/apps", appsRouter);

// Serve the host router over HTTP.
serve({
	fetch: server.fetch,
	port: 3000,
});
```

Requests reach your routing server, where `appsRouter` executes them in a
cached agentOS VM embedded in the same process. There is no network hop to
another service, and warm requests never touch storage. Apps export fetch
handlers; Dynamic Apps owns any listener inside the VM.

## Deployment is separate from serving

`deployApp()` builds the files in a sandboxed agentOS build VM and publishes an
immutable release. The default `@rivet-dev/dynamic-apps` package stores
releases in Rivet. `@rivet-dev/dynamic-apps-core` lets you
supply another store.

## App-defined actors

An app may also export a RivetKit registry. Those app-defined actors use normal
Rivet routing for durable state, actions, events, and connections. Dynamic Apps
imports the app once into a cached server process, then sends both ordinary HTTP
and Rivet callbacks through that process.

> **NOTE:** agentOS provides the filesystem, process, environment, and network permission boundary for direct requests and app-defined actor workers.
