Episode 11: a read-only agent that reads the security logs, one agent vs a crew of three

New: ops-readonly, the read-only twin of the episode 9 ops-actions runner
(fixed question list, three read-only mounts, no docker socket, read_only,
cap_drop ALL). Four role skills in agents/skills/, loaded at run time from
this repo's Forgejo. Two workflows: E (one agent, seven tools, one skill) and
F (three agents, one skill each, the workflow is the orchestrator).

Measured over 20 runs: no run of either variant produced all numbers and
lists correct. The solo agent confuses similar counts and skips a tool; the
crew calls every tool but drops fields at each role boundary. Sharpening the
skill files did not help the crew, it made it worse. Logs are attacker text:
an instruction in a request path is carried into the report as data, not
obeyed, but carried.

Claude-Session: https://claude.ai/code/session_017iP581CYtPfxdZYPPAyWZA
This commit is contained in:
Maximilian Grimm 2026-09-13 10:01:13 +02:00
parent bed41cfaf3
commit d147f21490
13 changed files with 2088 additions and 0 deletions

View file

@ -286,6 +286,30 @@ services:
command: ["apk add --no-cache python3 >/dev/null && exec python3 -u /runner.py"]
networks: [proxy]
# The read-only twin of ops-actions (episode 11). Behind the security report
# agents: three read-only mounts, a fixed list of questions, no docker
# socket, no shell. The agents get answers to these questions and nothing
# else. Token comes from ops-readonly/token.env (see token.env.example), so
# .env does not grow another secret.
#
# Start it with: docker compose --profile agent up -d
ops-readonly:
build: ./ops-readonly
image: selfhostedops/ops-readonly:ep11
profiles: [agent]
restart: unless-stopped
read_only: true
cap_drop: [ALL]
security_opt: ["no-new-privileges:true"]
env_file:
- path: ./ops-readonly/token.env
required: false
volumes:
- ./logs/caddy:/logs/caddy:ro
- /var/log/journal:/var/log/journal:ro
- /var/run/fail2ban:/run/fail2ban:ro
networks: [proxy]
# --- CI (episode 2) -- start with: docker compose --profile ci up -d ---
docker-in-docker: