Use bendo new and dev to scaffold and develop a Bend server with Bun.
Use build and start for a native executable. You need Bun and Bend (the
version in .bend-version); native builds also need clang. The CLI
finds Bend on PATH, then in ~/.bend/bin. Run bendo doctor to check it.
Until the toolkit is published, run it from a checkout:
export PATH="$HOME/.bend/bin:$HOME/.elan/bin:$HOME/.bun/bin:$PATH"
bun /path/to/bendo/packages/bendo/src/cli.ts new my-app
cd my-app
bun /path/to/bendo/packages/bendo/src/cli.ts devThe template serves Hello from Bendo! at / on port 8787. Set PORT to use a
different port. It imports only bendo/main.bend, the public Bend entrypoint.
new copies the framework into the app's bendo/ directory so the app works
before a BendHub release exists. Keep that directory with your app. The generated
package scripts assume bendo is available on PATH; the toolkit itself is not
yet available from npm. An existing destination is refused without overwriting it.
For an app with typed routes, or one with a React front end, use
create-bendo instead of bendo new.
Every command supports --help.
bendo new <dir>creates a minimal app, a package file, and.gitignore.bendo dev [entry] [--native]watches.bendfiles below the current directory and resolved local import directories, including targets outside the app reached through symlinks. The entry defaults tomain.bend. By default, Bend emits JavaScript and Bun runs it;--nativecompiles a native executable instead. Saves are debounced for 100 ms, including atomic editor saves..cache,dist,node_modules, and.gitare ignored.bendo build [entry]builds a native executable atdist/server. Run it from the app directory. The entry defaults tomain.bend.bendo build --shared [dir] [--out dir]compiles eachdir/*.bend(defaultshared) to an ES module and its TypeScript declarations indist/shared, for browsers and other JavaScript runtimes. See shared.md.bendo startrunsdist/serverwithout compiling or requiring Bend. It inherits your environment, includingPORT. Runbuildfirst.bendo routes [entry]builds the app if needed, runs it with--bendo-routes, validates its versioned JSON envelope, and prints a method/path table. The entry defaults tomain.bend. It reuses thebuildcache without publishingdist/server.bendo prove [file]runsbend <file> --verdict, defaulting toPROOF.bend. It printsALL PROOFS CHECKon success, preserves Bend's failure exit code, and shows law diagnostics and source locations without JavaScript stack traces. Proof checks have a ten-minute limit.bendo gen [entry]uses the same cached build and manifest validation asroutes. It writes TypeScript route types to.bendo/routes.tsand the typed client's params and response schemas to.bendo/routes.schemas.ts, and reports the number of routes.bendo openapi [entry]builds the app likeroutesand writes an OpenAPI 3.1 document from its manifest toopenapi.json.--bend <file>also writes a Bend module that embeds it,--config <file>sets info, servers, tags and per-route overrides, and--manifest <file>reads a saved manifest instead of building. See OpenAPI and API docs.bendo doctorchecks Bend, elan, Lean, clang, and Bun, printing versions and paths. Bend must match.bend-version; Lean must matchlean-toolchain. A pin is resolved by elan itself (elan dump-state --no-netwith the pin inELAN_TOOLCHAIN, run from the pin file's directory), so shorthand (v4.34.0), channels, custom origins, linked toolchains and path toolchains mean what they mean tolake, and an inheritedELAN_TOOLCHAINorelan overridecannot stand in for the project's pin. An official release is compared with the version Lean reports; anything else only has to be installed. Doctor runs the installedleanbinary directly and never downloads a toolchain. Pins come from the current directory when present, otherwise the toolkit checkout. Lean is selected explicitly from that pin, so no global elan default is needed. Lean is selected explicitly from that pin, so no global elan default is needed. Missing tools, failed version probes, and mismatches return a nonzero exit code with installation commands from the agent guide. Add~/.bend/bin,~/.elan/bin, and~/.bun/bintoPATH. Version probes have five-second limits.bendo --versionprints the toolkit version.
dev keeps the previous server serving while Bend builds its replacement. A
failed compile prints a file:line: message diagnostic without a JavaScript stack
trace; the next good save retries. If the compiler supplies only an excerpt that
matches multiple files, the CLI reports an ambiguous location, lists the candidate
files and retains the excerpt. It does not guess an entry-file line number.
A new save cancels the obsolete compiler process group and starts the latest
revision after the debounce. SIGINT and SIGTERM stop the server, candidate and
compiler process groups, including native compiler children. A process that
ignores termination is killed after 500 ms. Compilation has a 180-second limit.
The dev loop owns the public loopback port (PORT, default 8787) and forwards TCP
connections to the current server. Candidates receive a private PORT; apps must
honor it and listen on 127.0.0.1, as the template does through
B.config(), whose server reads PORT at startup. A candidate must accept
TCP connections within ten seconds before it replaces the old server. Failed
startup leaves the old server available. The framework's listening message shows
the candidate's private port; use the public port for requests. On replacement,
existing connections to the old process close, and new connections reach the new
server. build and start run directly without the dev proxy.
Builds use .cache/bendo/, keyed by target, canonical transitive Bend imports,
foreign effects and their C/JS twins, Bend executable/version, native toolchain
versions, platform, and architecture. Canonical paths prevent symlink cycles in
the import scan. Successful outputs are published atomically from immutable
source snapshots. A failed build preserves the last dist/server. Delete
.cache/bendo to force a fresh compile. Effect changes invalidate the next build;
automatic watching currently triggers on .bend saves only.
Use the JavaScript default for development. Bend's TCP JavaScript effects use
bun:ffi, so this mode requires Bun. Native mode exercises the production runtime
but also pays for C generation and clang on every uncached source revision.
build and start remain native regardless of the dev mode.
Bend 2.0.38 checks the whole imported program on every compilation. Its --help
and guide expose no incremental checker, imported-module cache, or flag that
skips checking unchanged imports. Checking the template alone costs about six
seconds on ms-a2; --check-only still traverses the whole framework. Reaching the
two-second fresh-save target requires incremental checking upstream. Cached source
revisions skip compilation, but a fresh save still needs that whole-program check.
compile: ready reports compiler time. restart: launched includes waiting for
the candidate to listen and stopping the old process. Measure save-to-new-response
time with HTTP against the public port. The following measurements use three
fresh template response edits per mode on 2026-10-23, with Bend 2.0.38. They are
observations, not latency guarantees.
| Host | JavaScript dev median | Native dev median |
|---|---|---|
| ms-a2 (shared server) | 5.829 s | 18.564 s |
| Boat large | 11.823 s | 29.363 s |
All sampled requests during those six ms-a2 rebuilds received the old or new response, with no failures. The remaining delay is compiler work; the old server continues answering during it.
The build pipeline calls generateSchemas before compilation, including cache
hits. Its precompile phase reports that production schema generation is pending
BND-34/35. It does not run the research generator in prototypes/0003-typed-values
or check generated-file freshness. Schema-dependent applications must wait for
the production generator.
gen builds the app, then calls the manifest phase of that same seam. A manifest
requires an executable, so this phase runs after compilation. Both routes and
gen require { "bendo": 1, "routes": [...] } with a method and absolute path
for every route. Invalid JSON, unsupported versions, and malformed routes fail
with a readable error. Manifest export has a ten-second limit and an 8 MiB output
limit. The app must exit without starting a server when passed --bendo-routes.
Decision 0003 specifies the precompile stage:
evaluate schema metadata through .mjs, generate named Bend records and
decoders, then compile handlers. The manifest phase now writes
TypeScript route types. build --shared remains a separate step (BND-58).
Unsupported options fail explicitly.
Run the CLI integration tests with bun test packages/bendo --timeout 30000
from the repository root. The ticket's packages/bendo/cli path corresponds to
packages/bendo/src/*test.ts in this checkout.