The runtime has no build step
The impressive part of Loftur isn't a feature — it's an absence. There is no bundler, no Node, no build server, no local toolchain. Source becomes running code through two cheap steps and a runtime that instantiates per request.
Transpile on write, not build on deploy
When the agent writes a file, it's transpiled immediately with Sucrase — TypeScript and JSX stripped to plain ESM, per file, in the Worker. No type-checking, no bundling. The compiled text is cached; unchanged modules are never recompiled. A write that fails to transpile is rejected on the spot.
There is no separate "build" that turns your tree into a deployable artifact. The module map is assembled at load time and handed to Worker Loader, which links it itself. Bare imports are rewritten to relative module paths — a regex where a bundler would use an AST.
Isolates are content-addressed
Every running site is a Worker isolate keyed by the hash of its bundle:
draft:<runtime>:<sha256(bundle)>:<deps> ← your working tree
site:v<N>:<runtime> ← a published snapshot
Change a byte and the draft id changes, so the next request spins up a fresh isolate — then caches it. Change nothing and the cached isolate is reused. Nobody wrote cache-invalidation logic; it falls out of content-addressing for free.
Bumping the runtime version (the shim, vendored Preact, the builder) rolls the id prefix, invalidating every cached isolate at once — a one-line global cache bust.
npm without a bundler
You can import any workerd-compatible package. On first use Loftur crawls the
esm.sh module graph into a self-contained set, content-addresses every module into
R2, and pins a lockfile entry. Then it does the clever bit: it test-loads the
snapshot in a throwaway isolate to decide, empirically, whether the package
works — no curated allowlist. A package that reaches for a node: builtin fails
legibly, at write time, and the verdict is cached.
Capabilities, not connections
The site isolate never sees a raw database or secret. Its env is a set of
loopback entrypoints injected per request — GraphQL, records, a feature-DB SQL
seam, secrets, mail, uploads, a mediated fetch. Two tenants with byte-identical
bundles still get separate isolates, because their capability env differs.
The payoff of all this: the edit → see-it-live loop is a single request, and preview and production run the exact same code path.