Skip to main content

The problem policies solve

When agents make LLM calls, costs accumulate in the background. Without limits, a single runaway agent or a misconfigured loop can burn through a month’s budget in hours. Budget policies let you set hard caps and choose what happens when those caps are hit. Policies are evaluated at authorization time, before any LLM call is made. If an agent is over budget, the authorization check fails before your code even runs.
Policies stack. An agent can have a per-agent policy, a per-user policy, and a per-tenant policy all active at once. KavachOS evaluates all of them and returns the first one that is exceeded.

Data model

id
string
Stable identifier with a pol_ prefix.
agentId
string
default:"undefined (all agents)"
Agent this policy applies to. Omit to create a global policy that applies to all agents.
userId
string
default:"undefined (all users)"
User this policy applies to. Omit to apply regardless of owner.
tenantId
string
default:"undefined (all tenants)"
Tenant this policy applies to.
limits
BudgetLimits
The numeric thresholds for this policy.
action
'warn' | 'throttle' | 'block' | 'revoke'
What happens when a limit is exceeded.
status
'active' | 'triggered' | 'disabled'
Current policy state. ‘triggered’ means a limit has been hit.
currentUsage
BudgetUsage
Running counters for this policy.

BudgetLimits

maxTokensCostPerDay
number
default:"undefined (no daily token limit)"
Maximum token cost units allowed per day. Resets at midnight UTC.
maxTokensCostPerMonth
number
default:"undefined (no monthly token limit)"
Maximum token cost units allowed per calendar month.
maxCallsPerDay
number
default:"undefined (no daily call limit)"
Maximum authorize() calls allowed per day.
maxCallsPerMonth
number
default:"undefined (no monthly call limit)"
Maximum authorize() calls allowed per calendar month.

Actions

ActionWhat happens
warnAuthorization still succeeds. The policy is marked as triggered.
throttleAuthorization fails. The agent must wait for the reset cycle.
blockAuthorization fails. Stays blocked until the limit resets or you reset manually.
revokeAuthorization fails. The agent’s token is revoked immediately.
warn is useful for sending alerts before you start blocking. Set a warn policy at 80% of your limit and a block policy at 100%.

Creating a policy

// Per-agent daily token cap
const policy = await kavach.policy.create({
  agentId: 'agt_abc123',
  limits: {
    maxTokensCostPerDay: 1000,
    maxCallsPerDay: 500,
  },
  action: 'block',
});

// Per-user monthly cap (applies to all agents owned by this user)
const userPolicy = await kavach.policy.create({
  userId: 'user-456',
  limits: {
    maxTokensCostPerMonth: 10_000,
  },
  action: 'throttle',
});

// Tenant-wide monthly cap
const tenantPolicy = await kavach.policy.create({
  tenantId: 'tnt_acme',
  limits: {
    maxTokensCostPerMonth: 50_000,
    maxCallsPerMonth: 1_000_000,
  },
  action: 'block',
});

Checking a budget before a call

Call checkBudget with a speculative tokensCost to see whether this call would exceed any policy. The cost is included in the check but not recorded yet.
const check = await kavach.policy.checkBudget('agt_abc123', 50);

if (!check.allowed) {
  console.error(check.reason);
  // check.policy contains the policy that was exceeded
}
checkBudget evaluates all active policies for the agent (both exact-match and global). If any policy is exceeded, it returns the first violation and stops.

Recording usage after a call

Call recordUsage after the LLM call completes to update the counters.
const result = await llm.complete(prompt);
const actualCost = result.usage.totalTokens;

await kavach.policy.recordUsage('agt_abc123', actualCost);
recordUsage increments callsToday, callsThisMonth, tokensCostToday, and tokensCostThisMonth on every active policy that applies to this agent. It also transitions any policy from active to triggered if the new totals cross a threshold.

Resetting counters

Reset daily counters on a UTC midnight cron job:
const { reset } = await kavach.policy.resetDaily();
console.log(`Reset ${reset} policies`);
Reset monthly counters on the first of each month:
const { reset } = await kavach.policy.resetMonthly();
After a reset, policies that were triggered are automatically moved back to active if the new totals are within limits.

Listing and updating policies

// All policies for an agent (including global ones)
const policies = await kavach.policy.list({ agentId: 'agt_abc123' });

// Change the limit and action
await kavach.policy.update(policy.id, {
  limits: { maxTokensCostPerDay: 2000 },
  action: 'warn',
});

// Remove a policy
await kavach.policy.remove(policy.id);

Combining warn and block

A common pattern: warn at a soft limit, block at the hard limit.
// Warn at 800 tokens/day
await kavach.policy.create({
  agentId: 'agt_abc123',
  limits: { maxTokensCostPerDay: 800 },
  action: 'warn',
});

// Block at 1000 tokens/day
await kavach.policy.create({
  agentId: 'agt_abc123',
  limits: { maxTokensCostPerDay: 1000 },
  action: 'block',
});
When the agent hits 800, the warn policy triggers and you can send an alert (via a lifecycle hook). At 1000, the block policy triggers and requests stop.

Next steps

Lifecycle hooks

Fire custom logic when a policy is triggered.

Cost tracking

Aggregate token costs for billing reports.

Multi-tenant isolation

Attach policies to entire tenants.