From adc2c04a1e33ac953ef698b83a1b17ca471de68a Mon Sep 17 00:00:00 2001 From: Sam & Claude Date: Sat, 20 Jun 2026 09:05:30 +0200 Subject: [PATCH] docs(vault): use local test agent in first-proof runbook (Sam & Pi) Correct the raw socket spawn example to use the bootstrapped colibri-test-agent in the jail instead of the default remote-provider agent binary. Use a harmless FIRST_PROOF_KEY item for the throwaway collection.\n\nAlso convert packaging/freebsd/colibri-agent-loop.md to normal Markdown so the repository formatting gate passes.\n\nChecks: ./scripts/check-format.sh; git diff --check --- docs/VAULT-PROVISION-FIRST-PROOF.md | 17 +++-- packaging/freebsd/colibri-agent-loop.md | 93 ++++++++++++++----------- 2 files changed, 64 insertions(+), 46 deletions(-) diff --git a/docs/VAULT-PROVISION-FIRST-PROOF.md b/docs/VAULT-PROVISION-FIRST-PROOF.md index 72e1629..83cbb10 100644 --- a/docs/VAULT-PROVISION-FIRST-PROOF.md +++ b/docs/VAULT-PROVISION-FIRST-PROOF.md @@ -57,12 +57,13 @@ sudo agent-jail-bootstrap.sh "$T" # runtime pkgs + colibri binar ## Step 2 — test collection in Vaultwarden In the web UI, create a **Collection named exactly `$T`**, and add one **Login** item: -- **Name** = an env var, e.g. `DEEPSEEK_API_KEY` + +- **Name** = a harmless env var, e.g. `FIRST_PROOF_KEY` - **Password field** = a throwaway value (this validates the name-based contract) The bootstrap account must have read access to that collection. -## Step 3 — register the tenant *(interim: manual SQLite — gap #101)* +## Step 3 — register the tenant _(interim: manual SQLite — gap #101)_ ```sh T=proof0 @@ -79,19 +80,21 @@ sudo sqlite3 "$DB" "INSERT INTO tenants (it's vestigial; see #88/#93). - SQLite runs in WAL, so this insert is safe while the daemon is up. -## Step 4 — trigger a jailed spawn *(interim: raw socket JSON — gap #102)* +## Step 4 — trigger a jailed spawn _(interim: raw socket JSON — gap #102)_ ```sh T=proof0 SOCK=/var/run/colibri/colibri.sock printf '%s\n' \ - '{"cmd":"spawn-agent","provider":"deepseek","model":"deepseek-chat","jail":{"name":"'"$T"'","root_path":"/usr/local/bastille/jails/'"$T"'/root"}}' \ + '{"cmd":"spawn-agent","provider":"local","model":"/usr/local/bin/colibri-test-agent","local_args":["--session-id","'"$T"'-proof","--step-ms","10","--hold-secs","1"],"jail":{"name":"'"$T"'","root_path":"/usr/local/bastille/jails/'"$T"'/root"}}' \ | sudo nc -U "$SOCK" ``` +- `provider: "local"` uses the `colibri-test-agent` binary copied into the jail by + `agent-jail-bootstrap.sh`, so the proof does not depend on provider API keys or a + separate `COLIBRI_AGENT_BINARY` being present in the jail. - `jail.name` → `jexec` into the existing jail; `jail.root_path` → where the hook writes - `.env`. Use a `provider`/`model` your daemon accepts; the provision hook fires as part - of the spawn. + `.env`. The provision hook fires after the local test agent spawns successfully. - (Equivalent: send the same JSON line with the Python raw-socket helper used by the poller.) @@ -101,7 +104,7 @@ printf '%s\n' \ T=proof0; DB=/var/db/colibri/colibri.sqlite; R=/usr/local/bastille/jails/$T/root # daemon log shows: "provisioning tenant env from vault" then "vault provision complete" sudo stat -f '%Sp %N' "$R/.env" # expect -rw------- (0600) -sudo grep -c '^DEEPSEEK_API_KEY=' "$R/.env" # expect 1 (value not printed) +sudo grep -c '^FIRST_PROOF_KEY=' "$R/.env" # expect 1 (value not printed) sudo sqlite3 "$DB" "SELECT tenant_id,status FROM tenants WHERE tenant_id='$T';" # expect active ``` diff --git a/packaging/freebsd/colibri-agent-loop.md b/packaging/freebsd/colibri-agent-loop.md index 4dc6b2c..359c2c7 100644 --- a/packaging/freebsd/colibri-agent-loop.md +++ b/packaging/freebsd/colibri-agent-loop.md @@ -1,40 +1,55 @@ # Colibri Agent Loop — Hermes Cronjob Configuration -# -# The 2min/5min polling cadence runs inside Hermes' internal scheduler, -# NOT FreeBSD cron(8). These jobs are managed with: -# -# hermes cronjob list # view all jobs -# hermes cronjob pause # pause a job -# hermes cronjob resume # resume a job -# -# Install (run inside a Hermes CLI session): -# -# # 1. Poller — script-only, silent when no tasks found -# hermes cronjob create colibri-poller \ -# --script colibri_poll.sh \ -# --schedule "every 2m" \ -# --no-agent -# -# # 2. Worker — Hermes acts on poller output via context_from -# hermes cronjob create colibri-worker \ -# --prompt "You are the Colibri task worker for this host..." \ -# --context-from colibri-poller \ -# --schedule "every 5m" \ -# --deliver origin -# -# On OSA, the poller wrapper script sets: -# COLIBRI_AGENT_ID="2bffa478-ef23-4777-a618-23dc5446ba69" -# COLIBRI_SOCKET="/var/run/colibri/colibri.sock" -# -# On Debby, the poller wrapper sets: -# COLIBRI_AGENT_ID="38840b37-261f-45bf-bd01-5ec51f392198" -# COLIBRI_SOCKET="/tmp/osa-colibri.sock" (socat bridge) -# -# The poller scripts live in: -# ~/.hermes/scripts/colibri_poll.sh (host-specific wrapper) -# ~/.hermes/scripts/colibri_poll.py (generic, portable) -# ~/.hermes/scripts/colibri_task_done.py (task completion) -# -# Copies for reference also live in this repo at: -# scripts/colibri_poll.py -# scripts/colibri_task_done.py + +The 2min/5min polling cadence runs inside Hermes' internal scheduler, **not** FreeBSD cron(8). These jobs are managed with: + +```sh +hermes cronjob list # view all jobs +hermes cronjob pause # pause a job +hermes cronjob resume # resume a job +``` + +Install by running these inside a Hermes CLI session: + +```sh +# 1. Poller — script-only, silent when no tasks found +hermes cronjob create colibri-poller \ + --script colibri_poll.sh \ + --schedule "every 2m" \ + --no-agent + +# 2. Worker — Hermes acts on poller output via context_from +hermes cronjob create colibri-worker \ + --prompt "You are the Colibri task worker for this host..." \ + --context-from colibri-poller \ + --schedule "every 5m" \ + --deliver origin +``` + +On OSA, the poller wrapper script sets: + +```sh +COLIBRI_AGENT_ID="2bffa478-ef23-4777-a618-23dc5446ba69" +COLIBRI_SOCKET="/var/run/colibri/colibri.sock" +``` + +On Debby, the poller wrapper sets: + +```sh +COLIBRI_AGENT_ID="38840b37-261f-45bf-bd01-5ec51f392198" +COLIBRI_SOCKET="/tmp/osa-colibri.sock" # socat bridge +``` + +The poller scripts live in: + +```text +~/.hermes/scripts/colibri_poll.sh (host-specific wrapper) +~/.hermes/scripts/colibri_poll.py (generic, portable) +~/.hermes/scripts/colibri_task_done.py (task completion) +``` + +Copies for reference also live in this repo at: + +```text +scripts/colibri_poll.py +scripts/colibri_task_done.py +``` -- 2.45.3