Skip to main content

What anomaly detection does

KavachOS scans the audit log for behavioral patterns that suggest an agent is operating outside its intended scope. The most common signal is a cluster of denied calls that match privilege escalation patterns, an agent repeatedly attempting to access resources it was never granted. Anomaly detection is not a background process. You call scan() and get back a list of findings at that point in time. This keeps the system predictable: there are no background threads, no persistent state, and no surprise side effects.

Anomaly types

TypeSeverityDescription
privilege_escalationcriticalDenied calls with reasons matching INSUFFICIENT_PERMISSIONS, privilege, or escalation.
high_denial_ratewarningMore than 20% of the agent’s recent calls were denied.
rapid_firewarningCall volume in the last hour exceeds a configurable threshold.
unusual_resource_accessinfoThe agent accessed a resource it has no explicit permission for, even if the call succeeded.
off_hours_activityinfoCalls outside the agent’s declared timeWindow constraint, if one is set.
scan() queries audit logs directly, it is not a background process and does not push alerts. Call it from a scheduled job, a webhook handler, or a dashboard endpoint.

Anomaly fields

agentId
string
The agent where the anomaly was detected.
type
'privilege_escalation' | 'high_denial_rate' | 'rapid_fire' | 'unusual_resource_access' | 'off_hours_activity'
Classification of the detected pattern.
severity
'critical' | 'warning' | 'info'
How urgent the finding is.
description
string
Human-readable explanation of what was observed.
count
number
Number of matching events in the scanned window.
detectedAt
string
ISO timestamp of when the scan ran.
evidence
AuditEntry[]
The specific audit log entries that triggered this finding.

AnomalyConfig options

since
Date
Start of the window to scan. Defaults to 24 hours ago.
until
Date
End of the window to scan. Defaults to now.
minDenialRate
number
Denial rate percentage above which high_denial_rate fires. Default is 20.
rapidFireThreshold
number
Calls per hour above which rapid_fire fires. Default is 100.
agentId
string
Scope the scan to a single agent. Omit to scan all agents.

Code examples

Scan a single agent for anomalies

const anomalies = await kavach.audit.query({
  agentId: 'agt_abc123',
  result: 'denied',
  since: new Date(Date.now() - 24 * 3_600_000),
});

// Check for privilege escalation pattern
const escalations = anomalies.filter((entry) => {
  const reason = entry.reason ?? '';
  return (
    reason.includes('INSUFFICIENT_PERMISSIONS') ||
    reason.toLowerCase().includes('privilege') ||
    reason.toLowerCase().includes('escalation')
  );
});

if (escalations.length > 0) {
  console.warn(`${escalations.length} escalation attempt(s) detected for agt_abc123`);
}

Get a denial-rate summary for all agents

const since = new Date(Date.now() - 24 * 3_600_000);

const logs = await kavach.audit.query({ since, limit: 5000 });

const byAgent: Record<string, { total: number; denied: number }> = {};

for (const entry of logs) {
  const bucket = (byAgent[entry.agentId] ??= { total: 0, denied: 0 });
  bucket.total++;
  if (entry.result === 'denied') bucket.denied++;
}

const highDenialAgents = Object.entries(byAgent)
  .filter(([, { total, denied }]) => total > 0 && denied / total > 0.2)
  .map(([agentId, { total, denied }]) => ({
    agentId,
    denialRate: (denied / total) * 100,
  }));

console.log('Agents with >20% denial rate:', highDenialAgents);

Act on critical findings

The most effective response to a privilege escalation detection is to pause the agent while you investigate:
const denied = await kavach.audit.query({
  agentId: 'agt_abc123',
  result: 'denied',
  since: new Date(Date.now() - 3_600_000),
});

const escalations = denied.filter((e) =>
  (e.reason ?? '').includes('INSUFFICIENT_PERMISSIONS'),
);

if (escalations.length >= 3) {
  await kavach.agent.revoke('agt_abc123');
  console.log('Agent revoked after repeated escalation attempts.');
}
agent.revoke() is permanent. If you want to temporarily suspend an agent and restore it later, create a new agent with the same configuration instead of revoking.

Use trust scores as the anomaly aggregate

The anomalyCount field on a trust score is a pre-computed count of privilege escalation attempts, updated each time computeScore runs. It is a cheaper signal than a full scan when you only need the count:
const score = await kavach.trust.computeScore('agt_abc123');

if (score.factors.anomalyCount > 0) {
  console.warn(
    `${score.factors.anomalyCount} anomalies detected. Last denial: ${score.factors.lastViolation}`,
  );
}

Next steps

Trust scoring

Translate anomaly counts into a graduated trust level.

Approval flows

Route flagged agents through human review before they act.

Audit trail

Query the raw log data that anomaly detection reads.