Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<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 executes the request in the V8 isolate for that app." style="width:100%;height:auto;max-width:680px;display:block;margin:2.5rem auto 1rem;">
<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;">
<defs>
<marker id="apps-arrow" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M0,0 L10,5 L0,10 z" fill="#1b1916" />
Expand All @@ -25,5 +25,5 @@
<line x1="485" y1="80" x2="509" y2="80" stroke="#1b1916" stroke-width="1.5" marker-end="url(#apps-arrow)" />

<rect x="512" y="50" width="140" height="60" rx="8" fill="#ffffff" stroke="#1b1916" stroke-width="1.2" />
<text x="582" y="84" text-anchor="middle" font-family="var(--sl-font)" font-size="12" font-weight="600" fill="#1b1916">V8 isolate</text>
<text x="582" y="84" text-anchor="middle" font-family="var(--sl-font)" font-size="12" font-weight="600" fill="#1b1916">agentOS VM</text>
</svg>
11 changes: 6 additions & 5 deletions vendor/dynamic-apps/docs/content/docs/architecture.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ The request lifecycle looks like:

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.
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

Expand All @@ -35,11 +36,11 @@ 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, while the
same app's ordinary HTTP handler still runs through the agentOS VM.
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. App-defined actor workers share the host process,
so run one trust domain per container for mutually untrusted tenants.
boundary for direct requests and app-defined actor workers.
</Note>
5 changes: 5 additions & 0 deletions vendor/dynamic-apps/docs/content/docs/core.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,9 @@ change for the entire lifetime of every serving process.
Call `await dynamicApps.dispose()` during shutdown to release subscriptions,
build resources, cached runtimes, and agentOS contexts.

Deployed app entrypoints only export a Fetch handler; they never bind a port.
If Core loads an actor-enabled release, provide its `server.environment` and a
shared `serverRuntime`. The standard `@rivet-dev/dynamic-apps` package supplies
that actor runtime automatically.

Get started with the [Quickstart (Core)](/dynamic-apps/docs/quickstart-core).
21 changes: 6 additions & 15 deletions vendor/dynamic-apps/docs/content/docs/deploy.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -44,29 +44,20 @@ await deployApp({
An app needs two things:

- A `package.json`.
- An entrypoint that either listens on the `PORT` environment variable or
default-exports a `fetch` handler.
- An entrypoint that default-exports a `fetch` handler.

**Listen on `PORT`:**
Dynamic Apps owns the HTTP listener. Application code must not call `serve()`,
`listen()`, or `registry.start()`.

With Hono, export the app directly:

```ts
import { serve } from "@hono/node-server";
import { Hono } from "hono";

const app = new Hono();
app.get("/", (c) => c.json({ ok: true }));

serve({ fetch: app.fetch, port: Number(process.env.PORT ?? 3000) });
```

**Or export a `fetch` handler:**

```ts
export default {
fetch() {
return new Response("hello");
},
};
export default app;
```

## Build and repair loop
Expand Down
2 changes: 1 addition & 1 deletion vendor/dynamic-apps/docs/content/docs/generate.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ generated code compiles and serves on the first try more often.

| Skill | What the model learns |
| --- | --- |
| `webServerSkill` | The supported TypeScript HTTP server layout: `package.json`, `tsconfig.json`, and a Hono entrypoint. |
| `webServerSkill` | The supported TypeScript Fetch-handler layout: `package.json`, `tsconfig.json`, and a Hono entrypoint with no application-owned listener. |
| `rivetActorsSkill` | The same layout plus Rivet Actors: durable state, actions, and HTTP routes backed by actors. |

More skills will be added over time.
2 changes: 1 addition & 1 deletion vendor/dynamic-apps/docs/content/docs/multiplayer.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import ExampleLinkBar from "@/components/docs/ExampleLinkBar.astro";

## Example generated code

<CodeSnippet file="examples/apps-multiplayer/fixtures/app/src/index.ts" title="Server" />
<CodeSnippet file="examples/apps-multiplayer/fixtures/app/src/index.ts" title="App" />

## Deploy and connect

Expand Down
3 changes: 2 additions & 1 deletion vendor/dynamic-apps/docs/content/docs/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ npm pkg set type=module

Mount the private Rivet callback separately from application traffic. The
callback lets Rivet reach your server to manage deployments. Ordinary app
requests are served by `appsRouter` through a cached agentOS VM.
requests are served by `appsRouter` through agentOS. Generated app code only
exports a Fetch handler; it does not create its own listener.

<CodeSnippet file="examples/apps-hello-world/src/server.ts" title="src/server.ts" />

Expand Down
2 changes: 1 addition & 1 deletion vendor/dynamic-apps/docs/content/docs/sqlite.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import ExampleLinkBar from "@/components/docs/ExampleLinkBar.astro";

## Example generated code

<CodeSnippet file="examples/apps-sqlite/fixtures/app/src/index.ts" title="Server" />
<CodeSnippet file="examples/apps-sqlite/fixtures/app/src/index.ts" title="App" />

## Deploy and connect

Expand Down
2 changes: 1 addition & 1 deletion vendor/dynamic-apps/docs/content/docs/workflows.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import ExampleLinkBar from "@/components/docs/ExampleLinkBar.astro";

## Example generated code

<CodeSnippet file="examples/apps-workflows/fixtures/app/src/index.ts" title="Server" />
<CodeSnippet file="examples/apps-workflows/fixtures/app/src/index.ts" title="App" />

## Deploy and connect

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,10 @@
"check-types": "tsc --noEmit"
},
"dependencies": {
"@hono/node-server": "2.0.11",
"hono": "4.12.9"
"hono": "4.13.5"
},
"devDependencies": {
"@types/node": "22.19.15",
"typescript": "5.7.3"
"@types/node": "22.20.1",
"typescript": "5.9.3"
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { serve } from "@hono/node-server";
import { Hono } from "hono";

const app = new Hono();
Expand All @@ -9,7 +8,4 @@ app.get("/", (context) =>
}),
);

serve({
fetch: app.fetch,
port: Number(process.env.PORT ?? 3000),
});
export default app;
16 changes: 8 additions & 8 deletions vendor/dynamic-apps/examples/apps-ai-builder/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,16 @@
"check-types": "tsc --noEmit"
},
"dependencies": {
"@ai-sdk/anthropic": "^4.0.19",
"@hono/node-server": "^2.0.11",
"@ai-sdk/anthropic": "^4.0.46",
"@hono/node-server": "^2.1.1",
"@rivet-dev/dynamic-apps": "workspace:*",
"ai": "^7.0.37",
"hono": "^4.12.9",
"zod": "^4.1.0"
"ai": "^7.0.86",
"hono": "^4.13.5",
"zod": "^4.5.4"
},
"devDependencies": {
"@types/node": "^22.19.15",
"tsx": "^4.20.6",
"typescript": "^5.7.3"
"@types/node": "^22.20.1",
"tsx": "^4.23.13",
"typescript": "^5.9.3"
}
}
2 changes: 1 addition & 1 deletion vendor/dynamic-apps/examples/apps-ai-builder/src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ import {
webServerSkill,
} from "@rivet-dev/dynamic-apps";
import { generateObject } from "ai";
import { z } from "zod";
import { Hono } from "hono";
import { z } from "zod";

const editablePaths = [
"package.json",
Expand Down
10 changes: 5 additions & 5 deletions vendor/dynamic-apps/examples/apps-core-quickstart/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@
"check-types": "tsc --noEmit"
},
"dependencies": {
"@hono/node-server": "^2.0.11",
"@hono/node-server": "^2.1.1",
"@rivet-dev/dynamic-apps-core": "workspace:*",
"hono": "^4.12.9"
"hono": "^4.13.5"
},
"devDependencies": {
"@types/node": "^22.19.15",
"tsx": "^4.20.6",
"typescript": "^5.7.3"
"@types/node": "^22.20.1",
"tsx": "^4.23.13",
"typescript": "^5.9.3"
}
}
38 changes: 11 additions & 27 deletions vendor/dynamic-apps/examples/apps-core-quickstart/src/server.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// docs:start setup
import { serve } from "@hono/node-server";
import {
type ActiveRelease,
Expand All @@ -19,12 +20,6 @@ const dynamicApps = createDynamicApps({
...input.artifact,
bytes: new Uint8Array(input.artifact.bytes),
},
regions: input.regions ?? ["local"],
scaling: {
minReplicas: input.scaling?.minReplicas ?? 0,
maxReplicas: input.scaling?.maxReplicas ?? 1,
targetConcurrency: input.scaling?.targetConcurrency ?? 8,
},
maxRequestBytes: 1024 * 1024,
maxResponseBytes: 4 * 1024 * 1024,
};
Expand All @@ -38,8 +33,6 @@ const dynamicApps = createDynamicApps({
return release
? {
...release,
regions: [...release.regions],
scaling: { ...release.scaling },
artifact: {
...release.artifact,
bytes: new Uint8Array(release.artifact.bytes),
Expand All @@ -57,21 +50,9 @@ const dynamicApps = createDynamicApps({
};
},
});
// docs:end setup

const app = new Hono();
app.route("/apps", dynamicApps.appsRouter);

let server: ReturnType<typeof serve> | undefined;
let shuttingDown = false;
const shutdown = async () => {
if (shuttingDown) return;
shuttingDown = true;
await dynamicApps.dispose();
server?.close();
};
process.once("SIGINT", () => void shutdown());
process.once("SIGTERM", () => void shutdown());

// docs:start deploy
await dynamicApps.deployApp({
appId: "hello",
files: {
Expand All @@ -89,10 +70,13 @@ await dynamicApps.deployApp({
`,
},
});
// docs:end deploy

// docs:start routing
const app = new Hono();
app.route("/apps", dynamicApps.appsRouter);

const hostIndex = process.argv.indexOf("--host");
const hostname = hostIndex >= 0 ? process.argv[hostIndex + 1] : "127.0.0.1";
if (!hostname) throw new Error("--host requires a value");
const port = Number(process.env.PORT ?? 3000);
server = serve({ fetch: app.fetch, hostname, port });
console.log(`Dynamic Apps Core listening on http://${hostname}:${port}`);
serve({ fetch: app.fetch, port });
console.log(`Dynamic Apps Core listening on http://localhost:${port}`);
// docs:end routing
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,7 @@
"version": "0.0.0",
"private": true,
"main": "src/index.ts",
"scripts": {
"check-types": "node --check src/index.mjs"
},
"dependencies": {
"hono": "^4.12.9"
"hono": "4.13.5"
}
}
10 changes: 5 additions & 5 deletions vendor/dynamic-apps/examples/apps-hello-world/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,13 @@
"check-types": "tsc --noEmit"
},
"dependencies": {
"@hono/node-server": "^2.0.11",
"@hono/node-server": "^2.1.1",
"@rivet-dev/dynamic-apps": "workspace:*",
"hono": "^4.12.9"
"hono": "^4.13.5"
},
"devDependencies": {
"@types/node": "^22.19.15",
"tsx": "^4.20.6",
"typescript": "^5.7.3"
"@types/node": "^22.20.1",
"tsx": "^4.23.13",
"typescript": "^5.9.3"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ await deployApp({
type: "module",
main: "src/index.ts",
dependencies: {
hono: "^4.12.9",
hono: "4.13.5",
},
}),
"src/index.ts": `
Expand Down
15 changes: 15 additions & 0 deletions vendor/dynamic-apps/examples/apps-multiplayer/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# Dynamic Apps: Multiplayer

The deployed app defines one keyed room actor. Clients share player positions
and receive realtime events whenever a player joins or moves.

Run the example with Node.js 22 or newer:

```sh
pnpm --dir examples/apps-multiplayer start
# In another terminal:
pnpm --dir examples/apps-multiplayer client
```

The app's ordinary HTTP handler is available at
`http://localhost:3000/apps/multiplayer-room/`.
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@
"type": "module",
"main": "src/index.ts",
"dependencies": {
"@hono/node-server": "2.1.1",
"hono": "4.13.3",
"hono": "4.13.5",
"rivetkit": "2.3.11"
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { serve } from "@hono/node-server";
import { Hono } from "hono";
import { actor, event, setup } from "rivetkit";

Expand Down Expand Up @@ -30,15 +29,4 @@ app.get("/", (c) =>
c.json({ message: "Use the RivetKit client to join a room." }),
);

// Dynamic Apps runs the actors in serverless mode and waits for this listener.
if (process.env.RIVETKIT_RUNTIME_MODE === "serverless") {
await new Promise<void>((resolve, reject) => {
const server = serve(
{ fetch: app.fetch, port: Number(process.env.PORT), hostname: "0.0.0.0" },
() => resolve(),
);
server.once("error", reject);
});
}

export default app;
10 changes: 5 additions & 5 deletions vendor/dynamic-apps/examples/apps-multiplayer/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,14 @@
"check-types": "tsc --noEmit"
},
"dependencies": {
"@hono/node-server": "^2.0.11",
"@hono/node-server": "^2.1.1",
"@rivet-dev/dynamic-apps": "workspace:*",
"hono": "^4.12.9",
"hono": "^4.13.5",
"rivetkit": "2.3.11"
},
"devDependencies": {
"@types/node": "^22.19.15",
"tsx": "^4.20.6",
"typescript": "^5.7.3"
"@types/node": "^22.20.1",
"tsx": "^4.23.13",
"typescript": "^5.9.3"
}
}
Loading
Loading