feat(port): bundle colibri rc.d services into the canonical port #113

Merged
clawdie merged 1 commit from feat/port-bundle-rcd into main 2026-06-20 21:38:41 +02:00
5 changed files with 351 additions and 2 deletions

View file

@ -8,11 +8,19 @@ port tracks the source. See clawdie-iso `docs/POUDRIERE-BUILD-SERVER.md`.
```
sysutils/colibri/
├── Makefile port recipe (cargo; ships 6 runtime/operator binaries)
├── Makefile port recipe (cargo; ships 6 runtime/operator binaries + rc.d)
├── pkg-descr package description
└── pkg-plist installed file list
├── pkg-plist installed file list
└── files/ rc.d service templates installed into etc/rc.d/
├── colibri_daemon.in
└── colibri_bridge.in
```
The `files/` rc.d templates mirror the canonical live-image copies in
`packaging/freebsd/`; poudriere builds in a clean jail and only sees the port
directory, so the package carries its own copy. This is the single canonical
port — the clawdie-iso repo consumes it from here and keeps no duplicate.
## Generated content
- **`CARGO_CRATES`** (committed, in the `Makefile`) — the crates.io dependency

View file

@ -392,5 +392,11 @@ do-install:
${CARGO_TARGET_DIR}/${CARGO_BUILD_TARGET}/release/${b} \
${STAGEDIR}${PREFIX}/bin/${b}
.endfor
# rc.d services (the daemon's rc.d expects the binary at PREFIX/bin/colibri-daemon).
@${MKDIR} ${STAGEDIR}${PREFIX}/etc/rc.d
${INSTALL_SCRIPT} ${FILESDIR}/colibri_daemon.in \
${STAGEDIR}${PREFIX}/etc/rc.d/colibri_daemon
${INSTALL_SCRIPT} ${FILESDIR}/colibri_bridge.in \
${STAGEDIR}${PREFIX}/etc/rc.d/colibri_bridge
.include <bsd.port.mk>

View file

@ -0,0 +1,135 @@
#!/bin/sh
#
# Colibri TCP bridge — FreeBSD rc.d service
#
# Bridges the colibri-daemon Unix socket to a TCP port on the Tailscale
# interface so remote hosts (debby, domedog) can reach the Colibri control
# plane through the mesh VPN.
#
# socat runs in the FOREGROUND — it does not self-daemonize. rc.d runs it
# under daemon(8), which backgrounds it, writes a pidfile, restarts on crash,
# and redirects stdout/stderr to a logfile.
#
# Setup (one-time, as root):
# cp /home/clawdie/ai/hermes-bsd/packaging/freebsd/colibri_bridge.in \
# /usr/local/etc/rc.d/colibri_bridge
# chmod 555 /usr/local/etc/rc.d/colibri_bridge
# sysrc colibri_bridge_enable=YES
#
# Requires:
# - socat installed (pkg install socat)
# - colibri_daemon running (socket at /var/run/colibri/colibri.sock)
# - pf rule for the bridge port on tailscale0
# PROVIDE: colibri_bridge
# REQUIRE: LOGIN cleanvar colibri_daemon
# BEFORE: hermes_dashboard
# KEYWORD: shutdown
. /etc/rc.subr
name="colibri_bridge"
rcvar="colibri_bridge_enable"
load_rc_config $name
: ${colibri_bridge_enable:="NO"}
: ${colibri_bridge_user:="clawdie"}
: ${colibri_bridge_group:="clawdie"}
: ${colibri_bridge_listen_addr:="100.72.229.63"}
: ${colibri_bridge_listen_port:="9190"}
: ${colibri_bridge_socket:="/var/run/colibri/colibri.sock"}
: ${colibri_bridge_run_dir:="/var/run/colibri"}
: ${colibri_bridge_logfile:="/var/log/colibri/bridge.log"}
: ${colibri_bridge_socat:="/usr/local/bin/socat"}
pidfile="${colibri_bridge_run_dir}/colibri-bridge.pid"
supervisor_pidfile="${colibri_bridge_run_dir}/colibri-bridge-supervisor.pid"
command="/usr/sbin/daemon"
command_args="-P ${supervisor_pidfile} -p ${pidfile} -t ${name} \
-o ${colibri_bridge_logfile} ${colibri_bridge_socat} \
TCP-LISTEN:${colibri_bridge_listen_port},bind=${colibri_bridge_listen_addr},fork,reuseaddr \
UNIX-CONNECT:${colibri_bridge_socket}"
# procname must match the actual child process so rc.subr finds the right PID
# and doesn't kill the wrong daemon(8) child.
procname="socat"
start_precmd="colibri_bridge_prestart"
stop_cmd="colibri_bridge_stop"
status_cmd="colibri_bridge_status"
extra_commands="health"
colibri_bridge_prestart()
{
install -d -o "${colibri_bridge_user}" -g "${colibri_bridge_group}" -m 0750 \
"${colibri_bridge_run_dir}"
install -d -o "${colibri_bridge_user}" -g "${colibri_bridge_group}" -m 0750 \
"$(/usr/bin/dirname "${colibri_bridge_logfile}")"
rm -f "${pidfile}" "${supervisor_pidfile}"
if [ ! -x "${colibri_bridge_socat}" ]; then
echo "ERROR: socat not found at ${colibri_bridge_socat}"
return 1
fi
if [ ! -S "${colibri_bridge_socket}" ]; then
echo "ERROR: colibri socket not found at ${colibri_bridge_socket}"
echo " Start colibri_daemon first: service colibri_daemon start"
return 1
fi
}
colibri_bridge_stop()
{
local _sup=""
[ -f "${supervisor_pidfile}" ] && _sup=$(cat "${supervisor_pidfile}" 2>/dev/null)
if [ -n "${_sup}" ] && kill -0 "${_sup}" 2>/dev/null; then
echo "Stopping ${name} (daemon(8) supervisor pid ${_sup})."
kill -TERM "${_sup}" 2>/dev/null
local _n=0
while kill -0 "${_sup}" 2>/dev/null && [ ${_n} -lt 30 ]; do
sleep 1
_n=$((_n + 1))
done
if kill -0 "${_sup}" 2>/dev/null; then
echo "Supervisor did not exit in time; sending SIGKILL."
kill -KILL "${_sup}" 2>/dev/null
fi
else
echo "${name} is not running."
fi
local _ch=""
[ -f "${pidfile}" ] && _ch=$(cat "${pidfile}" 2>/dev/null)
if [ -n "${_ch}" ] && kill -0 "${_ch}" 2>/dev/null; then
kill -TERM "${_ch}" 2>/dev/null
fi
rm -f "${supervisor_pidfile}" "${pidfile}"
}
health_cmd="colibri_bridge_health"
colibri_bridge_health()
colibri_bridge_status()
{
tcolibri_bridge_health
}
{
if ! pgrep -f "socat.*${colibri_bridge_listen_port}" >/dev/null 2>&1; then
echo "colibri-bridge socat process not found"
return 1
fi
_resp=$(printf '{"cmd":"status"}\n' | \
nc -w 2 "${colibri_bridge_listen_addr}" "${colibri_bridge_listen_port}" 2>/dev/null)
if [ -n "${_resp}" ]; then
echo "colibri-bridge is healthy (port ${colibri_bridge_listen_port} responding)"
return 0
else
echo "colibri-bridge socat running but no response on port ${colibri_bridge_listen_port}"
return 1
fi
}
run_rc_command "$1"

View file

@ -0,0 +1,198 @@
#!/bin/sh
#
# Colibri daemon — FreeBSD rc.d service
#
# colibri-daemon runs in the FOREGROUND — it does not self-daemonize or write a
# pidfile. rc.d runs it under daemon(8), which backgrounds it, writes the
# child pidfile (colibri-daemon PID), restarts on crash, and redirects
# stdout/stderr (tracing) to a logfile. rc.subr performs the privilege drop
# through ${name}_user.
#
# Setup (one-time, as root):
# pw groupadd colibri
# pw useradd colibri -g colibri -d /var/db/colibri -s /usr/sbin/nologin
# cp packaging/freebsd/colibri_daemon.in /usr/local/etc/rc.d/colibri_daemon
# chmod 555 /usr/local/etc/rc.d/colibri_daemon
# sysrc colibri_daemon_enable=YES # or NO during dual-run
# cp packaging/freebsd/provider.env.example /usr/local/etc/colibri/provider.env
# chmod 600 /usr/local/etc/colibri/provider.env
# $EDITOR /usr/local/etc/colibri/provider.env # fill in vault credentials
#
# Runtime:
# service colibri_daemon start
# service colibri_daemon status
# service colibri_daemon stop
#
# Requires:
# - colibri-daemon binary at /usr/local/bin/colibri-daemon
# - colibri user/group (privilege drop target)
# PROVIDE: colibri_daemon
# REQUIRE: LOGIN cleanvar
# KEYWORD: shutdown
. /etc/rc.subr
name="colibri_daemon"
rcvar="colibri_daemon_enable"
load_rc_config $name
: ${colibri_daemon_enable:="NO"}
: ${colibri_daemon_user:="colibri"}
: ${colibri_daemon_group:="colibri"}
: ${colibri_daemon_binary:="/usr/local/bin/colibri-daemon"}
: ${colibri_daemon_data_dir:="/var/db/colibri"}
: ${colibri_daemon_run_dir:="/var/run/colibri"}
: ${colibri_daemon_socket:="${colibri_daemon_run_dir}/colibri.sock"}
: ${colibri_daemon_db_path:="${colibri_daemon_data_dir}/colibri.sqlite"}
: ${colibri_daemon_logfile:="/var/log/colibri/daemon.log"}
: ${colibri_daemon_provider_env:="/usr/local/etc/colibri/provider.env"}
: ${colibri_daemon_host:="$(/bin/hostname)"}
: ${colibri_daemon_cost_mode:="smart"}
pidfile="${colibri_daemon_run_dir}/colibri-daemon.pid"
# Supervisor pidfile (the daemon(8) parent). Kept distinct from the child
# pidfile so `stop` can target the supervisor — see colibri_daemon_stop.
supervisor_pidfile="${colibri_daemon_run_dir}/colibri-daemon-supervisor.pid"
# Run colibri-daemon under daemon(8):
# -P supervisor pidfile (the daemon(8) parent — used by stop)
# -p child pidfile (writes colibri-daemon PID — used by start/status)
# -r restart on crash, -t process title,
# -o append stdout/stderr to log.
# User selection is handled by rc.subr through colibri_daemon_user.
command="/usr/sbin/daemon"
command_args="-P ${supervisor_pidfile} -p ${pidfile} -r -t ${name} \
-o ${colibri_daemon_logfile} ${colibri_daemon_binary}"
# Use the child's process name so rc.subr can find the right process via the
# child pidfile. Using the daemon(8) supervisor path would collide with
# tailscaled and other daemon(8)-managed services on the system.
procname="colibri-daemon"
start_precmd="colibri_daemon_prestart"
start_postcmd="colibri_daemon_poststart"
stop_cmd="colibri_daemon_stop"
stop_postcmd="colibri_daemon_poststop"
extra_commands="health"
colibri_daemon_prestart()
{
# /var/run is tmpfs on FreeBSD (wiped each boot) — recreate every start.
install -d -o "${colibri_daemon_user}" -g "${colibri_daemon_group}" -m 0750 \
"${colibri_daemon_run_dir}"
install -d -o "${colibri_daemon_user}" -g "${colibri_daemon_group}" -m 0750 \
"${colibri_daemon_data_dir}"
install -d -o "${colibri_daemon_user}" -g "${colibri_daemon_group}" -m 0750 \
"$(/usr/bin/dirname "${colibri_daemon_logfile}")"
# Clear stale pidfiles before starting so a previous hard-killed run does
# not trip rc.subr's "already running" check. The socket is left to the
# daemon: it removes a stale socket on bind but refuses to start if a live
# daemon is already listening, so an rc-side rm here could only clobber a
# running instance.
rm -f "${pidfile}" "${supervisor_pidfile}"
# Provider keys are optional. Keep them in a root-owned env file instead of
# rc.conf so they are easy to rotate and not world-readable.
if [ -r "${colibri_daemon_provider_env}" ]; then
set -a
. "${colibri_daemon_provider_env}"
set +a
fi
# Config is passed to the child via the environment.
export COLIBRI_DAEMON_DATA_DIR="${colibri_daemon_data_dir}"
export COLIBRI_DAEMON_SOCKET="${colibri_daemon_socket}"
export COLIBRI_DB_PATH="${colibri_daemon_db_path}"
export COLIBRI_HOST="${colibri_daemon_host}"
export COLIBRI_COST_MODE="${colibri_daemon_cost_mode}"
export PATH="/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin"
}
colibri_daemon_poststart()
{
# Wait for the socket to appear (daemon forks, child binds socket).
local timeout=10
local waited=0
while [ ! -S "${colibri_daemon_socket}" ] && [ $waited -lt $timeout ]; do
sleep 1
waited=$((waited + 1))
done
if [ -S "${colibri_daemon_socket}" ]; then
echo "colibri-daemon socket ready after ${waited}s"
else
echo "WARNING: colibri-daemon socket not ready after ${timeout}s"
fi
}
colibri_daemon_stop()
{
# daemon(8) -r restarts the child if it is killed directly, so a plain
# SIGTERM to the child pidfile would just be undone. Stop the supervisor
# instead: on SIGTERM it forwards the signal to the child and exits without
# restarting it.
local _sup=""
[ -f "${supervisor_pidfile}" ] && _sup=$(cat "${supervisor_pidfile}" 2>/dev/null)
if [ -n "${_sup}" ] && kill -0 "${_sup}" 2>/dev/null; then
echo "Stopping ${name} (daemon(8) supervisor pid ${_sup})."
kill -TERM "${_sup}" 2>/dev/null
local _n=0
while kill -0 "${_sup}" 2>/dev/null && [ ${_n} -lt 30 ]; do
sleep 1
_n=$((_n + 1))
done
if kill -0 "${_sup}" 2>/dev/null; then
echo "Supervisor did not exit in time; sending SIGKILL."
kill -KILL "${_sup}" 2>/dev/null
fi
else
echo "${name} is not running."
fi
# Belt-and-suspenders: terminate the child if it somehow outlived the
# supervisor (e.g. supervisor SIGKILLed before it could clean up).
local _ch=""
[ -f "${pidfile}" ] && _ch=$(cat "${pidfile}" 2>/dev/null)
if [ -n "${_ch}" ] && kill -0 "${_ch}" 2>/dev/null; then
kill -TERM "${_ch}" 2>/dev/null
fi
rm -f "${supervisor_pidfile}" "${pidfile}"
}
colibri_daemon_poststop()
{
# Clean up tmpfs artifacts on graceful shutdown.
# The pidfile is managed by daemon(8); socket is the child's.
if [ -S "${colibri_daemon_socket}" ]; then
rm -f "${colibri_daemon_socket}"
fi
}
health_cmd="colibri_daemon_health"
status_cmd="colibri_daemon_status"
colibri_daemon_health()
{
if [ ! -S "${colibri_daemon_socket}" ]; then
echo "colibri-daemon socket not found"
return 1
fi
_resp=$(printf '{"cmd":"status"}\n' | nc -U "${colibri_daemon_socket}" -w 2 2>/dev/null)
if [ -n "${_resp}" ]; then
echo "colibri-daemon is healthy (socket responding)"
return 0
else
echo "colibri-daemon socket exists but no response"
return 1
fi
}
colibri_daemon_status()
{
colibri_daemon_health
}
run_rc_command "$1"

View file

@ -4,3 +4,5 @@ bin/colibri-daemon
bin/colibri-mcp
bin/colibri-test-agent
bin/colibri-tui
etc/rc.d/colibri_daemon
etc/rc.d/colibri_bridge