CLI
Provision the toolchain and scaffold, serve, and build Chroma apps with the chroma command.
The chroma command (cli/chroma.sh on macOS/Linux, cli/chroma.ps1 on Windows — same commands, same flags, same chroma.json schema on both) is the fastest way to work with Chroma: it provisions the toolchain once, then scaffolds, serves, and builds apps without you touching wasm-pack or esbuild directly.
This page covers the CLI end to end. For what actually serves your files while you develop, see the dev server below and the full Server page. For running a finished build outside the browser, see Desktop.
Install
The fastest way to get chroma is the one-line installer — it downloads chroma-cli.zip and runs chroma.sh install / chroma.ps1 install for you, no manual unzip step:
sh <(curl https://docs.targapps.xyz/sh)irm https://docs.targapps.xyz/ps | iexUnder the hood this is exactly the standalone package below: it downloads chroma-cli.zip and runs its installer automatically.
Manual install (download the package)
Prefer to inspect the installer script before running it, or need to install offline? install also runs by hand, in one of two modes, detected automatically from where you run it:
Standalone mode — from the downloadable package
No Rust/wasm-pack needed. Download chroma-cli.zip, extract it, then:
# macOS/Linux
cd chroma-cli
chmod +x chroma.sh
./chroma.sh install# Windows (PowerShell)
cd chroma-cli
Unblock-File .\chroma.ps1
.\chroma.ps1 installIf Windows blocks the script with an execution-policy error: Unblock-File .\chroma.ps1 (marks the download as trusted) or run it explicitly with powershell -ExecutionPolicy Bypass -File .\chroma.ps1 install.
Installs the prebuilt engine as-is. dynamic apps lose the ability to set engine.rebuildWasm: true — there's no Rust source in this package to recompile.
Monorepo mode — from a full source checkout
Run once from a checkout of the Chroma repo (a Cargo.toml sits next to cli/):
./cli/chroma.sh install # macOS/Linux
.\cli\chroma.ps1 install # WindowsProvisions Rust (via rustup, if not already installed), the wasm32-unknown-unknown target, and wasm-pack; vendors the engine's Rust source into ~/.chroma so dynamic apps can later recompile it (engine.rebuildWasm: true); builds pkg/ from source if it isn't already built (./build.sh, which runs wasm-pack build --target web and appends an auto-init line — await __wbg_init(); — to chroma.js so importing the module instantiates the WASM automatically, no explicit init() call needed).
Either way, install also copies the prebuilt engine dev-server binary (built from Chroma-Server — see The dev server below, and the full Server page), copies the static/dynamic templates, installs the chroma command itself, and adds ~/.chroma/bin to your PATH (appending an export line to .zshrc/.bashrc/.profile on macOS/Linux depending on $SHELL, or prepending to the user Path environment variable on Windows — only if it isn't there already).
By default every run wipes $CHROMA_HOME and reinstalls from scratch — the safe way to recover from a broken or partial install. Pass --no-rebase-root to update in place instead:
./cli/chroma.sh install --no-rebase-rootCHROMA_HOME defaults to ~/.chroma and can be overridden by exporting it before running install. Its layout:
~/.chroma/
├── bin/ # chroma, engine, chroma-desktop
├── engine-src/ # compiled pkg/ (+ Rust source, in monorepo mode)
├── plugins/ # chroma-fs (built on install)
└── templates/ # static, dynamic, standardThe two scripts are behaviorally equivalent, but not byte-identical in implementation: on Windows, chroma.ps1 calls wasm-pack build --target web directly and appends the auto-init line itself, rather than invoking build.sh (a POSIX shell script). The observable result — a pkg/ with the same auto-initializing chroma.js — is the same either way.
Commands
chroma new <name> [--template static|dynamic|standard]
Scaffolds a new app into ./<name>, copying the chosen template plus a fresh vendored pkg/, and rewriting the name field in the new package.json/chroma.json to match.
chroma new my-app --template static # default if --template is omitted
chroma new my-app --template dynamic
chroma new my-app --template standard # dynamic + Chroma-Fs file explorer demoFails if <name> already exists in the current directory, if the template name isn't static, dynamic, or standard, or if chroma install hasn't been run yet.
chroma fs init
Creates chroma-fs.json in the current app directory. Backend defaults depend on mode in chroma.json — indexeddb for static, filesystem for dynamic (and standard).
chroma fs link
Builds Chroma-Fs from the TargApps workspace checkout and links @chroma/fs into the current project via npm install file:.... Requires Node.js. The standard template exposes this as npm run fs:link.
chroma dev [--port <n>] [--no-desktop]
Serves the current app with the installed Chroma-Server binary (engine), rooted at the current directory:
cd my-app
chroma dev # http://localhost:4173 (or chroma.json's dev.port)
chroma dev --port 5000
chroma dev --no-desktop # skip Chroma Desktop even on dynamic templateStatic and dynamic apps are served identically in development — both are plain ES modules the browser loads natively, so nothing is bundled or transformed for dev. Must be run from a directory containing chroma.json. Internally this execs engine --port <port> --dir . — see The dev server.
Dynamic template + dev.desktop: true (the dynamic template default): chroma dev also launches Chroma Desktop in develop mode — your project opens full-window immediately, without the import library. The CLI starts engine in the background, then runs chroma-desktop --develop --url http://127.0.0.1:<port>/<entry> --title <name>. Requires chroma install from a workspace checkout so chroma-desktop is built into $CHROMA_HOME/bin. Pass --no-desktop to serve in the browser only.
chroma build
Compiles and packages the current app per chroma.json, into chroma.json's outDir (default dist/), which is wiped and recreated on every run. What exactly happens depends on mode — see Templates below.
chroma help / chroma --version
chroma help (also --help, -h, or no arguments) prints the full command reference. chroma --version/-v prints the CLI version.
Templates: static, dynamic, and standard
Both static and dynamic templates vendor only a pkg/ directory (chroma.js + chroma_bg.wasm) at the project root — that's the entire runtime dependency. The engine server binary itself is never copied into a project; dev/build reach it through the CLI install.
standard — like dynamic, but ships a complete Chroma-Fs integration: chroma-fs.json, plugins/loader.js, a file explorer demo (src/components/FileExplorer.js), and npm run fs:init / npm run fs:link. See Chroma-Fs templates.
static — flat HTML/JS files, no module graph, no bundler.
buildminifies the entry HTML and every file listed inbuild.scripts/build.stylesas written (viaesbuild --minify, file by file), and inlineschroma_bg.wasmas base64 directly into a minifiedchroma.js— the shippedpkg/becomes a single JS file, no separate.wasmrequest, and the engine is never recompiled.
dynamic — ES modules across multiple files, imported from a single jsEntry.
buildbundles the whole module graph withesbuild --bundle --format=esm --minifyinto oneoutfile(defaultbundle.js), and copieschroma_bg.wasmnext to it unmodified (wasm-bindgen resolves it at runtime vianew URL(..., import.meta.url), so it must sit alongside the bundle).- If
engine.rebuildWasmistrue, the engine is recompiled from the vendored source at$CHROMA_HOME/engine-src(./build.shon macOS/Linux, an equivalentwasm-pack build --target webcall on Windows) before packaging — useful if you've modified the engine itself. Requires monorepo-mode install. Iffalse, the already-compiledpkg/atengine.vendorDiris used as-is. - If
package.zipistrue, the output directory is packaged after bundling into an archive named aftername, written next tooutDir(not inside it):zipif available on macOS/Linux (falling back to.tar.gzif thezipbinary isn't installed), orCompress-Archive(always produces a.zip) on Windows. This archive is the bundle Chroma Desktop imports — see Desktop → Importing an app.
chroma.json
| Field | Applies to | Meaning |
|---|---|---|
name | both | Project name; also the output archive name when package.zip is true. |
mode | both | "static" or "dynamic" — selects the build path above. |
entry | both | Path to the HTML entry file. |
outDir | both | Build output directory (default dist). |
engine.vendorDir | both | Where the vendored pkg/ lives in the project (typically "pkg"). |
engine.rebuildWasm | dynamic | Recompile the engine from source before bundling. |
build.scripts / build.styles | static | Arrays of JS/CSS files to minify individually. |
build.jsEntry | dynamic | Entry module for the esbuild bundle. |
build.outfile | dynamic | Bundled output filename (default bundle.js). |
package.zip | dynamic | Zip (or tar.gz) the build output after bundling. |
plugins | both | Array of plugin names vendored into plugins/ on chroma new (e.g. ["chroma-fs"]). |
dev.port | both | Default port for chroma dev (overridable with --port). |
dev.desktop | both | When true and mode is dynamic, chroma dev opens Chroma Desktop in develop mode. Default false (static), true (dynamic). |
chroma.json is a small, fixed schema. The CLI reads it by key regardless of nesting depth (via jq, falling back to a grep/sed extraction when jq isn't installed) — so the table above shows where each field conventionally lives, not a requirement that it be flat.
Example (static, from Chroma-CLI/examples/static/chroma.json):
{
"name": "chroma-static-app",
"mode": "static",
"plugins": ["chroma-fs"],
"entry": "index.html",
"outDir": "dist",
"engine": { "vendorDir": "pkg", "rebuildWasm": false },
"build": { "scripts": ["index.js", "plugins/loader.js"], "styles": [], "minify": true, "sourcemap": false },
"dev": { "port": 4173, "desktop": false }
}Example (dynamic, from Chroma-CLI/examples/dynamic/chroma.json):
{
"name": "chroma-dynamic-app",
"mode": "dynamic",
"plugins": ["chroma-fs"],
"entry": "index.html",
"outDir": "dist",
"engine": { "vendorDir": "pkg", "rebuildWasm": true },
"build": { "jsEntry": "src/main.js", "outfile": "bundle.js", "format": "esm", "minify": true, "sourcemap": true },
"package": { "zip": true },
"dev": { "port": 5173, "desktop": true }
}See Chroma-Fs for chroma-fs.json and the storage plugin API.
The dev server (engine)
chroma dev serves your app with engine — the prebuilt binary from Chroma-Server that chroma install copies to $CHROMA_HOME/bin. It's a zero-dependency, std-only HTTP/1.1 static file server: binds 127.0.0.1 only (never 0.0.0.0, never reachable from the network), one thread per connection, GET/HEAD only, no keep-alive, no TLS, no directory listing. / maps to index.html; unknown paths and paths that ..-escape the served root are rejected (404/403) rather than followed.
Chroma Desktop embeds a Rust module built to the same design (loopback-only, one thread per connection, path-traversal-safe) to serve imported apps outside the browser. The two are independent implementations of the same design, not the same binary — full details, including request parsing and path resolution, are in the Server page.
Requirements
- Rust +
wasm32-unknown-unknown, and wasm-pack — only for monorepo-modeinstalland fordynamicbuilds withrebuildWasm: true. - Node.js (with
npx) — required by bothinstall(to warn you upfront) andbuild(esbuild-based minification/bundling runs throughnpx --yes esbuild).
Troubleshooting
| Message | Cause | Fix |
|---|---|---|
Neither a Chroma checkout (Cargo.toml next to cli/) nor a prebuilt pkg/ next to this script was found. | install run from somewhere that's neither a full checkout nor a standalone CLI package. | Run install from cli/chroma.sh inside a real checkout, or from an unmodified Chroma-CLI package that still has its pkg/. |
Node.js is required for this step (bundling/minifying). Install it from https://nodejs.org and try again. | chroma build needs esbuild (via npx), and Node isn't on PATH. | Install Node.js, then retry chroma build. |
chroma.json not found in the current directory. | chroma dev/chroma build run outside an app directory. | cd into a directory created by chroma new (or containing your own chroma.json). |
'<name>' already exists in the current directory. | chroma new target directory is already taken. | Pick a different name, or remove/rename the existing directory first. |
Invalid template: '<x>' (use 'static', 'dynamic', or 'standard') | --template got an unsupported value. | Use static, dynamic, or standard. |
Chroma environment not found at $CHROMA_HOME. Run 'chroma install' first (from the repo). | chroma new/chroma dev run before install, or $CHROMA_HOME was deleted. | Run chroma install (from a checkout) first. |
Compiled engine not found at .../pkg. Run 'chroma install' first. | Templates directory exists but the vendored engine doesn't (partial/corrupted install). | Re-run chroma install (it wipes and rebuilds $CHROMA_HOME by default). |
wasm-pack not found. Run 'chroma install'. | dynamic build with engine.rebuildWasm: true, but wasm-pack isn't installed (e.g. standalone-mode install). | Install from monorepo mode, or set engine.rebuildWasm: false to build against the already-vendored engine. |
engine binary not found at .../bin/engine. Run 'chroma install'. | chroma dev run without the server binary installed. | Re-run chroma install. |
chroma-desktop not found. Run 'chroma install' from a full workspace checkout, or pass --no-desktop. | Dynamic develop mode requested but Desktop binary missing. | Run chroma install from the TargApps workspace, build Chroma Desktop manually, or pass --no-desktop. |
Plugin '<name>' not found at .../plugins/<name>. Run 'chroma install'. | chroma new references a plugin that wasn't installed. | Re-run chroma install (builds Chroma-Fs when the source is present). |
Unknown mode in chroma.json: '<x>' | mode isn't "static" or "dynamic". | Fix chroma.json's mode field. |
Every usage error above exits with status 1; success exits 0.
Release build (monorepo maintainers)
To assemble the standalone Chroma-CLI/ package, refresh chroma-cli.zip in the docs downloads folder, and build desktop installers for the current OS, run from the TargApps workspace root:
./scripts/build-chroma-cli.shpwsh ./scripts/build-chroma-cli.ps1Pass --skip-zip / -SkipZip to skip the final archive step. Full script reference: scripts/index.md.
Where this fits in the pipeline
chroma newscaffolds an app from a template.chroma devserves it locally while you iterate — see Server for exactly how requests are handled.chroma buildcompiles and packages it, optionally into a.zip(package.zip: true,dynamicmode).- That
.zipis dropped onto Chroma Desktop to run outside the browser, with no dev server involved.