@exulu/backend 1.48.2 → 1.49.0

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.
Files changed (164) hide show
  1. package/dist/index.cjs +351 -42
  2. package/dist/index.d.cts +96 -1
  3. package/dist/index.d.ts +96 -1
  4. package/dist/index.js +340 -38
  5. package/ee/{markdown.ts → chunking/markdown.ts} +2 -2
  6. package/ee/python/README.md +295 -0
  7. package/ee/python/documents/processing/README.md +155 -0
  8. package/ee/{documents → python/documents}/processing/doc_processor.ts +25 -17
  9. package/ee/{documents/processing/pdf_to_markdown.py → python/documents/processing/document_to_markdown.py} +3 -10
  10. package/ee/python/setup.sh +180 -0
  11. package/package.json +14 -3
  12. package/scripts/postinstall.cjs +149 -0
  13. package/.agents/skills/mintlify/SKILL.md +0 -347
  14. package/.editorconfig +0 -15
  15. package/.eslintrc.json +0 -52
  16. package/.github/workflows/release-backend.yml +0 -38
  17. package/.husky/commit-msg +0 -1
  18. package/.jscpd.json +0 -18
  19. package/.mcp.json +0 -25
  20. package/.nvmrc +0 -1
  21. package/.prettierignore +0 -5
  22. package/.prettierrc.json +0 -12
  23. package/CHANGELOG.md +0 -8
  24. package/SECURITY.md +0 -5
  25. package/commitlint.config.js +0 -4
  26. package/devops/documentation/patch-older-releases.md +0 -42
  27. package/ee/documents/processing/build_pdf_processor.sh +0 -35
  28. package/ee/documents/processing/chunk_markdown.py +0 -263
  29. package/ee/documents/processing/pdf_processor.spec +0 -115
  30. package/eslint.config.js +0 -88
  31. package/jest.config.ts +0 -25
  32. package/mintlify-docs/.mintignore +0 -7
  33. package/mintlify-docs/AGENTS.md +0 -33
  34. package/mintlify-docs/CLAUDE.MD +0 -50
  35. package/mintlify-docs/CONTRIBUTING.md +0 -32
  36. package/mintlify-docs/LICENSE +0 -21
  37. package/mintlify-docs/README.md +0 -55
  38. package/mintlify-docs/ai-tools/claude-code.mdx +0 -43
  39. package/mintlify-docs/ai-tools/cursor.mdx +0 -39
  40. package/mintlify-docs/ai-tools/windsurf.mdx +0 -39
  41. package/mintlify-docs/api-reference/core-types/agent-types.mdx +0 -110
  42. package/mintlify-docs/api-reference/core-types/analytics-types.mdx +0 -95
  43. package/mintlify-docs/api-reference/core-types/configuration-types.mdx +0 -83
  44. package/mintlify-docs/api-reference/core-types/evaluation-types.mdx +0 -106
  45. package/mintlify-docs/api-reference/core-types/job-types.mdx +0 -135
  46. package/mintlify-docs/api-reference/core-types/overview.mdx +0 -73
  47. package/mintlify-docs/api-reference/core-types/prompt-types.mdx +0 -102
  48. package/mintlify-docs/api-reference/core-types/rbac-types.mdx +0 -163
  49. package/mintlify-docs/api-reference/core-types/session-types.mdx +0 -77
  50. package/mintlify-docs/api-reference/core-types/user-management.mdx +0 -112
  51. package/mintlify-docs/api-reference/core-types/workflow-types.mdx +0 -88
  52. package/mintlify-docs/api-reference/core-types.mdx +0 -585
  53. package/mintlify-docs/api-reference/dynamic-types.mdx +0 -851
  54. package/mintlify-docs/api-reference/endpoint/create.mdx +0 -4
  55. package/mintlify-docs/api-reference/endpoint/delete.mdx +0 -4
  56. package/mintlify-docs/api-reference/endpoint/get.mdx +0 -4
  57. package/mintlify-docs/api-reference/endpoint/webhook.mdx +0 -4
  58. package/mintlify-docs/api-reference/introduction.mdx +0 -661
  59. package/mintlify-docs/api-reference/mutations.mdx +0 -1012
  60. package/mintlify-docs/api-reference/openapi.json +0 -217
  61. package/mintlify-docs/api-reference/queries.mdx +0 -1154
  62. package/mintlify-docs/backend/introduction.mdx +0 -218
  63. package/mintlify-docs/changelog.mdx +0 -387
  64. package/mintlify-docs/community-edition.mdx +0 -304
  65. package/mintlify-docs/core/exulu-agent/api-reference.mdx +0 -894
  66. package/mintlify-docs/core/exulu-agent/configuration.mdx +0 -690
  67. package/mintlify-docs/core/exulu-agent/introduction.mdx +0 -552
  68. package/mintlify-docs/core/exulu-app/api-reference.mdx +0 -481
  69. package/mintlify-docs/core/exulu-app/configuration.mdx +0 -319
  70. package/mintlify-docs/core/exulu-app/introduction.mdx +0 -117
  71. package/mintlify-docs/core/exulu-authentication.mdx +0 -810
  72. package/mintlify-docs/core/exulu-chunkers/api-reference.mdx +0 -1011
  73. package/mintlify-docs/core/exulu-chunkers/configuration.mdx +0 -596
  74. package/mintlify-docs/core/exulu-chunkers/introduction.mdx +0 -403
  75. package/mintlify-docs/core/exulu-context/api-reference.mdx +0 -911
  76. package/mintlify-docs/core/exulu-context/configuration.mdx +0 -648
  77. package/mintlify-docs/core/exulu-context/introduction.mdx +0 -394
  78. package/mintlify-docs/core/exulu-database.mdx +0 -811
  79. package/mintlify-docs/core/exulu-default-agents.mdx +0 -545
  80. package/mintlify-docs/core/exulu-eval/api-reference.mdx +0 -772
  81. package/mintlify-docs/core/exulu-eval/configuration.mdx +0 -680
  82. package/mintlify-docs/core/exulu-eval/introduction.mdx +0 -459
  83. package/mintlify-docs/core/exulu-logging.mdx +0 -464
  84. package/mintlify-docs/core/exulu-otel.mdx +0 -670
  85. package/mintlify-docs/core/exulu-queues/api-reference.mdx +0 -648
  86. package/mintlify-docs/core/exulu-queues/configuration.mdx +0 -650
  87. package/mintlify-docs/core/exulu-queues/introduction.mdx +0 -474
  88. package/mintlify-docs/core/exulu-reranker/api-reference.mdx +0 -630
  89. package/mintlify-docs/core/exulu-reranker/configuration.mdx +0 -663
  90. package/mintlify-docs/core/exulu-reranker/introduction.mdx +0 -516
  91. package/mintlify-docs/core/exulu-tool/api-reference.mdx +0 -723
  92. package/mintlify-docs/core/exulu-tool/configuration.mdx +0 -805
  93. package/mintlify-docs/core/exulu-tool/introduction.mdx +0 -539
  94. package/mintlify-docs/core/exulu-variables/api-reference.mdx +0 -699
  95. package/mintlify-docs/core/exulu-variables/configuration.mdx +0 -736
  96. package/mintlify-docs/core/exulu-variables/introduction.mdx +0 -511
  97. package/mintlify-docs/development.mdx +0 -94
  98. package/mintlify-docs/docs.json +0 -248
  99. package/mintlify-docs/enterprise-edition.mdx +0 -538
  100. package/mintlify-docs/essentials/code.mdx +0 -35
  101. package/mintlify-docs/essentials/images.mdx +0 -59
  102. package/mintlify-docs/essentials/markdown.mdx +0 -88
  103. package/mintlify-docs/essentials/navigation.mdx +0 -87
  104. package/mintlify-docs/essentials/reusable-snippets.mdx +0 -110
  105. package/mintlify-docs/essentials/settings.mdx +0 -318
  106. package/mintlify-docs/favicon.svg +0 -3
  107. package/mintlify-docs/frontend/introduction.mdx +0 -39
  108. package/mintlify-docs/getting-started.mdx +0 -267
  109. package/mintlify-docs/guides/custom-agent.mdx +0 -608
  110. package/mintlify-docs/guides/first-agent.mdx +0 -315
  111. package/mintlify-docs/images/admin_ui.png +0 -0
  112. package/mintlify-docs/images/contexts.png +0 -0
  113. package/mintlify-docs/images/create_agents.png +0 -0
  114. package/mintlify-docs/images/evals.png +0 -0
  115. package/mintlify-docs/images/graphql.png +0 -0
  116. package/mintlify-docs/images/graphql_api.png +0 -0
  117. package/mintlify-docs/images/hero-dark.png +0 -0
  118. package/mintlify-docs/images/hero-light.png +0 -0
  119. package/mintlify-docs/images/hero.png +0 -0
  120. package/mintlify-docs/images/knowledge_sources.png +0 -0
  121. package/mintlify-docs/images/mcp.png +0 -0
  122. package/mintlify-docs/images/scaling.png +0 -0
  123. package/mintlify-docs/index.mdx +0 -411
  124. package/mintlify-docs/logo/dark.svg +0 -9
  125. package/mintlify-docs/logo/light.svg +0 -9
  126. package/mintlify-docs/partners.mdx +0 -558
  127. package/mintlify-docs/products.mdx +0 -77
  128. package/mintlify-docs/snippets/snippet-intro.mdx +0 -4
  129. package/mintlify-docs/styles.css +0 -207
  130. package/ngrok.bash +0 -1
  131. package/ngrok.md +0 -6
  132. package/ngrok.yml +0 -10
  133. package/release.config.cjs +0 -15
  134. package/skills-lock.json +0 -10
  135. package/types/context-processor.ts +0 -45
  136. package/types/enums/eval-types.ts +0 -5
  137. package/types/enums/field-types.ts +0 -1
  138. package/types/enums/jobs.ts +0 -11
  139. package/types/enums/statistics.ts +0 -13
  140. package/types/exulu-table-definition.ts +0 -79
  141. package/types/file-types.ts +0 -18
  142. package/types/models/agent-session.ts +0 -27
  143. package/types/models/agent.ts +0 -68
  144. package/types/models/context.ts +0 -53
  145. package/types/models/embedding.ts +0 -17
  146. package/types/models/eval-run.ts +0 -40
  147. package/types/models/exulu-agent-tool-config.ts +0 -11
  148. package/types/models/item.ts +0 -21
  149. package/types/models/job.ts +0 -8
  150. package/types/models/project.ts +0 -16
  151. package/types/models/rate-limiter-rules.ts +0 -7
  152. package/types/models/test-case.ts +0 -25
  153. package/types/models/tool.ts +0 -9
  154. package/types/models/user-role.ts +0 -12
  155. package/types/models/user.ts +0 -20
  156. package/types/models/variable.ts +0 -8
  157. package/types/models/vector-methods.ts +0 -7
  158. package/types/provider-config.ts +0 -21
  159. package/types/queue-config.ts +0 -16
  160. package/types/rbac-rights-modes.ts +0 -1
  161. package/types/statistics.ts +0 -20
  162. package/types/workflow.ts +0 -31
  163. /package/ee/{documents → python/documents}/THIRD_PARTY_LICENSES/docling.txt +0 -0
  164. /package/ee/{documents/processing → python}/requirements.txt +0 -0
@@ -1,810 +0,0 @@
1
- ---
2
- title: "ExuluAuthentication"
3
- description: "Authentication function for API keys, auth tokens, and internal service communication"
4
- ---
5
-
6
- ## Overview
7
-
8
- `ExuluAuthentication` is an authentication function that verifies users through three methods: API keys, session tokens (NextAuth/JWT), or internal service keys. It returns authenticated user information with role-based permissions.
9
-
10
- ## Authentication methods
11
-
12
- <CardGroup cols={3}>
13
- <Card title="API Keys" icon="key">
14
- For programmatic access to Exulu APIs
15
- </Card>
16
- <Card title="Session Tokens" icon="shield">
17
- For web application authentication via NextAuth
18
- </Card>
19
- <Card title="Internal Keys" icon="lock">
20
- For secure service-to-service communication
21
- </Card>
22
- </CardGroup>
23
-
24
- ## What is ExuluAuthentication?
25
-
26
- ExuluAuthentication provides a unified authentication interface that:
27
-
28
- - **Validates API keys**: Securely compares bcrypt-hashed API keys
29
- - **Verifies session tokens**: Validates NextAuth JWT tokens from web applications
30
- - **Authorizes internal services**: Allows trusted service-to-service communication
31
- - **Retrieves user data**: Returns user information with role permissions
32
- - **Updates usage tracking**: Records last API key usage timestamp
33
-
34
- ## Quick start
35
-
36
- ```typescript
37
- import { ExuluAuthentication, postgresClient } from "@exulu/backend";
38
-
39
- const { db } = await postgresClient();
40
-
41
- // Authenticate with API key
42
- const result = await ExuluAuthentication.authenticate({
43
- apikey: "exl_abc123.../my-key-name",
44
- db
45
- });
46
-
47
- if (result.error) {
48
- console.error(`Auth failed: ${result.message}`);
49
- // Handle error (return 401, etc.)
50
- } else {
51
- console.log(`Authenticated as: ${result.user?.email}`);
52
- // Proceed with authenticated request
53
- }
54
- ```
55
-
56
- ## Authentication methods
57
-
58
- ### 1. API Key authentication
59
-
60
- API keys are used for programmatic access to Exulu APIs.
61
-
62
- **Format:** `{hashed_key}/{key_name}`
63
-
64
- **Example:** `exl_abc123def456.../production-api-key`
65
-
66
- ```typescript
67
- import { ExuluAuthentication, postgresClient } from "@exulu/backend";
68
-
69
- const { db } = await postgresClient();
70
-
71
- const result = await ExuluAuthentication.authenticate({
72
- apikey: "exl_abc123def456.../production-api-key",
73
- db
74
- });
75
-
76
- if (result.error) {
77
- return { status: result.code, message: result.message };
78
- }
79
-
80
- const user = result.user;
81
- console.log(`Authenticated: ${user?.email}`);
82
- console.log(`Role: ${user?.role.name}`);
83
- console.log(`Permissions:`, user?.role);
84
- ```
85
-
86
- **How it works:**
87
-
88
- 1. Extracts key name from format: `{key}/{name}`
89
- 2. Queries database for API users with matching key name
90
- 3. Compares hashed portion using bcrypt
91
- 4. Updates `last_used` timestamp on successful match
92
- 5. Returns user with role information
93
-
94
- **API key structure:**
95
-
96
- - **Before `/`**: Hashed key value (bcrypt)
97
- - **After `/`**: Human-readable key name
98
-
99
- ### 2. Session token authentication
100
-
101
- Session tokens are JWT tokens issued by NextAuth for web application authentication.
102
-
103
- ```typescript
104
- import { ExuluAuthentication, postgresClient } from "@exulu/backend";
105
-
106
- const { db } = await postgresClient();
107
-
108
- // Token from NextAuth session
109
- const authToken = {
110
- email: "user@example.com",
111
- name: "John Doe",
112
- // ... other session data
113
- };
114
-
115
- const result = await ExuluAuthentication.authenticate({
116
- authtoken: authToken,
117
- db
118
- });
119
-
120
- if (result.error) {
121
- return { status: 401, message: "Unauthorized" };
122
- }
123
-
124
- const user = result.user;
125
- console.log(`User: ${user?.firstname} ${user?.lastname}`);
126
- ```
127
-
128
- **How it works:**
129
-
130
- 1. Extracts email from session token
131
- 2. Queries database for user by email
132
- 3. Loads user's role information
133
- 4. Returns user object with role
134
-
135
- **Usage in Express middleware:**
136
-
137
- ```typescript
138
- import { ExuluAuthentication, postgresClient } from "@exulu/backend";
139
- import { getToken } from "next-auth/jwt";
140
- import express from "express";
141
-
142
- const app = express();
143
-
144
- app.use(async (req, res, next) => {
145
- const token = await getToken({ req });
146
-
147
- const { db } = await postgresClient();
148
- const result = await ExuluAuthentication.authenticate({
149
- authtoken: token,
150
- db
151
- });
152
-
153
- if (result.error) {
154
- return res.status(result.code || 401).json({
155
- error: result.message
156
- });
157
- }
158
-
159
- req.user = result.user;
160
- next();
161
- });
162
- ```
163
-
164
- ### 3. Internal key authentication
165
-
166
- Internal keys enable secure communication between internal Exulu services (e.g., backend and file upload service).
167
-
168
- **Environment variable:** `INTERNAL_SECRET`
169
-
170
- ```typescript
171
- import { ExuluAuthentication, postgresClient } from "@exulu/backend";
172
-
173
- const { db } = await postgresClient();
174
-
175
- const result = await ExuluAuthentication.authenticate({
176
- internalkey: process.env.INTERNAL_SECRET,
177
- db
178
- });
179
-
180
- if (result.error) {
181
- throw new Error("Internal authentication failed");
182
- }
183
-
184
- // Returns a synthetic "internal" user
185
- const user = result.user;
186
- console.log(user?.email); // "internal@exulu.com"
187
- console.log(user?.role.name); // "Internal"
188
- ```
189
-
190
- **How it works:**
191
-
192
- 1. Checks if `INTERNAL_SECRET` environment variable is set
193
- 2. Compares provided internal key with `INTERNAL_SECRET`
194
- 3. Returns a synthetic "internal" user with read-only role permissions
195
-
196
- **Synthetic internal user:**
197
-
198
- ```typescript
199
- {
200
- type: "api",
201
- id: 192837465,
202
- email: "internal@exulu.com",
203
- firstname: "API",
204
- lastname: "User",
205
- role: {
206
- id: "internal",
207
- name: "Internal",
208
- agents: "read",
209
- workflows: "read",
210
- variables: "read",
211
- users: "read",
212
- evals: "read"
213
- }
214
- }
215
- ```
216
-
217
- **Use case:**
218
-
219
- When the backend and file upload service (Uppy) run on different networks or environments, internal key authentication allows them to communicate securely without requiring user credentials.
220
-
221
- ## Function signature
222
-
223
- ```typescript
224
- ExuluAuthentication.authenticate({
225
- apikey?: string;
226
- authtoken?: any;
227
- internalkey?: string;
228
- db: Knex;
229
- }): Promise<{
230
- error: boolean;
231
- message?: string;
232
- code?: number;
233
- user?: User;
234
- }>
235
- ```
236
-
237
- ### Parameters
238
-
239
- <ParamField path="apikey" type="string">
240
- API key in format `{hashed_key}/{key_name}`
241
- </ParamField>
242
-
243
- <ParamField path="authtoken" type="any">
244
- NextAuth session token object (must contain `email` field)
245
- </ParamField>
246
-
247
- <ParamField path="internalkey" type="string">
248
- Internal service key matching `INTERNAL_SECRET` environment variable
249
- </ParamField>
250
-
251
- <ParamField path="db" type="Knex" required>
252
- Knex database connection instance
253
- </ParamField>
254
-
255
- ### Return value
256
-
257
- <ResponseField name="error" type="boolean">
258
- Whether authentication failed
259
- </ResponseField>
260
-
261
- <ResponseField name="message" type="string">
262
- Error message (only present if `error: true`)
263
- </ResponseField>
264
-
265
- <ResponseField name="code" type="number">
266
- HTTP status code (200 for success, 401 for failure)
267
- </ResponseField>
268
-
269
- <ResponseField name="user" type="User">
270
- Authenticated user object with role information
271
- ```typescript
272
- {
273
- id: number;
274
- firstname?: string;
275
- lastname?: string;
276
- email: string;
277
- type?: "api" | "user";
278
- role: {
279
- id: string;
280
- name: string;
281
- agents: "read" | "write";
282
- evals: "read" | "write";
283
- workflows: "read" | "write";
284
- variables: "read" | "write";
285
- users: "read" | "write";
286
- };
287
- }
288
- ```
289
- </ResponseField>
290
-
291
- ## Usage patterns
292
-
293
- ### Express middleware
294
-
295
- ```typescript
296
- import { ExuluAuthentication, postgresClient } from "@exulu/backend";
297
- import express from "express";
298
-
299
- const app = express();
300
-
301
- // Authentication middleware
302
- app.use(async (req, res, next) => {
303
- const { db } = await postgresClient();
304
-
305
- // Extract credentials from headers
306
- const apiKey = req.headers["x-api-key"] as string;
307
- const internalKey = req.headers["internal"] as string;
308
- const authHeader = req.headers["authorization"];
309
-
310
- // Session token from Bearer token
311
- let authToken;
312
- if (authHeader?.startsWith("Bearer ")) {
313
- const token = authHeader.substring(7);
314
- authToken = await decodeJWT(token); // Decode JWT
315
- }
316
-
317
- const result = await ExuluAuthentication.authenticate({
318
- apikey: apiKey,
319
- authtoken: authToken,
320
- internalkey: internalKey,
321
- db
322
- });
323
-
324
- if (result.error) {
325
- return res.status(result.code || 401).json({
326
- error: result.message
327
- });
328
- }
329
-
330
- // Attach user to request
331
- req.user = result.user;
332
- next();
333
- });
334
-
335
- // Protected route
336
- app.get("/api/agents", async (req, res) => {
337
- // User is available from middleware
338
- const user = req.user;
339
-
340
- if (user.role.agents !== "read" && user.role.agents !== "write") {
341
- return res.status(403).json({ error: "Forbidden" });
342
- }
343
-
344
- // Fetch agents
345
- const agents = await db.from("agents").select("*");
346
- res.json(agents);
347
- });
348
- ```
349
-
350
- ### Role-based access control
351
-
352
- ```typescript
353
- import { ExuluAuthentication, postgresClient } from "@exulu/backend";
354
-
355
- async function checkPermission(
356
- apiKey: string,
357
- resource: "agents" | "evals" | "workflows" | "variables" | "users",
358
- requiredPermission: "read" | "write"
359
- ) {
360
- const { db } = await postgresClient();
361
-
362
- const result = await ExuluAuthentication.authenticate({ apikey: apiKey, db });
363
-
364
- if (result.error) {
365
- throw new Error(`Authentication failed: ${result.message}`);
366
- }
367
-
368
- const userPermission = result.user?.role[resource];
369
-
370
- if (userPermission !== requiredPermission && userPermission !== "write") {
371
- throw new Error(
372
- `Insufficient permissions. User has ${userPermission} but ${requiredPermission} is required.`
373
- );
374
- }
375
-
376
- return result.user;
377
- }
378
-
379
- // Use
380
- try {
381
- const user = await checkPermission(apiKey, "agents", "write");
382
- // Proceed with agent creation
383
- } catch (error) {
384
- console.error(error.message);
385
- // Return 403 Forbidden
386
- }
387
- ```
388
-
389
- ### Multi-method authentication
390
-
391
- Support multiple authentication methods in the same endpoint:
392
-
393
- ```typescript
394
- import { ExuluAuthentication, postgresClient } from "@exulu/backend";
395
- import { getToken } from "next-auth/jwt";
396
-
397
- async function authenticateRequest(req: Request) {
398
- const { db } = await postgresClient();
399
-
400
- // Try API key
401
- const apiKey = req.headers.get("x-api-key");
402
- if (apiKey) {
403
- return await ExuluAuthentication.authenticate({ apikey: apiKey, db });
404
- }
405
-
406
- // Try session token
407
- const token = await getToken({ req });
408
- if (token) {
409
- return await ExuluAuthentication.authenticate({ authtoken: token, db });
410
- }
411
-
412
- // Try internal key
413
- const internalKey = req.headers.get("internal");
414
- if (internalKey) {
415
- return await ExuluAuthentication.authenticate({ internalkey: internalKey, db });
416
- }
417
-
418
- // No credentials provided
419
- return {
420
- error: true,
421
- message: "No authentication credentials provided",
422
- code: 401
423
- };
424
- }
425
-
426
- // Use in API route
427
- export async function GET(req: Request) {
428
- const authResult = await authenticateRequest(req);
429
-
430
- if (authResult.error) {
431
- return new Response(
432
- JSON.stringify({ error: authResult.message }),
433
- { status: authResult.code }
434
- );
435
- }
436
-
437
- const user = authResult.user;
438
- // Process request with authenticated user
439
- }
440
- ```
441
-
442
- ### Caching authenticated users
443
-
444
- For high-traffic APIs, cache user lookups:
445
-
446
- ```typescript
447
- import { ExuluAuthentication, postgresClient } from "@exulu/backend";
448
-
449
- class AuthCache {
450
- private cache = new Map<string, { user: User; expires: number }>();
451
- private ttl = 5 * 60 * 1000; // 5 minutes
452
-
453
- async authenticate(apiKey: string) {
454
- const cached = this.cache.get(apiKey);
455
-
456
- if (cached && cached.expires > Date.now()) {
457
- return { error: false, code: 200, user: cached.user };
458
- }
459
-
460
- const { db } = await postgresClient();
461
- const result = await ExuluAuthentication.authenticate({ apikey: apiKey, db });
462
-
463
- if (!result.error && result.user) {
464
- this.cache.set(apiKey, {
465
- user: result.user,
466
- expires: Date.now() + this.ttl
467
- });
468
- }
469
-
470
- return result;
471
- }
472
-
473
- invalidate(apiKey: string) {
474
- this.cache.delete(apiKey);
475
- }
476
-
477
- clear() {
478
- this.cache.clear();
479
- }
480
- }
481
-
482
- const authCache = new AuthCache();
483
-
484
- // Use
485
- const result = await authCache.authenticate(apiKey);
486
- ```
487
-
488
- ## Generating API keys
489
-
490
- API keys must be generated and stored in the database:
491
-
492
- ```typescript
493
- import bcrypt from "bcryptjs";
494
- import { randomBytes } from "crypto";
495
- import { postgresClient } from "@exulu/backend";
496
-
497
- async function generateApiKey(name: string, email: string) {
498
- const { db } = await postgresClient();
499
-
500
- // Generate random key
501
- const keyValue = `exl_${randomBytes(32).toString("hex")}`;
502
-
503
- // Hash the key
504
- const hashedKey = await bcrypt.hash(keyValue, 10);
505
-
506
- // Create API key string
507
- const apiKey = `${hashedKey}/${name}`;
508
-
509
- // Create API user
510
- await db.into("users").insert({
511
- email,
512
- type: "api",
513
- apikey: apiKey,
514
- role: "api_user", // Role ID
515
- created_at: new Date()
516
- });
517
-
518
- // Return the unhashed key (only time user sees it)
519
- return `${keyValue}/${name}`;
520
- }
521
-
522
- // Generate
523
- const apiKey = await generateApiKey("production-key", "api@example.com");
524
- console.log("API Key (save this!):", apiKey);
525
- // exl_abc123def456.../production-key
526
- ```
527
-
528
- <Warning>
529
- The unhashed API key is only available at generation time. Store it securely, as it cannot be retrieved later.
530
- </Warning>
531
-
532
- ## Database schema
533
-
534
- ### Users table
535
-
536
- ```sql
537
- CREATE TABLE users (
538
- id SERIAL PRIMARY KEY,
539
- firstname VARCHAR(255),
540
- lastname VARCHAR(255),
541
- email VARCHAR(255) UNIQUE NOT NULL,
542
- type VARCHAR(10), -- 'api' or 'user'
543
- apikey TEXT, -- Format: {hashed_key}/{key_name}
544
- role VARCHAR(255), -- Role ID
545
- last_used TIMESTAMP,
546
- created_at TIMESTAMP DEFAULT NOW(),
547
- updated_at TIMESTAMP DEFAULT NOW()
548
- );
549
- ```
550
-
551
- ### Roles table
552
-
553
- ```sql
554
- CREATE TABLE roles (
555
- id VARCHAR(255) PRIMARY KEY,
556
- name VARCHAR(255) NOT NULL,
557
- agents VARCHAR(10), -- 'read' or 'write'
558
- evals VARCHAR(10),
559
- workflows VARCHAR(10),
560
- variables VARCHAR(10),
561
- users VARCHAR(10)
562
- );
563
- ```
564
-
565
- ### Example role
566
-
567
- ```sql
568
- INSERT INTO roles (id, name, agents, evals, workflows, variables, users)
569
- VALUES (
570
- 'api_user',
571
- 'API User',
572
- 'write',
573
- 'write',
574
- 'read',
575
- 'read',
576
- 'read'
577
- );
578
- ```
579
-
580
- ## Error handling
581
-
582
- ExuluAuthentication returns different error messages for different failure scenarios:
583
-
584
- ### API key errors
585
-
586
- ```typescript
587
- // No API key name
588
- {
589
- error: true,
590
- message: "Provided api key does not include postfix with key name ({key}/{name}).",
591
- code: 401
592
- }
593
-
594
- // Invalid format
595
- {
596
- error: true,
597
- message: "Provided api key is not in the correct format.",
598
- code: 401
599
- }
600
-
601
- // No API users in database
602
- {
603
- error: true,
604
- message: "No API users found.",
605
- code: 401
606
- }
607
-
608
- // No matching key
609
- {
610
- error: true,
611
- message: "No matching api key found.",
612
- code: 401
613
- }
614
- ```
615
-
616
- ### Session token errors
617
-
618
- ```typescript
619
- // No email in token
620
- {
621
- error: true,
622
- message: "No email provided in session {...}",
623
- code: 401
624
- }
625
-
626
- // User not found
627
- {
628
- error: true,
629
- message: "No user found for email: user@example.com",
630
- code: 401
631
- }
632
-
633
- // Invalid token
634
- {
635
- error: true,
636
- message: "Invalid token.",
637
- code: 401
638
- }
639
- ```
640
-
641
- ### Internal key errors
642
-
643
- ```typescript
644
- // INTERNAL_SECRET not set
645
- {
646
- error: true,
647
- message: 'Header "internal" provided, but no INTERNAL_SECRET was provided in the environment variables.',
648
- code: 401
649
- }
650
-
651
- // Key mismatch
652
- {
653
- error: true,
654
- message: "Internal key was provided in header but did not match the INTERNAL_SECRET environment variable.",
655
- code: 401
656
- }
657
- ```
658
-
659
- ### General error
660
-
661
- ```typescript
662
- // No credentials
663
- {
664
- error: true,
665
- message: "Either an api key or authorization key must be provided.",
666
- code: 401
667
- }
668
- ```
669
-
670
- ## Security best practices
671
-
672
- <Tip>
673
- **API key rotation**: Regularly rotate API keys and revoke old ones by deleting the user record.
674
- </Tip>
675
-
676
- <Note>
677
- **HTTPS only**: Always use HTTPS in production to protect API keys and tokens in transit.
678
- </Note>
679
-
680
- <Warning>
681
- **Never log keys**: Never log API keys or internal secrets in application logs or error messages.
682
- </Warning>
683
-
684
- <Info>
685
- **Rate limiting**: Implement rate limiting per API key to prevent abuse.
686
- </Info>
687
-
688
- ## Common patterns
689
-
690
- ### Permission checking helper
691
-
692
- ```typescript
693
- function hasPermission(
694
- user: User,
695
- resource: keyof User["role"],
696
- requiredLevel: "read" | "write"
697
- ): boolean {
698
- const userPermission = user.role[resource];
699
-
700
- if (requiredLevel === "read") {
701
- return userPermission === "read" || userPermission === "write";
702
- }
703
-
704
- return userPermission === "write";
705
- }
706
-
707
- // Use
708
- if (!hasPermission(user, "agents", "write")) {
709
- return res.status(403).json({ error: "Forbidden" });
710
- }
711
- ```
712
-
713
- ### Audit logging
714
-
715
- ```typescript
716
- async function logAuthAttempt(
717
- method: "apikey" | "authtoken" | "internalkey",
718
- success: boolean,
719
- userId?: number,
720
- error?: string
721
- ) {
722
- const { db } = await postgresClient();
723
-
724
- await db.into("auth_logs").insert({
725
- method,
726
- success,
727
- user_id: userId,
728
- error_message: error,
729
- timestamp: new Date()
730
- });
731
- }
732
-
733
- // Use
734
- const result = await ExuluAuthentication({ apikey, db });
735
-
736
- await logAuthAttempt(
737
- "apikey",
738
- !result.error,
739
- result.user?.id,
740
- result.message
741
- );
742
- ```
743
-
744
- ### API key revocation
745
-
746
- ```typescript
747
- async function revokeApiKey(userId: number) {
748
- const { db } = await postgresClient();
749
-
750
- await db
751
- .from("users")
752
- .where({ id: userId, type: "api" })
753
- .delete();
754
-
755
- console.log(`Revoked API key for user ${userId}`);
756
- }
757
- ```
758
-
759
- ## Integration with ExuluApp
760
-
761
- Use ExuluAuthentication to protect ExuluApp API endpoints:
762
-
763
- ```typescript
764
- import { ExuluApp, ExuluAuthentication, postgresClient } from "@exulu/backend";
765
-
766
- const app = new ExuluApp();
767
-
768
- await app.create({
769
- config: {
770
- express: {
771
- enabled: true,
772
- middleware: async (expressApp) => {
773
- // Add authentication middleware
774
- expressApp.use(async (req, res, next) => {
775
- const { db } = await postgresClient();
776
- const apiKey = req.headers["x-api-key"] as string;
777
-
778
- const result = await ExuluAuthentication.authenticate({ apikey: apiKey, db });
779
-
780
- if (result.error) {
781
- return res.status(401).json({ error: "Unauthorized" });
782
- }
783
-
784
- req.user = result.user;
785
- next();
786
- });
787
- }
788
- }
789
- },
790
- contexts: {},
791
- agents: {}
792
- });
793
- ```
794
-
795
- ## Next steps
796
-
797
- <CardGroup cols={2}>
798
- <Card title="ExuluApp" icon="box" href="/core/exulu-app/introduction">
799
- Integrate authentication with ExuluApp
800
- </Card>
801
- <Card title="ExuluVariables" icon="key" href="/core/exulu-variables/introduction">
802
- Store INTERNAL_SECRET securely
803
- </Card>
804
- <Card title="ExuluProvider" icon="robot" href="/core/exulu-agent/introduction">
805
- Protect provider endpoints with authentication
806
- </Card>
807
- <Card title="API Reference" icon="book" href="/api-reference/introduction">
808
- View API documentation
809
- </Card>
810
- </CardGroup>