Clean sweep — no kill on the Colibri wire protocol, CLI surface, TUI keybinding, or documentation. Backward-compat aliases removed; daemon and client deploy together so no transitional period needed. Wire: KillAgent→StopAgent, "kill-agent"→"stop-agent" (no alias) CLI: colibri kill→stop, Command::KillAgent→StopAgent Lib: client.kill_agent()→stop_agent() TUI: kill_selected()→stop_selected(), "kill"→"stop" label Docs: spawn/kill→spawn/stop, kill-agent→stop-agent (40+ instances) Retained kill only where it belongs: - child.kill() / handle.kill() (OS SIGKILL) - Unix kill(1) in sigterm tests - OOM kill, process-group kill comments (kernel mechanism)
96 lines
4.8 KiB
Markdown
96 lines
4.8 KiB
Markdown
# Colibri bridge — Linux (systemd)
|
||
|
||
Linux/systemd peer of `packaging/freebsd/colibri_bridge.in`. Exposes the
|
||
colibri-daemon control-plane Unix socket as a TCP port **on the Tailscale
|
||
interface only**, so other mesh hosts can drive the control plane.
|
||
|
||
The FreeBSD bridge already runs on hermes (binds hermes's own tailnet address);
|
||
this is the matching domedog side. The network gate (ufw) is **already applied
|
||
on domedog** (see "domedog host facts"); the systemd unit here is the proposed
|
||
service — review the open questions with hermes before `enable --now`.
|
||
|
||
> Real `100.x` Tailscale addresses are never committed to git. Each host's
|
||
> address is supplied at deploy time (`tailscale ip -4`) via
|
||
> `/etc/colibri/bridge.env` — see `COLIBRI_BRIDGE_LISTEN_ADDR=TAILSCALE_IP_REQUIRED`.
|
||
|
||
## Files
|
||
|
||
| File | Purpose |
|
||
| ---------------------------- | ------------------------------------------------------------ |
|
||
| `colibri-bridge.service` | systemd unit running socat (peer of the rc.d script) |
|
||
| `colibri-bridge.env.example` | tunables — install to `/etc/colibri/bridge.env` |
|
||
| `colibri-bridge.nft` | nftables ruleset **for hosts without ufw** (see facts below) |
|
||
|
||
## Install (one-time, as root)
|
||
|
||
```sh
|
||
install -D -m 0644 packaging/linux/colibri-bridge.service \
|
||
/etc/systemd/system/colibri-bridge.service
|
||
install -D -m 0644 packaging/linux/colibri-bridge.env.example \
|
||
/etc/colibri/bridge.env # then edit for this host
|
||
|
||
# Firewall: scope port 9190 to tailscale0 (peer of the hermes pf rule).
|
||
# domedog runs ufw (active, default-deny input) — the allow goes in ufw, and
|
||
# ufw's default-deny already blocks 9190 on every other interface:
|
||
ufw allow in on tailscale0 to any port 9190 proto tcp comment 'colibri-bridge'
|
||
# On a host WITHOUT ufw / with a default-accept input, use the nft table instead:
|
||
# install -D -m 0644 packaging/linux/colibri-bridge.nft /etc/colibri/colibri-bridge.nft
|
||
# nft -f /etc/colibri/colibri-bridge.nft
|
||
|
||
systemctl daemon-reload
|
||
systemctl enable --now colibri-bridge.service
|
||
systemctl status colibri-bridge.service
|
||
```
|
||
|
||
Quick check from another tailnet host:
|
||
|
||
```sh
|
||
printf '{"cmd":"status"}\n' | nc -w2 "$(tailscale ip -4)" 9190 # run on/against this host
|
||
```
|
||
|
||
## domedog host facts (verified 26.jun.2026)
|
||
|
||
The networking reality this packaging assumes, recorded so the next person
|
||
doesn't have to re-derive it:
|
||
|
||
- **Tailnet IPv4:** each host has a distinct `100.x` address — get it with
|
||
`tailscale ip -4` (not committed to git per policy).
|
||
- **Init / service mgr:** systemd (Ubuntu 24.04). FreeBSD/hermes uses rc.d.
|
||
- **Daemon user:** `clawdija` (hermes: `clawdie`). No dedicated `colibri`
|
||
group exists on domedog; the bridge runs as the daemon's own user so it can
|
||
reach the 0770 socket.
|
||
- **Firewall:** **ufw active + enabled**, default **deny (incoming)** /
|
||
allow (outgoing), backed by nftables, with fail2ban on top. Live packet
|
||
counters confirm it's enforcing, not just configured.
|
||
- **Currently-allowed inbound ports:** 22/tcp (ssh), 80/tcp, 443 (tcp+udp),
|
||
8433–8443/tcp, and now **9190/tcp scoped to `tailscale0`** (the bridge, added
|
||
for this work).
|
||
- **Port 8443 = CloudPanel** server control panel (nginx vhost
|
||
`cloudpanel.conf`, run by the `clp` user from `/home/clp/services/nginx/`).
|
||
Only 8443 actually listens; 8433–8442 are allowed but unused (range wider
|
||
than needed). ⚠️ CloudPanel's admin login is exposed to **Anywhere** (public),
|
||
not tailnet-scoped — the opposite posture from this bridge; flagged for the
|
||
host-exposure review.
|
||
|
||
## Open questions for the hermes discussion
|
||
|
||
1. **The socket has no auth — the tailnet boundary is the auth.** With both
|
||
hosts bridging the control plane, any tailnet peer that can reach either host
|
||
can issue full control-plane commands (`spawn-agent`, `stop-agent`,
|
||
`terminal-*`). Consider a Tailscale ACL scoping `:9190` to specific peers.
|
||
2. **Socket path parity.** Both sides assume `/run/colibri/colibri.sock`
|
||
(FreeBSD: `/var/run/colibri/colibri.sock`). domedog's daemon must be started
|
||
with a matching `COLIBRI_DAEMON_SOCKET`; the daemon's default is under
|
||
`$XDG_DATA_HOME`, which the bridge can't see under `ProtectHome=yes`.
|
||
3. **CloudPanel public exposure** (8443) — decide whether to keep it public or
|
||
move it behind the tailnet like the bridge.
|
||
|
||
## Notes
|
||
|
||
- The FreeBSD `colibri_bridge.in` `health`/`status` block bug (scrambled
|
||
function definitions) was **fixed and pushed** on the hermes side during this
|
||
work.
|
||
- `colibri-bridge.nft` is retained for hosts with a default-accept input chain;
|
||
on a ufw host its `accept` is not reliable (ufw's `drop` is terminal across
|
||
base chains) and its `drop` is redundant (ufw already default-denies). Kept as
|
||
documentation of intent and for the no-ufw case.
|