The story
It's 3 a.m. Your pager fires. PagerDuty says the checkout service is throwing 502s. You are one of two SREs at a small company, your co-lead is on holiday, and getting to your laptop means finding your glasses, walking downstairs, and unlocking a VPN. Before all of that, you want a first-pass answer: what dashboards look bad, what changed in the last 24 hours, what runbook applies.
Rousseau lives on the ops box in your closet. It has read-only credentials to your logging stack, read-only kubectl in one namespace, and a Slack Socket Mode connection into #incident-oncall. You tap the DM notification on your phone:
what changed in checkout in the last 24h?
Rousseau reads the git log for the checkout service repo, cross-references against your deploy log (from a bind-mounted directory), and replies:
Two changes: PR #4821 (payment retry logic, deployed 21:14 UTC) and a Helm value bump on
checkout-webat 22:03 UTC. The payment retry change is the more suspicious — it touches the same code path the current 502s originate from.
You ask:
pull the last 100 error lines from checkout-web
Rousseau runs kubectl logs -n checkout deploy/checkout-web --tail=100 --previous under its read-only kubeconfig, and pastes back the salient lines. You spot a null-pointer trace. You DM back:
revert PR #4821 in staging first — call me when it's confirmed green
Rousseau posts to #incident-oncall with a plan, opens a revert PR against staging, and pings back once staging is green. You get up and get to your laptop.
What that requires
The daemon
Rousseau runs as a rootless Podman container on the ops box:
- Provider:
bedrock— your company already has a Bedrock spend commitment; no per-user API keys required. - Transport: Slack Socket Mode — no inbound HTTP surface, WebSocket outbound only.
- State:
~/.local/share/rousseau/sessions.db, on a LUKS-encrypted disk.
Config
provider: bedrock
bedrock:
region: us-east-1
profile: rousseau-oncall
model: anthropic.claude-sonnet-4-6-20250101-v1:0
log:
level: info
format: json
state:
path: /var/lib/rousseau/sessions.db
agent:
max_iterations: 32
approver:
mode: pattern
default: deny
reason: "read-only on-call posture — ask an operator to widen the scope"
allow:
- {tool: read, match: ".*"}
- {tool: grep, match: ".*"}
- {tool: bash, match: "^\\s*\"command\":\\s*\"(kubectl get|kubectl describe|kubectl logs|git log|git diff|git show|cat|grep|rg|head|tail|wc) "}
- {tool: bash, match: "^\\s*\"command\":\\s*\"gh pr (view|list|diff) "}
- {tool: bash, match: "^\\s*\"command\":\\s*\"gh pr create --draft "} # allows opening a draft revert
deny:
- {tool: bash, match: "kubectl (delete|apply|edit|scale|rollout undo|exec)"}
- {tool: bash, match: "gh pr merge|gh pr close --delete-branch"}
slack:
app_token: xapp-<...>
bot_token: xoxb-<...>
allowlist:
- U012ABCXYZ # your Slack user ID
- U012DEFGHI # your co-lead's Slack user ID
The bind mounts
- Repo checkouts under
/workspace/repos/(read-only). - Deploy log under
/workspace/deploys/(read-only). - kubeconfig at
/home/rousseau/.kube/config— mounted read-only, service account has read-only cluster role in thecheckoutnamespace. - AWS credentials via IAM Role for Service Accounts (IRSA) if on EKS, or via a mounted
~/.aws/for on-prem.
The systemd Quadlet unit
The reference docker/rousseau-agent.container with:
ReadOnly=trueDropCapability=allNoNewPrivileges=trueRestart=on-failure
Boots on host restart. Journal available via journalctl --user -u rousseau-agent.service.
The security posture
- Slack allowlist ensures only you and your co-lead can drive the daemon. Every other DM is silently dropped.
- Pattern approver with
default: denyblocks anything outside the whitelist. If the model wants to runkubectl delete pod, it gets atool_resulterror explaining the block and reroutes to a plan document. - Read-only kubeconfig + read-only repo mounts mean the daemon cannot mutate production even if the approver failed open.
- Belt, braces, and a second belt — each layer fails safely.
What rousseau does not do here
- It does not page you. PagerDuty is the source of truth for who is on-call.
- It does not merge PRs. The approver blocks
gh pr merge. Rousseau can open a draft revert; a human still has to confirm. - It does not run
kubectl exec. Any command that could mutate cluster state is denied. - It does not learn from the incident. Cross-session recall via FTS5 means the next incident's rousseau will find keywords from tonight's session; the semantic conclusions are still the operator's job.
What you'd change under load
If two 3 a.m. pages a month become two a week:
- Consider promoting more
bashmatchers intoallowas you gain confidence. - Wire the slog output into Loki so post-mortem reviews can cite the exact tool calls rousseau made.
- Add scheduled tasks so rousseau runs a nightly digest of open incidents into your morning Slack.
Related pages
- Guides: Audit + Approval Policies — the safety lever.
- Guides: Read-only Mode — the strictest posture.
- Slack transport — Socket Mode wiring.
- Bedrock provider — auth chain.