kavachos 0.0.1 → 0.0.2

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/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # kavachos
2
+
3
+ Auth OS for AI agents. Identity, permissions, delegation, and audit.
4
+
5
+ [![npm](https://img.shields.io/npm/v/kavachos)](https://www.npmjs.com/package/kavachos)
6
+ [![license](https://img.shields.io/badge/license-MIT-blue)](https://github.com/kavachos/kavachos/blob/main/LICENSE)
7
+
8
+ ## Install
9
+
10
+ ```bash
11
+ npm install kavachos
12
+ # or
13
+ pnpm add kavachos
14
+ ```
15
+
16
+ ## Quick start
17
+
18
+ ```typescript
19
+ import { createKavach } from 'kavachos';
20
+
21
+ const kavach = createKavach({
22
+ database: { provider: 'sqlite', url: 'kavach.db' },
23
+ });
24
+
25
+ // Create an agent with scoped permissions
26
+ const agent = await kavach.agent.create({
27
+ ownerId: 'user-123',
28
+ name: 'github-reader',
29
+ type: 'autonomous',
30
+ permissions: [
31
+ { resource: 'mcp:github:*', actions: ['read'] },
32
+ {
33
+ resource: 'mcp:deploy:production',
34
+ actions: ['execute'],
35
+ constraints: { requireApproval: true },
36
+ },
37
+ ],
38
+ });
39
+
40
+ // Authorize an action
41
+ const result = await kavach.authorize(agent.id, {
42
+ action: 'read',
43
+ resource: 'mcp:github:repos',
44
+ });
45
+ // { allowed: true, auditId: 'aud_...' }
46
+
47
+ // Query the audit trail
48
+ const logs = await kavach.audit.query({ agentId: agent.id });
49
+ ```
50
+
51
+ ## What's included
52
+
53
+ - **Agent identity** - create, scope, revoke, and rotate agent credentials. Each agent gets an opaque bearer token (`kv_...`) and a permanent audit identity.
54
+ - **Permission engine** - resource-based access control with colon-separated hierarchies (`mcp:github:*`) and wildcard matching. Constraints support rate limits, time windows, and human-in-the-loop approval gates.
55
+ - **Delegation chains** - an orchestrator can delegate a subset of its permissions to a sub-agent, with depth limits and expiry. Chains are auditable and revocable at any point.
56
+ - **Audit trail** - every authorization decision is written to an immutable log. Export as JSON or CSV for EU AI Act Article 12, SOC 2 CC6.1-CC7.2, and ISO 42001 compliance.
57
+ - **MCP OAuth 2.1** - spec-compliant authorization server for the Model Context Protocol, with PKCE (S256), Protected Resource Metadata (RFC 9728), and Resource Indicators (RFC 8707).
58
+
59
+ ## Full docs
60
+
61
+ [kavachos.com/docs](https://kavachos.com/docs)
62
+
63
+ ## Source
64
+
65
+ [github.com/kavachos/kavachos](https://github.com/kavachos/kavachos)
66
+
67
+ ## License
68
+
69
+ MIT
@@ -1,9 +1,10 @@
1
- import { D as Database, C as CreateAgentInput, A as AgentIdentity, d as AgentFilter, U as UpdateAgentInput } from '../types-fHHAt3tt.js';
2
- export { j as AgentConfig } from '../types-fHHAt3tt.js';
1
+ import { D as Database, C as CreateAgentInput, A as AgentIdentity, d as AgentFilter, U as UpdateAgentInput } from '../types-WP-mKSdQ.js';
2
+ export { k as AgentConfig } from '../types-WP-mKSdQ.js';
3
+ import '../types-_7hIICee.js';
4
+ import '../types-B4sQA44H.js';
5
+ import 'zod';
3
6
  import 'drizzle-orm/better-sqlite3';
4
7
  import 'drizzle-orm/sqlite-core';
5
- import '../types-C5htunW6.js';
6
- import 'zod';
7
8
 
8
9
  interface AgentModuleConfig {
9
10
  db: Database;
@@ -1,5 +1,5 @@
1
- export { createAgentModule } from '../chunk-DTCKF26N.js';
2
- import '../chunk-XSYYQH75.js';
1
+ export { createAgentModule } from '../chunk-I4J4KKKK.js';
2
+ import '../chunk-UEE7OYLG.js';
3
3
  import '../chunk-PZ5AY32C.js';
4
4
  //# sourceMappingURL=index.js.map
5
5
  //# sourceMappingURL=index.js.map
@@ -1,8 +1,9 @@
1
- import { D as Database, g as AuditFilter, h as AuditEntry, i as AuditExportOptions } from '../types-fHHAt3tt.js';
1
+ import { D as Database, g as AuditFilter, h as AuditEntry, i as AuditExportOptions } from '../types-WP-mKSdQ.js';
2
+ import '../types-_7hIICee.js';
3
+ import '../types-B4sQA44H.js';
4
+ import 'zod';
2
5
  import 'drizzle-orm/better-sqlite3';
3
6
  import 'drizzle-orm/sqlite-core';
4
- import '../types-C5htunW6.js';
5
- import 'zod';
6
7
 
7
8
  interface AuditModuleConfig {
8
9
  db: Database;
@@ -14,6 +15,11 @@ interface AuditModuleConfig {
14
15
  declare function createAuditModule(config: AuditModuleConfig): {
15
16
  query: (filter: AuditFilter) => Promise<AuditEntry[]>;
16
17
  export: (options: AuditExportOptions) => Promise<string>;
18
+ cleanup: (options: {
19
+ retentionDays: number;
20
+ }) => Promise<{
21
+ deleted: number;
22
+ }>;
17
23
  };
18
24
 
19
25
  export { AuditEntry, AuditExportOptions, AuditFilter, createAuditModule };
@@ -1,5 +1,5 @@
1
- export { createAuditModule } from '../chunk-XW2X3O53.js';
2
- import '../chunk-XSYYQH75.js';
1
+ export { createAuditModule } from '../chunk-N7VZO6SP.js';
2
+ import '../chunk-UEE7OYLG.js';
3
3
  import '../chunk-PZ5AY32C.js';
4
4
  //# sourceMappingURL=index.js.map
5
5
  //# sourceMappingURL=index.js.map
@@ -1,2 +1,143 @@
1
+ import { z } from 'zod';
2
+ import { A as AuthAdapter, R as ResolvedUser } from '../types-_7hIICee.js';
1
3
 
2
- export { }
4
+ /**
5
+ * JWT bearer-token auth adapter.
6
+ *
7
+ * Verifies a JWT from the `Authorization: Bearer <token>` header using a
8
+ * symmetric secret (HS256/HS384/HS512). Extracts `sub`, `email`, `name`, and
9
+ * `picture` from the payload and returns them as a `ResolvedUser`.
10
+ *
11
+ * @example
12
+ * ```typescript
13
+ * import { bearerAuth } from 'kavachos/auth';
14
+ *
15
+ * const adapter = bearerAuth({
16
+ * secret: process.env.JWT_SECRET,
17
+ * issuer: 'https://my-app.example.com',
18
+ * audience: 'kavachos',
19
+ * });
20
+ * ```
21
+ */
22
+
23
+ declare const BearerAuthOptionsSchema: z.ZodObject<{
24
+ /**
25
+ * Secret used to verify HS256/HS384/HS512 tokens.
26
+ * Must be at least 32 characters.
27
+ */
28
+ secret: z.ZodString;
29
+ /** Expected `iss` claim. Omit to skip issuer validation. */
30
+ issuer: z.ZodOptional<z.ZodString>;
31
+ /** Expected `aud` claim. Omit to skip audience validation. */
32
+ audience: z.ZodOptional<z.ZodString>;
33
+ }, "strip", z.ZodTypeAny, {
34
+ secret: string;
35
+ issuer?: string | undefined;
36
+ audience?: string | undefined;
37
+ }, {
38
+ secret: string;
39
+ issuer?: string | undefined;
40
+ audience?: string | undefined;
41
+ }>;
42
+ type BearerAuthOptions = z.infer<typeof BearerAuthOptionsSchema>;
43
+ /**
44
+ * Create an `AuthAdapter` that validates a JWT from the `Authorization: Bearer`
45
+ * header and maps its claims to a `ResolvedUser`.
46
+ *
47
+ * Returns `null` when:
48
+ * - No `Authorization` header is present
49
+ * - The header does not use the `Bearer` scheme
50
+ * - The JWT signature is invalid, the token is expired, or claims do not match
51
+ * the configured `issuer` / `audience`
52
+ */
53
+ declare function bearerAuth(options: BearerAuthOptions): AuthAdapter;
54
+
55
+ /**
56
+ * Custom resolver auth adapter.
57
+ *
58
+ * Wraps an arbitrary resolver function as an `AuthAdapter`. Use this when
59
+ * you need to integrate with an auth provider that does not have a built-in
60
+ * adapter (e.g. better-auth, Auth.js, Clerk, Supabase Auth, etc.).
61
+ *
62
+ * @example Clerk session cookie
63
+ * ```typescript
64
+ * import { customAuth } from 'kavachos/auth';
65
+ * import { clerkClient } from '@clerk/clerk-sdk-node';
66
+ *
67
+ * const adapter = customAuth(async (request) => {
68
+ * const sessionToken = request.headers.get('cookie')
69
+ * ?.split('; ')
70
+ * .find(c => c.startsWith('__session='))
71
+ * ?.split('=')[1];
72
+ *
73
+ * if (!sessionToken) return null;
74
+ *
75
+ * const session = await clerkClient.sessions.verifySession('', sessionToken);
76
+ * return { id: session.userId };
77
+ * });
78
+ * ```
79
+ *
80
+ * @example better-auth
81
+ * ```typescript
82
+ * import { customAuth } from 'kavachos/auth';
83
+ * import { auth } from './lib/auth'; // your better-auth instance
84
+ *
85
+ * const adapter = customAuth(async (request) => {
86
+ * const session = await auth.api.getSession({ headers: request.headers });
87
+ * if (!session?.user) return null;
88
+ * return {
89
+ * id: session.user.id,
90
+ * email: session.user.email ?? undefined,
91
+ * name: session.user.name ?? undefined,
92
+ * image: session.user.image ?? undefined,
93
+ * };
94
+ * });
95
+ * ```
96
+ */
97
+
98
+ /**
99
+ * Create an `AuthAdapter` from a custom resolver function.
100
+ *
101
+ * The resolver receives the raw `Request` and must return either a
102
+ * `ResolvedUser` or `null` (unauthenticated).
103
+ */
104
+ declare function customAuth(resolver: (request: Request) => Promise<ResolvedUser | null>): AuthAdapter;
105
+
106
+ /**
107
+ * Header-based auth adapter.
108
+ *
109
+ * Extracts the user ID from a trusted request header. Designed for services
110
+ * deployed behind an auth proxy (e.g. Nginx, Cloudflare Access, AWS ALB) that
111
+ * injects a verified user-identity header before forwarding requests.
112
+ *
113
+ * @example Default header (`X-User-Id`)
114
+ * ```typescript
115
+ * import { headerAuth } from 'kavachos/auth';
116
+ *
117
+ * const adapter = headerAuth();
118
+ * ```
119
+ *
120
+ * @example Custom header
121
+ * ```typescript
122
+ * const adapter = headerAuth({ header: 'X-Authenticated-User' });
123
+ * ```
124
+ *
125
+ * IMPORTANT: Only use this adapter when the header cannot be forged by the
126
+ * client (i.e. the upstream proxy strips or overrides it).
127
+ */
128
+
129
+ interface HeaderAuthOptions {
130
+ /**
131
+ * Name of the HTTP header that carries the user ID.
132
+ * Defaults to `X-User-Id`.
133
+ */
134
+ header?: string;
135
+ }
136
+ /**
137
+ * Create an `AuthAdapter` that extracts the user identity from a request header.
138
+ *
139
+ * Returns `null` when the header is absent or its value is empty.
140
+ */
141
+ declare function headerAuth(options?: HeaderAuthOptions): AuthAdapter;
142
+
143
+ export { AuthAdapter, type BearerAuthOptions, type HeaderAuthOptions, ResolvedUser, bearerAuth, customAuth, headerAuth };
@@ -1,3 +1,4 @@
1
-
1
+ export { bearerAuth, customAuth, headerAuth } from '../chunk-7RKVTHFC.js';
2
+ import '../chunk-PZ5AY32C.js';
2
3
  //# sourceMappingURL=index.js.map
3
4
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,96 @@
1
+ import { createSecretKey } from 'crypto';
2
+ import { jwtVerify } from 'jose';
3
+ import { z } from 'zod';
4
+
5
+ // src/auth/adapters/bearer.ts
6
+ var BearerAuthOptionsSchema = z.object({
7
+ /**
8
+ * Secret used to verify HS256/HS384/HS512 tokens.
9
+ * Must be at least 32 characters.
10
+ */
11
+ secret: z.string().min(1, "secret is required"),
12
+ /** Expected `iss` claim. Omit to skip issuer validation. */
13
+ issuer: z.string().optional(),
14
+ /** Expected `aud` claim. Omit to skip audience validation. */
15
+ audience: z.string().optional()
16
+ });
17
+ var JwtPayloadSchema = z.object({
18
+ sub: z.string(),
19
+ email: z.string().optional(),
20
+ name: z.string().optional(),
21
+ // Both `picture` (OIDC) and `image` (custom) are accepted.
22
+ picture: z.string().optional(),
23
+ image: z.string().optional()
24
+ });
25
+ function bearerAuth(options) {
26
+ const parsed = BearerAuthOptionsSchema.parse(options);
27
+ const keyObject = createSecretKey(Buffer.from(parsed.secret, "utf-8"));
28
+ return {
29
+ async resolveUser(request) {
30
+ const authHeader = request.headers.get("authorization");
31
+ if (!authHeader) return null;
32
+ const [scheme, token] = authHeader.split(" ");
33
+ if (scheme?.toLowerCase() !== "bearer" || !token) return null;
34
+ try {
35
+ const { payload } = await jwtVerify(token, keyObject, {
36
+ issuer: parsed.issuer,
37
+ audience: parsed.audience
38
+ });
39
+ const claims = JwtPayloadSchema.safeParse(payload);
40
+ if (!claims.success) return null;
41
+ const { sub, email, name, picture, image } = claims.data;
42
+ const metadata = {};
43
+ for (const [k, v] of Object.entries(payload)) {
44
+ if (![
45
+ "sub",
46
+ "email",
47
+ "name",
48
+ "picture",
49
+ "image",
50
+ "iat",
51
+ "exp",
52
+ "iss",
53
+ "aud",
54
+ "nbf",
55
+ "jti"
56
+ ].includes(k)) {
57
+ metadata[k] = v;
58
+ }
59
+ }
60
+ return {
61
+ id: sub,
62
+ ...email !== void 0 && { email },
63
+ ...name !== void 0 && { name },
64
+ ...picture !== void 0 || image !== void 0 ? { image: picture ?? image } : {},
65
+ ...Object.keys(metadata).length > 0 && { metadata }
66
+ };
67
+ } catch {
68
+ return null;
69
+ }
70
+ }
71
+ };
72
+ }
73
+
74
+ // src/auth/adapters/custom.ts
75
+ function customAuth(resolver) {
76
+ return {
77
+ resolveUser: resolver
78
+ };
79
+ }
80
+
81
+ // src/auth/adapters/header.ts
82
+ function headerAuth(options) {
83
+ const headerName = options?.header ?? "X-User-Id";
84
+ const normalised = headerName.toLowerCase();
85
+ return {
86
+ async resolveUser(request) {
87
+ const value = request.headers.get(normalised);
88
+ if (!value || value.trim() === "") return null;
89
+ return { id: value.trim() };
90
+ }
91
+ };
92
+ }
93
+
94
+ export { bearerAuth, customAuth, headerAuth };
95
+ //# sourceMappingURL=chunk-7RKVTHFC.js.map
96
+ //# sourceMappingURL=chunk-7RKVTHFC.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/auth/adapters/bearer.ts","../src/auth/adapters/custom.ts","../src/auth/adapters/header.ts"],"names":[],"mappings":"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","file":"chunk-7RKVTHFC.js","sourcesContent":["/**\n * JWT bearer-token auth adapter.\n *\n * Verifies a JWT from the `Authorization: Bearer <token>` header using a\n * symmetric secret (HS256/HS384/HS512). Extracts `sub`, `email`, `name`, and\n * `picture` from the payload and returns them as a `ResolvedUser`.\n *\n * @example\n * ```typescript\n * import { bearerAuth } from 'kavachos/auth';\n *\n * const adapter = bearerAuth({\n * secret: process.env.JWT_SECRET,\n * issuer: 'https://my-app.example.com',\n * audience: 'kavachos',\n * });\n * ```\n */\n\nimport { createSecretKey } from \"node:crypto\";\nimport { jwtVerify } from \"jose\";\nimport { z } from \"zod\";\nimport type { AuthAdapter, ResolvedUser } from \"../types.js\";\n\n// ---------------------------------------------------------------------------\n// Options schema\n// ---------------------------------------------------------------------------\n\nconst BearerAuthOptionsSchema = z.object({\n\t/**\n\t * Secret used to verify HS256/HS384/HS512 tokens.\n\t * Must be at least 32 characters.\n\t */\n\tsecret: z.string().min(1, \"secret is required\"),\n\t/** Expected `iss` claim. Omit to skip issuer validation. */\n\tissuer: z.string().optional(),\n\t/** Expected `aud` claim. Omit to skip audience validation. */\n\taudience: z.string().optional(),\n});\n\nexport type BearerAuthOptions = z.infer<typeof BearerAuthOptionsSchema>;\n\n// ---------------------------------------------------------------------------\n// Payload schema – only the fields KavachOS cares about\n// ---------------------------------------------------------------------------\n\nconst JwtPayloadSchema = z.object({\n\tsub: z.string(),\n\temail: z.string().optional(),\n\tname: z.string().optional(),\n\t// Both `picture` (OIDC) and `image` (custom) are accepted.\n\tpicture: z.string().optional(),\n\timage: z.string().optional(),\n});\n\n// ---------------------------------------------------------------------------\n// Factory\n// ---------------------------------------------------------------------------\n\n/**\n * Create an `AuthAdapter` that validates a JWT from the `Authorization: Bearer`\n * header and maps its claims to a `ResolvedUser`.\n *\n * Returns `null` when:\n * - No `Authorization` header is present\n * - The header does not use the `Bearer` scheme\n * - The JWT signature is invalid, the token is expired, or claims do not match\n * the configured `issuer` / `audience`\n */\nexport function bearerAuth(options: BearerAuthOptions): AuthAdapter {\n\tconst parsed = BearerAuthOptionsSchema.parse(options);\n\n\t// Pre-compute the KeyObject once so we don't recreate it per request.\n\tconst keyObject = createSecretKey(Buffer.from(parsed.secret, \"utf-8\"));\n\n\treturn {\n\t\tasync resolveUser(request: Request): Promise<ResolvedUser | null> {\n\t\t\tconst authHeader = request.headers.get(\"authorization\");\n\t\t\tif (!authHeader) return null;\n\n\t\t\tconst [scheme, token] = authHeader.split(\" \");\n\t\t\tif (scheme?.toLowerCase() !== \"bearer\" || !token) return null;\n\n\t\t\ttry {\n\t\t\t\tconst { payload } = await jwtVerify(token, keyObject, {\n\t\t\t\t\tissuer: parsed.issuer,\n\t\t\t\t\taudience: parsed.audience,\n\t\t\t\t});\n\n\t\t\t\tconst claims = JwtPayloadSchema.safeParse(payload);\n\t\t\t\tif (!claims.success) return null;\n\n\t\t\t\tconst { sub, email, name, picture, image } = claims.data;\n\n\t\t\t\t// Strip undefined fields from metadata so callers get a clean object.\n\t\t\t\tconst metadata: Record<string, unknown> = {};\n\t\t\t\tfor (const [k, v] of Object.entries(payload)) {\n\t\t\t\t\tif (\n\t\t\t\t\t\t![\n\t\t\t\t\t\t\t\"sub\",\n\t\t\t\t\t\t\t\"email\",\n\t\t\t\t\t\t\t\"name\",\n\t\t\t\t\t\t\t\"picture\",\n\t\t\t\t\t\t\t\"image\",\n\t\t\t\t\t\t\t\"iat\",\n\t\t\t\t\t\t\t\"exp\",\n\t\t\t\t\t\t\t\"iss\",\n\t\t\t\t\t\t\t\"aud\",\n\t\t\t\t\t\t\t\"nbf\",\n\t\t\t\t\t\t\t\"jti\",\n\t\t\t\t\t\t].includes(k)\n\t\t\t\t\t) {\n\t\t\t\t\t\tmetadata[k] = v;\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\treturn {\n\t\t\t\t\tid: sub,\n\t\t\t\t\t...(email !== undefined && { email }),\n\t\t\t\t\t...(name !== undefined && { name }),\n\t\t\t\t\t...(picture !== undefined || image !== undefined ? { image: picture ?? image } : {}),\n\t\t\t\t\t...(Object.keys(metadata).length > 0 && { metadata }),\n\t\t\t\t};\n\t\t\t} catch {\n\t\t\t\t// Token verification failed (expired, bad signature, wrong issuer, etc.)\n\t\t\t\treturn null;\n\t\t\t}\n\t\t},\n\t};\n}\n","/**\n * Custom resolver auth adapter.\n *\n * Wraps an arbitrary resolver function as an `AuthAdapter`. Use this when\n * you need to integrate with an auth provider that does not have a built-in\n * adapter (e.g. better-auth, Auth.js, Clerk, Supabase Auth, etc.).\n *\n * @example Clerk session cookie\n * ```typescript\n * import { customAuth } from 'kavachos/auth';\n * import { clerkClient } from '@clerk/clerk-sdk-node';\n *\n * const adapter = customAuth(async (request) => {\n * const sessionToken = request.headers.get('cookie')\n * ?.split('; ')\n * .find(c => c.startsWith('__session='))\n * ?.split('=')[1];\n *\n * if (!sessionToken) return null;\n *\n * const session = await clerkClient.sessions.verifySession('', sessionToken);\n * return { id: session.userId };\n * });\n * ```\n *\n * @example better-auth\n * ```typescript\n * import { customAuth } from 'kavachos/auth';\n * import { auth } from './lib/auth'; // your better-auth instance\n *\n * const adapter = customAuth(async (request) => {\n * const session = await auth.api.getSession({ headers: request.headers });\n * if (!session?.user) return null;\n * return {\n * id: session.user.id,\n * email: session.user.email ?? undefined,\n * name: session.user.name ?? undefined,\n * image: session.user.image ?? undefined,\n * };\n * });\n * ```\n */\n\nimport type { AuthAdapter, ResolvedUser } from \"../types.js\";\n\n/**\n * Create an `AuthAdapter` from a custom resolver function.\n *\n * The resolver receives the raw `Request` and must return either a\n * `ResolvedUser` or `null` (unauthenticated).\n */\nexport function customAuth(\n\tresolver: (request: Request) => Promise<ResolvedUser | null>,\n): AuthAdapter {\n\treturn {\n\t\tresolveUser: resolver,\n\t};\n}\n","/**\n * Header-based auth adapter.\n *\n * Extracts the user ID from a trusted request header. Designed for services\n * deployed behind an auth proxy (e.g. Nginx, Cloudflare Access, AWS ALB) that\n * injects a verified user-identity header before forwarding requests.\n *\n * @example Default header (`X-User-Id`)\n * ```typescript\n * import { headerAuth } from 'kavachos/auth';\n *\n * const adapter = headerAuth();\n * ```\n *\n * @example Custom header\n * ```typescript\n * const adapter = headerAuth({ header: 'X-Authenticated-User' });\n * ```\n *\n * IMPORTANT: Only use this adapter when the header cannot be forged by the\n * client (i.e. the upstream proxy strips or overrides it).\n */\n\nimport type { AuthAdapter, ResolvedUser } from \"../types.js\";\n\nexport interface HeaderAuthOptions {\n\t/**\n\t * Name of the HTTP header that carries the user ID.\n\t * Defaults to `X-User-Id`.\n\t */\n\theader?: string;\n}\n\n/**\n * Create an `AuthAdapter` that extracts the user identity from a request header.\n *\n * Returns `null` when the header is absent or its value is empty.\n */\nexport function headerAuth(options?: HeaderAuthOptions): AuthAdapter {\n\tconst headerName = options?.header ?? \"X-User-Id\";\n\t// Normalise to lower-case for case-insensitive lookup via the Headers API.\n\tconst normalised = headerName.toLowerCase();\n\n\treturn {\n\t\tasync resolveUser(request: Request): Promise<ResolvedUser | null> {\n\t\t\tconst value = request.headers.get(normalised);\n\t\t\tif (!value || value.trim() === \"\") return null;\n\n\t\t\treturn { id: value.trim() };\n\t\t},\n\t};\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { auditLogs, rateLimits } from './chunk-XSYYQH75.js';
1
+ import { auditLogs, rateLimits } from './chunk-UEE7OYLG.js';
2
2
  import { randomUUID } from 'crypto';
3
3
  import { and, eq, gte } from 'drizzle-orm';
4
4
 
@@ -16,6 +16,34 @@ function matchResource(pattern, resource) {
16
16
  function matchAction(allowedActions, requestedAction) {
17
17
  return allowedActions.includes(requestedAction) || allowedActions.includes("*");
18
18
  }
19
+ function parseIPv4(ip) {
20
+ const parts = ip.split(".");
21
+ if (parts.length !== 4) return null;
22
+ let result = 0;
23
+ for (const part of parts) {
24
+ const num = parseInt(part, 10);
25
+ if (Number.isNaN(num) || num < 0 || num > 255) return null;
26
+ result = result << 8 | num;
27
+ }
28
+ return result >>> 0;
29
+ }
30
+ function matchesIPEntry(entry, ip) {
31
+ const slashIndex = entry.indexOf("/");
32
+ if (slashIndex === -1) {
33
+ return entry === ip;
34
+ }
35
+ const cidrIp = entry.slice(0, slashIndex);
36
+ const prefixLen = parseInt(entry.slice(slashIndex + 1), 10);
37
+ if (Number.isNaN(prefixLen) || prefixLen < 0 || prefixLen > 32) return false;
38
+ const entryNum = parseIPv4(cidrIp);
39
+ const ipNum = parseIPv4(ip);
40
+ if (entryNum === null || ipNum === null) return false;
41
+ const mask = prefixLen === 0 ? 0 : -1 << 32 - prefixLen >>> 0;
42
+ return (entryNum & mask) === (ipNum & mask);
43
+ }
44
+ function isIPAllowed(allowlist, ip) {
45
+ return allowlist.some((entry) => matchesIPEntry(entry, ip));
46
+ }
19
47
  function validateArgPatterns(patterns, args) {
20
48
  for (const pattern of patterns) {
21
49
  const regex = new RegExp(pattern);
@@ -143,6 +171,20 @@ async function evaluateConstraints(db, agent, request, constraints) {
143
171
  };
144
172
  }
145
173
  }
174
+ if (constraints.ipAllowlist && constraints.ipAllowlist.length > 0) {
175
+ if (!request.ip) {
176
+ return {
177
+ allowed: false,
178
+ reason: "IP_NOT_ALLOWED: No IP address provided; resource requires an IP allowlist match"
179
+ };
180
+ }
181
+ if (!isIPAllowed(constraints.ipAllowlist, request.ip)) {
182
+ return {
183
+ allowed: false,
184
+ reason: `IP_NOT_ALLOWED: IP "${request.ip}" is not in the allowlist for this resource`
185
+ };
186
+ }
187
+ }
146
188
  return { allowed: true };
147
189
  }
148
190
  async function writeAuditLog(db, agent, request, result, startTime, auditId) {
@@ -157,7 +199,9 @@ async function writeAuditLog(db, agent, request, result, startTime, auditId) {
157
199
  result: result.allowed ? "allowed" : "denied",
158
200
  reason: result.reason ?? null,
159
201
  durationMs,
160
- timestamp: /* @__PURE__ */ new Date()
202
+ timestamp: /* @__PURE__ */ new Date(),
203
+ ip: request.context?.ip ?? null,
204
+ userAgent: request.context?.userAgent ?? null
161
205
  });
162
206
  }
163
207
 
@@ -203,5 +247,5 @@ function getPermissionTemplate(name) {
203
247
  }
204
248
 
205
249
  export { createPermissionEngine, getPermissionTemplate, permissionTemplates };
206
- //# sourceMappingURL=chunk-D2LJLY7F.js.map
207
- //# sourceMappingURL=chunk-D2LJLY7F.js.map
250
+ //# sourceMappingURL=chunk-DEVV32BE.js.map
251
+ //# sourceMappingURL=chunk-DEVV32BE.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/permission/engine.ts","../src/permission/templates.ts"],"names":["result"],"mappings":";;;;AAwBA,SAAS,aAAA,CAAc,SAAiB,QAAA,EAA2B;AAClE,EAAA,IAAI,OAAA,KAAY,KAAK,OAAO,IAAA;AAE5B,EAAA,MAAM,YAAA,GAAe,OAAA,CAAQ,KAAA,CAAM,GAAG,CAAA;AACtC,EAAA,MAAM,aAAA,GAAgB,QAAA,CAAS,KAAA,CAAM,GAAG,CAAA;AAExC,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,YAAA,CAAa,QAAQ,CAAA,EAAA,EAAK;AAC7C,IAAA,MAAM,IAAA,GAAO,aAAa,CAAC,CAAA;AAC3B,IAAA,IAAI,IAAA,KAAS,KAAK,OAAO,IAAA;AACzB,IAAA,IAAI,IAAA,KAAS,aAAA,CAAc,CAAC,CAAA,EAAG,OAAO,KAAA;AAAA,EACvC;AAEA,EAAA,OAAO,YAAA,CAAa,WAAW,aAAA,CAAc,MAAA;AAC9C;AAKA,SAAS,WAAA,CAAY,gBAA0B,eAAA,EAAkC;AAChF,EAAA,OAAO,eAAe,QAAA,CAAS,eAAe,CAAA,IAAK,cAAA,CAAe,SAAS,GAAG,CAAA;AAC/E;AAKA,SAAS,UAAU,EAAA,EAA2B;AAC7C,EAAA,MAAM,KAAA,GAAQ,EAAA,CAAG,KAAA,CAAM,GAAG,CAAA;AAC1B,EAAA,IAAI,KAAA,CAAM,MAAA,KAAW,CAAA,EAAG,OAAO,IAAA;AAC/B,EAAA,IAAI,MAAA,GAAS,CAAA;AACb,EAAA,KAAA,MAAW,QAAQ,KAAA,EAAO;AACzB,IAAA,MAAM,GAAA,GAAM,QAAA,CAAS,IAAA,EAAM,EAAE,CAAA;AAC7B,IAAA,IAAI,MAAA,CAAO,MAAM,GAAG,CAAA,IAAK,MAAM,CAAA,IAAK,GAAA,GAAM,KAAK,OAAO,IAAA;AACtD,IAAA,MAAA,GAAU,UAAU,CAAA,GAAK,GAAA;AAAA,EAC1B;AACA,EAAA,OAAO,MAAA,KAAW,CAAA;AACnB;AAMA,SAAS,cAAA,CAAe,OAAe,EAAA,EAAqB;AAC3D,EAAA,MAAM,UAAA,GAAa,KAAA,CAAM,OAAA,CAAQ,GAAG,CAAA;AACpC,EAAA,IAAI,eAAe,EAAA,EAAI;AACtB,IAAA,OAAO,KAAA,KAAU,EAAA;AAAA,EAClB;AAEA,EAAA,MAAM,MAAA,GAAS,KAAA,CAAM,KAAA,CAAM,CAAA,EAAG,UAAU,CAAA;AACxC,EAAA,MAAM,YAAY,QAAA,CAAS,KAAA,CAAM,MAAM,UAAA,GAAa,CAAC,GAAG,EAAE,CAAA;AAC1D,EAAA,IAAI,MAAA,CAAO,MAAM,SAAS,CAAA,IAAK,YAAY,CAAA,IAAK,SAAA,GAAY,IAAI,OAAO,KAAA;AAEvE,EAAA,MAAM,QAAA,GAAW,UAAU,MAAM,CAAA;AACjC,EAAA,MAAM,KAAA,GAAQ,UAAU,EAAE,CAAA;AAC1B,EAAA,IAAI,QAAA,KAAa,IAAA,IAAQ,KAAA,KAAU,IAAA,EAAM,OAAO,KAAA;AAEhD,EAAA,MAAM,OAAO,SAAA,KAAc,CAAA,GAAI,IAAK,EAAC,IAAM,KAAK,SAAA,KAAgB,CAAA;AAChE,EAAA,OAAA,CAAQ,QAAA,GAAW,WAAW,KAAA,GAAQ,IAAA,CAAA;AACvC;AAKA,SAAS,WAAA,CAAY,WAAqB,EAAA,EAAqB;AAC9D,EAAA,OAAO,UAAU,IAAA,CAAK,CAAC,UAAU,cAAA,CAAe,KAAA,EAAO,EAAE,CAAC,CAAA;AAC3D;AAKA,SAAS,mBAAA,CACR,UACA,IAAA,EACsC;AACtC,EAAA,KAAA,MAAW,WAAW,QAAA,EAAU;AAC/B,IAAA,MAAM,KAAA,GAAQ,IAAI,MAAA,CAAO,OAAO,CAAA;AAEhC,IAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA,EAAG;AAChD,MAAA,IAAI,OAAO,KAAA,KAAU,QAAA,IAAY,CAAC,KAAA,CAAM,IAAA,CAAK,KAAK,CAAA,EAAG;AACpD,QAAA,OAAO;AAAA,UACN,KAAA,EAAO,KAAA;AAAA,UACP,QAAQ,CAAA,UAAA,EAAa,GAAG,CAAA,SAAA,EAAY,KAAK,6BAA6B,OAAO,CAAA,CAAA;AAAA,SAC9E;AAAA,MACD;AAAA,IACD;AAAA,EACD;AACA,EAAA,OAAO,EAAE,OAAO,IAAA,EAAK;AACtB;AAKA,eAAe,cAAA,CACd,EAAA,EACA,OAAA,EACA,QAAA,EACA,eAAA,EACiD;AACjD,EAAA,MAAM,UAAA,GAAa,IAAI,IAAA,CAAK,IAAA,CAAK,KAAI,GAAI,EAAA,GAAK,KAAK,GAAI,CAAA;AAEvD,EAAA,MAAM,OAAO,MAAM,EAAA,CACjB,QAAO,CACP,IAAA,CAAK,UAAU,CAAA,CACf,KAAA;AAAA,IACA,GAAA;AAAA,MACC,EAAA,CAAG,UAAA,CAAW,OAAA,EAAS,OAAO,CAAA;AAAA,MAC9B,EAAA,CAAG,UAAA,CAAW,QAAA,EAAU,QAAQ,CAAA;AAAA,MAChC,GAAA,CAAI,UAAA,CAAW,WAAA,EAAa,UAAU;AAAA;AACvC,GACD;AAED,EAAA,MAAM,UAAA,GAAa,KAAK,MAAA,CAAO,CAAC,KAAK,CAAA,KAAM,GAAA,GAAM,CAAA,CAAE,KAAA,EAAO,CAAC,CAAA;AAE3D,EAAA,IAAI,cAAc,eAAA,EAAiB;AAClC,IAAA,OAAO;AAAA,MACN,OAAA,EAAS,KAAA;AAAA,MACT,QAAQ,CAAA,qBAAA,EAAwB,UAAU,CAAA,CAAA,EAAI,eAAe,iCAAiC,QAAQ,CAAA,CAAA;AAAA,KACvG;AAAA,EACD;AAGA,EAAA,MAAM,aAAA,GAAgB,IAAI,IAAA,CAAK,IAAA,CAAK,MAAM,IAAA,CAAK,GAAA,EAAI,IAAK,CAAA,GAAI,EAAA,GAAK,GAAA,CAAK,CAAA,IAAK,CAAA,GAAI,KAAK,GAAA,CAAK,CAAA;AACzF,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,WAAA,CAAY,OAAA,EAAQ,KAAM,aAAA,CAAc,OAAA,EAAS,CAAA;AAErF,EAAA,IAAI,QAAA,EAAU;AACb,IAAA,MAAM,GACJ,MAAA,CAAO,UAAU,EACjB,GAAA,CAAI,EAAE,OAAO,QAAA,CAAS,KAAA,GAAQ,CAAA,EAAG,EACjC,KAAA,CAAM,EAAA,CAAG,WAAW,EAAA,EAAI,QAAA,CAAS,EAAE,CAAC,CAAA;AAAA,EACvC,CAAA,MAAO;AACN,IAAA,MAAM,EAAA,CAAG,MAAA,CAAO,UAAU,CAAA,CAAE,MAAA,CAAO;AAAA,MAClC,IAAI,UAAA,EAAW;AAAA,MACf,OAAA;AAAA,MACA,QAAA;AAAA,MACA,WAAA,EAAa,aAAA;AAAA,MACb,KAAA,EAAO;AAAA,KACP,CAAA;AAAA,EACF;AAEA,EAAA,OAAO,EAAE,SAAS,IAAA,EAAK;AACxB;AAKO,SAAS,uBAAuB,MAAA,EAAgC;AACtE,EAAA,MAAM,EAAE,EAAA,EAAI,QAAA,EAAS,GAAI,MAAA;AAMzB,EAAA,eAAe,SAAA,CACd,OACA,OAAA,EAC2B;AAC3B,IAAA,MAAM,SAAA,GAAY,YAAY,GAAA,EAAI;AAClC,IAAA,MAAM,UAAU,UAAA,EAAW;AAG3B,IAAA,MAAM,kBAAA,GAAqB,MAAM,WAAA,CAAY,IAAA;AAAA,MAC5C,CAAC,CAAA,KAAM,aAAA,CAAc,CAAA,CAAE,QAAA,EAAU,OAAA,CAAQ,QAAQ,CAAA,IAAK,WAAA,CAAY,CAAA,CAAE,OAAA,EAAS,OAAA,CAAQ,MAAM;AAAA,KAC5F;AAEA,IAAA,IAAI,CAAC,kBAAA,EAAoB;AACxB,MAAA,MAAMA,OAAAA,GAA0B;AAAA,QAC/B,OAAA,EAAS,KAAA;AAAA,QACT,MAAA,EAAQ,+BAA+B,KAAA,CAAM,IAAI,gBAAgB,OAAA,CAAQ,MAAM,CAAA,MAAA,EAAS,OAAA,CAAQ,QAAQ,CAAA,CAAA,CAAA;AAAA,QACxG;AAAA,OACD;AACA,MAAA,IAAI,QAAA,EAAU;AACb,QAAA,MAAM,cAAc,EAAA,EAAI,KAAA,EAAO,OAAA,EAASA,OAAAA,EAAQ,WAAW,OAAO,CAAA;AAAA,MACnE;AACA,MAAA,OAAOA,OAAAA;AAAA,IACR;AAGA,IAAA,IAAI,mBAAmB,WAAA,EAAa;AACnC,MAAA,MAAM,mBAAmB,MAAM,mBAAA;AAAA,QAC9B,EAAA;AAAA,QACA,KAAA;AAAA,QACA,OAAA;AAAA,QACA,kBAAA,CAAmB;AAAA,OACpB;AACA,MAAA,IAAI,CAAC,iBAAiB,OAAA,EAAS;AAC9B,QAAA,MAAMA,OAAAA,GAA0B;AAAA,UAC/B,OAAA,EAAS,KAAA;AAAA,UACT,QAAQ,gBAAA,CAAiB,MAAA;AAAA,UACzB;AAAA,SACD;AACA,QAAA,IAAI,QAAA,EAAU;AACb,UAAA,MAAM,cAAc,EAAA,EAAI,KAAA,EAAO,OAAA,EAASA,OAAAA,EAAQ,WAAW,OAAO,CAAA;AAAA,QACnE;AACA,QAAA,OAAOA,OAAAA;AAAA,MACR;AAAA,IACD;AAEA,IAAA,MAAM,MAAA,GAA0B,EAAE,OAAA,EAAS,IAAA,EAAM,OAAA,EAAQ;AACzD,IAAA,IAAI,QAAA,EAAU;AACb,MAAA,MAAM,cAAc,EAAA,EAAI,KAAA,EAAO,OAAA,EAAS,MAAA,EAAQ,WAAW,OAAO,CAAA;AAAA,IACnE;AACA,IAAA,OAAO,MAAA;AAAA,EACR;AAEA,EAAA,OAAO,EAAE,SAAA,EAAU;AACpB;AAEA,eAAe,mBAAA,CACd,EAAA,EACA,KAAA,EACA,OAAA,EACA,WAAA,EACiD;AAEjD,EAAA,IAAI,YAAY,eAAA,EAAiB;AAChC,IAAA,MAAM,aAAa,MAAM,cAAA;AAAA,MACxB,EAAA;AAAA,MACA,KAAA,CAAM,EAAA;AAAA,MACN,OAAA,CAAQ,QAAA;AAAA,MACR,WAAA,CAAY;AAAA,KACb;AACA,IAAA,IAAI,CAAC,WAAW,OAAA,EAAS;AACxB,MAAA,OAAO,UAAA;AAAA,IACR;AAAA,EACD;AAGA,EAAA,IAAI,WAAA,CAAY,kBAAA,IAAsB,OAAA,CAAQ,SAAA,EAAW;AACxD,IAAA,MAAM,aAAA,GAAgB,mBAAA,CAAoB,WAAA,CAAY,kBAAA,EAAoB,QAAQ,SAAS,CAAA;AAC3F,IAAA,IAAI,CAAC,cAAc,KAAA,EAAO;AACzB,MAAA,OAAO,EAAE,OAAA,EAAS,KAAA,EAAO,MAAA,EAAQ,cAAc,MAAA,EAAO;AAAA,IACvD;AAAA,EACD;AAGA,EAAA,IAAI,YAAY,eAAA,EAAiB;AAChC,IAAA,OAAO;AAAA,MACN,OAAA,EAAS,KAAA;AAAA,MACT,MAAA,EAAQ;AAAA,KACT;AAAA,EACD;AAGA,EAAA,IAAI,YAAY,UAAA,EAAY;AAC3B,IAAA,MAAM,GAAA,uBAAU,IAAA,EAAK;AACrB,IAAA,MAAM,KAAA,GAAQ,IAAI,QAAA,EAAS;AAC3B,IAAA,MAAM,OAAA,GAAU,IAAI,UAAA,EAAW;AAC/B,IAAA,MAAM,cAAc,CAAA,EAAG,MAAA,CAAO,KAAK,CAAA,CAAE,SAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAA,EAAI,OAAO,OAAO,CAAA,CAAE,QAAA,CAAS,CAAA,EAAG,GAAG,CAAC,CAAA,CAAA;AAEzF,IAAA,IAAI,cAAc,WAAA,CAAY,UAAA,CAAW,SAAS,WAAA,GAAc,WAAA,CAAY,WAAW,GAAA,EAAK;AAC3F,MAAA,OAAO;AAAA,QACN,OAAA,EAAS,KAAA;AAAA,QACT,MAAA,EAAQ,kCAAkC,WAAA,CAAY,UAAA,CAAW,KAAK,CAAA,KAAA,EAAQ,WAAA,CAAY,WAAW,GAAG,CAAA;AAAA,OACzG;AAAA,IACD;AAAA,EACD;AAGA,EAAA,IAAI,WAAA,CAAY,WAAA,IAAe,WAAA,CAAY,WAAA,CAAY,SAAS,CAAA,EAAG;AAClE,IAAA,IAAI,CAAC,QAAQ,EAAA,EAAI;AAChB,MAAA,OAAO;AAAA,QACN,OAAA,EAAS,KAAA;AAAA,QACT,MAAA,EAAQ;AAAA,OACT;AAAA,IACD;AACA,IAAA,IAAI,CAAC,WAAA,CAAY,WAAA,CAAY,WAAA,EAAa,OAAA,CAAQ,EAAE,CAAA,EAAG;AACtD,MAAA,OAAO;AAAA,QACN,OAAA,EAAS,KAAA;AAAA,QACT,MAAA,EAAQ,CAAA,oBAAA,EAAuB,OAAA,CAAQ,EAAE,CAAA,2CAAA;AAAA,OAC1C;AAAA,IACD;AAAA,EACD;AAEA,EAAA,OAAO,EAAE,SAAS,IAAA,EAAK;AACxB;AAEA,eAAe,cACd,EAAA,EACA,KAAA,EACA,OAAA,EACA,MAAA,EACA,WACA,OAAA,EACgB;AAChB,EAAA,MAAM,aAAa,IAAA,CAAK,KAAA,CAAM,WAAA,CAAY,GAAA,KAAQ,SAAS,CAAA;AAE3D,EAAA,MAAM,EAAA,CAAG,MAAA,CAAO,SAAS,CAAA,CAAE,MAAA,CAAO;AAAA,IACjC,EAAA,EAAI,OAAA;AAAA,IACJ,SAAS,KAAA,CAAM,EAAA;AAAA,IACf,QAAQ,KAAA,CAAM,OAAA;AAAA,IACd,QAAQ,OAAA,CAAQ,MAAA;AAAA,IAChB,UAAU,OAAA,CAAQ,QAAA;AAAA,IAClB,UAAA,EAAY,OAAA,CAAQ,SAAA,IAAa,EAAC;AAAA,IAClC,MAAA,EAAQ,MAAA,CAAO,OAAA,GAAU,SAAA,GAAY,QAAA;AAAA,IACrC,MAAA,EAAQ,OAAO,MAAA,IAAU,IAAA;AAAA,IACzB,UAAA;AAAA,IACA,SAAA,sBAAe,IAAA,EAAK;AAAA,IACpB,EAAA,EAAI,OAAA,CAAQ,OAAA,EAAS,EAAA,IAAM,IAAA;AAAA,IAC3B,SAAA,EAAW,OAAA,CAAQ,OAAA,EAAS,SAAA,IAAa;AAAA,GACzC,CAAA;AACF;;;AC7TO,IAAM,mBAAA,GAAsB;AAAA;AAAA,EAElC,QAAA,EAAU,CAAC,EAAE,QAAA,EAAU,KAAK,OAAA,EAAS,CAAC,MAAM,CAAA,EAAG,CAAA;AAAA;AAAA,EAG/C,SAAA,EAAW,CAAC,EAAE,QAAA,EAAU,GAAA,EAAK,SAAS,CAAC,MAAA,EAAQ,OAAO,CAAA,EAAG,CAAA;AAAA;AAAA,EAGzD,KAAA,EAAO,CAAC,EAAE,QAAA,EAAU,KAAK,OAAA,EAAS,CAAC,GAAG,CAAA,EAAG,CAAA;AAAA;AAAA,EAGzC,QAAA,EAAU,CAAC,EAAE,QAAA,EAAU,OAAA,EAAS,SAAS,CAAC,MAAA,EAAQ,SAAS,CAAA,EAAG,CAAA;AAAA;AAAA,EAG9D,OAAA,EAAS,CAAC,EAAE,QAAA,EAAU,OAAA,EAAS,OAAA,EAAS,CAAC,MAAA,EAAQ,OAAA,EAAS,SAAS,CAAA,EAAG,CAAA;AAAA;AAAA,EAGtE,eAAA,EAAiB;AAAA,IAChB;AAAA,MACC,QAAA,EAAU,GAAA;AAAA,MACV,OAAA,EAAS,CAAC,MAAM,CAAA;AAAA,MAChB,WAAA,EAAa,EAAE,eAAA,EAAiB,GAAA;AAAI;AACrC,GACD;AAAA;AAAA,EAGA,gBAAA,EAAkB;AAAA,IACjB;AAAA,MACC,QAAA,EAAU,GAAA;AAAA,MACV,OAAA,EAAS,CAAC,GAAG,CAAA;AAAA,MACb,WAAA,EAAa,EAAE,eAAA,EAAiB,IAAA;AAAK;AACtC,GACD;AAAA;AAAA,EAGA,aAAA,EAAe;AAAA,IACd;AAAA,MACC,QAAA,EAAU,GAAA;AAAA,MACV,OAAA,EAAS,CAAC,MAAA,EAAQ,OAAA,EAAS,SAAS,CAAA;AAAA,MACpC,WAAA,EAAa,EAAE,UAAA,EAAY,EAAE,OAAO,OAAA,EAAS,GAAA,EAAK,SAAQ;AAAE;AAC7D;AAEF;AAQO,SAAS,sBAAsB,IAAA,EAA4C;AACjF,EAAA,OAAO,KAAK,KAAA,CAAM,IAAA,CAAK,UAAU,mBAAA,CAAoB,IAAI,CAAC,CAAC,CAAA;AAC5D","file":"chunk-DEVV32BE.js","sourcesContent":["import { randomUUID } from \"node:crypto\";\nimport { and, eq, gte } from \"drizzle-orm\";\nimport type { Database } from \"../db/database.js\";\nimport { auditLogs, rateLimits } from \"../db/schema.js\";\nimport type {\n\tAgentIdentity,\n\tAuthorizeRequest,\n\tAuthorizeResult,\n\tPermissionConstraints,\n} from \"../types.js\";\n\ninterface PermissionEngineConfig {\n\tdb: Database;\n\tauditAll: boolean;\n}\n\n/**\n * Match a resource pattern against a requested resource.\n *\n * Supports wildcards:\n * - \"mcp:github:*\" matches \"mcp:github:create_issue\"\n * - \"tool:*\" matches \"tool:file_read\"\n * - \"*\" matches everything\n */\nfunction matchResource(pattern: string, resource: string): boolean {\n\tif (pattern === \"*\") return true;\n\n\tconst patternParts = pattern.split(\":\");\n\tconst resourceParts = resource.split(\":\");\n\n\tfor (let i = 0; i < patternParts.length; i++) {\n\t\tconst part = patternParts[i];\n\t\tif (part === \"*\") return true;\n\t\tif (part !== resourceParts[i]) return false;\n\t}\n\n\treturn patternParts.length === resourceParts.length;\n}\n\n/**\n * Check if an action is allowed by a permission's actions list.\n */\nfunction matchAction(allowedActions: string[], requestedAction: string): boolean {\n\treturn allowedActions.includes(requestedAction) || allowedActions.includes(\"*\");\n}\n\n/**\n * Parse an IPv4 address into a 32-bit integer.\n */\nfunction parseIPv4(ip: string): number | null {\n\tconst parts = ip.split(\".\");\n\tif (parts.length !== 4) return null;\n\tlet result = 0;\n\tfor (const part of parts) {\n\t\tconst num = parseInt(part, 10);\n\t\tif (Number.isNaN(num) || num < 0 || num > 255) return null;\n\t\tresult = (result << 8) | num;\n\t}\n\treturn result >>> 0;\n}\n\n/**\n * Check whether an IP matches a CIDR range or exact IP entry.\n * Supports both \"10.0.0.1\" and \"10.0.0.0/8\" notation (IPv4 only).\n */\nfunction matchesIPEntry(entry: string, ip: string): boolean {\n\tconst slashIndex = entry.indexOf(\"/\");\n\tif (slashIndex === -1) {\n\t\treturn entry === ip;\n\t}\n\n\tconst cidrIp = entry.slice(0, slashIndex);\n\tconst prefixLen = parseInt(entry.slice(slashIndex + 1), 10);\n\tif (Number.isNaN(prefixLen) || prefixLen < 0 || prefixLen > 32) return false;\n\n\tconst entryNum = parseIPv4(cidrIp);\n\tconst ipNum = parseIPv4(ip);\n\tif (entryNum === null || ipNum === null) return false;\n\n\tconst mask = prefixLen === 0 ? 0 : (~0 << (32 - prefixLen)) >>> 0;\n\treturn (entryNum & mask) === (ipNum & mask);\n}\n\n/**\n * Check whether an IP is in the allowlist (exact IPs or CIDR ranges).\n */\nfunction isIPAllowed(allowlist: string[], ip: string): boolean {\n\treturn allowlist.some((entry) => matchesIPEntry(entry, ip));\n}\n\n/**\n * Validate argument patterns against the request arguments.\n */\nfunction validateArgPatterns(\n\tpatterns: string[],\n\targs: Record<string, unknown>,\n): { valid: boolean; reason?: string } {\n\tfor (const pattern of patterns) {\n\t\tconst regex = new RegExp(pattern);\n\t\t// Check all string arguments against the pattern\n\t\tfor (const [key, value] of Object.entries(args)) {\n\t\t\tif (typeof value === \"string\" && !regex.test(value)) {\n\t\t\t\treturn {\n\t\t\t\t\tvalid: false,\n\t\t\t\t\treason: `Argument \"${key}\" value \"${value}\" does not match pattern \"${pattern}\"`,\n\t\t\t\t};\n\t\t\t}\n\t\t}\n\t}\n\treturn { valid: true };\n}\n\n/**\n * Check rate limits for an agent on a specific resource.\n */\nasync function checkRateLimit(\n\tdb: Database,\n\tagentId: string,\n\tresource: string,\n\tmaxCallsPerHour: number,\n): Promise<{ allowed: boolean; reason?: string }> {\n\tconst oneHourAgo = new Date(Date.now() - 60 * 60 * 1000);\n\n\tconst rows = await db\n\t\t.select()\n\t\t.from(rateLimits)\n\t\t.where(\n\t\t\tand(\n\t\t\t\teq(rateLimits.agentId, agentId),\n\t\t\t\teq(rateLimits.resource, resource),\n\t\t\t\tgte(rateLimits.windowStart, oneHourAgo),\n\t\t\t),\n\t\t);\n\n\tconst totalCalls = rows.reduce((sum, r) => sum + r.count, 0);\n\n\tif (totalCalls >= maxCallsPerHour) {\n\t\treturn {\n\t\t\tallowed: false,\n\t\t\treason: `Rate limit exceeded: ${totalCalls}/${maxCallsPerHour} calls per hour for resource \"${resource}\"`,\n\t\t};\n\t}\n\n\t// Increment counter\n\tconst currentWindow = new Date(Math.floor(Date.now() / (5 * 60 * 1000)) * (5 * 60 * 1000)); // 5-min windows\n\tconst existing = rows.find((r) => r.windowStart.getTime() === currentWindow.getTime());\n\n\tif (existing) {\n\t\tawait db\n\t\t\t.update(rateLimits)\n\t\t\t.set({ count: existing.count + 1 })\n\t\t\t.where(eq(rateLimits.id, existing.id));\n\t} else {\n\t\tawait db.insert(rateLimits).values({\n\t\t\tid: randomUUID(),\n\t\t\tagentId,\n\t\t\tresource,\n\t\t\twindowStart: currentWindow,\n\t\t\tcount: 1,\n\t\t});\n\t}\n\n\treturn { allowed: true };\n}\n\n/**\n * Create the permission/authorization engine.\n */\nexport function createPermissionEngine(config: PermissionEngineConfig) {\n\tconst { db, auditAll } = config;\n\n\t/**\n\t * Check if an agent is authorized to perform an action.\n\t * This is the core authorization function.\n\t */\n\tasync function authorize(\n\t\tagent: AgentIdentity,\n\t\trequest: AuthorizeRequest,\n\t): Promise<AuthorizeResult> {\n\t\tconst startTime = performance.now();\n\t\tconst auditId = randomUUID();\n\n\t\t// Find matching permission\n\t\tconst matchingPermission = agent.permissions.find(\n\t\t\t(p) => matchResource(p.resource, request.resource) && matchAction(p.actions, request.action),\n\t\t);\n\n\t\tif (!matchingPermission) {\n\t\t\tconst result: AuthorizeResult = {\n\t\t\t\tallowed: false,\n\t\t\t\treason: `No permission grants agent \"${agent.name}\" access to \"${request.action}\" on \"${request.resource}\"`,\n\t\t\t\tauditId,\n\t\t\t};\n\t\t\tif (auditAll) {\n\t\t\t\tawait writeAuditLog(db, agent, request, result, startTime, auditId);\n\t\t\t}\n\t\t\treturn result;\n\t\t}\n\n\t\t// Check constraints\n\t\tif (matchingPermission.constraints) {\n\t\t\tconst constraintResult = await evaluateConstraints(\n\t\t\t\tdb,\n\t\t\t\tagent,\n\t\t\t\trequest,\n\t\t\t\tmatchingPermission.constraints,\n\t\t\t);\n\t\t\tif (!constraintResult.allowed) {\n\t\t\t\tconst result: AuthorizeResult = {\n\t\t\t\t\tallowed: false,\n\t\t\t\t\treason: constraintResult.reason,\n\t\t\t\t\tauditId,\n\t\t\t\t};\n\t\t\t\tif (auditAll) {\n\t\t\t\t\tawait writeAuditLog(db, agent, request, result, startTime, auditId);\n\t\t\t\t}\n\t\t\t\treturn result;\n\t\t\t}\n\t\t}\n\n\t\tconst result: AuthorizeResult = { allowed: true, auditId };\n\t\tif (auditAll) {\n\t\t\tawait writeAuditLog(db, agent, request, result, startTime, auditId);\n\t\t}\n\t\treturn result;\n\t}\n\n\treturn { authorize };\n}\n\nasync function evaluateConstraints(\n\tdb: Database,\n\tagent: AgentIdentity,\n\trequest: AuthorizeRequest,\n\tconstraints: PermissionConstraints,\n): Promise<{ allowed: boolean; reason?: string }> {\n\t// Rate limit check\n\tif (constraints.maxCallsPerHour) {\n\t\tconst rateResult = await checkRateLimit(\n\t\t\tdb,\n\t\t\tagent.id,\n\t\t\trequest.resource,\n\t\t\tconstraints.maxCallsPerHour,\n\t\t);\n\t\tif (!rateResult.allowed) {\n\t\t\treturn rateResult;\n\t\t}\n\t}\n\n\t// Argument pattern check\n\tif (constraints.allowedArgPatterns && request.arguments) {\n\t\tconst patternResult = validateArgPatterns(constraints.allowedArgPatterns, request.arguments);\n\t\tif (!patternResult.valid) {\n\t\t\treturn { allowed: false, reason: patternResult.reason };\n\t\t}\n\t}\n\n\t// Human-in-the-loop check\n\tif (constraints.requireApproval) {\n\t\treturn {\n\t\t\tallowed: false,\n\t\t\treason: \"This action requires human approval before execution\",\n\t\t};\n\t}\n\n\t// Time window check\n\tif (constraints.timeWindow) {\n\t\tconst now = new Date();\n\t\tconst hours = now.getHours();\n\t\tconst minutes = now.getMinutes();\n\t\tconst currentTime = `${String(hours).padStart(2, \"0\")}:${String(minutes).padStart(2, \"0\")}`;\n\n\t\tif (currentTime < constraints.timeWindow.start || currentTime > constraints.timeWindow.end) {\n\t\t\treturn {\n\t\t\t\tallowed: false,\n\t\t\t\treason: `Action is only allowed between ${constraints.timeWindow.start} and ${constraints.timeWindow.end}`,\n\t\t\t};\n\t\t}\n\t}\n\n\t// IP allowlist check\n\tif (constraints.ipAllowlist && constraints.ipAllowlist.length > 0) {\n\t\tif (!request.ip) {\n\t\t\treturn {\n\t\t\t\tallowed: false,\n\t\t\t\treason: \"IP_NOT_ALLOWED: No IP address provided; resource requires an IP allowlist match\",\n\t\t\t};\n\t\t}\n\t\tif (!isIPAllowed(constraints.ipAllowlist, request.ip)) {\n\t\t\treturn {\n\t\t\t\tallowed: false,\n\t\t\t\treason: `IP_NOT_ALLOWED: IP \"${request.ip}\" is not in the allowlist for this resource`,\n\t\t\t};\n\t\t}\n\t}\n\n\treturn { allowed: true };\n}\n\nasync function writeAuditLog(\n\tdb: Database,\n\tagent: AgentIdentity,\n\trequest: AuthorizeRequest,\n\tresult: AuthorizeResult,\n\tstartTime: number,\n\tauditId: string,\n): Promise<void> {\n\tconst durationMs = Math.round(performance.now() - startTime);\n\n\tawait db.insert(auditLogs).values({\n\t\tid: auditId,\n\t\tagentId: agent.id,\n\t\tuserId: agent.ownerId,\n\t\taction: request.action,\n\t\tresource: request.resource,\n\t\tparameters: request.arguments ?? {},\n\t\tresult: result.allowed ? \"allowed\" : \"denied\",\n\t\treason: result.reason ?? null,\n\t\tdurationMs,\n\t\ttimestamp: new Date(),\n\t\tip: request.context?.ip ?? null,\n\t\tuserAgent: request.context?.userAgent ?? null,\n\t});\n}\n","import type { Permission } from \"../types.js\";\n\n/**\n * Pre-built permission templates for common access patterns.\n * Use these as starting points when creating agents.\n */\nexport const permissionTemplates = {\n\t/** Read-only access to all resources */\n\treadonly: [{ resource: \"*\", actions: [\"read\"] }] satisfies Permission[],\n\n\t/** Read and write access to all resources */\n\treadwrite: [{ resource: \"*\", actions: [\"read\", \"write\"] }] satisfies Permission[],\n\n\t/** Full access to all resources and actions */\n\tadmin: [{ resource: \"*\", actions: [\"*\"] }] satisfies Permission[],\n\n\t/** Standard MCP tool access - read + execute */\n\tmcpBasic: [{ resource: \"mcp:*\", actions: [\"read\", \"execute\"] }] satisfies Permission[],\n\n\t/** MCP tool access with write - read + write + execute */\n\tmcpFull: [{ resource: \"mcp:*\", actions: [\"read\", \"write\", \"execute\"] }] satisfies Permission[],\n\n\t/** Rate-limited read access (100 calls/hour) */\n\trateLimitedRead: [\n\t\t{\n\t\t\tresource: \"*\",\n\t\t\tactions: [\"read\"],\n\t\t\tconstraints: { maxCallsPerHour: 100 },\n\t\t},\n\t] satisfies Permission[],\n\n\t/** Approval-required access (human-in-the-loop for everything) */\n\tapprovalRequired: [\n\t\t{\n\t\t\tresource: \"*\",\n\t\t\tactions: [\"*\"],\n\t\t\tconstraints: { requireApproval: true },\n\t\t},\n\t] satisfies Permission[],\n\n\t/** Business hours only access (9am-5pm) */\n\tbusinessHours: [\n\t\t{\n\t\t\tresource: \"*\",\n\t\t\tactions: [\"read\", \"write\", \"execute\"],\n\t\t\tconstraints: { timeWindow: { start: \"09:00\", end: \"17:00\" } },\n\t\t},\n\t] satisfies Permission[],\n} as const;\n\nexport type PermissionTemplateName = keyof typeof permissionTemplates;\n\n/**\n * Get a permission template by name.\n * Returns a fresh copy of the permissions array.\n */\nexport function getPermissionTemplate(name: PermissionTemplateName): Permission[] {\n\treturn JSON.parse(JSON.stringify(permissionTemplates[name])) as Permission[];\n}\n"]}
@@ -1,4 +1,4 @@
1
- import { agents, permissions } from './chunk-XSYYQH75.js';
1
+ import { agents, permissions } from './chunk-UEE7OYLG.js';
2
2
  import { randomUUID, createHash, randomBytes } from 'crypto';
3
3
  import { and, eq } from 'drizzle-orm';
4
4
 
@@ -204,5 +204,5 @@ function toPermission(row) {
204
204
  }
205
205
 
206
206
  export { createAgentModule };
207
- //# sourceMappingURL=chunk-DTCKF26N.js.map
208
- //# sourceMappingURL=chunk-DTCKF26N.js.map
207
+ //# sourceMappingURL=chunk-I4J4KKKK.js.map
208
+ //# sourceMappingURL=chunk-I4J4KKKK.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/agent/agent.ts"],"names":[],"mappings":"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","file":"chunk-DTCKF26N.js","sourcesContent":["import { createHash, randomBytes, randomUUID } from \"node:crypto\";\nimport { and, eq } from \"drizzle-orm\";\nimport type { Database } from \"../db/database.js\";\nimport { agents, permissions } from \"../db/schema.js\";\nimport type {\n\tAgentFilter,\n\tAgentIdentity,\n\tCreateAgentInput,\n\tPermission,\n\tUpdateAgentInput,\n} from \"../types.js\";\n\ninterface AgentModuleConfig {\n\tdb: Database;\n\tmaxPerUser: number;\n\tdefaultPermissions: string[];\n\ttokenExpiry: string;\n}\n\n/**\n * Generate a secure agent token.\n * Returns { token, hash, prefix } where:\n * - token: the full token (given to the agent, never stored)\n * - hash: SHA-256 hash (stored in DB)\n * - prefix: first 8 chars (for identification in logs/UI)\n */\nfunction generateAgentToken(): { token: string; hash: string; prefix: string } {\n\tconst tokenBytes = randomBytes(32);\n\tconst token = `kv_${tokenBytes.toString(\"base64url\")}`;\n\tconst hash = createHash(\"sha256\").update(token).digest(\"hex\");\n\tconst prefix = token.slice(0, 11); // \"kv_\" + 8 chars\n\treturn { token, hash, prefix };\n}\n\nfunction parseTokenExpiry(expiry: string): Date {\n\tconst now = Date.now();\n\tconst match = expiry.match(/^(\\d+)([smhd])$/);\n\tif (!match) {\n\t\tthrow new Error(`Invalid token expiry format: ${expiry}. Use format like \"24h\", \"7d\", \"30m\".`);\n\t}\n\tconst value = Number.parseInt(match[1] as string, 10);\n\tconst unit = match[2];\n\tconst multipliers: Record<string, number> = {\n\t\ts: 1000,\n\t\tm: 60 * 1000,\n\t\th: 60 * 60 * 1000,\n\t\td: 24 * 60 * 60 * 1000,\n\t};\n\treturn new Date(now + value * (multipliers[unit as string] ?? 0));\n}\n\n/**\n * Create the agent identity module.\n * Handles CRUD operations for AI agent identities.\n */\nexport function createAgentModule(config: AgentModuleConfig) {\n\tconst { db, maxPerUser, tokenExpiry } = config;\n\n\tasync function create(input: CreateAgentInput): Promise<AgentIdentity & { token: string }> {\n\t\t// Check max agents per user\n\t\tconst existing = await db\n\t\t\t.select()\n\t\t\t.from(agents)\n\t\t\t.where(and(eq(agents.ownerId, input.ownerId), eq(agents.status, \"active\")));\n\n\t\tif (existing.length >= maxPerUser) {\n\t\t\tthrow new Error(\n\t\t\t\t`User ${input.ownerId} has reached the maximum of ${maxPerUser} active agents.`,\n\t\t\t);\n\t\t}\n\n\t\tconst id = randomUUID();\n\t\tconst { token, hash, prefix } = generateAgentToken();\n\t\tconst now = new Date();\n\t\tconst expires = input.expiresAt ?? parseTokenExpiry(tokenExpiry);\n\n\t\t// Insert agent\n\t\tawait db.insert(agents).values({\n\t\t\tid,\n\t\t\townerId: input.ownerId,\n\t\t\tname: input.name,\n\t\t\ttype: input.type,\n\t\t\tstatus: \"active\",\n\t\t\ttokenHash: hash,\n\t\t\ttokenPrefix: prefix,\n\t\t\texpiresAt: expires,\n\t\t\tmetadata: input.metadata ?? {},\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t});\n\n\t\t// Insert permissions\n\t\tif (input.permissions.length > 0) {\n\t\t\tawait db.insert(permissions).values(\n\t\t\t\tinput.permissions.map((p) => ({\n\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\tagentId: id,\n\t\t\t\t\tresource: p.resource,\n\t\t\t\t\tactions: p.actions,\n\t\t\t\t\tconstraints: p.constraints ?? null,\n\t\t\t\t\tcreatedAt: now,\n\t\t\t\t})),\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tid,\n\t\t\townerId: input.ownerId,\n\t\t\tname: input.name,\n\t\t\ttype: input.type,\n\t\t\ttoken,\n\t\t\tpermissions: input.permissions,\n\t\t\tstatus: \"active\",\n\t\t\texpiresAt: expires,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t};\n\t}\n\n\tasync function get(agentId: string): Promise<AgentIdentity | null> {\n\t\tconst rows = await db.select().from(agents).where(eq(agents.id, agentId)).limit(1);\n\t\tconst agent = rows[0];\n\t\tif (!agent) return null;\n\n\t\tconst perms = await db.select().from(permissions).where(eq(permissions.agentId, agentId));\n\n\t\treturn {\n\t\t\tid: agent.id,\n\t\t\townerId: agent.ownerId,\n\t\t\tname: agent.name,\n\t\t\ttype: agent.type as AgentIdentity[\"type\"],\n\t\t\ttoken: \"\", // never return token after creation\n\t\t\tpermissions: perms.map(toPermission),\n\t\t\tstatus: agent.status as AgentIdentity[\"status\"],\n\t\t\texpiresAt: agent.expiresAt,\n\t\t\tcreatedAt: agent.createdAt,\n\t\t\tupdatedAt: agent.updatedAt,\n\t\t};\n\t}\n\n\tasync function list(filter?: AgentFilter): Promise<AgentIdentity[]> {\n\t\tlet query = db.select().from(agents).$dynamic();\n\n\t\tconst conditions = [];\n\t\tif (filter?.userId) conditions.push(eq(agents.ownerId, filter.userId));\n\t\tif (filter?.status) conditions.push(eq(agents.status, filter.status));\n\t\tif (filter?.type) conditions.push(eq(agents.type, filter.type));\n\n\t\tif (conditions.length > 0) {\n\t\t\tquery = query.where(and(...conditions));\n\t\t}\n\n\t\tconst rows = await query;\n\n\t\t// Load permissions for all agents\n\t\tconst agentIds = rows.map((r) => r.id);\n\t\tconst permsByAgent = new Map<string, Permission[]>();\n\t\tfor (const id of agentIds) {\n\t\t\tconst perms = await db.select().from(permissions).where(eq(permissions.agentId, id));\n\t\t\tpermsByAgent.set(id, perms.map(toPermission));\n\t\t}\n\n\t\treturn rows.map((agent) => ({\n\t\t\tid: agent.id,\n\t\t\townerId: agent.ownerId,\n\t\t\tname: agent.name,\n\t\t\ttype: agent.type as AgentIdentity[\"type\"],\n\t\t\ttoken: \"\",\n\t\t\tpermissions: permsByAgent.get(agent.id) ?? [],\n\t\t\tstatus: agent.status as AgentIdentity[\"status\"],\n\t\t\texpiresAt: agent.expiresAt,\n\t\t\tcreatedAt: agent.createdAt,\n\t\t\tupdatedAt: agent.updatedAt,\n\t\t}));\n\t}\n\n\tasync function update(agentId: string, input: UpdateAgentInput): Promise<AgentIdentity> {\n\t\tconst existing = await get(agentId);\n\t\tif (!existing) throw new Error(`Agent ${agentId} not found.`);\n\n\t\tconst now = new Date();\n\n\t\tawait db\n\t\t\t.update(agents)\n\t\t\t.set({\n\t\t\t\tname: input.name ?? existing.name,\n\t\t\t\texpiresAt: input.expiresAt ?? existing.expiresAt,\n\t\t\t\tmetadata: input.metadata,\n\t\t\t\tupdatedAt: now,\n\t\t\t})\n\t\t\t.where(eq(agents.id, agentId));\n\n\t\t// Replace permissions if provided\n\t\tif (input.permissions) {\n\t\t\tawait db.delete(permissions).where(eq(permissions.agentId, agentId));\n\t\t\tif (input.permissions.length > 0) {\n\t\t\t\tawait db.insert(permissions).values(\n\t\t\t\t\tinput.permissions.map((p) => ({\n\t\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\t\tagentId,\n\t\t\t\t\t\tresource: p.resource,\n\t\t\t\t\t\tactions: p.actions,\n\t\t\t\t\t\tconstraints: p.constraints ?? null,\n\t\t\t\t\t\tcreatedAt: now,\n\t\t\t\t\t})),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst updated = await get(agentId);\n\t\tif (!updated) throw new Error(`Agent ${agentId} disappeared after update.`);\n\t\treturn updated;\n\t}\n\n\tasync function revoke(agentId: string): Promise<void> {\n\t\tconst existing = await get(agentId);\n\t\tif (!existing) throw new Error(`Agent ${agentId} not found.`);\n\n\t\tawait db\n\t\t\t.update(agents)\n\t\t\t.set({ status: \"revoked\", updatedAt: new Date() })\n\t\t\t.where(eq(agents.id, agentId));\n\t}\n\n\tasync function rotate(agentId: string): Promise<AgentIdentity & { token: string }> {\n\t\tconst existing = await get(agentId);\n\t\tif (!existing) throw new Error(`Agent ${agentId} not found.`);\n\t\tif (existing.status !== \"active\")\n\t\t\tthrow new Error(`Cannot rotate token for ${existing.status} agent.`);\n\n\t\tconst { token, hash, prefix } = generateAgentToken();\n\t\tconst now = new Date();\n\n\t\tawait db\n\t\t\t.update(agents)\n\t\t\t.set({ tokenHash: hash, tokenPrefix: prefix, updatedAt: now })\n\t\t\t.where(eq(agents.id, agentId));\n\n\t\treturn { ...existing, token, updatedAt: now };\n\t}\n\n\t/**\n\t * Validate an agent token and return the agent identity.\n\t * Used internally by the authorization engine.\n\t */\n\tasync function validateToken(token: string): Promise<AgentIdentity | null> {\n\t\tconst hash = createHash(\"sha256\").update(token).digest(\"hex\");\n\t\tconst rows = await db.select().from(agents).where(eq(agents.tokenHash, hash)).limit(1);\n\t\tconst agent = rows[0];\n\t\tif (!agent) return null;\n\n\t\t// Check status\n\t\tif (agent.status !== \"active\") return null;\n\n\t\t// Check expiry\n\t\tif (agent.expiresAt && agent.expiresAt < new Date()) {\n\t\t\tawait db\n\t\t\t\t.update(agents)\n\t\t\t\t.set({ status: \"expired\", updatedAt: new Date() })\n\t\t\t\t.where(eq(agents.id, agent.id));\n\t\t\treturn null;\n\t\t}\n\n\t\t// Update last active\n\t\tawait db.update(agents).set({ lastActiveAt: new Date() }).where(eq(agents.id, agent.id));\n\n\t\tconst perms = await db.select().from(permissions).where(eq(permissions.agentId, agent.id));\n\n\t\treturn {\n\t\t\tid: agent.id,\n\t\t\townerId: agent.ownerId,\n\t\t\tname: agent.name,\n\t\t\ttype: agent.type as AgentIdentity[\"type\"],\n\t\t\ttoken: \"\",\n\t\t\tpermissions: perms.map(toPermission),\n\t\t\tstatus: \"active\",\n\t\t\texpiresAt: agent.expiresAt,\n\t\t\tcreatedAt: agent.createdAt,\n\t\t\tupdatedAt: agent.updatedAt,\n\t\t};\n\t}\n\n\treturn { create, get, list, update, revoke, rotate, validateToken };\n}\n\nfunction toPermission(row: {\n\tresource: string;\n\tactions: string[];\n\tconstraints: unknown;\n}): Permission {\n\treturn {\n\t\tresource: row.resource,\n\t\tactions: row.actions,\n\t\tconstraints: (row.constraints as Permission[\"constraints\"]) ?? undefined,\n\t};\n}\n"]}
1
+ {"version":3,"sources":["../src/agent/agent.ts"],"names":[],"mappings":"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","file":"chunk-I4J4KKKK.js","sourcesContent":["import { createHash, randomBytes, randomUUID } from \"node:crypto\";\nimport { and, eq } from \"drizzle-orm\";\nimport type { Database } from \"../db/database.js\";\nimport { agents, permissions } from \"../db/schema.js\";\nimport type {\n\tAgentFilter,\n\tAgentIdentity,\n\tCreateAgentInput,\n\tPermission,\n\tUpdateAgentInput,\n} from \"../types.js\";\n\ninterface AgentModuleConfig {\n\tdb: Database;\n\tmaxPerUser: number;\n\tdefaultPermissions: string[];\n\ttokenExpiry: string;\n}\n\n/**\n * Generate a secure agent token.\n * Returns { token, hash, prefix } where:\n * - token: the full token (given to the agent, never stored)\n * - hash: SHA-256 hash (stored in DB)\n * - prefix: first 8 chars (for identification in logs/UI)\n */\nfunction generateAgentToken(): { token: string; hash: string; prefix: string } {\n\tconst tokenBytes = randomBytes(32);\n\tconst token = `kv_${tokenBytes.toString(\"base64url\")}`;\n\tconst hash = createHash(\"sha256\").update(token).digest(\"hex\");\n\tconst prefix = token.slice(0, 11); // \"kv_\" + 8 chars\n\treturn { token, hash, prefix };\n}\n\nfunction parseTokenExpiry(expiry: string): Date {\n\tconst now = Date.now();\n\tconst match = expiry.match(/^(\\d+)([smhd])$/);\n\tif (!match) {\n\t\tthrow new Error(`Invalid token expiry format: ${expiry}. Use format like \"24h\", \"7d\", \"30m\".`);\n\t}\n\tconst value = Number.parseInt(match[1] as string, 10);\n\tconst unit = match[2];\n\tconst multipliers: Record<string, number> = {\n\t\ts: 1000,\n\t\tm: 60 * 1000,\n\t\th: 60 * 60 * 1000,\n\t\td: 24 * 60 * 60 * 1000,\n\t};\n\treturn new Date(now + value * (multipliers[unit as string] ?? 0));\n}\n\n/**\n * Create the agent identity module.\n * Handles CRUD operations for AI agent identities.\n */\nexport function createAgentModule(config: AgentModuleConfig) {\n\tconst { db, maxPerUser, tokenExpiry } = config;\n\n\tasync function create(input: CreateAgentInput): Promise<AgentIdentity & { token: string }> {\n\t\t// Check max agents per user\n\t\tconst existing = await db\n\t\t\t.select()\n\t\t\t.from(agents)\n\t\t\t.where(and(eq(agents.ownerId, input.ownerId), eq(agents.status, \"active\")));\n\n\t\tif (existing.length >= maxPerUser) {\n\t\t\tthrow new Error(\n\t\t\t\t`User ${input.ownerId} has reached the maximum of ${maxPerUser} active agents.`,\n\t\t\t);\n\t\t}\n\n\t\tconst id = randomUUID();\n\t\tconst { token, hash, prefix } = generateAgentToken();\n\t\tconst now = new Date();\n\t\tconst expires = input.expiresAt ?? parseTokenExpiry(tokenExpiry);\n\n\t\t// Insert agent\n\t\tawait db.insert(agents).values({\n\t\t\tid,\n\t\t\townerId: input.ownerId,\n\t\t\tname: input.name,\n\t\t\ttype: input.type,\n\t\t\tstatus: \"active\",\n\t\t\ttokenHash: hash,\n\t\t\ttokenPrefix: prefix,\n\t\t\texpiresAt: expires,\n\t\t\tmetadata: input.metadata ?? {},\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t});\n\n\t\t// Insert permissions\n\t\tif (input.permissions.length > 0) {\n\t\t\tawait db.insert(permissions).values(\n\t\t\t\tinput.permissions.map((p) => ({\n\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\tagentId: id,\n\t\t\t\t\tresource: p.resource,\n\t\t\t\t\tactions: p.actions,\n\t\t\t\t\tconstraints: p.constraints ?? null,\n\t\t\t\t\tcreatedAt: now,\n\t\t\t\t})),\n\t\t\t);\n\t\t}\n\n\t\treturn {\n\t\t\tid,\n\t\t\townerId: input.ownerId,\n\t\t\tname: input.name,\n\t\t\ttype: input.type,\n\t\t\ttoken,\n\t\t\tpermissions: input.permissions,\n\t\t\tstatus: \"active\",\n\t\t\texpiresAt: expires,\n\t\t\tcreatedAt: now,\n\t\t\tupdatedAt: now,\n\t\t};\n\t}\n\n\tasync function get(agentId: string): Promise<AgentIdentity | null> {\n\t\tconst rows = await db.select().from(agents).where(eq(agents.id, agentId)).limit(1);\n\t\tconst agent = rows[0];\n\t\tif (!agent) return null;\n\n\t\tconst perms = await db.select().from(permissions).where(eq(permissions.agentId, agentId));\n\n\t\treturn {\n\t\t\tid: agent.id,\n\t\t\townerId: agent.ownerId,\n\t\t\tname: agent.name,\n\t\t\ttype: agent.type as AgentIdentity[\"type\"],\n\t\t\ttoken: \"\", // never return token after creation\n\t\t\tpermissions: perms.map(toPermission),\n\t\t\tstatus: agent.status as AgentIdentity[\"status\"],\n\t\t\texpiresAt: agent.expiresAt,\n\t\t\tcreatedAt: agent.createdAt,\n\t\t\tupdatedAt: agent.updatedAt,\n\t\t};\n\t}\n\n\tasync function list(filter?: AgentFilter): Promise<AgentIdentity[]> {\n\t\tlet query = db.select().from(agents).$dynamic();\n\n\t\tconst conditions = [];\n\t\tif (filter?.userId) conditions.push(eq(agents.ownerId, filter.userId));\n\t\tif (filter?.status) conditions.push(eq(agents.status, filter.status));\n\t\tif (filter?.type) conditions.push(eq(agents.type, filter.type));\n\n\t\tif (conditions.length > 0) {\n\t\t\tquery = query.where(and(...conditions));\n\t\t}\n\n\t\tconst rows = await query;\n\n\t\t// Load permissions for all agents\n\t\tconst agentIds = rows.map((r) => r.id);\n\t\tconst permsByAgent = new Map<string, Permission[]>();\n\t\tfor (const id of agentIds) {\n\t\t\tconst perms = await db.select().from(permissions).where(eq(permissions.agentId, id));\n\t\t\tpermsByAgent.set(id, perms.map(toPermission));\n\t\t}\n\n\t\treturn rows.map((agent) => ({\n\t\t\tid: agent.id,\n\t\t\townerId: agent.ownerId,\n\t\t\tname: agent.name,\n\t\t\ttype: agent.type as AgentIdentity[\"type\"],\n\t\t\ttoken: \"\",\n\t\t\tpermissions: permsByAgent.get(agent.id) ?? [],\n\t\t\tstatus: agent.status as AgentIdentity[\"status\"],\n\t\t\texpiresAt: agent.expiresAt,\n\t\t\tcreatedAt: agent.createdAt,\n\t\t\tupdatedAt: agent.updatedAt,\n\t\t}));\n\t}\n\n\tasync function update(agentId: string, input: UpdateAgentInput): Promise<AgentIdentity> {\n\t\tconst existing = await get(agentId);\n\t\tif (!existing) throw new Error(`Agent ${agentId} not found.`);\n\n\t\tconst now = new Date();\n\n\t\tawait db\n\t\t\t.update(agents)\n\t\t\t.set({\n\t\t\t\tname: input.name ?? existing.name,\n\t\t\t\texpiresAt: input.expiresAt ?? existing.expiresAt,\n\t\t\t\tmetadata: input.metadata,\n\t\t\t\tupdatedAt: now,\n\t\t\t})\n\t\t\t.where(eq(agents.id, agentId));\n\n\t\t// Replace permissions if provided\n\t\tif (input.permissions) {\n\t\t\tawait db.delete(permissions).where(eq(permissions.agentId, agentId));\n\t\t\tif (input.permissions.length > 0) {\n\t\t\t\tawait db.insert(permissions).values(\n\t\t\t\t\tinput.permissions.map((p) => ({\n\t\t\t\t\t\tid: randomUUID(),\n\t\t\t\t\t\tagentId,\n\t\t\t\t\t\tresource: p.resource,\n\t\t\t\t\t\tactions: p.actions,\n\t\t\t\t\t\tconstraints: p.constraints ?? null,\n\t\t\t\t\t\tcreatedAt: now,\n\t\t\t\t\t})),\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\tconst updated = await get(agentId);\n\t\tif (!updated) throw new Error(`Agent ${agentId} disappeared after update.`);\n\t\treturn updated;\n\t}\n\n\tasync function revoke(agentId: string): Promise<void> {\n\t\tconst existing = await get(agentId);\n\t\tif (!existing) throw new Error(`Agent ${agentId} not found.`);\n\n\t\tawait db\n\t\t\t.update(agents)\n\t\t\t.set({ status: \"revoked\", updatedAt: new Date() })\n\t\t\t.where(eq(agents.id, agentId));\n\t}\n\n\tasync function rotate(agentId: string): Promise<AgentIdentity & { token: string }> {\n\t\tconst existing = await get(agentId);\n\t\tif (!existing) throw new Error(`Agent ${agentId} not found.`);\n\t\tif (existing.status !== \"active\")\n\t\t\tthrow new Error(`Cannot rotate token for ${existing.status} agent.`);\n\n\t\tconst { token, hash, prefix } = generateAgentToken();\n\t\tconst now = new Date();\n\n\t\tawait db\n\t\t\t.update(agents)\n\t\t\t.set({ tokenHash: hash, tokenPrefix: prefix, updatedAt: now })\n\t\t\t.where(eq(agents.id, agentId));\n\n\t\treturn { ...existing, token, updatedAt: now };\n\t}\n\n\t/**\n\t * Validate an agent token and return the agent identity.\n\t * Used internally by the authorization engine.\n\t */\n\tasync function validateToken(token: string): Promise<AgentIdentity | null> {\n\t\tconst hash = createHash(\"sha256\").update(token).digest(\"hex\");\n\t\tconst rows = await db.select().from(agents).where(eq(agents.tokenHash, hash)).limit(1);\n\t\tconst agent = rows[0];\n\t\tif (!agent) return null;\n\n\t\t// Check status\n\t\tif (agent.status !== \"active\") return null;\n\n\t\t// Check expiry\n\t\tif (agent.expiresAt && agent.expiresAt < new Date()) {\n\t\t\tawait db\n\t\t\t\t.update(agents)\n\t\t\t\t.set({ status: \"expired\", updatedAt: new Date() })\n\t\t\t\t.where(eq(agents.id, agent.id));\n\t\t\treturn null;\n\t\t}\n\n\t\t// Update last active\n\t\tawait db.update(agents).set({ lastActiveAt: new Date() }).where(eq(agents.id, agent.id));\n\n\t\tconst perms = await db.select().from(permissions).where(eq(permissions.agentId, agent.id));\n\n\t\treturn {\n\t\t\tid: agent.id,\n\t\t\townerId: agent.ownerId,\n\t\t\tname: agent.name,\n\t\t\ttype: agent.type as AgentIdentity[\"type\"],\n\t\t\ttoken: \"\",\n\t\t\tpermissions: perms.map(toPermission),\n\t\t\tstatus: \"active\",\n\t\t\texpiresAt: agent.expiresAt,\n\t\t\tcreatedAt: agent.createdAt,\n\t\t\tupdatedAt: agent.updatedAt,\n\t\t};\n\t}\n\n\treturn { create, get, list, update, revoke, rotate, validateToken };\n}\n\nfunction toPermission(row: {\n\tresource: string;\n\tactions: string[];\n\tconstraints: unknown;\n}): Permission {\n\treturn {\n\t\tresource: row.resource,\n\t\tactions: row.actions,\n\t\tconstraints: (row.constraints as Permission[\"constraints\"]) ?? undefined,\n\t};\n}\n"]}