CLI Reference
bext-lite is two binaries. bext-lite-build exports a PRISM app to a portable
bundle; bext-lite serves, packs, checks, signs and mobile-packages that bundle.
bext-lite-build- AOT exportbext-lite serve- run an exported appbext-lite check- lite-eligibility and policy gatebext-lite pack- build a single self-contained binarybext-lite keygen/publish- OTA signing and updatesbext-lite mobile- iOS and Android packaging
bext-lite-build#
The AOT exporter. Compiles a PRISM site's routes, API routes, actions and islands
into self-contained JS bundles plus a manifest.json, so the runtime can serve
the site with no compiler on the device.
bext-lite-build <site_dir> [-o <out_dir>]
| Flag | Default | Meaning |
|---|---|---|
<site_dir> |
required | The PRISM app source directory. |
-o, --output <dir> |
<site_dir>/dist-lite |
Where to write the exported bundle. |
-h, --help |
Print usage. |
The bundles are byte-identical to what the full bext server produces, and render byte-identical HTML on QuickJS and V8. The exporter generates wrappers from source for every route, so the app does not need to have been served first.
"use client" React pages are compiled through a real React SSR build (via bun),
emitting a shared react-base.js once and a small per-route bundle that
requires it. String-builder pages need no bun and no React.
Environment variables
| Variable | Effect |
|---|---|
BEXT_LITE_VENDOR_NODE_MODULES |
Path to a node_modules holding react / react-dom / scheduler, used for the React SSR build. Only needed if the app has "use client" pages and does not vendor its own React. |
BEXT_SHARED_FRAMEWORK_DIR |
Path to the shared framework (@bext-stack/framework) for apps that import it. |
BEXT_LITE_BUN |
Override the bun binary used for the React build (default: bun on PATH). |
BEXT_LITE_ALLOW_DEGRADED_CLIENT |
1/true/yes/on: if a React page cannot be compiled, ship a string-builder fallback tagged renderer:"degraded" instead of failing the export. Off by default (a broken React bundle fails the build). |
# String-builder-only app: no bun, no env vars needed.
bext-lite-build ./app -o ./dist
# App with React pages, using the repo's vendored React.
BEXT_LITE_VENDOR_NODE_MODULES="$PWD/vendor/node_modules" \
bext-lite-build ./app -o ./dist
bext-lite serve#
Serve an exported dist-lite/ over HTTP.
bext-lite serve <dist-lite-dir> [--port N] [--host H] \
[--update-url URL --pubkey HEX [--data-dir D]]
| Flag | Default | Meaning |
|---|---|---|
<dist-lite-dir> |
required (unless packed) | The exported bundle to serve. |
-p, --port N |
8787 |
TCP port. |
--host H |
127.0.0.1 |
Bind address. |
--update-url URL |
Enable OTA content updates from this URL (see OTA). Requires --pubkey. file:// and http(s):// are both supported. |
|
--pubkey HEX |
The publisher's ed25519 public key. Unsigned bundles are refused. | |
--data-dir D |
<dist>/.bext-lite-data |
Where app data (KV / SQLite / fs) and the OTA bundle store live. Kept outside the swappable bundle so an update never wipes user data. |
-h, --help |
Print usage. |
bext-lite serve ./dist --port 8080
Pages render on a warm-context pool (cold first hit, then about 1 ms warm). Static
files under public/ are served directly. /api/* routes run their handler per
call.
--update-url requires --pubkey. Passing an update URL with no public key is a
hard error - the runtime refuses to run an unsigned or tampered bundle.
bext-lite check#
The lite-eligibility gate. Renders every route once under QuickJS and runs a static policy scan, then reports whether the app is publishable. Exits non-zero if any route errors or the policy denies the bundle - wire it into CI.
bext-lite check <dist-lite-dir> [--data-dir D]
| Flag | Default | Meaning |
|---|---|---|
<dist-lite-dir> |
required | The exported bundle to check. |
--data-dir D |
Data dir for routes that touch on-device data during the render. |
Output lists each route with its status, then a summary and the policy findings:
lite-eligibility check - site 'device-playground'
page / 200 html (17842 B)
...
N rendered, 0 engine error(s)
policy: 0 deny, 0 warn
=> PUBLISHABLE (lite-eligible + policy-clean)
The policy scan denies dynamic code execution (eval, Function) - QuickJS with
no compiler on the device cannot run it, and it is a signing risk.
bext-lite pack#
Bundle an exported dist-lite/ into the runtime binary itself, producing one
self-contained executable that serves the embedded app with no external files.
bext-lite pack <dist-lite-dir> --out <file>
| Flag | Default | Meaning |
|---|---|---|
<dist-lite-dir> |
required | The exported bundle to embed. |
-o, --out <file> |
required | Output binary path. |
bext-lite pack ./dist --out ./myapp
./myapp --port 8080 # runs the embedded app, no dist dir needed
A packed binary takes the same --port / --host flags as serve, and unpacks
the embedded archive to a temp dir on launch.
OTA signing and updates#
A bext-lite app splits into the native runtime plus a dist-lite/ payload of
interpreted JS. That lets you update the payload (routes, API, UI, logic) over the
air with no binary change and no app-store review - the CodePush / Expo model.
Every bundle is ed25519-signed; the runtime refuses anything that does not
verify.
bext-lite keygen#
Generate a signing keypair. Keep the private key offline.
bext-lite keygen
# private_key=<hex>
# public_key=<hex>
Serve with the public key (--pubkey <public_key>); publish with the private key.
bext-lite publish#
Sign an exported bundle into a release you can host.
bext-lite publish <dist-lite-dir> --key <hex|file> --version <V> --out <dir> \
[--url <archive-url>] [--notes <text>] [--min-runtime <ver>] [--skip-policy]
| Flag | Default | Meaning |
|---|---|---|
<dist-lite-dir> |
required | The exported bundle to sign. |
--key <hex|file> |
required | The private key: a hex string, or a path to a file holding it (including keygen's private_key=... line). |
-v, --version <V> |
required | Content version for this release. |
-o, --out <dir> |
required | Output directory for release.json + bundle.bin. |
--url <archive-url> |
The URL the bundle will be hosted at (embedded in release.json). |
|
--notes <text> |
Human-readable release notes. | |
--min-runtime <ver> |
Lowest native runtime version this content needs. A runtime older than this refuses the update with a 409 so a content update never lands on a runtime missing bridges it depends on. |
|
--skip-policy |
off | Publish even if the static policy scan denies the bundle (operator escape hatch). By default a policy DENY blocks signing. |
bext-lite publish ./dist --key ./signing.key --version 1.2.0 --out ./release
Serving with OTA on#
bext-lite serve ./dist --update-url https://cdn.example.com/app/release.json \
--pubkey <public_key> --data-dir /var/lib/myapp
The runtime downloads the release, verifies the signature and version, extracts to a temp dir, then atomically swaps the live app behind a lock - in-flight requests finish on the old bundle, new ones get the new one, no restart. A failed or tampered download never becomes current.
Control plane#
| Endpoint | Access | Purpose |
|---|---|---|
GET /__bext-lite/version |
open | Reports version (content) and runtime (native). |
POST /__bext-lite/update |
loopback only | Trigger an update check. Returns 409 { nativeUpdateRequired: true, ... } when the bundle's minRuntime exceeds the installed runtime. |
Set BEXT_LITE_UPDATE_ON_LAUNCH=1 to check for updates at boot. A PRISM app's own
UI can offer "Check for updates" by fetch('/__bext-lite/update', {method:'POST'}).
bext-lite mobile#
Package an exported app as a native iOS or Android app. The command family mirrors the desktop and server flows: it orchestrates the exporter plus the platform build tools rather than reimplementing a build.
bext-lite mobile init [--platform ios|android|both] [--name <app>] [--id <bundle.id>] [--out <dir>] [--force]
bext-lite mobile run <ios|android> --dist <dist-lite-dir> [--device <id>]
bext-lite mobile build <ios|android> --dist <dist-lite-dir> [--release]
bext-lite mobile dev <ios|android> --dist <dist-lite-dir>
Device builds need a platform toolchain: a Mac with Xcode for iOS, or the Android
SDK + NDK for Android. When the toolchain is absent, run / build / dev print
exactly what is missing and how to install it, then exit non-zero - they never
panic and never produce a broken artifact. So the same command is safe to run on a
CI box or a Linux laptop; it simply stops at the guard.
mobile init#
Scaffold the native shell project(s). Non-destructive: it refuses to overwrite a
non-empty existing shell without --force.
| Flag | Default | Meaning |
|---|---|---|
--platform ios|android|both |
both |
Which shell(s) to scaffold. |
--name <app> |
BextApp |
App display name. |
--id <bundle.id> |
dev.bext.app |
Bundle / application id. |
-o, --out <dir> |
mobile |
Output directory for the scaffold. |
--force |
off | Overwrite a non-empty existing shell dir. |
bext-lite mobile init --platform ios --name "My App" --id com.acme.myapp
mobile run#
Export, bundle dist-lite/ into the shell, build, and launch on a simulator or
device.
| Flag | Default | Meaning |
|---|---|---|
<ios|android> |
required | Target platform. |
--dist <dist-lite-dir> |
required | The exported app to bundle. |
--device <id> |
Target a specific simulator / device. |
bext-lite-build ./app -o ./dist
bext-lite mobile run ios --dist ./dist
mobile build#
Produce a store artifact: .ipa for iOS, .aab for Android.
| Flag | Default | Meaning |
|---|---|---|
<ios|android> |
required | Target platform. |
--dist <dist-lite-dir> |
required | The exported app to bundle. |
--release |
off | Release (vs debug) build. |
bext-lite mobile build android --dist ./dist --release
mobile dev#
Describe and run the live-reload loop: re-export on change, then hot-swap the bundle into the running app via Tier-1 content OTA. Content-only changes need no native rebuild.
| Flag | Default | Meaning |
|---|---|---|
<ios|android> |
required | Target platform. |
--dist <dist-lite-dir> |
required | The exported app to hot-swap. |
bext-lite mobile dev ios --dist ./dist
At launch the shell boots an embedded bext-lite, points a native WebView at
http://127.0.0.1:<port>/, and answers the device/* SDK namespace (camera,
biometrics, geolocation, secure store, haptics, share, notifications) with Swift /
Kotlin. See the Introduction for how the same source runs
headless and on-device.