kavachOS

Ephemeral sessions

Short-lived, auto-expiring agent credentials for single-task computer-use agents.

What ephemeral sessions are

An ephemeral session is a temporary agent identity that expires on its own. You create one, hand the token to an AI agent, and the token stops working when the TTL runs out or the agent exhausts its action budget — whichever comes first.

The pattern is designed for computer-use agents (Claude computer use, GPT browsing, operator loops) that need just enough access to complete one task without holding persistent credentials across invocations.

Key differences from a regular agent:

Regular agentEphemeral session
LifetimeIndefinite by defaultBounded TTL (default 5 min)
TokenRotatable, persistentSingle-use window
CleanupManual revocationAutomatic
Action budgetNoneOptional hard cap
AuditPer-agentGrouped under one session ID

Setup

No extra configuration is needed — ephemeral sessions are part of the kavachos core package and share the same database as the rest of KavachOS.

import { createKavach } from 'kavachos';
import { createEphemeralSessionModule } from 'kavachos/auth';

const kavach = await createKavach({
  database: { provider: 'sqlite', url: 'kavach.db' },
});

const ephemeral = createEphemeralSessionModule({
  db: kavach.db,
  defaultTtlSeconds: 300,   // 5 minutes
  maxTtlSeconds: 3600,      // 1 hour ceiling
  autoRevokeOnExpiry: true, // revoke underlying agent on expiry
  auditGrouping: true,      // group all actions under one audit session ID
});

Creating a session for a computer-use agent

Call createSession right before you hand control to the agent. The token is shown exactly once.

const result = await ephemeral.createSession({
  ownerId: 'user-abc',          // the human who owns this task
  name: 'fill-checkout-form',   // optional label
  permissions: [
    { resource: 'tool:browser', actions: ['navigate', 'click', 'type'] },
  ],
  ttlSeconds: 120,              // 2 minutes for this particular task
  maxActions: 20,               // hard cap: fail after 20 actions
});

if (!result.success) throw new Error(result.error.message);

const { token, expiresAt, auditGroupId } = result.data;
// Pass `token` to the agent. Never store it or log it.

The returned token starts with kveph_ to distinguish it from long-lived agent tokens (kv_).

Validating a session

Each time the agent makes a request, validate the token before granting access.

const check = await ephemeral.validateSession(token);

if (!check.success) {
  // error.code is one of:
  // SESSION_NOT_FOUND, SESSION_EXPIRED, SESSION_EXHAUSTED, SESSION_REVOKED
  return new Response('Unauthorized', { status: 401 });
}

const { sessionId, agentId, remainingActions, expiresIn, auditGroupId } = check.data;

expiresIn is in seconds. remainingActions is null when no action cap was set.

Tracking actions

Call consumeAction once per agent action to decrement the budget counter.

const consumed = await ephemeral.consumeAction(token);

if (!consumed.success) {
  // SESSION_EXHAUSTED when the budget runs out
  return new Response('Budget exhausted', { status: 429 });
}

const { actionsRemaining } = consumed.data;
// actionsRemaining is null when no maxActions was configured

When actionsRemaining hits zero, the session transitions to exhausted and the underlying agent is automatically revoked.

Action limits

maxActions is a hard cap on how many times consumeAction can succeed. It is independent of the TTL — a session can expire by time with actions remaining, or exhaust its action budget before the TTL lapses.

Set a tight budget for tasks with a well-known scope and leave it as null for tasks where the step count is unpredictable.

// A session with both a time limit and an action cap
await ephemeral.createSession({
  ownerId: userId,
  permissions: [{ resource: 'tool:search', actions: ['query'] }],
  ttlSeconds: 60,
  maxActions: 5,
});

Revoking early

If the agent completes the task before the TTL expires, revoke the session manually.

await ephemeral.revokeSession(sessionId);

Revocation is idempotent — calling it on an already-revoked session returns success.

Listing active sessions

Useful for dashboards or for building kill-switch UI.

const result = await ephemeral.listActiveSessions('user-abc');

if (result.success) {
  for (const session of result.data) {
    console.log(session.sessionId, session.expiresAt, session.actionsUsed);
  }
}

The returned objects have token set to "" — the token is never readable after the initial createSession response.

Cleanup strategies

Two approaches to cleaning up expired sessions:

Scheduled background job

Run cleanupExpired() on a cron schedule — every minute for busy systems, every 5 minutes for lighter loads.

// With node:timers/promises or any scheduler
setInterval(async () => {
  const result = await ephemeral.cleanupExpired();
  if (result.success && result.data.count > 0) {
    console.log(`Cleaned up ${result.data.count} expired sessions`);
  }
}, 60_000);

On-demand at validate time

validateSession already detects and transitions expired sessions. For low-traffic systems, this is enough — no background job needed.

Audit grouping

When auditGrouping: true (the default), all actions within a session share the same auditGroupId. This makes it trivial to reconstruct the full activity trace for a single task.

const validated = await ephemeral.validateSession(token);
if (validated.success) {
  // Attach to every audit entry for this action
  const { auditGroupId } = validated.data;
}

Session status lifecycle

created → active → exhausted (maxActions reached)
                 → expired   (TTL elapsed)
                 → revoked   (manual revocation)

Once a session leaves active, it cannot be reactivated. Create a new session for a new task.

Error codes

CodeMeaning
SESSION_NOT_FOUNDToken does not match any session
SESSION_EXPIREDTTL has elapsed
SESSION_EXHAUSTEDAction budget is fully consumed
SESSION_REVOKEDManually revoked
TTL_EXCEEDS_MAXRequested TTL is above maxTtlSeconds
VALIDATION_ERRORInput failed schema validation (e.g. empty permissions)

On this page