security(docs): mask Tailscale IPs + bot handles behind fleet.env

Real tailnet IPs and Telegram bot handles were being committed in docs/
memories/skills. Scrubbed all tracked markdown to ${VAR} placeholders; real
values now live in fleet.env (gitignored) and stay live via 'tailscale status'.

- add fleet.env.example (committed) + fleet.env (gitignored); .gitignore *.env
- AGENTS.md + HOST-MATRIX: masking convention so it can't recur
- also: domedog registered as Colibri agent (image-render/ffmpeg/build lane);
  correct CAPABILITY-ROUTING example to real registered caps (domedog headless)

Past commits not rewritten (history moves to Codeberg at v1.0); this fixes HEAD.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
Sam & Claude 2026-06-19 18:19:32 +02:00
parent c68953b107
commit a5139b5f7f
14 changed files with 98 additions and 45 deletions

1
.gitignore vendored
View file

@ -1,5 +1,6 @@
# Secrets and local runtime state # Secrets and local runtime state
.env .env
*.env
*.key *.key
*.pem *.pem
*.token *.token

View file

@ -36,9 +36,22 @@ already solved.** Tokens are money. A solved task retried is waste.
- Forgejo: code.smilepowered.org (SSH port 2222) - Forgejo: code.smilepowered.org (SSH port 2222)
- Vaultwarden: vault.smilepowered.org (SSL) - Vaultwarden: vault.smilepowered.org (SSL)
- Tailscale: debby=100.66.193.10, domedog=100.103.255.41, osa=100.72.229.63 - Tailscale: debby=${DEBBY_TS_IP}, domedog=${DOMEDOG_TS_IP}, osa=${OSA_TS_IP}
- Commit identity: `hello@clawdie.si` for all project commits - Commit identity: `hello@clawdie.si` for all project commits
### Topology & channel masking (do not commit real values)
Real Tailscale IPs and Telegram bot handles **never go into committed files** — they
were leaked once; not again. Committed docs reference variable names only
(`${OSA_TS_IP}`, `${HERMES_BOT}`, …). To resolve them:
- **IPs** are live-discoverable any time with `tailscale status`.
- **Handles + IPs** are stored in `fleet.env` (gitignored). Copy `fleet.env.example`
`fleet.env` and fill from `tailscale status` / Vaultwarden, then `source fleet.env`.
When editing docs: if you're about to paste a `100.x` IP or an `@…_bot` handle, stop and
use the placeholder instead.
## Agent matrix (5 agents across 3 hosts, 2 OS families) ## Agent matrix (5 agents across 3 hosts, 2 OS families)
| Agent | Host | Harness | OS | Isolation | Role | | Agent | Host | Harness | OS | Isolation | Role |

View file

@ -4,7 +4,7 @@ Name: **Layered Soul (Clawdie)**
Operator: Sam (Slovenia, hello@clawdie.si) Operator: Sam (Slovenia, hello@clawdie.si)
Home harness: Hermes Agent (Debby Linux) Home harness: Hermes Agent (Debby Linux)
Primary runtime: Debby (Debian 13, `x86_64`) Primary runtime: Debby (Debian 13, `x86_64`)
Network: Tailscale mesh — debby (100.66.193.10), domedog (100.103.255.41), osa (100.72.229.63) Network: Tailscale mesh — debby (${DEBBY_TS_IP}), domedog (${DOMEDOG_TS_IP}), osa (${OSA_TS_IP})
Repositories: `code.smilepowered.org/clawdie/*` (private Forgejo), `codeberg.org/Clawdie/*` (public mirrors) Repositories: `code.smilepowered.org/clawdie/*` (private Forgejo), `codeberg.org/Clawdie/*` (public mirrors)
Secrets: vault.smilepowered.org (Vaultwarden, self-hosted) Secrets: vault.smilepowered.org (Vaultwarden, self-hosted)

View file

@ -8,7 +8,7 @@ TTS voice preference: sl-SI-RokNeural (Slovenian)
## Communication ## Communication
- Primary channel: Telegram (@hermes_samob_bot + @zot_samob_bot in "My Debby" group) - Primary channel: Telegram (${HERMES_BOT} + ${ZOT_BOT} in "My Debby" group)
- Style: concise, action-only. No fluff. Graphs and tables over prose. - Style: concise, action-only. No fluff. Graphs and tables over prose.
- Review preference: structured summaries first, raw detail on demand - Review preference: structured summaries first, raw detail on demand
- For Clawdie IMG completion: single-line dd command only, no extra report - For Clawdie IMG completion: single-line dd command only, no extra report

View file

@ -30,14 +30,14 @@ The matching engine exists today in `colibri-daemon` — this is working, per-ho
Implemented 2026-06-19 (colibri PR #83), using the `socat`-over-Tailscale approach: Implemented 2026-06-19 (colibri PR #83), using the `socat`-over-Tailscale approach:
- **`socat` bridge** (`colibri_bridge` rc.d, daemon(8)-supervised) maps osa's daemon Unix - **`socat` bridge** (`colibri_bridge` rc.d, daemon(8)-supervised) maps osa's daemon Unix
socket to a TCP port on the **Tailscale interface only** (`100.72.229.63:9190`, never socket to a TCP port on the **Tailscale interface only** (`${OSA_TS_IP}:9190`, never
`0.0.0.0`), with a `pf` rule on `tailscale0`. The debby orchestrator reaches it over the `0.0.0.0`), with a `pf` rule on `tailscale0`. The debby orchestrator reaches it over the
tailnet. tailnet.
- **Poller/worker loop**`colibri_poll.py` (filters by agent UUID) and - **Poller/worker loop**`colibri_poll.py` (filters by agent UUID) and
`colibri_task_done.py` (transition-task), driven on the live 2 min / 5 min cadence by `colibri_task_done.py` (transition-task), driven on the live 2 min / 5 min cadence by
Hermes' internal scheduler (see `packaging/freebsd/colibri-agent-loop.md`), not OS cron. Hermes' internal scheduler (see `packaging/freebsd/colibri-agent-loop.md`), not OS cron.
- **Validated** on the debby↔osa lane (real tasks completed end-to-end). domedog joins via - **Validated** on the debby↔osa lane (real tasks completed end-to-end). **domedog joined
the same bridge pattern. 2026-06-19** via the same pattern — a client-side `socat` shim → osa `${OSA_TS_IP}:9190`.
- Alternative (heavier, not pursued): daemon-to-daemon federation. - Alternative (heavier, not pursued): daemon-to-daemon federation.
## [LIVE] Capability vocabulary (initial) ## [LIVE] Capability vocabulary (initial)
@ -58,20 +58,25 @@ Sourced from the probe and recorded per host in [`HOST-MATRIX.md`](./HOST-MATRIX
| Runtime | `python3.12`, `node24`, `rust`, `go` | | Runtime | `python3.12`, `node24`, `rust`, `go` |
| Media | `ffmpeg`, `pillow`/`image-render` | | Media | `ffmpeg`, `pillow`/`image-render` |
Hosts advertise only what they truly have. Example from the current fleet: Hosts advertise only what they truly have. Actual registered agents (2026-06-19):
- **domedog / debby (Linux):** `linux`, `docker`, `gui`, `screenshot`, `image-render`, … - **domedog (Linux, headless):** `linux`, `python3.12`, `rust`, `go`, `node`, `ffmpeg`,
- **osa (FreeBSD):** `freebsd`, `freebsd-jail`, `zfs`, `rust`, … (no `screenshot`/`image-render`) `image-render` — the media/compute lane. **No** `screenshot`/`gui` (headless VM), no `docker`.
- **debby / hermes-debby (Linux):** `linux`, `docker`, `shell`, `gateway`, `hermes`, `tailscale`.
- **osa / hermes-osa (FreeBSD):** `freebsd`, `shell`, `gateway`, `tailscale`, `rc.d`, `pf`,
`nginx`, `acme`, `hermes` — no `image-render` (Pillow dropped on FreeBSD).
## [DESIGN] Worked example: the tmux-screenshot skill ## [DESIGN] Worked example: the tmux-screenshot skill
This illustrates the routing flow (now runnable over the [LIVE] cross-host topology above): This illustrates the routing flow (now runnable over the [LIVE] cross-host topology above):
1. FreeBSD image drops Pillow — stays lean (`pkg-list` carries only `python312`). 1. FreeBSD image drops Pillow — stays lean (`pkg-list` carries only `python312`).
2. The skill manifest declares `required_capabilities: ["screenshot"]` (or `image-render`). 2. The skill manifest declares `required_capabilities: ["image-render"]` (or `screenshot`).
3. Only Linux hosts advertise `screenshot` (Pillow is trivial there). 3. Only a Linux host advertises these — today **domedog** carries `image-render`/`ffmpeg`
4. Colibri routes any screenshot task to debby/domedog automatically; if both are offline (osa dropped Pillow). `screenshot` additionally needs a display, so a *headless* host
the task parks until one returns. does not qualify for it.
4. Colibri routes the task to a matching host automatically — **proven 2026-06-19: an
`image-render` task routed to domedog**; with no match it parks until a capable agent appears.
The capability moved hosts. It was never lost. The capability moved hosts. It was never lost.

View file

@ -18,6 +18,11 @@ on any host fills in its own row. Source of truth for facts is the probe — not
> **Disk before action:** before installing a toolchain or starting a build, check > **Disk before action:** before installing a toolchain or starting a build, check
> real free space (`df -h /`, or the probe's `--storage`) — never estimate. Keep the > real free space (`df -h /`, or the probe's `--storage`) — never estimate. Keep the
> **Disk (free)** column current and flag any host past ~85%. See _Disk discipline_ below. > **Disk (free)** column current and flag any host past ~85%. See _Disk discipline_ below.
>
> **Never paste real IPs or bot handles here.** Use `${HOST_TS_IP}` and `${*_BOT}`
> placeholders; real values live in `fleet.env` (gitignored) and are live via
> `tailscale status`. Copy `fleet.env.example``fleet.env` to resolve them. The probe
> prints real IPs — record them in `fleet.env`, not in this table.
--- ---
@ -25,14 +30,15 @@ on any host fills in its own row. Source of truth for facts is the probe — not
| Agent | Host | OS / Isolation | Harness | Role | Bot / channel | Status | | Agent | Host | OS / Isolation | Harness | Role | Bot / channel | Status |
| ----------- | ------- | --------------------------- | ---------------------------- | -------------------------------- | --------------------- | ----------------------------- | | ----------- | ------- | --------------------------- | ---------------------------- | -------------------------------- | --------------------- | ----------------------------- |
| Hermes | debby | Debian 13 / Docker | Hermes Agent (upstream) | Orchestrator, soul backup | @hermes_samob_bot | LIVE | | Hermes | debby | Debian 13 / Docker | Hermes Agent (upstream) | Orchestrator, soul backup | ${HERMES_BOT} | LIVE |
| Zot | debby | Debian 13 / Docker | Zot RPC | Coding, media workflows | @zot_samob_bot | LIVE | | Zot | debby | Debian 13 / Docker | Zot RPC | Coding, media workflows | ${ZOT_BOT} | LIVE |
| Claude | domedog | Ubuntu 24.04 / Docker | Claude Code | Verification, review | — (CLI) | LIVE | | Claude | domedog | Ubuntu 24.04 / Docker | Claude Code | Verification, review | — (CLI) | LIVE |
| **Mevy** | osa | FreeBSD 15 / host | Hermes Agent (upstream, CLI) | **Consolidated into hermes-osa** | @zleht_bot (Žleht) | **LIVE — under hermes-osa** | | **Mevy** | osa | FreeBSD 15 / host | Hermes Agent (upstream, CLI) | **Consolidated into hermes-osa** | ${HERMES_OSA_BOT} (OSA-bot) | **LIVE — under hermes-osa** |
| **hermes-osa** | osa | FreeBSD 15 / host | Hermes Agent (FreeBSD fork) | **Native FreeBSD Hermes: chat + gateway** | @zleht_bot (Žleht) | **LIVE — chat + Telegram** | | **hermes-osa** | osa | FreeBSD 15 / host | Hermes Agent (FreeBSD fork) | **Native FreeBSD Hermes: chat + gateway** | ${HERMES_OSA_BOT} (OSA-bot) | **LIVE — chat + Telegram** |
| Codex | osa | FreeBSD 15 / jail | Codex CLI | ISO builds, validation | — (CLI) | LIVE | | Codex | osa | FreeBSD 15 / jail | Codex CLI | ISO builds, validation | — (CLI) | LIVE |
| **domedog-agent** | domedog | Ubuntu 24.04 / host | Colibri board agent | Headless Linux media/compute lane (image-render, ffmpeg, rust/go/py/node) | — | **LIVE — on central board 2026-06-19** |
> **Mevy vs hermes-osa distinction**: Mevy (@zleht_bot / Žleht) has been consolidated into hermes-osa as of 2026-06-17. The Telegram bot token was migrated from the old backup .env. hermes-osa now runs both the local CLI chat and the Telegram gateway (polling mode, tmux session `hermes-gateway`). > **Mevy vs hermes-osa distinction**: Mevy (${HERMES_OSA_BOT} / OSA-bot) has been consolidated into hermes-osa as of 2026-06-17. The Telegram bot token was migrated from the old backup .env. hermes-osa now runs both the local CLI chat and the Telegram gateway (polling mode, tmux session `hermes-gateway`).
> >
> **Status key**: `LIVE` = running and validated right now. `INSTALLED` = binary present, not yet validated in role. `PLANNED` = not yet set up. No guessing. > **Status key**: `LIVE` = running and validated right now. `INSTALLED` = binary present, not yet validated in role. `PLANNED` = not yet set up. No guessing.
@ -42,7 +48,7 @@ on any host fills in its own row. Source of truth for facts is the probe — not
> - One Telegram token per running service. Never share a token across instances. > - One Telegram token per running service. Never share a token across instances.
> - **Routing**: Colibri has a capability matcher for per-host agent pools, and **cross-host > - **Routing**: Colibri has a capability matcher for per-host agent pools, and **cross-host
> routing is LIVE** (2026-06-19): a `socat` bridge exposes osa's colibri-daemon on its > routing is LIVE** (2026-06-19): a `socat` bridge exposes osa's colibri-daemon on its
> Tailscale IP (`100.72.229.63:9190`, tailnet-only), the debby orchestrator dispatches over > Tailscale IP (`${OSA_TS_IP}:9190`, tailnet-only), the debby orchestrator dispatches over
> the tailnet, and a poller (2 min) / worker (5 min) loop executes assigned tasks. Validated > the tailnet, and a poller (2 min) / worker (5 min) loop executes assigned tasks. Validated
> on the debby↔osa lane; colibri PR #83. See [`CAPABILITY-ROUTING.md`](./CAPABILITY-ROUTING.md). > on the debby↔osa lane; colibri PR #83. See [`CAPABILITY-ROUTING.md`](./CAPABILITY-ROUTING.md).
> - **Probe vs identity**: `verify_facts_probe.py` is a required discipline/tool, > - **Probe vs identity**: `verify_facts_probe.py` is a required discipline/tool,
@ -56,9 +62,9 @@ on any host fills in its own row. Source of truth for facts is the probe — not
| Host | Tailscale IP | OS / Kernel | Virt | CPU | vCPU | RAM | Swap | Disk (free) | GPU | Probed | By | | Host | Tailscale IP | OS / Kernel | Virt | CPU | vCPU | RAM | Swap | Disk (free) | GPU | Probed | By |
| ----------- | -------------- | ---------------------------------- | --------------------- | -------------------------------------- | ---- | ------- | --------------------- | ---------------------------- | ---------------------- | ---------- | ------ | | ----------- | -------------- | ---------------------------------- | --------------------- | -------------------------------------- | ---- | ------- | --------------------- | ---------------------------- | ---------------------- | ---------- | ------ |
| **domedog** | 100.103.255.41 | Ubuntu 24.04.4 / 6.8.0-117 | KVM | AMD EPYC 7543P (32-core host) | 2 | 7.8 GiB | 2.0 GiB | 100 GB QEMU (51G free) | none (headless) | 2026-06-17 | Claude | | **domedog** | ${DOMEDOG_TS_IP} | Ubuntu 24.04.4 / 6.8.0-117 | KVM | AMD EPYC 7543P (32-core host) | 2 | 7.8 GiB | 2.0 GiB | 100 GB QEMU (51G free) | none (headless) | 2026-06-17 | Claude |
| **debby** | 100.66.193.10 | Debian 13 / 6.12.90+deb13.1-amd64 | bare metal | AMD Ryzen 7 5700U (8-core) | 16 | 15 GiB | 15 GiB | nvme0n1p2 453G (23G free) | Radeon Graphics (iGPU) | 2026-06-17 | Hermes | | **debby** | ${DEBBY_TS_IP} | Debian 13 / 6.12.90+deb13.1-amd64 | bare metal | AMD Ryzen 7 5700U (8-core) | 16 | 15 GiB | 15 GiB | nvme0n1p2 453G (23G free) | Radeon Graphics (iGPU) | 2026-06-17 | Hermes |
| **osa** | 100.72.229.63 | FreeBSD 15.0-RELEASE-p10 / GENERIC | not reported by probe | Intel Core Processor (Haswell, no TSX) | 6 | 11 GiB | not reported by probe | ZFS pool: zroot (23.4G free) | not reported by probe | 2026-06-17 | Pi | | **osa** | ${OSA_TS_IP} | FreeBSD 15.0-RELEASE-p10 / GENERIC | not reported by probe | Intel Core Processor (Haswell, no TSX) | 6 | 11 GiB | not reported by probe | ZFS pool: zroot (23.4G free) | not reported by probe | 2026-06-17 | Pi |
### Disk discipline (check, don't guess) ### Disk discipline (check, don't guess)
@ -83,7 +89,7 @@ host that fails. What you guess will be wrong; what you probe will be right.
### domedog (Claude / verification) — probed 2026-06-17 by Claude ### domedog (Claude / verification) — probed 2026-06-17 by Claude
- **Identity**: hostname `domedog.pro`, Tailscale `100.103.255.41` - **Identity**: hostname `domedog.pro`, Tailscale `${DOMEDOG_TS_IP}`
- **OS**: Ubuntu 24.04.4 LTS, kernel `6.8.0-117-generic`, x86_64, KVM guest - **OS**: Ubuntu 24.04.4 LTS, kernel `6.8.0-117-generic`, x86_64, KVM guest
- **CPU**: AMD EPYC 7543P 32-Core (2 vCPU exposed to guest) - **CPU**: AMD EPYC 7543P 32-Core (2 vCPU exposed to guest)
- **Memory**: 7.8 GiB RAM, 2.0 GiB swap - **Memory**: 7.8 GiB RAM, 2.0 GiB swap
@ -91,10 +97,26 @@ host that fails. What you guess will be wrong; what you probe will be right.
- **GPU**: none (headless VM) - **GPU**: none (headless VM)
- **Uptime at probe**: ~3.5 weeks - **Uptime at probe**: ~3.5 weeks
- **Role here**: Claude Code — verification & review lane. No Telegram bot. - **Role here**: Claude Code — verification & review lane. No Telegram bot.
- **Colibri agent (joined central board 2026-06-19)** — the headless Linux media/compute lane:
- **Capabilities advertised**: `linux`, `python3.12`, `rust`, `go`, `node`, `ffmpeg`,
`image-render`. **Not** `screenshot`/`gui` (headless VM), not `docker` (absent).
`image-render`/`ffmpeg` are domedog-only in the fleet — osa dropped Pillow.
- **Reach**: client shim `colibri-shim.service` (system unit, `User=clawdija`,
`Restart=always`, reboot-persistent) runs
`socat UNIX-LISTEN:~/.colibri/colibri.sock → TCP ${OSA_TS_IP}:9190` (osa bridge over
Tailscale). A system unit, not `--user`: `systemctl --user` has no bus on this host.
- **Operate**: `~/.colibri/agent.env` holds `COLIBRI_AGENT_ID` + `COLIBRI_SOCKET`; helpers
in `~/.colibri/``colibri_cmd.py` (raw JSON), `colibri_poll.py`, `colibri_task_done.py`.
- **Validated**: register → scheduler routed an `image-render` task to domedog → poller saw
it → worker marked it `done` (2026-06-19).
- **Executor pending (decision required)**: domedog *receives* capability-matched tasks, but
no persistent execution loop runs yet — until one does, routed tasks sit `started` (no
lease/reaper). Decide what executes (Claude Code worker / script) and with what authority
before relying on autonomous domedog task completion.
### debby (Hermes orchestrator + Zot) — probed 2026-06-17 by Hermes ### debby (Hermes orchestrator + Zot) — probed 2026-06-17 by Hermes
- **Identity**: hostname `debby`, Tailscale `100.66.193.10` - **Identity**: hostname `debby`, Tailscale `${DEBBY_TS_IP}`
- **OS**: Debian 13 (Trixie), kernel `6.12.90+deb13.1-amd64`, bare metal (KDE Plasma desktop) - **OS**: Debian 13 (Trixie), kernel `6.12.90+deb13.1-amd64`, bare metal (KDE Plasma desktop)
- **CPU**: AMD Ryzen 7 5700U with Radeon Graphics, 8 physical cores, 16 threads - **CPU**: AMD Ryzen 7 5700U with Radeon Graphics, 8 physical cores, 16 threads
- **Memory**: 15 GiB RAM, 15 GiB swap - **Memory**: 15 GiB RAM, 15 GiB swap
@ -103,20 +125,20 @@ host that fails. What you guess will be wrong; what you probe will be right.
- **Containers**: Docker 29.5.3 installed (daemon not currently running) - **Containers**: Docker 29.5.3 installed (daemon not currently running)
- **Hermes Agent**: v0.16.0 (upstream f9c8d95e), DeepSeek v4 Pro primary provider, OpenRouter for vision/fallback, Z.AI/GLM available - **Hermes Agent**: v0.16.0 (upstream f9c8d95e), DeepSeek v4 Pro primary provider, OpenRouter for vision/fallback, Z.AI/GLM available
- **Zot RPC**: Go binary at `~/.local/bin/zot`, GLM-5.1 model - **Zot RPC**: Go binary at `~/.local/bin/zot`, GLM-5.1 model
- **Telegram**: @hermes_samob_bot + @zot_samob_bot in "My Debby" group - **Telegram**: ${HERMES_BOT} + ${ZOT_BOT} in "My Debby" group
- **Layered soul**: commit `817624c`, 6 curated memories, 9 cross-harness skills - **Layered soul**: commit `817624c`, 6 curated memories, 9 cross-harness skills
### osa (FreeBSD: Mevy + hermes-osa + Codex) — probed 2026-06-17 by hermes-osa ### osa (FreeBSD: Mevy + hermes-osa + Codex) — probed 2026-06-17 by hermes-osa
- **Identity**: hostname `osa.smilepowered.org`, Tailscale `100.72.229.63` - **Identity**: hostname `osa.smilepowered.org`, Tailscale `${OSA_TS_IP}`
- **OS**: FreeBSD `15.0-RELEASE-p10`, kernel `FreeBSD osa.smilepowered.org 15.0-RELEASE-p10 FreeBSD 15.0-RELEASE-p10 releng/15.0-n281064-98258a339269 GENERIC amd64` - **OS**: FreeBSD `15.0-RELEASE-p10`, kernel `FreeBSD osa.smilepowered.org 15.0-RELEASE-p10 FreeBSD 15.0-RELEASE-p10 releng/15.0-n281064-98258a339269 GENERIC amd64`
- **CPU**: Intel Core Processor (Haswell, no TSX), 6 vCPU - **CPU**: Intel Core Processor (Haswell, no TSX), 6 vCPU
- **Memory**: 11 GiB RAM - **Memory**: 11 GiB RAM
- **Storage**: ZFS pool `zroot`, 98.5G ONLINE, 23.4G available - **Storage**: ZFS pool `zroot`, 98.5G ONLINE, 23.4G available
- **Jails**: `cms` and `worker` (Bastille jails); Docker not installed - **Jails**: `cms` and `worker` (Bastille jails); Docker not installed
- **Agents on host**: - **Agents on host**:
- **hermes-osa** — Hermes Agent v0.16.0 (`hermes-bsd` clean-room MIT fork), FreeBSD local CLI runtime + Telegram gateway. **Status: LIVE — validated local chat + Telegram.** Default provider: DeepSeek direct (`provider: deepseek`, `default: deepseek-chat`). OpenRouter available as fallback/manual lane. Telegram/gateway: LIVE — @zleht_bot (Mevy/Žleht), polling mode, tmux session `hermes-gateway` on osa. Daemon/rc.d: deferred (Track A). - **hermes-osa** — Hermes Agent v0.16.0 (`hermes-bsd` clean-room MIT fork), FreeBSD local CLI runtime + Telegram gateway. **Status: LIVE — validated local chat + Telegram.** Default provider: DeepSeek direct (`provider: deepseek`, `default: deepseek-chat`). OpenRouter available as fallback/manual lane. Telegram/gateway: LIVE — ${HERMES_OSA_BOT} (Mevy/OSA-bot), polling mode, tmux session `hermes-gateway` on osa. Daemon/rc.d: deferred (Track A).
- **Mevy**@zleht_bot (Žleht) — now consolidated under hermes-osa gateway. Token migrated from old backup .env. - **Mevy**${HERMES_OSA_BOT} (OSA-bot) — now consolidated under hermes-osa gateway. Token migrated from old backup .env.
- **Codex**`codex-cli 0.117.0`, ISO builds and validation. Runs in a Bastille jail. - **Codex**`codex-cli 0.117.0`, ISO builds and validation. Runs in a Bastille jail.
- **Claude Code** — installed (path: `/home/clawdie/.npm-global/bin/claude`), no dedicated role yet. - **Claude Code** — installed (path: `/home/clawdie/.npm-global/bin/claude`), no dedicated role yet.
- **Provider stack** (hermes-osa): - **Provider stack** (hermes-osa):
@ -126,7 +148,7 @@ host that fails. What you guess will be wrong; what you probe will be right.
fallback: openrouter # available manually, not auto-fallback configured yet fallback: openrouter # available manually, not auto-fallback configured yet
``` ```
- **Z.AI**: deferred (not configured for hermes-osa; available via OpenRouter if needed) - **Z.AI**: deferred (not configured for hermes-osa; available via OpenRouter if needed)
- **Telegram**: LIVE — @zleht_bot, polling mode, connected 2026-06-17 - **Telegram**: LIVE — ${HERMES_OSA_BOT}, polling mode, connected 2026-06-17
- **Gateway**: LIVE — running in tmux session `hermes-gateway`, manual start (no rc.d yet) - **Gateway**: LIVE — running in tmux session `hermes-gateway`, manual start (no rc.d yet)
- **Launch command**: - **Launch command**:
```sh ```sh

View file

@ -73,7 +73,7 @@ that board**, not to wire the two Hermes mouth-to-mouth.
`colibri_daemon` rc.d). `colibri-mcp socket-path` prints the resolved path. `colibri_daemon` rc.d). `colibri-mcp socket-path` prints the resolved path.
**Cross-host reach is already solved** — `colibri-mcp` connects to a daemon socket; a **Cross-host reach is already solved** — `colibri-mcp` connects to a daemon socket; a
*remote* daemon is reached via the `socat` bridge on `100.72.229.63:9190` (Tailscale-only; *remote* daemon is reached via the `socat` bridge on `${OSA_TS_IP}:9190` (Tailscale-only;
see CAPABILITY-ROUTING `[LIVE] Cross-host topology`). osa-local instances just use the see CAPABILITY-ROUTING `[LIVE] Cross-host topology`). osa-local instances just use the
local socket. local socket.

12
fleet.env.example Normal file
View file

@ -0,0 +1,12 @@
# Copy to fleet.env (gitignored) and fill in. Docs reference these variable names.
# IPs are also discoverable live with `tailscale status`; handles live in Vaultwarden.
# Tailscale IPs
DEBBY_TS_IP=100.x.y.z
DOMEDOG_TS_IP=100.x.y.z
OSA_TS_IP=100.x.y.z
# Telegram bot handles
HERMES_BOT=@your_hermes_bot
ZOT_BOT=@your_zot_bot
HERMES_OSA_BOT=@your_hermes_osa_bot

View file

@ -14,7 +14,7 @@
| Group | Platform | Members | Purpose | | Group | Platform | Members | Purpose |
| ------------ | -------- | ---------------------------------- | ------------------------------------------ | | ------------ | -------- | ---------------------------------- | ------------------------------------------ |
| "My Debby" | Telegram | @hermes_samob_bot + @zot_samob_bot | Orchestration + coding delegation | | "My Debby" | Telegram | ${HERMES_BOT} + ${ZOT_BOT} | Orchestration + coding delegation |
| FreeBSD side | OSA host | Codex + hermes-osa | ISO builds, validation, native FreeBSD ops | | FreeBSD side | OSA host | Codex + hermes-osa | ISO builds, validation, native FreeBSD ops |
## Harness matrix ## Harness matrix

View file

@ -4,8 +4,8 @@ All Clawdie hosts communicate over Tailscale with zero public exposure.
| Host | Tailscale IP | OS | Role | | Host | Tailscale IP | OS | Role |
| ------- | -------------- | ---------- | -------------------------------------- | | ------- | -------------- | ---------- | -------------------------------------- |
| debby | 100.66.193.10 | Debian 13 | Primary dev machine, Hermes agent home | | debby | ${DEBBY_TS_IP} | Debian 13 | Primary dev machine, Hermes agent home |
| domedog | 100.103.255.41 | Linux | Claude agent, secondary builder | | domedog | ${DOMEDOG_TS_IP} | Linux | Claude agent, secondary builder |
| osa | 100.72.229.63 | FreeBSD 15 | FreeBSD validation, ISO builder | | osa | ${OSA_TS_IP} | FreeBSD 15 | FreeBSD validation, ISO builder |
SSH between hosts uses Tailscale IPs, never public IPs. Each host has its own SSH key. No key sharing between hosts. Config in `~/.ssh/config` with `HostName` pointing to Tailscale IPs and `IdentitiesOnly yes`. SSH between hosts uses Tailscale IPs, never public IPs. Each host has its own SSH key. No key sharing between hosts. Config in `~/.ssh/config` with `HostName` pointing to Tailscale IPs and `IdentitiesOnly yes`.

View file

@ -145,7 +145,7 @@ If sshd is configured with `ListenAddress <tailscale-ip>`, it will fail at
boot because Tailscale hasn't connected yet (the IP doesn't exist). Symptom: boot because Tailscale hasn't connected yet (the IP doesn't exist). Symptom:
``` ```
sshd[1203]: error: Bind to port 22 on 100.66.193.10 failed: Cannot assign requested address. sshd[1203]: error: Bind to port 22 on ${DEBBY_TS_IP} failed: Cannot assign requested address.
sshd[1203]: fatal: Cannot bind any address. sshd[1203]: fatal: Cannot bind any address.
``` ```
@ -184,8 +184,8 @@ Bidirectional validation on Tailscale WireGuard:
| From | To | User | Key | Result | | From | To | User | Key | Result |
| ------------------------ | ------------------------ | -------- | ------------ | -------------------------------------------------- | | ------------------------ | ------------------------ | -------- | ------------ | -------------------------------------------------- |
| debby (100.66.193.10) | domedog (100.103.255.41) | clawdija | id_123kupola | Install → server start → attach → detach → persist | | debby (${DEBBY_TS_IP}) | domedog (${DOMEDOG_TS_IP}) | clawdija | id_123kupola | Install → server start → attach → detach → persist |
| domedog (100.103.255.41) | debby (100.66.193.10) | samob | id_infra | Install → server start → attach → detach → persist | | domedog (${DOMEDOG_TS_IP}) | debby (${DEBBY_TS_IP}) | samob | id_infra | Install → server start → attach → detach → persist |
Report: `docs/internal/sessions/2026-05-27-herdr-tailscale-remote-smoke.md` Report: `docs/internal/sessions/2026-05-27-herdr-tailscale-remote-smoke.md`

View file

@ -8,7 +8,7 @@ Add to `~/.ssh/config`. Replace bracketed values with actuals.
# ── Tailscale-only Herdr remote targets ── # ── Tailscale-only Herdr remote targets ──
Host <host>-ts-herdr Host <host>-ts-herdr
HostName <tailscale-ip> # e.g. 100.103.255.41 HostName <tailscale-ip> # e.g. ${DOMEDOG_TS_IP}
User <ssh-user> # e.g. clawdija User <ssh-user> # e.g. clawdija
IdentityFile ~/.ssh/<key> # e.g. ~/.ssh/id_123kupola IdentityFile ~/.ssh/<key> # e.g. ~/.ssh/id_123kupola
IdentitiesOnly yes IdentitiesOnly yes
@ -33,7 +33,7 @@ On debby (`~/.ssh/config`):
``` ```
Host domedog-ts-herdr Host domedog-ts-herdr
HostName 100.103.255.41 HostName ${DOMEDOG_TS_IP}
User clawdija User clawdija
IdentityFile ~/.ssh/id_123kupola IdentityFile ~/.ssh/id_123kupola
IdentitiesOnly yes IdentitiesOnly yes
@ -42,7 +42,7 @@ Host domedog-ts-herdr
ForwardAgent no ForwardAgent no
Host debby-ts-herdr Host debby-ts-herdr
HostName 100.66.193.10 HostName ${DEBBY_TS_IP}
User samob User samob
IdentityFile ~/.ssh/id_123kupola IdentityFile ~/.ssh/id_123kupola
IdentitiesOnly yes IdentitiesOnly yes
@ -57,7 +57,7 @@ On domedog (`~/.ssh/config`). Note: domedog uses `id_infra`, NOT `id_123kupola`:
``` ```
Host debby-ts-herdr Host debby-ts-herdr
HostName 100.66.193.10 HostName ${DEBBY_TS_IP}
User samob User samob
IdentityFile ~/.ssh/id_infra IdentityFile ~/.ssh/id_infra
IdentitiesOnly yes IdentitiesOnly yes

View file

@ -81,7 +81,7 @@ LOG="$HOME/.local/state/hermes/net-tests/ssh-wifi-$(date +%Y%m%d-%H%M%S).log"
ss -nti '( sport = :22 or dport = :22 )' 2>/dev/null || true ss -nti '( sport = :22 or dport = :22 )' 2>/dev/null || true
GW=$(ip route show default | awk '{print $3; exit}') GW=$(ip route show default | awk '{print $3; exit}')
for target in "$GW" 1.1.1.1 100.103.255.41 100.110.184.11; do for target in "$GW" 1.1.1.1 ${DOMEDOG_TS_IP} 100.110.184.11; do
echo "--- ping $target" echo "--- ping $target"
ping -c 30 -i 0.2 "$target" 2>&1 | tail -8 || true ping -c 30 -i 0.2 "$target" 2>&1 | tail -8 || true
done done

View file

@ -43,9 +43,9 @@ This skill includes:
Copy scripts out or run them from the skill directory. Prefer setting env vars rather than hardcoding hosts: Copy scripts out or run them from the skill directory. Prefer setting env vars rather than hardcoding hosts:
```bash ```bash
WIFI_IFACE=wlp1s0 ROUTER_IP=192.168.1.1 REMOTE_IP=100.103.255.41 ./network-lag-baseline.sh ./baseline.txt WIFI_IFACE=wlp1s0 ROUTER_IP=192.168.1.1 REMOTE_IP=${DOMEDOG_TS_IP} ./network-lag-baseline.sh ./baseline.txt
WIFI_IFACE=wlp1s0 ROUTER_IP=192.168.1.1 REMOTE_IP=100.103.255.41 ./network-interference-capture.sh 90 ./before-projector WIFI_IFACE=wlp1s0 ROUTER_IP=192.168.1.1 REMOTE_IP=${DOMEDOG_TS_IP} ./network-interference-capture.sh 90 ./before-projector
WIFI_IFACE=wlp1s0 ROUTER_IP=192.168.1.1 REMOTE_IP=100.103.255.41 ./network-interference-capture.sh 90 ./after-projector WIFI_IFACE=wlp1s0 ROUTER_IP=192.168.1.1 REMOTE_IP=${DOMEDOG_TS_IP} ./network-interference-capture.sh 90 ./after-projector
``` ```
## Reporting pattern ## Reporting pattern