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 agent | Ephemeral session | |
|---|---|---|
| Lifetime | Indefinite by default | Bounded TTL (default 5 min) |
| Token | Rotatable, persistent | Single-use window |
| Cleanup | Manual revocation | Automatic |
| Action budget | None | Optional hard cap |
| Audit | Per-agent | Grouped 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 configuredWhen 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
| Code | Meaning |
|---|---|
SESSION_NOT_FOUND | Token does not match any session |
SESSION_EXPIRED | TTL has elapsed |
SESSION_EXHAUSTED | Action budget is fully consumed |
SESSION_REVOKED | Manually revoked |
TTL_EXCEEDS_MAX | Requested TTL is above maxTtlSeconds |
VALIDATION_ERROR | Input failed schema validation (e.g. empty permissions) |