Targ Apps Docs
Chroma

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 | iex

Under 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 install

If 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       # Windows

Provisions 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-root

CHROMA_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, standard

The 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 demo

Fails 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.jsonindexeddb for static, filesystem for dynamic (and standard).

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 template

Static 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.

  • build minifies the entry HTML and every file listed in build.scripts/build.styles as written (via esbuild --minify, file by file), and inlines chroma_bg.wasm as base64 directly into a minified chroma.js — the shipped pkg/ becomes a single JS file, no separate .wasm request, and the engine is never recompiled.

dynamic — ES modules across multiple files, imported from a single jsEntry.

  • build bundles the whole module graph with esbuild --bundle --format=esm --minify into one outfile (default bundle.js), and copies chroma_bg.wasm next to it unmodified (wasm-bindgen resolves it at runtime via new URL(..., import.meta.url), so it must sit alongside the bundle).
  • If engine.rebuildWasm is true, the engine is recompiled from the vendored source at $CHROMA_HOME/engine-src (./build.sh on macOS/Linux, an equivalent wasm-pack build --target web call on Windows) before packaging — useful if you've modified the engine itself. Requires monorepo-mode install. If false, the already-compiled pkg/ at engine.vendorDir is used as-is.
  • If package.zip is true, the output directory is packaged after bundling into an archive named after name, written next to outDir (not inside it): zip if available on macOS/Linux (falling back to .tar.gz if the zip binary isn't installed), or Compress-Archive (always produces a .zip) on Windows. This archive is the bundle Chroma Desktop imports — see Desktop → Importing an app.

chroma.json

FieldApplies toMeaning
namebothProject name; also the output archive name when package.zip is true.
modeboth"static" or "dynamic" — selects the build path above.
entrybothPath to the HTML entry file.
outDirbothBuild output directory (default dist).
engine.vendorDirbothWhere the vendored pkg/ lives in the project (typically "pkg").
engine.rebuildWasmdynamicRecompile the engine from source before bundling.
build.scripts / build.stylesstaticArrays of JS/CSS files to minify individually.
build.jsEntrydynamicEntry module for the esbuild bundle.
build.outfiledynamicBundled output filename (default bundle.js).
package.zipdynamicZip (or tar.gz) the build output after bundling.
pluginsbothArray of plugin names vendored into plugins/ on chroma new (e.g. ["chroma-fs"]).
dev.portbothDefault port for chroma dev (overridable with --port).
dev.desktopbothWhen 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-mode install and for dynamic builds with rebuildWasm: true.
  • Node.js (with npx) — required by both install (to warn you upfront) and build (esbuild-based minification/bundling runs through npx --yes esbuild).

Troubleshooting

MessageCauseFix
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.sh
pwsh ./scripts/build-chroma-cli.ps1

Pass --skip-zip / -SkipZip to skip the final archive step. Full script reference: scripts/index.md.

Where this fits in the pipeline

  1. chroma new scaffolds an app from a template.
  2. chroma dev serves it locally while you iterate — see Server for exactly how requests are handled.
  3. chroma build compiles and packages it, optionally into a .zip (package.zip: true, dynamic mode).
  4. That .zip is dropped onto Chroma Desktop to run outside the browser, with no dev server involved.

On this page