Keryxdocs

Threat model

The full document lives in the project's design repository; this is the summary a user needs.

What Keryx is, in security terms

An in-cluster service that reads a customer's telemetry, sends it to a third-party LLM, and publishes conclusions to Slack and a web page. Three sentences generate most of the model:

  1. Keryx executes no writes. Its value proposition is a negative capability, so the interesting failures are the ones where a write becomes possible.
  2. Reading is exporting. Everything Keryx reads leaves the cluster — to the LLM provider on every model call, and to Slack as evidence excerpts. A read-only agent is still an exfiltration channel.
  3. Its inputs are attacker-influenced by design. An investigator that only ever read trusted input would not be investigating.

Trust boundaries

#BoundaryCrossing
B1Cluster API ↔ KeryxRead-only ServiceAccount, namespace-scoped
B2Telemetry ↔ the model's contextNonce-fenced evidence blocks
B3Keryx ↔ LLM providerEverything read, over the network, BYOK
B4Keryx ↔ Slack / web pageEvidence excerpts and finding prose
B5Keryx ↔ a third-party agentkeryx mcp, read-only, fenced
B6Registry / CI ↔ clusterImages and charts Flux pulls

Cluster mutation

Enforced at the RBAC layer, not the tool layer. The ServiceAccount has no write verbs; the GitHub token is read-only and fine-grained. MCP read-only flags are defense in depth — CVE-2026-46519 hit a Kubernetes MCP server whose flag was enforced at tools/list but not tools/call. A denylist test covers every mounted server's write-capable tools.

Prompt injection

Tool output is wrapped in nonce-fenced blocks the system prompt marks instruction-inert. The model calls parameterised MCP tools only — no shell, no raw kubectl, no arbitrary URLs — and ANSI/control characters are stripped.

keryx.md, the small always-loaded environment document, is treated as semi-trusted: it is cluster-resident config injected into every prompt, so a compromised repo or a careless edit would otherwise poison every investigation. It is fenced like telemetry.

The honest status. The defenses are tested: an injection suite drives the whole loop with a scripted model that obeys the injection, plus a 124-tool denylist sweep and the chart's RBAC assertions, all green in CI. Two live injection scenarios are specified and have never been run end to end against a real model. Until they have, the claim to make is "the defenses are tested" — not "the agent has been tested against injection."

Exfiltration

Redaction happens at the Slack boundary, opt-in and off by default; stored evidence stays verbatim. That asymmetry is deliberate: core-api enforces citations as verbatim substrings of stored output, and the eval harness verifies citations by re-execution, so redacting at capture would break both. The operator configures what leaves the cluster.

Never create a second incident

Keryx runs inside a cluster that is already having a bad day. Query-cost caps, a per-investigation token budget shared across retries, a wall-clock cap, and alert-storm coalescing all exist so an investigation cannot become the outage.

Residual risks we publish

The threat model carries its own register of what is designed but unbuilt, and what is claimed but unproven. The largest entry is the one above: the live injection evals. A threat model whose residual register is empty is a threat model nobody finished reading.