docs(vault): use local test agent in first-proof runbook (Sam & Pi) #105

Merged
clawdie merged 1 commit from fix/vault-first-proof-runbook-local-agent into main 2026-06-20 09:07:02 +02:00
2 changed files with 64 additions and 46 deletions

View file

@ -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
```

View file

@ -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 <job_id> # pause a job
# hermes cronjob resume <job_id> # 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 <job_id> # pause a job
hermes cronjob resume <job_id> # 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
```