The brainy CLI
In this section
npx brainy execs one of two bundled Rust binaries, resolved from optional npm packages — the native server (brainy-serve: every engine door over HTTP, SSE, WebSockets, gRPC and MCP) and the embedding + rerank service (brawn, a separate project this CLI resolves and execs the same way it always has — see @soulcraft/brawn). Every one of these doors is optional: nothing about them is required to run @soulcraft/brainy, and a customer who installs neither package loses nothing except these doors — every other engine capability runs in-process.
command | what it does | optional package |
|---|---|---|
| The starter — serves |
|
| Bind the listeners yourself — full detail at brainy serve |
|
| Speak MCP over stdio — full detail at MCP |
|
| Run the bundled embedding + rerank service on a box of your own |
|
| Build an air-gapped install kit from every optional package this install pins | none — builds a kit covering whichever of the above ARE pinned |
This page covers the CLI shims — install lines, resolution, refusals. The native server's own doors, tools and protocol are documented in full at brainy serve and MCP; nothing about them is restated here.
Models
Three models ship inside @soulcraft/brainy itself (~305 MB together, every platform, zero configuration): the embedder (all-MiniLM-L6-v2), the default rerank cross-encoder (ms-marco-MiniLM-L6-v2), and the late-interaction encoder (answerai-colbert-small-v1). One model is optional — the 12-layer cross-encoder, @soulcraft/brainy-model-ms-marco-minilm-l12-v2, packaged the same way as the two binaries above:
npm install @soulcraft/brainy-model-ms-marco-minilm-l12-v2BASHSee the rerank stage for what it's for and how it's loaded. Installing none of these three optional packages — this model, brainy-serve, brawn — costs you nothing except the doors and checkpoints they carry; every other engine capability runs in-process with what already shipped.
Air-gapped hosts: npx brainy bundle --out <dir> builds a local install kit carrying every optional package above this install pins — see npx brainy bundle below.
npx brainy — the starter, serve, mcp
npm install @soulcraft/brainy-serve-linux-x64-gnuBASHLinux x64 (glibc) is the only platform packaged in this release; on any other platform, or with the package not installed, every one of these three commands refuses by name — the bare starter, serve, and mcp alike:
[brainy] REFUSED — the @soulcraft/brainy-serve-linux-x64-gnu optional package is not installed.
[brainy] THE CURE — npm install @soulcraft/brainy-serve-linux-x64-gnuOnce installed:
npx brainy # the starter — zero configuration
npx brainy serve --listen 127.0.0.1:8300 # bind the listeners yourself
npx brainy mcp # speak MCP over stdioBASHserve and mcp pass every flag straight through to the brainy-serve binary, unvalidated — this CLI carries no copy of its grammar (--listen, --brain, --keys, ...) and no defaults of its own. Run the resolved binary's own --help (or see brainy serve) for the exact options. $BRAINY_KEY — the principal mcp's stdio transport runs as, since stdio carries no header to put a credential on — is read from the environment, never an argument, and reaches the binary because this shim never touches the environment; it only execs.
The bare starter takes no arguments at all: brainy-serve with zero arguments is its own distinct, deliberate mode (open ./brain, pick a free port, mint a key on first run, print the connect snippet), not serve with defaults filled in. Want --help? npx brainy --help (or -h, or help) prints this CLI's own usage — the one keystroke that stays available.
npx brainy embed-service — the embedding + rerank service
Embedding on a CPU costs tens of milliseconds per row. A GPU does thousands of rows a second. npx brainy embed-service puts the engine's own embedder — served by brawn, the model runner (@soulcraft/brawn, a separate package since 12.0) — on a GPU (or CPU) box of your own, so every other box running @soulcraft/brainy can reach it over your private network — with zero configuration on the engines that use it.
Install
npm install @soulcraft/brawn-linux-x64-gnuBASHThat is the whole install for a CPU service. Linux x64 (glibc) is the only platform packaged today; running the command on any other platform refuses by name — there is no install line that would fix it yet.
The CUDA package ships on brawn's own cadence, separate from this engine's — see @soulcraft/brawn's own release notes for whether it has shipped yet. The CUDA package, @soulcraft/brawn-linux-x64-gnu-cuda, carries ONE BINARY PER COMPUTE CAPABILITY — bin/brawn-sm89 (Ada: L4, 4090 class) and bin/brawn-sm120 (Blackwell) — plus the two ONNX Runtime execution-provider shared objects the CUDA provider needs beside them (bin/libonnxruntime_providers_cuda.so, bin/libonnxruntime_providers_shared.so), and a CAPS.txt recording what each binary was actually compiled for. There is no single binary that serves both capabilities — no PTX is embedded in either build, so an sm_89 binary refuses an sm_120 host and vice versa.
npm install @soulcraft/brawn-linux-x64-gnu-cudaBASHThe package is not a dependency of @soulcraft/brainy — it is an optionalDependency, so npm install skips it on a platform that does not declare it, automatically.
Run
npx brainy embed-service --bind 127.0.0.1:8200BASH--bind is required and has no default — a default is how something ends up listening on an interface nobody meant to expose. 0.0.0.0 and :: are refused, and so is any address outside the private ranges. For a GPU box of your own serving OTHER boxes on your network, bind its private LAN address:
npx brainy embed-service --bind 10.0.0.5:8200BASHThe chooser — how npx brainy embed-service picks a binary
The resolver reads this host's own CUDA compute capability with nvidia-smi --query-gpu=compute_cap --format=csv,noheader (probed in a separate subprocess — never by loading CUDA inside the CLI's own process, which has segfaulted on a cardless box in this fleet's own runtimes before), and follows one rule above everything else: once the CUDA package is installed, it either serves or refuses by name — it never silently falls back to the CPU package, even when the CPU package is also installed. A host that installed the CUDA package wants GPU service; silently handing it CPU service instead would hide exactly the failure an operator needs to fix.
this host | CUDA package installed? | what happens |
|---|---|---|
no GPU | no | the CPU package serves — the steady state |
no GPU | yes | refused by name — no NVIDIA driver found; the CPU package is named as the one that serves this host |
a GPU, cap | yes, carries that cap's binary | that cap's binary serves, with the package's |
a GPU, any other cap | yes | refused by name — the host's own cap is named alongside the caps the installed package actually carries |
a GPU | no | refused by name — a GPU is present but the CUDA package is not installed; the exact install line is named |
Every refusal in the last three rows names what was checked and what the cure is, on stderr, before any GPU is touched:
[brainy] REFUSED — a GPU is present on this host (CUDA compute capability 8.9) but @soulcraft/brawn-linux-x64-gnu-cuda is not installed.
[brainy] THE CURE — npm install @soulcraft/brawn-linux-x64-gnu-cuda[brainy] REFUSED — this host reports CUDA compute capability 7.5, which the installed @soulcraft/brawn-linux-x64-gnu-cuda package carries no binary for (it carries: sm89, sm120).
[brainy] There is no install line that fixes this in the current release.Before brawn's CUDA package has shipped, a GPU host sees the "not installed" refusal above even though the install line it names is not yet resolvable — there is no CUDA package on the registry until brawn publishes one (see "Install" above). This is deliberate: a GPU host that goes unnoticed and quietly serves at CPU speed is a worse outcome than a refusal that says exactly what is missing and exactly what to install once it exists.
USAGE
npx brainy embed-service --bind <ADDRESS:PORT> [OPTIONS]
OPTIONS
--model <DIR> Use a specific model assets directory instead of the
bundled one (passed through as the binary's own --assets).
-h, --help Usage text.
Every other flag the service binary itself accepts (--data-dir, --batch-max,
--queue-cap, --vram-budget-bytes, --nice) passes straight through — run the
resolved binary with its own --help for the exact grammar.License
Running the service requires a Brainy license carrying the services add-on capability — a claim on the same offline key the engine already verifies (see License Artifacts). A license without it is refused here, by name, before any package is resolved or any GPU is touched:
[brainy] REFUSED — the "services" capability is not on this license.
[brainy] your license does not carry the "services" capability.
[brainy] your key: tier pro, fingerprint 9f2c1a7b3e4d5680
[brainy] THE CURE — get or upgrade a license that carries "services" at https://soulcraft.com/accountThis is a separate, additive claim from your license's tier — tiers price the scale a brain runs at and never gate a capability; services is the one deliberate exception, gating this one optional door.
Zero-config discovery — nothing to configure on the engines that use it
Once the service is running, every @soulcraft/brainy engine on the same network finds it by convention, with no configuration:
Inside your own environment, point your DNS or hosts file at the conventional name
brainy-embed, on the conventional port8200— e.g.10.0.0.5 brainy-embed. An engine opening anywhere on that network resolvesbrainy-embed:8200, runs the identity handshake (GET /health) and an equivalence gate against a small sample, and starts routing bulk embedding work to it automatically. No answer is not an error — the store is served by the local CPU leg exactly as before, narrated once.BRAINY_EMBED_ENDPOINTis the last escape hatch, never the first configuration step: set it to override discovery entirely — a bare host, ahost:port, or a fullhttp://host:portURL. Useful for a laptop pointing at a box, a staging endpoint, or any shape the conventional name can't express. It has one other recognized value,local(BRAINY_EMBED_ENDPOINT=local) — the explicit opt-out: discovery does not run at all, no name is resolved and no endpoint is dialed even if one answers on the network, and the store is embedded by the local leg on purpose. This is the one door back to local embedding for an operator who wants it despite a service being present.BRAINY_EMBED_ENDPOINTS(plural) names an ORDERED, comma-separated list of endpoints for more than one GPU box —BRAINY_EMBED_ENDPOINTkeeps working as its one-element case.BRAINY_EMBED_TOKENcarries the bearer token an engine authenticates with. On first start, a service with no fleet secret configured mints its own token, writes it to its data directory (mode0600), and prints it ONCE — copy it intoBRAINY_EMBED_TOKENon every box that should reach this service. An engine that finds the service but has no token REFUSES BY NAME rather than silently falling back to the CPU: on the first bulk embed call since —addMany()/import()'s bulk door, or the re-embed ceremony — it throwsEmbedServiceNotAdmittedError, naming the endpoint, the status (401/403) and this variable, and nothing is embedded, locally or remotely, before it does. "I found a service but can't authenticate" and "there is no service here" are different facts, and only one of them is yours to fix with one environment variable — or, if local embedding is what you actually want, withBRAINY_EMBED_ENDPOINT=localabove. (A singleadd()and the per-row deferred-embed drain stay on the query path — local by default regardless of this — so this refusal is a bulk-work fact, not one every write hits.)
An endpoint is never trusted on its name alone. Every engine that discovers one runs an identity handshake (comparing model id, width and weights digest) and an equivalence gate (embedding a small sample through both the local and the remote embedder and requiring near-identical vectors) before routing a single row to it — a service serving the wrong model, or one whose weights were corrupted, is refused by name and the store keeps being served locally.
What this door does not change
The query path — the vectors your searches compare against — stays local by default; only bulk embedding work (writes, the re-embed ceremony) routes to the service automatically. And running no brawn at all is a fully supported, permanent configuration: the engine's in-process CPU embedder is always there, on every platform, with or without this package installed.
npx brainy bundle — an air-gapped install kit
Every door above resolves an optional package by installing it with npm install — which needs The Source reachable. npx brainy bundle is for a host that will never be able to reach it: it builds a local install kit, once, from a machine that CAN reach The Source, for installing somewhere that cannot.
npx brainy bundle --out ./brainy-bundleBASHThis resolves the CURRENT install's own @soulcraft/brainy version and every optionalDependencies pin it carries (any of the three packages above that this install has pinned), fetches each one's EXACT tarball, and writes:
./brainy-bundle/packages/*.tgz every tarball (the engine + each optional package)
./brainy-bundle/MANIFEST.sha256 every tarball's digest
./brainy-bundle/install.sh the offline install
./brainy-bundle/README.md sizes, versions, the air-gap steps--dry-run prints the plan (what would be fetched, and from which registry) without touching the network or the filesystem.
A pin this install carries that The Source does not have at that EXACT version is a hard refusal, named by package — this never substitutes "latest" or a nearest match.
Copy ./brainy-bundle to the disconnected host and run its own install.sh there:
sha256sum -c MANIFEST.sha256 # verify before trusting
./install.shBASHScope, stated honestly: this bundles the engine and its optionalDependencies pins ONLY. @soulcraft/brainy's ordinary dependencies (third-party packages from the public npm registry) are not vendored by this bundle — install.sh still needs those resolvable from npm's local cache or a reachable registry. "Air-gapped" names what this removes: ever needing The Source reachable again, not needing any registry reachable for anything.