loftur

Concepts

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.