Skip to content

Starting a Node ​

A "node" is a machine running zqagent: it has a local working directory, model configuration, and session data on that machine. It can be used locally only, or connected to the User Center for remote management and viewing.

This page explains how to start, keep running, and connect a node.

Three Run Forms ​

FormStartup methodBest for
Interactive CLIzqagentChatting and working on the local machine; most commonly used
Long-running service nodezqagent serveRunning on a server/always-on machine for remote management
Desktop appInstall the desktop appWhen you want a GUI; shares the same boot-start node with the long-running service

The desktop app and the long-running service node are the same node service (same data directory and port), only the UI differs: installing the desktop app and then running install.sh will not create a second node. The interactive CLI uses independent data in the current directory, and the three can be combined as needed.

Interactive CLI ​

bash
zqagent

Once in the interactive interface, enter natural-language tasks; configure the model with /connect (see Quick Start). For all commands, see CLI Usage.

The interactive CLI is a node that is "usable locally" but does not listen on a port and cannot be managed remotely. For remote management, see zqagent serve below.

Start a Long-running Node ​

zqagent serve starts the service headlessly, listens on an HTTP port, and runs in the background.

bash
zqagent serve

After startup, the logs print the listening address, data directory, node ID, and demo tenant information (on first startup, a demo tenant demo is created automatically; its apiKey is randomly generated and printed in the startup logs, and can be used directly for local debugging).

Options ​

OptionDescriptionDefault
--port <n>Listening port; if occupied, automatically switches to the next available port (the actual port is in the data directory server-port, and the logs also state it)8787 (or environment variable PORT)
--host <addr>Listening address; set to 0.0.0.0 to allow external access127.0.0.1 (or HOST)
--data-dir <dir>Data directory (projects, sessions, ledger, credentials)./.zqagent_data (or DATA_DIR)
--hub <url>User Center URL; use with --enroll-code to connect on startup—
--enroll-code <code>One-time enrollment code—
--label <name>Node display nameHostname

By default it listens only on 127.0.0.1, i.e. accessible only from the local machine, which is the safest. If LAN/public access is really needed, set --host 0.0.0.0 and pay attention to firewall and access control.

Persistent Background Operation ​

Running in the foreground exits when the terminal closes; for long-term operation, install it as a system service (start on boot + automatic restart on crash).

Windows: There is no systemd / launchd. Use Task Scheduler to create a "Run at logon" task; set the program to the absolute path of zqagent.exe and the arguments to serve --port 8787 --data-dir "%APPDATA%\zqagent"; alternatively, put a shortcut to zqagent.exe serve ... in the Startup folder (Win+R → shell:startup). Stop/restart via Task Scheduler or Task Manager.

Recommended: one command (automatically downloads the latest single file, installs the service, and enables automatic updates; see Single-file Installation):

bash
curl -fsSL https://agent.cd-zq.com/install.sh | sudo sh

After installation:

bash
sudo sh -s -- status        # 运行状态
sudo sh -s -- logs          # 跟踪日志
sudo sh -s -- restart       # 重启
sudo sh -s -- rollback      # 回滚到上次自动更新前
sudo sh -s -- uninstall     # 停止并移除

(sudo sh -s -- <子命令> is equivalent to curl ... | sudo sh -s -- <子命令>; if the script has already been downloaded, you can also run sudo sh install.sh status directly.)

Install from repository source (on a development machine / when already installed with npm). macOS uses launchd; Linux uses systemd:

bash
# 在仓库 deploy/service 目录下执行
bash install.sh install     # 安装并启动(开机自启)
bash install.sh status      # 运行状态
bash install.sh logs        # 跟踪日志
bash install.sh restart     # 重启
bash install.sh uninstall   # 停止并移除

Default data directories: macOS ~/Library/Application Support/zqagent, Linux ~/.zqagent_data; logs: macOS ~/Library/Logs/zqagent/node.log, Linux ~/.zqagent_data/node.log. (Early Linux versions used /var/lib/zqagent; for existing installations, the script follows the path in the original unit and does not migrate.) You can override with PORT / HOST / DATA_DIR / LOG / SERVICE_NAME, for example:

bash
sudo PORT=9000 DATA_DIR=/data/zqagent bash install.sh install

The service starts with an "absolute-path node + CLI entry point" and does not depend on the PATH from nvm/volta; extra environment variables on Linux (LLM key, HUB_URL, SECRET_KEY, etc.) can be written to /etc/zqagent.env. Shutdown uses SIGTERM, and serve persists the ledger before exiting (default grace period 3s, adjustable with ZQAGENT_SHUTDOWN_GRACE_MS).

The script checks whether the current CLI supports serve: older versions (such as 0.1.33) do not have this subcommand. Run npm i -g @zqagent/core@latest first; otherwise the process silently enters an interactive session.

Hand-written systemd unit (when not using the script)

ini
# /etc/systemd/system/zqagent.service
[Unit]
Description=zqagent node
After=network-online.target

[Service]
# 把 <用户名> 换成实际运行节点的用户;数据放在该用户家目录,权限天然归它所有
User=<用户名>
ExecStart=/usr/local/bin/zqagent serve --port 8787 --data-dir %h/.zqagent_data
Restart=always
RestartSec=3
KillSignal=SIGTERM
TimeoutStopSec=15

[Install]
WantedBy=multi-user.target
bash
sudo systemctl daemon-reload
sudo systemctl enable --now zqagent
journalctl -u zqagent -f      # 查看日志

pm2 (alternative)

Note that you cannot put the entire command into a single string (it will be treated as a bash -c script, arguments will be lost, and it will fall back to an interactive session):

bash
pm2 start "$(command -v zqagent)" --name zqagent -- serve --port 8787 --data-dir "$HOME/.zqagent_data"
pm2 save && pm2 startup   # 开机自启

nohup (temporary debugging)

bash
nohup zqagent serve --port 8787 --data-dir "$HOME/.zqagent_data" \
  > "$HOME/.zqagent_data/node.log" 2>&1 &

Automatic Updates ​

After installing as a single file (SEA) and handing it over to a system service, the node keeps itself up to date. On a brand-new machine, it is recommended to use the one-line command from Single-file Installation, which installs both the binary and the service:

  1. Checks once 5 minutes after startup, then once every 6 hours (±15% jitter)
  2. If a new version exists, silently downloads it and verifies SHA-256, then runs a --version self-check
  3. Waits until continuously idle for 60 seconds (no sessions executing and no sessions waiting for approval/answer) before replacing itself and exiting
  4. systemd / launchd brings up the new version, and the version is automatically updated in the console

Failures do not affect node operation: download/verification failures are only logged, and after 3 consecutive failures for the same version, it is skipped.

Prerequisites ​

Automatic updates take effect only when all three conditions are met at the same time (all are required):

  • Single-file form (started from the SEA artifact, not node dist/cli/index.js)
  • Managed by systemd / launchd (installed by install.sh)
  • Signature verification public key ZQAGENT_UPDATE_PUBKEY is configured

Nodes installed with npm are not automatically replaced; upgrade with npm i -g @zqagent/core@latest.

macOS: Managed detection is the same as on Linux. It takes effect directly inside the service process; when manually running zqagent update --status/--check/--now in a terminal, it also automatically detects the managed state, public key, and channel through the launchd job (pid / program) and EnvironmentVariables in the plist, without needing to source any environment variables first. To uninstall, use sudo sh -s -- uninstall.

Windows: There is no systemd / launchd, and single-file nodes are not automatically updated (zqagent update --status will show "Service managed: unmanaged (foreground/nohup)"). To upgrade, stop the serve process, overwrite the old file with the newly downloaded exe, and restart; in an unmanaged environment, zqagent update --now refuses to execute directly.

The installation script fetches the public key by default, so one command completes installation + enables automatic updates:

bash
curl -fsSL https://agent.cd-zq.com/install.sh | sudo sh

To override the automatically fetched key with an out-of-band saved public key, add --pubkey "..." (an explicit public key takes precedence and will not be overwritten by online fetching).

Switches and Configuration ​

Environment variableDefaultDescription
ZQAGENT_AUTO_UPDATE1Set to 0 to completely disable automatic updates
ZQAGENT_UPDATE_PUBKEYAutomatically written during installationSignature verification public key (PEM or base64 SPKI DER); if missing, no automatic installation is performed
ZQAGENT_UPDATE_CHANNELstableManifest channel (stable / beta)
ZQAGENT_UPDATE_URLhttps://agent.cd-zq.com/downloads/zqagent/<channel>.jsonManifest URL; can be overridden on an intranet
ZQAGENT_UPDATE_INTERVAL_MS21600000 (6h)Check interval
ZQAGENT_UPDATE_MIN_UPTIME_MS600000 (10min)How long after startup before updates are allowed
ZQAGENT_UPDATE_IDLE_MS60000How long it must be continuously idle before installation
ZQAGENT_UPDATE_INSECURE—Set to 1 to allow an http manifest (intranet self-testing only)

Check for Updates ​

bash
zqagent update --check       # 只查有没有新版(不下载,秒回)
zqagent update --status      # 当前版本、通道、上次检查时间、暂存/失败原因

Example output:

当前版本  v0.2.1
运行形态  单文件(SEA)
服务托管  systemd
更新通道  stable
清单地址  https://agent.cd-zq.com/downloads/zqagent/stable.json
自动更新  已启用

--check prints "Already the latest version (vX)" or "New version vY found (current vX)".

Manual Intervention ​

bash
zqagent update --now         # 立即下载安装(跳过空闲等待,需重启服务)
zqagent update --rollback    # 回到上次更新前的版本

Service-side rollback (automatic restart):

bash
sudo sh -s -- rollback    # 单文件安装(从下载目录取脚本时用 sudo sh install.sh rollback)
sudo bash install.sh rollback        # 仓库内安装

After rollback, that version is skipped: automatic updates will not immediately install it again until a newer version is released; if you really need to force installation, use zqagent update --now.

Release Side ​

One command does it all: build the SEA single file → generate and sign the manifest → sync to the download directory. npm is not involved at all, and the node side does not need Node either (the binary bundles the runtime).

bash
# 首次:生成签名密钥对(私钥只放发布机或 CI secret,公钥分发给节点)
node packages/core/scripts/gen_update_keys.mjs --out .update-keys

# 每次发版(参数全有默认值:版本取 packages/core/package.json,私钥取 .update-keys/update-private.pem)
bash scripts/release_node.sh

Common options:

OptionDefaultDescription
--version XVersion in packages/core/package.jsonBare version number; the script verifies both match
--key FILE.update-keys/update-private.pemEd25519 private key used for manifest signing
--pubkey-file FILEupdate-public.b64 in the same directory as the private keyNode signature verification public key; verifies before release that it pairs with the private key, and syncs it to the download directory
--host USER@HOSTEmpty = local releaseOmit when the release machine is the site server
--dest DIR/var/www/zqagent-downloads/zqagentDirectory that nginx /downloads/ actually points to
--notes TEXT本次更新内容: 例行更新Written into the manifest and visible in client logs
--skip-build—Reuse existing artifacts from packages/core/release/cli
--dry-run—Only prints the commands to be executed

Three typical usages:

bash
# 1) 发布机就是站点服务器:直接写本机下载目录
bash scripts/release_node.sh --skip-build

# 2) 从别的机器发布:ssh + rsync 推到站点服务器
bash scripts/release_node.sh --host deploy@your-server --skip-build

# 3) 先看清楚会做什么
bash scripts/release_node.sh --dry-run

Cross-platform artifacts can only be built on the corresponding OS/architecture. For an all-platform release, build once in each CI matrix entry, then aggregate and publish with --skip-build --host ....

Directory structure after publishing:

bash
/downloads/zqagent/
├── install.sh           # 一键安装脚本
├── install-node.sh      # 旧地址兼容副本(内容同 install.sh)
├── update-public.b64    # 验签公钥(安装时用 --pubkey 传入)
├── stable.json          # 更新清单(节点读取)
├── stable.json.sig      # 清单签名
├── stable.txt           # 纯文本清单(安装脚本用)
└── v0.2.2/
    └── zqagent-<platform>-<arch>

The node downloads the binary only after signature verification passes, and then performs another check using the SHA-256 in the manifest. Self-check after publishing:

bash
curl -s https://agent.cd-zq.com/downloads/zqagent/stable.txt
curl -s https://agent.cd-zq.com/downloads/zqagent/stable.json | head -20

For the complete release and rollback process, see docs/release-runbook.md in the repository.

Connect to the User Center (Optional) ​

After connecting, you can view node online status, projects, and events in the User Center. There are two methods; choose either one:

  • Hub account (recommended): directly use the email/phone number and password registered in the User Center;
  • One-time enrollment code: log in to the User Center → create Node Management → generate an enrollment code (valid for 15 minutes).

Already Running Node: Hot Connect (No Restart) ​

bash
# 账号方式:邮箱或手机号 + 密码
zqagent link --hub https://hub.cd-zq.com --username you@example.com --password '<密码>'
zqagent link --hub https://hub.cd-zq.com --username 13800000000 --password '<密码>'

# 手机号 + 短信验证码
zqagent link --hub https://hub.cd-zq.com --username 13800000000 --sms-code <验证码>

# 或一次性接入码
zqagent link --hub https://hub.cd-zq.com --code <接入码>

Windows single file:

bat
zqagent.exe link --hub https://hub.cd-zq.com --username you@example.com --password <密码>
zqagent.exe link --hub https://hub.cd-zq.com --code <接入码>

When no credential arguments are provided, zqagent link enters an interactive wizard that asks in turn for the Hub URL, login method, and account/password (password input is hidden). The password can also be provided via the environment variable ZQAGENT_HUB_PASSWORD to avoid leaving it in command history.

The default data directory on Windows is %APPDATA%\zqagent; link automatically discovers it from runtime\service.json registered by the desktop app. You only need to add --data-dir when you manually configure a scheduled task and the data directory is not in the default location.

link first attempts to hot-connect to the local running serve. It traces back the process command line via /proc on Linux, ps on macOS, and PowerShell's Win32_Process on Windows, so it can be discovered even if serve uses a custom --data-dir / --port; if PowerShell is unavailable, it falls back to "write fleet.json, effective on next startup".

When multiple instances are found, link prefers the service managed by systemd / launchd (starts on boot and remains after restart), followed by a temporarily launched serve from the command line; .zqagent_data in the current directory is only used as a last resort, to prevent credentials from being intercepted by a temporary instance, causing the command to report success while the managed service remains offline.

Node Name ​

For nodes connected without --label, the hostname is used as the display name by default; what you see in the Hub is the machine name, not the nodeId. You can change it later in two places, and both modify the same record:

  • Hub User Center → Node Management → Node "Rename";
  • Execute on the node machine (takes effect live when a local service is running, no restart needed):
bash
zqagent rename ten004-dev

If the node has connected to the Hub, this command uses the node's own credentials to sync the Hub record, and it takes effect after refreshing the console; if not connected, it only writes the local configuration, which takes effect the next time serve starts.

When the service is not currently running, link also parses the plist / systemd unit to find its registered data directory and writes the credentials there (the service automatically connects on next startup). Therefore, whether the node was installed by the desktop app or by install.sh, users do not need to manually pass --data-dir.

Generally, you do not need to manually write --data-dir: the command above automatically discovers the local node. You only need to add it when prompted "No local node service found", and you must use a directory writable by the current user (the default for each platform's installation script):

PlatformDefault data directory
macOS$HOME/Library/Application Support/zqagent
Linux~/.zqagent_data (owned by the current user; early versions used /var/lib/zqagent)
Windows%APPDATA%\zqagent

Do not copy --data-dir /var/lib/zqagent from old tutorials: new installations default to ~/.zqagent_data, and manually specifying a system directory will instead hit EACCES: permission denied. Just omit this option.

Connect on Startup ​

Include the Hub URL and account (or enrollment code) when running serve:

bash
# 账号方式(适合 systemd / 容器等非交互环境)
zqagent serve --hub https://hub.cd-zq.com --username you@example.com --password '<密码>'

# 接入码方式
zqagent serve --hub https://hub.cd-zq.com --enroll-code <接入码>

You can also use environment variables (suitable for containers / scripts):

bash
HUB_URL=https://hub.cd-zq.com ZQAGENT_HUB_USERNAME=you@example.com \
  ZQAGENT_HUB_PASSWORD='<密码>' zqagent serve

HUB_URL=https://hub.cd-zq.com HUB_ENROLL_CODE=<接入码> zqagent serve

Connect from the Interactive CLI ​

Directly in the zqagent interface:

/link                                # 进入账号接入向导
/link <接入码> [Hub地址]

Docker ​

bash
docker run -d -p 8787:8787 -v zqagent-data:/data \
  -e HUB_URL=https://hub.cd-zq.com -e HUB_ENROLL_CODE=<接入码> \
  ghcr.io/<owner>/zqagent-server

Credentials and Network ​

  • After a successful connection, credentials are written to {DATA_DIR}/fleet.json; subsequent restarts do not require the account or enrollment code again;
  • The node only needs an outbound connection to the gateway (wss://, usually over 443); no public inbound access and no open ports are required;
  • To rebind or unbind: operate from the node details page in the User Center, or delete fleet.json and reconnect.

Data Directory ​

All long-term node data is in {DATA_DIR} (default ./.zqagent_data, following the project):

PathContent
{DATA_DIR}/admin.jsonAdministrator key
{DATA_DIR}/fleet.jsonConnection credentials (0600)
{DATA_DIR}/usage/Usage ledger, summaries, and price table
Sessions and projectsSaved by each project according to the working directory

Model API keys are encrypted and stored separately in the user configuration directory (macOS / Linux ~/.config/zqagent/, Windows %APPDATA%\zqagent\), separate from the data directory.

Verify That the Node Is Online ​

  1. Open the User Center in a browser and go to the corresponding Node Management;
  2. The node appears in the node list with the status Online;
  3. Click the node to enter the Node Console, where you can view projects, sessions, usage, and events.

If the node is offline: confirm that the serve process is alive and that the machine can access the gateway address outbound (FLEET_GATEWAY_URL).

Next Steps ​