Deploy flow
From POST /v1/deploys to :slug.apps.omg.dev serving 200.
A "deploy" turns a durable version snapshot into a public app at
:slug.apps.omg.dev. The editor sandbox does not become the production app.
Control-plane publishes a runId by resolving its resultSnapshotId first,
then infra forks a short-lived builder from that snapshot. The flow lives in
apps/infra/internal/orchestrator/manager.go : Deploy().
Steps
InsertDeploy, status=building
Control-plane passes snapshotId + runId
Fresh VM from the files snapshot
vibes-build → dist/ + .vibes/
static vs serverful path
- Reserve slug —
InsertDeploywith status=building. Concurrent first-deploys of the same slug seeErrSlugExists. - Resolve version snapshot — control-plane waits for the version's
end-of-turn files snapshot (
runs.resultSnapshotId) and callsPOST /v1/deployswith{ snapshotId, slug, runId, async: true }. Publishing never snapshots or stops the live editor VM. - Fork builder — fresh Firecracker VM forked from the version snapshot,
used only to run the build. For
files_onlysnapshots this restores the project tarball on top of the warm template. - Build — runs
vibes-buildinside the builder. Emitsdist/,.vibes/server.mjs,.vibes/data.db,.vibes/artifacts.json. - Branch on manifest — read
.vibes/artifacts.json. Ifstatic && !hasDb && functions==0→ static path; else serverful.
Static path (no VM at runtime)
Tar dist/ only inside the builder, read the tarball back via guest
agent, walk it in-memory with archive/tar, and upload each entry to
static-deploys/:slug/v:N/:path with per-file Cache-Control. No runtime
VM. Finish with UpdateDeploy(Static=true, StaticVersion=N) — atomic flip.
Serverful path
- Tar
dist/+.vibes/into a single artifact tarball, upload toproject-tarballs/:artifactId/. Stop builder. - Fork runtime from the production base or warm template (not from the
version snapshot). If that base is absent locally, restore the warm rootfs
from Tigris once per snapshot, then extract
project.tar.gzon top. startDeployServer(slug, restoreV=N-1, replicateV=N):litestream restorefromdeploys-db/:slug/v:N-1/data.db(skipped on first deploy).litestream replicate -exec bun .vibes/server.mjsagainstdeploys-db/:slug/v:N/data.db.
WaitForDeployReady(forked.VMIP, 30s)— block until bun is accepting TCP on :3000.UpdateDeploy(SandboxID=forked.ID, ArtifactID=..., Version=N). Atomic flip.
Node loss and cold-start failover
The project artifact and warm rootfs are both durable in Tigris; local rootfs files are caches. The edge keeps a deploy on its assigned node while that node's heartbeat is healthy. A stale, draining, or storage-starved owner is replaced by a healthy peer, which restores the missing base and project artifact. The peer claims the deploy in sqld with a compare-and-swap, clearing the old sandbox pointer, then binds its new sandbox under the claimed node. This prevents two peers from cold-starting the same deploy or a late old-node write from stealing ownership back.
Atomic flip
Both paths converge on UpdateDeploy as the single source of truth
for "what is the current version?". DeployProxy.ServeHTTP reads
store.GetDeploy(slug) per request — there is no caching layer
between sqld and the proxy decision. Edge-cache TTLs are short enough
(60s for HTML) and v:N GC is deferred enough (90s) that the cached
old HTML never out-lives its referenced assets.
Rollback
Pre-flip: anything failing in steps 5–8 cleans up the new sandbox and
GCs the v:N Tigris prefix it was about to populate. Post-flip: a
rollback flips the active static version by runId when retained; otherwise
the same version can be rebuilt and published again. A failed deploy never
modifies the live Deploy row.
Redeploy GC
After a successful flip:
- Serverful — drain old sandbox (litestream sigterm flush), stop,
GC
v:N-1Tigris prefix immediately. - Static — defer
DeleteStaticVersion(N-1)by 90s so edge-cached oldindex.html(max-age=60s) doesn't 404 on its hashed assets during the transition.
The 90s defer must stay > the index.html max-age. serveStatic
also falls back to v:N-1 on a hashed-asset miss, which is the
lookup half of the same property.