kavachos 0.0.2 → 0.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,19 +1,319 @@
1
- import { createAgentModule } from './chunk-I4J4KKKK.js';
2
- export { createAgentModule } from './chunk-I4J4KKKK.js';
3
- export { bearerAuth, customAuth, headerAuth } from './chunk-7RKVTHFC.js';
4
- import { createPermissionEngine } from './chunk-DEVV32BE.js';
5
- export { createPermissionEngine, getPermissionTemplate, permissionTemplates } from './chunk-DEVV32BE.js';
6
- import { createAuditModule } from './chunk-N7VZO6SP.js';
7
- export { createAuditModule } from './chunk-N7VZO6SP.js';
8
- import { schema_exports, delegationChains, mcpServers, sessions } from './chunk-UEE7OYLG.js';
9
- export { agents, auditLogs, delegationChains, mcpServers, oauthAccessTokens, oauthAuthorizationCodes, oauthClients, permissions, rateLimits, sessions, users } from './chunk-UEE7OYLG.js';
1
+ import { createAgentModule } from './chunk-5DT4DN4Y.js';
2
+ export { createAgentModule } from './chunk-5DT4DN4Y.js';
3
+ import { createSessionManager, createMagicLinkModule, createEmailOtpModule, createTotpModule, createPasskeyModule, createOrgModule, createSsoModule, createAdminModule, createApiKeyManagerModule, createUsernameAuthModule, createPhoneAuthModule, createCaptchaModule, createWebhookModule } from './chunk-KL6XW4S4.js';
4
+ export { HibpApiError, HibpBreachedError, OAuthProxyError, OneTapVerifyError, SSO_ERROR, SsoError, additionalFields, admin, anonymousAuth, apiKeys, bearerAuth, createAdditionalFieldsModule, createAdminModule, createAnonymousAuthModule, createApiKeyManagerModule, createCaptchaModule, createCustomSessionModule, createDeviceAuthModule, createEmailOtpModule, createGdprModule, createHibpModule, createJwtSessionModule, createLastLoginModule, createMagicLinkModule, createOAuthProxyModule, createOidcProviderModule, createOneTapModule, createOneTimeTokenModule, createOpenApiModule, createOrgModule, createPasskeyModule, createPhoneAuthModule, createPolarModule, createRateLimiter, createScimModule, createSessionManager, createSiweModule, createSsoModule, createStripeModule, createTotpModule, createTrustedDeviceModule, createUsernameAuthModule, customAuth, customSession, deviceAuth, deviceLabelFromRequest, emailOtp, gdpr, headerAuth, magicLink, oauthProxy, oneTap, organization, passkey, polar, scim, siwe, stripe, twoFactor, withRateLimit } from './chunk-KL6XW4S4.js';
5
+ import { createPermissionEngine } from './chunk-OVGNZ5OX.js';
6
+ export { createPermissionEngine, getPermissionTemplate, permissionTemplates } from './chunk-OVGNZ5OX.js';
7
+ import { createAuditModule } from './chunk-SJGSPIAD.js';
8
+ export { createAuditModule } from './chunk-SJGSPIAD.js';
9
+ import { schema_exports, approvalRequests, sessions, delegationChains, agentDids, mcpServers, budgetPolicies, agents, permissions, auditLogs, tenants, trustScores } from './chunk-V66UUIA7.js';
10
+ export { agentCards, agentDids, agents, apiKeys as apiKeysTable, approvalRequests, auditLogs, budgetPolicies, delegationChains, emailOtps, magicLinks, mcpServers, oauthAccessTokens, oauthAuthorizationCodes, oauthClients, orgInvitations, orgMembers, orgRoles, organizations, passkeyChallenges, passkeyCredentials, permissions, rateLimits, sessions, ssoConnections, tenants, totpRecords, trustScores, users } from './chunk-V66UUIA7.js';
10
11
  import './chunk-PZ5AY32C.js';
12
+ import { eq, and, lt, ne, or, isNull, gte } from 'drizzle-orm';
13
+ import { randomUUID } from 'crypto';
11
14
  import BetterSqlite3 from 'better-sqlite3';
12
15
  import { drizzle } from 'drizzle-orm/better-sqlite3';
13
- import { createSecretKey, randomUUID } from 'crypto';
14
- import { and, eq } from 'drizzle-orm';
15
- import { SignJWT, jwtVerify } from 'jose';
16
+ import { generateKeyPair, exportJWK, importJWK, SignJWT, jwtVerify } from 'jose';
16
17
 
18
+ var DEFAULT_LOOKBACK_DAYS = 30;
19
+ function isWildcard(value) {
20
+ return value === "*" || value.endsWith(":*") || value.endsWith("/*");
21
+ }
22
+ function deriveScore(findings) {
23
+ const hasCritical = findings.some((f) => f.severity === "critical");
24
+ const wildcardCount = findings.filter((f) => f.type === "wildcard_permission").length;
25
+ const warningCount = findings.filter((f) => f.severity === "warning").length;
26
+ if (hasCritical || wildcardCount >= 2) return "wildcard-heavy";
27
+ if (wildcardCount === 1 || warningCount >= 2) return "over-permissioned";
28
+ if (findings.length === 0) return "minimal";
29
+ return "appropriate";
30
+ }
31
+ function buildRecommendations(findings, usedResources) {
32
+ const recs = [];
33
+ for (const finding of findings) {
34
+ if (finding.type === "wildcard_permission" && finding.permission) {
35
+ const { resource, actions } = finding.permission;
36
+ const wildcardBase = resource.replace(/:?\*$/, "");
37
+ const relevantUsed = [...usedResources].filter(
38
+ (r) => wildcardBase ? r.startsWith(wildcardBase) : true
39
+ );
40
+ if (relevantUsed.length > 0) {
41
+ recs.push(`Narrow \`${resource}\` to \`${relevantUsed.join(", ")}\``);
42
+ } else {
43
+ recs.push(`Remove unused wildcard permission \`${resource}\``);
44
+ }
45
+ if (actions.includes("*")) {
46
+ const usedActions = ["read"];
47
+ recs.push(
48
+ `Replace wildcard actions on \`${resource}\` with explicit actions: ${usedActions.join(", ")}`
49
+ );
50
+ }
51
+ }
52
+ if (finding.type === "unused_permission" && finding.permission) {
53
+ recs.push(
54
+ `Remove unused permission \`${finding.permission.resource}\` (no activity in last ${DEFAULT_LOOKBACK_DAYS} days)`
55
+ );
56
+ }
57
+ if (finding.type === "overly_broad" && finding.permission) {
58
+ const { resource } = finding.permission;
59
+ const relevantUsed = [...usedResources].filter((r) => {
60
+ const prefix = resource.replace(/:?\*$/, "");
61
+ return r.startsWith(prefix);
62
+ });
63
+ if (relevantUsed.length > 0) {
64
+ recs.push(
65
+ `Narrow \`${resource}\` to the specific resources used: \`${relevantUsed.join(", ")}\``
66
+ );
67
+ }
68
+ }
69
+ if (finding.type === "no_constraints") {
70
+ recs.push("Add rate limits or approval gates to sensitive permissions");
71
+ }
72
+ if (finding.type === "no_expiry") {
73
+ recs.push("Set an expiry date on this agent to enforce periodic credential rotation");
74
+ }
75
+ }
76
+ return [...new Set(recs)];
77
+ }
78
+ function createPrivilegeAnalyzer(db) {
79
+ async function analyzeAgent(agentId, options) {
80
+ const agentRows = await db.select({ id: agents.id, name: agents.name, expiresAt: agents.expiresAt }).from(agents).where(eq(agents.id, agentId)).limit(1);
81
+ const agent = agentRows[0];
82
+ if (!agent) {
83
+ return {
84
+ agentId,
85
+ agentName: "unknown",
86
+ score: "appropriate",
87
+ findings: [],
88
+ recommendations: []
89
+ };
90
+ }
91
+ const permRows = await db.select({
92
+ resource: permissions.resource,
93
+ actions: permissions.actions,
94
+ constraints: permissions.constraints
95
+ }).from(permissions).where(eq(permissions.agentId, agentId));
96
+ const agentPermissions = permRows.map((r) => ({
97
+ resource: r.resource,
98
+ actions: r.actions,
99
+ constraints: r.constraints ?? void 0
100
+ }));
101
+ const since = options?.since ?? new Date(Date.now() - DEFAULT_LOOKBACK_DAYS * 24 * 60 * 60 * 1e3);
102
+ const auditRows = await db.select({ resource: auditLogs.resource, action: auditLogs.action }).from(auditLogs).where(and(eq(auditLogs.agentId, agentId), gte(auditLogs.timestamp, since)));
103
+ const usedResources = new Set(auditRows.map((r) => r.resource));
104
+ const findings = [];
105
+ for (const perm of agentPermissions) {
106
+ const hasWildcardResource = isWildcard(perm.resource);
107
+ const hasWildcardAction = perm.actions.includes("*");
108
+ if (hasWildcardResource || hasWildcardAction) {
109
+ findings.push({
110
+ type: "wildcard_permission",
111
+ severity: "critical",
112
+ description: hasWildcardResource ? `Permission resource \`${perm.resource}\` uses a wildcard` : `Permission \`${perm.resource}\` has wildcard action \`*\``,
113
+ permission: { resource: perm.resource, actions: perm.actions }
114
+ });
115
+ continue;
116
+ }
117
+ const wasUsed = [...usedResources].some((used) => {
118
+ if (perm.resource === used) return true;
119
+ const permBase = perm.resource.replace(/:?\*$/, "");
120
+ return used.startsWith(permBase);
121
+ });
122
+ if (!wasUsed) {
123
+ findings.push({
124
+ type: "unused_permission",
125
+ severity: "warning",
126
+ description: `Permission \`${perm.resource}\` has not been used in the last ${DEFAULT_LOOKBACK_DAYS} days`,
127
+ permission: { resource: perm.resource, actions: perm.actions }
128
+ });
129
+ }
130
+ if (perm.resource.includes(":")) {
131
+ const permBase = perm.resource.replace(/:?\*$/, "");
132
+ const coveredUsed = [...usedResources].filter((r) => r.startsWith(permBase));
133
+ if (coveredUsed.length > 0 && coveredUsed.length < 3) {
134
+ const segments = perm.resource.split(":");
135
+ if (segments.length <= 2 && coveredUsed.every((r) => r.split(":").length > segments.length)) {
136
+ findings.push({
137
+ type: "overly_broad",
138
+ severity: "warning",
139
+ description: `Permission \`${perm.resource}\` is broader than necessary; only \`${coveredUsed.join(", ")}\` was actually used`,
140
+ permission: { resource: perm.resource, actions: perm.actions }
141
+ });
142
+ }
143
+ }
144
+ }
145
+ const hasConstraints = perm.constraints && (perm.constraints.maxCallsPerHour !== void 0 || perm.constraints.timeWindow !== void 0 || perm.constraints.requireApproval === true || perm.constraints.ipAllowlist && perm.constraints.ipAllowlist.length > 0);
146
+ if (!hasConstraints) {
147
+ findings.push({
148
+ type: "no_constraints",
149
+ severity: "info",
150
+ description: `Permission \`${perm.resource}\` has no rate limits, time windows, or approval gates`,
151
+ permission: { resource: perm.resource, actions: perm.actions }
152
+ });
153
+ }
154
+ }
155
+ if (!agent.expiresAt) {
156
+ findings.push({
157
+ type: "no_expiry",
158
+ severity: "info",
159
+ description: "Agent has no expiry date set"
160
+ });
161
+ }
162
+ const score = deriveScore(findings);
163
+ const recommendations = buildRecommendations(findings, usedResources);
164
+ return {
165
+ agentId,
166
+ agentName: agent.name,
167
+ score,
168
+ findings,
169
+ recommendations
170
+ };
171
+ }
172
+ async function analyzeAll(options) {
173
+ const activeAgents = await db.select({ id: agents.id }).from(agents).where(eq(agents.status, "active"));
174
+ const results = await Promise.all(activeAgents.map((a) => analyzeAgent(a.id, options)));
175
+ return results;
176
+ }
177
+ async function getSummary() {
178
+ const analyses = await analyzeAll();
179
+ const byScore = {};
180
+ let criticalFindings = 0;
181
+ for (const analysis of analyses) {
182
+ byScore[analysis.score] = (byScore[analysis.score] ?? 0) + 1;
183
+ criticalFindings += analysis.findings.filter((f) => f.severity === "critical").length;
184
+ }
185
+ return {
186
+ total: analyses.length,
187
+ byScore,
188
+ criticalFindings
189
+ };
190
+ }
191
+ return { analyzeAgent, analyzeAll, getSummary };
192
+ }
193
+ function rowToApproval(row) {
194
+ return {
195
+ id: row.id,
196
+ agentId: row.agentId,
197
+ userId: row.userId,
198
+ action: row.action,
199
+ resource: row.resource,
200
+ arguments: row.arguments ?? void 0,
201
+ status: row.status,
202
+ expiresAt: row.expiresAt,
203
+ respondedAt: row.respondedAt ?? void 0,
204
+ respondedBy: row.respondedBy ?? void 0,
205
+ createdAt: row.createdAt
206
+ };
207
+ }
208
+ async function notifyWebhook(url, approvalRequest) {
209
+ try {
210
+ await fetch(url, {
211
+ method: "POST",
212
+ headers: { "Content-Type": "application/json" },
213
+ body: JSON.stringify({
214
+ event: "approval_needed",
215
+ request: {
216
+ ...approvalRequest,
217
+ expiresAt: approvalRequest.expiresAt.toISOString(),
218
+ createdAt: approvalRequest.createdAt.toISOString()
219
+ }
220
+ })
221
+ });
222
+ } catch {
223
+ }
224
+ }
225
+ function createApprovalModule(config, db) {
226
+ const ttlSeconds = config.ttl ?? 300;
227
+ async function request(input) {
228
+ const now = /* @__PURE__ */ new Date();
229
+ const id = `apr_${randomUUID()}`;
230
+ const expiresAt = new Date(now.getTime() + ttlSeconds * 1e3);
231
+ await db.insert(approvalRequests).values({
232
+ id,
233
+ agentId: input.agentId,
234
+ userId: input.userId,
235
+ action: input.action,
236
+ resource: input.resource,
237
+ arguments: input.arguments ?? null,
238
+ status: "pending",
239
+ expiresAt,
240
+ respondedAt: null,
241
+ respondedBy: null,
242
+ createdAt: now
243
+ });
244
+ const approvalRequest = {
245
+ id,
246
+ agentId: input.agentId,
247
+ userId: input.userId,
248
+ action: input.action,
249
+ resource: input.resource,
250
+ arguments: input.arguments,
251
+ status: "pending",
252
+ expiresAt,
253
+ createdAt: now
254
+ };
255
+ if (config.webhookUrl) {
256
+ void notifyWebhook(config.webhookUrl, approvalRequest);
257
+ }
258
+ if (config.onApprovalNeeded) {
259
+ void config.onApprovalNeeded(approvalRequest);
260
+ }
261
+ return approvalRequest;
262
+ }
263
+ async function resolve(requestId, newStatus, respondedBy) {
264
+ const rows = await db.select().from(approvalRequests).where(eq(approvalRequests.id, requestId)).limit(1);
265
+ const row = rows[0];
266
+ if (!row) {
267
+ throw new Error(`Approval request "${requestId}" not found`);
268
+ }
269
+ if (row.status !== "pending") {
270
+ throw new Error(
271
+ `Approval request "${requestId}" is already ${row.status} and cannot be updated`
272
+ );
273
+ }
274
+ const now = /* @__PURE__ */ new Date();
275
+ await db.update(approvalRequests).set({ status: newStatus, respondedAt: now, respondedBy: respondedBy ?? null }).where(eq(approvalRequests.id, requestId));
276
+ return rowToApproval({
277
+ ...row,
278
+ status: newStatus,
279
+ respondedAt: now,
280
+ respondedBy: respondedBy ?? null
281
+ });
282
+ }
283
+ async function approve(requestId, respondedBy) {
284
+ return resolve(requestId, "approved", respondedBy);
285
+ }
286
+ async function deny(requestId, respondedBy) {
287
+ return resolve(requestId, "denied", respondedBy);
288
+ }
289
+ async function get(requestId) {
290
+ const rows = await db.select().from(approvalRequests).where(eq(approvalRequests.id, requestId)).limit(1);
291
+ const row = rows[0];
292
+ if (!row) return null;
293
+ return rowToApproval(row);
294
+ }
295
+ async function listPending(userId) {
296
+ const conditions = [eq(approvalRequests.status, "pending")];
297
+ if (userId) conditions.push(eq(approvalRequests.userId, userId));
298
+ const rows = await db.select().from(approvalRequests).where(and(...conditions));
299
+ return rows.map(rowToApproval);
300
+ }
301
+ async function cleanup() {
302
+ const now = /* @__PURE__ */ new Date();
303
+ const expiredRows = await db.select({ id: approvalRequests.id }).from(approvalRequests).where(and(eq(approvalRequests.status, "pending"), lt(approvalRequests.expiresAt, now)));
304
+ if (expiredRows.length === 0) return { expired: 0 };
305
+ await db.update(approvalRequests).set({ status: "expired" }).where(and(eq(approvalRequests.status, "pending"), lt(approvalRequests.expiresAt, now)));
306
+ return { expired: expiredRows.length };
307
+ }
308
+ return {
309
+ request,
310
+ approve,
311
+ deny,
312
+ get,
313
+ listPending,
314
+ cleanup
315
+ };
316
+ }
17
317
  async function createDatabase(config) {
18
318
  if (config.provider === "sqlite") {
19
319
  const sqlite = new BetterSqlite3(config.url);
@@ -71,14 +371,43 @@ function buildStatements(provider) {
71
371
  // kavach_users
72
372
  // ------------------------------------------------------------------
73
373
  `CREATE TABLE ${ifne} kavach_users (
74
- id TEXT NOT NULL PRIMARY KEY,
75
- email TEXT NOT NULL UNIQUE,
76
- name TEXT,
77
- external_id TEXT,
78
- external_provider TEXT,
79
- metadata ${json},
80
- created_at ${ts} NOT NULL,
81
- updated_at ${ts} NOT NULL
374
+ id TEXT NOT NULL PRIMARY KEY,
375
+ email TEXT NOT NULL UNIQUE,
376
+ username TEXT UNIQUE,
377
+ name TEXT,
378
+ external_id TEXT,
379
+ external_provider TEXT,
380
+ metadata ${json},
381
+ banned ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
382
+ ban_reason TEXT,
383
+ ban_expires_at ${tsNull},
384
+ force_password_reset ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
385
+ stripe_customer_id TEXT UNIQUE,
386
+ stripe_subscription_id TEXT,
387
+ stripe_subscription_status TEXT,
388
+ stripe_price_id TEXT,
389
+ stripe_current_period_end ${tsNull},
390
+ stripe_cancel_at_period_end ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
391
+ polar_customer_id TEXT UNIQUE,
392
+ polar_subscription_id TEXT,
393
+ polar_subscription_status TEXT,
394
+ polar_product_id TEXT,
395
+ polar_current_period_end ${tsNull},
396
+ polar_cancel_at_period_end ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
397
+ created_at ${ts} NOT NULL,
398
+ updated_at ${ts} NOT NULL
399
+ )`,
400
+ // ------------------------------------------------------------------
401
+ // kavach_tenants (must come before kavach_agents – agents FK to tenants)
402
+ // ------------------------------------------------------------------
403
+ `CREATE TABLE ${ifne} kavach_tenants (
404
+ id TEXT NOT NULL PRIMARY KEY,
405
+ name TEXT NOT NULL,
406
+ slug TEXT NOT NULL UNIQUE,
407
+ settings ${json},
408
+ status TEXT NOT NULL DEFAULT 'active',
409
+ created_at ${ts} NOT NULL,
410
+ updated_at ${ts} NOT NULL
82
411
  )`,
83
412
  // ------------------------------------------------------------------
84
413
  // kavach_agents
@@ -86,6 +415,7 @@ function buildStatements(provider) {
86
415
  `CREATE TABLE ${ifne} kavach_agents (
87
416
  id TEXT NOT NULL PRIMARY KEY,
88
417
  owner_id TEXT NOT NULL REFERENCES kavach_users(id),
418
+ tenant_id TEXT REFERENCES kavach_tenants(id),
89
419
  name TEXT NOT NULL,
90
420
  type TEXT NOT NULL,
91
421
  status TEXT NOT NULL DEFAULT 'active',
@@ -223,7 +553,328 @@ function buildStatements(provider) {
223
553
  resource TEXT,
224
554
  expires_at ${ts} NOT NULL,
225
555
  created_at ${ts} NOT NULL
226
- )`
556
+ )`,
557
+ // ------------------------------------------------------------------
558
+ // kavach_budget_policies
559
+ // ------------------------------------------------------------------
560
+ `CREATE TABLE ${ifne} kavach_budget_policies (
561
+ id TEXT NOT NULL PRIMARY KEY,
562
+ agent_id TEXT REFERENCES kavach_agents(id) ON DELETE CASCADE,
563
+ user_id TEXT REFERENCES kavach_users(id),
564
+ tenant_id TEXT REFERENCES kavach_tenants(id),
565
+ limits ${json} NOT NULL,
566
+ current_usage ${json} NOT NULL,
567
+ action TEXT NOT NULL DEFAULT 'warn',
568
+ status TEXT NOT NULL DEFAULT 'active',
569
+ created_at ${ts} NOT NULL
570
+ )`,
571
+ // ------------------------------------------------------------------
572
+ // kavach_agent_cards (A2A discovery)
573
+ // ------------------------------------------------------------------
574
+ `CREATE TABLE ${ifne} kavach_agent_cards (
575
+ id TEXT NOT NULL PRIMARY KEY,
576
+ agent_id TEXT NOT NULL REFERENCES kavach_agents(id) ON DELETE CASCADE,
577
+ name TEXT NOT NULL,
578
+ description TEXT,
579
+ version TEXT NOT NULL,
580
+ protocols ${json} NOT NULL,
581
+ capabilities ${json} NOT NULL,
582
+ auth_requirements ${json} NOT NULL,
583
+ endpoint TEXT,
584
+ metadata ${json},
585
+ created_at ${ts} NOT NULL,
586
+ updated_at ${ts} NOT NULL
587
+ )`,
588
+ // ------------------------------------------------------------------
589
+ // kavach_approval_requests (CIBA async approval flows)
590
+ // ------------------------------------------------------------------
591
+ `CREATE TABLE ${ifne} kavach_approval_requests (
592
+ id TEXT NOT NULL PRIMARY KEY,
593
+ agent_id TEXT NOT NULL REFERENCES kavach_agents(id) ON DELETE CASCADE,
594
+ user_id TEXT NOT NULL REFERENCES kavach_users(id),
595
+ action TEXT NOT NULL,
596
+ resource TEXT NOT NULL,
597
+ arguments ${json},
598
+ status TEXT NOT NULL DEFAULT 'pending',
599
+ expires_at ${ts} NOT NULL,
600
+ responded_at ${tsNull},
601
+ responded_by TEXT,
602
+ created_at ${ts} NOT NULL
603
+ )`,
604
+ // ------------------------------------------------------------------
605
+ // kavach_trust_scores (graduated autonomy scoring)
606
+ // ------------------------------------------------------------------
607
+ `CREATE TABLE ${ifne} kavach_trust_scores (
608
+ agent_id TEXT NOT NULL PRIMARY KEY REFERENCES kavach_agents(id) ON DELETE CASCADE,
609
+ score INTEGER NOT NULL,
610
+ level TEXT NOT NULL,
611
+ factors ${json} NOT NULL,
612
+ computed_at ${ts} NOT NULL
613
+ )`,
614
+ // ------------------------------------------------------------------
615
+ // kavach_organizations
616
+ // ------------------------------------------------------------------
617
+ `CREATE TABLE ${ifne} kavach_organizations (
618
+ id TEXT NOT NULL PRIMARY KEY,
619
+ name TEXT NOT NULL,
620
+ slug TEXT NOT NULL UNIQUE,
621
+ owner_id TEXT NOT NULL REFERENCES kavach_users(id),
622
+ metadata ${json},
623
+ created_at ${ts} NOT NULL,
624
+ updated_at ${ts} NOT NULL
625
+ )`,
626
+ // ------------------------------------------------------------------
627
+ // kavach_org_members
628
+ // ------------------------------------------------------------------
629
+ `CREATE TABLE ${ifne} kavach_org_members (
630
+ id TEXT NOT NULL PRIMARY KEY,
631
+ org_id TEXT NOT NULL REFERENCES kavach_organizations(id) ON DELETE CASCADE,
632
+ user_id TEXT NOT NULL REFERENCES kavach_users(id),
633
+ role TEXT NOT NULL DEFAULT 'member',
634
+ joined_at ${ts} NOT NULL,
635
+ UNIQUE(org_id, user_id)
636
+ )`,
637
+ // ------------------------------------------------------------------
638
+ // kavach_org_invitations
639
+ // ------------------------------------------------------------------
640
+ `CREATE TABLE ${ifne} kavach_org_invitations (
641
+ id TEXT NOT NULL PRIMARY KEY,
642
+ org_id TEXT NOT NULL REFERENCES kavach_organizations(id) ON DELETE CASCADE,
643
+ email TEXT NOT NULL,
644
+ role TEXT NOT NULL DEFAULT 'member',
645
+ invited_by TEXT NOT NULL REFERENCES kavach_users(id),
646
+ status TEXT NOT NULL DEFAULT 'pending',
647
+ expires_at ${ts} NOT NULL,
648
+ created_at ${ts} NOT NULL
649
+ )`,
650
+ // ------------------------------------------------------------------
651
+ // kavach_org_roles
652
+ // ------------------------------------------------------------------
653
+ `CREATE TABLE ${ifne} kavach_org_roles (
654
+ id TEXT NOT NULL PRIMARY KEY,
655
+ org_id TEXT NOT NULL REFERENCES kavach_organizations(id) ON DELETE CASCADE,
656
+ name TEXT NOT NULL,
657
+ permissions ${json} NOT NULL,
658
+ UNIQUE(org_id, name)
659
+ )`,
660
+ // ------------------------------------------------------------------
661
+ // kavach_passkey_credentials (WebAuthn / FIDO2 passkeys)
662
+ // ------------------------------------------------------------------
663
+ `CREATE TABLE ${ifne} kavach_passkey_credentials (
664
+ id TEXT NOT NULL PRIMARY KEY,
665
+ user_id TEXT NOT NULL REFERENCES kavach_users(id),
666
+ credential_id TEXT NOT NULL UNIQUE,
667
+ public_key TEXT NOT NULL,
668
+ counter INTEGER NOT NULL DEFAULT 0,
669
+ device_name TEXT,
670
+ transports TEXT,
671
+ created_at ${ts} NOT NULL,
672
+ last_used_at ${ts} NOT NULL
673
+ )`,
674
+ // ------------------------------------------------------------------
675
+ // kavach_passkey_challenges (short-lived WebAuthn challenges)
676
+ // ------------------------------------------------------------------
677
+ `CREATE TABLE ${ifne} kavach_passkey_challenges (
678
+ id TEXT NOT NULL PRIMARY KEY,
679
+ challenge TEXT NOT NULL UNIQUE,
680
+ user_id TEXT,
681
+ type TEXT NOT NULL,
682
+ expires_at ${ts} NOT NULL,
683
+ created_at ${ts} NOT NULL
684
+ )`,
685
+ // ------------------------------------------------------------------
686
+ // kavach_one_time_tokens (email verify, password reset, invitation)
687
+ // ------------------------------------------------------------------
688
+ `CREATE TABLE ${ifne} kavach_one_time_tokens (
689
+ id TEXT NOT NULL PRIMARY KEY,
690
+ token_hash TEXT NOT NULL UNIQUE,
691
+ purpose TEXT NOT NULL,
692
+ identifier TEXT NOT NULL,
693
+ metadata ${json},
694
+ used ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
695
+ expires_at ${ts} NOT NULL,
696
+ created_at ${ts} NOT NULL
697
+ )`,
698
+ // ------------------------------------------------------------------
699
+ // kavach_agent_dids (W3C Decentralized Identifiers per agent)
700
+ // ------------------------------------------------------------------
701
+ `CREATE TABLE ${ifne} kavach_agent_dids (
702
+ agent_id TEXT NOT NULL PRIMARY KEY REFERENCES kavach_agents(id) ON DELETE CASCADE,
703
+ did TEXT NOT NULL UNIQUE,
704
+ method TEXT NOT NULL,
705
+ public_key_jwk TEXT NOT NULL,
706
+ did_document TEXT NOT NULL,
707
+ created_at ${ts} NOT NULL
708
+ )`,
709
+ // ------------------------------------------------------------------
710
+ // kavach_magic_links (passwordless email login)
711
+ // ------------------------------------------------------------------
712
+ `CREATE TABLE ${ifne} kavach_magic_links (
713
+ id TEXT NOT NULL PRIMARY KEY,
714
+ email TEXT NOT NULL,
715
+ token TEXT NOT NULL UNIQUE,
716
+ expires_at ${ts} NOT NULL,
717
+ used ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
718
+ created_at ${ts} NOT NULL
719
+ )`,
720
+ // ------------------------------------------------------------------
721
+ // kavach_email_otps (one-time password login)
722
+ // ------------------------------------------------------------------
723
+ `CREATE TABLE ${ifne} kavach_email_otps (
724
+ id TEXT NOT NULL PRIMARY KEY,
725
+ email TEXT NOT NULL,
726
+ code_hash TEXT NOT NULL,
727
+ expires_at ${ts} NOT NULL,
728
+ attempts INTEGER NOT NULL DEFAULT 0,
729
+ created_at ${ts} NOT NULL
730
+ )`,
731
+ // ------------------------------------------------------------------
732
+ // kavach_totp (TOTP two-factor authentication)
733
+ // ------------------------------------------------------------------
734
+ `CREATE TABLE ${ifne} kavach_totp (
735
+ user_id TEXT NOT NULL PRIMARY KEY REFERENCES kavach_users(id),
736
+ secret TEXT NOT NULL,
737
+ enabled ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
738
+ backup_codes ${json} NOT NULL,
739
+ created_at ${ts} NOT NULL,
740
+ updated_at ${ts} NOT NULL
741
+ )`,
742
+ // ------------------------------------------------------------------
743
+ // kavach_sso_connections (SAML 2.0 / OIDC enterprise SSO)
744
+ // ------------------------------------------------------------------
745
+ `CREATE TABLE ${ifne} kavach_sso_connections (
746
+ id TEXT NOT NULL PRIMARY KEY,
747
+ org_id TEXT NOT NULL,
748
+ provider_id TEXT NOT NULL,
749
+ type TEXT NOT NULL,
750
+ domain TEXT NOT NULL UNIQUE,
751
+ enabled INTEGER NOT NULL DEFAULT 1,
752
+ created_at ${ts} NOT NULL
753
+ )`,
754
+ // ------------------------------------------------------------------
755
+ // kavach_api_keys (static bearer tokens with permission scopes)
756
+ // ------------------------------------------------------------------
757
+ `CREATE TABLE ${ifne} kavach_api_keys (
758
+ id TEXT NOT NULL PRIMARY KEY,
759
+ user_id TEXT NOT NULL REFERENCES kavach_users(id),
760
+ name TEXT NOT NULL,
761
+ key_hash TEXT NOT NULL,
762
+ key_prefix TEXT NOT NULL,
763
+ permissions ${json} NOT NULL,
764
+ expires_at ${tsNull},
765
+ last_used_at ${tsNull},
766
+ created_at ${ts} NOT NULL
767
+ )`,
768
+ // ------------------------------------------------------------------
769
+ // kavach_username_accounts (username + password auth)
770
+ // ------------------------------------------------------------------
771
+ `CREATE TABLE ${ifne} kavach_username_accounts (
772
+ id TEXT NOT NULL PRIMARY KEY,
773
+ user_id TEXT NOT NULL REFERENCES kavach_users(id) ON DELETE CASCADE,
774
+ username TEXT NOT NULL UNIQUE,
775
+ password_hash TEXT NOT NULL,
776
+ created_at ${ts} NOT NULL,
777
+ updated_at ${ts} NOT NULL
778
+ )`,
779
+ // ------------------------------------------------------------------
780
+ // kavach_phone_verifications (SMS OTP)
781
+ // ------------------------------------------------------------------
782
+ `CREATE TABLE ${ifne} kavach_phone_verifications (
783
+ id TEXT NOT NULL PRIMARY KEY,
784
+ phone_number TEXT NOT NULL,
785
+ code_hash TEXT NOT NULL,
786
+ attempts INTEGER NOT NULL DEFAULT 0,
787
+ expires_at ${ts} NOT NULL,
788
+ created_at ${ts} NOT NULL
789
+ )`,
790
+ // ------------------------------------------------------------------
791
+ // kavach_trusted_devices (skip 2FA on trusted devices for a window)
792
+ // ------------------------------------------------------------------
793
+ `CREATE TABLE ${ifne} kavach_trusted_devices (
794
+ id TEXT NOT NULL PRIMARY KEY,
795
+ user_id TEXT NOT NULL REFERENCES kavach_users(id) ON DELETE CASCADE,
796
+ fingerprint TEXT NOT NULL,
797
+ label TEXT NOT NULL,
798
+ trusted_at ${ts} NOT NULL,
799
+ expires_at ${ts} NOT NULL
800
+ )`,
801
+ // ------------------------------------------------------------------
802
+ // kavach_login_history (last-login method tracking per user)
803
+ // ------------------------------------------------------------------
804
+ `CREATE TABLE ${ifne} kavach_login_history (
805
+ id TEXT NOT NULL PRIMARY KEY,
806
+ user_id TEXT NOT NULL REFERENCES kavach_users(id) ON DELETE CASCADE,
807
+ method TEXT NOT NULL,
808
+ ip TEXT,
809
+ user_agent TEXT,
810
+ timestamp ${ts} NOT NULL
811
+ )`,
812
+ `CREATE INDEX ${ifne} kavach_login_history_user_ts
813
+ ON kavach_login_history (user_id, timestamp DESC)`,
814
+ // ------------------------------------------------------------------
815
+ // kavach_oidc_clients (OIDC Provider — registered relying parties)
816
+ // ------------------------------------------------------------------
817
+ `CREATE TABLE ${ifne} kavach_oidc_clients (
818
+ id TEXT NOT NULL PRIMARY KEY,
819
+ client_id TEXT NOT NULL UNIQUE,
820
+ client_secret_hash TEXT NOT NULL,
821
+ client_name TEXT NOT NULL,
822
+ redirect_uris ${json} NOT NULL,
823
+ grant_types ${json} NOT NULL,
824
+ response_types ${json} NOT NULL,
825
+ scopes ${json} NOT NULL,
826
+ token_endpoint_auth_method TEXT NOT NULL DEFAULT 'client_secret_post',
827
+ created_at ${ts} NOT NULL,
828
+ updated_at ${ts} NOT NULL
829
+ )`,
830
+ // ------------------------------------------------------------------
831
+ // kavach_oidc_auth_codes (OIDC Provider — authorization codes)
832
+ // ------------------------------------------------------------------
833
+ `CREATE TABLE ${ifne} kavach_oidc_auth_codes (
834
+ id TEXT NOT NULL PRIMARY KEY,
835
+ code_hash TEXT NOT NULL UNIQUE,
836
+ client_id TEXT NOT NULL,
837
+ user_id TEXT NOT NULL,
838
+ redirect_uri TEXT NOT NULL,
839
+ scopes TEXT NOT NULL,
840
+ nonce TEXT,
841
+ code_challenge TEXT,
842
+ code_challenge_method TEXT,
843
+ used ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
844
+ expires_at ${ts} NOT NULL,
845
+ created_at ${ts} NOT NULL
846
+ )`,
847
+ // ------------------------------------------------------------------
848
+ // kavach_oidc_refresh_tokens (OIDC Provider — refresh tokens)
849
+ // ------------------------------------------------------------------
850
+ `CREATE TABLE ${ifne} kavach_oidc_refresh_tokens (
851
+ id TEXT NOT NULL PRIMARY KEY,
852
+ token_hash TEXT NOT NULL UNIQUE,
853
+ client_id TEXT NOT NULL,
854
+ user_id TEXT NOT NULL,
855
+ scopes TEXT NOT NULL,
856
+ revoked ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
857
+ expires_at ${ts} NOT NULL,
858
+ created_at ${ts} NOT NULL
859
+ )`,
860
+ // ------------------------------------------------------------------
861
+ // kavach_jwt_refresh_tokens (JWT session plugin — general purpose)
862
+ // ------------------------------------------------------------------
863
+ `CREATE TABLE ${ifne} kavach_jwt_refresh_tokens (
864
+ id TEXT NOT NULL PRIMARY KEY,
865
+ token_hash TEXT NOT NULL UNIQUE,
866
+ user_id TEXT NOT NULL REFERENCES kavach_users(id) ON DELETE CASCADE,
867
+ used ${bool} NOT NULL DEFAULT ${isPostgres ? "FALSE" : "0"},
868
+ expires_at ${ts} NOT NULL,
869
+ created_at ${ts} NOT NULL
870
+ )`,
871
+ `CREATE INDEX ${ifne} kavach_jwt_refresh_tokens_user_id
872
+ ON kavach_jwt_refresh_tokens (user_id)`
873
+ // ------------------------------------------------------------------
874
+ // kavach_users ban columns (ALTER TABLE IF NOT EXISTS — safe no-ops)
875
+ // These are appended as separate ALTER statements for existing DBs.
876
+ // For SQLite we use a separate migration path since SQLite ALTER is limited.
877
+ // ------------------------------------------------------------------
227
878
  ];
228
879
  }
229
880
  async function createTables(db, provider) {
@@ -269,10 +920,10 @@ async function createTables(db, provider) {
269
920
  }
270
921
  function isPermissionSubset(parentPerms, childPerms) {
271
922
  for (const childPerm of childPerms) {
272
- const parentMatch = parentPerms.find((p) => {
273
- if (!isResourceSubset(p.resource, childPerm.resource)) return false;
923
+ const parentMatch = parentPerms.find((p2) => {
924
+ if (!isResourceSubset(p2.resource, childPerm.resource)) return false;
274
925
  for (const action of childPerm.actions) {
275
- if (!p.actions.includes(action) && !p.actions.includes("*")) return false;
926
+ if (!p2.actions.includes(action) && !p2.actions.includes("*")) return false;
276
927
  }
277
928
  return true;
278
929
  });
@@ -315,9 +966,9 @@ function createDelegationModule(config) {
315
966
  id,
316
967
  fromAgentId: input.fromAgent,
317
968
  toAgentId: input.toAgent,
318
- permissions: input.permissions.map((p) => ({
319
- resource: p.resource,
320
- actions: p.actions
969
+ permissions: input.permissions.map((p2) => ({
970
+ resource: p2.resource,
971
+ actions: p2.actions
321
972
  })),
322
973
  depth: currentDepth,
323
974
  maxDepth,
@@ -370,9 +1021,9 @@ function createDelegationModule(config) {
370
1021
  id: c.id,
371
1022
  fromAgent: c.fromAgentId,
372
1023
  toAgent: c.toAgentId,
373
- permissions: c.permissions.map((p) => ({
374
- resource: p.resource,
375
- actions: p.actions
1024
+ permissions: c.permissions.map((p2) => ({
1025
+ resource: p2.resource,
1026
+ actions: p2.actions
376
1027
  })),
377
1028
  expiresAt: c.expiresAt,
378
1029
  depth: c.depth,
@@ -381,80 +1032,1469 @@ function createDelegationModule(config) {
381
1032
  }
382
1033
  return { delegate, revokeDelegation, getEffectivePermissions, listChains };
383
1034
  }
384
- var DEFAULT_MAX_AGE_SECONDS = 60 * 60 * 24 * 7;
385
- function createSessionManager(config, db) {
386
- if (!config.secret || config.secret.length < 32) {
387
- throw new Error("SessionManager: secret must be at least 32 characters.");
388
- }
389
- const maxAge = config.maxAge ?? DEFAULT_MAX_AGE_SECONDS;
390
- const keyBytes = new TextEncoder().encode(config.secret);
391
- const keyObject = createSecretKey(keyBytes);
392
- function rowToSession(row) {
1035
+ var BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
1036
+ function base58btcEncode(bytes) {
1037
+ let leadingZeros = 0;
1038
+ for (const byte of bytes) {
1039
+ if (byte !== 0) break;
1040
+ leadingZeros++;
1041
+ }
1042
+ const digits = [0];
1043
+ for (const byte of bytes) {
1044
+ let carry = byte;
1045
+ for (let i = 0; i < digits.length; i++) {
1046
+ carry += (digits[i] ?? 0) * 256;
1047
+ digits[i] = carry % 58;
1048
+ carry = Math.floor(carry / 58);
1049
+ }
1050
+ while (carry > 0) {
1051
+ digits.push(carry % 58);
1052
+ carry = Math.floor(carry / 58);
1053
+ }
1054
+ }
1055
+ const result = digits.reverse().map((d) => BASE58_ALPHABET[d] ?? "1").join("");
1056
+ return "1".repeat(leadingZeros) + result;
1057
+ }
1058
+ function base64urlToBytes(b64url) {
1059
+ const padded = b64url.replace(/-/g, "+").replace(/_/g, "/");
1060
+ const padLen = (4 - padded.length % 4) % 4;
1061
+ const b64 = padded + "=".repeat(padLen);
1062
+ const binary = atob(b64);
1063
+ const bytes = new Uint8Array(binary.length);
1064
+ for (let i = 0; i < binary.length; i++) {
1065
+ bytes[i] = binary.charCodeAt(i);
1066
+ }
1067
+ return bytes;
1068
+ }
1069
+ function publicKeyJwkToDidKey(publicKeyJwk) {
1070
+ if (!publicKeyJwk.x) {
1071
+ throw new Error("Ed25519 JWK must have an 'x' parameter");
1072
+ }
1073
+ const rawKey = base64urlToBytes(publicKeyJwk.x);
1074
+ const prefix = new Uint8Array([237, 1]);
1075
+ const multicodecKey = new Uint8Array(prefix.length + rawKey.length);
1076
+ multicodecKey.set(prefix);
1077
+ multicodecKey.set(rawKey, prefix.length);
1078
+ return `did:key:z${base58btcEncode(multicodecKey)}`;
1079
+ }
1080
+ function buildDidDocument(did, publicKeyJwk) {
1081
+ const keyId = `${did}#${did.slice("did:key:".length)}`;
1082
+ const verificationMethod = {
1083
+ id: keyId,
1084
+ type: "JsonWebKey2020",
1085
+ controller: did,
1086
+ publicKeyJwk
1087
+ };
1088
+ return {
1089
+ "@context": ["https://www.w3.org/ns/did/v1", "https://w3id.org/security/suites/jws-2020/v1"],
1090
+ id: did,
1091
+ controller: did,
1092
+ verificationMethod: [verificationMethod],
1093
+ authentication: [keyId],
1094
+ assertionMethod: [keyId],
1095
+ capabilityInvocation: [keyId],
1096
+ capabilityDelegation: [keyId]
1097
+ };
1098
+ }
1099
+ async function generateDidKey() {
1100
+ const { publicKey, privateKey } = await generateKeyPair("EdDSA", {
1101
+ crv: "Ed25519",
1102
+ extractable: true
1103
+ });
1104
+ const publicKeyJwk = await exportJWK(publicKey);
1105
+ const privateKeyJwk = await exportJWK(privateKey);
1106
+ publicKeyJwk.crv = "Ed25519";
1107
+ publicKeyJwk.kty = "OKP";
1108
+ privateKeyJwk.crv = "Ed25519";
1109
+ privateKeyJwk.kty = "OKP";
1110
+ const did = publicKeyJwkToDidKey(publicKeyJwk);
1111
+ const didDocument = buildDidDocument(did, publicKeyJwk);
1112
+ return {
1113
+ did,
1114
+ publicKeyJwk,
1115
+ privateKeyJwk,
1116
+ didDocument
1117
+ };
1118
+ }
1119
+ function resolveDidKey(did) {
1120
+ if (!did.startsWith("did:key:z")) return null;
1121
+ const keyId = `${did}#${did.slice("did:key:".length)}`;
1122
+ const verificationMethod = {
1123
+ id: keyId,
1124
+ type: "JsonWebKey2020",
1125
+ controller: did,
1126
+ // Public key JWK is not reconstructed here — callers who need to verify
1127
+ // signatures supply the JWK separately via verifyPayload().
1128
+ publicKeyJwk: { kty: "OKP", crv: "Ed25519" }
1129
+ };
1130
+ return {
1131
+ "@context": ["https://www.w3.org/ns/did/v1", "https://w3id.org/security/suites/jws-2020/v1"],
1132
+ id: did,
1133
+ controller: did,
1134
+ verificationMethod: [verificationMethod],
1135
+ authentication: [keyId],
1136
+ assertionMethod: [keyId],
1137
+ capabilityInvocation: [keyId],
1138
+ capabilityDelegation: [keyId]
1139
+ };
1140
+ }
1141
+ async function signPayload(payload, privateKeyJwk, did) {
1142
+ const privateKey = await importJWK(privateKeyJwk, "EdDSA");
1143
+ const kid = `${did}#${did.split(":").pop() ?? "key-1"}`;
1144
+ const jws = await new SignJWT(payload).setProtectedHeader({ alg: "EdDSA", kid }).setIssuer(did).setIssuedAt().sign(privateKey);
1145
+ return {
1146
+ jws,
1147
+ payload,
1148
+ issuer: did
1149
+ };
1150
+ }
1151
+ async function verifyPayload(jws, publicKeyJwk) {
1152
+ try {
1153
+ const publicKey = await importJWK(publicKeyJwk, "EdDSA");
1154
+ const { payload } = await jwtVerify(jws, publicKey);
1155
+ const issuer = typeof payload.iss === "string" ? payload.iss : void 0;
1156
+ const { iss, iat, exp, nbf, jti, aud, sub, ...rest } = payload;
1157
+ void iss;
1158
+ void iat;
1159
+ void exp;
1160
+ void nbf;
1161
+ void jti;
1162
+ void aud;
1163
+ void sub;
1164
+ return {
1165
+ valid: true,
1166
+ payload: rest,
1167
+ issuer
1168
+ };
1169
+ } catch (err) {
393
1170
  return {
394
- id: row.id,
395
- userId: row.userId,
396
- expiresAt: row.expiresAt,
397
- createdAt: row.createdAt,
398
- ...row.metadata !== null && { metadata: row.metadata }
1171
+ valid: false,
1172
+ error: err instanceof Error ? err.message : "Verification failed"
399
1173
  };
400
1174
  }
401
- async function create(userId, metadata) {
402
- const id = randomUUID();
403
- const now = /* @__PURE__ */ new Date();
404
- const expiresAt = new Date(now.getTime() + maxAge * 1e3);
405
- await db.insert(sessions).values({
406
- id,
407
- userId,
408
- expiresAt,
409
- metadata: metadata ?? null,
410
- createdAt: now
411
- });
412
- const token = await new SignJWT({ sub: id }).setProtectedHeader({ alg: "HS256" }).setIssuedAt().setExpirationTime(Math.floor(expiresAt.getTime() / 1e3)).sign(keyObject);
413
- const session = {
414
- id,
415
- userId,
416
- expiresAt,
417
- createdAt: now,
418
- ...metadata !== void 0 && { metadata }
1175
+ }
1176
+ async function createPresentation(options) {
1177
+ const { agentId, did, privateKeyJwk, capabilities, audience, expiresIn = 300 } = options;
1178
+ const privateKey = await importJWK(privateKeyJwk, "EdDSA");
1179
+ const kid = `${did}#${did.split(":").pop() ?? "key-1"}`;
1180
+ const builder = new SignJWT({
1181
+ agentId,
1182
+ capabilities,
1183
+ type: "VerifiablePresentation"
1184
+ }).setProtectedHeader({ alg: "EdDSA", kid }).setIssuer(did).setSubject(agentId).setIssuedAt().setExpirationTime(Math.floor(Date.now() / 1e3) + expiresIn);
1185
+ if (audience) {
1186
+ builder.setAudience(audience);
1187
+ }
1188
+ return builder.sign(privateKey);
1189
+ }
1190
+ async function verifyPresentation(jwt, publicKeyJwk) {
1191
+ try {
1192
+ const publicKey = await importJWK(publicKeyJwk, "EdDSA");
1193
+ const { payload } = await jwtVerify(jwt, publicKey);
1194
+ const agentId = typeof payload.agentId === "string" ? payload.agentId : void 0;
1195
+ const did = typeof payload.iss === "string" ? payload.iss : void 0;
1196
+ const capabilities = Array.isArray(payload.capabilities) ? payload.capabilities : void 0;
1197
+ return {
1198
+ valid: true,
1199
+ agentId,
1200
+ did,
1201
+ capabilities
1202
+ };
1203
+ } catch (err) {
1204
+ return {
1205
+ valid: false,
1206
+ error: err instanceof Error ? err.message : "Presentation verification failed"
419
1207
  };
420
- return { session, token };
421
1208
  }
422
- async function validate(token) {
423
- let sessionId;
424
- try {
425
- const { payload } = await jwtVerify(token, keyObject);
426
- if (typeof payload.sub !== "string" || !payload.sub) return null;
427
- sessionId = payload.sub;
428
- } catch {
429
- return null;
430
- }
431
- const now = /* @__PURE__ */ new Date();
432
- const rows = await db.select().from(sessions).where(and(eq(sessions.id, sessionId)));
433
- const row = rows[0];
434
- if (!row) return null;
435
- if (row.expiresAt <= now) {
436
- await db.delete(sessions).where(eq(sessions.id, sessionId));
437
- return null;
438
- }
439
- return rowToSession(row);
1209
+ }
1210
+
1211
+ // src/did/web-method.ts
1212
+ function buildDidWeb(config, agentId) {
1213
+ const domain = config.domain.replace(/\//g, ":");
1214
+ if (config.path) {
1215
+ const path = config.path.replace(/\//g, ":").replace(/^:|:$/g, "");
1216
+ return `did:web:${domain}:${path}:${agentId}`;
440
1217
  }
441
- async function revoke(sessionId) {
442
- await db.delete(sessions).where(eq(sessions.id, sessionId));
1218
+ return `did:web:${domain}:${agentId}`;
1219
+ }
1220
+ async function generateDidWeb(config, agentId) {
1221
+ const { publicKeyJwk, privateKeyJwk } = await generateDidKey();
1222
+ const did = buildDidWeb(config, agentId);
1223
+ const didDocument = buildDidDocument(did, publicKeyJwk);
1224
+ return {
1225
+ did,
1226
+ publicKeyJwk,
1227
+ privateKeyJwk,
1228
+ didDocument
1229
+ };
1230
+ }
1231
+ function getDidWebUrl(did) {
1232
+ if (!did.startsWith("did:web:")) {
1233
+ throw new Error(`Not a did:web identifier: ${did}`);
443
1234
  }
444
- async function revokeAll(userId) {
445
- await db.delete(sessions).where(eq(sessions.userId, userId));
1235
+ const methodSpecific = did.slice("did:web:".length);
1236
+ const parts = methodSpecific.split(":");
1237
+ const decoded = parts.map((p2) => decodeURIComponent(p2));
1238
+ if (decoded.length === 1) {
1239
+ return `https://${decoded[0]}/.well-known/did.json`;
446
1240
  }
447
- async function list(userId) {
448
- const now = /* @__PURE__ */ new Date();
449
- const rows = await db.select().from(sessions).where(and(eq(sessions.userId, userId)));
450
- return rows.filter((row) => row.expiresAt > now).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()).map(rowToSession);
1241
+ const domain = decoded[0];
1242
+ const pathSegments = decoded.slice(1);
1243
+ return `https://${domain}/${pathSegments.join("/")}/did.json`;
1244
+ }
1245
+ async function resolveDidWeb(did) {
1246
+ let url;
1247
+ try {
1248
+ url = getDidWebUrl(did);
1249
+ } catch {
1250
+ return null;
1251
+ }
1252
+ try {
1253
+ const response = await fetch(url, {
1254
+ headers: { Accept: "application/json" }
1255
+ });
1256
+ if (!response.ok) return null;
1257
+ const doc = await response.json();
1258
+ if (!doc["@context"] || !doc.id) return null;
1259
+ return doc;
1260
+ } catch {
1261
+ return null;
451
1262
  }
452
- return { create, validate, revoke, revokeAll, list };
453
1263
  }
454
1264
 
455
- // src/kavach.ts
456
- async function createKavach(config) {
457
- const authAdapter = config.auth?.adapter ?? null;
1265
+ // src/did/module.ts
1266
+ function createDidModule(db, config) {
1267
+ async function generateKey(agentId) {
1268
+ const keyPair = await generateDidKey();
1269
+ const now = /* @__PURE__ */ new Date();
1270
+ await db.insert(agentDids).values({
1271
+ agentId,
1272
+ did: keyPair.did,
1273
+ method: "key",
1274
+ publicKeyJwk: JSON.stringify(keyPair.publicKeyJwk),
1275
+ didDocument: JSON.stringify(keyPair.didDocument),
1276
+ createdAt: now
1277
+ });
1278
+ const agentDid = {
1279
+ agentId,
1280
+ did: keyPair.did,
1281
+ method: "key",
1282
+ publicKeyJwk: keyPair.publicKeyJwk,
1283
+ didDocument: keyPair.didDocument,
1284
+ createdAt: now
1285
+ };
1286
+ return { agentDid, privateKeyJwk: keyPair.privateKeyJwk };
1287
+ }
1288
+ async function generateWeb(agentId) {
1289
+ if (!config?.web) {
1290
+ throw new Error(
1291
+ "did:web requires a web config (domain). Pass { web: { domain: 'example.com' } } to createDidModule()."
1292
+ );
1293
+ }
1294
+ const keyPair = await generateDidWeb(config.web, agentId);
1295
+ const now = /* @__PURE__ */ new Date();
1296
+ await db.insert(agentDids).values({
1297
+ agentId,
1298
+ did: keyPair.did,
1299
+ method: "web",
1300
+ publicKeyJwk: JSON.stringify(keyPair.publicKeyJwk),
1301
+ didDocument: JSON.stringify(keyPair.didDocument),
1302
+ createdAt: now
1303
+ });
1304
+ const agentDid = {
1305
+ agentId,
1306
+ did: keyPair.did,
1307
+ method: "web",
1308
+ publicKeyJwk: keyPair.publicKeyJwk,
1309
+ didDocument: keyPair.didDocument,
1310
+ createdAt: now
1311
+ };
1312
+ return { agentDid, privateKeyJwk: keyPair.privateKeyJwk };
1313
+ }
1314
+ async function resolve(did) {
1315
+ if (did.startsWith("did:key:")) {
1316
+ return resolveDidKey(did);
1317
+ }
1318
+ if (did.startsWith("did:web:")) {
1319
+ return resolveDidWeb(did);
1320
+ }
1321
+ return null;
1322
+ }
1323
+ async function getAgentDid(agentId) {
1324
+ const rows = await db.select().from(agentDids).where(eq(agentDids.agentId, agentId));
1325
+ const row = rows[0];
1326
+ if (!row) return null;
1327
+ return {
1328
+ agentId: row.agentId,
1329
+ did: row.did,
1330
+ method: row.method,
1331
+ publicKeyJwk: JSON.parse(row.publicKeyJwk),
1332
+ didDocument: JSON.parse(row.didDocument),
1333
+ createdAt: row.createdAt
1334
+ };
1335
+ }
1336
+ async function sign(agentId, payload, privateKeyJwk) {
1337
+ const agentDid = await getAgentDid(agentId);
1338
+ if (!agentDid) {
1339
+ throw new Error(`No DID found for agent "${agentId}". Call generateKey() first.`);
1340
+ }
1341
+ return signPayload(payload, privateKeyJwk, agentDid.did);
1342
+ }
1343
+ async function verify(jws, did) {
1344
+ if (!did) {
1345
+ return {
1346
+ valid: false,
1347
+ error: "A DID is required to look up the public key for verification."
1348
+ };
1349
+ }
1350
+ const rows = await db.select().from(agentDids).where(eq(agentDids.did, did));
1351
+ const row = rows[0];
1352
+ if (!row) {
1353
+ return {
1354
+ valid: false,
1355
+ error: `No stored public key found for DID "${did}"`
1356
+ };
1357
+ }
1358
+ const publicKeyJwk = JSON.parse(row.publicKeyJwk);
1359
+ return verifyPayload(jws, publicKeyJwk);
1360
+ }
1361
+ async function createPresentationForAgent(options) {
1362
+ const agentDid = await getAgentDid(options.agentId);
1363
+ if (!agentDid) {
1364
+ throw new Error(`No DID found for agent "${options.agentId}". Call generateKey() first.`);
1365
+ }
1366
+ return createPresentation({
1367
+ agentId: options.agentId,
1368
+ did: agentDid.did,
1369
+ privateKeyJwk: options.privateKeyJwk,
1370
+ capabilities: options.capabilities,
1371
+ audience: options.audience,
1372
+ expiresIn: options.expiresIn
1373
+ });
1374
+ }
1375
+ async function verifyPresentationForAgent(jwt) {
1376
+ const parts = jwt.split(".");
1377
+ if (parts.length !== 3) {
1378
+ return { valid: false, error: "Malformed JWT: expected 3 parts" };
1379
+ }
1380
+ let issuerDid;
1381
+ try {
1382
+ const payloadPart = parts[1] ?? "";
1383
+ const padded = payloadPart.replace(/-/g, "+").replace(/_/g, "/");
1384
+ const padLen = (4 - padded.length % 4) % 4;
1385
+ const decoded = atob(padded + "=".repeat(padLen));
1386
+ const claims = JSON.parse(decoded);
1387
+ issuerDid = typeof claims.iss === "string" ? claims.iss : void 0;
1388
+ } catch {
1389
+ return { valid: false, error: "Failed to decode JWT payload" };
1390
+ }
1391
+ if (!issuerDid) {
1392
+ return { valid: false, error: "JWT missing 'iss' claim" };
1393
+ }
1394
+ const rows = await db.select().from(agentDids).where(eq(agentDids.did, issuerDid));
1395
+ const row = rows[0];
1396
+ if (!row) {
1397
+ return {
1398
+ valid: false,
1399
+ error: `No stored public key found for DID "${issuerDid}"`
1400
+ };
1401
+ }
1402
+ const publicKeyJwk = JSON.parse(row.publicKeyJwk);
1403
+ const result = await verifyPresentation(jwt, publicKeyJwk);
1404
+ if (!result.valid) {
1405
+ return { valid: false, error: result.error };
1406
+ }
1407
+ return {
1408
+ valid: true,
1409
+ issuer: result.did,
1410
+ payload: void 0,
1411
+ capabilities: result.capabilities
1412
+ };
1413
+ }
1414
+ return {
1415
+ generateKey,
1416
+ generateWeb,
1417
+ resolve,
1418
+ getAgentDid,
1419
+ sign,
1420
+ verify,
1421
+ createPresentation: createPresentationForAgent,
1422
+ verifyPresentation: verifyPresentationForAgent
1423
+ };
1424
+ }
1425
+
1426
+ // src/email/templates.ts
1427
+ var OUTER_STYLES = 'font-family:Inter,-apple-system,BlinkMacSystemFont,"Segoe UI",sans-serif;background:#f4f4f5;margin:0;padding:0;';
1428
+ var CONTAINER_STYLES = "max-width:560px;margin:32px auto;background:#ffffff;border-radius:8px;overflow:hidden;";
1429
+ var HEADER_STYLES = "background:#C9A84C;padding:24px 32px;";
1430
+ var HEADER_H1_STYLES = "color:#ffffff;margin:0;font-size:20px;font-weight:600;letter-spacing:-0.3px;";
1431
+ var BODY_STYLES = "padding:32px;";
1432
+ var P_STYLES = "margin:0 0 16px;color:#3f3f46;font-size:15px;line-height:1.6;";
1433
+ var CODE_STYLES = "display:inline-block;background:#fef9ec;border:1px solid #e9c97e;border-radius:6px;padding:12px 24px;font-family:JetBrains Mono,monospace;font-size:24px;font-weight:700;letter-spacing:4px;color:#8B6914;";
1434
+ var BUTTON_STYLES = "display:inline-block;background:#C9A84C;color:#ffffff;text-decoration:none;padding:12px 24px;border-radius:6px;font-size:15px;font-weight:600;";
1435
+ var FOOTER_STYLES = "border-top:1px solid #e4e4e7;padding:16px 32px;color:#a1a1aa;font-size:13px;";
1436
+ function html(appName, title, body) {
1437
+ return `<!DOCTYPE html>
1438
+ <html lang="en">
1439
+ <head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>${title}</title></head>
1440
+ <body style="${OUTER_STYLES}">
1441
+ <table width="100%" cellpadding="0" cellspacing="0" border="0"><tr><td>
1442
+ <div style="${CONTAINER_STYLES}">
1443
+ <div style="${HEADER_STYLES}"><h1 style="${HEADER_H1_STYLES}">${appName}</h1></div>
1444
+ <div style="${BODY_STYLES}">${body}</div>
1445
+ <div style="${FOOTER_STYLES}">You received this email because of activity on your ${appName} account.</div>
1446
+ </div>
1447
+ </td></tr></table>
1448
+ </body>
1449
+ </html>`;
1450
+ }
1451
+ function p(content) {
1452
+ return `<p style="${P_STYLES}">${content}</p>`;
1453
+ }
1454
+ function button(url, label) {
1455
+ return `<p style="margin:24px 0;"><a href="${url}" style="${BUTTON_STYLES}">${label}</a></p>`;
1456
+ }
1457
+ function code(value) {
1458
+ return `<p style="margin:24px 0;"><span style="${CODE_STYLES}">${value}</span></p>`;
1459
+ }
1460
+ function verificationTemplate(appName, appUrl, vars) {
1461
+ const email = vars.email ?? "";
1462
+ const verifyUrl = vars.verifyUrl ?? `${appUrl}/verify?token=${vars.token ?? ""}`;
1463
+ return {
1464
+ subject: `Verify your email - ${appName}`,
1465
+ text: [
1466
+ `Verify your email address`,
1467
+ ``,
1468
+ `Hi${email ? ` ${email}` : ""},`,
1469
+ ``,
1470
+ `Please verify your email address by visiting the link below:`,
1471
+ ``,
1472
+ verifyUrl,
1473
+ ``,
1474
+ `This link expires in 24 hours. If you did not create an account, you can ignore this email.`
1475
+ ].join("\n"),
1476
+ html: html(
1477
+ appName,
1478
+ `Verify your email`,
1479
+ [
1480
+ p(`Hi${email ? ` <strong>${email}</strong>` : ""},`),
1481
+ p("Please verify your email address to complete your sign-up."),
1482
+ button(verifyUrl, "Verify email"),
1483
+ p(`Or copy this link: <a href="${verifyUrl}" style="color:#C9A84C;">${verifyUrl}</a>`),
1484
+ p(
1485
+ `This link expires in 24 hours. If you did not create an account, you can safely ignore this email.`
1486
+ )
1487
+ ].join("")
1488
+ )
1489
+ };
1490
+ }
1491
+ function passwordResetTemplate(appName, appUrl, vars) {
1492
+ const email = vars.email ?? "";
1493
+ const resetUrl = vars.resetUrl ?? `${appUrl}/reset-password?token=${vars.token ?? ""}`;
1494
+ return {
1495
+ subject: `Reset your password - ${appName}`,
1496
+ text: [
1497
+ `Reset your password`,
1498
+ ``,
1499
+ `Hi${email ? ` ${email}` : ""},`,
1500
+ ``,
1501
+ `We received a request to reset your password. Click the link below to proceed:`,
1502
+ ``,
1503
+ resetUrl,
1504
+ ``,
1505
+ `This link expires in 1 hour. If you did not request a password reset, you can ignore this email.`
1506
+ ].join("\n"),
1507
+ html: html(
1508
+ appName,
1509
+ `Reset your password`,
1510
+ [
1511
+ p(`Hi${email ? ` <strong>${email}</strong>` : ""},`),
1512
+ p("We received a request to reset your password."),
1513
+ button(resetUrl, "Reset password"),
1514
+ p(`Or copy this link: <a href="${resetUrl}" style="color:#C9A84C;">${resetUrl}</a>`),
1515
+ p(
1516
+ `This link expires in 1 hour. If you did not request a password reset, you can safely ignore this email.`
1517
+ )
1518
+ ].join("")
1519
+ )
1520
+ };
1521
+ }
1522
+ function magicLinkTemplate(appName, _appUrl, vars) {
1523
+ const email = vars.email ?? "";
1524
+ const url = vars.url ?? "";
1525
+ return {
1526
+ subject: `Sign in to ${appName}`,
1527
+ text: [
1528
+ `Sign in to ${appName}`,
1529
+ ``,
1530
+ `Hi${email ? ` ${email}` : ""},`,
1531
+ ``,
1532
+ `Click the link below to sign in to your account. This link expires in 15 minutes and can only be used once.`,
1533
+ ``,
1534
+ url
1535
+ ].join("\n"),
1536
+ html: html(
1537
+ appName,
1538
+ `Sign in to ${appName}`,
1539
+ [
1540
+ p(`Hi${email ? ` <strong>${email}</strong>` : ""},`),
1541
+ p(
1542
+ "Click the button below to sign in. This link expires in 15 minutes and can only be used once."
1543
+ ),
1544
+ button(url, `Sign in to ${appName}`),
1545
+ p(`Or copy this link: <a href="${url}" style="color:#C9A84C;">${url}</a>`)
1546
+ ].join("")
1547
+ )
1548
+ };
1549
+ }
1550
+ function emailOtpTemplate(appName, _appUrl, vars) {
1551
+ const email = vars.email ?? "";
1552
+ const otpCode = vars.code ?? "";
1553
+ return {
1554
+ subject: `Your verification code: ${otpCode}`,
1555
+ text: [
1556
+ `Your verification code`,
1557
+ ``,
1558
+ `Hi${email ? ` ${email}` : ""},`,
1559
+ ``,
1560
+ `Your ${appName} verification code is:`,
1561
+ ``,
1562
+ otpCode,
1563
+ ``,
1564
+ `This code expires in 10 minutes. Do not share it with anyone.`
1565
+ ].join("\n"),
1566
+ html: html(
1567
+ appName,
1568
+ `Your verification code`,
1569
+ [
1570
+ p(`Hi${email ? ` <strong>${email}</strong>` : ""},`),
1571
+ p(`Your ${appName} verification code is:`),
1572
+ code(otpCode),
1573
+ p("This code expires in 10 minutes. Do not share it with anyone.")
1574
+ ].join("")
1575
+ )
1576
+ };
1577
+ }
1578
+ function invitationTemplate(appName, _appUrl, vars) {
1579
+ const email = vars.email ?? "";
1580
+ const orgName = vars.orgName ?? "an organization";
1581
+ const inviteUrl = vars.inviteUrl ?? "";
1582
+ return {
1583
+ subject: `You've been invited to ${orgName}`,
1584
+ text: [
1585
+ `You've been invited to ${orgName}`,
1586
+ ``,
1587
+ `Hi${email ? ` ${email}` : ""},`,
1588
+ ``,
1589
+ `You've been invited to join ${orgName} on ${appName}. Click the link below to accept:`,
1590
+ ``,
1591
+ inviteUrl,
1592
+ ``,
1593
+ `If you were not expecting this invitation, you can ignore this email.`
1594
+ ].join("\n"),
1595
+ html: html(
1596
+ appName,
1597
+ `You've been invited to ${orgName}`,
1598
+ [
1599
+ p(`Hi${email ? ` <strong>${email}</strong>` : ""},`),
1600
+ p(`You've been invited to join <strong>${orgName}</strong> on ${appName}.`),
1601
+ button(inviteUrl, `Accept invitation`),
1602
+ p(`Or copy this link: <a href="${inviteUrl}" style="color:#C9A84C;">${inviteUrl}</a>`),
1603
+ p("If you were not expecting this invitation, you can safely ignore this email.")
1604
+ ].join("")
1605
+ )
1606
+ };
1607
+ }
1608
+ function welcomeTemplate(appName, appUrl, vars) {
1609
+ const email = vars.email ?? "";
1610
+ const name = vars.name ?? email;
1611
+ return {
1612
+ subject: `Welcome to ${appName}`,
1613
+ text: [
1614
+ `Welcome to ${appName}`,
1615
+ ``,
1616
+ `Hi ${name},`,
1617
+ ``,
1618
+ `Your account is ready. Head over to ${appUrl} to get started.`,
1619
+ ``,
1620
+ `If you have any questions, reply to this email.`
1621
+ ].join("\n"),
1622
+ html: html(
1623
+ appName,
1624
+ `Welcome to ${appName}`,
1625
+ [
1626
+ p(`Hi <strong>${name}</strong>,`),
1627
+ p(`Your account is ready. Welcome to ${appName}.`),
1628
+ button(appUrl, `Get started`),
1629
+ p("If you have any questions, just reply to this email.")
1630
+ ].join("")
1631
+ )
1632
+ };
1633
+ }
1634
+ function createEmailTemplates(config = {}) {
1635
+ const appName = config.appName ?? "KavachOS";
1636
+ const appUrl = config.appUrl ?? "http://localhost:3000";
1637
+ const overrides = config.templates ?? {};
1638
+ function render(name, vars) {
1639
+ const override = overrides[name];
1640
+ if (override) {
1641
+ return override(vars);
1642
+ }
1643
+ switch (name) {
1644
+ case "verification":
1645
+ return verificationTemplate(appName, appUrl, vars);
1646
+ case "passwordReset":
1647
+ return passwordResetTemplate(appName, appUrl, vars);
1648
+ case "magicLink":
1649
+ return magicLinkTemplate(appName, appUrl, vars);
1650
+ case "emailOtp":
1651
+ return emailOtpTemplate(appName, appUrl, vars);
1652
+ case "invitation":
1653
+ return invitationTemplate(appName, appUrl, vars);
1654
+ case "welcome":
1655
+ return welcomeTemplate(appName, appUrl, vars);
1656
+ }
1657
+ }
1658
+ return { render };
1659
+ }
1660
+
1661
+ // src/hooks/lifecycle.ts
1662
+ function classifyViolation(reason) {
1663
+ const r = reason?.toLowerCase() ?? "";
1664
+ if (r.includes("rate") || r.includes("rate_limited")) return "rate_limited";
1665
+ if (r.includes("ip") || r.includes("allowlist")) return "ip_blocked";
1666
+ if (r.includes("time") || r.includes("window")) return "time_restricted";
1667
+ if (r.includes("approval")) return "approval_required";
1668
+ return "permission_denied";
1669
+ }
1670
+
1671
+ // src/i18n/locales/en.ts
1672
+ var en = {
1673
+ // Auth errors
1674
+ "auth.invalidCredentials": "Invalid email or password.",
1675
+ "auth.emailNotVerified": "Please verify your email address before signing in.",
1676
+ "auth.accountLocked": "Your account has been locked. Contact support to unlock it.",
1677
+ "auth.rateLimited": "Too many requests. Try again in {{retryAfter}} seconds.",
1678
+ "auth.emailAlreadyExists": "An account with that email already exists.",
1679
+ "auth.weakPassword": "Password is too weak. Use at least 8 characters with a mix of letters, numbers, and symbols.",
1680
+ "auth.tokenExpired": "This link has expired. Request a new one.",
1681
+ "auth.tokenInvalid": "This link is invalid or has already been used.",
1682
+ "auth.unauthorized": "You are not authorized to perform this action.",
1683
+ // Agent errors
1684
+ "agent.notFound": "Agent not found.",
1685
+ "agent.revoked": "This agent's access has been revoked.",
1686
+ "agent.limitExceeded": "Agent limit reached for this account.",
1687
+ "agent.permissionDenied": "Agent does not have permission to perform this action.",
1688
+ // 2FA
1689
+ "twoFactor.invalidCode": "Invalid verification code. Check your authenticator app and try again.",
1690
+ "twoFactor.alreadyEnabled": "Two-factor authentication is already enabled on this account.",
1691
+ "twoFactor.notEnabled": "Two-factor authentication is not enabled on this account.",
1692
+ // Email subjects
1693
+ "email.verification.subject": "Verify your email address",
1694
+ "email.passwordReset.subject": "Reset your password",
1695
+ "email.magicLink.subject": "Your sign-in link",
1696
+ "email.otp.subject": "Your one-time code",
1697
+ "email.invitation.subject": "You have been invited to join {{orgName}}",
1698
+ "email.welcome.subject": "Welcome to {{appName}}",
1699
+ // General
1700
+ "general.serverError": "Something went wrong. Try again later.",
1701
+ "general.badRequest": "The request could not be processed.",
1702
+ "general.notFound": "The requested resource was not found."
1703
+ };
1704
+
1705
+ // src/i18n/i18n.ts
1706
+ function interpolate(template, vars) {
1707
+ return template.replace(/\{\{(\w+)\}\}/g, (match, key) => {
1708
+ return Object.hasOwn(vars, key) ? vars[key] ?? match : `{{${key}}}`;
1709
+ });
1710
+ }
1711
+ function resolveLocale(requested, registry, defaultLocale) {
1712
+ if (registry.has(requested)) return requested;
1713
+ const prefix = requested.split("-")[0];
1714
+ if (prefix && registry.has(prefix)) return prefix;
1715
+ if (registry.has(defaultLocale)) return defaultLocale;
1716
+ return "en";
1717
+ }
1718
+ function createI18n(config = {}) {
1719
+ const defaultLocale = config.defaultLocale ?? "en";
1720
+ const registry = /* @__PURE__ */ new Map();
1721
+ registry.set("en", { ...en });
1722
+ if (config.translations) {
1723
+ for (const [locale, keys] of Object.entries(config.translations)) {
1724
+ const existing = registry.get(locale) ?? {};
1725
+ registry.set(locale, { ...existing, ...keys });
1726
+ }
1727
+ }
1728
+ function lookup(key, locale) {
1729
+ const resolved = resolveLocale(locale, registry, defaultLocale);
1730
+ const localeMap = registry.get(resolved);
1731
+ if (localeMap && key in localeMap) {
1732
+ return localeMap[key];
1733
+ }
1734
+ const englishMap = registry.get("en");
1735
+ if (englishMap && key in englishMap) {
1736
+ return englishMap[key];
1737
+ }
1738
+ return key;
1739
+ }
1740
+ function t(key, varsOrLocale, maybeLocale) {
1741
+ if (typeof varsOrLocale === "string" || varsOrLocale === void 0) {
1742
+ const locale2 = varsOrLocale ?? defaultLocale;
1743
+ return lookup(key, locale2);
1744
+ }
1745
+ const locale = maybeLocale ?? defaultLocale;
1746
+ const raw = lookup(key, locale);
1747
+ return interpolate(raw, varsOrLocale);
1748
+ }
1749
+ function addLocale(locale, translations) {
1750
+ const existing = registry.get(locale) ?? {};
1751
+ registry.set(locale, { ...existing, ...translations });
1752
+ }
1753
+ function getLocales() {
1754
+ return Array.from(registry.keys());
1755
+ }
1756
+ return { t, addLocale, getLocales };
1757
+ }
1758
+
1759
+ // src/i18n/locales/de.ts
1760
+ var de = {
1761
+ // Auth errors
1762
+ "auth.invalidCredentials": "Ung\xFCltige E-Mail-Adresse oder falsches Passwort.",
1763
+ "auth.emailNotVerified": "Bitte best\xE4tige deine E-Mail-Adresse, bevor du dich anmeldest.",
1764
+ "auth.accountLocked": "Dein Konto wurde gesperrt. Wende dich an den Support, um es freizuschalten.",
1765
+ "auth.rateLimited": "Zu viele Anfragen. Versuche es in {{retryAfter}} Sekunden erneut.",
1766
+ "auth.emailAlreadyExists": "Ein Konto mit dieser E-Mail-Adresse existiert bereits.",
1767
+ "auth.weakPassword": "Das Passwort ist zu schwach. Verwende mindestens 8 Zeichen mit Buchstaben, Zahlen und Symbolen.",
1768
+ "auth.tokenExpired": "Dieser Link ist abgelaufen. Fordere einen neuen an.",
1769
+ "auth.tokenInvalid": "Dieser Link ist ung\xFCltig oder wurde bereits verwendet.",
1770
+ "auth.unauthorized": "Du bist nicht berechtigt, diese Aktion auszuf\xFChren.",
1771
+ // Agent errors
1772
+ "agent.notFound": "Agent nicht gefunden.",
1773
+ "agent.revoked": "Der Zugriff dieses Agenten wurde widerrufen.",
1774
+ "agent.limitExceeded": "Agentenlimit f\xFCr dieses Konto erreicht.",
1775
+ "agent.permissionDenied": "Der Agent hat keine Berechtigung f\xFCr diese Aktion.",
1776
+ // 2FA
1777
+ "twoFactor.invalidCode": "Ung\xFCltiger Best\xE4tigungscode. \xDCberpr\xFCfe deine Authentifizierungs-App und versuche es erneut.",
1778
+ "twoFactor.alreadyEnabled": "Die Zwei-Faktor-Authentifizierung ist f\xFCr dieses Konto bereits aktiviert.",
1779
+ "twoFactor.notEnabled": "Die Zwei-Faktor-Authentifizierung ist f\xFCr dieses Konto nicht aktiviert.",
1780
+ // Email subjects
1781
+ "email.verification.subject": "Best\xE4tige deine E-Mail-Adresse",
1782
+ "email.passwordReset.subject": "Setze dein Passwort zur\xFCck",
1783
+ "email.magicLink.subject": "Dein Anmelde-Link",
1784
+ "email.otp.subject": "Dein Einmalcode",
1785
+ "email.invitation.subject": "Du wurdest eingeladen, {{orgName}} beizutreten",
1786
+ "email.welcome.subject": "Willkommen bei {{appName}}",
1787
+ // General
1788
+ "general.serverError": "Etwas ist schiefgelaufen. Versuche es sp\xE4ter erneut.",
1789
+ "general.badRequest": "Die Anfrage konnte nicht verarbeitet werden.",
1790
+ "general.notFound": "Die angeforderte Ressource wurde nicht gefunden."
1791
+ };
1792
+
1793
+ // src/i18n/locales/es.ts
1794
+ var es = {
1795
+ // Auth errors
1796
+ "auth.invalidCredentials": "Correo electr\xF3nico o contrase\xF1a incorrectos.",
1797
+ "auth.emailNotVerified": "Verifica tu direcci\xF3n de correo electr\xF3nico antes de iniciar sesi\xF3n.",
1798
+ "auth.accountLocked": "Tu cuenta ha sido bloqueada. Contacta con soporte para desbloquearla.",
1799
+ "auth.rateLimited": "Demasiadas solicitudes. Int\xE9ntalo de nuevo en {{retryAfter}} segundos.",
1800
+ "auth.emailAlreadyExists": "Ya existe una cuenta con ese correo electr\xF3nico.",
1801
+ "auth.weakPassword": "La contrase\xF1a es demasiado d\xE9bil. Usa al menos 8 caracteres con letras, n\xFAmeros y s\xEDmbolos.",
1802
+ "auth.tokenExpired": "Este enlace ha caducado. Solicita uno nuevo.",
1803
+ "auth.tokenInvalid": "Este enlace no es v\xE1lido o ya ha sido utilizado.",
1804
+ "auth.unauthorized": "No tienes autorizaci\xF3n para realizar esta acci\xF3n.",
1805
+ // Agent errors
1806
+ "agent.notFound": "Agente no encontrado.",
1807
+ "agent.revoked": "El acceso de este agente ha sido revocado.",
1808
+ "agent.limitExceeded": "L\xEDmite de agentes alcanzado para esta cuenta.",
1809
+ "agent.permissionDenied": "El agente no tiene permiso para realizar esta acci\xF3n.",
1810
+ // 2FA
1811
+ "twoFactor.invalidCode": "C\xF3digo de verificaci\xF3n incorrecto. Comprueba tu aplicaci\xF3n autenticadora e int\xE9ntalo de nuevo.",
1812
+ "twoFactor.alreadyEnabled": "La autenticaci\xF3n de dos factores ya est\xE1 activada en esta cuenta.",
1813
+ "twoFactor.notEnabled": "La autenticaci\xF3n de dos factores no est\xE1 activada en esta cuenta.",
1814
+ // Email subjects
1815
+ "email.verification.subject": "Verifica tu direcci\xF3n de correo electr\xF3nico",
1816
+ "email.passwordReset.subject": "Restablece tu contrase\xF1a",
1817
+ "email.magicLink.subject": "Tu enlace de acceso",
1818
+ "email.otp.subject": "Tu c\xF3digo de un solo uso",
1819
+ "email.invitation.subject": "Has sido invitado a unirte a {{orgName}}",
1820
+ "email.welcome.subject": "Bienvenido a {{appName}}",
1821
+ // General
1822
+ "general.serverError": "Algo sali\xF3 mal. Int\xE9ntalo de nuevo m\xE1s tarde.",
1823
+ "general.badRequest": "La solicitud no pudo procesarse.",
1824
+ "general.notFound": "El recurso solicitado no fue encontrado."
1825
+ };
1826
+
1827
+ // src/i18n/locales/fr.ts
1828
+ var fr = {
1829
+ // Auth errors
1830
+ "auth.invalidCredentials": "Adresse e-mail ou mot de passe incorrect.",
1831
+ "auth.emailNotVerified": "Veuillez v\xE9rifier votre adresse e-mail avant de vous connecter.",
1832
+ "auth.accountLocked": "Votre compte a \xE9t\xE9 verrouill\xE9. Contactez le support pour le d\xE9verrouiller.",
1833
+ "auth.rateLimited": "Trop de tentatives. R\xE9essayez dans {{retryAfter}} secondes.",
1834
+ "auth.emailAlreadyExists": "Un compte avec cette adresse e-mail existe d\xE9j\xE0.",
1835
+ "auth.weakPassword": "Le mot de passe est trop faible. Utilisez au moins 8 caract\xE8res avec des lettres, des chiffres et des symboles.",
1836
+ "auth.tokenExpired": "Ce lien a expir\xE9. Demandez-en un nouveau.",
1837
+ "auth.tokenInvalid": "Ce lien est invalide ou a d\xE9j\xE0 \xE9t\xE9 utilis\xE9.",
1838
+ "auth.unauthorized": "Vous n'\xEAtes pas autoris\xE9 \xE0 effectuer cette action.",
1839
+ // Agent errors
1840
+ "agent.notFound": "Agent introuvable.",
1841
+ "agent.revoked": "L'acc\xE8s de cet agent a \xE9t\xE9 r\xE9voqu\xE9.",
1842
+ "agent.limitExceeded": "Limite d'agents atteinte pour ce compte.",
1843
+ "agent.permissionDenied": "L'agent n'est pas autoris\xE9 \xE0 effectuer cette action.",
1844
+ // 2FA
1845
+ "twoFactor.invalidCode": "Code de v\xE9rification invalide. V\xE9rifiez votre application d'authentification et r\xE9essayez.",
1846
+ "twoFactor.alreadyEnabled": "L'authentification \xE0 deux facteurs est d\xE9j\xE0 activ\xE9e sur ce compte.",
1847
+ "twoFactor.notEnabled": "L'authentification \xE0 deux facteurs n'est pas activ\xE9e sur ce compte.",
1848
+ // Email subjects
1849
+ "email.verification.subject": "V\xE9rifiez votre adresse e-mail",
1850
+ "email.passwordReset.subject": "R\xE9initialisez votre mot de passe",
1851
+ "email.magicLink.subject": "Votre lien de connexion",
1852
+ "email.otp.subject": "Votre code \xE0 usage unique",
1853
+ "email.invitation.subject": "Vous avez \xE9t\xE9 invit\xE9 \xE0 rejoindre {{orgName}}",
1854
+ "email.welcome.subject": "Bienvenue sur {{appName}}",
1855
+ // General
1856
+ "general.serverError": "Une erreur s'est produite. R\xE9essayez plus tard.",
1857
+ "general.badRequest": "La requ\xEAte n'a pas pu \xEAtre trait\xE9e.",
1858
+ "general.notFound": "La ressource demand\xE9e est introuvable."
1859
+ };
1860
+
1861
+ // src/i18n/locales/ja.ts
1862
+ var ja = {
1863
+ // Auth errors
1864
+ "auth.invalidCredentials": "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u307E\u305F\u306F\u30D1\u30B9\u30EF\u30FC\u30C9\u304C\u6B63\u3057\u304F\u3042\u308A\u307E\u305B\u3093\u3002",
1865
+ "auth.emailNotVerified": "\u30B5\u30A4\u30F3\u30A4\u30F3\u3059\u308B\u524D\u306B\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
1866
+ "auth.accountLocked": "\u30A2\u30AB\u30A6\u30F3\u30C8\u304C\u30ED\u30C3\u30AF\u3055\u308C\u3066\u3044\u307E\u3059\u3002\u30B5\u30DD\u30FC\u30C8\u306B\u9023\u7D61\u3057\u3066\u30ED\u30C3\u30AF\u3092\u89E3\u9664\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
1867
+ "auth.rateLimited": "\u30EA\u30AF\u30A8\u30B9\u30C8\u304C\u591A\u3059\u304E\u307E\u3059\u3002{{retryAfter}}\u79D2\u5F8C\u306B\u518D\u8A66\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
1868
+ "auth.emailAlreadyExists": "\u305D\u306E\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u306E\u30A2\u30AB\u30A6\u30F3\u30C8\u306F\u3059\u3067\u306B\u5B58\u5728\u3057\u307E\u3059\u3002",
1869
+ "auth.weakPassword": "\u30D1\u30B9\u30EF\u30FC\u30C9\u304C\u5F31\u3059\u304E\u307E\u3059\u3002\u6587\u5B57\u3001\u6570\u5B57\u3001\u8A18\u53F7\u3092\u7D44\u307F\u5408\u308F\u305B\u305F8\u6587\u5B57\u4EE5\u4E0A\u306E\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u4F7F\u7528\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
1870
+ "auth.tokenExpired": "\u3053\u306E\u30EA\u30F3\u30AF\u306E\u6709\u52B9\u671F\u9650\u304C\u5207\u308C\u3066\u3044\u307E\u3059\u3002\u65B0\u3057\u3044\u30EA\u30F3\u30AF\u3092\u30EA\u30AF\u30A8\u30B9\u30C8\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
1871
+ "auth.tokenInvalid": "\u3053\u306E\u30EA\u30F3\u30AF\u306F\u7121\u52B9\u304B\u3001\u3059\u3067\u306B\u4F7F\u7528\u3055\u308C\u3066\u3044\u307E\u3059\u3002",
1872
+ "auth.unauthorized": "\u3053\u306E\u64CD\u4F5C\u3092\u5B9F\u884C\u3059\u308B\u6A29\u9650\u304C\u3042\u308A\u307E\u305B\u3093\u3002",
1873
+ // Agent errors
1874
+ "agent.notFound": "\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002",
1875
+ "agent.revoked": "\u3053\u306E\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u306E\u30A2\u30AF\u30BB\u30B9\u304C\u53D6\u308A\u6D88\u3055\u308C\u307E\u3057\u305F\u3002",
1876
+ "agent.limitExceeded": "\u3053\u306E\u30A2\u30AB\u30A6\u30F3\u30C8\u306E\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u4E0A\u9650\u306B\u9054\u3057\u307E\u3057\u305F\u3002",
1877
+ "agent.permissionDenied": "\u30A8\u30FC\u30B8\u30A7\u30F3\u30C8\u306B\u306F\u3053\u306E\u64CD\u4F5C\u3092\u5B9F\u884C\u3059\u308B\u6A29\u9650\u304C\u3042\u308A\u307E\u305B\u3093\u3002",
1878
+ // 2FA
1879
+ "twoFactor.invalidCode": "\u78BA\u8A8D\u30B3\u30FC\u30C9\u304C\u6B63\u3057\u304F\u3042\u308A\u307E\u305B\u3093\u3002\u8A8D\u8A3C\u30A2\u30D7\u30EA\u3092\u78BA\u8A8D\u3057\u3066\u518D\u8A66\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
1880
+ "twoFactor.alreadyEnabled": "\u3053\u306E\u30A2\u30AB\u30A6\u30F3\u30C8\u3067\u306F\u4E8C\u8981\u7D20\u8A8D\u8A3C\u304C\u3059\u3067\u306B\u6709\u52B9\u306B\u306A\u3063\u3066\u3044\u307E\u3059\u3002",
1881
+ "twoFactor.notEnabled": "\u3053\u306E\u30A2\u30AB\u30A6\u30F3\u30C8\u3067\u306F\u4E8C\u8981\u7D20\u8A8D\u8A3C\u304C\u6709\u52B9\u306B\u306A\u3063\u3066\u3044\u307E\u305B\u3093\u3002",
1882
+ // Email subjects
1883
+ "email.verification.subject": "\u30E1\u30FC\u30EB\u30A2\u30C9\u30EC\u30B9\u3092\u78BA\u8A8D\u3057\u3066\u304F\u3060\u3055\u3044",
1884
+ "email.passwordReset.subject": "\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u30EA\u30BB\u30C3\u30C8",
1885
+ "email.magicLink.subject": "\u30B5\u30A4\u30F3\u30A4\u30F3\u30EA\u30F3\u30AF",
1886
+ "email.otp.subject": "\u30EF\u30F3\u30BF\u30A4\u30E0\u30B3\u30FC\u30C9",
1887
+ "email.invitation.subject": "{{orgName}}\u3078\u306E\u62DB\u5F85",
1888
+ "email.welcome.subject": "{{appName}}\u3078\u3088\u3046\u3053\u305D",
1889
+ // General
1890
+ "general.serverError": "\u554F\u984C\u304C\u767A\u751F\u3057\u307E\u3057\u305F\u3002\u5F8C\u3067\u3082\u3046\u4E00\u5EA6\u304A\u8A66\u3057\u304F\u3060\u3055\u3044\u3002",
1891
+ "general.badRequest": "\u30EA\u30AF\u30A8\u30B9\u30C8\u3092\u51E6\u7406\u3067\u304D\u307E\u305B\u3093\u3067\u3057\u305F\u3002",
1892
+ "general.notFound": "\u30EA\u30AF\u30A8\u30B9\u30C8\u3055\u308C\u305F\u30EA\u30BD\u30FC\u30B9\u304C\u898B\u3064\u304B\u308A\u307E\u305B\u3093\u3002"
1893
+ };
1894
+
1895
+ // src/i18n/locales/zh.ts
1896
+ var zh = {
1897
+ // Auth errors
1898
+ "auth.invalidCredentials": "\u90AE\u7BB1\u6216\u5BC6\u7801\u4E0D\u6B63\u786E\u3002",
1899
+ "auth.emailNotVerified": "\u8BF7\u5728\u767B\u5F55\u524D\u9A8C\u8BC1\u60A8\u7684\u7535\u5B50\u90AE\u4EF6\u5730\u5740\u3002",
1900
+ "auth.accountLocked": "\u60A8\u7684\u8D26\u6237\u5DF2\u88AB\u9501\u5B9A\uFF0C\u8BF7\u8054\u7CFB\u652F\u6301\u56E2\u961F\u89E3\u9501\u3002",
1901
+ "auth.rateLimited": "\u8BF7\u6C42\u8FC7\u4E8E\u9891\u7E41\uFF0C\u8BF7\u5728 {{retryAfter}} \u79D2\u540E\u91CD\u8BD5\u3002",
1902
+ "auth.emailAlreadyExists": "\u8BE5\u90AE\u7BB1\u5730\u5740\u5DF2\u6CE8\u518C\u8D26\u6237\u3002",
1903
+ "auth.weakPassword": "\u5BC6\u7801\u5F3A\u5EA6\u4E0D\u8DB3\uFF0C\u8BF7\u4F7F\u7528\u81F3\u5C11 8 \u4F4D\u5305\u542B\u5B57\u6BCD\u3001\u6570\u5B57\u548C\u7B26\u53F7\u7684\u5BC6\u7801\u3002",
1904
+ "auth.tokenExpired": "\u6B64\u94FE\u63A5\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u91CD\u65B0\u7533\u8BF7\u3002",
1905
+ "auth.tokenInvalid": "\u6B64\u94FE\u63A5\u65E0\u6548\u6216\u5DF2\u88AB\u4F7F\u7528\u3002",
1906
+ "auth.unauthorized": "\u60A8\u6CA1\u6709\u6743\u9650\u6267\u884C\u6B64\u64CD\u4F5C\u3002",
1907
+ // Agent errors
1908
+ "agent.notFound": "\u672A\u627E\u5230\u8BE5\u4EE3\u7406\u3002",
1909
+ "agent.revoked": "\u8BE5\u4EE3\u7406\u7684\u8BBF\u95EE\u6743\u9650\u5DF2\u88AB\u64A4\u9500\u3002",
1910
+ "agent.limitExceeded": "\u5DF2\u8FBE\u5230\u8BE5\u8D26\u6237\u7684\u4EE3\u7406\u6570\u91CF\u4E0A\u9650\u3002",
1911
+ "agent.permissionDenied": "\u4EE3\u7406\u6CA1\u6709\u6267\u884C\u6B64\u64CD\u4F5C\u7684\u6743\u9650\u3002",
1912
+ // 2FA
1913
+ "twoFactor.invalidCode": "\u9A8C\u8BC1\u7801\u65E0\u6548\uFF0C\u8BF7\u68C0\u67E5\u60A8\u7684\u9A8C\u8BC1\u5E94\u7528\u5E76\u91CD\u8BD5\u3002",
1914
+ "twoFactor.alreadyEnabled": "\u8BE5\u8D26\u6237\u5DF2\u542F\u7528\u53CC\u91CD\u9A8C\u8BC1\u3002",
1915
+ "twoFactor.notEnabled": "\u8BE5\u8D26\u6237\u672A\u542F\u7528\u53CC\u91CD\u9A8C\u8BC1\u3002",
1916
+ // Email subjects
1917
+ "email.verification.subject": "\u8BF7\u9A8C\u8BC1\u60A8\u7684\u7535\u5B50\u90AE\u4EF6\u5730\u5740",
1918
+ "email.passwordReset.subject": "\u91CD\u7F6E\u60A8\u7684\u5BC6\u7801",
1919
+ "email.magicLink.subject": "\u60A8\u7684\u767B\u5F55\u94FE\u63A5",
1920
+ "email.otp.subject": "\u60A8\u7684\u4E00\u6B21\u6027\u9A8C\u8BC1\u7801",
1921
+ "email.invitation.subject": "\u60A8\u5DF2\u88AB\u9080\u8BF7\u52A0\u5165 {{orgName}}",
1922
+ "email.welcome.subject": "\u6B22\u8FCE\u4F7F\u7528 {{appName}}",
1923
+ // General
1924
+ "general.serverError": "\u51FA\u73B0\u4E86\u4E00\u4E9B\u95EE\u9898\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5\u3002",
1925
+ "general.badRequest": "\u65E0\u6CD5\u5904\u7406\u8BE5\u8BF7\u6C42\u3002",
1926
+ "general.notFound": "\u672A\u627E\u5230\u8BF7\u6C42\u7684\u8D44\u6E90\u3002"
1927
+ };
1928
+
1929
+ // src/plugin/router.ts
1930
+ function matchPath(pattern, pathname) {
1931
+ const patternParts = pattern.split("/");
1932
+ const pathParts = pathname.split("/");
1933
+ if (patternParts.length !== pathParts.length) return null;
1934
+ const params = {};
1935
+ for (let i = 0; i < patternParts.length; i++) {
1936
+ const patternPart = patternParts[i];
1937
+ const pathPart = pathParts[i];
1938
+ if (patternPart === void 0 || pathPart === void 0) return null;
1939
+ if (patternPart.startsWith(":")) {
1940
+ const paramName = patternPart.slice(1);
1941
+ params[paramName] = decodeURIComponent(pathPart);
1942
+ } else if (patternPart !== pathPart) {
1943
+ return null;
1944
+ }
1945
+ }
1946
+ return params;
1947
+ }
1948
+ function createPluginRouter(endpoints) {
1949
+ return {
1950
+ async handle(request, basePath, endpointCtx) {
1951
+ const url = new URL(request.url);
1952
+ let pathname = url.pathname;
1953
+ const base = basePath.endsWith("/") ? basePath.slice(0, -1) : basePath;
1954
+ if (base && pathname.startsWith(base)) {
1955
+ pathname = pathname.slice(base.length) || "/";
1956
+ }
1957
+ if (!pathname.startsWith("/")) {
1958
+ pathname = `/${pathname}`;
1959
+ }
1960
+ if (pathname.length > 1 && pathname.endsWith("/")) {
1961
+ pathname = pathname.slice(0, -1);
1962
+ }
1963
+ const method = request.method.toUpperCase();
1964
+ for (const endpoint of endpoints) {
1965
+ if (endpoint.method !== method) continue;
1966
+ const params = matchPath(endpoint.path, pathname);
1967
+ if (params === null) continue;
1968
+ const enrichedUrl = new URL(request.url);
1969
+ for (const [key, value] of Object.entries(params)) {
1970
+ enrichedUrl.searchParams.set(`_param_${key}`, value);
1971
+ }
1972
+ const enrichedRequest = new Request(enrichedUrl.toString(), request);
1973
+ return endpoint.handler(enrichedRequest, endpointCtx);
1974
+ }
1975
+ return null;
1976
+ },
1977
+ getEndpoints() {
1978
+ return [...endpoints];
1979
+ }
1980
+ };
1981
+ }
1982
+
1983
+ // src/plugin/runner.ts
1984
+ async function runMigrations(db, provider, statements) {
1985
+ if (statements.length === 0) return;
1986
+ if (provider === "sqlite") {
1987
+ const session = db.session;
1988
+ if (session?.client?.exec) {
1989
+ session.client.exec(`${statements.join(";\n")};`);
1990
+ return;
1991
+ }
1992
+ const anyDb2 = db;
1993
+ for (const sql of statements) {
1994
+ await anyDb2.run(sql);
1995
+ }
1996
+ return;
1997
+ }
1998
+ const anyDb = db;
1999
+ if (provider === "postgres") {
2000
+ const client = anyDb.$client ?? anyDb.session?.client;
2001
+ if (!client) {
2002
+ throw new Error(
2003
+ "KavachOS plugin migrations: cannot access underlying pg client from Drizzle instance."
2004
+ );
2005
+ }
2006
+ for (const sql of statements) {
2007
+ await client.query(sql);
2008
+ }
2009
+ return;
2010
+ }
2011
+ if (provider === "mysql") {
2012
+ const client = anyDb.$client ?? anyDb.session?.client;
2013
+ if (!client) {
2014
+ throw new Error(
2015
+ "KavachOS plugin migrations: cannot access underlying mysql2 client from Drizzle instance."
2016
+ );
2017
+ }
2018
+ for (const sql of statements) {
2019
+ await client.execute(sql);
2020
+ }
2021
+ return;
2022
+ }
2023
+ throw new Error(`runMigrations: unsupported provider "${provider}"`);
2024
+ }
2025
+ async function initializePlugins(plugins, db, config) {
2026
+ const registry = {
2027
+ endpoints: [],
2028
+ migrations: [],
2029
+ hooks: {
2030
+ onRequest: [],
2031
+ onAuthenticate: [],
2032
+ onSessionCreate: [],
2033
+ onSessionRevoke: []
2034
+ },
2035
+ pluginContext: {}
2036
+ };
2037
+ for (const plugin of plugins) {
2038
+ const pluginMigrations = [];
2039
+ const ctx = {
2040
+ db,
2041
+ config,
2042
+ addEndpoint(endpoint) {
2043
+ registry.endpoints.push(endpoint);
2044
+ },
2045
+ addMigration(sql) {
2046
+ pluginMigrations.push(sql);
2047
+ registry.migrations.push(sql);
2048
+ }
2049
+ };
2050
+ if (plugin.init) {
2051
+ const result = await plugin.init(ctx);
2052
+ if (result?.context) {
2053
+ Object.assign(registry.pluginContext, result.context);
2054
+ }
2055
+ }
2056
+ if (plugin.hooks) {
2057
+ if (plugin.hooks.onRequest) {
2058
+ registry.hooks.onRequest.push(plugin.hooks.onRequest);
2059
+ }
2060
+ if (plugin.hooks.onAuthenticate) {
2061
+ registry.hooks.onAuthenticate.push(plugin.hooks.onAuthenticate);
2062
+ }
2063
+ if (plugin.hooks.onSessionCreate) {
2064
+ registry.hooks.onSessionCreate.push(plugin.hooks.onSessionCreate);
2065
+ }
2066
+ if (plugin.hooks.onSessionRevoke) {
2067
+ registry.hooks.onSessionRevoke.push(plugin.hooks.onSessionRevoke);
2068
+ }
2069
+ }
2070
+ if (pluginMigrations.length > 0) {
2071
+ await runMigrations(db, config.database.provider, pluginMigrations);
2072
+ }
2073
+ }
2074
+ return registry;
2075
+ }
2076
+ function emptyUsage() {
2077
+ return {
2078
+ tokensCostToday: 0,
2079
+ tokensCostThisMonth: 0,
2080
+ callsToday: 0,
2081
+ callsThisMonth: 0,
2082
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
2083
+ };
2084
+ }
2085
+ function rowToPolicy(row) {
2086
+ return {
2087
+ id: row.id,
2088
+ agentId: row.agentId ?? void 0,
2089
+ userId: row.userId ?? void 0,
2090
+ tenantId: row.tenantId ?? void 0,
2091
+ limits: row.limits ?? {},
2092
+ currentUsage: row.currentUsage ?? emptyUsage(),
2093
+ action: row.action,
2094
+ status: row.status,
2095
+ createdAt: row.createdAt
2096
+ };
2097
+ }
2098
+ function isExceeded(limits, usage) {
2099
+ if (limits.maxCallsPerDay !== void 0 && usage.callsToday >= limits.maxCallsPerDay) return true;
2100
+ if (limits.maxCallsPerMonth !== void 0 && usage.callsThisMonth >= limits.maxCallsPerMonth)
2101
+ return true;
2102
+ if (limits.maxTokensCostPerDay !== void 0 && usage.tokensCostToday >= limits.maxTokensCostPerDay)
2103
+ return true;
2104
+ if (limits.maxTokensCostPerMonth !== void 0 && usage.tokensCostThisMonth >= limits.maxTokensCostPerMonth)
2105
+ return true;
2106
+ return false;
2107
+ }
2108
+ function createPolicyModule(db) {
2109
+ async function create(input) {
2110
+ const id = `pol_${randomUUID().replace(/-/g, "")}`;
2111
+ const now = /* @__PURE__ */ new Date();
2112
+ const usage = emptyUsage();
2113
+ await db.insert(budgetPolicies).values({
2114
+ id,
2115
+ agentId: input.agentId ?? null,
2116
+ userId: input.userId ?? null,
2117
+ tenantId: input.tenantId ?? null,
2118
+ limits: input.limits,
2119
+ currentUsage: usage,
2120
+ action: input.action,
2121
+ status: "active",
2122
+ createdAt: now
2123
+ });
2124
+ return {
2125
+ id,
2126
+ agentId: input.agentId,
2127
+ userId: input.userId,
2128
+ tenantId: input.tenantId,
2129
+ limits: input.limits,
2130
+ currentUsage: usage,
2131
+ action: input.action,
2132
+ status: "active",
2133
+ createdAt: now
2134
+ };
2135
+ }
2136
+ async function get(policyId) {
2137
+ const rows = await db.select().from(budgetPolicies).where(eq(budgetPolicies.id, policyId)).limit(1);
2138
+ const row = rows[0];
2139
+ if (!row) return null;
2140
+ return rowToPolicy(row);
2141
+ }
2142
+ async function list(filters) {
2143
+ let query = db.select().from(budgetPolicies).$dynamic();
2144
+ const conditions = [];
2145
+ if (filters?.agentId !== void 0) {
2146
+ conditions.push(
2147
+ or(eq(budgetPolicies.agentId, filters.agentId), isNull(budgetPolicies.agentId))
2148
+ );
2149
+ }
2150
+ if (filters?.userId !== void 0) {
2151
+ conditions.push(or(eq(budgetPolicies.userId, filters.userId), isNull(budgetPolicies.userId)));
2152
+ }
2153
+ if (filters?.tenantId !== void 0) {
2154
+ conditions.push(
2155
+ or(eq(budgetPolicies.tenantId, filters.tenantId), isNull(budgetPolicies.tenantId))
2156
+ );
2157
+ }
2158
+ if (conditions.length > 0) {
2159
+ query = query.where(and(...conditions));
2160
+ }
2161
+ const rows = await query;
2162
+ return rows.map(rowToPolicy);
2163
+ }
2164
+ async function update(policyId, updates) {
2165
+ const existing = await get(policyId);
2166
+ if (!existing) throw new Error(`Policy "${policyId}" not found.`);
2167
+ await db.update(budgetPolicies).set({
2168
+ limits: updates.limits ?? existing.limits,
2169
+ currentUsage: updates.currentUsage ?? existing.currentUsage,
2170
+ action: updates.action ?? existing.action,
2171
+ status: updates.status ?? existing.status
2172
+ }).where(eq(budgetPolicies.id, policyId));
2173
+ const updated = await get(policyId);
2174
+ if (!updated) throw new Error(`Policy "${policyId}" disappeared after update.`);
2175
+ return updated;
2176
+ }
2177
+ async function remove(policyId) {
2178
+ const existing = await get(policyId);
2179
+ if (!existing) throw new Error(`Policy "${policyId}" not found.`);
2180
+ await db.delete(budgetPolicies).where(eq(budgetPolicies.id, policyId));
2181
+ }
2182
+ async function checkBudget(agentId, tokensCost) {
2183
+ const rows = await db.select().from(budgetPolicies).where(
2184
+ and(
2185
+ ne(budgetPolicies.status, "disabled"),
2186
+ or(eq(budgetPolicies.agentId, agentId), isNull(budgetPolicies.agentId))
2187
+ )
2188
+ );
2189
+ for (const row of rows) {
2190
+ const policy = rowToPolicy(row);
2191
+ const usage = { ...policy.currentUsage };
2192
+ if (tokensCost !== void 0) {
2193
+ usage.tokensCostToday += tokensCost;
2194
+ usage.tokensCostThisMonth += tokensCost;
2195
+ }
2196
+ if (isExceeded(policy.limits, usage)) {
2197
+ return {
2198
+ allowed: policy.action === "warn",
2199
+ reason: `Budget policy "${policy.id}" exceeded (action: ${policy.action})`,
2200
+ policy
2201
+ };
2202
+ }
2203
+ }
2204
+ return { allowed: true };
2205
+ }
2206
+ async function recordUsage(agentId, tokensCost) {
2207
+ const rows = await db.select().from(budgetPolicies).where(
2208
+ and(
2209
+ ne(budgetPolicies.status, "disabled"),
2210
+ or(eq(budgetPolicies.agentId, agentId), isNull(budgetPolicies.agentId))
2211
+ )
2212
+ );
2213
+ for (const row of rows) {
2214
+ const policy = rowToPolicy(row);
2215
+ const usage = {
2216
+ tokensCostToday: policy.currentUsage.tokensCostToday + (tokensCost ?? 0),
2217
+ tokensCostThisMonth: policy.currentUsage.tokensCostThisMonth + (tokensCost ?? 0),
2218
+ callsToday: policy.currentUsage.callsToday + 1,
2219
+ callsThisMonth: policy.currentUsage.callsThisMonth + 1,
2220
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
2221
+ };
2222
+ const exceeded = isExceeded(policy.limits, usage);
2223
+ const newStatus = exceeded ? "triggered" : policy.status;
2224
+ await db.update(budgetPolicies).set({ currentUsage: usage, status: newStatus }).where(eq(budgetPolicies.id, policy.id));
2225
+ }
2226
+ }
2227
+ async function resetDaily() {
2228
+ const rows = await db.select().from(budgetPolicies);
2229
+ let reset = 0;
2230
+ for (const row of rows) {
2231
+ const policy = rowToPolicy(row);
2232
+ const usage = {
2233
+ ...policy.currentUsage,
2234
+ tokensCostToday: 0,
2235
+ callsToday: 0,
2236
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
2237
+ };
2238
+ const stillExceeded = isExceeded(policy.limits, usage);
2239
+ const newStatus = stillExceeded ? "triggered" : policy.status === "triggered" ? "active" : policy.status;
2240
+ await db.update(budgetPolicies).set({ currentUsage: usage, status: newStatus }).where(eq(budgetPolicies.id, policy.id));
2241
+ reset++;
2242
+ }
2243
+ return { reset };
2244
+ }
2245
+ async function resetMonthly() {
2246
+ const rows = await db.select().from(budgetPolicies);
2247
+ let reset = 0;
2248
+ for (const row of rows) {
2249
+ const policy = rowToPolicy(row);
2250
+ const usage = {
2251
+ ...policy.currentUsage,
2252
+ tokensCostThisMonth: 0,
2253
+ callsThisMonth: 0,
2254
+ lastUpdated: (/* @__PURE__ */ new Date()).toISOString()
2255
+ };
2256
+ const stillExceeded = isExceeded(policy.limits, usage);
2257
+ const newStatus = stillExceeded ? "triggered" : policy.status === "triggered" ? "active" : policy.status;
2258
+ await db.update(budgetPolicies).set({ currentUsage: usage, status: newStatus }).where(eq(budgetPolicies.id, policy.id));
2259
+ reset++;
2260
+ }
2261
+ return { reset };
2262
+ }
2263
+ return { create, get, list, update, remove, checkBudget, recordUsage, resetDaily, resetMonthly };
2264
+ }
2265
+ function slugRegex() {
2266
+ return /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
2267
+ }
2268
+ function rowToTenant(row) {
2269
+ return {
2270
+ id: row.id,
2271
+ name: row.name,
2272
+ slug: row.slug,
2273
+ settings: row.settings ?? {},
2274
+ status: row.status,
2275
+ createdAt: row.createdAt,
2276
+ updatedAt: row.updatedAt
2277
+ };
2278
+ }
2279
+ function createTenantModule(db) {
2280
+ async function create(input) {
2281
+ if (!slugRegex().test(input.slug)) {
2282
+ throw new Error(
2283
+ `Invalid slug "${input.slug}". Use lowercase letters, numbers, and hyphens only.`
2284
+ );
2285
+ }
2286
+ const existing = await db.select().from(tenants).where(eq(tenants.slug, input.slug)).limit(1);
2287
+ if (existing.length > 0) {
2288
+ throw new Error(`Tenant with slug "${input.slug}" already exists.`);
2289
+ }
2290
+ const id = `tnt_${randomUUID().replace(/-/g, "")}`;
2291
+ const now = /* @__PURE__ */ new Date();
2292
+ const settings = input.settings ?? {};
2293
+ await db.insert(tenants).values({
2294
+ id,
2295
+ name: input.name,
2296
+ slug: input.slug,
2297
+ settings,
2298
+ status: "active",
2299
+ createdAt: now,
2300
+ updatedAt: now
2301
+ });
2302
+ return {
2303
+ id,
2304
+ name: input.name,
2305
+ slug: input.slug,
2306
+ settings,
2307
+ status: "active",
2308
+ createdAt: now,
2309
+ updatedAt: now
2310
+ };
2311
+ }
2312
+ async function get(tenantId) {
2313
+ const rows = await db.select().from(tenants).where(eq(tenants.id, tenantId)).limit(1);
2314
+ const row = rows[0];
2315
+ if (!row) return null;
2316
+ return rowToTenant(row);
2317
+ }
2318
+ async function getBySlug(slug) {
2319
+ const rows = await db.select().from(tenants).where(eq(tenants.slug, slug)).limit(1);
2320
+ const row = rows[0];
2321
+ if (!row) return null;
2322
+ return rowToTenant(row);
2323
+ }
2324
+ async function list() {
2325
+ const rows = await db.select().from(tenants);
2326
+ return rows.map(rowToTenant);
2327
+ }
2328
+ async function update(tenantId, updates) {
2329
+ const existing = await get(tenantId);
2330
+ if (!existing) throw new Error(`Tenant "${tenantId}" not found.`);
2331
+ if (updates.slug !== void 0 && updates.slug !== existing.slug) {
2332
+ if (!slugRegex().test(updates.slug)) {
2333
+ throw new Error(
2334
+ `Invalid slug "${updates.slug}". Use lowercase letters, numbers, and hyphens only.`
2335
+ );
2336
+ }
2337
+ const conflict = await db.select().from(tenants).where(eq(tenants.slug, updates.slug)).limit(1);
2338
+ if (conflict.length > 0) {
2339
+ throw new Error(`Tenant with slug "${updates.slug}" already exists.`);
2340
+ }
2341
+ }
2342
+ const now = /* @__PURE__ */ new Date();
2343
+ await db.update(tenants).set({
2344
+ name: updates.name ?? existing.name,
2345
+ slug: updates.slug ?? existing.slug,
2346
+ settings: updates.settings ? { ...existing.settings, ...updates.settings } : existing.settings,
2347
+ updatedAt: now
2348
+ }).where(eq(tenants.id, tenantId));
2349
+ const updated = await get(tenantId);
2350
+ if (!updated) throw new Error(`Tenant "${tenantId}" disappeared after update.`);
2351
+ return updated;
2352
+ }
2353
+ async function suspend(tenantId) {
2354
+ const existing = await get(tenantId);
2355
+ if (!existing) throw new Error(`Tenant "${tenantId}" not found.`);
2356
+ await db.update(tenants).set({ status: "suspended", updatedAt: /* @__PURE__ */ new Date() }).where(eq(tenants.id, tenantId));
2357
+ }
2358
+ async function activate(tenantId) {
2359
+ const existing = await get(tenantId);
2360
+ if (!existing) throw new Error(`Tenant "${tenantId}" not found.`);
2361
+ await db.update(tenants).set({ status: "active", updatedAt: /* @__PURE__ */ new Date() }).where(eq(tenants.id, tenantId));
2362
+ }
2363
+ return { create, get, getBySlug, list, update, suspend, activate };
2364
+ }
2365
+ var DEFAULT_THRESHOLDS = {
2366
+ untrusted: 20,
2367
+ limited: 40,
2368
+ standard: 60,
2369
+ trusted: 80,
2370
+ elevated: 95
2371
+ };
2372
+ function scoreToLevel(score, thresholds) {
2373
+ if (score >= thresholds.elevated) return "elevated";
2374
+ if (score >= thresholds.trusted) return "trusted";
2375
+ if (score >= thresholds.standard) return "standard";
2376
+ if (score >= thresholds.limited) return "limited";
2377
+ return "untrusted";
2378
+ }
2379
+ function clamp(value, min, max) {
2380
+ return Math.max(min, Math.min(max, value));
2381
+ }
2382
+ function rowToScore(row) {
2383
+ const factors = row.factors;
2384
+ return {
2385
+ agentId: row.agentId,
2386
+ score: row.score,
2387
+ level: row.level,
2388
+ factors,
2389
+ computedAt: row.computedAt.toISOString()
2390
+ };
2391
+ }
2392
+ function createTrustModule(config, db) {
2393
+ const thresholds = { ...DEFAULT_THRESHOLDS, ...config.thresholds };
2394
+ async function computeScore(agentId) {
2395
+ const now = /* @__PURE__ */ new Date();
2396
+ const agentRows = await db.select({ createdAt: agents.createdAt }).from(agents).where(eq(agents.id, agentId)).limit(1);
2397
+ const agentRow = agentRows[0];
2398
+ const ageInDays = agentRow ? (now.getTime() - agentRow.createdAt.getTime()) / (1e3 * 60 * 60 * 24) : 0;
2399
+ const allLogs = await db.select({
2400
+ result: auditLogs.result,
2401
+ reason: auditLogs.reason,
2402
+ timestamp: auditLogs.timestamp
2403
+ }).from(auditLogs).where(eq(auditLogs.agentId, agentId));
2404
+ const totalCalls = allLogs.length;
2405
+ const allowed = allLogs.filter((r) => r.result === "allowed").length;
2406
+ const denied = allLogs.filter((r) => r.result === "denied").length;
2407
+ const successRate = totalCalls > 0 ? allowed / totalCalls * 100 : 100;
2408
+ const denialRate = totalCalls > 0 ? denied / totalCalls * 100 : 0;
2409
+ const anomalyCount = allLogs.filter((r) => {
2410
+ if (r.result !== "denied") return false;
2411
+ const reason = r.reason ?? "";
2412
+ return reason.includes("INSUFFICIENT_PERMISSIONS") || reason.toLowerCase().includes("privilege") || reason.toLowerCase().includes("escalation");
2413
+ }).length;
2414
+ const violationLogs = allLogs.filter((r) => r.result === "denied").sort((a, b) => b.timestamp.getTime() - a.timestamp.getTime());
2415
+ const lastViolation = violationLogs[0]?.timestamp.toISOString();
2416
+ let score = 50;
2417
+ score += Math.min(25, Math.floor(allowed / 100));
2418
+ score -= denied * 5;
2419
+ score -= anomalyCount * 10;
2420
+ if (ageInDays > 30) score += 10;
2421
+ else if (ageInDays > 7) score += 5;
2422
+ score = clamp(Math.round(score), 0, 100);
2423
+ const level = scoreToLevel(score, thresholds);
2424
+ const factors = {
2425
+ successRate: Math.round(successRate * 10) / 10,
2426
+ denialRate: Math.round(denialRate * 10) / 10,
2427
+ ageInDays: Math.round(ageInDays * 10) / 10,
2428
+ totalCalls,
2429
+ anomalyCount,
2430
+ lastViolation
2431
+ };
2432
+ const existingRows = await db.select({ agentId: trustScores.agentId }).from(trustScores).where(eq(trustScores.agentId, agentId)).limit(1);
2433
+ if (existingRows.length > 0) {
2434
+ await db.update(trustScores).set({ score, level, factors, computedAt: now }).where(eq(trustScores.agentId, agentId));
2435
+ } else {
2436
+ await db.insert(trustScores).values({
2437
+ agentId,
2438
+ score,
2439
+ level,
2440
+ factors,
2441
+ computedAt: now
2442
+ });
2443
+ }
2444
+ return {
2445
+ agentId,
2446
+ score,
2447
+ level,
2448
+ factors,
2449
+ computedAt: now.toISOString()
2450
+ };
2451
+ }
2452
+ async function getScore(agentId) {
2453
+ const rows = await db.select().from(trustScores).where(eq(trustScores.agentId, agentId)).limit(1);
2454
+ const row = rows[0];
2455
+ if (!row) return null;
2456
+ return rowToScore(row);
2457
+ }
2458
+ async function computeAll() {
2459
+ const activeAgents = await db.select({ id: agents.id }).from(agents).where(eq(agents.status, "active"));
2460
+ const results = [];
2461
+ for (const agent of activeAgents) {
2462
+ const score = await computeScore(agent.id);
2463
+ results.push(score);
2464
+ }
2465
+ return results;
2466
+ }
2467
+ async function getScores(filters) {
2468
+ const rows = await db.select().from(trustScores);
2469
+ let scores = rows.map(rowToScore);
2470
+ if (filters?.level) {
2471
+ scores = scores.filter((s) => s.level === filters.level);
2472
+ }
2473
+ if (filters?.minScore !== void 0) {
2474
+ const min = filters.minScore;
2475
+ scores = scores.filter((s) => s.score >= min);
2476
+ }
2477
+ return scores;
2478
+ }
2479
+ return {
2480
+ computeScore,
2481
+ getScore,
2482
+ computeAll,
2483
+ getScores
2484
+ };
2485
+ }
2486
+
2487
+ // src/kavach.ts
2488
+ function classifyViolation2(reason) {
2489
+ const r = reason?.toLowerCase() ?? "";
2490
+ if (r.includes("rate") || r.includes("rate_limited")) return "rate_limited";
2491
+ if (r.includes("ip") || r.includes("allowlist")) return "ip_blocked";
2492
+ if (r.includes("time") || r.includes("window")) return "time_restricted";
2493
+ if (r.includes("approval")) return "approval_required";
2494
+ return "permission_denied";
2495
+ }
2496
+ async function createKavach(config) {
2497
+ const authAdapter = config.auth?.adapter ?? null;
458
2498
  const db = await createDatabase(config.database);
459
2499
  if (!config.database.skipMigrations) {
460
2500
  await createTables(db, config.database.provider);
@@ -473,7 +2513,58 @@ async function createKavach(config) {
473
2513
  const auditModule = createAuditModule({ db });
474
2514
  const delegationModule = createDelegationModule({ db });
475
2515
  const sessionManager = config.auth?.session ? createSessionManager(config.auth.session, db) : null;
2516
+ const privilegeAnalyzer = createPrivilegeAnalyzer(db);
2517
+ const hooks = config.hooks ?? {};
2518
+ const tenantModule = createTenantModule(db);
2519
+ const policyModule = createPolicyModule(db);
2520
+ const approvalModule = createApprovalModule(config.approval ?? {}, db);
2521
+ const trustModule = createTrustModule({}, db);
2522
+ const didModule = createDidModule(db, config.did);
2523
+ const magicLinkModule = config.magicLink && sessionManager ? createMagicLinkModule(config.magicLink, db, sessionManager) : null;
2524
+ const emailOtpModule = config.emailOtp && sessionManager ? createEmailOtpModule(config.emailOtp, db, sessionManager) : null;
2525
+ const totpModule = config.totp ? createTotpModule(config.totp, db) : null;
2526
+ const passkeyModule = config.passkey ? createPasskeyModule(config.passkey, db) : null;
2527
+ const orgModule = config.org ? createOrgModule(config.org, db) : null;
2528
+ const ssoModule = config.sso ? createSsoModule(config.sso, db) : null;
2529
+ const adminModule = config.admin ? createAdminModule(config.admin, db, sessionManager) : null;
2530
+ const apiKeyManagerModule = config.apiKeys ? createApiKeyManagerModule(config.apiKeys, db) : null;
2531
+ const usernameModule = config.username && sessionManager ? createUsernameAuthModule(config.username, db, sessionManager) : null;
2532
+ const phoneModule = config.phone && sessionManager ? createPhoneAuthModule(config.phone, db, sessionManager) : null;
2533
+ const captchaModule = config.captcha ? createCaptchaModule(config.captcha) : null;
2534
+ const webhookModule = config.webhooks && config.webhooks.length > 0 ? createWebhookModule(config.webhooks) : null;
2535
+ const pluginRegistry = await initializePlugins(config.plugins ?? [], db, config);
2536
+ const endpointCtx = {
2537
+ db,
2538
+ async getUser(request) {
2539
+ if (!authAdapter) return null;
2540
+ return authAdapter.resolveUser(request);
2541
+ },
2542
+ async getSession(token) {
2543
+ if (!sessionManager) return null;
2544
+ return sessionManager.validate(token);
2545
+ }
2546
+ };
2547
+ const pluginRouter = createPluginRouter(pluginRegistry.endpoints);
476
2548
  async function authorize(agentId, request, context) {
2549
+ if (hooks.beforeAuthorize) {
2550
+ const verdict = await hooks.beforeAuthorize({
2551
+ agentId,
2552
+ action: request.action,
2553
+ resource: request.resource,
2554
+ arguments: request.arguments
2555
+ });
2556
+ if (verdict && !verdict.allow) {
2557
+ const reason = verdict.reason ?? "Blocked by beforeAuthorize hook";
2558
+ void hooks.onViolation?.({
2559
+ type: classifyViolation2(reason),
2560
+ agentId,
2561
+ action: request.action,
2562
+ resource: request.resource,
2563
+ reason
2564
+ });
2565
+ return { allowed: false, reason, auditId: "" };
2566
+ }
2567
+ }
477
2568
  const agent = await agentModule.get(agentId);
478
2569
  if (!agent) {
479
2570
  return {
@@ -491,13 +2582,42 @@ async function createKavach(config) {
491
2582
  }
492
2583
  const enrichedRequest = context ? { ...request, context } : request;
493
2584
  const ownResult = await permissionEngine.authorize(agent, enrichedRequest);
494
- if (ownResult.allowed) return ownResult;
495
- const delegatedPerms = await delegationModule.getEffectivePermissions(agentId);
496
- if (delegatedPerms.length === 0) return ownResult;
497
- const agentWithDelegated = { ...agent, permissions: delegatedPerms };
498
- const delegatedResult = await permissionEngine.authorize(agentWithDelegated, enrichedRequest);
499
- if (delegatedResult.allowed) return delegatedResult;
500
- return ownResult;
2585
+ let finalResult;
2586
+ if (ownResult.allowed) {
2587
+ finalResult = ownResult;
2588
+ } else {
2589
+ const delegatedPerms = await delegationModule.getEffectivePermissions(agentId);
2590
+ if (delegatedPerms.length === 0) {
2591
+ finalResult = ownResult;
2592
+ } else {
2593
+ const agentWithDelegated = { ...agent, permissions: delegatedPerms };
2594
+ const delegatedResult = await permissionEngine.authorize(
2595
+ agentWithDelegated,
2596
+ enrichedRequest
2597
+ );
2598
+ finalResult = delegatedResult.allowed ? delegatedResult : ownResult;
2599
+ }
2600
+ }
2601
+ void hooks.afterAuthorize?.({
2602
+ agentId,
2603
+ action: request.action,
2604
+ resource: request.resource,
2605
+ result: {
2606
+ allowed: finalResult.allowed,
2607
+ reason: finalResult.reason,
2608
+ auditId: finalResult.auditId
2609
+ }
2610
+ });
2611
+ if (!finalResult.allowed) {
2612
+ void hooks.onViolation?.({
2613
+ type: classifyViolation2(finalResult.reason),
2614
+ agentId,
2615
+ action: request.action,
2616
+ resource: request.resource,
2617
+ reason: finalResult.reason ?? "Authorization denied"
2618
+ });
2619
+ }
2620
+ return finalResult;
501
2621
  }
502
2622
  async function authorizeByToken(token, request, context) {
503
2623
  const agent = await agentModule.validateToken(token);
@@ -519,6 +2639,31 @@ async function createKavach(config) {
519
2639
  }
520
2640
  return delegationModule.delegate(input, parentAgent.permissions);
521
2641
  }
2642
+ const agentProxy = {
2643
+ async create(...args) {
2644
+ const [input] = args;
2645
+ if (hooks.beforeAgentCreate) {
2646
+ const verdict = await hooks.beforeAgentCreate(input);
2647
+ if (verdict && !verdict.allow) {
2648
+ throw new Error(verdict.reason ?? "Agent creation blocked by beforeAgentCreate hook");
2649
+ }
2650
+ }
2651
+ const agent = await agentModule.create(input);
2652
+ void hooks.afterAgentCreate?.(agent);
2653
+ return agent;
2654
+ },
2655
+ async revoke(agentId) {
2656
+ await agentModule.revoke(agentId);
2657
+ void hooks.onAgentRevoke?.(agentId);
2658
+ },
2659
+ async rotate(...args) {
2660
+ return agentModule.rotate(...args);
2661
+ },
2662
+ get: agentModule.get,
2663
+ list: agentModule.list,
2664
+ update: agentModule.update,
2665
+ validateToken: agentModule.validateToken
2666
+ };
522
2667
  const mcpRegistry = {
523
2668
  /**
524
2669
  * Register a new MCP tool server.
@@ -581,15 +2726,7 @@ async function createKavach(config) {
581
2726
  }
582
2727
  };
583
2728
  return {
584
- agent: {
585
- create: agentModule.create,
586
- get: agentModule.get,
587
- list: agentModule.list,
588
- update: agentModule.update,
589
- revoke: agentModule.revoke,
590
- rotate: agentModule.rotate,
591
- validateToken: agentModule.validateToken
592
- },
2729
+ agent: agentProxy,
593
2730
  authorize,
594
2731
  authorizeByToken,
595
2732
  delegate,
@@ -610,6 +2747,17 @@ async function createKavach(config) {
610
2747
  * database table — no separate in-memory store needed.
611
2748
  */
612
2749
  mcp: mcpRegistry,
2750
+ /**
2751
+ * Least-privilege analyzer.
2752
+ *
2753
+ * Compare agent permissions against actual audit log usage to surface
2754
+ * wildcards, unused grants, and over-permissioned identities.
2755
+ */
2756
+ analyzer: {
2757
+ analyzeAgent: privilegeAnalyzer.analyzeAgent,
2758
+ analyzeAll: privilegeAnalyzer.analyzeAll,
2759
+ getSummary: privilegeAnalyzer.getSummary
2760
+ },
613
2761
  /**
614
2762
  * Human auth integration.
615
2763
  *
@@ -645,7 +2793,249 @@ async function createKavach(config) {
645
2793
  return authAdapter.resolveUser(request);
646
2794
  },
647
2795
  /** Direct database access for advanced usage */
648
- db
2796
+ db,
2797
+ /**
2798
+ * Multi-tenant isolation.
2799
+ *
2800
+ * Create and manage tenants (organizations) that share a single
2801
+ * KavachOS instance with full data isolation. Agents can be scoped
2802
+ * to a tenant via `tenantId`.
2803
+ */
2804
+ tenant: tenantModule,
2805
+ /**
2806
+ * Agent execution budget policies.
2807
+ *
2808
+ * Set spending caps (token cost, call counts) per agent, user, or
2809
+ * tenant. Exceeded policies trigger a configurable action: warn,
2810
+ * throttle, block, or revoke.
2811
+ */
2812
+ policies: policyModule,
2813
+ /**
2814
+ * CIBA-style async human approval flows.
2815
+ *
2816
+ * Create pending approval requests, notify humans via webhook or
2817
+ * custom handler, and resolve them with approve / deny.
2818
+ */
2819
+ approval: approvalModule,
2820
+ /**
2821
+ * Graduated autonomy trust scoring.
2822
+ *
2823
+ * Compute and persist 0-100 trust scores derived from audit history,
2824
+ * mapped to five levels: untrusted, limited, standard, trusted, elevated.
2825
+ */
2826
+ trust: trustModule,
2827
+ /**
2828
+ * W3C Decentralized Identifiers (DID) for agents.
2829
+ *
2830
+ * Generate did:key or did:web identities, sign payloads, and verify
2831
+ * signatures. Private keys are never stored — they are returned to
2832
+ * the caller on generation and must be stored securely.
2833
+ *
2834
+ * @example
2835
+ * ```typescript
2836
+ * const { agentDid, privateKeyJwk } = await kavach.did.generateKey(agentId);
2837
+ * const signed = await kavach.did.sign(agentId, { action: 'read' }, privateKeyJwk);
2838
+ * const result = await kavach.did.verify(signed.jws, agentDid.did);
2839
+ * ```
2840
+ */
2841
+ did: didModule,
2842
+ /**
2843
+ * Magic link (passwordless email) authentication.
2844
+ *
2845
+ * Null when `magicLink` config was not provided or `auth.session` is not
2846
+ * configured (sessions are required to issue tokens on verification).
2847
+ *
2848
+ * @example
2849
+ * ```typescript
2850
+ * // In your route handler
2851
+ * const response = await kavach.magicLink?.handleRequest(request);
2852
+ * if (response) return response;
2853
+ * ```
2854
+ */
2855
+ magicLink: magicLinkModule,
2856
+ /**
2857
+ * Email OTP (one-time password) authentication.
2858
+ *
2859
+ * Null when `emailOtp` config was not provided or `auth.session` is not
2860
+ * configured.
2861
+ *
2862
+ * @example
2863
+ * ```typescript
2864
+ * const response = await kavach.emailOtp?.handleRequest(request);
2865
+ * if (response) return response;
2866
+ * ```
2867
+ */
2868
+ emailOtp: emailOtpModule,
2869
+ /**
2870
+ * TOTP two-factor authentication.
2871
+ *
2872
+ * Null when `totp` config was not provided.
2873
+ *
2874
+ * @example
2875
+ * ```typescript
2876
+ * // On setup (show QR code to user)
2877
+ * const { secret, uri, backupCodes } = await kavach.totp.setup(userId);
2878
+ *
2879
+ * // After user scans QR and enters code
2880
+ * const { enabled } = await kavach.totp.enable(userId, totpCode);
2881
+ *
2882
+ * // On login (after password check)
2883
+ * const { valid } = await kavach.totp.verify(userId, totpCode);
2884
+ * ```
2885
+ */
2886
+ totp: totpModule,
2887
+ /**
2888
+ * Passkey / WebAuthn authentication.
2889
+ *
2890
+ * Null when `passkey` config was not provided.
2891
+ *
2892
+ * @example
2893
+ * ```typescript
2894
+ * // Registration — step 1: get options, send to browser
2895
+ * const options = await kavach.passkey.getRegistrationOptions(userId, userName);
2896
+ *
2897
+ * // Registration — step 2: verify browser response
2898
+ * const { credential } = await kavach.passkey.verifyRegistration(userId, response);
2899
+ *
2900
+ * // Authentication — step 1: get options
2901
+ * const options = await kavach.passkey.getAuthenticationOptions(userId);
2902
+ *
2903
+ * // Authentication — step 2: verify browser response
2904
+ * const result = await kavach.passkey.verifyAuthentication(response);
2905
+ * if (result) console.log('Authenticated user:', result.userId);
2906
+ * ```
2907
+ */
2908
+ passkey: passkeyModule,
2909
+ /**
2910
+ * Organizations + RBAC.
2911
+ *
2912
+ * Null when `org` config was not provided.
2913
+ *
2914
+ * @example
2915
+ * ```typescript
2916
+ * const org = await kavach.org?.create({ name: 'Acme', slug: 'acme', ownerId: userId });
2917
+ * const allowed = await kavach.org?.hasPermission(org.id, userId, 'agents:create');
2918
+ * ```
2919
+ */
2920
+ org: orgModule,
2921
+ /**
2922
+ * SSO (SAML 2.0 + OIDC) enterprise authentication.
2923
+ *
2924
+ * Null when `sso` config was not provided.
2925
+ *
2926
+ * @example
2927
+ * ```typescript
2928
+ * const conn = await kavach.sso?.createConnection({ orgId, providerId: 'okta', type: 'saml', domain: 'acme.com' });
2929
+ * const url = await kavach.sso?.getSamlAuthUrl(conn.id);
2930
+ * ```
2931
+ */
2932
+ sso: ssoModule,
2933
+ /**
2934
+ * Admin module.
2935
+ *
2936
+ * Null when `admin` config was not provided.
2937
+ *
2938
+ * @example
2939
+ * ```typescript
2940
+ * await kavach.admin?.banUser(userId, 'Spam');
2941
+ * const { session } = await kavach.admin?.impersonate(adminId, userId);
2942
+ * ```
2943
+ */
2944
+ admin: adminModule,
2945
+ /**
2946
+ * API key management.
2947
+ *
2948
+ * Null when `apiKeys` config was not provided.
2949
+ *
2950
+ * @example
2951
+ * ```typescript
2952
+ * const { key, apiKey } = await kavach.apiKeys?.create({ userId, name: 'CI', permissions: ['agents:read'] });
2953
+ * const result = await kavach.apiKeys?.validate(key);
2954
+ * ```
2955
+ */
2956
+ apiKeys: apiKeyManagerModule,
2957
+ /**
2958
+ * Username + password authentication.
2959
+ *
2960
+ * Null when `username` config was not provided or `auth.session` is not
2961
+ * configured (sessions are required to issue tokens on sign-in/up).
2962
+ *
2963
+ * @example
2964
+ * ```typescript
2965
+ * const response = await kavach.username?.handleRequest(request);
2966
+ * if (response) return response;
2967
+ * ```
2968
+ */
2969
+ username: usernameModule,
2970
+ /**
2971
+ * Phone number (SMS OTP) authentication.
2972
+ *
2973
+ * Null when `phone` config was not provided or `auth.session` is not
2974
+ * configured.
2975
+ *
2976
+ * @example
2977
+ * ```typescript
2978
+ * const response = await kavach.phone?.handleRequest(request);
2979
+ * if (response) return response;
2980
+ * ```
2981
+ */
2982
+ phone: phoneModule,
2983
+ /**
2984
+ * Captcha integration (reCAPTCHA, hCaptcha, Cloudflare Turnstile).
2985
+ *
2986
+ * Null when `captcha` config was not provided.
2987
+ *
2988
+ * @example
2989
+ * ```typescript
2990
+ * const result = await kavach.captcha?.verify(token, ip);
2991
+ * if (!result?.success) return new Response('Captcha failed', { status: 403 });
2992
+ * ```
2993
+ */
2994
+ captcha: captchaModule,
2995
+ /**
2996
+ * Webhook system.
2997
+ *
2998
+ * Null when `webhooks` config was not provided or the array is empty.
2999
+ *
3000
+ * @example
3001
+ * ```typescript
3002
+ * kavach.webhooks?.emit('user.created', { userId: user.id });
3003
+ * ```
3004
+ */
3005
+ webhooks: webhookModule,
3006
+ /**
3007
+ * Plugin system.
3008
+ *
3009
+ * Route incoming HTTP requests through plugin-registered endpoints,
3010
+ * retrieve all endpoints for adapter mounting, or access plugin-provided
3011
+ * context values.
3012
+ *
3013
+ * @example
3014
+ * ```typescript
3015
+ * // In a framework adapter
3016
+ * app.all('/kavach/*', async (req) => {
3017
+ * const response = await kavach.plugins.handleRequest(req);
3018
+ * if (response) return response;
3019
+ * return new Response('Not Found', { status: 404 });
3020
+ * });
3021
+ * ```
3022
+ */
3023
+ plugins: {
3024
+ /** Route a request through plugin endpoints. Returns null if no plugin handles it. */
3025
+ handleRequest(request, basePath = "") {
3026
+ return pluginRouter.handle(request, basePath, endpointCtx);
3027
+ },
3028
+ /** Get all endpoints registered by plugins (for framework adapter mounting). */
3029
+ getEndpoints() {
3030
+ return pluginRouter.getEndpoints();
3031
+ },
3032
+ /** Get the merged plugin context (values returned from plugin init). */
3033
+ getContext() {
3034
+ return { ...pluginRegistry.pluginContext };
3035
+ },
3036
+ /** Access the raw plugin registry (hooks, migrations, etc.). */
3037
+ registry: pluginRegistry
3038
+ }
649
3039
  };
650
3040
  }
651
3041
 
@@ -1053,6 +3443,491 @@ function generateOpenAPISpec(options) {
1053
3443
  };
1054
3444
  }
1055
3445
 
1056
- export { createDatabase, createDatabaseSync, createDelegationModule, createKavach, createSessionManager, createTables, generateOpenAPISpec };
3446
+ // src/session/cookie.ts
3447
+ var IS_PRODUCTION = typeof process !== "undefined" && process.env.NODE_ENV === "production";
3448
+ var DEFAULT_OPTIONS = {
3449
+ httpOnly: true,
3450
+ secure: IS_PRODUCTION,
3451
+ sameSite: "lax",
3452
+ path: "/"
3453
+ };
3454
+ function serializeCookie(name, value, options) {
3455
+ validateCookieName(name);
3456
+ const opts = { ...DEFAULT_OPTIONS, ...options };
3457
+ const parts = [`${name}=${encodeURIComponent(value)}`];
3458
+ if (opts.httpOnly) parts.push("HttpOnly");
3459
+ if (opts.secure) parts.push("Secure");
3460
+ const sameSite = opts.sameSite ?? "lax";
3461
+ parts.push(`SameSite=${capitalize(sameSite)}`);
3462
+ const path = opts.path ?? "/";
3463
+ parts.push(`Path=${path}`);
3464
+ if (options?.domain) parts.push(`Domain=${options.domain}`);
3465
+ if (options?.maxAge !== void 0) {
3466
+ parts.push(`Max-Age=${options.maxAge}`);
3467
+ const expiryDate = new Date(Date.now() + options.maxAge * 1e3);
3468
+ parts.push(`Expires=${expiryDate.toUTCString()}`);
3469
+ } else if (options?.expires) {
3470
+ parts.push(`Expires=${options.expires.toUTCString()}`);
3471
+ }
3472
+ if (options?.partitioned) parts.push("Partitioned");
3473
+ return parts.join("; ");
3474
+ }
3475
+ function serializeCookieDeletion(name, options) {
3476
+ return serializeCookie(name, "", {
3477
+ ...options,
3478
+ maxAge: 0,
3479
+ expires: /* @__PURE__ */ new Date(0)
3480
+ });
3481
+ }
3482
+ function parseCookies(header) {
3483
+ const result = {};
3484
+ if (!header || !header.trim()) return result;
3485
+ for (const pair of header.split(";")) {
3486
+ const eqIndex = pair.indexOf("=");
3487
+ if (eqIndex === -1) continue;
3488
+ const name = pair.slice(0, eqIndex).trim();
3489
+ const raw = pair.slice(eqIndex + 1).trim();
3490
+ if (!name) continue;
3491
+ try {
3492
+ result[name] = decodeURIComponent(raw);
3493
+ } catch {
3494
+ }
3495
+ }
3496
+ return result;
3497
+ }
3498
+ function getCookie(header, name) {
3499
+ return parseCookies(header)[name];
3500
+ }
3501
+ function parseCookiesFromRequest(request) {
3502
+ return parseCookies(request.headers.get("cookie") ?? "");
3503
+ }
3504
+ function capitalize(s) {
3505
+ return s.charAt(0).toUpperCase() + s.slice(1);
3506
+ }
3507
+ var COOKIE_NAME_SEPARATORS = /[\s()<>@,;:\\"/[\]?={}]/;
3508
+ function validateCookieName(name) {
3509
+ if (!name) {
3510
+ throw new Error(`Invalid cookie name: "${name}"`);
3511
+ }
3512
+ for (let i = 0; i < name.length; i++) {
3513
+ const code2 = name.charCodeAt(i);
3514
+ if (code2 <= 31 || code2 === 127) {
3515
+ throw new Error(`Invalid cookie name: "${name}"`);
3516
+ }
3517
+ }
3518
+ if (COOKIE_NAME_SEPARATORS.test(name)) {
3519
+ throw new Error(`Invalid cookie name: "${name}"`);
3520
+ }
3521
+ }
3522
+
3523
+ // src/session/csrf.ts
3524
+ var TOKEN_BYTE_LENGTH = 32;
3525
+ function generateCsrfToken() {
3526
+ const bytes = new Uint8Array(TOKEN_BYTE_LENGTH);
3527
+ crypto.getRandomValues(bytes);
3528
+ return uint8ArrayToBase64Url(bytes);
3529
+ }
3530
+ function validateCsrfToken(requestToken, cookieToken) {
3531
+ if (!requestToken || !cookieToken) {
3532
+ return { valid: false, reason: "Missing CSRF token" };
3533
+ }
3534
+ if (!timingSafeEqual(requestToken, cookieToken)) {
3535
+ return { valid: false, reason: "CSRF token mismatch" };
3536
+ }
3537
+ return { valid: true };
3538
+ }
3539
+ function validateOrigin(request, trustedOrigins, allowMissingOrigin = false) {
3540
+ const normalised = trustedOrigins.map(normaliseOrigin);
3541
+ const originHeader = request.headers.get("origin");
3542
+ if (originHeader) {
3543
+ if (originHeader === "null") {
3544
+ return { valid: false, reason: "Opaque origin rejected" };
3545
+ }
3546
+ const requestOrigin = normaliseOrigin(originHeader);
3547
+ if (normalised.includes(requestOrigin)) {
3548
+ return { valid: true };
3549
+ }
3550
+ return {
3551
+ valid: false,
3552
+ reason: `Origin "${originHeader}" is not in the trusted list`
3553
+ };
3554
+ }
3555
+ const refererHeader = request.headers.get("referer");
3556
+ if (refererHeader) {
3557
+ try {
3558
+ const refererOrigin = normaliseOrigin(new URL(refererHeader).origin);
3559
+ if (normalised.includes(refererOrigin)) {
3560
+ return { valid: true };
3561
+ }
3562
+ return {
3563
+ valid: false,
3564
+ reason: `Referer origin "${refererOrigin}" is not in the trusted list`
3565
+ };
3566
+ } catch {
3567
+ return { valid: false, reason: "Malformed Referer header" };
3568
+ }
3569
+ }
3570
+ if (allowMissingOrigin) {
3571
+ return { valid: true };
3572
+ }
3573
+ return { valid: false, reason: "No Origin or Referer header present" };
3574
+ }
3575
+ function normaliseOrigin(origin) {
3576
+ return origin.replace(/\/$/, "").toLowerCase();
3577
+ }
3578
+ function timingSafeEqual(a, b) {
3579
+ const aBytes = new TextEncoder().encode(a);
3580
+ const bBytes = new TextEncoder().encode(b);
3581
+ if (aBytes.length !== bBytes.length) {
3582
+ let _diff = 0;
3583
+ const max = Math.max(aBytes.length, bBytes.length);
3584
+ for (let i = 0; i < max; i++) {
3585
+ _diff |= (aBytes[i] ?? 0) ^ (bBytes[i] ?? 0);
3586
+ }
3587
+ return false;
3588
+ }
3589
+ let diff = 0;
3590
+ for (let i = 0; i < aBytes.length; i++) {
3591
+ diff |= (aBytes[i] ?? 0) ^ (bBytes[i] ?? 0);
3592
+ }
3593
+ return diff === 0;
3594
+ }
3595
+ function uint8ArrayToBase64Url(bytes) {
3596
+ if (typeof Buffer !== "undefined") {
3597
+ return Buffer.from(bytes).toString("base64url");
3598
+ }
3599
+ let binary = "";
3600
+ for (const byte of bytes) {
3601
+ binary += String.fromCharCode(byte);
3602
+ }
3603
+ return btoa(binary).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
3604
+ }
3605
+ var DEFAULT_SESSION_NAME = "kavach_session";
3606
+ var DEFAULT_MAX_AGE_SECONDS = 60 * 60 * 24 * 7;
3607
+ function createCookieSessionManager(config, db) {
3608
+ const sessionName = config.sessionName ?? DEFAULT_SESSION_NAME;
3609
+ const maxAgeSecs = config.maxAge ?? DEFAULT_MAX_AGE_SECONDS;
3610
+ const autoRefresh = config.autoRefresh ?? true;
3611
+ const raw = createSessionManager(config, db);
3612
+ const baseCookieOpts = {
3613
+ httpOnly: true,
3614
+ sameSite: "lax",
3615
+ path: "/",
3616
+ ...config.cookieOptions,
3617
+ maxAge: maxAgeSecs
3618
+ };
3619
+ function buildSetCookie(token) {
3620
+ return serializeCookie(sessionName, token, baseCookieOpts);
3621
+ }
3622
+ function buildDeleteCookie() {
3623
+ const { maxAge: _omit, ...rest } = baseCookieOpts;
3624
+ return serializeCookieDeletion(sessionName, rest);
3625
+ }
3626
+ async function createSession(userId, metadata) {
3627
+ const { session, token } = await raw.create(userId, metadata);
3628
+ return { session, setCookieHeader: buildSetCookie(token) };
3629
+ }
3630
+ async function validateSession(cookieHeader) {
3631
+ const token = getCookie(cookieHeader, sessionName);
3632
+ if (!token) {
3633
+ return { session: null, refreshCookieHeader: null };
3634
+ }
3635
+ const session = await raw.validate(token);
3636
+ if (!session) {
3637
+ return { session: null, refreshCookieHeader: null };
3638
+ }
3639
+ if (autoRefresh) {
3640
+ const refreshed = await refreshSession(session.id);
3641
+ if (refreshed) {
3642
+ return { session: refreshed.session, refreshCookieHeader: refreshed.setCookieHeader };
3643
+ }
3644
+ }
3645
+ return { session, refreshCookieHeader: null };
3646
+ }
3647
+ async function refreshSession(sessionId) {
3648
+ const rows = await db.select().from(sessions).where(and(eq(sessions.id, sessionId)));
3649
+ const row = rows[0];
3650
+ if (!row) return null;
3651
+ if (row.expiresAt <= /* @__PURE__ */ new Date()) return null;
3652
+ await db.delete(sessions).where(eq(sessions.id, sessionId));
3653
+ const { session: newSession, token: newToken } = await raw.create(
3654
+ row.userId,
3655
+ row.metadata ?? void 0
3656
+ );
3657
+ return { session: newSession, setCookieHeader: buildSetCookie(newToken) };
3658
+ }
3659
+ async function revokeSession(sessionId) {
3660
+ await raw.revoke(sessionId);
3661
+ return { deleteCookieHeader: buildDeleteCookie() };
3662
+ }
3663
+ async function revokeAllSessions(userId) {
3664
+ await raw.revokeAll(userId);
3665
+ return { deleteCookieHeader: buildDeleteCookie() };
3666
+ }
3667
+ async function listSessions(userId) {
3668
+ return raw.list(userId);
3669
+ }
3670
+ function buildLogoutCookie() {
3671
+ return buildDeleteCookie();
3672
+ }
3673
+ return {
3674
+ createSession,
3675
+ validateSession,
3676
+ refreshSession,
3677
+ revokeSession,
3678
+ revokeAllSessions,
3679
+ listSessions,
3680
+ buildLogoutCookie,
3681
+ raw
3682
+ };
3683
+ }
3684
+ var MultiSessionLimitError = class extends Error {
3685
+ code = "SESSION_LIMIT_REACHED";
3686
+ constructor(userId, max) {
3687
+ super(`User ${userId} has reached the maximum of ${max} concurrent sessions`);
3688
+ }
3689
+ };
3690
+ function parseUserAgent(ua) {
3691
+ if (!ua) return void 0;
3692
+ let os;
3693
+ if (/iphone|ipad|ipod/i.test(ua)) {
3694
+ os = "iOS";
3695
+ } else if (/android/i.test(ua)) {
3696
+ os = "Android";
3697
+ } else if (/macintosh|mac os x/i.test(ua)) {
3698
+ os = "macOS";
3699
+ } else if (/windows/i.test(ua)) {
3700
+ os = "Windows";
3701
+ } else if (/linux/i.test(ua)) {
3702
+ os = "Linux";
3703
+ } else {
3704
+ os = "Unknown OS";
3705
+ }
3706
+ let browser;
3707
+ if (/edg\//i.test(ua)) {
3708
+ browser = "Edge";
3709
+ } else if (/opr\//i.test(ua) || /opera/i.test(ua)) {
3710
+ browser = "Opera";
3711
+ } else if (/firefox\//i.test(ua)) {
3712
+ browser = "Firefox";
3713
+ } else if (/chrome\//i.test(ua) && !/chromium/i.test(ua)) {
3714
+ browser = "Chrome";
3715
+ } else if (/safari\//i.test(ua) && !/chrome/i.test(ua)) {
3716
+ browser = "Safari";
3717
+ } else if (/curl\//i.test(ua)) {
3718
+ browser = "curl";
3719
+ } else if (/python-requests/i.test(ua)) {
3720
+ browser = "Python";
3721
+ } else {
3722
+ browser = "Unknown";
3723
+ }
3724
+ return `${browser} on ${os}`;
3725
+ }
3726
+ function rowToSessionInfo(row) {
3727
+ const metadata = row.metadata ?? void 0;
3728
+ const device = metadata && typeof metadata.device === "string" ? metadata.device : void 0;
3729
+ const ip = metadata && typeof metadata.ip === "string" ? metadata.ip : void 0;
3730
+ let cleanMetadata;
3731
+ if (metadata) {
3732
+ const { device: _d, ip: _i, ...rest } = metadata;
3733
+ cleanMetadata = Object.keys(rest).length > 0 ? rest : void 0;
3734
+ }
3735
+ return {
3736
+ id: row.id,
3737
+ createdAt: row.createdAt,
3738
+ expiresAt: row.expiresAt,
3739
+ ...cleanMetadata !== void 0 && { metadata: cleanMetadata },
3740
+ ...device !== void 0 && { device },
3741
+ ...ip !== void 0 && { ip }
3742
+ };
3743
+ }
3744
+ function createMultiSessionModule(config, db, sessionManager) {
3745
+ const maxSessions = config.maxSessions ?? 10;
3746
+ const overflowStrategy = config.overflowStrategy ?? "evict-oldest";
3747
+ async function listSessions(userId) {
3748
+ const now = /* @__PURE__ */ new Date();
3749
+ const rows = await db.select().from(sessions).where(eq(sessions.userId, userId));
3750
+ return rows.filter((r) => r.expiresAt > now).sort((a, b) => b.createdAt.getTime() - a.createdAt.getTime()).map(rowToSessionInfo);
3751
+ }
3752
+ async function revokeSession(sessionId) {
3753
+ await sessionManager.revoke(sessionId);
3754
+ }
3755
+ async function revokeOtherSessions(userId, currentSessionId) {
3756
+ const now = /* @__PURE__ */ new Date();
3757
+ const activeRows = await db.select({ id: sessions.id, expiresAt: sessions.expiresAt }).from(sessions).where(and(eq(sessions.userId, userId), ne(sessions.id, currentSessionId)));
3758
+ const activeIds = activeRows.filter((r) => r.expiresAt > now).map((r) => r.id);
3759
+ for (const id of activeIds) {
3760
+ await sessionManager.revoke(id);
3761
+ }
3762
+ return activeIds.length;
3763
+ }
3764
+ async function getSessionCount(userId) {
3765
+ const now = /* @__PURE__ */ new Date();
3766
+ const rows = await db.select({ id: sessions.id, expiresAt: sessions.expiresAt }).from(sessions).where(eq(sessions.userId, userId));
3767
+ return rows.filter((r) => r.expiresAt > now).length;
3768
+ }
3769
+ async function enforceSessionLimit(userId) {
3770
+ const now = /* @__PURE__ */ new Date();
3771
+ const rows = await db.select({ id: sessions.id, expiresAt: sessions.expiresAt, createdAt: sessions.createdAt }).from(sessions).where(eq(sessions.userId, userId));
3772
+ const activeSessions = rows.filter((r) => r.expiresAt > now).sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
3773
+ if (activeSessions.length < maxSessions) return;
3774
+ if (overflowStrategy === "reject") {
3775
+ throw new MultiSessionLimitError(userId, maxSessions);
3776
+ }
3777
+ const toEvict = activeSessions.slice(0, activeSessions.length - maxSessions + 1);
3778
+ for (const s of toEvict) {
3779
+ await sessionManager.revoke(s.id);
3780
+ }
3781
+ }
3782
+ return { listSessions, revokeSession, revokeOtherSessions, getSessionCount, enforceSessionLimit };
3783
+ }
3784
+ function buildSessionMetadata(request, extra) {
3785
+ const ua = request.headers.get("user-agent");
3786
+ const ip = request.headers.get("x-forwarded-for")?.split(",")[0]?.trim() ?? request.headers.get("x-real-ip") ?? void 0;
3787
+ const device = parseUserAgent(ua);
3788
+ return {
3789
+ ...device !== void 0 && { device },
3790
+ ...ip !== void 0 && { ip },
3791
+ ...extra
3792
+ };
3793
+ }
3794
+
3795
+ // src/webhooks/webhook.ts
3796
+ function generateId() {
3797
+ const bytes = new Uint8Array(16);
3798
+ crypto.getRandomValues(bytes);
3799
+ return Array.from(bytes, (b) => b.toString(16).padStart(2, "0")).join("");
3800
+ }
3801
+ async function signPayload2(secret, body) {
3802
+ const encoder = new TextEncoder();
3803
+ const keyData = encoder.encode(secret);
3804
+ const messageData = encoder.encode(body);
3805
+ const key = await crypto.subtle.importKey(
3806
+ "raw",
3807
+ keyData,
3808
+ { name: "HMAC", hash: "SHA-256" },
3809
+ false,
3810
+ ["sign"]
3811
+ );
3812
+ const signature = await crypto.subtle.sign("HMAC", key, messageData);
3813
+ const hex = Array.from(new Uint8Array(signature), (b) => b.toString(16).padStart(2, "0")).join(
3814
+ ""
3815
+ );
3816
+ return `sha256=${hex}`;
3817
+ }
3818
+ async function deliverWebhook(url, event, payload, deliveryId, timestamp, signature, timeoutMs) {
3819
+ try {
3820
+ const response = await fetch(url, {
3821
+ method: "POST",
3822
+ headers: {
3823
+ "content-type": "application/json",
3824
+ "x-kavach-event": event,
3825
+ "x-kavach-delivery": deliveryId,
3826
+ "x-kavach-timestamp": timestamp,
3827
+ "x-kavach-signature": signature
3828
+ },
3829
+ body: JSON.stringify(payload),
3830
+ signal: AbortSignal.timeout(timeoutMs)
3831
+ });
3832
+ return { success: response.ok, statusCode: response.status };
3833
+ } catch (err) {
3834
+ return {
3835
+ success: false,
3836
+ error: err instanceof Error ? err.message : "Unknown error"
3837
+ };
3838
+ }
3839
+ }
3840
+ async function dispatchWithRetry(url, event, payload, config) {
3841
+ const deliveryId = generateId();
3842
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
3843
+ const body = JSON.stringify(payload);
3844
+ const signature = await signPayload2(config.secret, body);
3845
+ const delays = [1e3, 2e3, 4e3];
3846
+ for (let attempt = 0; attempt < config.maxRetries; attempt++) {
3847
+ if (attempt > 0) {
3848
+ await new Promise((resolve) => setTimeout(resolve, delays[attempt - 1] ?? 4e3));
3849
+ }
3850
+ const result = await deliverWebhook(
3851
+ url,
3852
+ event,
3853
+ payload,
3854
+ deliveryId,
3855
+ timestamp,
3856
+ signature,
3857
+ config.timeoutMs
3858
+ );
3859
+ if (result.success) {
3860
+ return;
3861
+ }
3862
+ }
3863
+ }
3864
+ function createWebhookModule2(config) {
3865
+ const resolvedConfig = {
3866
+ secret: config.secret,
3867
+ maxRetries: config.maxRetries ?? 3,
3868
+ timeoutMs: config.timeoutMs ?? 1e4
3869
+ };
3870
+ const subscriptions = /* @__PURE__ */ new Map();
3871
+ async function subscribe(url, events) {
3872
+ const sub = {
3873
+ id: generateId(),
3874
+ url,
3875
+ events,
3876
+ active: true,
3877
+ createdAt: /* @__PURE__ */ new Date()
3878
+ };
3879
+ subscriptions.set(sub.id, sub);
3880
+ return sub;
3881
+ }
3882
+ async function unsubscribe(subscriptionId) {
3883
+ subscriptions.delete(subscriptionId);
3884
+ }
3885
+ async function list() {
3886
+ return Array.from(subscriptions.values());
3887
+ }
3888
+ function dispatch(event, payload) {
3889
+ const matching = Array.from(subscriptions.values()).filter(
3890
+ (sub) => sub.active && sub.events.includes(event)
3891
+ );
3892
+ for (const sub of matching) {
3893
+ void dispatchWithRetry(sub.url, event, payload, resolvedConfig);
3894
+ }
3895
+ }
3896
+ async function test(subscriptionId) {
3897
+ const sub = subscriptions.get(subscriptionId);
3898
+ if (!sub) {
3899
+ return { success: false, error: "Subscription not found" };
3900
+ }
3901
+ const deliveryId = generateId();
3902
+ const timestamp = (/* @__PURE__ */ new Date()).toISOString();
3903
+ const pingPayload = { event: "ping", subscriptionId, timestamp };
3904
+ const body = JSON.stringify(pingPayload);
3905
+ const signature = await signPayload2(resolvedConfig.secret, body);
3906
+ return deliverWebhook(
3907
+ sub.url,
3908
+ "auth.login",
3909
+ // placeholder event type for test delivery
3910
+ pingPayload,
3911
+ deliveryId,
3912
+ timestamp,
3913
+ signature,
3914
+ resolvedConfig.timeoutMs
3915
+ );
3916
+ }
3917
+ return { subscribe, unsubscribe, list, dispatch, test };
3918
+ }
3919
+ async function verifyWebhookSignature(secret, rawBody, signature) {
3920
+ const expected = await signPayload2(secret, rawBody);
3921
+ if (expected.length !== signature.length) return false;
3922
+ const a = new TextEncoder().encode(expected);
3923
+ const b = new TextEncoder().encode(signature);
3924
+ let diff = 0;
3925
+ for (let i = 0; i < a.length; i++) {
3926
+ diff |= (a[i] ?? 0) ^ (b[i] ?? 0);
3927
+ }
3928
+ return diff === 0;
3929
+ }
3930
+
3931
+ export { MultiSessionLimitError, buildDidDocument, buildSessionMetadata, classifyViolation, createApprovalModule, createCookieSessionManager, createDatabase, createDatabaseSync, createDelegationModule, createDidModule, createEmailTemplates, createI18n, createKavach, createMultiSessionModule, createPluginRouter, createPolicyModule, createPresentation, createPrivilegeAnalyzer, createTables, createTenantModule, createTrustModule, createWebhookModule2 as createWebhookModule, de, en, es, fr, generateCsrfToken, generateDidKey, generateDidWeb, generateOpenAPISpec, getCookie, getDidWebUrl, initializePlugins, ja, parseCookies, parseCookiesFromRequest, resolveDidKey, resolveDidWeb, serializeCookie, serializeCookieDeletion, signPayload, validateCsrfToken, validateOrigin, verifyPayload, verifyPresentation, verifyWebhookSignature, zh };
1057
3932
  //# sourceMappingURL=index.js.map
1058
3933
  //# sourceMappingURL=index.js.map