kavachOS

Quickstart

Get KavachOS running in five minutes.

Your first agent

Install

terminal
pnpm add kavachos
terminal
npm install kavachos
terminal
yarn add kavachos

Create an instance

Pass a database config to createKavach. Use SQLite for local development and Postgres in production.

import { createKavach } from 'kavachos';

const kavach = createKavach({
  database: { provider: 'sqlite', url: 'kavach.db' },
  agents: {
    enabled: true,
    maxPerUser: 10,
    auditAll: true,    // record every authorize() call
    tokenExpiry: '24h',
  },
});

For in-memory storage (useful in tests), pass url: ':memory:' instead.

Set requireVerification: false during development so you can sign in without verifying email. Remove this in production.

Create an agent

An agent always has an owner: the user ID from your existing auth system. KavachOS does not manage human authentication.

const agent = await kavach.agent.create({
  ownerId: 'user-123',          // from your auth provider
  name: 'github-reader',
  type: 'autonomous',
  permissions: [
    {
      resource: 'mcp:github:*',
      actions: ['read'],
    },
    {
      resource: 'mcp:deploy:production',
      actions: ['execute'],
      constraints: {
        requireApproval: true,   // human-in-the-loop gate
        maxCallsPerHour: 5,
      },
    },
  ],
});

// agent.token is the bearer token: "kv_..."
console.log(agent.token);

The token is shown exactly once, at creation time. Store it immediately in your secrets manager or pass it directly to the agent. It cannot be recovered after this point — only rotated.

There are three agent types:

TypeWhen to use
autonomousRuns without human involvement. Default for most agents.
delegatedReceives permissions from another agent via a delegation chain.
serviceLong-lived service account identity.

Authorize an action

Call kavach.authorize before any sensitive operation. It returns { allowed, reason?, auditId }.

const result = await kavach.authorize(agent.id, {
  action: 'read',
  resource: 'mcp:github:repos',
});

if (!result.allowed) {
  throw new Error(`Denied: ${result.reason}`);
}

// result.auditId links this decision to its audit log entry

If you only have the raw bearer token (from an incoming HTTP request, for example), use authorizeByToken instead:

const result = await kavach.authorizeByToken(bearerToken, {
  action: 'read',
  resource: 'mcp:github:repos',
});

Check the audit trail

Every authorization decision is logged. Query by agent, filter by result, or export for compliance.

// All decisions for an agent
const logs = await kavach.audit.query({ agentId: agent.id });

// Only the denials
const denied = await kavach.audit.query({
  agentId: agent.id,
  result: 'denied',
});

// Export everything as CSV
const csv = await kavach.audit.export({ format: 'csv' });

Delegation

An orchestrator agent can delegate a subset of its permissions to a sub-agent. The delegation has its own expiry and a maxDepth to prevent unbounded chains.

const sub = await kavach.agent.create({
  ownerId: 'user-123',
  name: 'sub-reader',
  type: 'delegated',
  permissions: [],  // starts empty; receives permissions via delegation
});

await kavach.delegate({
  fromAgent: agent.id,
  toAgent: sub.id,
  permissions: [{ resource: 'mcp:github:issues', actions: ['read'] }],
  expiresAt: new Date(Date.now() + 3_600_000),  // 1 hour
  maxDepth: 2,
});

// Resolves the full effective permission set, including delegated ones
const perms = await kavach.delegation.getEffectivePermissions(sub.id);

An agent cannot delegate permissions it does not hold itself. Attempts to escalate are rejected at the point of delegation, not at authorization time.

Full working example

A self-contained script covering agent creation, authorization by ID and token, token rotation, delegation, and audit export.

import { createKavach } from 'kavachos';

const kavach = createKavach({
  database: { provider: 'sqlite', url: ':memory:' },
  agents: { enabled: true, maxPerUser: 10, auditAll: true, tokenExpiry: '24h' },
});

// Create an agent with wildcard read on github resources
const agent = await kavach.agent.create({
  ownerId: 'user-123',
  name: 'github-reader',
  type: 'autonomous',
  permissions: [
    { resource: 'mcp:github:*', actions: ['read'] },
    { resource: 'mcp:github:issues', actions: ['read', 'comment'] },
  ],
});

// Allowed: mcp:github:repos matches mcp:github:*
const r1 = await kavach.authorize(agent.id, {
  action: 'read',
  resource: 'mcp:github:repos',
});
console.log(r1.allowed);  // true

// Denied: no write permission
const r2 = await kavach.authorize(agent.id, {
  action: 'write',
  resource: 'mcp:github:repos',
});
console.log(r2.allowed);  // false

// Authorize using the raw bearer token
const r3 = await kavach.authorizeByToken(agent.token, {
  action: 'read',
  resource: 'mcp:github:issues',
});
console.log(r3.allowed);  // true

// Rotate: old token is immediately invalid
const rotated = await kavach.agent.rotate(agent.id);
console.log(rotated.token);  // new kv_... token

// Delegate a subset to a sub-agent
const sub = await kavach.agent.create({
  ownerId: 'user-123',
  name: 'sub-reader',
  type: 'delegated',
  permissions: [],
});

await kavach.delegate({
  fromAgent: agent.id,
  toAgent: sub.id,
  permissions: [{ resource: 'mcp:github:issues', actions: ['read'] }],
  expiresAt: new Date(Date.now() + 3_600_000),
  maxDepth: 2,
});

const perms = await kavach.delegation.getEffectivePermissions(sub.id);
console.log(perms);
// [{ resource: 'mcp:github:issues', actions: ['read'] }]

// Export the full audit trail
const csv = await kavach.audit.export({ format: 'csv' });

Cloudflare Workers

KavachOS runs on Workers with no changes. Pass a D1 binding as the database and use the Hono adapter.

import { createKavach } from 'kavachos';
import { Hono } from 'hono';

type Env = { KAVACH_DB: D1Database };

const app = new Hono<{ Bindings: Env }>();

app.get('/health', async (c) => {
  const kavach = await createKavach({
    database: { provider: 'd1', binding: c.env.KAVACH_DB },
  });

  const agent = await kavach.agent.create({
    ownerId: 'user-1',
    name: 'my-agent',
    type: 'autonomous',
    permissions: [{ resource: 'mcp:github:*', actions: ['read'] }],
  });

  return c.json({ agent });
});

export default app;

Bind a D1 database in your wrangler.toml:

[[d1_databases]]
binding = "KAVACH_DB"
database_name = "kavach"
database_id = "<your-database-id>"

Run npx wrangler d1 execute kavach --file=./kavach-schema.sql to apply the schema, or set skipMigrations: false to let KavachOS run migrations on first boot.

createKavach is async when using D1. Workers and Deno both support top-level await, so you can also initialize outside the handler if you use a module worker.

Troubleshooting

"Invalid email or password" after sign-up

Sign-in requires email verification by default. Either:

  1. Verify the email using the token from the sign-up response
  2. Set requireVerification: false in the emailPassword() config

"FOREIGN KEY constraint failed" when creating agents

You need a user in the kavach_users table before creating agents. Sign up via the email auth plugin, or seed a user manually:

kavach.db.insert(users).values({
  id: 'user-1',
  email: 'test@example.com',
  name: 'Test',
  createdAt: new Date(),
  updatedAt: new Date(),
}).run();

Session not persisting after page reload

The React hooks store sessions in localStorage. Make sure your app is wrapped in <KavachProvider>. If using SSR (Next.js), wrap the provider in a "use client" component.

Next steps

On this page