?

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-web at 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 the checkout namespace.
  • 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=true
  • DropCapability=all
  • NoNewPrivileges=true
  • Restart=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: deny blocks anything outside the whitelist. If the model wants to run kubectl delete pod, it gets a tool_result error 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 bash matchers into allow as 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

Type to search 150+ pages. Ranking: BM25 with title/description boost.