openclaw-ops — "Jarvis", the read-only infrastructure investigator
Jarvis is a dedicated, read-only ops bot for the Scanners platform. You @-mention it in a Slack alert thread and it investigates that alert against live AWS state plus a curated knowledge base of the platform, then posts a structured finding back in the thread. It can investigate and recommend — it can never act.
:::info Status
Live in production (ECS Fargate, eu-north-1, account 429134227608). It
runs as a 2-container service in the scanners cluster. See also
Monitoring & Alerting and
Logging & SIEM.
:::
How it works (behaviour)
On-demand, mention-gated — not auto-investigate
Jarvis does not investigate every incoming alert. It stays silent until the owner explicitly @-mentions it in a thread. Two independent gates in the Slack channel config enforce this:
allowBots: "mentions"— bot-authored messages (Grafana, AWS Chatbot) reach Jarvis only if they @-mention it, which alerts never do, so an incoming alert never auto-triggers an investigation.requireMention: trueon all five alert channels — even a human message triggers Jarvis only when it @-mentions the bot.groupPolicy: "allowlist"+allowFrom— only the owner can summon it.
When tagged, the reply is threaded under the message you tagged, and the alert
text above is readable as context (allowBots gates inbound dispatch, not
thread-history reads).
What a single investigation does
- You reply in an alert's thread:
@Jarvis what's going on here? - Jarvis treats the alert/log text as untrusted data, never instructions.
- It reads the alert metadata literally first (e.g.
DatasourceNoData≠DatasourceError), then gathers evidence with its read-only AWS tools and thecheck_lokiprobe — always confirming live state, never trusting memory. - It produces a structured report:
root_cause,severity(Sev1/2/3),confidence,evidence(specific log lines / metric deltas / a preceding deploy),suggested_fix(text only),correlated_alerts. - It posts that as a threaded reply, with secrets/PII redacted from quotes.
Its investigation discipline (symptom ≠ root cause, disconfirm before asserting,
don't diagnose what you can't observe, calibrate confidence to evidence) and its
platform knowledge base are baked into the image as workspace files (SOUL.md and
AGENTS.md).
How it was built (architecture)
Slack (Socket Mode, outbound WSS — no inbound)
│ @mention in a thread (owner only)
▼
ECS Fargate task "openclaw-ops" (cluster: scanners, desired 1)
┌──────────────────────────────────────────────────────────────┐
│ container: openclaw-ops (OpenClaw gateway) │
│ • model: AWS Bedrock EU inference profile (task-role auth) │
│ • persona + KB: SOUL.md + AGENTS.md │
│ • tools: DENY-LIST (exec/fs/automation/... denied) │
│ │ localhost HTTP (127.0.0.1:8000) │
│ ▼ │
│ container: aws-readonly-mcp (FastMCP sidecar) │
│ • 9 read-only tools (boto3 + the Loki probe) │
│ • keeps the TASK-ROLE creds natively │
└──────────────────────────────────────────────────────────────┘
│ read-only AWS APIs │ HTTP GET (read) to Loki :3100
▼ ▼
CloudWatch · Logs · ECS · Loki on the private Grafana box
RDS · ElastiCache (auth-less, in-VPC)
Key decisions:
- Official, pinned OpenClaw, fully locked down. Built
FROMthe official, pinned image (MIT). Only our MCP server + locked-down config +SOUL.md/AGENTS.mdare added; the two official plugins used (Slack channel + Amazon Bedrock provider) are installed at build time. No community/ClawHub plugins, no runtime code pulls. - Model = AWS Bedrock, no API key. Claude runs via the EU cross-region
inference profile, authenticated by the ECS task role through the AWS SDK
chain — there is no Anthropic API key to store. (Claude 4.x has no on-demand
model in
eu-north-1, so the inference profile is required.) - AWS access is ONLY the MCP sidecar. All AWS reads go through a small FastMCP server (read APIs only, log redaction). It runs as a second container over localhost HTTP — not a gateway-spawned subprocess — so it keeps the ECS task-role credentials natively.
- Tools are a deny-list.
exechard-denied, elevated off, filesystem / automation / browser / cron / gateway / nodes denied. What remains is read + web-search + the read-only MCP tools.
:::note The check_loki probe
Jarvis's AWS tools see CloudWatch/ECS/RDS/Redis but not Loki/Grafana — so it
used to guess "the log pipeline is broken" from a Grafana DatasourceNoData
alert. check_loki closes that blind spot: it hits Loki directly (/ready,
/labels, count_over_time for the alert's stream + heartbeats) and returns
reachable / ready / match_count / heartbeat_count, so Jarvis can confirm
whether Loki is down vs simply empty. The Grafana box SG allows port 3100
ingress from the openclaw-ops task SG; Jarvis has only a read (GET) tool, no push.
:::
Security model
| CAN ✅ | CANNOT ❌ |
|---|---|
| Read AWS via the read-only MCP server only | Write / modify / delete anything |
Read Loki (GET) via check_loki | Push to Loki / change Grafana |
| Search the web for knowledge | Execute code / shell |
| Post threaded Slack replies | Pull/install external or community code |
| Run only the curated investigator agent | Touch filesystem / cron / browser / gateway / nodes |
Two enforcement layers: the OpenClaw config and a dedicated read-only IAM task role (allow-list + explicit Deny) — the hard backstop that physically denies every write even if an alert carries attacker-controlled text. Alert text is treated as untrusted input; no action tool is ever wired in; the alert channels are private and quoted excerpts are redacted before they leave the bot.
The 9 read-only tools
list_alarms, get_metric_stats, query_logs, list_log_groups,
describe_services, service_events, describe_rds, describe_redis, and
check_loki. Every function calls only read APIs, and the IAM role denies
writes regardless.
How to use it
As an operator (the normal path): an alert lands in a #alerts-* channel and
Jarvis stays silent. Reply in that alert's thread, mentioning the bot —
@Jarvis is this real? what's the root cause? — and it investigates and replies in
the thread. Only the owner can trigger it.
Read Grafana / Loki yourself: the box has no inbound path; tunnel in via AWS
SSM Session Manager port-forwarding (port 3000 for Grafana, 3100 for ad-hoc
Loki) — see Monitoring & Alerting.
What's deferred (deliberately)
- Sev1 → WhatsApp paging — disabled for now (needs a paired WhatsApp session); on-call number on file. Enable in a follow-up if paging is wanted.
- Vector-RAG + growing incident-history store — not built and not needed
pre-launch: the curated
SOUL.md+AGENTS.mdknowledge base is sufficient at current scale, and an incident-write path would conflict with the read-only guarantee. Revisit once there is real incident volume.