workflow 5.0.0-beta.9 → 5.0.1

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 (265) hide show
  1. package/README.md +68 -23
  2. package/dist/api-workflow.d.ts +3 -1
  3. package/dist/api-workflow.d.ts.map +1 -1
  4. package/dist/api-workflow.js +2 -1
  5. package/dist/api.d.ts +5 -4
  6. package/dist/api.d.ts.map +1 -1
  7. package/dist/api.js +6 -7
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +6 -1
  11. package/dist/internal/builtins.d.ts +4 -4
  12. package/dist/internal/builtins.js +6 -6
  13. package/dist/internal/errors.d.ts +1 -1
  14. package/dist/internal/errors.d.ts.map +1 -1
  15. package/dist/internal/errors.js +2 -2
  16. package/dist/nest-builder.d.ts +2 -0
  17. package/dist/nest-builder.d.ts.map +1 -0
  18. package/dist/nest-builder.js +2 -0
  19. package/dist/nest-vercel-builder.d.ts +2 -0
  20. package/dist/nest-vercel-builder.d.ts.map +1 -0
  21. package/dist/nest-vercel-builder.js +2 -0
  22. package/dist/runtime.d.ts +2 -1
  23. package/dist/runtime.d.ts.map +1 -1
  24. package/dist/runtime.js +4 -1
  25. package/docs/advanced/dynamic-workflows.mdx +227 -0
  26. package/docs/advanced/index.mdx +13 -0
  27. package/docs/advanced/meta.json +5 -0
  28. package/docs/ai/chat-session-modeling.mdx +176 -422
  29. package/docs/ai/defining-tools.mdx +6 -7
  30. package/docs/ai/human-in-the-loop.mdx +16 -12
  31. package/docs/ai/index.mdx +67 -72
  32. package/docs/ai/message-queueing.mdx +71 -110
  33. package/docs/ai/meta.json +1 -0
  34. package/docs/ai/resumable-streams.mdx +40 -28
  35. package/docs/ai/sleep-and-delays.mdx +10 -10
  36. package/docs/ai/streaming-updates-from-tools.mdx +6 -6
  37. package/docs/api-reference/index.mdx +25 -1
  38. package/docs/api-reference/meta.json +8 -0
  39. package/docs/api-reference/vitest/index.mdx +68 -15
  40. package/docs/api-reference/workflow/create-hook.mdx +170 -10
  41. package/docs/api-reference/workflow/create-webhook.mdx +16 -15
  42. package/docs/api-reference/workflow/define-hook.mdx +41 -33
  43. package/docs/api-reference/workflow/fatal-error.mdx +30 -8
  44. package/docs/api-reference/workflow/fetch.mdx +14 -10
  45. package/docs/api-reference/workflow/get-step-metadata.mdx +2 -2
  46. package/docs/api-reference/workflow/get-workflow-metadata.mdx +3 -3
  47. package/docs/api-reference/workflow/get-writable.mdx +7 -7
  48. package/docs/api-reference/workflow/index.mdx +3 -3
  49. package/docs/api-reference/workflow/retryable-error.mdx +1 -1
  50. package/docs/api-reference/workflow/set-attributes.mdx +63 -0
  51. package/docs/api-reference/workflow/sleep.mdx +4 -4
  52. package/docs/api-reference/workflow-ai/durable-agent.mdx +63 -101
  53. package/docs/api-reference/workflow-ai/index.mdx +5 -5
  54. package/docs/api-reference/workflow-ai/workflow-chat-transport.mdx +67 -24
  55. package/docs/api-reference/workflow-api/get-hook-by-token.mdx +37 -15
  56. package/docs/api-reference/workflow-api/get-run.mdx +43 -8
  57. package/docs/api-reference/workflow-api/index.mdx +8 -9
  58. package/docs/api-reference/workflow-api/register-lifecycle-hooks.mdx +87 -0
  59. package/docs/api-reference/workflow-api/resume-hook.mdx +77 -12
  60. package/docs/api-reference/workflow-api/resume-webhook.mdx +15 -9
  61. package/docs/api-reference/workflow-api/start.mdx +107 -12
  62. package/docs/api-reference/workflow-astro/index.mdx +18 -0
  63. package/docs/api-reference/workflow-astro/meta.json +4 -0
  64. package/docs/api-reference/workflow-astro/workflow.mdx +45 -0
  65. package/docs/api-reference/workflow-errors/entity-conflict-error.mdx +4 -4
  66. package/docs/api-reference/workflow-errors/hook-conflict-error.mdx +60 -0
  67. package/docs/api-reference/workflow-errors/hook-force-claimed-error.mdx +70 -0
  68. package/docs/api-reference/workflow-errors/hook-not-found-error.mdx +8 -8
  69. package/docs/api-reference/workflow-errors/index.mdx +91 -0
  70. package/docs/api-reference/workflow-errors/meta.json +7 -0
  71. package/docs/api-reference/workflow-errors/precondition-failed-error.mdx +68 -0
  72. package/docs/api-reference/workflow-errors/run-expired-error.mdx +2 -2
  73. package/docs/api-reference/workflow-errors/run-not-supported-error.mdx +58 -0
  74. package/docs/api-reference/workflow-errors/step-not-registered-error.mdx +5 -5
  75. package/docs/api-reference/workflow-errors/throttle-error.mdx +2 -2
  76. package/docs/api-reference/workflow-errors/too-early-error.mdx +2 -2
  77. package/docs/api-reference/workflow-errors/workflow-error.mdx +52 -0
  78. package/docs/api-reference/workflow-errors/workflow-not-registered-error.mdx +5 -6
  79. package/docs/api-reference/workflow-errors/workflow-run-cancelled-error.mdx +13 -6
  80. package/docs/api-reference/workflow-errors/workflow-run-failed-error.mdx +12 -5
  81. package/docs/api-reference/workflow-errors/workflow-run-not-completed-error.mdx +58 -0
  82. package/docs/api-reference/workflow-errors/workflow-run-not-found-error.mdx +4 -4
  83. package/docs/api-reference/workflow-errors/workflow-runtime-error.mdx +58 -0
  84. package/docs/api-reference/workflow-errors/workflow-world-error.mdx +8 -8
  85. package/docs/api-reference/workflow-globals.mdx +19 -11
  86. package/docs/api-reference/workflow-nest/configure-workflow-controller.mdx +37 -0
  87. package/docs/api-reference/workflow-nest/index.mdx +31 -0
  88. package/docs/api-reference/workflow-nest/meta.json +9 -0
  89. package/docs/api-reference/workflow-nest/nest-local-builder.mdx +64 -0
  90. package/docs/api-reference/workflow-nest/workflow-controller.mdx +46 -0
  91. package/docs/api-reference/workflow-nest/workflow-module.mdx +134 -0
  92. package/docs/api-reference/workflow-next/with-workflow.mdx +39 -17
  93. package/docs/api-reference/workflow-nitro/index.mdx +60 -0
  94. package/docs/api-reference/workflow-nuxt/index.mdx +48 -0
  95. package/docs/api-reference/workflow-observability/hydrate-data.mdx +35 -0
  96. package/docs/api-reference/workflow-observability/hydrate-resource-io.mdx +62 -0
  97. package/docs/api-reference/workflow-observability/index.mdx +62 -0
  98. package/docs/api-reference/workflow-observability/meta.json +11 -0
  99. package/docs/api-reference/workflow-observability/observability-revivers.mdx +50 -0
  100. package/docs/api-reference/workflow-observability/parse-class-name.mdx +41 -0
  101. package/docs/api-reference/workflow-observability/parse-step-name.mdx +40 -0
  102. package/docs/api-reference/workflow-observability/parse-workflow-name.mdx +55 -0
  103. package/docs/api-reference/workflow-runtime/create-world.mdx +39 -0
  104. package/docs/api-reference/workflow-runtime/get-world-handlers.mdx +44 -0
  105. package/docs/api-reference/{workflow-api → workflow-runtime}/get-world.mdx +11 -14
  106. package/docs/api-reference/workflow-runtime/health-check.mdx +51 -0
  107. package/docs/api-reference/workflow-runtime/index.mdx +41 -0
  108. package/docs/api-reference/workflow-runtime/meta.json +12 -0
  109. package/docs/api-reference/workflow-runtime/set-world.mdx +51 -0
  110. package/docs/api-reference/workflow-runtime/workflow-entrypoint.mdx +43 -0
  111. package/docs/api-reference/workflow-runtime/world/analytics.mdx +315 -0
  112. package/docs/api-reference/workflow-runtime/world/index.mdx +60 -0
  113. package/docs/api-reference/workflow-runtime/world/meta.json +4 -0
  114. package/docs/api-reference/workflow-runtime/world/queue.mdx +88 -0
  115. package/docs/api-reference/{workflow-api → workflow-runtime}/world/storage.mdx +133 -35
  116. package/docs/api-reference/{workflow-api → workflow-runtime}/world/streams.mdx +8 -8
  117. package/docs/api-reference/workflow-serde/index.mdx +1 -2
  118. package/docs/api-reference/workflow-serde/workflow-deserialize.mdx +3 -4
  119. package/docs/api-reference/workflow-serde/workflow-serialize.mdx +8 -8
  120. package/docs/api-reference/workflow-sveltekit/index.mdx +18 -0
  121. package/docs/api-reference/workflow-sveltekit/meta.json +4 -0
  122. package/docs/api-reference/workflow-sveltekit/workflow-plugin.mdx +42 -0
  123. package/docs/api-reference/workflow-vite/index.mdx +18 -0
  124. package/docs/api-reference/workflow-vite/meta.json +4 -0
  125. package/docs/api-reference/workflow-vite/workflow.mdx +48 -0
  126. package/docs/changelog/attributes-mvp.mdx +53 -41
  127. package/docs/changelog/batched-event-writes.mdx +79 -0
  128. package/docs/changelog/eager-processing.mdx +110 -436
  129. package/docs/changelog/index.mdx +4 -2
  130. package/docs/changelog/lazy-event-creation.md +127 -0
  131. package/docs/changelog/lazy-hook-resume.mdx +78 -0
  132. package/docs/changelog/meta.json +11 -1
  133. package/docs/changelog/resilient-resume.mdx +32 -0
  134. package/docs/changelog/resilient-start.mdx +33 -285
  135. package/docs/changelog/step-message-ownership.mdx +360 -0
  136. package/docs/changelog/turbo-mode.md +87 -0
  137. package/docs/comparisons/index.mdx +66 -0
  138. package/docs/comparisons/meta.json +11 -0
  139. package/docs/comparisons/workflow-sdk-vs-aws-agentcore.mdx +55 -0
  140. package/docs/comparisons/workflow-sdk-vs-aws-step-functions.mdx +111 -0
  141. package/docs/comparisons/workflow-sdk-vs-cloudflare-workflows.mdx +71 -0
  142. package/docs/comparisons/workflow-sdk-vs-inngest.mdx +102 -0
  143. package/docs/comparisons/workflow-sdk-vs-temporal.mdx +123 -0
  144. package/docs/comparisons/workflow-sdk-vs-trigger-dev.mdx +104 -0
  145. package/docs/configuration/build-and-diagnostics.mdx +89 -0
  146. package/docs/configuration/cli-and-web-ui.mdx +241 -0
  147. package/docs/configuration/framework-options.mdx +165 -0
  148. package/docs/configuration/index.mdx +32 -0
  149. package/docs/configuration/meta.json +12 -0
  150. package/docs/configuration/runtime-tuning.mdx +424 -0
  151. package/docs/configuration/worlds.mdx +341 -0
  152. package/docs/cookbook/advanced/child-workflows.mdx +33 -25
  153. package/docs/cookbook/advanced/publishing-libraries.mdx +65 -56
  154. package/docs/cookbook/advanced/serializable-steps.mdx +48 -68
  155. package/docs/cookbook/advanced/upgrading-workflows.mdx +35 -31
  156. package/docs/cookbook/agent-patterns/agent-cancellation.mdx +78 -60
  157. package/docs/cookbook/agent-patterns/durable-agent.mdx +23 -135
  158. package/docs/cookbook/agent-patterns/human-in-the-loop.mdx +180 -195
  159. package/docs/cookbook/common-patterns/batching.mdx +20 -14
  160. package/docs/cookbook/common-patterns/idempotency.mdx +41 -53
  161. package/docs/cookbook/common-patterns/rate-limiting.mdx +8 -4
  162. package/docs/cookbook/common-patterns/saga.mdx +23 -19
  163. package/docs/cookbook/common-patterns/scheduling.mdx +30 -22
  164. package/docs/cookbook/common-patterns/sequential-and-parallel.mdx +29 -25
  165. package/docs/cookbook/common-patterns/timeouts.mdx +26 -21
  166. package/docs/cookbook/common-patterns/webhooks.mdx +12 -7
  167. package/docs/cookbook/common-patterns/workflow-composition.mdx +27 -17
  168. package/docs/cookbook/index.mdx +22 -22
  169. package/docs/cookbook/integrations/ai-sdk.mdx +63 -48
  170. package/docs/cookbook/integrations/chat-sdk.mdx +46 -33
  171. package/docs/cookbook/integrations/sandbox.mdx +58 -45
  172. package/docs/deploying.mdx +106 -0
  173. package/docs/errors/abort-signal-timeout-in-workflow.mdx +16 -12
  174. package/docs/errors/corrupted-event-log.mdx +39 -18
  175. package/docs/errors/deployment-mismatch.mdx +71 -0
  176. package/docs/errors/fetch-in-workflow.mdx +15 -14
  177. package/docs/errors/hook-conflict.mdx +38 -11
  178. package/docs/errors/hook-force-claimed.mdx +96 -0
  179. package/docs/errors/index.mdx +24 -37
  180. package/docs/errors/node-js-module-in-workflow.mdx +9 -5
  181. package/docs/errors/replay-divergence.mdx +27 -0
  182. package/docs/errors/run-expired.mdx +85 -0
  183. package/docs/errors/runtime-decryption-failed.mdx +77 -0
  184. package/docs/errors/serialization-failed.mdx +44 -12
  185. package/docs/errors/start-invalid-workflow-function.mdx +9 -5
  186. package/docs/errors/step-executed-multiple-times.mdx +23 -0
  187. package/docs/errors/step-not-registered.mdx +6 -6
  188. package/docs/errors/timeout-in-workflow.mdx +12 -8
  189. package/docs/errors/webhook-invalid-respond-with-value.mdx +18 -18
  190. package/docs/errors/webhook-response-not-sent.mdx +20 -16
  191. package/docs/errors/workflow-not-registered.mdx +5 -5
  192. package/docs/foundations/cancellation.mdx +31 -32
  193. package/docs/foundations/errors-and-retries.mdx +54 -11
  194. package/docs/foundations/hooks.mdx +187 -36
  195. package/docs/foundations/idempotency.mdx +267 -12
  196. package/docs/foundations/index.mdx +1 -26
  197. package/docs/foundations/serialization.mdx +22 -22
  198. package/docs/foundations/starting-workflows.mdx +104 -30
  199. package/docs/foundations/streaming.mdx +108 -60
  200. package/docs/foundations/versioning.mdx +4 -4
  201. package/docs/foundations/workflows-and-steps.mdx +10 -10
  202. package/docs/getting-started/astro.mdx +22 -18
  203. package/docs/getting-started/express.mdx +15 -11
  204. package/docs/getting-started/fastify.mdx +15 -11
  205. package/docs/getting-started/hono.mdx +15 -11
  206. package/docs/getting-started/index.mdx +10 -3
  207. package/docs/getting-started/meta.json +3 -1
  208. package/docs/getting-started/nestjs.mdx +264 -21
  209. package/docs/getting-started/next.mdx +18 -14
  210. package/docs/getting-started/nitro.mdx +22 -18
  211. package/docs/getting-started/nuxt.mdx +15 -11
  212. package/docs/getting-started/python.mdx +190 -41
  213. package/docs/getting-started/react-router/index.mdx +33 -0
  214. package/docs/getting-started/react-router/meta.json +5 -0
  215. package/docs/getting-started/react-router/v7.mdx +237 -0
  216. package/docs/getting-started/react-router/v8.mdx +232 -0
  217. package/docs/getting-started/sveltekit.mdx +20 -16
  218. package/docs/getting-started/tanstack-start.mdx +17 -13
  219. package/docs/getting-started/vite.mdx +15 -11
  220. package/docs/how-it-works/cancellation.mdx +63 -63
  221. package/docs/how-it-works/code-transform.mdx +82 -66
  222. package/docs/how-it-works/encryption.mdx +30 -26
  223. package/docs/how-it-works/event-sourcing.mdx +132 -35
  224. package/docs/how-it-works/framework-integrations.mdx +96 -337
  225. package/docs/how-it-works/understanding-directives.mdx +22 -22
  226. package/docs/internal/index.mdx +6 -4
  227. package/docs/internal/meta.json +6 -1
  228. package/docs/internal/nitro-native-build.mdx +38 -0
  229. package/docs/internal/nitro-web-ui.mdx +24 -0
  230. package/docs/internal/serializable-abort-controller.mdx +7 -7
  231. package/docs/meta.json +4 -2
  232. package/docs/observability/attributes.mdx +91 -21
  233. package/docs/observability/index.mdx +29 -15
  234. package/docs/observability/lifecycle-hooks.mdx +95 -0
  235. package/docs/observability/meta.json +1 -1
  236. package/docs/observability/retention.mdx +95 -0
  237. package/docs/observability/tracing.mdx +124 -0
  238. package/docs/testing/index.mdx +118 -38
  239. package/docs/testing/server-based.mdx +10 -10
  240. package/docs/whats-new.mdx +196 -0
  241. package/docs/worlds/building-a-world.mdx +600 -0
  242. package/docs/worlds/local.mdx +129 -0
  243. package/docs/worlds/meta.json +10 -0
  244. package/docs/worlds/postgres.mdx +428 -0
  245. package/docs/worlds/upgrading-to-v5.mdx +183 -0
  246. package/docs/worlds/vercel.mdx +389 -0
  247. package/package.json +17 -14
  248. package/docs/api-reference/workflow/experimental-set-attributes.mdx +0 -63
  249. package/docs/api-reference/workflow-api/world/index.mdx +0 -58
  250. package/docs/api-reference/workflow-api/world/meta.json +0 -4
  251. package/docs/api-reference/workflow-api/world/observability.mdx +0 -164
  252. package/docs/api-reference/workflow-api/world/queue.mdx +0 -86
  253. package/docs/deploying/building-a-world.mdx +0 -251
  254. package/docs/deploying/index.mdx +0 -95
  255. package/docs/deploying/meta.json +0 -4
  256. package/docs/deploying/world/local-world.mdx +0 -84
  257. package/docs/deploying/world/meta.json +0 -4
  258. package/docs/deploying/world/postgres-world.mdx +0 -224
  259. package/docs/deploying/world/vercel-world.mdx +0 -181
  260. package/docs/migration-guides/index.mdx +0 -34
  261. package/docs/migration-guides/meta.json +0 -9
  262. package/docs/migration-guides/migrating-from-aws-step-functions.mdx +0 -358
  263. package/docs/migration-guides/migrating-from-inngest.mdx +0 -304
  264. package/docs/migration-guides/migrating-from-temporal.mdx +0 -313
  265. package/docs/migration-guides/migrating-from-trigger-dev.mdx +0 -328
@@ -0,0 +1,129 @@
1
+ ---
2
+ title: Local World
3
+ description: Zero-config world bundled with Workflow for local development. No external services required.
4
+ type: integration
5
+ summary: Set up the Local World for zero-configuration workflow development on your machine.
6
+ prerequisites:
7
+ - /docs/deploying
8
+ related:
9
+ - /worlds/postgres
10
+ - /worlds/vercel
11
+ ---
12
+
13
+ The Local World is bundled with `workflow` and used automatically during local development. It requires no installation or configuration.
14
+
15
+ To explicitly use the Local World in any environment, set this environment variable:
16
+
17
+ ```bash
18
+ WORKFLOW_TARGET_WORLD=local
19
+ ```
20
+
21
+ ## Observability
22
+
23
+ The Workflow CLI uses the Local World by default. Run these commands inside your workflow project to view your local development workflows:
24
+
25
+ ```bash
26
+ # List recent workflow runs
27
+ npx workflow inspect runs
28
+
29
+ # Launch the web UI
30
+ npx workflow web
31
+ ```
32
+
33
+ Learn more in the [Observability](/docs/observability) documentation.
34
+
35
+ ## Testing & compatibility
36
+
37
+ <WorldTestingPerformance worldId="local" />
38
+
39
+ ## Configuration
40
+
41
+ The Local World requires no configuration, but you can customize its behavior through environment variables or programmatically through `createWorld()`.
42
+
43
+ ### `WORKFLOW_LOCAL_DATA_DIR`
44
+
45
+ Directory for storing workflow data as JSON files. Default: `.workflow-data/`
46
+
47
+ ### `PORT`
48
+
49
+ The application dev server port. Used to deliver workflow queue messages to the combined flow route. Default: auto-detected
50
+
51
+ ### `WORKFLOW_LOCAL_BASE_URL`
52
+
53
+ Full base URL override for HTTPS or custom hostnames. Default: `http://localhost:{port}`
54
+
55
+ Port resolution priority: `baseUrl` > `port` > `PORT` > auto-detected
56
+
57
+ ### `WORKFLOW_LOCAL_QUEUE_CONCURRENCY`
58
+
59
+ Maximum number of concurrent queue message handlers. Default: `1000`
60
+
61
+ ### `WORKFLOW_LOCAL_QUEUE_MAX_VISIBILITY`
62
+
63
+ Maximum number of seconds a local queue message can stay hidden before the handler rechecks the run. Default: unlimited.
64
+
65
+ ### `WORKFLOW_LOCAL_RUN_STATUS_POLL_INTERVAL_MS`
66
+
67
+ How often a wait for a terminal run status re-reads the run file, in milliseconds. Default: `100`.
68
+
69
+ `await run.returnValue` asks the World to wait for the run to finish. When the run and the caller share a process (the usual development server case), an in-process signal resolves the wait as soon as the run ends, and this interval never comes into play. The interval covers activity the signal cannot detect, primarily a second process awaiting a run over the same data directory, so it is set far below Workflow's own polling interval.
70
+
71
+ ### `WORKFLOW_LOCAL_HEADERS_TIMEOUT_MS`
72
+
73
+ Maximum time in milliseconds to wait for a local queue handler to begin responding before redelivering the durable message. Default: `0` (no deadline).
74
+
75
+ A delivery executes inline steps before the handler responds, so a deadline shorter than your longest step redelivers a healthy delivery while it is still running and executes the same step again. Set this only if you would rather a hung handler be redelivered, and set it above the longest inline work you expect.
76
+
77
+ ### `WORKFLOW_LOCAL_BODY_TIMEOUT_MS`
78
+
79
+ Maximum gap in milliseconds between response body chunks from a local queue handler before redelivering the durable message. Default: `0` (no deadline). The same caution as `WORKFLOW_LOCAL_HEADERS_TIMEOUT_MS` applies.
80
+
81
+ ### `WORKFLOW_NODE_HTTP`
82
+
83
+ Whether queue deliveries go out through Node's built-in `node:http` and `node:https` modules instead of the HTTP client library this World normally uses. Default: disabled. Set to `1` to switch to Node's modules. Socket pooling, keep-alive, and both timeouts above apply either way. See [`WORKFLOW_NODE_HTTP`](/docs/configuration/runtime-tuning#workflow_node_http) for what else changes.
84
+
85
+ ### `WORKFLOW_LOCAL_RECOVER_ACTIVE_RUNS`
86
+
87
+ Whether pending and running runs found in the data directory are re-enqueued when the World starts. Set to `0` or `false` to skip recovery and leave stale runs untouched. Default: `true`.
88
+
89
+ ### `WORKFLOW_LOCAL_HOOK_RETENTION_LIMIT_DAYS`
90
+
91
+ Maximum [`experimental_minRetention`](/docs/api-reference/workflow/create-hook#keep-a-token-unavailable-after-the-run-ends) accepted by the Local World, in days. Default: `30`. Set this to the same limit as your production World so oversized values fail during local development.
92
+
93
+ ### `WORKFLOW_STREAM_FLUSH_INTERVAL_MS`
94
+
95
+ Group-commit window, in milliseconds, for the leading chunk of an idle stream. Default: `0` (dispatch immediately). A positive value holds the first chunk up to that long to collect a group, trading first-chunk latency for fewer requests. Chunks arriving while a request is in flight always coalesce into the next group regardless.
96
+
97
+ ### Programmatic configuration
98
+
99
+ Options passed to `createWorld()` take precedence over the environment variables above. Export the configured World from a module and point `WORKFLOW_TARGET_WORLD` at that module:
100
+
101
+ ```typescript title="my-world.ts" lineNumbers
102
+ import { createWorld } from "@workflow/world-local";
103
+
104
+ export default createWorld({
105
+ dataDir: "./custom-workflow-data",
106
+ port: 5173,
107
+ // baseUrl overrides port if set
108
+ baseUrl: "https://local.example.com:3000",
109
+ recoverActiveRuns: true, // overrides WORKFLOW_LOCAL_RECOVER_ACTIVE_RUNS
110
+ streamFlushIntervalMs: 10, // WORKFLOW_STREAM_FLUSH_INTERVAL_MS, if set, overrides this
111
+ });
112
+ ```
113
+
114
+ ```bash title=".env"
115
+ WORKFLOW_TARGET_WORLD="./my-world.ts"
116
+ ```
117
+
118
+ `createWorld()` also accepts `tag`, which scopes local storage files to a suffix. It is mainly used by test harnesses that share one `.workflow-data` directory.
119
+
120
+ ## Limitations
121
+
122
+ The Local World is designed for development, not production:
123
+
124
+ - **In-memory queue**: Workflow messages, including queued step invocations, do not persist across server restarts.
125
+ - **Filesystem storage**: Data is stored in local JSON files.
126
+ - **Single instance**: The Local World cannot handle distributed deployments.
127
+ - **No authentication**: The Local World is suitable only for local development.
128
+
129
+ For production deployments, use the [Vercel World](/worlds/vercel), which handles execution, persistence, multi-tenancy, scale, observability, and security for you on Vercel, or check out the [Postgres World](/worlds/postgres) - a tested reference implementation that implements the complete World spec and can be used to deploy workflows anywhere.
@@ -0,0 +1,10 @@
1
+ {
2
+ "title": "Worlds",
3
+ "pages": [
4
+ "local",
5
+ "vercel",
6
+ "postgres",
7
+ "building-a-world",
8
+ "upgrading-to-v5"
9
+ ]
10
+ }
@@ -0,0 +1,428 @@
1
+ ---
2
+ title: Postgres World
3
+ description: Self-hosted reference world using PostgreSQL for storage and graphile-worker for job processing.
4
+ type: integration
5
+ summary: Deploy workflows to your own infrastructure using PostgreSQL and graphile-worker.
6
+ prerequisites:
7
+ - /docs/deploying
8
+ related:
9
+ - /worlds/local
10
+ - /worlds/vercel
11
+ ---
12
+
13
+ The Postgres World is a self-hosted backend that uses PostgreSQL for durable storage and [graphile-worker](https://github.com/graphile/worker) for reliable job processing.
14
+
15
+ Use the Postgres World to deploy workflows on your own infrastructure outside Vercel, such as a Docker container, Kubernetes cluster, or any cloud that supports long-running servers.
16
+
17
+ <Callout type="warn">
18
+ The Postgres World is a **reference implementation**: it implements the
19
+ complete World spec and is tested, but it is not optimized for scale, speed,
20
+ or security, and it ships with **no authentication** on the workflow HTTP
21
+ routes. For production use-cases we recommend cloning it and adapting it to
22
+ your persistence, network stack, scale, and security requirements. If you
23
+ deploy it as is, you must restrict those routes yourself. Read
24
+ [Security](#security) first.
25
+ </Callout>
26
+
27
+ ## Installation
28
+
29
+ Install the Postgres World package in your workflow project:
30
+
31
+ ```package-install
32
+ @workflow/world-postgres
33
+ ```
34
+
35
+ <Callout type="info">
36
+ Keep `workflow` and `@workflow/world-postgres` on the same major version and release
37
+ cycle. If your app uses a prerelease Workflow version, install the matching prerelease Postgres
38
+ World package. Mismatched versions fail before starting a run with an error that names the
39
+ spec versions the runtime supports and the one the World declares.
40
+ </Callout>
41
+
42
+ Configure the required environment variables to use the world and point it to your PostgreSQL database:
43
+
44
+ ```bash title=".env"
45
+ WORKFLOW_TARGET_WORLD="@workflow/world-postgres"
46
+ WORKFLOW_POSTGRES_URL="postgres://user:password@host:5432/database"
47
+ ```
48
+
49
+ Run the migration script to create the necessary tables in your database. Ensure `WORKFLOW_POSTGRES_URL` or `DATABASE_URL` is set when running this command:
50
+
51
+ <Tabs items={["npm", "pnpm", "Yarn", "Bun"]}>
52
+
53
+ <Tab value="npm">
54
+
55
+ ```bash
56
+ npx --package=@workflow/world-postgres bootstrap
57
+ ```
58
+
59
+ </Tab>
60
+
61
+ <Tab value="pnpm">
62
+
63
+ ```bash
64
+ pnpm dlx --package @workflow/world-postgres bootstrap
65
+ ```
66
+
67
+ </Tab>
68
+
69
+ <Tab value="Yarn">
70
+
71
+ ```bash
72
+ yarn dlx --package @workflow/world-postgres bootstrap
73
+ ```
74
+
75
+ </Tab>
76
+
77
+ <Tab value="Bun">
78
+
79
+ ```bash
80
+ bunx --package @workflow/world-postgres bootstrap
81
+ ```
82
+
83
+ </Tab>
84
+
85
+ </Tabs>
86
+
87
+ <Callout type="info">
88
+ The migration is idempotent and can safely be run as a post-deployment lifecycle script.
89
+ </Callout>
90
+
91
+ ## Starting the World
92
+
93
+ ### Experimental invocation delivery
94
+
95
+ Set `WORKFLOW_POSTGRES_INVOKE=1`, or pass `enableInvoke: true` to the Postgres
96
+ World's factory, to send hook inputs to the run's executor and wait for its
97
+ response. Invocation is off by default. Apply the migrations and upgrade
98
+ producers and workers together before enabling it.
99
+
100
+ The executor validates the hook input and writes its event before responding.
101
+ Workflow user code can consume the event later. The World stores inputs and
102
+ responses in `workflow.workflow_invocations`. Inserting an input and enqueueing
103
+ a workflow execution job share one transaction. Retries also enqueue an execution
104
+ job, including when a retained response already exists, so committed events can
105
+ be replayed after a runner failure.
106
+
107
+ With the default job prefix, Graphile uses `workflow_flows_executor` for workflow
108
+ execution and `workflow_flows` for step jobs and health checks. Execution jobs
109
+ use a named queue per run, `workflow_flows:<runId>:executor`. Graphile permits
110
+ one active job on each named queue across workers. Other runs, steps, and health
111
+ checks can run concurrently. `queueConcurrency` limits total active jobs per
112
+ worker process and defaults to 50. A custom job prefix replaces `workflow_` in
113
+ these names.
114
+
115
+ The HTTP receiver checks each execution request against its active Graphile job
116
+ before processing pending inputs. Updated workers and receivers move legacy
117
+ orchestration requests to the run's named queue. Before acknowledging an execution
118
+ job, the World finishes any input already being processed and replays newly
119
+ committed events. These entry checks do not stop an old handler from writing
120
+ after its job is reclaimed.
121
+
122
+ The caller receives a stored return value or a restored Workflow error. Terminal
123
+ errors are retained as outcomes; transient or unrecognized failures leave inputs
124
+ pending for Graphile to retry. Migration 0022 preserves the meaning of responses
125
+ stored by earlier previews. Failure to store or read a response leaves the
126
+ processing outcome unknown. Durable resume identities deduplicate the hook event
127
+ when response storage must be retried.
128
+
129
+ A shared `LISTEN/NOTIFY` connection signals changes to pending inputs and
130
+ responses. Notifications contain identifiers, and readers fetch data from the
131
+ table. Use a session-capable connection for `LISTEN`. Readers fall back to polling
132
+ at 1s intervals if notifications fail, and connection retries use a 1s backoff.
133
+ Notification failure and recovery are logged once per transition without
134
+ payloads or connection details.
135
+
136
+ The response-wait timeout defaults to 30s and can be set with `timeoutMs`. Encoded
137
+ inputs and outcomes are limited to 1 MiB each. A timeout does not cancel
138
+ processing. `$retention: 0` purges invocation data and prevents late writes from
139
+ restoring it. Callers whose result has expired receive a 410 error with code
140
+ `INVOCATION_DATA_EXPIRED`, even if the hook event committed earlier.
141
+
142
+ Automatic cleanup of other results, stopping writes from reclaimed handlers, and
143
+ routing requests across incompatible worker versions remain unimplemented.
144
+
145
+ ### Server startup
146
+
147
+ To subscribe to the graphile-worker queue, your workflow app needs to start the world on server start. Here are examples for a few frameworks:
148
+
149
+ <Callout type="warn">
150
+ This step is specific to worlds that run a background worker, such as Postgres
151
+ World. Worlds whose queue delivers work over HTTP, including the Vercel World,
152
+ have no worker to subscribe, so `start()` does nothing there while the import
153
+ still adds overhead. Pulling in `workflow/runtime` from a server-startup hook puts
154
+ the whole runtime into the cold-start path before the first request is served.
155
+ </Callout>
156
+
157
+ <Tabs items={["Next.js", "SvelteKit", "Nitro"]}>
158
+
159
+ <Tab value="Next.js">
160
+
161
+ Create an `instrumentation.ts` file in your project root:
162
+
163
+ ```ts title="instrumentation.ts" lineNumbers
164
+ export async function register() {
165
+ if (process.env.NEXT_RUNTIME !== "edge") {
166
+ const { getWorld } = await import("workflow/runtime");
167
+ const world = await getWorld();
168
+ await world.start?.();
169
+ }
170
+ }
171
+ ```
172
+
173
+ <Callout type="info">
174
+ Learn more about [Next.js Instrumentation](https://nextjs.org/docs/app/guides/instrumentation).
175
+ </Callout>
176
+
177
+ </Tab>
178
+
179
+ <Tab value="SvelteKit">
180
+
181
+ Create a `src/hooks.server.ts` file:
182
+
183
+ ```ts title="src/hooks.server.ts" lineNumbers
184
+ import type { ServerInit } from "@sveltejs/kit";
185
+
186
+ export const init: ServerInit = async () => {
187
+ const { getWorld } = await import("workflow/runtime");
188
+ const world = await getWorld();
189
+ await world.start?.();
190
+ };
191
+ ```
192
+
193
+ <Callout type="info">
194
+ Learn more about [SvelteKit Hooks](https://svelte.dev/docs/kit/hooks).
195
+ </Callout>
196
+
197
+ </Tab>
198
+
199
+ <Tab value="Nitro">
200
+
201
+ Create a plugin to start the world on server initialization:
202
+
203
+ ```ts title="plugins/start-pg-world.ts" lineNumbers
204
+ import { defineNitroPlugin } from "nitro/~internal/runtime/plugin";
205
+
206
+ export default defineNitroPlugin(async () => {
207
+ const { getWorld } = await import("workflow/runtime");
208
+ const world = await getWorld();
209
+ await world.start?.();
210
+ });
211
+ ```
212
+
213
+ Register the plugin in your config:
214
+
215
+ ```ts title="nitro.config.ts"
216
+ import { defineNitroConfig } from "nitropack";
217
+
218
+ export default defineNitroConfig({
219
+ modules: ["workflow/nitro"],
220
+ plugins: ["plugins/start-pg-world.ts"],
221
+ });
222
+ ```
223
+
224
+ <Callout type="info">
225
+ Learn more about [Nitro Plugins](https://v3.nitro.build/docs/plugins).
226
+ </Callout>
227
+
228
+ </Tab>
229
+
230
+ </Tabs>
231
+
232
+ <Callout type="info">
233
+ The Postgres World requires a long-lived worker process that polls the database for jobs. This does not work on serverless environments. For Vercel deployments, use the [Vercel World](/worlds/vercel) instead.
234
+ </Callout>
235
+
236
+ ## Observability
237
+
238
+ Use the Workflow CLI to inspect workflows stored in PostgreSQL:
239
+
240
+ ```bash
241
+ # Set your database URL
242
+ export WORKFLOW_POSTGRES_URL="postgres://user:password@host:5432/database"
243
+
244
+ # List workflow runs
245
+ npx workflow inspect runs --backend @workflow/world-postgres
246
+
247
+ # Launch the web UI
248
+ npx workflow web --backend @workflow/world-postgres
249
+ ```
250
+
251
+ If `WORKFLOW_POSTGRES_URL` is not set, the CLI defaults to `postgres://world:world@localhost:5432/world`.
252
+
253
+ Learn more in the [Observability](/docs/observability) documentation.
254
+
255
+ ## Testing & compatibility
256
+
257
+ <WorldTestingPerformance worldId="postgres" />
258
+
259
+ ## Configuration
260
+
261
+ You can set all configuration options through environment variables or programmatically through `createWorld()`.
262
+
263
+ ### `WORKFLOW_POSTGRES_URL`
264
+
265
+ PostgreSQL connection string used by the runtime World.
266
+
267
+ Precedence: `WORKFLOW_POSTGRES_URL` > `DATABASE_URL` > `postgres://world:world@localhost:5432/world`
268
+
269
+ The `bootstrap` migration command uses the same precedence.
270
+
271
+ ### `WORKFLOW_POSTGRES_JOB_PREFIX`
272
+
273
+ Prefix for graphile-worker queue job names. Useful when sharing a database between multiple applications. Default: `workflow_`
274
+
275
+ ### `WORKFLOW_POSTGRES_WORKER_CONCURRENCY`
276
+
277
+ Number of concurrent workers polling for jobs. Default: `50`.
278
+
279
+ This value also bounds how many parent→child workflow polls can be in flight simultaneously. Every `await childRun.returnValue` inside a workflow holds a worker slot until the child run terminates. If you expect recursive or highly-fanned-out parent/child workflows, raise this ceiling above the peak number of concurrent polls. With the default of 50, the included `fibonacciWorkflow` end-to-end test (`fib(6)`, about 24 concurrent polls at peak) passes; deeper recursion or larger fanouts need a correspondingly larger setting.
280
+
281
+ ### `WORKFLOW_POSTGRES_POLL_INTERVAL_MS`
282
+
283
+ Milliseconds between idle job fetches per worker. Each worker polls on its own, so an idle process runs about `queueConcurrency × 1000 / pollInterval` fetches per second. Default: `500`.
284
+
285
+ ### `WORKFLOW_POSTGRES_MAX_POOL_SIZE`
286
+
287
+ Maximum size of the internal `pg.Pool` used when `createWorld()` constructs the pool. Default: the `pg` default (`10`).
288
+
289
+ For higher worker concurrency, Graphile Worker recommends setting `maxPoolSize` to `10` or `queueConcurrency + 2`, whichever is larger.
290
+
291
+ ### `WORKFLOW_POSTGRES_APPLICATION_MANAGED_SHUTDOWN`
292
+
293
+ Set to `1` when the application or framework coordinates shutdown and awaits `world.close()` before closing its workflow HTTP server and any caller-owned pool. Default: unset (`false`).
294
+
295
+ ### `WORKFLOW_POSTGRES_RUN_STATUS_POLL_INTERVAL_MS`
296
+
297
+ How often a wait for a terminal run status re-reads the run, in milliseconds. Default: `1000`.
298
+
299
+ `await run.returnValue` asks the World to wait for the run to finish, and the Postgres World parks that wait on a `NOTIFY` issued by the run-terminal write. This interval is only the backstop: it bounds how long a lost notification can go unnoticed, so lowering it costs a query per waiting run per interval and buys nothing while notifications are arriving.
300
+
301
+ ### `WORKFLOW_POSTGRES_HEADERS_TIMEOUT_MS`
302
+
303
+ Maximum time in milliseconds a queue delivery waits for the workflow handler to begin responding before the delivery is failed and Graphile Worker redelivers the message. Default: `0` (no deadline).
304
+
305
+ A delivery executes the workflow body inline, so the handler responds only once that work is done. A deadline shorter than your longest inline step declares a healthy delivery crashed and redelivers it while the original is still running, executing the same steps twice. Set this only if you would rather a hung handler be redelivered than hold its worker slot until the process restarts, and set it above the longest inline work you expect.
306
+
307
+ ### `WORKFLOW_POSTGRES_BODY_TIMEOUT_MS`
308
+
309
+ Maximum gap in milliseconds between response body chunks from the workflow handler before the delivery is failed and redelivered. Default: `0` (no deadline). The same caution as `WORKFLOW_POSTGRES_HEADERS_TIMEOUT_MS` applies.
310
+
311
+ ### `WORKFLOW_POSTGRES_HOOK_RETENTION_LIMIT_DAYS`
312
+
313
+ Maximum [`experimental_minRetention`](/docs/api-reference/workflow/create-hook#keep-a-token-unavailable-after-the-run-ends) accepted by the Postgres World, in days. Default: `30`. Set this to the same limit as your production World so oversized values fail during development.
314
+
315
+ ### `WORKFLOW_QUEUE_NAMESPACE`
316
+
317
+ Queue topic namespace shared by build output and the Postgres World. Default: unset.
318
+
319
+ For example, `custom` changes the queue topic prefix from `__wkf_workflow_` to `__custom_wkf_workflow_`. The value must be lowercase alphanumeric and start with a letter.
320
+
321
+ ### `WORKFLOW_STREAM_FLUSH_INTERVAL_MS`
322
+
323
+ Group-commit window, in milliseconds, for the leading chunk of an idle stream. Default: `0` (dispatch immediately). A positive value holds the first chunk up to that long to collect a group, trading first-chunk latency for fewer requests. Chunks arriving while a request is in flight always coalesce into the next group regardless.
324
+
325
+ ### Programmatic configuration
326
+
327
+ {/*@skip-typecheck: incomplete code sample*/}
328
+
329
+ ```typescript title="my-world.ts" lineNumbers
330
+ import { createWorld } from "@workflow/world-postgres";
331
+
332
+ export default createWorld({
333
+ connectionString:
334
+ process.env.WORKFLOW_POSTGRES_URL ?? process.env.DATABASE_URL!,
335
+ jobPrefix: "myapp_",
336
+ namespace: "myapp",
337
+ queueConcurrency: 50,
338
+ maxPoolSize: 52, // overrides WORKFLOW_POSTGRES_MAX_POOL_SIZE
339
+ streamFlushIntervalMs: 10,
340
+ });
341
+ ```
342
+
343
+ Options passed to `createWorld()` take precedence over the environment variables above. Export the World from a module and point `WORKFLOW_TARGET_WORLD` at that module:
344
+
345
+ You can also pass an existing `pg.Pool` as `pool` instead of a connection string.
346
+
347
+ ```bash title=".env"
348
+ WORKFLOW_TARGET_WORLD="./my-world.ts"
349
+ ```
350
+
351
+ ### Application-managed shutdown
352
+
353
+ Graphile Worker responds automatically when the application is asked to shut down. If your application or framework already coordinates a broader shutdown sequence, set `WORKFLOW_POSTGRES_APPLICATION_MANAGED_SHUTDOWN=1` when selecting the package directly with `WORKFLOW_TARGET_WORLD`, or set `applicationManagedShutdown: true` in a programmatic World. Use the application's normal shutdown hook. This prevents Graphile Worker's default handler from terminating the process as soon as its queue stops. The hook must handle cleanup errors and await resources in this order:
354
+
355
+ 1. `world.close()`
356
+ 2. The workflow HTTP server
357
+ 3. Any caller-owned `pg.Pool`
358
+
359
+ Closing the world stops new queue claims and waits for active jobs. After Graphile Worker's grace period, a pending workflow HTTP request is aborted. Graphile Worker unlocks that same row through its normal failure handling. The already-claimed delivery consumes a Graphile attempt and is retried only if its attempt budget remains; a one-attempt or final-attempt job is not retried. The shutdown handler does not insert a successor row. Because a client abort does not prove the server handler stopped, workflow and step handlers must tolerate at-least-once execution.
360
+
361
+ ## How it works
362
+
363
+ The Postgres World uses PostgreSQL as a durable backend:
364
+
365
+ - **Storage**: Workflow runs, events, steps, and hooks are stored in PostgreSQL tables.
366
+ - **Job queue**: [graphile-worker](https://github.com/graphile/worker) handles reliable job processing with retries.
367
+ - **Streaming**: PostgreSQL NOTIFY/LISTEN enables real-time event distribution.
368
+
369
+ This architecture ensures workflows survive application restarts with all state reliably persisted. For implementation details, see the [source code](https://github.com/vercel/workflow/tree/main/packages/world-postgres).
370
+
371
+ ## Security
372
+
373
+ Unlike the [Vercel World](/worlds/vercel), which authenticates and [encrypts](/docs/how-it-works/encryption) workflow traffic automatically, the Postgres World does **not** automatically authenticate requests that drive workflow execution. Adding that is your responsibility and should be in place before an app using this World is used in production and reachable by untrusted clients. We also recommend [enabling encryption](/docs/how-it-works/encryption#custom-world-implementations).
374
+
375
+ ### Protect the queue route
376
+
377
+ `POST /.well-known/workflow/v1/flow` is where the worker delivers workflow orchestration and queued step invocations. It is mounted in your application like any other route, so it is publicly reachable by default, and it accepts any request whose headers and body are well-formed: there is no signature, shared secret, or caller check. Anyone who can reach it can forge or replay a workflow or step invocation, including steps your application would only reach after its own gating. Restrict it before you expose the app.
378
+
379
+ The other routes under `/.well-known/workflow/v1/` differ:
380
+
381
+ - `webhook/:token`, created by [`createWebhook()`](/docs/api-reference/workflow/create-webhook), is authorized by the token in the URL and nothing else. Use [`createHook()`](/docs/api-reference/workflow/create-hook) behind your own authenticated route and [`resumeHook()`](/docs/api-reference/workflow-api/resume-hook) when you need more than that.
382
+ - `manifest.json` responds with `404` unless the app was built with `WORKFLOW_PUBLIC_MANIFEST=1`. That variable is read at build time, so unsetting it in the runtime environment of an already-built deployment does not withdraw the manifest. Leave it unset outside of testing, because the manifest lists your workflow and step names.
383
+
384
+ ### Bringing your own auth
385
+
386
+ Workflow does not prescribe an auth mechanism for self-hosted Worlds, so restrict the flow route at the network edge rather than inside the application:
387
+
388
+ - **Keep the flow route unreachable from outside.** By default the worker delivers to a loopback address (`http://localhost:{PORT}`, or `WORKFLOW_LOCAL_BASE_URL` when set), so in the common single-process topology nothing outside the container needs to reach it. Blocking external requests to `/.well-known/workflow/v1/flow` at your ingress, reverse proxy, or firewall costs you nothing, because loopback delivery never traverses that layer. Do not block the whole `/.well-known/workflow/` prefix if you use [`createWebhook()`](/docs/api-reference/workflow/create-webhook): its `webhook/:token` route sits under the same prefix and has to stay reachable by whoever calls it.
389
+ - **Authenticate at the proxy when the routes must cross hosts.** If your web tier and workers are separate deployments, require mTLS or a shared-secret header at the proxy in front of the application, and strip any client-supplied copy of that header at the edge.
390
+ - **Do not gate these paths in framework middleware.** The [Next.js setup guide](/docs/getting-started/next) tells you to exclude `/.well-known/workflow/*` from your proxy matcher, because a handler that consumes the internal request body breaks execution. Adding the paths back in order to gate them reintroduces that failure mode.
391
+ - **Do not expect the World to present a credential.** It does not sign its delivery requests or attach a secret to them, so an in-application check that requires one will reject the World's own deliveries.
392
+
393
+ ### Data at rest
394
+
395
+ The Postgres World does not currently implement `getEncryptionKeyForRun()`, so it does not participate in Workflow's [end-to-end encryption](/docs/how-it-works/encryption): workflow and step inputs and return values, hook payloads and metadata, and stream chunks are all stored in your database in readable form. A World derived from this one can opt in by implementing that one method; see [Custom World implementations](/docs/how-it-works/encryption#custom-world-implementations). Until then, protect the database, its credentials, and its backups accordingly.
396
+
397
+ The [observability UI](/docs/observability) has no authentication of its own either. If you self-host `@workflow/web` against your Postgres database, put it behind your own auth layer, because everyone who can reach it can read every run.
398
+
399
+ ## Deployment
400
+
401
+ Deploy your application to any cloud that supports long-running servers:
402
+
403
+ - Docker containers
404
+ - Kubernetes clusters
405
+ - Virtual machines
406
+ - Platform-as-a-service (PaaS) providers, such as Railway, Render, and Fly.io
407
+
408
+ Ensure your deployment has:
409
+
410
+ - Network access to your PostgreSQL database
411
+ - Environment variables configured correctly
412
+ - The `start()` function called on server initialization
413
+ - `/.well-known/workflow/v1/flow` restricted so untrusted clients cannot reach it (see [Security](#security))
414
+
415
+ <Callout type="info">
416
+ The Postgres World is not compatible with Vercel deployments. On Vercel, workflows automatically use the [Vercel World](/worlds/vercel) with zero configuration.
417
+ </Callout>
418
+
419
+ ## Limitations
420
+
421
+ - **Reference implementation**: Tested and implements the complete World spec, but is not optimized for scale, speed, or security; a production deployment typically clones it and adapts it, or runs workers in separate processes with a more robust queuing system
422
+ - **No built-in authentication**: The workflow HTTP routes accept any request that reaches them; you must restrict them yourself (see [Security](#security))
423
+ - **No encryption**: Workflow and step data is stored unencrypted; the World does not currently implement [end-to-end encryption](/docs/how-it-works/encryption)
424
+ - **Requires long-running process**: Must call `start()` on server initialization; not compatible with serverless platforms
425
+ - **PostgreSQL infrastructure**: Requires a PostgreSQL database (self-hosted or managed)
426
+ - **Not compatible with Vercel**: Use the [Vercel World](/worlds/vercel) for Vercel deployments
427
+
428
+ For local development, use the [Local World](/worlds/local) which requires no external services.