Keyboard shortcuts

Press or to navigate between chapters

Press ? to show this help

Press Esc to hide this help

FreeBSD

ZeroClaw 可在 FreeBSD 上原生运行(已在 FreeBSD 15.0-RELEASE,amd64 上测试)。与 Linux/macOS/Windows 路径有两点不同:

  1. 没有预构建的二进制文件,也不支持 install.sh FreeBSD 不是 bootstrap 安装程序的目标平台,因此你需要使用系统的 Rust 工具链从源码进行构建。
  2. 没有 zeroclaw service 后端。 zeroclaw service install 命令支持 systemd、OpenRC、launchd 和 Windows Task Scheduler,但不支持 FreeBSD 的 rc.d。你需要自己安装一个小的 rc.d 脚本。本页面为你提供了一个完整且经过测试的脚本。

其他所有内容,包括配置、提供程序、通道、守护进程和网关,都与任何其他平台完全相同。

何时使用 FreeBSD。 FreeBSD 部署常见于网络设备、嵌入式系统以及基于 jail 的托管环境中,运维人员看重基础系统的稳定性、ZFS + jail 原语,或出于策略/许可方面的原因需要使用 FreeBSD。由于没有预构建的二进制文件,且 rc.d 配置需要手动完成,因此这条路径适合熟悉 FreeBSD 惯例的运维人员。如果你只是在评估各类平台、并没有特定的 FreeBSD 需求,那么 Linux(systemd)或 macOS(launchd)可通过 install.shzeroclaw service install 更快地完成部署。

直接获取文件,而非复制粘贴。 下面展示的每个 shell 脚本和示例配置都包含在 dist/freebsd/ 中:请直接将它们复制到你的主机。本演练会说明每个部分的作用及其原因。

系统依赖

pkg 安装工具链和运行时:

sh

doas pkg install -y rust git
软件包为什么
rust用于提供 cargorustc 以构建二进制文件。ZeroClaw 工作区的 MSRV 为 Rust 1.96.0;FreeBSD 的 rust 移植包跟踪更新的稳定版本,因此 pkg install rust 即可满足要求。
git克隆仓库时使用,如果你使用任何基于 git 的工具,则在运行时也需要。

doas,而非 sudo FreeBSD 将 doas 作为基础提权工具随系统提供;sudo 则是可选的 port。本文示例使用 doas。一个最简的 /usr/local/etc/doas.conf,用于授予 wheel 组免密码提权,如下所示:

permit nopass keepenv :wheel

从源代码构建

sh

git clone https://github.com/zeroclaw-labs/zeroclaw.git
cd zeroclaw
cargo build --release

发布版二进制文件位于 target/release/zeroclaw。在配置一般的硬件上,完整构建默认功能集需要一段时间,这是正常现象;ZeroClaw 是一个大型的 Rust workspace。

要精简构建,请禁用你不需要的功能(在 Linux 机器上参见 ./install.sh --list-features,或查看 Cargo.toml):

sh

cargo build --release --no-default-features --features agent-runtime

安装二进制文件

把它放在 PATH 中的某个位置。/usr/local/bin 是 FreeBSD 上通过 ports 安装的二进制文件的常规位置:

sh

doas install -m 755 target/release/zeroclaw /usr/local/bin/zeroclaw
zeroclaw --version

如果你更倾向于按用户安装,使用 ~/.cargo/bin/zeroclaw 同样可行。

首次运行配置

sh

zeroclaw quickstart

这会创建 ~/.zeroclaw/,其中包含一份初始配置,并引导你完成 provider 设置。配置布局和优先级与其他所有平台完全相同:参见 Reference → Config

提供商身份验证

提供商认证并非 FreeBSD 特有。API 密钥类提供商只需通过网关、zerocode、zeroclaw config set 或环境变量设置密钥即可。OAuth 和订阅类提供商(例如 OpenAI/Codex ChatGPT 订阅、Anthropic Claude Pro/Team)则从供应商自己的控制面板或登录流程中获取令牌,然后你可以像配置 API 密钥那样进行配置。

有关完整的凭据模型(API 密钥、OAuth/订阅令牌、环境变量覆盖以及密钥存储),请参阅 Provider Configuration → CredentialsOAuth and subscription auth。该页面是所有平台的权威参考来源。

作为服务运行(rc.d

由于 zeroclaw service install 没有 FreeBSD 后端,请使用 FreeBSD 原生的 daemon(8)rc.d 脚本下管理该守护进程。这样你就可以使用 service zeroclaw start|stop|restart|status,并获得崩溃后自动重启、pidfile 以及开机自启动等功能。

下面每个脚本的可直接安装副本都位于 dist/freebsd/zeroclaw-run.sh、基础版 zeroclaw.rc 以及强化版 zeroclaw-hardened.rc)。这两个 rc.d 脚本带有 @@ZEROCLAW_USER@@ 占位符,你可在安装时用 sed 替换,因此可以直接获取这些文件,无需复制粘贴:参见 dist/freebsd/README.md。下面的演练将解释每个部分的作用。

1. 启动器脚本

daemon(8) 会以最小化的环境启动子进程,因此请导出完整的 PATH(FreeBSD 将 gitpython3 等放在 /usr/local/bin 下,而该目录_不在_默认的服务 PATH 中)。rc.d 脚本会通过 daemon -u <user> 来运行此命令,根据 daemon(8) 的说明,它会在 exec 之前依据该账户 passwd 条目中的信息设置 HOMEUSERSHELL,因此 ${HOME} 已经是该服务账户的主目录(对于主目录位于其他位置的账户,以及 rc.conf 中的 run-as 覆盖设置,均可正常工作)。将其保存为 /usr/local/libexec/zeroclaw-run.sh

sh

#!/bin/sh
# daemon -u <user> has already set HOME from the account用户的 passwd 条目。
export PATH="/usr/local/bin:/usr/local/sbin:/usr/bin:/bin:/usr/sbin:/sbin:${HOME}/bin"
exec /usr/local/bin/zeroclaw daemon --config-dir "${HOME}/.zeroclaw"

sh

doas install -m 755 zeroclaw-run.sh /usr/local/libexec/zeroclaw-run.sh

2. rc.d 脚本

另存为 /usr/local/etc/rc.d/zeroclaw

sh

#!/bin/sh
#
# PROVIDE: zeroclaw
# REQUIRE: NETWORKING DAEMON
# 关键字:shutdown

. /etc/rc.subr

name=zeroclaw
rcvar="zeroclaw_enable"

load_rc_config $name

: ${zeroclaw_enable:=NO}
# 请勿将其命名为 ${name}_user —— 否则 rc.subr 会执行其自身的 su 用户切换
# and collide with daemon -u (设置用户环境失败).
: ${zeroclaw_runas:="youruser"}

rundir=/var/run/zeroclaw
pidfile=${rundir}/zeroclaw.pid
logfile=/var/log/${name}.log
launcher=/usr/local/libexec/zeroclaw-run.sh

command=/usr/sbin/daemon
command_args=-r -P ${pidfile} -o ${logfile} -u ${zeroclaw_runas} ${launcher}

start_precmd="zeroclaw_precmd"

zeroclaw_precmd()
{
    # rundir + logfile 保持 root 所有权:rc.d(root)将守护进程的 -P pidfile
    # 写入此处并在之后信任它,因此非特权服务用户必须无法
    # 伪造它。daemon -o 会在降权到 ${zeroclaw_runas} 之前打开 logfile。
    install -d -o root -g wheel -m 755 ${rundir}
    install -o root -g wheel -m 640 /dev/null "${logfile}"
}

run_rc_command $1

sh

doas install -m 755 zeroclaw /usr/local/etc/rc.d/zeroclaw

标志的作用:

  • -r:监视子进程,并在其退出时重启(崩溃恢复)。
  • -P ${pidfile}:写入 supervisor 的 pid,以便 service zeroclaw stop 可以向其发送信号。
  • -o ${logfile}:将子进程的 stdout/stderr 重定向到日志文件。
  • -u ${zeroclaw_runas}:以非特权用户而非 root 身份运行 zeroclaw。

为何使用 daemon -u 而非 su -m 一种常见的写法是 daemon ... su -m user -c launcher。请避免这样做:su(1) 不会SIGTERM 转发给其子进程,因此 service zeroclaw stop 会终止 daemon 监管进程,却留下一个孤立的 zeroclaw 进程,而下一次 start 又会叠加出第二个副本。daemon -u user 会让 daemon(8) 成为 zeroclaw 的直接父进程,从而转发停止信号并干净地关闭。(如果你因其他原因不得不使用基于 su 的脚本,请在其停止流程中加上一条 pkill -f "zeroclaw daemon" 清扫命令。)

3. 启用并启动

sh

doas sysrc zeroclaw_enable=YES
doas sysrc zeroclaw_runas=youruser     拥有 ~/.zeroclaw 的账户

doas service zeroclaw start
doas service zeroclaw status

service zeroclaw stop / restart 可按预期工作。由于 /etc/rc.conf 中包含 zeroclaw_enable=YES(由 sysrc 写入),该守护进程也会在开机时启动。

4. 针对无人值守和远程操作的安全加固

上面的脚本对于交互式、单实例的安装是正确的。但一旦你远程(通过 ssh)操作该服务,或运行多个副本时,三个 daemon(8) 的行为会让你大吃一惊。这三个问题都曾困扰过生产环境部署;修复方法都很简单。一个整合了下面所有修复的完整脚本随 dist/freebsd/zeroclaw-hardened.rc 一起发布:请用它替换基础的 zeroclaw 脚本进行安装。

远程执行 service ... start 时挂起。 daemon -r 会继承并持续占用其启动时所附带的 stdin/stdout/stderr。运行 ssh host 'service zeroclaw start' 时,supervisor 会一直保持你的 ssh 会话的 stdout fd 处于打开状态,因此 ssh 永远收不到 EOF,即使守护进程已正常启动,命令仍会挂起。请分离 supervisor 自身的描述符:-o ${logfile} 已经将_子进程_的输出重定向出去,因此不会丢失任何内容:

sh

command_args=-r -P ${pidfile} -o ${logfile} -u ${zeroclaw_runas} ${launcher}
# ...调用守护进程,并将其自身的 std{in,out,err} 重定向到 /dev/null:
/usr/sbin/daemon ${command_args} </dev/null >/dev/null 2>&1

如果你使用标准的 command/command_args 形式,请将启动操作封装在自定义的 start_cmd 中,以便你控制重定向。正是这一项更改,使得从 ssh、CI 或配置管理推送中调用 service zeroclaw start 变得安全。

重复执行 start 会堆积孤儿监管进程。 普通的 start 不会检查监管进程是否已在运行,因此第二次 start(或在崩溃后遗留了过期 pidfile 时执行的 start)会启动另一个 daemon,与第一个争夺网关端口。应让 start 具备幂等性,当已存在活跃的监管进程时拒绝启动。通过启动器路径来匹配监管进程,而非仅凭 pidfile(pidfile 可能已过期)。执行此操作时有两个 FreeBSD 特有的陷阱:

  • daemon(8) 会_将其监管进程重命名_为 daemon: /usr/local/libexec/zeroclaw-run.sh[<childpid>] (daemon)。因此 pgrep -f zeroclaw-run.sh 能匹配到该监管进程,但用 pgrep -f 搜索二进制文件名却不行。请绑定到字面量 daemon: 前缀:它能匹配监管进程,而绝不会匹配子进程、手动运行的启动器,或 rc shell 本身。同时也绑定 daemon 的 [<childpid>] 中开头的 [,这样一个名称仅仅_以_ zeroclaw-run.sh _开头_的同级启动器就无法被匹配(一旦你运行实例池,这一点就很重要,参见下文 运行实例池)。
  • FreeBSD pgrep -f 会针对该重命名字符串遵循前导的 ^ 锚点:pgrep -f '^daemon: ...' 不会匹配任何内容。请去掉 ^;依靠 daemon: 前缀来保证特异性,并将 .sh 中的点号转义为 [.](将方括号转义为 [[]),使它们成为字面字符。

sh

launcher_pat=daemon:/usr/local/libexec/zeroclaw-run[.]sh[[]

zeroclaw_running()
{
    pgrep -f "${launcher_pat}" >/dev/null 2>&1
}

daemon -P 的 pidfile 中执行 read 会报告假阴性结果。 daemon -P 写入 pid 时不带末尾换行符,因此 IFS= read -r pid < "${pidfile}" 会返回_非零_状态(在换行符之前遇到 EOF),即便它已正确设置了 pid。如果你用 read -r pid < "$pf" || return 1 这样的方式进行守卫判断,那么每个正在运行的实例都会显示为已停止,而你那个幂等的 start 就会欣然启动一个重复实例。不要将成功路径绑定在 read 的退出状态上:而应改为校验该值本身:

sh

pid=""
IFS= read -r pid < "${pidfile}"     # 此处不要 `|| return 1`
case ${pid} in
    ''|*[!0-9]*) return 1 ;;        # 为空或非数字 → 视为未运行
esac

运行实例池。 要运行 N 个守护进程(例如一个工作进程池),请为每个进程分配各自的 pidfile 和 logfile(worker.$i.pidworker.$i.log),并对 $i 循环执行启动/停止操作。由于每个实例的 supervisor 重命名标题都是相同的,且不包含各实例特有的参数,因此 pidfile 是唯一区分实例的句柄:通过 pidfile 来驱动停止/状态操作,并在执行全量停止时清扫掉任何没有存活 pidfile 指向的残留 supervisor(手动启动的,或其 pidfile 已失效的)。

在监狱中运行

Jail 为 ZeroClaw 提供一个隔离的 root,拥有自己的软件包、服务用户,并可选择拥有独立的 IP,这在主机运行其他服务或你希望约束该代理时非常有用。服务的设置方式与主机场景完全相同;你只需在 jail 内部 运行即可。 本文将介绍一个使用基础系统工具的经典厚 jail(无需 jail 管理器)。

一步式选项。 dist/freebsd/zeroclaw-jail-setup.sh 自动执行下方的步骤 1–3:它会创建 jail、提取匹配的基础系统、添加 /etc/jail.conf 条目、启动 jail,并在其中安装启动器以及加固后的 rc.d 脚本(doas sh zeroclaw-jail-setup.sh,可通过环境变量覆盖 JAIL_NAME / JAIL_PATH / ZPOOL / ZEROCLAW_USER)。下方的手动操作说明解释了它的工作原理。

1. 创建 jail

sh

# 用于 jail 的 ZFS 数据集(如果使用 UFS,则用普通目录)。
doas zfs create -o mountpoint=/jails/zeroclaw zroot/jails/zeroclaw   # 调整池

# 将与 HOST 版本匹配的基础系统解压到其中。
doas fetch -o /tmp/base.txz \
    "https://download.freebsd.org/releases/$(uname -m)/$(freebsd-version -u)/base.txz"
doas tar -xpf /tmp/base.txz -C /jails/zeroclaw
doas cp /etc/resolv.conf /jails/zeroclaw/etc/

2. 配置并启动

/etc/jail.conf(宿主机端)添加一个 jail 条目。本示例共享宿主机网络;如果为该 jail 分配专用地址,请改为设置 ip4.addr

zeroclaw {
    host.hostname = "zeroclaw";
    path = "/jails/zeroclaw";
    exec.start = "/bin/sh /etc/rc";
    exec.stop  = "/bin/sh /etc/rc.shutdown";
    exec.clean;
    mount.devfs;
    persist;
}

sh

doas sysrc jail_enable=YES
doas sysrc jail_list+=零爪
doas service jail start zeroclaw

3. 在 jail 中安装 ZeroClaw

上述各节中的所有内容都在 jail 内部 运行:在命令前加上 doas jexec zeroclaw …,或使用 doas jexec zeroclaw /bin/sh 打开 shell:

sh

doas jexec zeroclaw pkg install -y rust git     # 或复制在主机上构建的二进制文件
# build + install zeroclaw to /usr/local/bin/zeroclaw exactly as above, then:
doas jexec zeroclaw pw useradd zeroclaw -m -s /usr/sbin/nologin

将启动器和 rc.d 脚本安装到 jail 的文件系统中(从主机来看,jail 根目录带有前缀:/jails/zeroclaw/usr/local/libexec/…/jails/zeroclaw/usr/local/etc/rc.d/…)。然后在 jail _内部_启用并启动该服务:

sh

doas jexec zeroclaw sysrc zeroclaw_enable=YES
doas jexec zeroclaw service zeroclaw start
doas jexec zeroclaw service zeroclaw status

特定于 Jail 的说明

  • 从宿主机使用 tee 而非 cp /dev/stdin 编辑 jail 文件。 通过管道传输 … | doas tee /jails/zeroclaw/usr/local/etc/rc.d/zeroclaw >/dev/nulldoas cp /dev/stdin … 可能在复制过程中失败并报错 cp: /dev/stdin: File changed
  • 网关在监狱内绑定。 守护进程默认监听环回地址:要从主机或局域网访问它,请使用 --host 0.0.0.0 启动 zeroclaw(编辑 zeroclaw-run.sh),并为监狱分配一个可访问的地址,或从主机进行代理。
  • 在 jail 中优先使用经过加固的 rc.d 脚本。 你通常会通过 jexec/ssh 以非交互方式驱动 service,而这恰恰是基础脚本的 start 挂起和孤儿进程堆积问题最容易暴露的地方:参见加固。它还会使 jail 内的 /var/run/zeroclaw 保持归 root 所有,从而使非特权服务用户无法伪造 supervisor 的 pidfile。
  • 在单个 jail 中运行多个守护进程(例如一个工作进程池)遵循加固章节中的进程池说明:每个实例使用独立的 pidfile/logfile,并通过绑定到启动器重命名标题的 pgrep 进行匹配,因为该 jail 共享同一个进程表。

在 Podman + Linuxulator 下运行 Linux 镜像

上面的原生构建是 ZeroClaw 本身的正确路径。但某些基于 Python 的工具和技能依赖于仅支持 manylinux 的 wheel 包:例如 polarspyarroworacledb 都不发布 FreeBSD wheel 包,因此导入它们的工具无法在原生 FreeBSD python3 下运行。FreeBSD 的 Linuxulator(Linux 二进制兼容层)加上 Podman,可让你在 FreeBSD 主机上运行官方 Linux 容器镜像,为这些工具提供它们所需的 Linux ABI。这与原生 rc.d 守护进程互为补充:你可以运行其中之一,或两者并行运行。

1. 前提条件

启用 Linux ABI 并确认其报告了 Linux 版本:

sh

doas sysrc linux_enable=YES
doas service linux start              # 加载模块并挂载 /compat/linux
sysctl compat.linux.osrelease         例如:compat.linux.osrelease: 5.15.0

/etc/rc.conf 中设置 linux_enable="YES" 也会在启动时加载 ABI。然后安装 Podman:

sh

doas pkg install -y podman

2. 拉取镜像:强制使用 Linux 平台

FreeBSD 上的 Podman 在解析 manifest 列表时默认使用 os=freebsd。ZeroClaw 的镜像仅针对 linux/amd64linux/arm64 发布,因此直接执行 podman pull 会失败并报错 no image found in manifest list for architecture ..., OS freebsd。请显式强制指定 Linux 平台:

sh

doas podman pull --os linux --arch amd64 ghcr.io/zeroclaw-labs/zeroclaw:debian

使用 debian 标签而非 latest:distroless 的 latest 镜像不含 shell,这使得在模拟环境下调试很不方便。完整镜像列表请参阅 Docker & Containers

3. 运行容器

Linux 镜像的行为与 Docker & Containers 中所述完全一致,它期望持久化状态位于 /zeroclaw-data,并在首次运行时引导生成配置:

sh

doas podman run -d --name zeroclaw --restart=always \
    --os linux --arch amd64 \
    -p 42617:42617 \
    -v /var/db/zeroclaw:/zeroclaw-data \
    ghcr.io/zeroclaw-labs/zeroclaw:debian

doas podman exec -it zeroclaw zeroclaw quickstart

在每个 run 命令(而不仅仅是 pull)上保留 --os linux --arch amd64 标志,以便 Podman 不会重新解析为 FreeBSD 默认值。

Linuxulator 说明

  • 开机持久化。 Podman 自带的 --restart=always 仅在 Podman 运行期间重启容器;它自身无法在主机重启后存留。请通过 rc.d 脚本(与原生服务相同的模式)或 @reboot cron 条目来监管 podman start,以便主机重启后容器能够恢复运行。
  • 网络配置。 -p 42617:42617 通过 Podman 的网桥发布网关。如果你的主机上未配置网桥/CNI,--network host 是最简单的替代方案:此时容器将直接共享主机的网络栈。
  • 并非所有程序都能完美仿真。 Linuxulator 覆盖了常见的系统调用接口,但某些特殊的二进制程序可能会触发未实现的调用。如果某个工具运行异常,请先检查 dmesg 中的 linux: 警告,然后再判断是否为 ZeroClaw 的 bug。

日志

sh

tail -f /var/log/zeroclaw.log

通过标准配置/环境变量设置日志级别:参见 Operations → Logs & observability

验证

sh

zeroclaw --version
service zeroclaw status
# 如果守护进程暴露了本地网关(默认为 127.0.0.1:42617):
fetch -qo - http://127.0.0.1:42617/health

"status":"ok" 健康检查负载表示网关已正常运行;响应的 runtime 字段携带各组件的健康状况(通道、提供方等等)。

卸载

sh

doas service zeroclaw stop
doas sysrc -x zeroclaw_enable
doas rm /usr/local/etc/rc.d/zeroclaw /usr/local/libexec/zeroclaw-run.sh
doas rm /usr/local/bin/zeroclaw
rm -rf ~/.zeroclaw        # 可选 — 删除配置和历史记录

下一个