@agent-native/core 0.164.11 → 0.164.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (106) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/calendar/.agents/skills/event-management/SKILL.md +84 -0
  3. package/corpus/templates/calendar/AGENTS.md +5 -0
  4. package/corpus/templates/calendar/actions/delete-event.ts +1 -1
  5. package/corpus/templates/calendar/actions/delete-events.ts +508 -0
  6. package/corpus/templates/calendar/actions/event-action-helpers.ts +1 -1
  7. package/corpus/templates/calendar/actions/list-events.ts +53 -0
  8. package/corpus/templates/calendar/changelog/2026-08-18-the-agent-can-now-remove-many-meetings-at-once-such-as-every.md +6 -0
  9. package/corpus/templates/calendar/server/lib/event-weekday.ts +112 -0
  10. package/corpus/templates/content/actions/_database-source-utils.ts +25 -2
  11. package/corpus/templates/content/actions/_database-utils.ts +2 -0
  12. package/corpus/templates/content/actions/_local-folder-source.ts +186 -2
  13. package/corpus/templates/content/actions/connect-local-folder-source.ts +23 -8
  14. package/corpus/templates/content/actions/disconnect-local-folder-source.ts +3 -3
  15. package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +2 -2
  16. package/corpus/templates/content/actions/sync-local-folder-source.ts +130 -12
  17. package/corpus/templates/content/app/components/LocalFolderLiveSync.tsx +77 -0
  18. package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +354 -8
  19. package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +23 -1
  20. package/corpus/templates/content/app/components/editor/VisualEditor.tsx +78 -1
  21. package/corpus/templates/content/app/components/editor/database/sidebar.tsx +36 -0
  22. package/corpus/templates/content/app/components/editor/local-source-write-state.ts +52 -0
  23. package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +244 -6
  24. package/corpus/templates/content/app/components/sidebar/local-source-hierarchy.ts +189 -0
  25. package/corpus/templates/content/app/hooks/use-create-page.ts +26 -4
  26. package/corpus/templates/content/app/hooks/use-documents.ts +83 -0
  27. package/corpus/templates/content/app/i18n-data.ts +79 -0
  28. package/corpus/templates/content/app/lib/desktop-content-files.ts +57 -1
  29. package/corpus/templates/content/app/lib/local-content-source-files.ts +210 -34
  30. package/corpus/templates/content/app/lib/local-folder-live-sync.ts +191 -0
  31. package/corpus/templates/content/app/root.tsx +2 -1
  32. package/corpus/templates/content/app/routes/_app.local-files.tsx +103 -38
  33. package/corpus/templates/content/docs/product/capabilities/content.source.file-folder.md +2 -2
  34. package/corpus/templates/content/docs/product/features/content.feature.bring-your-local-work.md +3 -3
  35. package/corpus/templates/content/docs/product/roadmap.md +2 -2
  36. package/corpus/templates/content/docs/solutions/2026-08-18-content-navigation-coherence-shape.md +482 -0
  37. package/corpus/templates/content/shared/api.ts +25 -0
  38. package/corpus/templates/factory/AGENTS.md +2 -1
  39. package/corpus/templates/factory/actions/get-factory-graph-version.ts +69 -0
  40. package/corpus/templates/factory/actions/list-factory-graph-versions.ts +82 -0
  41. package/corpus/templates/factory/actions/restore-factory-graph-version.ts +163 -0
  42. package/corpus/templates/factory/actions/save-factory-graph.ts +93 -60
  43. package/corpus/templates/factory/app/components/factory/FactoryHistoryView.tsx +709 -0
  44. package/corpus/templates/factory/app/components/factory/FactoryInspector.tsx +62 -2
  45. package/corpus/templates/factory/app/components/ui/alert-dialog.tsx +1 -0
  46. package/corpus/templates/factory/app/i18n/en-US.ts +43 -0
  47. package/corpus/templates/factory/app/routes/factory.tsx +115 -15
  48. package/corpus/templates/factory/server/plugins/agent-chat.ts +3 -1
  49. package/dist/app-config/auth.d.ts +4 -0
  50. package/dist/app-config/auth.js +7 -0
  51. package/dist/app-config/schema.d.ts +3 -0
  52. package/dist/app-config/schema.js +2 -0
  53. package/dist/cli/code-agent-executor.js +6 -1
  54. package/dist/cli/code-agent-mcp-config.d.ts +2 -2
  55. package/dist/cli/code-agent-mcp-config.js +34 -9
  56. package/dist/client/org/OrgSwitcher.js +8 -4
  57. package/dist/collab/awareness.d.ts +2 -2
  58. package/dist/collab/routes.d.ts +1 -1
  59. package/dist/collab/struct-routes.d.ts +1 -1
  60. package/dist/mcp-client/config.d.ts +11 -0
  61. package/dist/mcp-client/config.js +33 -26
  62. package/dist/notifications/routes.d.ts +3 -3
  63. package/dist/observability/routes.d.ts +3 -3
  64. package/dist/progress/routes.d.ts +1 -1
  65. package/dist/resources/handlers.d.ts +2 -2
  66. package/dist/secrets/routes.d.ts +9 -9
  67. package/dist/server/agent-engine-api-key-route.d.ts +1 -1
  68. package/dist/server/auth.js +15 -1
  69. package/dist/server/realtime-token.d.ts +1 -1
  70. package/docs/content/actions-access-control.mdx +136 -2
  71. package/docs/content/actions-agent-tools.mdx +101 -0
  72. package/docs/content/actions-defining.mdx +2 -2
  73. package/docs/content/audit-log.mdx +1 -1
  74. package/docs/content/authentication.mdx +1 -1
  75. package/docs/content/cloudflare.mdx +43 -0
  76. package/docs/content/creating-templates.mdx +1 -1
  77. package/docs/content/deploy-an-app.mdx +109 -0
  78. package/docs/content/deployment-environment-variables.mdx +101 -0
  79. package/docs/content/deployment.mdx +125 -792
  80. package/docs/content/docs-components.mdx +2 -2
  81. package/docs/content/environment-variables.mdx +41 -41
  82. package/docs/content/file-uploads.mdx +74 -40
  83. package/docs/content/harness-agents.mdx +2 -2
  84. package/docs/content/internationalization.mdx +61 -65
  85. package/docs/content/key-concepts.mdx +3 -3
  86. package/docs/content/multi-app-workspace.mdx +2 -2
  87. package/docs/content/netlify.mdx +47 -0
  88. package/docs/content/node-docker.mdx +138 -0
  89. package/docs/content/organizations-teams-permissions.mdx +5 -5
  90. package/docs/content/other-platforms.mdx +27 -0
  91. package/docs/content/ssr-caching.mdx +45 -0
  92. package/docs/content/template-assets-developers.mdx +2 -2
  93. package/docs/content/template-chat-developers.mdx +2 -2
  94. package/docs/content/template-content-developers.mdx +2 -2
  95. package/docs/content/{local-file-mode.mdx → template-content-local-files.mdx} +2 -1
  96. package/docs/content/template-content-sync.mdx +2 -2
  97. package/docs/content/toolkit-agent-ux.mdx +1 -1
  98. package/docs/content/toolkit-resources.mdx +2 -2
  99. package/docs/content/updating-ui-in-production.mdx +35 -0
  100. package/docs/content/vercel.mdx +38 -0
  101. package/docs/content/what-is-agent-native.mdx +1 -1
  102. package/docs/content/workspace-deployment.mdx +350 -0
  103. package/package.json +1 -1
  104. package/docs/content/agents.mdx +0 -9
  105. package/docs/content/database.mdx +0 -317
  106. package/docs/content/human-approval.mdx +0 -156
@@ -1,845 +1,178 @@
1
1
  ---
2
2
  title: "Deployment"
3
- description: "Deploy agent-native apps to any platform with Nitro presets — Node.js, Vercel, Netlify, Cloudflare, AWS, and more."
3
+ description: "Deploy an Agent-Native app or workspace to a Nitro-compatible platform with a persistent SQL database."
4
+ search: "deployment deploy app workspace hosting database Nitro Vercel Netlify Cloudflare Node.js"
4
5
  ---
5
6
 
6
7
  # Deployment
7
8
 
8
- Agent-native apps use [Nitro](https://nitro.build) under the hood, which means you can deploy to any platform with zero config changes — just set a preset.
9
+ Agent-Native apps run on [Nitro](https://nitro.build), so the same app can
10
+ deploy to Node.js, serverless functions, edge workers, or a managed host. Pick
11
+ the path that matches what you are shipping:
9
12
 
10
- ## Fast path: self-host a Chat app locally {#self-host-quickstart}
13
+ <Cards columns={2}>
11
14
 
12
- There are two good starting points, depending on your role:
15
+ ### :server: [Deploy an app](/docs/deploy-an-app)
13
16
 
14
- - **Building an app?** Start with [Getting Started](/docs/getting-started), then return here when you are ready to put the result somewhere permanent.
15
- - **Operating an app?** Use the local Docker path below first. It exercises the same Node.js, Postgres, authentication, and environment-variable shape you will use on a small server.
17
+ Build and launch one standalone app, or deploy one app from a workspace on its
18
+ own domain.
16
19
 
17
- The local stack intentionally uses a fake auth secret, trust authentication, and a local Postgres volume. It is for `localhost`, not an internet-facing deployment. Do not reuse the trust-authentication setting in production.
20
+ ### :network: [Deploy a workspace](/docs/workspace-deployment)
18
21
 
19
- ### 1. Create the app
22
+ Build several apps behind one origin with shared login and same-origin app to
23
+ app communication.
20
24
 
21
- ```bash
22
- npx @agent-native/core@latest create my-app --standalone --template chat
23
- cd my-app
24
- pnpm install
25
- ```
25
+ </Cards>
26
26
 
27
- The install step creates the lockfile that the production Dockerfile below copies into its build stage.
28
-
29
- ### 2. Add the local Docker files
30
-
31
- Create `Dockerfile`, `.dockerignore`, and `docker-compose.yml` in the app root. `docker compose up --build` needs the Dockerfile, and `.dockerignore` keeps host-only files such as `node_modules` out of the Linux build context. Compose starts Postgres only on the private Compose network.
32
-
33
- You can also download the [Dockerfile](https://www.agent-native.com/examples/self-hosted-chat/Dockerfile), [.dockerignore](https://www.agent-native.com/examples/self-hosted-chat/.dockerignore), [Compose file](https://www.agent-native.com/examples/self-hosted-chat/docker-compose.yml), and [environment template](https://www.agent-native.com/examples/self-hosted-chat/env.example) as a starter fixture. The [Dockerfile](#docker) below matches the fixture.
34
-
35
- ```yaml filename="docker-compose.yml" maxLines=40
36
- services:
37
- app:
38
- build: .
39
- ports:
40
- - "127.0.0.1:3000:3000"
41
- environment:
42
- DATABASE_URL: postgres://agent_native@postgres:5432/agent_native
43
- BETTER_AUTH_SECRET: local-development-secret-change-me-1234567890
44
- ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY:-}
45
- depends_on:
46
- postgres:
47
- condition: service_healthy
48
-
49
- postgres:
50
- image: postgres:18
51
- environment:
52
- POSTGRES_DB: agent_native
53
- POSTGRES_USER: agent_native
54
- POSTGRES_HOST_AUTH_METHOD: trust
55
- volumes:
56
- - postgres-data-v18:/var/lib/postgresql
57
- healthcheck:
58
- test: ["CMD-SHELL", "pg_isready -U agent_native -d agent_native"]
59
- interval: 5s
60
- timeout: 5s
61
- retries: 10
62
-
63
- volumes:
64
- postgres-data-v18:
65
- ```
27
+ ## Before you deploy: choose a persistent database {#persistent-database}
66
28
 
67
- PostgreSQL 18 uses `/var/lib/postgresql` as its volume mount path, unlike PostgreSQL 17 and earlier. The versioned volume name keeps this fresh fixture separate from older local stacks. If you are upgrading an existing PostgreSQL 17 or earlier stack, back up and migrate its data before changing the image or mount path. Do not point this fixture at the old volume without a migration.
29
+ Every deployed app needs a persistent SQL database. Local development can use
30
+ the SQLite file at `data/app.db`, but container, preview, and serverless
31
+ filesystems can be reset at any time.
68
32
 
69
- ### 3. Run it
33
+ <Steps>
70
34
 
71
- ```bash
72
- docker compose up --build
73
- ```
35
+ ### Choose a provider
74
36
 
75
- Open [http://localhost:3000](http://localhost:3000). The app infers its local auth origin from the browser request. Set `ANTHROPIC_API_KEY` in a local `.env` file if you want to use the embedded agent; leave it empty if you only want to verify that the app and database start. Stop the stack with `docker compose down`; add `-v` when you intentionally want to reset the local database.
37
+ Use any Drizzle-compatible SQL backend, such as Postgres, Neon, Supabase,
38
+ Railway, RDS, Turso, or libSQL. The framework detects the database dialect from
39
+ the connection URL.
76
40
 
77
- Before putting this on the public internet, replace the example secret, configure HTTPS and OAuth callback URLs, use a persistent production database, and complete the [production environment checklist](#env-required-prod).
41
+ ### Set the connection string
78
42
 
79
- ## Before You Deploy: Pick a Persistent Database {#persistent-database}
43
+ Set `DATABASE_URL` in the deployment environment before the first production
44
+ deploy. Use `DATABASE_AUTH_TOKEN` too when the provider requires a separate
45
+ token, such as Turso or libSQL.
80
46
 
81
- Every deployed app needs a persistent SQL database. In local development, agent-native falls back to a SQLite file at `data/app.db`; that is convenient on your machine, but it is not durable in containers, previews, or serverless environments where the filesystem can be reset.
47
+ ### Add production secrets
82
48
 
83
- Set `DATABASE_URL` in your deploy provider before promoting an app to production. Agent-native uses Drizzle for schema and queries, so the data layer is portable across Drizzle-compatible SQL backends and the framework auto-detects the dialect from the URL. See [Database](/docs/database#production) for the adapter list and dialect details.
49
+ Set a stable `BETTER_AUTH_SECRET` for a standalone app. A workspace also needs
50
+ `A2A_SECRET` when it uses signed cross-app requests or background handoffs. The
51
+ [deployment environment reference](/docs/deployment-environment-variables)
52
+ covers the rest of the production configuration.
84
53
 
85
- Use `DATABASE_AUTH_TOKEN` only when your database provider requires a separate token, such as Turso/libSQL. For workspaces, all apps inherit the root `DATABASE_URL` by default; set `<APP_NAME>_DATABASE_URL` when one app should use a different database.
54
+ </Steps>
86
55
 
87
- ## Workspace Deploy: One Origin, Many Apps {#workspace-deploy}
56
+ <Callout tone="info">
88
57
 
89
- If your project is a [workspace](/docs/multi-app-workspace), you can ship every app in it to a single origin with one command:
58
+ In a workspace, every app inherits the root `DATABASE_URL` by default. Set
59
+ `<APP_NAME>_DATABASE_URL` only when one app needs a different database.
90
60
 
91
- ```bash
92
- npx @agent-native/core@latest deploy
93
- # https://your-agents.com/mail/* → apps/mail
94
- # https://your-agents.com/calendar/* → apps/calendar
95
- # https://your-agents.com/forms/* → apps/forms
96
- ```
61
+ </Callout>
62
+
63
+ ## Supported deployment targets
97
64
 
98
- Each app is built with `APP_BASE_PATH=/<name>` and `VITE_APP_BASE_PATH=/<name>`, then packaged for the target Nitro preset. Cloudflare Pages is the default preset and uses a generated dispatcher worker at `dist/_worker.js`; Netlify uses one function per app in `.netlify/functions-internal/<app>-server` plus generated redirects; Vercel writes a workspace-level `.vercel/output` using the Build Output API.
65
+ Set a Nitro preset for the target you choose. The app code and database
66
+ contract stay the same.
99
67
 
100
- <Diagram id="doc-block-1aw1umr" title="One origin, many apps" summary={"Each workspace app is built with its own base path and mounted under a path prefix on a single origin — so login and cross-app A2A are same-origin and free."}>
68
+ <Diagram id="doc-block-deployment-targets" title="Supported deployment targets" summary="Agent-Native apps can deploy to Node.js, Vercel, Netlify, Cloudflare, AWS Lambda, and Deno Deploy. Provider logos are shown for the hosted targets with first-party deployment pages.">
101
69
 
102
70
  ```html
103
- <div class="diagram-ws">
104
- <div class="diagram-panel" data-rough>
105
- <strong>https://your-agents.com</strong>
106
- <div class="diagram-row">
107
- <span class="diagram-pill accent">/mail/*</span
108
- ><small class="diagram-muted">apps/mail</small>
109
- </div>
110
- <div class="diagram-row">
111
- <span class="diagram-pill accent">/calendar/*</span
112
- ><small class="diagram-muted">apps/calendar</small>
113
- </div>
114
- <div class="diagram-row">
115
- <span class="diagram-pill accent">/forms/*</span
116
- ><small class="diagram-muted">apps/forms</small>
117
- </div>
118
- </div>
119
- <div class="diagram-col wins">
120
- <span class="diagram-pill ok">shared login session</span
121
- ><span class="diagram-pill ok">zero-config cross-app A2A</span>
122
- </div>
71
+ <div class="deployment-target-grid">
72
+ <a class="deployment-target" href="/docs/node-docker">
73
+ <span class="deployment-target-mark text-mark">N</span>
74
+ <strong>Node.js</strong>
75
+ </a>
76
+ <a class="deployment-target" href="/docs/vercel">
77
+ <span class="deployment-target-mark">
78
+ <img src="/integration-logos/vercel.svg" alt="" />
79
+ </span>
80
+ <strong>Vercel</strong>
81
+ </a>
82
+ <a class="deployment-target" href="/docs/netlify">
83
+ <span class="deployment-target-mark">
84
+ <img src="/integration-logos/netlify.svg" alt="" />
85
+ </span>
86
+ <strong>Netlify</strong>
87
+ </a>
88
+ <a class="deployment-target" href="/docs/cloudflare">
89
+ <span class="deployment-target-mark">
90
+ <img src="/integration-logos/cloudflare.svg" alt="" />
91
+ </span>
92
+ <strong>Cloudflare</strong>
93
+ </a>
94
+ <a class="deployment-target" href="/docs/other-platforms#aws-lambda">
95
+ <span class="deployment-target-mark text-mark">AWS</span>
96
+ <strong>AWS Lambda</strong>
97
+ </a>
98
+ <a class="deployment-target" href="/docs/other-platforms#deno-deploy">
99
+ <span class="deployment-target-mark text-mark">D</span>
100
+ <strong>Deno Deploy</strong>
101
+ </a>
123
102
  </div>
124
103
  ```
125
104
 
126
105
  ```css
127
- .diagram-ws {
128
- display: flex;
129
- align-items: center;
130
- gap: 16px;
131
- flex-wrap: wrap;
132
- }
133
- .diagram-ws .diagram-panel {
134
- display: flex;
135
- flex-direction: column;
136
- gap: 6px;
137
- padding: 14px 16px;
106
+ .deployment-target-grid {
107
+ display: grid;
108
+ grid-template-columns: repeat(3, minmax(0, 1fr));
109
+ gap: 10px;
138
110
  }
139
- .diagram-ws .diagram-row {
111
+ .deployment-target {
140
112
  display: flex;
141
113
  align-items: center;
142
- gap: 8px;
114
+ gap: 10px;
115
+ min-height: 58px;
116
+ padding: 10px 12px;
117
+ border: 1px solid hsl(var(--border));
118
+ border-radius: 10px;
119
+ color: inherit;
120
+ text-decoration: none;
143
121
  }
144
- .diagram-ws .wins {
145
- display: flex;
146
- flex-direction: column;
147
- gap: 8px;
148
- align-items: flex-start;
122
+ .deployment-target:hover {
123
+ border-color: hsl(var(--primary));
149
124
  }
150
- ```
151
-
152
- </Diagram>
153
-
154
- Same-origin deploy gives you two big wins for free:
155
-
156
- - **Shared login session** — log into any app, every app is logged in.
157
- - **Zero-config cross-app A2A** — tagging `@calendar` from mail is a same-origin fetch; no CORS, no JWT signing between siblings.
158
-
159
- Publish the output with:
160
-
161
- ```bash
162
- wrangler pages deploy dist
163
- ```
164
-
165
- For Netlify unified deploys, use the Netlify preset:
166
-
167
- ```bash
168
- npx @agent-native/core@latest deploy --preset netlify
169
- ```
170
-
171
- For Vercel unified deploys, use the Vercel preset:
172
-
173
- ```bash
174
- npx @agent-native/core@latest deploy --preset vercel
175
- ```
176
-
177
- When configuring a provider build command, use the same command with `--build-only`. Vercel should run `npx @agent-native/core@latest deploy --preset vercel --build-only`; the command writes `.vercel/output` directly, so no `vercel.json` is required for workspace routing.
178
-
179
- Hosted workspace builds require `A2A_SECRET` in the deploy provider environment.
180
- This makes Slack, inbound webhooks, and cross-app A2A resume work through signed
181
- background processors. Local `--build-only` artifact checks still run without it.
182
-
183
- Per-app independent deploy is still supported — just `cd apps/<name> && npx @agent-native/core@latest build` like a standalone scaffold.
184
-
185
- ## How It Works {#how-it-works}
186
-
187
- When you run `npx @agent-native/core@latest build`, Nitro builds both the client SPA and the server API into `.output/`:
188
-
189
- <FileTree
190
- id="doc-block-1vbo81"
191
- title="Build output"
192
- entries={[
193
- {
194
- path: ".output/",
195
- note: "self-contained — copy to any environment and run",
196
- },
197
- {
198
- path: ".output/public/",
199
- note: "built SPA (static assets)",
200
- },
201
- {
202
- path: ".output/server/index.mjs",
203
- note: "server entry point",
204
- },
205
- {
206
- path: ".output/server/chunks/",
207
- note: "server code chunks",
208
- },
209
- ]}
210
- />
211
-
212
- The output is self-contained — copy `.output/` to any environment and run it.
213
-
214
- <Diagram id="doc-block-1gqwzzp" title="Build to deploy" summary={"One source tree builds to a Nitro preset; the same self-contained output runs on Node, Vercel, Netlify, Cloudflare, AWS, or Deno. Every instance points at the same persistent DATABASE_URL."}>
215
-
216
- ```html
217
- <div class="diagram-deploy">
218
- <div class="diagram-box" data-rough>App source</div>
219
- <div class="diagram-arrow diagram-muted" aria-hidden="true">&rarr;</div>
220
- <div class="diagram-panel center" data-rough>
221
- <span class="diagram-pill accent">build</span
222
- ><small class="diagram-muted">Nitro preset</small>
223
- </div>
224
- <div class="diagram-arrow diagram-muted" aria-hidden="true">&rarr;</div>
225
- <div class="diagram-grid">
226
- <span class="diagram-pill">Node.js</span
227
- ><span class="diagram-pill">Vercel</span
228
- ><span class="diagram-pill">Netlify</span
229
- ><span class="diagram-pill">Cloudflare</span
230
- ><span class="diagram-pill">AWS Lambda</span
231
- ><span class="diagram-pill">Deno</span>
232
- </div>
233
- <div class="diagram-arrow diagram-muted" aria-hidden="true">&rarr;</div>
234
- <div class="diagram-box" data-rough>
235
- Persistent DATABASE_URL<br /><small class="diagram-muted"
236
- >shared by every instance</small
237
- >
238
- </div>
239
- </div>
240
- ```
241
-
242
- ```css
243
- .diagram-deploy {
244
- display: flex;
245
- align-items: center;
246
- gap: 12px;
247
- flex-wrap: wrap;
125
+ .deployment-target-mark {
126
+ display: grid;
127
+ width: 30px;
128
+ height: 30px;
129
+ place-items: center;
130
+ flex: 0 0 auto;
248
131
  }
249
- .diagram-deploy .center {
250
- display: flex;
251
- flex-direction: column;
252
- align-items: center;
253
- gap: 4px;
254
- padding: 14px 16px;
132
+ .deployment-target-mark img {
133
+ width: 26px;
134
+ height: 26px;
135
+ object-fit: contain;
255
136
  }
256
- .diagram-deploy .diagram-arrow {
257
- font-size: 22px;
258
- line-height: 1;
137
+ .deployment-target-mark.text-mark {
138
+ border-radius: 7px;
139
+ background: hsl(var(--muted));
140
+ font-size: 12px;
141
+ font-weight: 700;
259
142
  }
260
- .diagram-deploy .diagram-grid {
261
- display: grid;
262
- grid-template-columns: repeat(3, minmax(0, 1fr));
263
- gap: 8px;
143
+ @media (max-width: 640px) {
144
+ .deployment-target-grid {
145
+ grid-template-columns: repeat(2, minmax(0, 1fr));
146
+ }
264
147
  }
265
148
  ```
266
149
 
267
150
  </Diagram>
268
151
 
269
- ## Setting the Preset {#setting-the-preset}
270
-
271
- By default, Nitro builds for Node.js. To target a different platform, set the preset in your `vite.config.ts`:
272
-
273
- ```ts
274
- import { agentNative } from "@agent-native/core/vite";
275
- import { defineConfig } from "vite";
276
-
277
- export default defineConfig({
278
- plugins: [agentNative({ nitro: { preset: "vercel" } })],
279
- });
280
- ```
281
-
282
- Or use the `NITRO_PRESET` environment variable at build time:
283
-
284
- ```bash
285
- NITRO_PRESET=netlify npx @agent-native/core@latest build
286
- ```
287
-
288
- ## Node.js (Default) {#nodejs}
289
-
290
- The default preset. Build and run:
291
-
292
- ```bash
293
- npx @agent-native/core@latest build
294
- node .output/server/index.mjs
295
- ```
296
-
297
- Set `PORT` to configure the listen port (default: `3000`).
298
-
299
- Use the current Node.js LTS line for production deploys. As of May 2026, that
300
- is Node.js 24; Node.js 20 reached end-of-life on April 30, 2026 and no longer
301
- receives upstream security updates.
302
-
303
- ### Docker {#docker}
304
-
305
- This Dockerfile works for the standalone app created above. Run `pnpm install` before building so `pnpm-lock.yaml` exists in the build context. Keep the dependency manifest and lockfile in sync when you update the app.
306
-
307
- ```dockerfile filename="Dockerfile"
308
- FROM node:24-slim AS build
309
- WORKDIR /app
310
- COPY package.json pnpm-lock.yaml ./
311
- RUN corepack enable && pnpm install --frozen-lockfile
312
- COPY . .
313
- RUN pnpm build
314
-
315
- FROM node:24-slim
316
- WORKDIR /app
317
- COPY --from=build /app/.output .output
318
- # data/ is a runtime-created SQLite directory — do not copy a dev DB into prod.
319
- # For production, set DATABASE_URL to a hosted Postgres or Turso instance.
320
- RUN mkdir -p /app/data
321
- ENV NODE_ENV=production
322
- ENV PORT=3000
323
- EXPOSE 3000
324
- CMD ["node", ".output/server/index.mjs"]
325
- ```
326
-
327
- Create `.dockerignore` alongside the Dockerfile:
328
-
329
- ```text filename=".dockerignore"
330
- node_modules
331
- .output
332
- data
333
- .env
334
- .env.*
335
- !.env.example
336
- .git
337
- ```
338
-
339
- ### Self-hosted workspace with Docker Compose {#workspace-docker-compose}
340
-
341
- The Dockerfile above is for one standalone app. A workspace is not one Nitro
342
- server process today: each app still builds to its own `.output/` directory and
343
- runs its own Node/Nitro server. For a VPS, run one container per workspace app,
344
- point every app at the same Postgres database and shared production secrets, and
345
- put a reverse proxy in front that routes path prefixes (`/mail`, `/calendar`,
346
- ...) to the matching app container.
347
-
348
- Build each app with the same base path that the proxy will use at runtime. The
349
- base path is a build-time input for the client bundle and a runtime input for
350
- the server:
351
-
352
- ```bash maxLines=8
353
- APP_BASE_PATH=/mail VITE_APP_BASE_PATH=/mail npx @agent-native/core@latest build
354
- node .output/server/index.mjs
355
- ```
356
-
357
- A reusable workspace Dockerfile can take the app name and path prefix as build
358
- arguments:
359
-
360
- ```dockerfile filename="Dockerfile.workspace-app" maxLines=12
361
- FROM node:24-slim AS build
362
- WORKDIR /workspace
363
- RUN corepack enable
364
- COPY package.json pnpm-lock.yaml pnpm-workspace.yaml ./
365
- COPY packages ./packages
366
- COPY apps ./apps
367
- ARG APP_NAME
368
- ARG APP_BASE_PATH
369
- ENV APP_BASE_PATH=$APP_BASE_PATH
370
- ENV VITE_APP_BASE_PATH=$APP_BASE_PATH
371
- RUN pnpm install --frozen-lockfile
372
- RUN pnpm --dir apps/$APP_NAME build
373
-
374
- FROM node:24-slim
375
- WORKDIR /app
376
- ARG APP_NAME
377
- ARG APP_BASE_PATH
378
- ENV NODE_ENV=production
379
- ENV PORT=3000
380
- ENV APP_BASE_PATH=$APP_BASE_PATH
381
- COPY --from=build /workspace/apps/$APP_NAME/.output .output
382
- EXPOSE 3000
383
- CMD ["node", ".output/server/index.mjs"]
384
- ```
385
-
386
- Then compose Postgres, the app containers, and your proxy. This example uses
387
- Caddy because it can terminate HTTP locally or sit behind Cloudflare's SSL
388
- proxy, but the important part is the path-prefix routing:
389
-
390
- ```yaml filename="docker-compose.yml" maxLines=30
391
- services:
392
- postgres:
393
- image: postgres:17
394
- restart: unless-stopped
395
- environment:
396
- POSTGRES_DB: agent_native
397
- POSTGRES_USER: agent_native
398
- POSTGRES_PASSWORD: change-me
399
- volumes:
400
- - postgres-data:/var/lib/postgresql/data
401
-
402
- mail:
403
- build:
404
- context: .
405
- dockerfile: Dockerfile.workspace-app
406
- args:
407
- APP_NAME: mail
408
- APP_BASE_PATH: /mail
409
- restart: unless-stopped
410
- environment:
411
- DATABASE_URL: postgres://agent_native:change-me@postgres:5432/agent_native
412
- BETTER_AUTH_URL: https://agents.example.com/mail
413
- BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET}
414
- A2A_SECRET: ${A2A_SECRET}
415
- ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
416
- depends_on:
417
- - postgres
418
-
419
- calendar:
420
- build:
421
- context: .
422
- dockerfile: Dockerfile.workspace-app
423
- args:
424
- APP_NAME: calendar
425
- APP_BASE_PATH: /calendar
426
- restart: unless-stopped
427
- environment:
428
- DATABASE_URL: postgres://agent_native:change-me@postgres:5432/agent_native
429
- BETTER_AUTH_URL: https://agents.example.com/calendar
430
- BETTER_AUTH_SECRET: ${BETTER_AUTH_SECRET}
431
- A2A_SECRET: ${A2A_SECRET}
432
- ANTHROPIC_API_KEY: ${ANTHROPIC_API_KEY}
433
- depends_on:
434
- - postgres
435
-
436
- proxy:
437
- image: caddy:2
438
- restart: unless-stopped
439
- ports:
440
- - "80:80"
441
- - "443:443"
442
- volumes:
443
- - ./Caddyfile:/etc/caddy/Caddyfile:ro
444
- depends_on:
445
- - mail
446
- - calendar
447
-
448
- volumes:
449
- postgres-data:
450
- ```
451
-
452
- ```caddyfile filename="Caddyfile"
453
- agents.example.com {
454
- reverse_proxy /mail* mail:3000
455
- reverse_proxy /calendar* calendar:3000
456
- }
457
- ```
458
-
459
- Notes for this shape:
460
-
461
- - Keep `DATABASE_URL`, `BETTER_AUTH_SECRET`, `A2A_SECRET`, and other production
462
- secrets identical across app containers when you want shared auth, shared
463
- credentials, and cross-app A2A.
464
- - Set `BETTER_AUTH_URL` to the public URL for that app path. OAuth callback URLs
465
- should use the same public URL.
466
- - Do not rely on the container filesystem for production data; use Postgres (or
467
- another persistent SQL backend) for `DATABASE_URL`.
468
- - If Cloudflare terminates TLS in front of the VPS, configure Cloudflare to send
469
- normal `X-Forwarded-Proto` / `X-Forwarded-Host` headers and route the public
470
- host to the proxy container.
471
-
472
- ## Vercel {#vercel}
473
-
474
- ```ts filename="vite.config.ts"
475
- export default defineConfig({
476
- plugins: [agentNative({ nitro: { preset: "vercel" } })],
477
- });
478
- ```
479
-
480
- Deploy via the Vercel CLI or git push:
481
-
482
- ```bash
483
- vercel deploy
484
- ```
485
-
486
- For a workspace, build every app into one Vercel Build Output API bundle:
487
-
488
- ```bash
489
- npx @agent-native/core@latest deploy --preset vercel
490
- ```
491
-
492
- For Vercel Git deployments, set the build command to:
493
-
494
- ```bash
495
- npx @agent-native/core@latest deploy --preset vercel --build-only
496
- ```
497
-
498
- The workspace build copies each app's Nitro `vercel` output into the root `.vercel/output`, gives each function its own mount-path environment, and writes the route config that serves apps at `/<app-id>`.
499
-
500
- ## Netlify {#netlify}
501
-
502
- The Nitro `netlify` preset works well and, in practice, has given us much faster cold starts than Cloudflare Pages (~200ms TTFB vs ~9s) for templates that talk to external Postgres (Neon). Either set the preset in `vite.config.ts`:
503
-
504
- ```ts filename="vite.config.ts"
505
- export default defineConfig({
506
- plugins: [agentNative({ nitro: { preset: "netlify" } })],
507
- });
508
- ```
509
-
510
- …or set `NITRO_PRESET=netlify` at build time.
511
-
512
- For a workspace, deploy every app from one Netlify site by running:
513
-
514
- ```bash
515
- npx @agent-native/core@latest deploy --preset netlify
516
- ```
517
-
518
- The workspace build writes static assets under `dist/_workspace_static/` and routes each app to its own Netlify function without forced asset redirects, so files like `/mail/assets/...` are served statically before the server function handles app routes.
519
-
520
- ### Keep-warm and scale-to-zero databases {#netlify-keep-warm}
521
-
522
- When enabled, the Netlify build emits a scheduled function that requests `/_agent-native/health` **every minute**. That keeps the server function and its database warm so the next visitor after an idle period doesn't pay a cold start (1–5s on Neon).
523
-
524
- Keep-warm is **disabled by default** because a once-a-minute wake prevents autosuspend on metered scale-to-zero databases. On a free-tier Neon this can exhaust the compute quota, and the database then rejects **all** reads mid-session — the symptom is a dead app, not a slow one. Set `AGENT_NATIVE_ENABLE_KEEP_WARM=1` when the lower next-visitor latency is worth the database awake-time.
525
-
526
- The keep-warm schedule and background-function warm only apply after the main keep-warm flag is enabled:
527
-
528
- | Variable | Effect |
529
- | ------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
530
- | `AGENT_NATIVE_ENABLE_KEEP_WARM` | Opts into the scheduled server/database warm. Unset by default. |
531
- | `AGENT_NATIVE_DISABLE_KEEP_WARM` | Compatibility kill switch that wins over the opt-in flag when set. |
532
- | `AGENT_NATIVE_KEEP_WARM_SCHEDULE` | Overrides the cadence with a 5-field cron expression, e.g. `*/5 * * * *`. The default cadence when enabled is `* * * * *`; an unparseable value **fails the build**. |
533
- | `AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND` | Drops only the background-function warm, keeping the server warm. Warming `server` is one request; warming `-background` starts a fresh container that re-probes the schema. |
534
-
535
- If you are on a free or otherwise quota-limited database tier, leave `AGENT_NATIVE_ENABLE_KEEP_WARM` unset. If you opt in, use a slower cadence or set `AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND=1` to drop the more expensive background warm.
536
-
537
- ### On-demand schema DDL {#skip-ensure-tables}
538
-
539
- Each store calls `ensureTable()` on its first database touch, which probes
540
- `information_schema` / `pg_indexes` and issues `CREATE TABLE` / `ADD COLUMN` /
541
- `CREATE INDEX` only for what is genuinely missing. On a long-lived Node server
542
- that cost is paid once per boot and is invisible. On serverless, "first touch" is
543
- **every cold start**, across ~57 stores — so a cold request can pay a few hundred
544
- serial round trips before it does any real work.
545
-
546
- Two things reduce it:
547
-
548
- - The probes are answered from **one batched introspection pass per database**
549
- (two queries), instead of one query per table, column, and index. This is
550
- automatic and needs no configuration.
551
- - `AGENT_NATIVE_SKIP_ENSURE_TABLES=1` skips the probe-and-DDL machinery
552
- altogether, which is the largest cold-start latency win available.
553
-
554
- Set `AGENT_NATIVE_SKIP_ENSURE_TABLES=1` when your database schema is already
555
- migrated and you do not want application boot creating or altering tables — the
556
- normal case for a production deployment with a real migration step.
557
-
558
- <Callout tone="risk">
559
-
560
- **This flag does not and cannot fail closed.** If the schema is in fact missing, the first real query fails loudly rather than being repaired on the fly — which is the point, but it means you own the migration story. Do not set it on a deployment that relies on application boot to provision its tables.
561
-
562
- </Callout>
563
-
564
- ## Cloudflare Pages {#cloudflare-pages}
565
-
566
- ```ts filename="vite.config.ts"
567
- export default defineConfig({
568
- plugins: [agentNative({ nitro: { preset: "cloudflare_pages" } })],
569
- });
570
- ```
571
-
572
- ## Cloudflare Workers {#cloudflare-workers}
573
-
574
- Use Nitro's native module output for a Cloudflare Worker. This keeps SSR
575
- enabled and emits the Worker entry point plus static assets under
576
- `.output/server` and `.output/public`:
577
-
578
- ```ts filename="vite.config.ts"
579
- export default defineConfig({
580
- plugins: [agentNative({ nitro: { preset: "cloudflare_module" } })],
581
- });
582
- ```
583
-
584
- Build and deploy the generated Worker with Wrangler:
585
-
586
- ```bash
587
- npx @agent-native/core@latest build
588
- npx wrangler deploy --config .output/server/wrangler.json
589
- ```
590
-
591
- You can also select the preset explicitly with
592
- `NITRO_PRESET=cloudflare_module`. Configure a durable external SQL database
593
- before deploying; a Worker filesystem is not a persistent application store.
594
-
595
- ## AWS Lambda {#aws-lambda}
596
-
597
- ```ts filename="vite.config.ts"
598
- export default defineConfig({
599
- plugins: [agentNative({ nitro: { preset: "aws_lambda" } })],
600
- });
601
- ```
602
-
603
- ## Deno Deploy {#deno-deploy}
604
-
605
- ```ts filename="vite.config.ts"
606
- export default defineConfig({
607
- plugins: [agentNative({ nitro: { preset: "deno_deploy" } })],
608
- });
609
- ```
610
-
611
- ## SSR Caching {#ssr-caching}
612
-
613
- Every SSR HTML response and every React Router `.data` response is one impersonal, public shell: `createH3SSRHandler` strips cookies before rendering, so the same bytes are correct for every visitor and all personalization happens on the client after load. That is what makes it safe to stamp a single hard-cache policy on those responses by default:
614
-
615
- ```txt
616
- cache-control: public, max-age=600, stale-while-revalidate=604800, stale-if-error=3600
617
- ```
618
-
619
- The same value is mirrored onto `cdn-cache-control` and `netlify-cdn-cache-control`. One shared CDN entry then serves the whole site instead of a per-request render, which is the single biggest lever on first-response latency — leave it on unless one of the cases below applies to your deployment.
620
-
621
- ### Overriding with `AGENT_NATIVE_SSR_CACHE` {#ssr-cache-env}
622
-
623
- Set `AGENT_NATIVE_SSR_CACHE` in the deploy environment to change the policy for the whole deployment:
624
-
625
- | Value | Result |
626
- | --------------------------------------------------- | ------------------------------------------------------------------------------------ |
627
- | unset, `on`, `default`, `true`, `1` | The default policy above, unchanged. Recommended — best performance. |
628
- | `off`, `false`, `0`, `none`, `no-store`, `disabled` | `no-store` on `cache-control`, `cdn-cache-control`, and `netlify-cdn-cache-control`. |
629
- | A duration: `30`, `30s`, `5m`, `2h` | `public, max-age=<n>, stale-while-revalidate=<n>, stale-if-error=3600`. |
630
-
631
- Bare numbers are seconds. `stale-while-revalidate` deliberately mirrors `max-age` for a custom duration: a short freshness window paired with the default seven-day stale window would hand back exactly the staleness you opted out of. An unrecognized value logs a warning and falls back to the default, so a typo can never silently disable the CDN.
632
-
633
- Turn it down or off when either of these is true:
634
-
635
- 1. **Your host does not purge its CDN on deploy.** Netlify and Vercel do; some self-managed setups do not. Without a purge, a shipped build can keep serving the previous shell for `max-age` plus `stale-while-revalidate`.
636
- 2. **Your loaders return mutable public data.** After a successful mutation, a `useRevalidator()` or redirect-after-action re-fetch can read the browser's cached `.data` copy instead of fresh loader output.
637
-
638
- The setting applies to every public-shell surface: React Router SSR HTML and `.data`, the login HTML shell, the `/_agent-native/speculation-rules.json` route, the docs site, and the public-form SSR in the forms template. The generated Cloudflare Worker resolves it at build time, so set it in the deploy environment before the build runs.
639
-
640
- Three things to keep in mind:
641
-
642
- - **Turning caching off does not make SSR personalized.** Cookies are still stripped before render and SSR loaders still see the anonymous branch. Per-user data must still be resolved client-side. This variable controls cache _duration_ only.
643
- - **It is deployment-wide, not per-route, by design.** A per-route or per-request cache override is how one visitor's payload ends up in another visitor's shared CDN entry, so the framework does not offer one — `guard:ssr-cache-shell` enforces it.
644
- - **For mutation-fresh data, prefer actions over disabling the cache.** App data belongs in [actions](/docs/actions-overview), read from the client with `useActionQuery` / `useActionMutation` and kept live by `useDbSync()` polling; none of that goes through the SSR shell cache. Keep SSR loaders rendering the public shell, and reach for this variable only when you genuinely want loader data fresher.
645
-
646
- ## Environment Variables {#environment-variables}
647
-
648
- For the repository-wide reference, including template, provider, local, and CI
649
- variables, see [Environment Variables](/docs/environment-variables). The tables
650
- below remain the authoritative deployment guide for production values and
651
- hosting behavior.
652
-
653
- ### Build / Runtime {#env-runtime}
654
-
655
- | Variable | Description |
656
- | ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
657
- | `PORT` | Server port (Node.js only) |
658
- | `NITRO_PRESET` | Override build preset at build time |
659
- | `APP_BASE_PATH` | Mount the app under a prefix (e.g. `/mail`). Set automatically by `npx @agent-native/core@latest deploy`; leave unset for standalone. |
660
- | `APP_URL` | Optional canonical public origin. Checked ahead of `BETTER_AUTH_URL` when resolving the A2A JWT audience, the OAuth public origin, self-dispatch webhook targets, and generated resource links. Set it explicitly on preview/staging deploys where the request host is unreliable. |
661
- | `AGENT_PROD_CODE_EXECUTION` | Optional production code-execution mode: `off` (default), `sandboxed`, or `trusted`. See [Production Code Execution](#production-code-execution). |
662
- | `AGENT_NATIVE_SSR_CACHE` | Deployment-wide SSR shell cache policy: unset/`on` keeps the default hard-cache, `off` sends `no-store`, or a duration such as `30s` / `5m` sets a shorter freshness. See [SSR Caching](#ssr-caching). |
663
- | `AGENT_NATIVE_ENABLE_KEEP_WARM` | Opt into the Netlify keep-warm scheduled function. Unset by default; see [Keep-warm](#netlify-keep-warm). |
664
- | `AGENT_NATIVE_DISABLE_KEEP_WARM` | Compatibility kill switch for the Netlify keep-warm scheduled function. See [Keep-warm](#netlify-keep-warm). |
665
- | `AGENT_NATIVE_KEEP_WARM_SCHEDULE` | Override the keep-warm cadence with a 5-field cron expression (default `* * * * *` when enabled). An unparseable value fails the build. See [Keep-warm](#netlify-keep-warm). |
666
- | `AGENT_NATIVE_DISABLE_KEEP_WARM_BACKGROUND` | Drop only the background-function warm and keep the server warm. See [Keep-warm](#netlify-keep-warm). |
667
- | `AGENT_NATIVE_DISABLE_INPROCESS_SWEEPS` | Turn off the in-process backstop sweep timers (automation redispatch, agent-teams reconciliation, sandbox-execution recovery). On serverless these run per warm container, so their query rate scales with instance count. Set it **only** where a durable scheduler already drives the same recovery, or queued work will not be retried. |
668
- | `AGENT_NATIVE_SKIP_ENSURE_TABLES` | Skip on-demand schema DDL entirely. Removes the schema-probe round trips every cold start pays before doing any work. Requires that your schema is already migrated — see [On-demand schema DDL](#skip-ensure-tables). |
669
- | `AGENT_NATIVE_BUILDER_RELAY_SECRET` | Dedicated 32+ char shared HMAC secret for preview-safe Builder authorization relay. Set the same value on the approved corporate callback deployment and preview deployments only when authorization must land in an isolated preview database. |
670
- | `AGENT_NATIVE_BUILDER_RELAY_TARGET_ORIGINS` | Corporate callback-only, comma-separated allowlist of exact trusted preview origins (for example, `https://0123456789abcdef01234567--content.netlify.app`). Set it only on the corporate callback deployment. Netlify origins must use an immutable, deploy-specific 24-hex permalink; mutable `deploy-preview-*` aliases, wildcards, and domain suffixes are rejected. |
671
-
672
- Database connection variables (`DATABASE_URL`, `DATABASE_AUTH_TOKEN`, per-app `<APP_NAME>_DATABASE_URL`) live in [Database](/docs/database#production).
673
-
674
- ### Production-sensitive configuration {#env-required-prod}
675
-
676
- These settings cover production-sensitive behavior. A standalone app using the
677
- framework auth needs a stable `BETTER_AUTH_SECRET` and a persistent database;
678
- the public auth URL is inferred from the request, known template, or hosting
679
- platform when it is unset. Workspace and A2A settings are conditional on those
680
- features. Missing values either fail-closed or fall back to weaker behavior
681
- with a loud warning.
682
-
683
- | Variable | Description |
684
- | ------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
685
- | `BETTER_AUTH_SECRET` | 32+ char random string. Signs session cookies and is the fallback for `OAUTH_STATE_SECRET` and app-local encrypted values. It is also a legacy read candidate for existing shared vault ciphertext. Hard-required: the framework throws on startup if missing in production. |
686
- | `BETTER_AUTH_URL` | Optional public origin override for this app (e.g. `https://mail.example.com`). The framework infers it from request, known-template, and hosting-platform context when unset. |
687
- | `ANTHROPIC_API_KEY` | API key for the embedded production agent. **In multi-tenant deploys**, the framework refuses to fall back to this when the user has no per-user key — bring-your-own-key is required. Single-tenant self-hosted installs use it as a global key. |
688
- | `OAUTH_STATE_SECRET` | Dedicated HMAC key for OAuth state envelopes (Google, Atlassian, Zoom). Falls back to `BETTER_AUTH_SECRET` when unset, but a dedicated value is recommended so rotating one doesn't invalidate the other. Generate via `openssl rand -hex 32`. |
689
- | `A2A_SECRET` | Shared HMAC for inter-app A2A JSON-RPC and signed background handoffs. Required for hosted workspace deploys and A2A features; without it, those endpoints fail closed in production. When no dedicated workspace vault key is set, shared vault ciphertext uses a purpose-derived key from this value, so rotating it also rotates the vault key. |
690
- | `WORKSPACE_SECRETS_ENCRYPTION_KEY` | Preferred stable AES-256-GCM key for the shared encrypted-at-rest secrets vault. Set the same value in every app that reads the vault. It is separate from app-local OAuth/credential encryption, so A2A trust can rotate without stranding vault rows. Existing A2A-, Better Auth-, and app-key ciphertext is migrated on successful reads. |
691
- | `WORKSPACE_SECRETS_ENCRYPTION_KEY_PREVIOUS` | Optional previous workspace vault key used only during a rotation window. Deploy it alongside the new `WORKSPACE_SECRETS_ENCRYPTION_KEY`, let successful reads re-encrypt rows, then remove it after migration. |
692
- | `SECRETS_ENCRYPTION_KEY` | Legacy combined key for app-local encryption and the shared vault. It remains supported, but new multi-app deployments should use `WORKSPACE_SECRETS_ENCRYPTION_KEY` for the shared vault so changing workspace encryption does not change app-local OAuth ciphertext. |
693
-
694
- ### Auth & Identity {#env-auth}
695
-
696
- OAuth provider credentials (Google, GitHub), static MCP bearer fallbacks (`ACCESS_TOKEN` / `ACCESS_TOKENS`), and email-verification toggles are documented in [Authentication](/docs/authentication). Set them there per the auth mode you choose.
697
-
698
- ### Inbound Webhooks {#env-webhooks}
699
-
700
- Each messaging integration requires its own signing secret in production (handlers fail-closed on forged requests when the secret is missing). The per-integration variables are listed in [Messaging](/docs/messaging) and [Security](/docs/security). For local development only, `AGENT_NATIVE_ALLOW_UNVERIFIED_WEBHOOKS=1` opts back into "warn and accept" — never set it in prod.
701
-
702
- ### Security Configuration (Opt-in) {#security-config}
703
-
704
- Defaults are strict. A handful of opt-in flags relax behavior (debug stack traces, unverified webhooks, workspace-scoped key fallback, the MCP hub multi-org switch, runtime env-var writes). They are documented with their security trade-offs in [Security](/docs/security). Don't set them unless you specifically want the relaxed path.
705
-
706
- ### Workspace .env Inheritance {#env-inheritance}
707
-
708
- Inside a workspace, the root `.env` is loaded into every app automatically, so shared keys like `ANTHROPIC_API_KEY`, `A2A_SECRET`, `BETTER_AUTH_SECRET`, and `OAUTH_STATE_SECRET` only need to be set once. Per-app `apps/<name>/.env` wins on conflict.
709
-
710
- ### Generating Strong Secrets {#env-generate-secrets}
711
-
712
- For any secret marked "32+ char random" (`BETTER_AUTH_SECRET`, `OAUTH_STATE_SECRET`, `A2A_SECRET`, `WORKSPACE_SECRETS_ENCRYPTION_KEY`, `SECRETS_ENCRYPTION_KEY`, `AGENT_NATIVE_BUILDER_RELAY_SECRET`), generate fresh values with:
713
-
714
- ```bash
715
- openssl rand -hex 32
716
- ```
717
-
718
- Rotate signing/session keys by replacing the env var on every instance and redeploying — sessions / OAuth state envelopes signed under the old key become invalid, so users may need to sign in again. Rotate `WORKSPACE_SECRETS_ENCRYPTION_KEY` with the `..._PREVIOUS` window described above so encrypted vault rows remain readable while they migrate.
719
-
720
- ## Production Agent Tools {#production-agent-tools}
721
-
722
- Production agents get the app's registered actions plus the framework's own
723
- tools. `frameworkTools` selects which of those framework tools this app exposes.
724
- Every group defaults to today's behavior, so omitting the option changes
725
- nothing:
726
-
727
- ```ts filename="server/plugins/agent-chat.ts"
728
- export default createAgentChatPlugin({
729
- frameworkTools: {
730
- database: "read", // "write" | "read" | "off" — default "read"
731
- extensions: true, // default false
732
- sharing: false, // this app has no share surface
733
- review: false,
734
- },
735
- });
736
- ```
737
-
738
- ```ts filename="server/plugins/core-routes.ts"
739
- export default createCoreRoutesPlugin({
740
- extensionTools: true,
741
- });
742
- ```
743
-
744
- `database` and `extensions` accept the values the old top-level flags did:
745
-
746
- - `database: "read"` — default. Registers only `db-schema` and `db-query`;
747
- agents inspect data with SQL but must use typed app actions for writes.
748
- - `database: "write"` or `true` — additionally registers `db-exec` and
749
- `db-patch` for deliberate raw SQL maintenance. Writes are scoped to the
750
- current user/org and schema changes are blocked.
751
- - `database: "off"` or `false` — removes raw database tools from the
752
- agent surface so the app's actions are the only data access path.
753
- - `extensions: true` — opts the app into framework extension-management
754
- actions and prompt guidance (`create-extension`, `update-extension`, etc.).
755
- Set `extensionTools: true` on the core-routes plugin as well to allow
756
- authenticated REST creation. Extensions are disabled by default.
757
-
758
- The remaining groups are booleans, all on by default: `sharing`, `review`,
759
- `history`, `featureFlags`, `localization`, `audit`, `contextXray`,
760
- `userProfile`, `automation`, `docs`, `resources`, `web`, `workspaceApps`,
761
- `chat`, and `email`. Use `frameworkTools: "minimal"` to turn every group off at
762
- once for a voice-first or single-purpose app, or
763
- `frameworkTools: { preset: "minimal", resources: true }` to keep just one — an
764
- explicit group key always wins over the preset.
765
-
766
- Switching a group off removes it from the **agent** surfaces only: interactive
767
- chat, MCP, A2A, and background runs. The matching HTTP action routes stay
768
- mounted, because the UI reaches them through client hooks — disabling `sharing`
769
- must not 404 a share dialog that is still on screen. Weigh that parity cost
770
- before using it: the framework's contract is that anything the UI can do the
771
- agent can do, so `sharing: false` alongside a visible share button is a
772
- deliberate break, and only makes sense when the app has no such surface at all.
773
-
774
- Framework tools are no longer promoted into the first model request by default,
775
- independent of this option. They stay in the searchable registry and load
776
- through `tool-search`, or immediately if the app names them in
777
- `initialToolNames`.
778
-
779
- <Callout tone="warning">
780
- The top-level `databaseTools` and `extensionTools` options are deprecated in
781
- favor of `frameworkTools.database` and `frameworkTools.extensions`. They are
782
- still honored, but setting a top-level flag and its `frameworkTools`
783
- equivalent to conflicting values throws at plugin startup rather than booting
784
- with a tool surface nobody chose.
785
- </Callout>
786
-
787
- ## Production Code Execution {#production-code-execution}
788
-
789
- By default, production agents run without code-execution tools. They can call app actions, database tools, MCP tools, browser/session tools, and other registered framework tools, but they do not get shell or filesystem access.
790
-
791
- Node-compatible deployments can opt into production code execution through the agent chat plugin or an environment override:
792
-
793
- ```ts filename="server/plugins/agent-chat.ts"
794
- export default createAgentChatPlugin({
795
- codeExecution: { production: "sandboxed" },
796
- });
797
- ```
798
-
799
- The available modes are:
800
-
801
- - `off` — the default. No code-execution tools are registered in production.
802
- - `sandboxed` — registers `run-code`, an isolated Node.js JavaScript runner with a scrubbed environment, a fresh temp directory, output/time limits, and a localhost bridge to allowlisted registered tools such as `provider-api-request`, `provider-api-docs`, `provider-api-catalog`, `web-request`, and the Resources-backed workspace file bridge used by `workspaceRead` / `workspaceWrite`.
803
- - `trusted` — registers `run-code` plus the full coding tool registry (`bash`, `read`, `edit`, `write`). Use this only for single-tenant or operator-controlled deployments where full shell access to the host is intentional.
804
-
805
- Set `AGENT_PROD_CODE_EXECUTION=sandboxed` or `AGENT_PROD_CODE_EXECUTION=trusted` to override the plugin option for a specific deployment without a code change. `AGENT_PROD_CODE_EXECUTION=off` forces code execution off even when the plugin option enables it.
806
-
807
- The `run-code` sandbox is process-level isolation, not an OS container. It strips app secrets from the child process environment and uses the Node permission model when available, but outbound network is not blocked by Node itself; authenticated calls should go through the bridge helpers the tool exposes.
808
-
809
- ## Updating UI in Production {#updating-ui-in-production}
810
-
811
- The ordinary deployed app agent works through app tools — actions, database,
812
- MCP, and configured integrations — rather than receiving ambient access to the
813
- app's source code. Source changes belong in the repository's development
814
- workflow. An embedded, local, or Builder frame can edit the repository when
815
- that frame is intentionally granted workspace and write tooling.
816
-
817
- In a standard production deployment with [production code execution](#production-code-execution) left off, the agent has access to app tools (actions, database, MCP) but not the filesystem. This means the agent can read and write data, run actions, and interact with external services — but it can't edit your React components or add new routes on a deployed instance.
818
-
819
- ### Builder.io: Visual Editing in Production {#builderio}
820
-
821
- [Builder.io](https://www.builder.io?utm_source=agent-native&utm_medium=docs&utm_campaign=framework&utm_content=deployment) is a separate managed code-capable workflow where an agent can modify your app's UI in production. Connect your repo to Builder.io (free tier available) and prompt for UI changes directly — no redeploy needed.
822
-
823
- **Connect Builder** in Settings uses Builder OAuth (`builder:ai:invoke`) with a same-origin HTTPS callback under `/_agent-native/builder/callback`, so hosted origins such as Railway work without a domain allowlist. Legacy `BUILDER_*` private keys remain a runtime fallback for existing connections.
824
-
825
- **How it works:**
152
+ ## Advanced deployment
826
153
 
827
- 1. Connect your agent-native repo to Builder.io (free tier available)
828
- 2. Builder.io provides a cloud frame with the agent, visual editing, and real-time collaboration
829
- 3. Prompt the agent to make UI changes — it edits your components, routes, and styles live
830
- 4. Changes are committed back to your repo
154
+ Start with the path above. Use these pages when you need provider-specific
155
+ settings or production controls:
831
156
 
832
- See [Frames](/docs/frames) for more on the embedded agent panel vs. cloud frame options.
157
+ - [**Node.js & Docker**](/docs/node-docker) - run the default preset directly or in a container
158
+ - [**Vercel**](/docs/vercel), [**Netlify**](/docs/netlify), and [**Cloudflare**](/docs/cloudflare) - configure the main hosted targets
159
+ - [**Other Platforms**](/docs/other-platforms) - deploy to AWS Lambda or Deno Deploy
160
+ - [**SSR Caching**](/docs/ssr-caching) - configure the public shell cache
161
+ - [**Deployment Environment Variables**](/docs/deployment-environment-variables) - production secrets, runtime flags, and workspace inheritance
162
+ - [**Production Agent Access**](/docs/actions-agent-tools) - control framework tools and code execution
163
+ - [**Updating UI in Production**](/docs/updating-ui-in-production) - understand the managed visual-editing workflow
833
164
 
834
- ## Multi-instance deploys {#multi-instance}
165
+ <a id="self-host-quickstart"></a>
166
+ <a id="skip-ensure-tables"></a>
835
167
 
836
- Agent-native apps store all state in SQL via Drizzle and sync the UI via [polling](/docs/key-concepts#polling-sync) against the database — no file-system state, no sticky sessions, no in-memory caches. That means multi-instance and serverless deployments work out of the box: point every instance at the same `DATABASE_URL` and they converge automatically. See [Key Concepts — Data in SQL](/docs/key-concepts#data-in-sql) and [Portability](/docs/key-concepts#hosting-agnostic).
168
+ For compatibility with older links, the local Docker quickstart now lives on
169
+ [Node.js & Docker](/docs/node-docker#self-host-quickstart). Schema setup and
170
+ cold-start guidance live in the [deployment environment reference](/docs/deployment-environment-variables#skip-ensure-tables).
837
171
 
838
172
  ## What's next
839
173
 
840
- - [**Database**](/docs/database#production) — pick a persistent SQL backend before your first production deploy
841
- - [**Security**](/docs/security) — the production checklist and opt-in flags referenced throughout this page
842
- - [**Authentication**](/docs/authentication) — OAuth credentials and env vars for the auth mode you choose
843
- - [**Multi-App Workspace**](/docs/multi-app-workspace) — the workspace model behind `deploy`'s one-origin build
844
- - [**Frames**](/docs/frames) — the embedded agent panel vs. Builder.io's cloud frame for production UI edits
845
- - [**Public Agent Web**](/docs/agent-web-surfaces) — the `robots.txt`, `llms.txt`, and markdown-mirror files a Vite build writes into the deploy output for public routes
174
+ - [**Deploy an app**](/docs/deploy-an-app)
175
+ - [**Deploy a workspace**](/docs/workspace-deployment)
176
+ - [**Database**](/docs/server-database#production)
177
+ - [**Security**](/docs/security)
178
+ - [**Authentication**](/docs/authentication)