Overview
You write one app — controllers, modules, providers that don't know what they're deployed to. vp build can
turn that same source into eight different native artifacts: a Cloudflare Worker with a complete
wrangler.json, a Vercel Build Output API tree, an AWS Lambda handler, a Deno Deploy script, and more. What
changes per target is one line — a platform entry in heximon.config.ts.
The part that matters most: not every platform can run every feature — a Lambda invocation has no standing
process for an in-process cron timer; Deno Deploy has no Durable Object equivalent. Heximon checks this at
compile time. Point a Durable Object at LambdaStrategy and the build fails with a diagnostic naming the
class and the platform, before anything ships — never a silent gap that only shows up in production.
Where are you deploying?
| Platform | Guide |
|---|---|
| Node, Bun, or Deno (no vendor, your own process) | Node — the zero-config default. |
| Cloudflare Workers | Cloudflare — Durable Objects, hibernating WebSockets, a real workerd dev host. |
| Vercel Functions or Edge | Vercel — cron, queues, and the one freezing platform with full workflow/saga support. |
| Netlify Functions or Edge | Netlify — scheduled functions, Async Workloads queue. |
| AWS Lambda | AWS Lambda — API Gateway v2, SQS push-consumer queues. |
| Deno Deploy | Deno Deploy — the smallest artifact: cron only, no queue, no DO. |
| Nitro (one codebase, many presets) | Nitro — Node, Cloudflare, Deno, Bun, Lambda from a single preset. |
| Nuxt (API + Vue on one server) | Nuxt — a Heximon API alongside your pages, SSR cookies included. |
Each guide covers exactly what lands in that platform's output tree and which of its own features are native vs. degraded.
The feature × platform matrix
Every cell below is enforced by a supportedFeatures flag on that platform's PlatformStrategy — the same
flag the compiler's plugins read to decide whether to fail your build.
Nitro reuses the underlying vendor strategy (so its supportedFeatures match column-for-column) but
subtracts two things because it supersedes the vendor's own entry emitter: cron only works for
Cloudflare-module/-durable and Vercel presets (Netlify natively supports cron, but Netlify-under-Nitro does
not), and the freeze-safe workflow/saga drain never wires up under any Nitro preset, even Vercel.
Both fail loud with a diagnostic naming the native host to use instead.
| Feature | Node | Cloudflare | Vercel Functions | Vercel Edge | Netlify Functions | Netlify Edge | AWS Lambda | Deno Deploy |
|---|---|---|---|---|---|---|---|---|
| HTTP controllers / contracts | yes | yes | yes | yes | yes | yes | yes | yes |
| WebSockets (plain) | yes | yes | no | no | no | no | no | no |
| WebSockets (durable, hibernating) | dev-only | yes | no | no | no | no | no | no |
| Server-Sent Events (plain) | yes | yes | no | no | no | no | no | no |
| Server-Sent Events (durable) | dev-only | yes | no | no | no | no | no | no |
| Durable Objects | dev shim only | yes | n/a | n/a | n/a | n/a | n/a | n/a |
Cron (@heximon/schedule) | yes | yes | yes | yes | yes | no | no | yes |
| Queue — producer | yes | yes | yes | no | yes | no | yes | no |
| Queue — consumer (poll: Drizzle/SQS/Upstash) | yes | n/a | no | no | no | no | no | no |
| Queue — consumer (push) | n/a | yes | yes | no | yes | no | yes | no |
| Durable workflows | yes | yes | yes | no | no | no | no | no |
| Saga timers | yes | yes | yes | no | no | no | no | no |
| Cross-service events — split / broadcast | yes | yes | no | no | no | no | no | no |
| Cross-service events — QStash push | yes | yes | yes | n/a | yes | n/a | yes | n/a |
| Auth — Scrypt hashing | yes | no | yes | no | yes | no | yes | no |
| Auth — Pbkdf2 hashing | yes | yes | yes | yes | yes | yes | yes | yes |
| OTel flush model | shutdown | waitUntil | perInvocation | waitUntil | perInvocation | waitUntil | perInvocation | waitUntil |
| Caching, health, notifications, OpenAPI/MCP | yes | yes | yes | yes | yes | yes | yes | yes |
yes = production-supported · no = fails the build at compile time with a named diagnostic · n/a =
the concept doesn't apply to this platform's model · dev-only = works in vp dev for local iteration,
not a production guarantee.
The pattern behind most of the nos: a freezing platform (Vercel, Netlify, Lambda) has no standing process to
hold a WebSocket, stream SSE, or run a long poll loop on — so plain WS/SSE, durable rooms, and poll-based queue
consumers are Node/Cloudflare-only, while push-based queues, cron (mostly), and QStash's push-broadcast leg
reach every platform instead. Two exceptions worth calling out by name:
- Vercel Functions runs workflows and saga timers despite freezing — a timer-wakeup switch (drain cron by default, or an opt-in delayed queue message) re-enters a suspended run; see Vercel.
- Auth's Scrypt hashing is Node-only because it imports
node:crypto;Pbkdf2(WebCrypto-only) is the edge-safe default and runs everywhere, including every edge target.
Netlify Edge additionally rejects cron outright (no scheduled-function primitive at all — the edge catch-all would silently swallow it), and Lambda has no first-class cron entry alongside its HTTP handler (wire EventBridge yourself, outside the framework).
Pick a strategy
A deploy strategy is a class you instantiate on the platform config key. It bundles everything the build
needs to know about the target: its runtime lifetime (a long-lived process vs. a request-scoped isolate), its
vendor identity, its supportedFeatures set, and — for vp build — how to assemble the platform's entry file
and where to write it.
import { defineHeximonConfig } from "@heximon/build";
import { CloudflareWorkersStrategy } from "@heximon/cloudflare";
import { HttpPlugin } from "@heximon/http/compiler";
export default defineHeximonConfig({
platform: new CloudflareWorkersStrategy(),
plugins: [new HttpPlugin()],
});
Omit platform entirely and Heximon resolves NodeHostStrategy — the zero-config default: a standalone
dist/server.js that runs on Node, Bun, or Deno.
| Platform | Strategy | Package |
|---|---|---|
| Node (default) | NodeHostStrategy | @heximon/node |
| Cloudflare Workers | CloudflareWorkersStrategy | @heximon/cloudflare |
| Vercel Functions / Edge | VercelFunctionsStrategy / VercelEdgeStrategy | @heximon/vercel |
| Netlify Functions / Edge | NetlifyFunctionsStrategy / NetlifyEdgeStrategy | @heximon/netlify |
| AWS Lambda | LambdaStrategy | @heximon/aws |
| Deno Deploy | DenoDeployStrategy | @heximon/deno |
A strategy also injects the compiler plugins its entry needs (the worker-entry emitter, a wrangler.json
writer, and so on) — you only ever list your own concept plugins (HttpPlugin, CqrsPlugin, …). Switching
platforms is a one-line change in heximon.config.ts; nothing in your app source references a vendor.
The plugin list itself tracks the platform: this real Lambda config omits SchedulePlugin, SagaPlugin, and
WorkflowPlugin because LambdaStrategy.supportedFeatures marks cron, saga, and workflows all false —
there's no point registering a plugin over a concept the target can't run.
export default defineHeximonConfig({
platform: new LambdaStrategy(),
plugins: [
new HttpPlugin(),
new CqrsPlugin(),
new EventEmitterPlugin(),
new DomainEventsPlugin(),
new IntegrationEventsPlugin(),
new HealthPlugin(),
new OtelPlugin(),
],
});
What vp build actually does
vp build runs the same compiler pass as vp dev, then hands the generated wiring to the configured
strategy instead of the default in-memory dev server:
- Compile — the compiler walks your
Module/Controller/handler classes, builds the static DI graph, and emits plain JS into.heximon/, exactly as it does forvp dev. - Assemble the entry — the strategy's
PlatformEmitterwrites the platform-specific boot file (worker-entry.js,vercel-entry.js,lambda-entry.js,deno-entry.js, …) that wires the generatedfetchhandler (plus scheduled/queue consumers, if your app declares them) into the shape the platform's runtime expects. - Write the deploy artifact — the strategy's
writeOutputstep produces the platform's native output tree:dist/worker.js+dist/wrangler.jsonfor Cloudflare,.vercel/output/for Vercel,.netlify/for Netlify,dist/lambda.jsfor AWS,dist/deno-worker.jsfor Deno.
Each platform's page in this section covers exactly what lands in its tree and why.
Two ways to ship: native vp build, or Nitro
Every platform above has a native strategy package (@heximon/cloudflare, @heximon/vercel, …) that
vp build targets directly — this is the deepest integration, and the only path for Durable Objects,
freeze-safe workflow drains, and a few other platform-specific guarantees called out per-page.
Nitro is the other path: one codebase, one preset string, covering Node, Cloudflare,
Deno, Bun, and Lambda without per-vendor packages — pick it when you want a single build to retarget across
runtimes with less platform-specific ceremony, and you don't need the two Nitro-subtracted features (native
cron on Netlify, freeze-safe workflow/saga drain) called out in the matrix above.
See Hosts for how the two paths relate to vp dev/vp build and the other host options
(library mode, alternate bundlers).
Verification status
Deploy claims in these pages are labeled by how they were checked:
- Live-verified — actually run against the real vendor (or the vendor's own runtime locally, e.g.
workerd via Miniflare). Cloudflare's REST-through-DO + SSE + 2-client WS + saga-timer path, Vercel's
durable-queue round trip and both
step.sleepwakeup legs, and Netlify's mixed-runtime split are all live-verified this way — seedocs/live-deploy-verification.mdfor the dated ledger. - Expected, not yet live-deployed — a real
wrangler deployto the Cloudflare network, any live AWS Lambda deploy, and any live Deno Deploy push are exercised viavp buildartifacts and in-process/e2e tests, not an actual vendor deploy yet. Each platform's page says so where it applies — don't read "supported" as "verified on the vendor's network" unless the ledger confirms it.
Ship it — heximon deploy
Once a platform is configured, vp build plus a hand-run vendor CLI command (wrangler deploy,
vercel deploy --prebuilt, …) is a valid path. The heximon deploy CLI command automates that
same build-then-ship sequence — it resolves the ship step straight off your configured platform instance, so
there's no separate deploy config to maintain:
heximon deploy # vp build, then ship via the configured platform's native CLI
heximon deploy --env production # --prod on Vercel/Netlify/Deno; production secrets on --provision
See the CLI deploy command for the full flag set — preflight checks, --create for a
fresh vendor account, --provision for secrets and infrastructure, --migrate, and --verify.
Next steps
- Node — the zero-config default: a standalone
dist/server.js. - Cloudflare — Workers,
wrangler.jsonas a build artifact, and the workerd dev host. - Vercel — Functions and Edge, the Build Output API tree, cron and queues.
- Netlify — Functions and Edge, scheduled functions, Async Workloads.
- AWS Lambda — the API Gateway v2 handler shape and SQS event-source queues.
- Deno Deploy — the self-contained Worker script and
Deno.cron. - Mixed-runtime split — one deploy, two runtimes, routed by prefix.
- Hosts — the full host picture: Vite dev server, Nitro, Nuxt, library mode, alternate bundlers.
Durable Objects
Author a single-threaded stateful actor at the edge with DurableObject and a DI-injected DurableFactory, address it by name, and reach storage through DurableContext.
Node
NodeHostStrategy, dist/server.js, @heximon/http/serve, node/bun/deno standalone deploy, the zero-config default platform.