Deploy Model
How Odyn builds, uploads, and serves your code through the CDN.
Odyn uses a build-then-upload pipeline: source files are bundled on the server, uploaded to Cloudflare R2, and served through a Cloudflare Worker at the edge. Every save-and-deploy cycle follows the same deterministic path regardless of deployment mode.
Pipeline Stages
Source files (editor)
|
Validate ---- errors shown in Problems panel
|
Sort files --- by sort_order, then alphabetically
|
Combine ------ file markers added for error mapping
|
esbuild ------ bundle, minify (production only), tree-shake (per-file only)
|
Upload ------- Cloudflare R2
|
Update KV ---- version pointer (production only)
|
Serve -------- Cloudflare Worker at edgeValidate
The build system checks for syntax errors, unresolved imports, and file constraint violations before invoking esbuild. If any check fails, the deploy is rejected and the Problems panel reports the error with the source file, line number, and a code snippet around the failure.
Sort
Files are ordered by their sort_order value (set via drag-and-drop in the file tree), then alphabetically within the same sort order. This ordering determines the concatenation sequence in bundled mode and the execution order in per-file mode.
Bundle
esbuild processes the sorted files with these settings:
| Setting | Staging | Production |
|---|---|---|
| Minify | No | Yes |
| Source maps | Inline | None |
| Target | ES2020 | ES2020 |
| Tree-shaking | Mode-dependent | Mode-dependent |
In bundled mode, output is a single IIFE. In per-file mode, output is ES modules with automatic code splitting. In folder bundle mode, each marked folder produces its own IIFE.
Upload
Built assets upload to Cloudflare R2. Staging writes to a fixed path that overwrites on each deploy. Production writes to a versioned path keyed by timestamp, preserving all previous versions.
Version Pointer
For production deploys, a Cloudflare KV entry maps the project's floating alias (@latest) to the newly uploaded version. Pinned version entries are also written so that @{versionNumber} URLs resolve correctly. See Version Pinning vs Floating Aliases.
Staging vs Production
Staging and production are separate environments with different storage, caching, and versioning behavior.
| Behavior | Staging | Production |
|---|---|---|
| R2 path | Fixed, overwritten each deploy | Versioned, never overwritten |
| KV pointer | None | @latest and @{version} |
| Cache headers | no-cache | Immutable |
| Source maps | Inline | None |
| Minification | Off | On |
| Rate limit | 60/min (auto-deploy) | 10/5min |
Staging is designed for fast iteration: deploy, refresh, verify. Production is designed for reliability: immutable assets, versioned URLs, edge caching.
Three Deployment Modes
Odyn supports three modes that affect how esbuild processes files and what URLs appear in the embed code.
- Bundled (default): All JS concatenated into a single IIFE (
bundle.js), all CSS intobundle.css. - Per-file: Each entry point produces its own ES module. Shared code is split into chunks automatically.
- Folder bundle: Folders marked as bundles produce isolated IIFE outputs.
See Deployment Modes Reference for embed examples and the mode comparison table.
Deploy Lock
Only one deploy can run at a time per project per environment. A Postgres-based lock with a 60-second TTL prevents concurrent esbuild invocations from corrupting output. If a deploy is already in progress, the API returns 409. Stale locks auto-expire if a process crashes. See Deployment Lock Contention.
Build Metadata
Each deployment stores metadata in the deployments.metadata JSONB column:
buildStats: per-language size breakdown (JS size, CSS size, total)deploySummary: file counts by category (bundled, per-file, excluded)fileHashes: content hashes for cache busting
See Build Metadata Reference for the full schema.