Anomaly detection
Identifying unusual agent behavior by scanning audit logs for denial patterns and privilege escalation attempts.
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
| Type | Severity | Description |
|---|---|---|
privilege_escalation | critical | Denied calls with reasons matching INSUFFICIENT_PERMISSIONS, privilege, or escalation. |
high_denial_rate | warning | More than 20% of the agent's recent calls were denied. |
rapid_fire | warning | Call volume in the last hour exceeds a configurable threshold. |
unusual_resource_access | info | The agent accessed a resource it has no explicit permission for, even if the call succeeded. |
off_hours_activity | info | Calls 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
Prop
Type
AnomalyConfig options
Prop
Type
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}`,
);
}