Skip to content

Commit 6d06f4e

Browse files
author
rivet-docs-sync[bot]
committed
docs(dynamic-apps): sync from rivet-dev/dynamic-apps@365240e
1 parent 1db1663 commit 6d06f4e

43 files changed

Lines changed: 702 additions & 419 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
<svg viewBox="0 0 680 146" role="img" aria-label="An HTTP request reaches your Node.js server. Your middleware runs first, then the Dynamic Apps router sends the request to the fetch handler in that app's agentOS VM." style="width:100%;height:auto;max-width:680px;display:block;margin:2.5rem auto 1rem;">
2+
<defs>
3+
<marker id="apps-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
4+
<path d="M0,0 L10,5 L0,10 z" fill="#1b1916" />
5+
</marker>
6+
</defs>
7+
8+
<rect x="8" y="50" width="114" height="60" rx="12" fill="#ffffff" stroke="#1b1916" stroke-width="1.5" />
9+
<text x="65" y="76" text-anchor="middle" font-family="var(--sl-font)" font-size="14" font-weight="600" fill="#1b1916">Request</text>
10+
<text x="65" y="95" text-anchor="middle" font-family="var(--sl-font)" font-size="9.5" fill="#56524a">Agent · Browser · API</text>
11+
12+
<line x1="125" y1="80" x2="149" y2="80" stroke="#1b1916" stroke-width="1.5" marker-end="url(#apps-arrow)" />
13+
14+
<rect x="152" y="16" width="520" height="114" rx="14" fill="#faf8f3" stroke="#1b1916" stroke-width="1.5" />
15+
<text x="172" y="40" font-family="var(--sl-font)" font-size="13" font-weight="600" fill="#1b1916">Your Node.js server</text>
16+
17+
<rect x="172" y="50" width="140" height="60" rx="8" fill="#ffffff" stroke="#1b1916" stroke-width="1.2" />
18+
<text x="242" y="84" text-anchor="middle" font-family="var(--sl-font)" font-size="12" font-weight="600" fill="#1b1916">Middleware</text>
19+
20+
<line x1="315" y1="80" x2="339" y2="80" stroke="#1b1916" stroke-width="1.5" marker-end="url(#apps-arrow)" />
21+
22+
<rect x="342" y="50" width="140" height="60" rx="8" fill="#ffffff" stroke="#1b1916" stroke-width="1.2" />
23+
<text x="412" y="84" text-anchor="middle" font-family="var(--sl-font)" font-size="12" font-weight="600" fill="#1b1916">Dynamic Apps router</text>
24+
25+
<line x1="485" y1="80" x2="509" y2="80" stroke="#1b1916" stroke-width="1.5" marker-end="url(#apps-arrow)" />
26+
27+
<rect x="512" y="50" width="140" height="60" rx="8" fill="#ffffff" stroke="#1b1916" stroke-width="1.2" />
28+
<text x="582" y="84" text-anchor="middle" font-family="var(--sl-font)" font-size="12" font-weight="600" fill="#1b1916">agentOS VM</text>
29+
</svg>
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
title: "Architecture"
3+
description: "How Dynamic Apps serves, builds, and stores user-generated applications."
4+
---
5+
6+
import ArchitectureDiagram from "./_architecture-diagram.astro";
7+
8+
9+
**Dynamic Apps is a library, not a hosted app deployment platform.** You own
10+
the routing server, its authentication, and the URL on which applications are
11+
mounted.
12+
13+
<ArchitectureDiagram />
14+
15+
The request lifecycle looks like:
16+
17+
1. Your routing server receives a request.
18+
2. Your middleware handles it: authentication, rate limits, whatever else you run.
19+
3. The request is passed to `appsRouter`.
20+
4. `appsRouter` executes it in the agentOS VM for that app, starting the VM if it isn't already running.
21+
22+
<CodeSnippet file="examples/apps-hello-world/src/server.ts" title="src/server.ts" />
23+
24+
Requests reach your routing server, where `appsRouter` executes them in a
25+
cached agentOS VM embedded in the same process. There is no network hop to
26+
another service, and warm requests never touch storage. Apps export fetch
27+
handlers; Dynamic Apps owns any listener inside the VM.
28+
29+
## Deployment is separate from serving
30+
31+
`deployApp()` builds the files in a sandboxed agentOS build VM and publishes an
32+
immutable release. The default `@rivet-dev/dynamic-apps` package stores
33+
releases in Rivet. `@rivet-dev/dynamic-apps-core` lets you
34+
supply another store.
35+
36+
## App-defined actors
37+
38+
An app may also export a RivetKit registry. Those app-defined actors use normal
39+
Rivet routing for durable state, actions, events, and connections. Dynamic Apps
40+
imports the app once into a cached server process, then sends both ordinary HTTP
41+
and Rivet callbacks through that process.
42+
43+
<Note>
44+
agentOS provides the filesystem, process, environment, and network permission
45+
boundary for direct requests and app-defined actor workers.
46+
</Note>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
---
2+
title: "Backends & REST APIs"
3+
description: "Serve HTTP backends and REST APIs from a Dynamic App."
4+
---
5+
6+
import ExampleLinkBar from "@/components/docs/ExampleLinkBar.astro";
7+
8+
<ExampleLinkBar href="https://github.com/rivet-dev/dynamic-apps/tree/main/examples/apps-hello-world" />
9+
10+
An app is a directory with a `package.json` and an entrypoint that
11+
default-exports a `fetch` handler. Any framework that speaks `fetch` works.
12+
The app does not bind a port; Dynamic Apps owns its listener. This app serves a
13+
frontend and a JSON API from the same handler.
14+
15+
## Example generated code
16+
17+
<CodeGroup>
18+
<CodeSnippet file="examples/apps-hello-world/fixtures/app/src/index.ts" title="src/index.ts" />
19+
<CodeSnippet file="examples/apps-hello-world/fixtures/app/package.json" title="package.json" />
20+
</CodeGroup>
21+
22+
## Deploy and route
23+
24+
Deploy the directory and every route the app defines is served under
25+
`/apps/:appId` on your routing server:
26+
27+
<CodeSnippet file="examples/apps-hello-world/src/server.ts" title="src/server.ts" />
28+
29+
See [Routing](/dynamic-apps/docs/routing) for how requests reach the app and
30+
[Deploy](/dynamic-apps/docs/deploy) for builds and releases.
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
---
2+
title: "Connect to Rivet"
3+
description: "Every deployed app gets its own Rivet namespace for actors, workflows, and data."
4+
skill: true
5+
---
6+
7+
Every deployment gets its own Rivet namespace. That gives each user's app:
8+
9+
- Complete per-tenant isolation
10+
- Per-user billing
11+
- Infrastructure that costs nothing when idle
12+
13+
## What namespaces are
14+
15+
A namespace holds an app's Rivet Actors, which power its workflows, SQLite,
16+
realtime state, queues, and crons. `deployApp()` creates one namespace per app
17+
and returns the endpoint, namespace, pool, and publishable token your client
18+
needs to connect.
19+
20+
## Connecting to Rivet
21+
22+
### Local development
23+
24+
No action needed. Dynamic Apps connects to the local Rivet Engine by default.
25+
26+
### Rivet Cloud
27+
28+
Set the `RIVET_CLOUD_TOKEN` environment variable. Issue a token from
29+
**Settings > Advanced > Cloud Token** in the
30+
[dashboard](https://dashboard.rivet.dev). `deployApp()` uses it to create each
31+
app's namespace through the Rivet Cloud API. Keep this token server-side.
32+
33+
### Self-hosting
34+
35+
Use your admin token. Nothing else is needed. The admin token is the same
36+
token your backend already uses to connect to Rivet, so namespace creation
37+
works out of the box.
38+
39+
## Disabling namespace creation
40+
41+
Set `createNamespace: false` on `deployApp()` for HTTP-only apps. Apps that
42+
use `rivetkit` require a namespace.

vendor/dynamic-apps/docs/content/docs/custom-storage.mdx renamed to vendor/dynamic-apps/docs/content/docs/core.mdx

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
---
2-
title: "Custom Storage"
3-
description: "Implement durable release publication, loading, and invalidation for Dynamic Apps Core."
2+
title: "Core"
3+
description: "Use Dynamic Apps Core to own release storage, loading, and invalidation."
44
skill: true
55
---
66

@@ -44,3 +44,10 @@ change for the entire lifetime of every serving process.
4444

4545
Call `await dynamicApps.dispose()` during shutdown to release subscriptions,
4646
build resources, cached runtimes, and agentOS contexts.
47+
48+
Deployed app entrypoints only export a Fetch handler; they never bind a port.
49+
If Core loads an actor-enabled release, provide its `server.environment` and a
50+
shared `serverRuntime`. The standard `@rivet-dev/dynamic-apps` package supplies
51+
that actor runtime automatically.
52+
53+
Get started with the [Quickstart (Core)](/dynamic-apps/docs/quickstart-core).
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
---
2+
title: "Customize the VM"
3+
description: "Configure the agentOS VM that builds and serves each app."
4+
skill: true
5+
---
6+
7+
With [Core](/dynamic-apps/docs/core), use the `vm` option to configure the
8+
agentOS VM that serves each app:
9+
10+
```ts
11+
const dynamicApps = createDynamicApps({
12+
// Release hooks omitted.
13+
vm: {
14+
software: [firstPackage, secondPackage],
15+
onAgentStderr: (event) => logger.error(event),
16+
onLimitWarning: (warning) => logger.warn(warning),
17+
},
18+
});
19+
```
20+
21+
Dynamic Apps itself controls where the app's code is placed in the VM and the
22+
V8 heap limit. `vm` options cannot override them.
23+
Use the separate `logger` option for application output and build events.
24+
25+
The VM itself is configured through agentOS:
26+
27+
- [Software](/agentos/docs/software): packages available inside the VM
28+
- [Resource Limits](/agentos/docs/resource-limits): CPU, memory, and disk caps
29+
- [Permissions](/agentos/docs/permissions): filesystem, process, and network boundaries
Lines changed: 34 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
---
2-
title: "Deploying Apps"
2+
title: "Deploy"
33
description: "Deploy a directory or generated files with deployApp(), preserve rollback, and configure app actors."
44
skill: true
55
---
66

7+
`deployApp()` publishes an app's files as a new release.
8+
9+
## Getting started
10+
711
Deploy a local application directory:
812

913
```ts
@@ -35,14 +39,28 @@ await deployApp({
3539
});
3640
```
3741

38-
The direct entrypoint must default-export a function or an object with
39-
`fetch(request)`. Code runs inside agentOS with filesystem, process,
40-
environment, and network permissions, and supported Node builtins are
41-
available. Directories that contain only static files are rejected; see
42-
[Static Websites](/dynamic-apps/docs/static-websites). Native addons are not
43-
supported.
42+
## Bundle requirements
43+
44+
An app needs two things:
45+
46+
- A `package.json`.
47+
- An entrypoint that default-exports a `fetch` handler.
48+
49+
Dynamic Apps owns the HTTP listener. Application code must not call `serve()`,
50+
`listen()`, or `registry.start()`.
51+
52+
With Hono, export the app directly:
53+
54+
```ts
55+
import { Hono } from "hono";
56+
57+
const app = new Hono();
58+
app.get("/", (c) => c.json({ ok: true }));
59+
60+
export default app;
61+
```
4462

45-
## Build repair and rollback
63+
## Build and repair loop
4664

4765
`deployApp()` rejects with bounded build diagnostics when generated source does
4866
not compile. Feed those diagnostics back to the generator and try again:
@@ -59,53 +77,16 @@ for (let attempt = 0; attempt < 3; attempt++) {
5977
}
6078
```
6179

62-
Include `webServerSkill` and `rivetActorsSkill` from `@rivet-dev/dynamic-apps`
63-
in the model prompt. They describe the supported TypeScript server layout and
64-
Rivet actor integration. [View the complete AI App Builder example](https://github.com/rivet-dev/dynamic-apps/tree/main/examples/apps-ai-builder).
65-
66-
A failed build or incomplete artifact write never replaces the active release.
67-
A successful call returns only after the immutable artifact is persisted and
68-
activated; it does not mean a request-serving replica was warmed.
69-
70-
With core, this is the `publishRelease` guarantee: make the complete artifact
71-
durable first, atomically replace the active release second, and resolve only
72-
when `loadActiveRelease` can read it. The default adapter provides those
73-
semantics through its per-app Rivet actor.
74-
75-
`appId` must contain 1–63 lowercase letters, numbers, or hyphens. Pass exactly
76-
one of `source` or `files`.
80+
A failed build never replaces the active release. A successful call resolves
81+
only after the new release is persisted and activated. Activation is
82+
all-or-nothing and rolls out instantly: warm VMs load the new release on the
83+
next request.
7784

7885
## Configuration
7986

80-
```ts
81-
await deployApp({
82-
appId: "my-app",
83-
source,
84-
regions: ["atl", "fra"],
85-
scaling: {
86-
minReplicas: 0,
87-
maxReplicas: 128,
88-
targetConcurrency: 8,
89-
},
90-
});
91-
```
92-
9387
| Option | Default | Meaning |
9488
| --- | --- | --- |
95-
| `regions` | State actor's current region | Stored compatibility metadata; it does not move direct HTTP execution |
96-
| `createNamespace` | none | Deprecated compatibility option; every app always receives its own stable namespace |
97-
| `scaling.minReplicas` | `0` | App-defined actor runner setting; compatibility metadata for direct HTTP |
98-
| `scaling.maxReplicas` | `128` | App-defined actor runner setting; compatibility metadata for direct HTTP |
99-
| `scaling.targetConcurrency` | `8` | App-defined actor runner setting; compatibility metadata for direct HTTP |
100-
101-
Every app receives its own Rivet namespace. Locally, configure an Engine secret
102-
token with permission to create namespaces. On Rivet Cloud, set
103-
`RIVET_CLOUD_TOKEN` to a `cloud_api_*` token for the project; `deployApp()` uses
104-
it to create the namespace and namespace-scoped actor credentials. Keep this
105-
management token server-side. The deployment result includes the app's Engine
106-
endpoint, namespace, pool, and publishable token for connecting to app-defined
107-
actors.
108-
109-
Rivet Compute automatically uses the deployment's `.rivet.run/api/rivet`
110-
callback. For another public host, set `DYNAMIC_APPS_CALLBACK_URL` to its
111-
origin.
89+
| `createNamespace` | `true` | Set `false` to disable namespace provisioning entirely. Unset keeps the default. Apps that use `rivetkit` require a namespace |
90+
91+
See [Connect to Rivet](/dynamic-apps/docs/connect) for credentials and
92+
namespace setup.
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
title: "Generate"
3+
description: "Generate an app's files with a model and the Dynamic Apps skills."
4+
skill: true
5+
---
6+
7+
import ExampleLinkBar from "@/components/docs/ExampleLinkBar.astro";
8+
9+
<ExampleLinkBar href="https://github.com/rivet-dev/dynamic-apps/tree/main/examples/apps-ai-builder" />
10+
11+
An LLM writes the app as a set of files.
12+
13+
## Getting started
14+
15+
Use `generateObject` with a schema for the file tree, and pass the skills from
16+
`@rivet-dev/dynamic-apps` as the system prompt so the model knows the supported
17+
project layout:
18+
19+
```ts
20+
import { anthropic } from "@ai-sdk/anthropic";
21+
import { rivetActorsSkill, webServerSkill } from "@rivet-dev/dynamic-apps";
22+
import { generateObject } from "ai";
23+
import { z } from "zod";
24+
25+
const { object } = await generateObject({
26+
model: anthropic("claude-sonnet-5"),
27+
// Skills teach the model the supported project layout.
28+
system: [webServerSkill, rivetActorsSkill].join("\n\n"),
29+
schema: z.object({ files: z.record(z.string(), z.string()) }),
30+
prompt: "Build a team board.",
31+
});
32+
33+
const { files } = object;
34+
```
35+
36+
Pass the generated `files` to [`deployApp()`](/dynamic-apps/docs/deploy). If the
37+
build fails, feed the diagnostics back to the model and deploy again. See
38+
[Deploy](/dynamic-apps/docs/deploy) for the repair loop.
39+
40+
## Skills
41+
42+
Skills are prompt fragments exported by `@rivet-dev/dynamic-apps`. Each one
43+
carries the instructions and a complete starter project for one kind of app, so
44+
generated code compiles and serves on the first try more often.
45+
46+
| Skill | What the model learns |
47+
| --- | --- |
48+
| `webServerSkill` | The supported TypeScript Fetch-handler layout: `package.json`, `tsconfig.json`, and a Hono entrypoint with no application-owned listener. |
49+
| `rivetActorsSkill` | The same layout plus Rivet Actors: durable state, actions, and HTTP routes backed by actors. |
50+
51+
More skills will be added over time.

0 commit comments

Comments
 (0)