Skip to content

CLI command reference ​

anet manages the Hub, accounts, Networks, nodes, and external channels. This page keeps the current commands and behavior most likely to cause mistakes; follow the linked guides for full configuration.

Install and get help ​

bash
# Stable
npm install -g @sleep2agi/agent-network@latest

# Preview
npm install -g @sleep2agi/agent-network@preview

anet --help
anet <command> --help
anet <command> <subcommand> --help   # e.g. anet node delete --help
anet -v

Node.js 22.13+ and Bun 1.2+ are required. --help (or -h) only prints help; it does not mint a token, start a service, or perform another business action. Every command and subcommand has its own usage text and exits 0.

Shortest startup path ​

bash
# Terminal 1
anet hub start

# Terminal 2
anet login --hub http://127.0.0.1:9200 --username admin

# After login
anet node create my-agent
anet node start my-agent

The initial password depends on the release channel: stable (@latest) uses a fixed default documented under --password in anet hub start --help; preview (@preview) prints a one-time random password on first start. Run anet passwd immediately after logging in.

See Getting started for installation and first-time setup.

Hub ​

CommandPurpose
anet hub startStart the Hub; listens on 127.0.0.1:9200 by default
anet hub stop [--port <p>]Stop the local Hub listening on a port
anet hub status [--port <p>]Show listener state, PID, and server version
anet hub dashboardStart the Dashboard on port 3000 by default
anet hub configInspect or change local Hub launch settings
anet hub admin reset-user --username <user>Reset a user's password and user tokens on the Hub host

Common start options:

OptionPurpose
--port <port>Hub port; default 9200
--host <host> / --ip <host>Bind address; defaults to loopback-only 127.0.0.1
--username <user>Set the bootstrap administrator username
--password <pass>Explicitly set the bootstrap administrator password
--dev-openDisable authentication; isolated development only
--version <v>commhub-server version to launch (an exact version, or latest / preview)
--channel <c>latest or preview; overrides the channel picked from anet's own version

Which Hub version starts: without --version, anet hub start first decides the channel by comparing the installed anet version with the @sleep2agi/agent-network dist-tags (equal to or behind latest means latest; a prerelease newer than latest means preview; the -preview.N suffix alone says nothing, since latest carries it too). It then takes that channel's @sleep2agi/commhub-server dist-tag and launches that exact version through bunx. If that tag is older than the minimum this anet supports, it uses the minimum instead. Start prints one line, Hub version: @sleep2agi/commhub-server@<version> [source: registry | cache | explicit | pinned minimum], naming the version and where it came from. When the npm registry can't be reached, it uses the newest version in the local bun cache that meets the minimum, with a warning. If the cache only has older versions, it does not start one silently: it prints a warning naming both versions and tells you to pass --version <cached version> if you want it anyway.

Do not use --dev-open or expose 0.0.0.0:9200 directly in production. See Production deployment.

Stopping the Hub (anet hub stop): it only stops a process that listens on that port and whose command line reads as commhub-server; it never matches processes by name. Finding the listener does not need lsof: on Linux it tries /proc/net/tcp(6) socket inodes against /proc/<pid>/fd, then ss -ltnp, lsof, netstat; on macOS lsof, then netstat -anv; on Windows netstat -ano. The first one that can run decides. When none can run, it uses ~/.anet/server/hub-<port>.pid.json, written by anet hub start (ignored if that PID has exited or now belongs to another program). The output lists every probe's result, the PID and command line it found, and which PID it actually stopped. It sends SIGTERM, and after 3 seconds SIGKILL to a survivor whose command line still reads as commhub-server.

CaseActionExit code
commhub-server found and stoppedstopped0
nothing listening on the port and /health does not answernothing0
the port belongs to another program, or its command line / owner cannot be readrefused; prints the PID and command line1
/health answers but its PID cannot be found on this machine (no probe works and no pid file)nothing; tells you to stop it from the process manager that started it (pm2, systemd, …)1
still running after SIGKILL, or /health still answers—1
--port is not a valid port—2

Accounts, Networks, and tokens ​

Accounts ​

CommandPurpose
anet registerCreate an account
anet loginLog in with username and password
anet login --token <token>Log in with an existing API token
anet logoutRevoke this login session on the Hub, then delete the locally saved login token (see Logging out below)
anet whoamiShow the current user and accessible Networks
anet passwdChange the password and rotate the current login token

Logging out (anet logout): with the saved token, calls GET /api/auth/sessions to get current_token_id, then DELETE /api/auth/sessions/<token_id> to revoke that login session (the same pair of endpoints the app's Settings → Account → Signed-in devices → sign out uses; see Signed-in devices API), then removes token / user / network_id / network_name from ~/.anet/config.json (hub is kept). Output shows only the token id, never the token.

CaseLocalServerExit code
Revoked, or the token was already invalid on the Hub (401)RemovedInvalid0
Hub unreachable, or Hub older than v0.9.0-preview.70 (/api/auth/sessions 404)RemovedStill valid; prints a ⚠ warning and how to revoke it0
The saved token is an explicit API token (anet login --token) or a node tokenRemovedNot revoked (it may be in use elsewhere); prints a ⚠ warning — revoke with anet token revoke <token-id>0
The local config file cannot be writtenNot removed—1

If the token is still valid on the server: sign that device out in the app under Settings → Account → Signed-in devices, or anet login again and run anet passwd (changing the password signs out every other login session). The COMMHUB_TOKEN that anet init project writes into each project's .anet/.env is a copy of the same token and stops working once it is revoked.

Networks ​

CommandPurpose
anet network lsList Networks the current user has joined
anet network create <name>Create a Network
anet network use <name>Switch the current Network
anet network infoInspect the current Network
anet network rename <old> <new>Rename a Network
anet network delete <name> --forceDelete a Network
anet network invite [options]Create an invite code
anet network join <code>Join with an invite code
anet network membersList current Network members

Invite options include --role admin|member|viewer, --uses <n>, and --expires <days>.

Tokens ​

CommandPurpose
anet token / anet token lsList the current user's API tokens
anet token create <name>Create an API token; plaintext is shown once
anet token revoke <token-id>Revoke a token

See Token model for token types, scopes, and compatibility behavior.

Nodes ​

Interactive menu: anet node ​

No commands to memorise: run anet node with no arguments in the node's directory. It lists every node in this directory — name, runtime (claude-agent-sdk / claude-code-cli / codex-* / grok-* / opencode-cli), state (running / stopped), model, plus a login column for codex nodes — then you pick a node and an action: start, stop, restart, attach (enter the TUI), show the recent log, change model, delete.

  • Before anything runs it prints the exact equivalent anet … command and runs it only after y; delete asks you to type the node name. What runs is the printed command; the menu does no lifecycle work itself.
  • A codex node goes to the codex actions (log in, continue, copy, …), the same as anet node codex — see the Codex node cheat sheet.
  • Not a terminal (piped, a script, an agent): it prints the node table, a short cheat sheet and the usual usage line, exit 0. No token appears in the output.
  • anet node --help / anet node help print help exactly as before.

Codex nodes without memorising commands: run anet node codex in the node's directory, pick a node and an action; it prints the equivalent command and asks before running it. Cheat sheet: Codex node cheat sheet.

CommandPurpose
anet node create <name>Create a node; opens the runtime wizard when omitted
anet node start <name>Start a node in the current terminal
anet node start <name> --tmuxStart or attach to a node in tmux
anet node stop <name>Stop the node and its same-name tmux session
anet node restart <name>Stop and start one node
anet node resume <name> [--session <id>]Request the saved or specified session; recovery depends on the runtime, including the V1 co-presence limitation below
anet node delete <name> --forceDelete the local node configuration and the node's row on the Hub
anet node rename <ref> <new>Rename a node already registered with the Hub
anet node clone <src> <new>Copy a node's settings into a node with a new identity (see "Clone a node" below)
anet node edit <ref> [--runtime <id>] [--model <id>]Change an existing node's runtime / model; takes effect on restart
anet node lsList local nodes and network state
anet node ls --all [--network <id|name>] [--json]List every node the Hub shows you in the current Network, grouped by machine (hostname); read-only, see below
anet node <start|stop|restart> <alias> --remoteStart / stop / restart a node on another machine: start / stop go through that machine's daemon, restart goes to the node itself (see "Managing nodes remotely" below)
anet node edit <alias> --model <id> --remoteSwitch the model of a node on another machine (no daemon needed)
anet info <name>Show node configuration, process, and recent tasks
anet logs <name> [--follow]Read or follow node logs
anet node migrate-token-to-envref <name>Replace plaintext secrets with envRef after writing a backup

OpenCode V1 co-presence recovery requires this node's valid history checkpoint. Old CLI 2.3.0-preview.171 / runtime 2.5.0-preview.137 cannot restore it. The new candidate pair CLI 2.3.0-preview.172 / runtime 2.5.0-preview.138 imports a private checkpoint into fresh launch data and verifies the original session/history; failed import does not silently create a new session. A legacy node without a checkpoint still creates a new session: capture its history from the running original bridge during a coordinated task/TUI quiet window before the first upgrade. Crash recovery covers only the last successful checkpoint; external file references are not migrated. Candidates are not downloadable until exact-main publication gates pass. See the repository runbook's V1 restart and upgrade boundary.

anet node ls --all ignores the current directory. It reads the Hub as you (your anet login) via /api/status + /api/host-supervisors and lists the nodes of the current Network (or the one named by --network <id|name>, matched by id, name, or a unique id prefix) grouped by machine: alias, runtime, status with last heartbeat, model, and whether that machine has a live daemon (anet daemon; only a machine with an online daemon can be managed remotely later).

text
Network: team (net_0123456) — 3 node(s) on 2 machine(s)

  host-alpha   daemon: alpha-daemon online — remote-manageable
    ALIAS     RUNTIME           STATUS   LAST SEEN  MODEL
    a-coder   claude-agent-sdk  idle     12s ago    claude-sonnet-4-5
    a-writer  codex-app-server  working  1m ago     gpt-5

  host-beta   daemon: none visible
    ALIAS     RUNTIME           STATUS   LAST SEEN  MODEL
    b-runner  grok-build-cli    offline  3d ago     -
  • Permissions are exactly what the Hub returns: a member with restricted agent access sees only the nodes granted to them. The Hub returns no daemons to such members, so the line reads daemon: none visible ("not visible to you", not "there is none").
  • Only your login token (token in ~/.anet/config.json) is used, never COMMHUB_TOKEN or a node token.
  • --json prints { network, daemons_readable, machines: [{ hostname, daemon, daemons, nodes }] }.
  • Without --all, anet node ls is unchanged.

Managing nodes remotely (--remote) ​

From any machine where you ran anet login, manage a node that lives on another machine (#562):

bash
anet node stop    coder-b --remote             # shows where it lives and whether the daemon is online, then asks y/N
anet node start   coder-b --remote --yes       # --yes skips the question (scripts)
anet node restart coder-b --remote --wait 90   # wait up to 90 s for the result (default 60, --wait 0 = don't wait)
anet node edit    coder-b --model m2 --remote  # switch model (the node restarts once to apply it)
text
Node coder-b — on machine-b, daemon daemon-b online; hub state: active / idle

Will run 将执行:
  anet node stop coder-b --remote --network net_271eb4d9e48c --yes
  (ask the hub to have daemon daemon-b on machine-b stop "coder-b" (its config is kept; start it again with --remote))
Run it? 执行? [y/N]: y
Dispatched (stop_node sr_c6dd58d6e565).
Waiting up to 60s for the hub to show the result …
✓ "coder-b" is stopped on machine-b
  • It uses the Hub's existing lifecycle tools (the same ones the app calls): start / stop are carried out by that machine's daemon (anet daemon); restart / model change are sent by the Hub to the node itself, which finishes its current turn, exits (code 75) and is respawned by its own anet node start wrapper — whether a daemon or a person started that wrapper.
  • start / stop require that the node's machine has an online daemon and that the node was created by that daemon (the app's "new node" on that machine). Otherwise it refuses before dispatching and says why, e.g. this machine can't be managed remotely: no daemon online on machine-c; a node started by hand with anet node start is started / stopped on its own machine.
  • restart / model change (#570) need no daemon, so they work for hand-started nodes too. They require that the node is running and reports config_update_capable (it runs under an anet node start wrapper, which respawns it on exit 75). Otherwise it refuses before dispatching: "<alias>" can't be restarted remotely: it doesn't support config updates (started without `anet node start`?); restart it on its machine — e.g. an agent-node run directly by pm2 / systemd, which nothing would bring back. The confirmation says how it will be done, e.g. ask "hand-b" on machine-b itself, through the hub, to restart (it's not managed by a daemon).
  • A restart / model change counts as done only when the Hub shows the node back (config revision advanced, a fresh report, not offline). On timeout it says what it saw last (went offline and never came back → look on that machine; still working → it restarts after its current turn).
  • Permissions are the Hub's: a viewer gets the Hub's permission_denied; a member with restricted agent access sees no daemons and cannot read the node's config, so start / stop / restart / model change are all refused before dispatching, with the reason — the node is not touched.
  • Only your login token (token in ~/.anet/config.json) is used, never COMMHUB_TOKEN; a saved node token is refused.
  • Not in a terminal (script / pipe) and no --yes: nothing is done, exit code 1. On timeout it says honestly that the Hub accepted the request but has not shown the result yet; check with anet node ls --all.
  • --network <id|name> picks another Network; local-only flags (--tmux, --copresence, …) are refused with --remote (exit code 2). stop --force stops a node that still has tasks in flight (the Hub audits it). Remote delete is out of scope for this step.

anet node delete <name> --force first runs the same stop as anet node stop (co-presence tmux sessions included: codex co-presence by marker + CODEX_HOME, others by exact session name, never a prefix), then deletes the local files (.anet/nodes/<id>/), then the node's row on the Hub, so it does not keep showing as "offline" in the app or dashboard. If the stop cannot be proven, it exits with an error and deletes nothing.

  • When the node is not in the current directory (a clone / codex fork made with --workdir), anet finds it through the source directory's .anet/child-workdirs.json and the codex login index, does not delete it for you, and prints cd <dir> && anet node delete <name>, exit code 1.

  • When several nodes match the name, it refuses and lists a delete-by-node_id command for each.

  • The Hub row is matched only by the node_id in the local config, never by name. A node on another machine that reuses the same name is not deleted; anet prints Left untouched: … belong to other node_id(s).

  • If the Hub is unreachable or refuses, the local files are still deleted, anet prints a warning with the exact retry command, and exits 1:

    bash
    anet node delete <node_id> --hub-only            # remove only the Hub row
    anet node delete <node_id> --hub-only --hub <url> # when the node used a different Hub than your login
  • With no Hub configured, only the local files are removed and the exit code is 0. Very old node configs have no node_id; their Hub row cannot be matched safely, so anet leaves it and tells you to remove it in the app or dashboard.

node delete does not automatically revoke the node's issued ntok_. To invalidate it completely, also run anet token revoke <token-id>.

anet node stop sends SIGTERM only and waits up to 8 seconds. It never escalates to SIGKILL — there is no --force on stop. If the node has not exited by then, anet prints pid <n> survived SIGTERM, keeps the pidfile, and exits 1: it reports the failure rather than claiming the node stopped, because a surviving process keeps heart-beating and would revert a rename. For the same reason node restart and node delete refuse to proceed. The only command that sends SIGKILL is anet node rename --force.

COMMHUB_TOKEN is not a CLI option, and there is no anet node start --token. Node authentication resolves in this order: node config, global config, then the legacy COMMHUB_TOKEN environment fallback. anet login --token logs in the CLI user; it does not inject a temporary node token into node start.

Common creation options:

OptionPurpose
--runtime <runtime>Select a runtime; use the current channel's wizard and Runtime comparison as the source of truth
--model <id>Override the runtime's default model
--resume <id>claude-code-cli: bind a specific Claude Code session
--resume-latestclaude-code-cli: bind the latest session in this project
--tools <list>Configure tools for runtimes that support this option

anet session ls lists Claude Code sessions for the current directory. Session semantics differ by runtime; do not use a Claude session ID as a Codex thread ID.

Clone a node ​

Full guide (clone vs fork, what is copied, the codex login, deleting the copy): Copying a node (clone / fork) and cleaning up.

To get "another node just like this one":

bash
anet node clone <source> <new-name>
# same thing
anet node create <new-name> --from <source>

The new node is registered with the Hub as its own node (the same endpoint anet node create uses) and gets its own node_id and ntok_. It is not started unless you pass --start. A table at the end lists every item as copied / regenerated / skipped.

CategoryWhat
Copiedruntime, model, tools, permission flags, system prompt, non-secret env, the commhub channel; with --workdir also the rules file (CLAUDE.md / AGENTS.md), skills directories and .mcp.json (env / header values blanked); for codex nodes codex-home/config.toml, AGENTS.md, skills/
Regeneratednode_id, alias, ntok_, the claude-code-cli session id, grok co-presence sockets, codexProjectDir
Not copiedthe token, every session / thread id, logs, pid / lock files, goals, inbox, channel bot credentials, codex auth.json, co-presence identity files, and the values of secret env entries (the key names are kept as an envRef pointing at the new node; fill them in before starting)
OptionPurpose
--workdir <dir>Put the clone in another project directory (created if missing). The path must be ASCII (a node name may be Chinese, a directory may not). Without it the clone shares the source's project directory, rules file and skills
--model <id>Use a different model
--startStart right after cloning (skipped, with a hint, while secrets are still unset)

Refused: an existing destination, a new name equal to the source's, a destination inside the source node's own directory, a non-ASCII --workdir, opencode-cli nodes (their runtime binding and login live outside the node config — create one with anet node create --runtime opencode-cli), and host daemons (role=host_supervisor).

A codex node's login is not copied. One login per node (refresh tokens are single-use; sharing knocks the others out): log the new node in before starting it, CODEX_HOME=<new-node-dir>/codex-home codex login --device-auth. Started without a login, anet stages this host's ~/.codex login — but refuses (exit 1) if another node on this host already uses it (--allow-shared-codex-login shares anyway, unsafely; see One login per node). For a registered account use anet node codex account install <new-name> --source codex-login:<profile-id>. To carry the codex conversation history too, use anet node codex fork (see Codex co-presence). A conversation you started with codex outside anet becomes a node with anet node codex adopt <new-name> --thread <id> (see Copying a node).

Never cp -r a node directory

.anet/nodes/<name>/config.json holds the node's node_id and ntok_. A verbatim copy is the same Hub identity as the source: both processes subscribe to the same inbox, every task runs twice and is answered twice, and the Dashboard cannot tell which one replied. Logs, sessions and the codex login are shared too. Always use anet node clone to copy a node.

Project-wide lifecycle ​

These commands scan .anet/nodes/ under the current directory:

CommandPurpose
anet project upStart every node that is not already running
anet project restartRestart every node
anet project downStop every node and report it offline

Shared options:

  • --stagger <seconds>: delay between nodes; default 3 seconds, 0 disables it.
  • --only a,b: operate only on listed aliases or node IDs.
  • --exclude x,y: skip listed aliases or node IDs.

See Batch agents for batch creation and cleanup.

Channels ​

CommandPurpose
anet channel add telegram <node> --bot-token <token> --allow <uid>Add Telegram
anet channel add feishu <node> ...Add Feishu; currently a preview feature
anet channel allow feishu <node> ...Change Feishu DM or group allowlists
anet channel ls [node]List channels
anet channel status [node]Show Telegram's effective config path and allowlist

Channel settings are not hot-reloaded; restart the node after changing them. anet channel add wechat has not shipped. See Channel integration.

Goals ​

CommandPurpose
anet goal list [node]List local goals
anet goal show <node> <id>Show details and progress records
anet goal wake-log <node> <id> [--tail N] [--json]Export the complete wake history
anet goal edit <node> <id> ...Change interval, text, or status
anet goal cancel <node> <id>Mark a goal cancelled
anet node loop <node> "<task>" [--every 5m]Create a recurring task on an online node and wait up to 15 seconds for confirmation

node loop submits /aloop through the Hub. goal edit/cancel modify .anet/nodes/<node>/goals.json directly. A running node does not hot-reload external file changes; restart it after edit/cancel. See Goals and Loops for native Dashboard /goal and /loop, ANet /aloop and /agoal, statuses, and self-management tools.

Diagnostics and maintenance ​

CommandPurpose
anet statusShow nodes and task summary for the current Network
anet tasks [status] [--limit <n>]Query tasks
anet doctorCheck configuration, Hub, dependencies, secrets, and channels
anet doctor --fixApply compatibility migrations and repair recoverable token problems; modifies configuration
`anet upgrade [--channel latestpreview] [--dry-run]`
anet config / anet config path / anet config jsonShow global config summary, path, or JSON (tokens masked)
anet initConfigure the Hub URL. When switching to a different Hub, the saved login session is first revoked on the old Hub (best effort — a failure only warns), then the old Hub's token and login info are removed from the config; the old token is never sent to the new Hub. Run anet login again afterwards. anet login --hub <another hub> does the same
anet init projectCreate CommHub MCP project files in the current directory
anet setupInstall dependencies for selected runtimes

See the Upgrade guide for upgrade details.

Reading the four numbers in anet status ​

  CommHub: http://127.0.0.1:9200
  Agents: 127 idle, 0 working, 1 needs attention, 143 offline
          └─ 18 offline for 1-3 days, 27 offline for more than 3 days
  SSE:    12 connected
  Tasks:  10 recent
NumberMeaning
idleFree, waiting for work
workingActively progressing a turn (includes waiting_input — the turn is alive, just waiting on a human)
needs attentionSomeone should look: blocked / error, plus any status this version does not recognise
offlineHeartbeat expired, or stopped cleanly

The line under offline: how long have they been gone ​

When there are offline nodes, a breakdown line is printed. "Just stopped" and "gone for three days and nobody noticed" used to be the same number. Measured once over 84 nodes: of 45 offline, 27 had been gone more than 3 days, 18 for 1-3 days, and none within the last 6 hours — "there is no live outage right now" and "45 nodes are down" are two completely different conclusions, and only the second one was on screen.

Nodes with no usable timestamp go into their own "no timestamp (we do not know how long)" bucket and are not folded into the freshest one — "I do not know how long it has been gone" and "it just went down" are different things.

🔴 blocked has no matching duration, and should not have one. The roster has no "when did it become blocked" field; updated_at keeps being refreshed by the heartbeat, so using it as a blocked-duration prints "4 minutes ago" for a node stuck for hours — worse than showing nothing. See Is this node still alive.

The four add up to the node total. The needs attention slot is hidden when it is 0.

🔴 blocked / error are not counted as working. They mean "stuck", not "making progress" — folding them into working would let an operator read "N working" as "all fine". When needs attention is above 0, the nodes behind it are listed with their self-reported status and the task they were on.

⚠️ These statuses are self-reported and carry no liveness proof. A node whose process died but has not yet been swept can still show as idle here. To confirm it is still there, send it a task — anet status answers "what it last said about itself", not "whether it is still running".

Node permissions: anet node perm ​

In development, not yet released. Show or change a node's permissions on the Hub: the mode, the peer allowlist (who it may send to) and the inbox allowlist (who may send to it).

bash
anet node perm <node> [--json]
anet node perm <node> [--mode normal|readonly|restricted]
                      [--peers <a,b,…>|none] [--peers-add <x>] [--peers-remove <x>]
                      [--inbox <a,user:bob,…>|none] [--inbox-add <x>] [--inbox-remove <x>]
                      [--hub <url>] [--json]
  • <node> is a local node name, a node_id or a Hub alias. Without a change flag it only prints the current settings (plus a read-only view of the tool / rest allowlists).
  • --inbox entries: <alias> / <node_id> is an agent, user:<username> / user:<id> is a person, user:* is any person, and !strict requires the node's owner and network owner/admin to be listed too. See inbox allowlist.
  • none clears that allowlist (no restriction). Removing the last entry with --*-remove is refused, because an empty list means fully open; write none explicitly.
  • Changes need the user token from anet login, as the node's owner, a network owner/admin or a hub admin. Against an old Hub that does not report inbox_allowlist, the display says so and --inbox* is refused rather than pretending to succeed.
  • --json prints {ok, state, changed} ({ok: false, error, …} on failure). A usage error exits 2; a Hub refusal or unreachable Hub exits 1.
bash
anet node perm reviewer --inbox planner,user:alice
anet node perm reviewer --inbox-add user:bob
anet node perm reviewer --peers none --mode normal --json

Node and daemon environment secrets (preview) ​

Node env: anet node secret ​

bash
anet node secret list  <node> [--json]
anet node secret set   <node> <KEY> [--from-env VAR]
anet node secret unset <node> <KEY>

Writes nodes/<node>/secrets.env (0600, atomic replace, symlinks refused) and lists key names only. Values come from --from-env or stdin; a value in argv is a usage error.

Daemon-scoped: anet daemon secret ​

bash
anet daemon secret list  <daemon> [--node <alias>] [--json]
anet daemon secret set   <daemon> <KEY> --from-env VAR (--node <alias> | --all)
anet daemon secret unset <daemon> <KEY> (--node <alias> | --all)

Manages scoped-secrets.json in the daemon directory (0600, revision CAS, the target must already be bound). set/unset require --node or --all: an unscoped write becomes the default for every child, which is a permission decision, never an accident. Receipts and list show key names and their source (daemon/node) only.

Both commands are entries into the existing daemon_provider_snapshot secret_* actions — no second read/write path. Values load on the node's next full start; restarting the bridge alone does not update the native TUI/server. A name on the Hub token page is not injected into this process. Precedence: Environment precedence at startup.

Adopt manually started nodes (preview source, board #626) ​

On Linux, configure the daemon's adopt_roots with absolute allowed workdirs; the empty default refuses adoption. Load configuration through the existing operator-managed daemon startup procedure. A node workdir must not be HOME or a system directory. Use your human anet login account from the manual node's workdir:

bash
anet daemon adopt demo --daemon <daemon-node-id>
anet daemon adopt demo --daemon <daemon-node-id> --yes
anet daemon adopt --all --daemon <daemon-node-id> --yes
anet daemon unadopt demo --yes

Without --yes, commands only show a plan. --all enumerates this workdir's .anet/nodes, not the whole machine. An accepted request is pending, not a successful adoption. The daemon checks UID, paths, configuration identity, /proc, HOME and reproducible environment before atomically recording and acknowledging it. Adoption and revocation never restart or signal the node. Only bare/default-socket tmux is supported; co-presence or unreproducible environments are refused (key names, never values). Run anet daemon adopted from the daemon workdir to list local records with active Hub authority.

adopt_path_writable_by_others: the workdir, .anet, .anet/nodes, the node directory or config.json is group/other-writable. Fix it with chmod go-w on the reported path. For a shared team workspace (e.g. 2775) that must stay group-writable, the workdir itself only may instead carry the sticky bit: a daemon-owned workdir with chmod +t <workdir> (e.g. drwxrwsr-t) is accepted, because other users then cannot rename or delete the daemon-owned .anet inside it. .anet and everything below it must still be go-w.

This increment binds nodes only; remote stop/start for adopted nodes requires board #627. Until then it fails explicitly instead of entering legacy child-process cleanup. Registry: <daemon-workdir>/.anet/child-workdirs.json. Original node configurations and secrets stay in place. Disaster recovery requires those private files and the Hub data backup; cloning source does not restore bindings, accounts or secrets. Revoking a binding does not delete the workdir.

Preview-only features ​

The following commands exist in the current preview and must not be presented as stable features:

CommandPurpose
anet daemon up [name]Create and start a host_supervisor (version requirements: which versions have anet daemon)
anet daemon init <name> / start <name> / restart <name> / listManage local daemons (same version requirements; for restart see the daemon page)
anet node start <name> --copresenceStart Codex app-server, bridge, and shared TUI
anet opencode ...Manage the preview OpenCode integration

🔴 There is no daemon-level stop / delete / status. A daemon is just an agent-node with role=host_supervisor, so use the node-level commands: anet node stop <name> to stop it, anet node delete <name> to remove it, anet node ls to see whether it is running (anet daemon list only lists locally configured daemons and carries no liveness). anet daemon restart calls that same stop internally.

Two counter-intuitive things about daemons

1. On an existing daemon, anet daemon init <name> changes nothing. It prints ✓ "<name>" already a host_supervisor daemon and returns — a green check mark with not one byte written. Changing the config requires --force (keeps node_id, but re-mints the token, and the daemon must be restarted for it to take effect).

2. The runtime list in the config is a write-time snapshot; it does not self-heal. Runtimes added later are not back-filled into existing daemon configs, which shows up as that machine offering fewer runtimes in the client's server picker. anet daemon list now prints which ones are missing and the command to back-fill.

--copresence only applies to runtime=codex-app-server. Its default sandbox is read-only. Full filesystem and network access requires --dangerously-allow-full-access; a TTY requires typing yes, and a non-TTY caller must also pass --yes-danger-full-access.

Resume a co-presence node with anet node start <name> --copresence; do not replace it with a normal node start.

opencode-cli is currently an agent-node-managed task runtime, not an attachable shared OpenCode TUI. The shared Grok TUI runtime grok-build-cli is also absent from the current preview packages; the available grok-build-acp runtime does not support attach.

Other commands ​

CommandPurpose
anet import [alias]Import recoverable local node configuration from the Hub
anet run --alias <name>Start a minimal SSE echo agent that does not invoke an LLM
anet demo [name]Run experimental demos; not a production orchestration path
anet batch <verb>Manage groups created by anet create --batch
anet license / anet activate <key>Legacy license compatibility; Apache-2.0 users normally do not need these

Legacy aliases such as anet create and anet start remain for compatibility. New documentation uses anet node ... consistently.

Exit codes ​

CodeMeaning
0Success, or only help was printed (for example anet node or anet network with no subcommand, or any command / subcommand with --help or -h)
1Failure: not initialized / not logged in / session expired, Hub unreachable or returned an error, object not found, local write failed, refused to act
2Usage error: missing required argument, unknown subcommand, invalid value

Scripts and CI can rely on anet … || exit 1. Before #515 the following printed an error but exited 0; they now exit non-zero:

CommandCaseBefore → now
anet whoaminot logged in / session expired / Hub unreachable0 → 1
anet network <ls|use|info|create|delete|rename|invite|join|members>not initialized / not logged in, Hub error, Network not found, unreachable0 → 1
anet network create|use|delete|rename|join (missing argument), anet network <unknown>usage error0 → 2
anet token ls|create|revokenot logged in, Hub error, unreachable0 → 1
anet token revoke (no token id)usage error0 → 2
anet passwdnot logged in, passwords differ, Hub refused, unreachable0 → 1
anet activatenot initialized, activation failed, unreachable / no license key0 → 1 / 0 → 2
anet status, anet tasksno Hub configured; anet tasks unreachable0 → 1
anet hub startHub did not come up within 15 s (including Bun missing)0 → 1
anet hub stopsee Stopping the Hub above0 → 1 / 2
anet hub admin reset-userno --username / DB not found, reset failed0 → 2 / 0 → 1
anet hub <unknown>, anet node <unknown>, anet session <unknown>, anet batch <unknown verb>usage error0 → 2
anet node resume (no node), anet logs (no node), anet batch <verb> (no prefix)usage error0 → 2
anet project down (a node failed to stop), anet node delete (node process refused to exit)already set exit code 1, but the CLI's final process.exit(0) overrode it0 → 1
anet upgradea package failed to upgrade or its registry lookup failed0 → 1
anet node delete --forcelocal files deleted, but the Hub row was not removed (unreachable / refused); prints a --hub-only retry commandnew: 1
anet create --batch, anet batch cleanupno Hub, invalid preset/option, auto-login failed, no node created0 → 1 / 2
anet demo …no Hub / token / key, creating the Network or nodes failed0 → 1

Some older usage-error paths still exit 1 (also non-zero, so scripts still catch them); they were not changed just to make them 2. anet hub status is a status query and still exits 0 when the Hub is not running.

Errors and secrets ​

  • On an error anet prints one sentence and the next command to run, without a stack trace. To see the stack: ANET_DEBUG=1 anet ….
  • A fatal error whose message is an error code still prints [anet] FATAL: Error: <CODE>, so scripts can pull the code from logs.
  • anet never prints a full secret: anet config, anet config json, anet node start and anet doctor show tokens as utok_…ab12 (prefix…last 4). Tokens, Bearer headers and token= URL parameters inside error messages are masked the same way.
  • anet node create --env KEY=… and anet node migrate-token-to-envref write the secret to .anet/nodes/<node>/.env (mode 600, gitignored) and print only the masked value plus an export command that reads it from that file.
  • Exceptions: anet token create and anet hub admin reset-user exist to hand out a new credential and show it once.

Configuration locations and environment variables ​

PathContents
~/.anet/config.jsonCurrent Hub, user token, and Network
.anet/nodes/<node>/config.jsonNode configuration
~/.commhub/commhub.dbDefault Hub SQLite database
~/.anet/server/admin-utok.jsonLocal administrator recovery token on the Hub host

Common environment variables:

VariablePurpose
COMMHUB_URLHub URL
COMMHUB_ALIASNode alias
COMMHUB_TOKENAuthentication token; a token in node configuration takes precedence
COMMHUB_AUTH_TOKENLegacy Hub master-token compatibility path; new deployments use user and node tokens
ANET_CODEX_RESUME_TIMEOUT_MSCodex session-resume deadline for anet node start --copresence (whole milliseconds, max 900000); when unset it starts at 300 s and adds 120 s per started GiB of rollout, capped at 15 minutes; timeout errors report elapsed time and rollout size

Codex co-presence startup creates a private recovery point after quiescing the old processes. A failed backup is fail-closed and its partial directory is removed. Only an operator who explicitly accepts losing safe rollback may pass --skip-recovery-backup; the command prints a prominent warning and does not present an older recovery point as the backup for this launch. | ANTHROPIC_BASE_URL | Anthropic-compatible model endpoint | | ANTHROPIC_AUTH_TOKEN | Credential for third-party Anthropic-compatible endpoints | | ANTHROPIC_API_KEY | Credential for Anthropic's official endpoint |

Use envRef for secrets; do not commit tokens or model keys in configuration. See Security model.

See also ​

Powered by Sleep2AGI

Download the app