Skip to main content

Start from a template

The fastest path is the scaffolder. One command, three prompts, a running Next.js SaaS with KavachOS wired up.
terminal
pnpm create kavachos-app
The CLI asks for a directory, a template, a package manager, and a database driver. Only the Next.js SaaS template is shipping today; the Hono MCP and Expo templates print a coming-soon note and exit. Placeholders in the template (__APP_NAME__, __DB_DRIVER__, __DB_URL__) are replaced on copy. Next steps are printed at the end: cd, install, db:push, dev. Prefer to wire up an existing app? Keep reading.

Your first agent

1

Install

terminal
pnpm add kavachos
2

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.
3

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.
4

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',
});
5

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

Permission engine

Wildcards, rate limits, time windows, IP allowlists, and approval gates.

Delegation chains

Sub-agent delegation with depth limits and cascading revocation.

MCP OAuth 2.1

Set up the authorization server for MCP tool servers.

Framework adapters

Drop-in middleware for Hono, Express, Next.js, and 4 more.

Auth adapters

Plug into better-auth, Auth.js, Clerk, or bring your own.

Configuration

All createKavach() options and environment patterns.