CLI reference
Every nodal-agents command, its flags, and the environment variables that affect the CLI.
The nodal-agents binary is the single entry point for the whole stack: it starts and stops the embedded Postgres, the runner, and the dashboard, and handles setup, updates, and logs.
Prerequisites
Requires Node.js 22 or newer. The published package declares engines: { "node": ">=22" }. On an older Node the global install prints an EBADENGINE warning and the binary then crashes with a confusing error — install Node 22+ first.
npm ships with Node.js, so if you don't have Node you don't have npm either. Install Node first, then:
npm install -g nodal-agentsnpx nodal-agents up runs the same stack without a global install.
Recent npm versions gate install scripts behind allowScripts and print
npm warn allow-scripts …. Approve @embedded-postgres/<platform> and install
again: that package prepares the database binaries. When they are not usable,
up stops on that command instead of on a Postgres error. See
Getting Started.
Commands
| Command | What it does |
|---|---|
nodal-agents | Alias for up — starts the whole stack. |
nodal-agents up | Start Postgres, runner, and web; open the browser when healthy. |
nodal-agents down | Stop all running services gracefully. |
nodal-agents init | Interactive setup wizard (LLM, ports, bind mode). |
nodal-agents update | Update to the latest npm version and restart. |
nodal-agents logs [service] | Tail logs for web, runner, or postgres. |
nodal-agents checkpoints | List the snapshots taken before an agent wrote in a folder. |
nodal-agents checkpoints restore <sha> | Put a workspace back to a snapshot. |
nodal-agents mcp serve | Serve this install as an MCP server on stdio (your client launches it). |
nodal-agents reset | Delete all data and config at ~/.nodalai. |
nodal-agents up
Starts everything in order: cleans up what a previous run left behind, starts embedded Postgres, applies Drizzle migrations, seeds the default user/entity/agent (local-trust mode only), spawns the runner and web, waits for both /api/health checks to pass, then opens the dashboard in your browser.
If a configured port is occupied or reserved by the OS, up rotates to the next free port and saves the new value to ~/.nodalai/config.json. If no config exists yet, it runs the init wizard first.
| Flag | Effect |
|---|---|
--dev | Run web in next dev mode (HMR, no prebuild). Slower first load, far faster iteration. Off by default. |
--detach (-d) | Start the services and return the terminal. Stop them later with nodal-agents down. |
What "cleans up" means, and what it refuses to do
up only ever kills a process it can show is its own: a PID it recorded itself, or one descending from it. Anything else holding a port it wants is a stranger, and up stops rather than kill it:
Port conflict with a process that is not ours:
- :3000 is held by pid 56980, which Nodal-Agents did not startEarlier versions killed whatever held the port. That silently destroyed other people's work — usually their own dev server on :3000 — to free a port we merely wanted. Refusing is recoverable; killing is not.
The cleanup also works after an abrupt end. On Windows, Ctrl+C on a .cmd launcher makes the console destroy the whole process group at once, including the CLI's own shutdown routine, mid-run — so cleanup cannot live in that routine. The process tree is recorded while the services are healthy, and the next up sweeps whatever survived:
Cleaned up 4 process(es) left by the previous sessionThat message is the mechanism working. Each process is matched by creation time as well as PID, so a number Windows has since recycled is never mistaken for one of ours, and the sweep reaches background workers that hold no port at all — which no port scan can see.
Running nodal-agents with no subcommand is equivalent to nodal-agents up and also accepts --dev.
nodal-agents down
Stops the runner and web (PIDs read from ~/.nodalai/pids/), sweeps any background workers they spawned, and stops Postgres gracefully via pg_ctl stop -m fast. The graceful Postgres stop is important on Windows: a hard kill leaves a shared-memory block that makes the next up fail until reboot.
down verifies before it claims. pg_ctl stop returns when the signal is delivered, not when the postmaster is actually gone, so down waits for the process to disappear before reporting success. If it genuinely survives — which does happen, and poisons the next boot — you get the PID and the exact command to end it, rather than a cheerful message over a database that is still running.
nodal-agents init
The interactive setup wizard. Walks through: LLM provider preset → endpoint URL → model (auto-fetched when the endpoint is reachable) → API key (remote providers only) → network bind mode (loopback or LAN). Writes the result to ~/.nodalai/config.json. If the LLM endpoint isn't reachable, it warns but still saves the config.
| Flag | Effect |
|---|---|
--force | Overwrite an existing config without the confirmation prompt. |
--non-interactive | Write a default config without prompting (LAN bind, local-auth mode, no LLM preset). Used by container entrypoints / headless servers. |
Without --force, init asks before overwriting an existing config.
nodal-agents update
Checks the npm registry for a newer version. If already on latest, it prints a notice and exits. Otherwise it stops the stack (down), runs npm install -g nodal-agents@latest, and restarts the stack detached in the background. Your data in ~/.nodalai/pg-data/ is never touched.
| Flag | Effect |
|---|---|
--no-restart | Install the update but do not auto-restart. Run nodal-agents up yourself afterward. |
If the registry is unreachable, or npm isn't on PATH, or the install hits a permission error, the command fails loud with an actionable message and exits non-zero (no silent fallback). The manual fallback is:
npm install -g nodal-agents@latest && nodal-agents upnodal-agents logs [service]
Prints the existing contents of a log file then follows new output (tail -f style). Logs live in ~/.nodalai/logs/.
nodal-agents logs runner # tail + follow the runner log
nodal-agents logs web # the web log
nodal-agents logs postgres # the Postgres log
nodal-agents logs # print all three (no follow)The valid service names are web, runner, and postgres. With no argument it prints all three and exits; with a single service it follows that log until you press Ctrl+C. An unknown service name exits with an error.
The cluster's own log is a directory, not a file
nodal-agents logs reads ~/.nodalai/logs/<service>.log. Postgres also keeps
its own log, written by the cluster itself, in
~/.nodalai/logs/postgres/<data-dir-name>-<digest>/postgresql-YYYY-MM-DD.log —
one subdirectory per data directory, one file per day, rotated. That is where a
backend crash is written (server process (PID …) was terminated by signal …),
and it is the file to read when Postgres will not start. The subdirectory is
per data directory on purpose: two clusters pointed at one directory would
write the same day-named file and blank each other's history.
nodal-agents checkpoints
Before any tool that can modify a workspace runs, Nodal snapshots that workspace
into a shadow git store at ~/.nodalai/checkpoints/. The model never sees them,
cannot call them and cannot skip them. This command is the other half — a net
nobody can reach is not a net.
nodal-agents checkpoints # the current directory
nodal-agents checkpoints list /path/to/project # a given workspace
nodal-agents checkpoints restore 4f3a9c21 # sha, or its first characters
nodal-agents checkpoints restore 4f3a9c21 /path/to/projectlist is the default subcommand, so nodal-agents checkpoints and
nodal-agents checkpoints list are the same thing. Each row prints the short
sha, how long ago it was taken, and what was about to happen (the tool name and
the job it belonged to).
Restoring is deliberately a human gesture. Deciding that a run went wrong is a judgement, and an agent able to roll itself back could roll back the evidence of what it did. A restore snapshots the state it is about to replace first, so it is itself undoable.
Checkpoints cover files tracked by an ordinary git add: anything the
project's own .gitignore excludes (.env, local data, caches) is not
snapshotted and cannot be restored. Copying ignored secrets into a second,
unmanaged store would be worse than the gap.
NODALAI_CHECKPOINTS_ROOT moves the store. Both the runner and this command read
it from the same place, so overriding it on one side only is not possible.
Full picture, including what a refused write tells you: Proof.
nodal-agents mcp serve
Starts Nodal's MCP server on stdio, so an external MCP client — your terminal,
a coding agent — can hand work to your agents. You never run this by hand:
your MCP client launches it. Register it once: Settings → Safety → MCP
server gives the exact line for Claude Code, and the entry (or a button that
writes it) for Claude Desktop. The line names the Node and the script that
started this install, because nodal-agents is not on the PATH of an npx
install or a source checkout:
claude mcp add nodal -- "<path to node>" "<path to the nodal-agents CLI>" mcp serveRun it on the machine that hosts Nodal. The command resolves the database
from ~/.nodalai/config.json (the same resolution up uses) — there is no
DATABASE_URL to provide and no secret ends up in your client's config. It
fails loud if no config exists (nodal-agents init first), and it refuses to
serve while the MCP server switch in Settings is off.
The server exposes a single tool, run_task, which creates a tracked job for
the workspace's root agent (or a chosen agent by slug). Jobs run under the
agent's own approval rules and budgets, and can never use the configuration
tools. When the client disconnects, the process exits cleanly.
These runs have no conversation — nobody typed them — so they land in their own
MCP folder under Channels (/chat?folder=mcp), whose rows open the run page
directly. They also appear in Logs → Activity like any other run.
| Env variable | Effect |
|---|---|
NODAL_MCP_AGENT_ID | Serve on behalf of a specific agent id instead of resolving the workspace root. |
nodal-agents reset
Destructive. Stops any running processes, then deletes the entire ~/.nodalai/ directory — config, the Postgres database, and logs. This is irreversible: your agents, jobs, memory, and provider keys are all gone.
nodal-agents reset # prompts for confirmation (default: no)
nodal-agents reset --yes # skip the confirmation prompt| Flag | Effect |
|---|---|
--yes | Skip the confirmation prompt. |
After a reset, run nodal-agents init to configure from scratch.
Environment variables
These environment variables change CLI behavior:
| Variable | Effect |
|---|---|
NODALAI_NO_BROWSER=1 | Skip opening the browser after up. Useful on headless servers and CI runners with no desktop. |
NODALAI_PG_LOG=1 | Verbose embedded-Postgres logging during startup — surfaces the real Postgres error instead of a generic failure. Use when Postgres won't start. |
NODALAI_CHECKPOINTS_ROOT | Move the shadow checkpoint store away from ~/.nodalai/checkpoints/. Read by the runner and by nodal-agents checkpoints alike. |
NODALAI_WORKSPACES_ROOT | Move the per-workspace file tree away from ~/.nodalai/workspaces/. |
Ports and bind mode are not environment variables — they live in ~/.nodalai/config.json (defaults: web 3000, runner 3001, Postgres 25432). Change them via nodal-agents init or by editing the config file. up automatically rotates any occupied or OS-reserved port to a free neighbour and persists the new value.
Data directory
All persistent state lives under ~/.nodalai/:
~/.nodalai/
config.json # ports, bind mode, worker secret, LLM preset
pg-data/ # embedded Postgres data files (your agents, jobs, memory, keys)
pids/ # PID files for runner + web (used by `down`)
logs/ # web.log, runner.log — what `nodal-agents logs` tails
logs/postgres/ # the cluster's own log, one subdirectory per data directory
workspaces/ # one subtree per workspace: shared/, skills/<slug>/, channel media
checkpoints/ # the shadow git store: store/, indexes/, gitconfigTo wipe everything, use nodal-agents reset (preferred — it stops services first) rather than deleting the folder by hand.