Skip to main content

Detections

Concepts

AIControls surfaces session anomalies (configured behavioral thresholds), attack pattern matches (ordered sequences of actions), and baseline alerts (deviations from learned historical activity). Use Security Events to investigate findings and follow links to available session, identity, or workload evidence.

Session anomalies​

Session anomaly checks run asynchronously after completed LLM requests with reported token usage. They evaluate the recent activity observed by that replica against configured thresholds; they do not delay or reverse the request that triggered the check. When a check fires:

  1. AIControls attempts to store a session anomaly event with the triggering metric and observed value. Persistence is best-effort; storage errors or timeouts can prevent the event from appearing.
  2. A notification can be delivered through the configured channels (Slack, email, in-app).
  3. If auto-pause is enabled and the cumulative anomaly count for the session meets or exceeds the pause threshold, the session is paused and the developer receives a clear message with a contact-admin prompt.

Anomalies do not block requests by themselves — only auto-pause does. Run detection in observation mode and tune thresholds before committing to enforcement.

In Security Events, filter by Session anomaly and open a finding to inspect the available details and session link.

When advanced surfaces are enabled, the Session Anomalies page lists flagged sessions with each session's newest events and its total event count. Select Show all on a session to load the rest, a page at a time. The Type filter matches every event of a session, not only the ones shown.

Cumulative anomaly counts remain available to session policy context and the auto-pause threshold.

Detection thresholds​

AIControls evaluates every session against three independent behavioral thresholds — call rate, token spike, and repeated-prompt count. Each threshold targets a different failure mode:

  • Max calls per minute guards against runaway call volume — a session completing more LLM requests with reported token usage than the configured threshold on the observing replica. MCP tool calls are not counted by this producer.
  • Token spike threshold guards against unexpectedly large context payloads, such as a read_file on a multi-megabyte binary piped into a prompt.
  • Max repeated prompts can guard against prompt-loop behavior when a producer supplies prompt text. The current LLM usage producer supplies no prompt text, so this setting does not establish repetition coverage on that path.

The thresholds are independent: any one of them can fire an anomaly event without the others being crossed. Because default values are calibrated for interactive developer use rather than automated or multi-agent workloads, thresholds usually need tuning before enforcement (auto-pause) is turned on — see Configure Detection Thresholds for the concrete settings and steps.

You can disable session anomaly and context-waste findings in Settings → Security & Privacy → Detection & Response → Session behavior & efficiency. This retains history and policy context measurements and does not disable independent policies, baseline detection, or attack-pattern/Judge behavior. Saved configuration and the serving replica's effective state are shown separately. Re-enabling starts fresh local comparisons while retaining cumulative anomaly counts. See Enable or disable session detection for propagation and auto-pause behavior.

Auto-pause​

By default, anomalies create alerts but never block developers. With auto-pause enabled, a session is paused once its cumulative anomaly count crosses a configurable threshold — the count requirement guards against pausing on a single transient spike. For the concrete settings and steps, see Configure Detection Thresholds.

When a session is paused, the developer's next request returns a session_paused error with instructions to contact their admin. Admins can open the linked session from Security Events and use Resume Session on the Sessions page.

warning

Enable auto-pause only after observing anomaly patterns in the feed for at least a week. Premature enablement with default thresholds can pause legitimate agentic workloads during normal operation.

Tuning for agentic workloads​

Default thresholds are calibrated for interactive developer use, not for automated agents. Agentic workloads — particularly Claude Code running in multi-agent mode, bulk file indexing, or iterative code-generation loops — routinely produce call rates, context sizes, and retry patterns that would look anomalous for a human developer but are normal for the workload. Without adjustment, these patterns trip the default thresholds and generate noisy alerts or, if auto-pause is enabled, pause sessions that are behaving correctly.

The fix is to raise the relevant threshold(s) to match the workload's real behavior rather than to disable detection outright — see Configure Detection Thresholds for recommended adjustments per workload pattern.

Attack pattern detection​

Anomaly thresholds (above) catch a single action that's unusually large or frequent. Attack pattern detection catches something different: an ordered sequence of individually unremarkable actions that only becomes dangerous in combination — a credential read followed by an outbound call to an external destination, for example. Order matters: the same two actions in reverse are not flagged.

AIControls checks activity against a small set of built-in patterns, including:

  • Sensitive read → Egress — a read against a datasource classified confidential or restricted, followed by an outbound call.
  • Undeclared upstream (Sensitive read → Write → Egress) — a sensitive read, then a write, then an egress call, consistent with data being staged before being sent out.
  • Delete → Egress — a destructive operation followed by an outbound call.
  • Bulk read → Egress — many individually low-sensitivity reads from one MCP server (the "mosaic effect"), followed by an outbound call. No single read is sensitive, but together they can assemble a sensitive dataset. Reads count toward the bulk-read threshold only when the server's datasource is classified internal or unclassified, the response reached the agent, and the read isn't already a sensitive read. The threshold is crossed when either the number of reads or their combined response size reaches its limit within the aggregation window (by default, 50 reads or 5 MB from one server within 60 minutes). Two further counts catch evasion: reads spread across several servers are also counted together for the agent (by default, 100 reads within 60 minutes), and a slow trickle of reads from one server is counted over a 24-hour window (by default, 100 reads). The pattern still requires an outbound call afterward.
  • Write → Execute — a file or config write followed by code execution.
  • Tier escalation (Privilege → Sensitive action) — a privilege-escalating action followed by a sensitive read, write, or egress.
  • Credential file opened → Ungoverned egress — a kernel-only pattern (see Kernel-sourced evidence below): a credential file opened, followed by an outbound connection, for a workload that may never have made a single call through the gateway.
  • Governed action → Undeclared egress — a governed, gateway-audited action (a read, write, or execute) followed by a kernel-observed outbound connection to a destination no governed call of that identity had named by then. Critical severity — this is the single case a kernel-only or network-only monitoring tool structurally cannot express, because it requires knowing what the agent said it would do, not just what happened at the wire.

Detection runs continuously in the background at three scopes: within a single session, across an identity's full activity history (catching an attempt deliberately split across multiple sessions to stay under a single session's radar), and — for the two kernel patterns above — per workload, so an agent that bypasses the gateway entirely and therefore has no session or identity to attach to is still caught. A match appears on the relevant session's Security tab, the identity's Safety tab, or the workload's Runtime tab, showing the matched sequence in order and its severity. When Runtime Controls isn't running, or matches aren't stored on the replica serving the page (local storage disabled), the tab shows Attack-pattern matches unavailable with the reason, rather than an empty list that would read as "nothing detected." On the workload's Runtime tab, an empty result is shown as No attack patterns detected only when kernel coverage is available; otherwise it reads No attack patterns detected in the evidence that could be checked, with the coverage reason. A new match, unless its skill or agent is exempt or a recorded untainted Judge downgrade suppresses it, also triggers a notification through your configured channels (Slack, Teams, email, in-app), the same way other baseline alerts do. Each match produces at most one in-app notification, even if delivery is retried.

Each evidence step expands to show its classification reason, timestamp, redacted request or command, response excerpt when retained, and resource or destination when known. View audit event (available to admin and viewer portal users) opens the underlying gateway event. Missing or truncated excerpts are labeled explicitly; historical matches may not have recorded a classification reason.

Kernel steps also show Kernel evidence reference when the observation identifier was retained with the match. This opaque reference identifies the recorded kernel observation; it is separate from gateway audit-event IDs and does not open a gateway audit event or the current runtime series. Historical kernel steps may have no retained reference.

A Sensitive response step means the response scanner found sensitive data, such as PII. It does not establish that a file was read. For example, a conversation summary containing an email address followed by a Git push forms an ordered pattern; it does not prove the email address was included in the pushed commits. Kernel-coverage notices and the optional LLM Judge verdict describe separate evidence and assessment capabilities.

Effective verdict shows the assessment after backend safeguards, which can differ from the Judge's recommendation. If a safeguard rejects a downgrade, the finding retains its configured action; the explanation records the override and original reasoning. A confirmed effective verdict therefore does not necessarily mean the model confirmed the attack.

Literal, recognized read-only gh pr checks and GraphQL queries are excluded from the default outbound-command signal. GitHub mutations, uploads, unknown commands and commands with unresolved shell expansions remain conservatively classified. In particular, complex shell loops and redirections may still produce this signal even when the underlying task is read-only.

Commands run through a shell tool count as steps by what they do, not only by the tool's access type, which is always execute:

  • Delete: rm, rmdir, shred, unlink, a find … -delete predicate (not a -delete passed to a command find -exec runs), or a find -exec that runs one of those programs (including through sh -c).
  • Write: an output redirection to a file (>, >>, 1>, &>, or a heredoc such as cat > script.sh <<EOF), tee with a file operand, cp, mv or install to a destination, or dd of=. Redirections to devices under /dev/… (but not /dev/shm or /dev/mqueue, which are writable storage), between file descriptors (2>&1, >&2) and > inside a quoted string don't count.
  • Privilege: sudo, su, doas, runuser or pkexec.

All three are recognized behind wrappers and their options, such as sudo -u root, env -u HOME, nohup or timeout 5; command -v only looks a name up and doesn't count. The command is read from a shell tool's command, cmd or script argument, and an MCP shell tool exposed under a server prefix (for example desktop-commander__execute_command) is treated like the built-in one. Non-shell tools that take a command argument aren't parsed this way. Separators (;, |, &) inside quotes, and $(…) inside single quotes, are text rather than commands, and sed -i/perl -i count as writes only when they name a file.

Identical ordered evidence for the same identity, severity and action produces one notification across identity and session scopes. Both scopes retain their match records. Different evidence chains, including ones that share only the first step, remain separate so cross-session attacks are not hidden. Notifications reference the triggering events without including captured request or response bodies. An active Judge assessment defers delivery. External delivery is best-effort: cancellation before a send allows retry, while a send followed by a failure to save completion may cause a bounded duplicate retry.

Every built-in pattern ships with its action set to Notify. Every non-exempt match not suppressed by a recorded untainted Judge downgrade goes to your enabled notification routes, whatever its action (subject to the consolidation above); Require approval and Deny are recorded with the match but don't hold or block the session. Each pattern also has a max gap: the longest time allowed between one step and the next. If the next step arrives later, the sequence starts over rather than counting as a match.

The category sequence each pattern matches on is fixed; custom patterns aren't supported. You can enable or disable each pattern, change its action (notify, require approval, or deny), adjust its maximum time gap, choose which evidence source it may draw on, exempt specific skills or agents, and turn on an optional LLM-based second-pass judge from Settings → Attack Patterns — see Configure attack pattern detection.

Matches also feed back into risk reporting. Each identity's risk score gains points for the attack patterns attributed to it. Each server card on the Datasources page shows how many of the matches first detected in the last 7 days involved that server (Risk posture).

Kernel-sourced evidence​

The gateway can only see what came through it. A workload that reads a credential file directly off disk, or opens a raw network connection that never touches AIControls, is invisible to gateway-only detection by construction — a clean audit log and an agent routing around AIControls entirely produce the same report. On self-hosted Kubernetes, the same runtime detection engine that powers Runtime Controls gives attack pattern detection a second, independent vantage point: file opens, executions, and network/DNS activity observed at the kernel level, correlated against gateway-audited calls for the same identity.

Every built-in pattern has a Kernel evidence mode:

ModeMeaning
Never (gateway only)Only audited gateway calls may satisfy this pattern's steps.
MixedEither gateway-audited calls or kernel-sourced observations may satisfy a step. The five original patterns above ship this way.
Only (kernel only)Only kernel-sourced observations may satisfy this pattern's steps — used by Credential file opened → Ungoverned egress, which by definition describes an interaction the gateway never saw.

A kernel-sourced step carries an attribution tier — how confidently it's tied to a specific identity — shown on the match itself so a match built partly from kernel evidence never looks identical in trust level to one built entirely from audited gateway calls:

TierBasis
UID-bound (strongest)Cryptographically tied to the same pod identity the gateway call used.
Content-corroboratedA gateway call in the same window already named the same destination or path, independently confirming the kernel observation.
Service-account nameJoined via the workload's Kubernetes ServiceAccount name.
Name-based (weakest allowed)Joined via a workload- or pod-name pattern match — a heuristic, not a cryptographic guarantee.
UnconfirmedCannot be tied to an identity with any of the above methods. Unconfirmed evidence can never satisfy a pattern step, no matter how the minimum attribution tier setting is configured.

A kernel-sourced step is also marked declared or undeclared: declared means a gateway call at or before the kernel event, within the same window, already named that destination, so the action wasn't hidden — it just happened to also be observed at the kernel level. Undeclared means no governed call had named it by then; a call that names the destination only afterwards does not make an earlier egress declared. Governed action → Undeclared egress matches only on the undeclared case.

Coverage is reported as available, degraded, unavailable, or unknown (with a reason) alongside every match, on the workload's Runtime tab, and on the Settings screen. This matters because "no kernel-sourced match" and "kernel evidence was checked and found nothing" are different claims: unavailable means the node can't produce this evidence class at all (for example, no node has bpffs mounted at /sys/fs/bpf, which the open/exec hooks for file-open and exec observations require), or kernel evidence is turned off in the attack-pattern settings; unknown means coverage couldn't be checked at all; degraded means evidence is collected but the last sweep checked only part of it, because a per-sweep limit on kernel observations or workloads was reached or the sweep stopped before it checked every subject. None of these should be read as a clean bill of health.

Precedence with skill behavioral indicators. A skill's own behavioral analysis (which can independently flag a skill across its whole lifetime, order-agnostic) and a sequence match (ordered, evidence-scoped to one session or workload) measure different things and are never merged. When a step's skill was also independently flagged, the match shows a "skill also flagged" tag; if the sequence verdict is later downgraded, the match notes that the downgrade applies to that sequence only — the skill's own risk score is unaffected.

Honest limits:

  • Each step is classified from a single event. Bulk read → Egress covers low-sensitivity reads that add up, per MCP server within the aggregation window, across all of an agent's servers within that window, and per server over a longer 24-hour window. Reads that stay below every threshold at once don't match. With the defaults, that means fewer than 50 reads and under 5 MB from any one server in an hour, fewer than 100 reads across all servers in an hour, and fewer than 100 reads from any one server in 24 hours. Lower the thresholds in Settings if your risk tolerance is tighter.
  • An MCP tool's access type trusts the server's self-reported read-only hint and tool description, so a compromised server could label a destructive tool read-only and its calls wouldn't count as write or delete steps. Egress steps don't depend on these hints.
  • File-open evidence (the basis for a credential-read step) requires the open/exec hooks, which need bpffs mounted at /sys/fs/bpf on each node. Most distributions mount it by default; kind and some minimal node images don't. Network and DNS evidence don't need the hooks.
  • Name-based attribution is a heuristic pattern match, not a guarantee — treat a name-based-only match with more caution than a UID-bound one.
  • Kernel-sourced evidence is only available for self-hosted Kubernetes deployments running the bundled runtime detection engine — it does not apply to the managed SaaS product.

Baseline alerts​

The behavioral baseline engine periodically learns from audit events and compares recent activity with historical profiles. Its existing signals include tool-call rate, tokens per call, deny rate, tool diversity, off-hours activity, and newly introduced tools. Statistical comparisons need enough observations to establish a baseline; an empty alert list is not proof of complete detection coverage.

Investigate baseline findings under Activity → All activity, with Source: Behavioral baseline. The view includes usage and security-related signals without assuming every finding is a security incident. Identity, severity, time and acknowledgement filters run on the server; the investigation drawer retains evidence, historical trends and review actions. Column sorting applies to the current page. Shared Activity identity (agent) and single severity (critical, warning, or info) filters also scope the list and bulk acknowledgement. Unsupported multi-value severity filters must be cleared explicitly before results load. Repeated scope parameters are treated as ambiguous and require explicit removal. When both time bounds are set, the start must be earlier than the end; the end timestamp remains inclusive. Invalid or reversed time bounds show a warning and pause loading and bulk acknowledgement; correct them in the picker or use Clear time range for malformed bounds. Legacy baselineIdentity and baselineSeverity bookmarks remain supported; conflicting shared and legacy values require correction before findings load. Switching to the activity feed preserves these filters and requires explicit removal where unsupported. Native activity counts are hidden while the baseline source is selected. The shared actor filter trims surrounding whitespace and matches recorded identity IDs only; it does not resolve display names or email aliases. A visible notice explains this limitation; remove the actor filter explicitly and select an exact identity when needed. Actor substring matching on SQLite does not guarantee Unicode case folding; use the exact identity filter for non-ASCII identifiers. Workload, cluster and session filters cannot scope identity-wide baselines; remove unsupported filters explicitly to view that broader history. All, including acknowledged shows retained reviewed findings. Administrators can acknowledge matching findings across pages; the confirmation applies the same identity, actor, severity and time filters as the list. Viewers can inspect findings but cannot acknowledge or dismiss them. The separate Alerts and Baselines pages and routes have been removed without compatibility aliases; update bookmarks to the Activity view. Baseline findings also remain available as Baseline alert events in Security Events. Depending on the signal, details include the observed value, expected historical values, a z-score, or a message explaining the boolean condition. Available history is subject to configured retention and alert-count limits.

Audit records matching the seven existing intent policy names also appear in Security Events and Correlated findings as Policy finding (Audit). This includes registration, access-tier, declared-tool, undeclared-server and conformance policy findings. Each finding links to its native audit record. Audit reports a policy violation without enforcing it: the finding alone does not establish whether the request was ultimately allowed, blocked or executed. Use the related request evidence to establish that outcome. Intent compares the registered declaration; baseline novelty compares retained history, so both findings may legitimately describe the same call. Repeated calls retain separate audit records. Other policy names and allowed-call records are excluded. Historical audit records retain the policy name, not a verified built-in policy revision: a custom policy using one of these names also appears as a generic Policy finding (Audit), not proof of intent drift. Inspect the recorded policy and declaration evidence before drawing that conclusion.

When an Audit record includes a workload tuple, Security Events retains that workload reference. For these Audit findings, a workload-filtered view also requires the controller’s trusted cluster identity; unknown cluster identity or records without matching attribution do not broaden the results. The explicit workload filter remains active when runtime controls are off.

Related identity activity opens Gateway activity for the exact recorded identity. For Kubernetes identities with cluster attribution, it retains the cluster so same-named identities in another cluster cannot enter the results. The Gateway table total and exports use this identity scope. If additional selected filters cannot be included in an export, Export is disabled until those filters are cleared; the scoped table remains available. Cross-source tab counts are hidden and other Activity tabs are unavailable while the scope is active; choose Remove identity scope to browse broader activity. Legacy identities without cluster attribution match their recorded identity exactly and cannot establish a cluster boundary. Security Events and Correlated Findings match recorded gateway attribution, stored baseline identity keys, and identity-level sequence subjects. Baseline keys do not retain separate attribution provenance, so a matching key does not independently verify cluster attribution. Correlated Findings reports this limitation, unsupported sources, and excluded clusterless history in its coverage notices. Open Gateway to inspect the recorded calls within that identity and any session filter, or explicitly remove the scope to broaden the view.

The former Baselines page's heatmap and severity tiles, which summarized the first 100 unacknowledged alerts, are no longer displayed. Use the identity and severity filters with the matching-findings total to inspect retained history across pages. Activity does not provide an aggregate identity-by-signal heatmap.

For statistical signals, repeated evaluation of the same identity, signal, hourly observation and severity reuses the retained finding instead of creating another row or notification. Changes to a still-open hourly aggregate at the same severity do not create additional findings; the retained row shows the evidence when it first fired. A new hourly observation or a severity escalation can produce a new finding. Recalculated expected values and acknowledgement alone do not make an observation new. Duplicate suppression survives worker restarts while the finding remains in retained history; retention or alert-count pruning removes that protection unless already-saved review history still reserves the finding. Acknowledgements and their review records are saved atomically. Alert claims and reviews serialize with pruning so an in-flight review cannot be missed when suppressing a repeated finding. Previously stored duplicate rows are preserved. After upgrading from random-ID alerts, an ongoing anomaly can produce one additional occurrence before the new suppression takes effect; older rows do not contain the observation-hour key needed for a safe backfill.

Off-hours alerts retain a one-hour cooldown, and new-tool alerts a 24-hour cooldown per tool message. Boolean occurrences use the current cooldown window and tool identity, so unchanged evidence can alert again after its cooldown expires. Reviews require an existing finding and cannot reserve a future alert ID. Notifications are attempted only after a new finding is successfully saved. Delivery remains best-effort: if notification capacity is exhausted, the finding remains in history without an automatic delivery retry. Info findings remain history-only.

Notification routing is separate from baseline computation. Policy, budget, and HITL notifications are not all baseline alerts. The session anomaly detection switch does not disable the behavioral baseline engine.

See also​