FreeBSD
ZeroClaw 可在 FreeBSD 上原生运行(已在 FreeBSD 15.0-RELEASE,amd64 上测试)。与 Linux/macOS/Windows 路径有两点不同:
- 没有预构建的二进制文件,也不支持
install.sh。 FreeBSD 不是 bootstrap 安装程序的目标平台,因此你需要使用系统的 Rust 工具链从源码进行构建。 - 没有
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.sh和zeroclaw service install更快地完成部署。直接获取文件,而非复制粘贴。 下面展示的每个 shell 脚本和示例配置都包含在
dist/freebsd/中:请直接将它们复制到你的主机。本演练会说明每个部分的作用及其原因。
系统依赖
从 pkg 安装工具链和运行时:
sh
doas pkg install -y rust git
| 软件包 | 为什么 |
|---|---|
rust | 用于提供 cargo 和 rustc 以构建二进制文件。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 → Credentials 和 OAuth 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 将 git、python3 等放在 /usr/local/bin 下,而该目录_不在_默认的服务 PATH 中)。rc.d 脚本会通过 daemon -u <user> 来运行此命令,根据 daemon(8) 的说明,它会在 exec 之前依据该账户 passwd 条目中的信息设置 HOME、USER 和 SHELL,因此 ${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.pid、worker.$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/null;doas 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 包:例如 polars、pyarrow 和 oracledb 都不发布 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/amd64 和 linux/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脚本(与原生服务相同的模式)或@rebootcron 条目来监管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 # 可选 — 删除配置和历史记录