Introduction
bext-lite runs a single PRISM app outside the multi-tenant platform, on a tiny runtime, across six targets from one source. Write the app once, then ship it as a native binary, a desktop app, an edge function, a browser app, a mobile app, or an embedded appliance. Same codebase, swappable runtime.
The full bext server is a 124 MB Rust binary with V8 isolate pools, a WAF, TLS,
nginx-compat and masquerade routing. That is the right shape for hosting many
tenants at *.bext.dev. It is the wrong shape for "double-click to run this app,"
a Cloudflare edge function, or an offline-first phone app. bext-lite is the small,
single-tenant runtime for those: no V8 cage, no Node, no Electron, no
multi-tenancy.
The whole thing is about 4 MB#
- A 1.8 MB v8-free host binary renders your app. There is no Node runtime, no V8 pointer cage, no install step.
- The app itself (bundles + static assets) is a couple more megabytes, so the whole thing ships in about 4 MB and starts instantly.
- The data-capable build (SQLite for KV and per-app DB, TLS for external fetch and OTA) is about 6 MB. Still megabytes, not hundreds.
Compare that to Electron at 150 MB+.
One app, every runtime#
The AOT exporter compiles your PRISM routes to plain JavaScript that references host functions by name. The output is the same for every target. What changes is the JavaScript engine and the host bridge underneath it, not your code.
| Target | Engine | Footprint | Good for |
|---|---|---|---|
| Single static binary | V8 or QuickJS | ~4 MB total | Internal tools, side-cars, air-gapped deploys |
| Desktop app | V8 (React snapshot) | 5-20 MB | Cross-platform SaaS as a desktop app |
| Edge functions | QuickJS on WASM | sub-MB cold | Globally distributed SSR, no servers to run |
| Browser / offline | QuickJS on WASM | ~3.5 MB cached | Demos, local-first apps, zero-backend tools |
| Mobile (iOS & Android) | QuickJS, no JIT | per-render <5 MB | Real native apps from one web codebase |
| IoT / embedded (ARM) | QuickJS | <5 MB + app | Kiosks, gateways, on-prem appliances |
The native binary target (bext-lite serve) is shipped and is what you run
today. Desktop, mobile, edge, browser and embedded reuse the same exported
bundle and the same runtime; some of their packaging steps need a platform
toolchain (a Mac for iOS, the Android NDK, a WASM toolchain for edge). See
CLI Reference for exactly what runs now.
The V8 / QuickJS split#
There is no single engine answer, and the split is forced by two hard facts, not preference:
- iOS bans JIT for third-party apps. A JIT V8 cannot run inside an iOS app. QuickJS is a pure interpreter with no JIT, so it is App Store legal by construction.
- You cannot embed V8 in a WASM module. Edge and browser targets are either someone else's engine you do not control, or WASM where only an interpreter fits.
So V8 powers the desktop and single-binary server (it already works, and the React snapshot is proven there), and QuickJS powers everything else: mobile, edge, browser and embedded. A compiled PRISM bundle plus 122 KB of pure-JS polyfills runs unchanged on both.
Byte-faithful rendering#
An exported bundle is byte-identical to what the full bext server produces (proven
by sha256), and it renders byte-identical HTML on QuickJS and V8. What you test is
what every device ships. Run bext-lite check to render every route once under
QuickJS and confirm the whole app is lite-eligible before you publish.
Who it is for#
- You want to hand someone a single file that runs your app with no install.
- You want a desktop app from your web codebase without shipping a browser.
- You want SSR at the edge, or a fully client-side offline app.
- You want a real native mobile app (camera, Face ID, offline data) from the same source.
- You want to run on a Raspberry Pi or an on-prem box with no cloud dependency.
Next steps#
- Installation gets the two binaries built and verified.
- Quickstart takes you from zero to a running app in a few commands.
- CLI Reference documents every command and flag.