From 12f11a49967fe59e3bf71ff2259e546695f3b50e Mon Sep 17 00:00:00 2001
From: Renn F
Date: Thu, 18 Jun 2026 18:47:03 +0200
Subject: [PATCH] =?UTF-8?q?docs(grok):=20drop=20the=20security=20disclaime?=
=?UTF-8?q?rs=20=E2=80=94=20injection=20guard=20closes=20the=20gap?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
With the prompt-injection guard now recreated for Grok (prior commit), the
"Grok lacks the injection guard / prefer Claude for delivery roles" warning is
no longer true, so remove it:
- Panel routing card: replace the amber "prefer Claude / not safe" caveat with
a neutral one-liner — Grok agents run on opencode; the command/secret-exfil
guard, the prompt-injection guard, and the cost cap all apply.
- docs/self/architecture/llm-provider-security.md: prompt-injection row flips to
"yes" for Grok; intro + routing recommendation updated to "effective security
parity, any agent (incl. delivery roles) can run on Grok"; the only remaining
unported hook is the non-security stop-guard.
- opencode_config docstring: the remaining gap is now just the stop-guard
(budget + injection are covered).
Panel tsc + eslint clean.
---
.../architecture/llm-provider-security.md | 16 ++++++----------
.../components/settings/ai-routing-card.tsx | 15 ++++-----------
roboco/llm/providers/opencode_config.py | 19 ++++++++++---------
3 files changed, 20 insertions(+), 30 deletions(-)
diff --git a/docs/self/architecture/llm-provider-security.md b/docs/self/architecture/llm-provider-security.md
index 6832ce8e..c92dcd1b 100644
--- a/docs/self/architecture/llm-provider-security.md
+++ b/docs/self/architecture/llm-provider-security.md
@@ -1,6 +1,6 @@
# LLM provider security posture
-RoboCo routes each agent to one of several LLM providers (the **Routing** card in the control panel). This note states — truthfully — what protections an agent gets on each provider, so the choice is made knowingly. The short version: **Grok agents do not have full guardrail parity with the rest, but they are still usable.** Use them for trusted work; prefer a Claude-Code-runtime provider for agents that ingest untrusted or cross-agent content.
+RoboCo routes each agent to one of several LLM providers (the **Routing** card in the control panel). This note states — truthfully — what protections an agent gets on each provider. The short version: **Grok now reaches effective security parity** — the command/secret-exfiltration guard, the budget/cost cap, and the prompt-injection guard all apply to Grok agents. The only Claude hook without an opencode equivalent is the stop-guard (terminal-verb enforcement), which is a workflow nicety, not a safety control. Any agent — including the delivery roles — can be routed to Grok.
## Two runtimes, not five
@@ -22,17 +22,13 @@ Anthropic, Ollama, and Self-Hosted all run on **Claude Code** and therefore keep
| MCP gateway + role tool-manifest | yes | yes (mounted by construction) |
| Command / secret-exfiltration guard (bash, credential files, internal-host calls, PAT exfil) | yes (`bash-guard-hook.sh`, PreToolUse) | **yes** — ported to opencode as the `secret-scrub.js` plugin (`tool.execute.before`) |
| Budget / runaway-cost kill-switch | yes (`post-tool-budget` hook against the SDK server) | **yes** — orchestrator-side cost watchdog (`ROBOCO_GROK_MAX_COST_USD`) reading the opencode store |
-| Prompt-injection guard (rejects "ignore previous instructions", role-override, fake escalations in incoming A2A / task / notification content) | yes (`user-prompt-hook.sh`, UserPromptSubmit, denies the turn) | **no** — opencode's incoming-message hook (`message.updated`) is observe-only and cannot block a turn before the model reads it |
-| Stop-guard (terminal-verb enforcement before a run ends) | yes (`stop-hook.sh`, Stop) | **no** — opencode's stop/idle hooks are observe-only |
+| Prompt-injection guard (rejects "ignore previous instructions", role-override, fake escalations in incoming A2A / task / notification content) | yes (`user-prompt-hook.sh`, UserPromptSubmit, denies the turn) | **yes** — recreated at RoboCo's input boundary (`prompt_guard.detect_injection`): the interactive driver scans every turn, the one-shot grok entrypoint scans the task prompt. Same patterns as the bash hook, kept in sync. opencode's lack of a blocking pre-prompt hook is irrelevant — we deny in our own code before calling the model |
+| Stop-guard (terminal-verb enforcement before a run ends) | yes (`stop-hook.sh`, Stop) | **no** — opencode's stop/idle hooks are observe-only (workflow nicety, not a security control) |
-## Why the two gaps exist (not a defer — a runtime limitation)
+## The remaining gap: the stop-guard
-opencode's plugin API exposes only observe-only events for incoming messages and session-stop, and **no token/usage hook at all**. So the prompt-injection and stop-guard hooks — both of which must *block* an action — have no faithful opencode equivalent today. Closing them would require an upstream opencode feature (a blocking message/stop hook). The budget guard was movable to the orchestrator (it reads the opencode cost store), which is why Grok keeps budget parity but not injection/stop parity.
+Every *security-relevant* Claude guard now applies to Grok — command/secret-exfiltration (`secret-scrub.js`), budget/runaway-cost (orchestrator cost watchdog), and prompt-injection (`prompt_guard`, recreated at the input boundary). The one Claude hook without an opencode equivalent is the **stop-guard** (it enforces that an agent calls a terminal MCP verb before a run ends), because opencode's session-stop events are observe-only. This is a workflow-completion guard, not a safety control: a Grok agent that ends without a terminal verb is recovered by the orchestrator reaper / idle watchdog, not left in a dangerous state.
## What this means for routing
-- **Grok is safe for trusted, self-contained work** — and for the interactive intake/secretary roles, whose input comes directly from the CEO (a small injection surface).
-- **The real injection exposure is the delivery roles** (developer / qa / pm / documenter), which routinely ingest *other agents'* and external content as data. The prompt-injection guard is what stops a poisoned A2A message or task description from steering them off-task; on Grok that guard is absent.
-- **Recommendation:** route delivery agents that handle untrusted or cross-agent content to a Claude-Code-runtime provider (Anthropic / Ollama / Self-Hosted). Route Grok where the content is trusted, or accept the reduced posture knowingly. The command/secret-exfiltration guard — the one that prevents actual credential leakage — *is* present on Grok, so the gap is about being socially-engineered off-task, not about secret exfiltration.
-
-This is the honest claim: **not full parity, still usable.** The control panel's Routing card surfaces a short version of this when Grok or Mix mode is selected.
+Grok is safe to route any agent to, **including the delivery roles** that ingest cross-agent / external content: the prompt-injection guard rejects a poisoned A2A message or task prompt before the model sees it (interactive turns in the driver, the one-shot task prompt in the entrypoint), and the secret-exfiltration guard blocks credential reads / internal-host calls. The only behavioural difference from a Claude-Code-runtime provider is the stop-guard noted above. So security parity is effectively reached; the remaining difference is non-security.
diff --git a/panel/src/components/settings/ai-routing-card.tsx b/panel/src/components/settings/ai-routing-card.tsx
index c5e9bafe..d73eae0d 100644
--- a/panel/src/components/settings/ai-routing-card.tsx
+++ b/panel/src/components/settings/ai-routing-card.tsx
@@ -492,17 +492,10 @@ export function AIRoutingCard() {
) : null}
{currentMode === "grok" || currentMode === "mix" ? (
-
-
-
- Grok agents run on the opencode runtime, not Claude Code: the
- command / secret-exfiltration guard and the cost cap apply, but
- the prompt-injection guard does not (opencode cannot block a
- turn before the model reads it). Anthropic, Ollama and
- Self-Hosted all run through Claude Code with the full guard set
- — prefer them for agents that ingest untrusted or cross-agent
- content. Grok is safe for trusted work.
-
+
+ Grok agents run on the opencode runtime; the command /
+ secret-exfiltration guard, the prompt-injection guard, and the
+ per-agent cost cap all apply.
) : null}
diff --git a/roboco/llm/providers/opencode_config.py b/roboco/llm/providers/opencode_config.py
index d6db7e70..76f3d517 100644
--- a/roboco/llm/providers/opencode_config.py
+++ b/roboco/llm/providers/opencode_config.py
@@ -20,15 +20,16 @@ Config shape per opencode docs (https://opencode.ai/docs/config):
idle stream hangs the parent run with no recovery (observed live on a PR
review). The request/stream timeouts below are the defence-in-depth backstop.
-GUARDRAIL PARITY: the bash-guard (PAT-scrub) deny rules ARE ported to opencode
-via the ``secret-scrub.js`` plugin (``tool.execute.before``), and token
-usage/cost IS captured from opencode's SQLite store at finalize. The remaining
-gap vs Claude Code is the budget / loop-detector / stop-guard / prompt-injection
-hooks — they fire against the SDK ``:9000`` server and have no opencode
-equivalent yet; closing them needs an opencode plugin that POSTs to a sidecar
-(a tracked follow-up / open decision). ``bash`` permission stays operator-tunable
-(``ROBOCO_GROK_BASH_PERMISSION``) so a deployment can fail closed
-(``deny``/``ask``) meanwhile.
+GUARDRAIL PARITY: the bash-guard (PAT-scrub) is ported via ``secret-scrub.js``
+(``tool.execute.before``); usage/cost is captured from opencode's SQLite store at
+finalize and bounded by the orchestrator cost watchdog
+(``ROBOCO_GROK_MAX_COST_USD``, which also catches runaway-loop burn); and the
+prompt-injection guard is recreated at RoboCo's input boundary
+(``roboco.agent_sdk.prompt_guard`` — the driver scans interactive turns, the
+entrypoint scans the one-shot task prompt). The only Claude hook without an
+opencode equivalent is the stop-guard (terminal-verb enforcement; opencode's
+stop events are observe-only) — a workflow nicety, not a security control.
+``bash`` permission stays operator-tunable (``ROBOCO_GROK_BASH_PERMISSION``).
"""
from __future__ import annotations