rotifex 0.1.7 → 0.1.8

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 CHANGED
@@ -1,1430 +1,66 @@
1
1
  ![banner](https://raw.githubusercontent.com/Grigary-C-Antony/Rotifex/master/banner-1.png)
2
2
 
3
- # Rotifex
3
+ <div align="center">
4
4
 
5
- > open the documentaion at [Docs](https://rotifex-docs.vercel.app/)
5
+ [![npm version](https://img.shields.io/npm/v/rotifex?color=4f6ef7&label=npm)](https://www.npmjs.com/package/rotifex)
6
+ [![Node.js](https://img.shields.io/badge/node-%3E%3D18-brightgreen)](https://nodejs.org)
7
+ [![License: MIT](https://img.shields.io/badge/license-MIT-blue)](LICENSE)
6
8
 
7
- ---
8
-
9
- ## Installation
10
-
11
- ```bash
12
- npm i rotifex
13
- ```
14
-
15
- Then start the server:
16
-
17
- ```bash
18
- npm start
19
- ```
20
-
21
- Or run directly without installing:
22
-
23
- ```bash
24
- npx rotifex start
25
- ```
26
-
27
- ## Commands
28
-
29
- | Command | Description |
30
- | ------- | ------------------------------------ |
31
- | `start` | Start the Rotifex development server |
32
-
33
- ### `start` flags
34
-
35
- | Flag | Description |
36
- | ----------------- | -------------------------------------------------------------------- |
37
- | `-p, --port <n>` | TCP port to listen on (overrides `ROTIFEX_PORT` and auto-fallback) |
38
- | `--host <host>` | Bind address (overrides `ROTIFEX_HOST`) |
39
- | `--verbose` | Enable debug-level logging |
40
-
41
- **Port auto-fallback:** If no port is explicitly set, Rotifex tries port `4994`, then `4995`, then `4996`. If all three are occupied it exits with a clear error. Pass `--port` or set `ROTIFEX_PORT` to pin a specific port and skip the fallback.
42
-
43
- ## Table of Contents
44
-
45
- 1. [Application Overview](#1-application-overview)
46
- 2. [Feature Documentation](#2-feature-documentation)
47
- 3. [API Documentation](#3-api-documentation)
48
- 4. [Authentication](#4-authentication)
49
- 5. [Models / Data Structures](#5-models--data-structures)
50
- 6. [AI / LLM Integration](#6-ai--llm-integration)
51
- 7. [File Storage / Media Handling](#7-file-storage--media-handling)
52
- 8. [Admin Panel Features](#8-admin-panel-features)
53
- 9. [Error Handling](#9-error-handling)
54
- 10. [Environment Configuration](#10-environment-configuration)
55
- 11. [Deployment](#11-deployment)
56
- 12. [Example Workflows](#12-example-workflows)
57
- 13. [Notes for Documentation Generators](#13-notes-for-documentation-generators)
58
-
59
- ---
60
-
61
- ## 1. Application Overview
62
-
63
- ### Description
64
-
65
- **Rotifex** is a self-hosted backend-as-a-service platform. It lets developers define data models through a JSON schema file or a visual admin panel, and instantly get a full REST API — no code generation required. It ships with JWT authentication, file storage, AI/LLM integration, agent execution, and a built-in admin dashboard.
66
-
67
- ### Core Purpose
68
-
69
- Rotifex eliminates boilerplate for backend development. Instead of writing CRUD endpoints, migrations, and auth logic manually, developers define a schema and Rotifex handles everything: table creation, route registration, validation, pagination, filtering, and sorting — live, without restarts.
70
-
71
- ### Key Capabilities
72
-
73
- - **Schema-driven REST API** — define a model, get five CRUD endpoints instantly
74
- - **Live schema updates** — add or remove models at runtime without restarting the server
75
- - **JWT Authentication** — register, login, refresh tokens, role-based access control
76
- - **File storage** — upload, download, manage public and private files with signed URLs
77
- - **AI/LLM integration** — connect OpenAI, Anthropic, Gemini, and Ollama; generate and chat
78
- - **AI Agents** — ReAct-loop agents with tools: calculator, web search, HTTP GET, DB query, datetime
79
- - **Token usage tracking** — persistent per-provider token consumption logged to disk
80
- - **Admin dashboard** — full SPA for managing schemas, users, files, AI providers, agents, logs, and settings
81
- - **Rate limiting, CORS, structured logging** — production-ready out of the box
82
-
83
- ### Target Users
84
-
85
- - Solo developers who need a backend quickly
86
- - Startups prototyping a product without a dedicated backend engineer
87
- - Internal tools teams who need structured data storage with an admin interface
88
- - Developers integrating LLMs into their applications
89
-
90
- ### Architecture Overview
91
-
92
- ```
93
- +-----------------------------------------------------+
94
- | Rotifex Server |
95
- | +----------+ +------------+ +-----------------+ |
96
- | | Fastify | | Schema | | SQLite (via | |
97
- | | HTTP | | Engine | | better-sqlite3)| |
98
- | | Server | | (live) | | | |
99
- | +----------+ +------------+ +-----------------+ |
100
- | +----------+ +------------+ +-----------------+ |
101
- | | JWT | | Storage | | AI / Agents | |
102
- | | Auth | | Manager | | System | |
103
- | +----------+ +------------+ +-----------------+ |
104
- | +------------------------------------------------+ |
105
- | | Admin SPA (React + Vite) | |
106
- | +------------------------------------------------+ |
107
- +-----------------------------------------------------+
108
- ```
109
-
110
- - **Framework:** Fastify v5 (Node.js)
111
- - **Database:** SQLite via `better-sqlite3`
112
- - **Admin frontend:** React 19 + Vite, served as a static SPA from `/`
113
- - **Config format:** JSON (`schema.json`, `ai.config.json`, `agents.config.json`)
114
- - **Persistence:** `.env` for secrets, JSON files for AI/agent config, SQLite for all app data
115
-
116
- ---
117
-
118
- ## 2. Feature Documentation
119
-
120
- ### 2.1 Dynamic REST Engine
121
-
122
- **Description:** The core of Rotifex. Reads `schema.json`, creates SQLite tables, and registers five CRUD routes per model — all at startup and live when models are added via the admin API.
123
-
124
- **Use case:** A developer defines a `Product` model with `name`, `price`, and `in_stock` fields. Rotifex immediately exposes `/api/products` with full CRUD, filtering, sorting, and pagination — no restart needed.
125
-
126
- **How it works:**
127
-
128
- 1. `schema.json` is parsed by `schemaLoader.js` into normalized model definitions.
129
- 2. `tableSync.js` runs `CREATE TABLE IF NOT EXISTS` for each model. On subsequent startups it **automatically adds missing columns** via `ALTER TABLE ADD COLUMN` — no manual migrations needed when fields are added to an existing model.
130
- 3. `routeFactory.js` registers generic parametric routes (`/api/:table`) that resolve the model from an in-memory store at request time.
131
- 4. When a model is added/removed via the admin API, the in-memory store is updated and routes resolve immediately.
132
-
133
- **Field types supported:**
134
-
135
- | Schema Type | SQLite Type | Notes |
136
- | ----------- | ----------- | ------------- |
137
- | `string` | `TEXT` | |
138
- | `number` | `REAL` | Float |
139
- | `integer` | `INTEGER` | |
140
- | `boolean` | `INTEGER` | Stored as 0/1 |
141
-
142
- **Field definition formats:**
143
-
144
- ```json
145
- // Shorthand
146
- { "name": "string" }
147
-
148
- // Full form
149
- {
150
- "name": {
151
- "type": "string",
152
- "required": true,
153
- "unique": false,
154
- "default": "Unnamed"
155
- }
156
- }
157
- ```
158
-
159
- **Auto-generated columns:** Every model automatically gets `id` (UUID), `created_at` (ISO 8601), and `updated_at` (ISO 8601).
160
-
161
- **Table naming:** Model names are lowercased and pluralized. `Product` -> table `products`, route `/api/products`.
162
-
163
- ---
164
-
165
- ### 2.2 JWT Authentication
166
-
167
- **Description:** Full authentication system with access tokens, refresh tokens, token rotation, logout/revocation, password hashing, and role-based access control.
168
-
169
- **Use case:** Secure user registration and login for a Rotifex-backed application. Protect admin routes from regular users.
170
-
171
- **How it works:**
172
-
173
- 1. `POST /auth/register` hashes the password with bcrypt (12 rounds) and inserts a user row.
174
- 2. `POST /auth/login` verifies credentials and issues a short-lived access token and long-lived refresh token.
175
- 3. The JWT middleware runs on every request, verifies the `Authorization: Bearer` header, and injects `x-user-id` / `x-user-role` headers that downstream routes use for authorization.
176
- 4. `POST /auth/refresh` issues a new token pair and **revokes the consumed refresh token** (single-use rotation). Each refresh token embeds a unique `jti` (JWT ID) used for targeted revocation.
177
- 5. `POST /auth/logout` invalidates the provided refresh token immediately.
178
- 6. `POST /auth/change-password` lets an authenticated user change their own password.
179
-
180
- **Token TTLs are configurable** via env vars (see Section 10). Defaults: access token 60 minutes, refresh token 30 days. The refresh TTL must be at least 2× the access TTL and no shorter than 2 hours.
181
-
182
- **Roles:** `user` (default) and `admin`. Admin access is required for all `/admin/api/*` endpoints.
183
-
184
- **Password rules:**
185
-
186
- - Minimum 8 characters
187
- - At least one letter
188
- - At least one number
189
-
190
- ---
191
-
192
- ### 2.3 File Storage
193
-
194
- **Description:** Upload, download, list, and delete files. Supports public and private visibility with HMAC-signed URLs for private file access.
195
-
196
- **Use case:** Allow users to upload profile pictures, documents, or any binary files. Private files can only be accessed by their uploader or admins, or via a time-limited signed URL.
197
-
198
- **How it works:**
199
-
200
- 1. Files are uploaded via multipart form to `POST /files/upload`.
201
- 2. The `StorageManager` validates MIME type and per-user storage quota.
202
- 3. Files are stored on disk with UUID-based names in separate `public/` and `private/` directories.
203
- 4. Metadata (name, MIME type, size, uploader, visibility) is recorded in the `_files` SQLite table.
204
- 5. Private files require a signed URL generated by `GET /files/:id/signed-url`. The URL includes an HMAC token and expiry timestamp; it is verified on download.
205
-
206
- ---
207
-
208
- ### 2.4 AI / LLM Integration
209
-
210
- **Description:** Connect multiple LLM providers and use them for text generation and multi-turn chat from a unified API.
211
-
212
- **Use case:** Add AI-powered features to your application — content generation, Q&A, summarization — using whichever provider you have access to.
213
-
214
- **How it works:**
215
-
216
- 1. Providers (OpenAI, Anthropic, Gemini, Ollama) are configured via the admin panel; API keys are stored in `ai.config.json`.
217
- 2. `POST /api/ai/generate` and `POST /api/ai/chat` proxy requests to the selected provider using native `fetch`.
218
- 3. Each call records token usage to `ai.usage.json` for persistent tracking.
219
-
220
- ---
221
-
222
- ### 2.5 AI Agents
223
-
224
- **Description:** Configurable AI agents that use a ReAct (Reasoning + Acting) loop to complete multi-step tasks using tools.
225
-
226
- **Use case:** An agent tasked with "What's 15% of the current Bitcoin price?" will search the web for the price, then use the calculator tool to compute the result — all automatically.
227
-
228
- **How it works:**
229
-
230
- 1. Agents are defined with a name, provider, model, system prompt, tool list, temperature, and iteration limits.
231
- 2. When run, the agent sends the task to the LLM with tool definitions.
232
- 3. If the LLM calls a tool, the tool is executed and the result is appended to the conversation.
233
- 4. The loop continues until the LLM returns a final answer or `maxIterations` is reached.
234
- 5. Steps (thinking, tool call, tool result, final answer) are returned for inspection.
235
-
236
- **Available tools:**
237
-
238
- | Tool | Description |
239
- | ---------------- | ------------------------------------------------------------------------ |
240
- | `get_datetime` | Returns current ISO 8601 datetime, UTC string, and Unix timestamp |
241
- | `calculate` | Safely evaluates math expressions (`+`, `-`, `*`, `/`, `%`, parentheses) |
242
- | `web_search` | DuckDuckGo instant-answer search (no API key required) |
243
- | `http_get` | Makes HTTP GET requests to public URLs; returns up to 4000 chars of body |
244
- | `database_query` | Runs read-only `SELECT` queries on the Rotifex SQLite database |
245
-
246
- ---
247
-
248
- ### 2.6 Admin Dashboard
249
-
250
- **Description:** A React SPA served at `/` providing a visual interface for all administrative operations.
251
-
252
- **Tabs:**
253
-
254
- - **Dashboard** — stat cards (schemas, records, users, files, storage, connected LLMs, agents, uptime, status) and overview tables
255
- - **Database Schemas** — create, view, and delete data models
256
- - **User Management** — view and manage registered users
257
- - **File Browser** — browse, preview, and delete uploaded files
258
- - **AI Integration** — configure providers, playground for generate/chat, agent management, API docs
259
- - **Server Logs** — real-time in-memory log viewer with level filtering
260
- - **Settings** — edit environment variables (port, CORS, rate limits, JWT secrets, storage limits)
261
-
262
- ---
263
-
264
- ## 3. API Documentation
265
-
266
- ### Base URL
267
-
268
- ```
269
- http://localhost:4994
270
- ```
271
-
272
- All API responses follow the envelope format:
273
-
274
- - **Success:** `{ "data": <payload>, "meta"?: <pagination> }`
275
- - **Error:** `{ "error": "<type>", "message": "<detail>", "statusCode": <number> }`
276
-
277
- ---
278
-
279
- ### 3.1 Health
280
-
281
- #### `GET /health`
282
-
283
- Check server status.
284
-
285
- **Auth:** None
286
-
287
- **Response:**
288
-
289
- ```json
290
- {
291
- "status": "ok",
292
- "uptime": 3600.5,
293
- "timestamp": "2026-03-06T12:00:00.000Z"
294
- }
295
- ```
296
-
297
- ---
298
-
299
- ### 3.2 Authentication Endpoints
300
-
301
- #### `POST /auth/register`
302
-
303
- Register a new user.
304
-
305
- **Auth:** None
306
-
307
- **Request Body:**
308
-
309
- ```json
310
- {
311
- "email": "user@example.com",
312
- "password": "secret123",
313
- "display_name": "Jane Doe",
314
- "role": "user"
315
- }
316
- ```
317
-
318
- | Field | Type | Required | Notes |
319
- | -------------- | ------ | -------- | ------------------------------------------- |
320
- | `email` | string | Yes | Must be a valid email |
321
- | `password` | string | Yes | Min 8 chars, 1 letter, 1 number |
322
- | `display_name` | string | No | Display name |
323
- | `role` | string | No | `"user"` or `"admin"`. Defaults to `"user"` |
324
-
325
- **Response `201`:**
326
-
327
- ```json
328
- {
329
- "data": {
330
- "id": "uuid",
331
- "email": "user@example.com",
332
- "display_name": "Jane Doe",
333
- "role": "user",
334
- "created_at": "2026-03-06T12:00:00.000Z"
335
- },
336
- "message": "User registered successfully"
337
- }
338
- ```
339
-
340
- **Errors:**
341
-
342
- | Code | Reason |
343
- | ----- | ------------------------------------------------ |
344
- | `400` | Validation failed (invalid email, weak password) |
345
- | `409` | Email already in use |
346
-
347
- ---
348
-
349
- #### `POST /auth/login`
350
-
351
- Authenticate and receive tokens.
352
-
353
- **Auth:** None
354
-
355
- **Request Body:**
356
-
357
- ```json
358
- {
359
- "email": "user@example.com",
360
- "password": "secret123"
361
- }
362
- ```
363
-
364
- **Response `200`:**
365
-
366
- ```json
367
- {
368
- "data": {
369
- "accessToken": "<jwt>",
370
- "refreshToken": "<jwt>",
371
- "user": {
372
- "id": "uuid",
373
- "email": "user@example.com",
374
- "display_name": "Jane Doe",
375
- "role": "user"
376
- }
377
- }
378
- }
379
- ```
380
-
381
- **Errors:**
382
-
383
- | Code | Reason |
384
- | ----- | ------------------------- |
385
- | `400` | Missing email or password |
386
- | `401` | Invalid credentials |
387
-
388
- ---
389
-
390
- #### `POST /auth/refresh`
391
-
392
- Exchange a refresh token for a new token pair. The consumed refresh token is immediately revoked (single-use rotation).
393
-
394
- **Auth:** None
395
-
396
- **Request Body:**
397
-
398
- ```json
399
- {
400
- "refreshToken": "<jwt>"
401
- }
402
- ```
403
-
404
- **Response `200`:**
405
-
406
- ```json
407
- {
408
- "data": {
409
- "accessToken": "<new-jwt>",
410
- "refreshToken": "<new-jwt>"
411
- }
412
- }
413
- ```
414
-
415
- **Errors:**
416
-
417
- | Code | Reason |
418
- | ----- | -------------------------------------------- |
419
- | `400` | Missing refreshToken |
420
- | `401` | Invalid, expired, or already-revoked token |
421
-
422
- ---
423
-
424
- #### `POST /auth/logout`
425
-
426
- Revoke a refresh token. After this call the token cannot be used to issue new pairs.
427
-
428
- **Auth:** None
429
-
430
- **Request Body:**
431
-
432
- ```json
433
- {
434
- "refreshToken": "<jwt>"
435
- }
436
- ```
437
-
438
- **Response `204`:** No content.
439
-
440
- **Errors:**
441
-
442
- | Code | Reason |
443
- | ----- | -------------------------------- |
444
- | `400` | Missing or invalid refreshToken |
445
-
446
- ---
447
-
448
- #### `POST /auth/change-password`
449
-
450
- Change the authenticated user's own password.
451
-
452
- **Auth:** `Authorization: Bearer <accessToken>`
453
-
454
- **Request Body:**
455
-
456
- ```json
457
- {
458
- "currentPassword": "old-password",
459
- "newPassword": "new-password123"
460
- }
461
- ```
462
-
463
- **Response `204`:** No content.
464
-
465
- **Errors:**
466
-
467
- | Code | Reason |
468
- | ----- | ----------------------------------- |
469
- | `400` | Missing fields or weak new password |
470
- | `401` | Wrong current password / bad token |
471
- | `404` | User not found |
472
-
473
- ---
474
-
475
- #### `GET /auth/me`
476
-
477
- Return the currently authenticated user.
478
-
479
- **Auth:** `Authorization: Bearer <accessToken>`
480
-
481
- **Response `200`:**
482
-
483
- ```json
484
- {
485
- "data": {
486
- "id": "uuid",
487
- "email": "user@example.com",
488
- "display_name": "Jane Doe",
489
- "role": "user",
490
- "created_at": "2026-03-06T12:00:00.000Z"
491
- }
492
- }
493
- ```
494
-
495
- **Errors:**
496
-
497
- | Code | Reason |
498
- | ----- | ------------------------ |
499
- | `401` | Missing or invalid token |
500
- | `404` | User not found |
501
-
502
- ---
503
-
504
- ### 3.3 Dynamic CRUD Endpoints
505
-
506
- These endpoints are available for every model defined in `schema.json`. Replace `:table` with the model's table name (e.g. `products` for a `Product` model).
507
-
508
- #### `GET /api/:table`
509
-
510
- List records with filtering, sorting, and pagination.
511
-
512
- **Auth:** Optional
513
-
514
- **Query Parameters:**
515
-
516
- | Parameter | Type | Default | Description |
517
- | --------- | ------- | ------------ | ---------------------------------------- |
518
- | `page` | integer | `1` | Page number |
519
- | `limit` | integer | `20` | Records per page (max 100) |
520
- | `sort` | string | `created_at` | Field to sort by |
521
- | `order` | string | `DESC` | `ASC` or `DESC` |
522
- | `<field>` | any | — | Filter by exact value on any model field |
523
-
524
- **Example:** `GET /api/products?sort=price&order=ASC&page=2&limit=10&in_stock=1`
525
-
526
- **Response `200`:**
527
-
528
- ```json
529
- {
530
- "data": [
531
- {
532
- "id": "uuid",
533
- "name": "Widget",
534
- "price": 9.99,
535
- "in_stock": 1,
536
- "created_at": "2026-03-06T12:00:00.000Z",
537
- "updated_at": "2026-03-06T12:00:00.000Z"
538
- }
539
- ],
540
- "meta": {
541
- "total": 42,
542
- "page": 2,
543
- "limit": 10,
544
- "pages": 5
545
- }
546
- }
547
- ```
548
-
549
- ---
550
-
551
- #### `GET /api/:table/:id`
552
-
553
- Get a single record by ID.
554
-
555
- **Response `200`:**
556
-
557
- ```json
558
- {
559
- "data": {
560
- "id": "uuid",
561
- "name": "Widget",
562
- "price": 9.99
563
- }
564
- }
565
- ```
566
-
567
- **Errors:**
568
-
569
- | Code | Reason |
570
- | ----- | --------------------------------- |
571
- | `404` | Record not found or unknown table |
572
-
573
- ---
574
-
575
- #### `POST /api/:table`
576
-
577
- Create a new record.
578
-
579
- **Auth:** Optional
580
-
581
- **Request Body:** JSON object matching the model's field definitions.
582
-
583
- ```json
584
- {
585
- "name": "Widget",
586
- "price": 9.99,
587
- "in_stock": true
588
- }
589
- ```
590
-
591
- **Response `201`:**
592
-
593
- ```json
594
- {
595
- "data": {
596
- "id": "uuid",
597
- "name": "Widget",
598
- "price": 9.99,
599
- "in_stock": 1,
600
- "created_at": "2026-03-06T12:00:00.000Z",
601
- "updated_at": "2026-03-06T12:00:00.000Z"
602
- }
603
- }
604
- ```
605
-
606
- **Errors:**
607
-
608
- | Code | Reason |
609
- | ----- | ------------------------------------------------------- |
610
- | `400` | Validation error (missing required fields, wrong types) |
611
- | `404` | Unknown table |
612
-
613
- ---
614
-
615
- #### `PUT /api/:table/:id`
616
-
617
- Update a record (partial — only send fields to change).
618
-
619
- **Request Body:**
620
-
621
- ```json
622
- {
623
- "price": 14.99
624
- }
625
- ```
626
-
627
- **Response `200`:**
628
-
629
- ```json
630
- {
631
- "data": {
632
- "id": "uuid",
633
- "name": "Widget",
634
- "price": 14.99
635
- }
636
- }
637
- ```
638
-
639
- **Errors:**
640
-
641
- | Code | Reason |
642
- | ----- | -------------------------------------- |
643
- | `400` | Validation error or no fields provided |
644
- | `404` | Record or table not found |
645
-
646
- ---
647
-
648
- #### `DELETE /api/:table/:id`
649
-
650
- Delete a record.
651
-
652
- **Response:** `204 No Content`
653
-
654
- **Errors:**
655
-
656
- | Code | Reason |
657
- | ----- | ------------------------- |
658
- | `404` | Record or table not found |
659
-
660
- ---
661
-
662
- ### 3.4 File Endpoints
663
-
664
- #### `POST /files/upload`
665
-
666
- Upload a file.
667
-
668
- **Auth:** Optional (identity from JWT `Authorization` header)
669
-
670
- **Request:** `multipart/form-data`
671
-
672
- | Field | Type | Required | Description |
673
- | ------------ | ------ | -------- | ----------------------------------- |
674
- | `file` | file | Yes | The file to upload |
675
- | `visibility` | string | No | `"public"` (default) or `"private"` |
676
-
677
- **Response `201`:**
678
-
679
- ```json
680
- {
681
- "data": {
682
- "id": "uuid",
683
- "original_name": "photo.jpg",
684
- "stored_name": "uuid.jpg",
685
- "mime_type": "image/jpeg",
686
- "size_bytes": 204800,
687
- "visibility": "public",
688
- "uploader_id": "user-uuid",
689
- "created_at": "2026-03-06T12:00:00.000Z"
690
- }
691
- }
692
- ```
693
-
694
- **Errors:**
695
-
696
- | Code | Reason |
697
- | ----- | ----------------------------------------- |
698
- | `400` | No file provided, invalid visibility |
699
- | `413` | File exceeds size limit or per-user quota |
700
-
701
- ---
702
-
703
- #### `GET /files`
704
-
705
- List files. Admins see all files; other users see only their own.
706
-
707
- **Response `200`:**
708
-
709
- ```json
710
- {
711
- "data": [
712
- /* array of file metadata objects */
713
- ],
714
- "meta": { "total": 5 }
715
- }
716
- ```
717
-
718
- ---
719
-
720
- #### `GET /files/:id`
721
-
722
- Get metadata for a single file.
723
-
724
- **Errors:**
725
-
726
- | Code | Reason |
727
- | ----- | --------------------------- |
728
- | `403` | Not the owner and not admin |
729
- | `404` | File not found |
730
-
731
- ---
732
-
733
- #### `GET /files/:id/download`
734
-
735
- Download a file. Public files are accessible directly. Private files require a signed URL.
736
-
737
- **Query Parameters (private files only):**
738
-
739
- | Parameter | Description |
740
- | --------- | ---------------------------------- |
741
- | `token` | HMAC token from the signed URL |
742
- | `expires` | Unix timestamp from the signed URL |
743
-
744
- **Response:** File stream with appropriate `Content-Type` and `Content-Disposition` headers.
745
-
746
- **Errors:**
747
-
748
- | Code | Reason |
749
- | ----- | ---------------------------------------------- |
750
- | `403` | Private file accessed without valid signed URL |
751
- | `404` | File not found |
752
-
753
- ---
754
-
755
- #### `GET /files/:id/signed-url`
756
-
757
- Generate a time-limited signed URL for a private file.
758
-
759
- **Auth:** Must be the file owner or admin.
760
-
761
- **Response `200`:**
762
-
763
- ```json
764
- {
765
- "data": {
766
- "url": "http://localhost:4994/files/uuid/download?token=abc123&expires=1741300000",
767
- "expires": 1741300000
768
- }
769
- }
770
- ```
771
-
772
- **Errors:**
773
-
774
- | Code | Reason |
775
- | ----- | --------------------------- |
776
- | `400` | File is not private |
777
- | `403` | Not the owner and not admin |
778
- | `404` | File not found |
779
-
780
- ---
781
-
782
- #### `DELETE /files/:id`
783
-
784
- Delete a file from disk and database.
785
-
786
- **Auth:** Must be the file owner or admin.
787
-
788
- **Response:** `204 No Content`
789
-
790
- ---
791
-
792
- ### 3.5 AI Endpoints
793
-
794
- #### `GET /api/ai/providers`
795
-
796
- List all enabled AI providers (public).
797
-
798
- **Response `200`:**
799
-
800
- ```json
801
- {
802
- "data": [
803
- {
804
- "id": "openai",
805
- "label": "OpenAI",
806
- "models": ["gpt-4o", "gpt-4o-mini"],
807
- "defaultModel": "gpt-4o"
808
- }
809
- ]
810
- }
811
- ```
812
-
813
- ---
814
-
815
- #### `GET /api/ai/models`
816
-
817
- Flat list of all models across all enabled providers.
818
-
819
- **Response `200`:**
820
-
821
- ```json
822
- {
823
- "data": [
824
- { "provider": "openai", "providerLabel": "OpenAI", "model": "gpt-4o" },
825
- {
826
- "provider": "anthropic",
827
- "providerLabel": "Anthropic",
828
- "model": "claude-sonnet-4-6"
829
- }
830
- ]
831
- }
832
- ```
833
-
834
- ---
835
-
836
- #### `GET /api/ai/models/:provider`
837
-
838
- Models for a specific provider.
839
-
840
- **Response `200`:**
841
-
842
- ```json
843
- { "data": ["gpt-4o", "gpt-4o-mini", "gpt-3.5-turbo"] }
844
- ```
845
-
846
- ---
847
-
848
- #### `POST /api/ai/generate`
849
-
850
- Generate a single text completion.
851
-
852
- **Request Body:**
853
-
854
- ```json
855
- {
856
- "provider": "openai",
857
- "model": "gpt-4o",
858
- "prompt": "Explain quantum entanglement in one sentence.",
859
- "system": "You are a physics professor.",
860
- "maxTokens": 256,
861
- "temperature": 0.7
862
- }
863
- ```
864
-
865
- | Field | Type | Required | Description |
866
- | ------------- | ------- | -------- | ------------------------------------------------------- |
867
- | `provider` | string | Yes | Provider ID (`openai`, `anthropic`, `gemini`, `ollama`) |
868
- | `prompt` | string | Yes | The user prompt |
869
- | `model` | string | No | Defaults to provider's `defaultModel` |
870
- | `system` | string | No | System instruction |
871
- | `maxTokens` | integer | No | Maximum tokens to generate |
872
- | `temperature` | number | No | Sampling temperature (0-2) |
873
-
874
- **Response `200`:**
875
-
876
- ```json
877
- {
878
- "data": {
879
- "text": "Quantum entanglement is a phenomenon where two particles...",
880
- "model": "gpt-4o",
881
- "usage": {
882
- "prompt_tokens": 42,
883
- "completion_tokens": 28
884
- }
885
- }
886
- }
887
- ```
888
-
889
- ---
890
-
891
- #### `POST /api/ai/chat`
892
-
893
- Multi-turn conversation.
894
-
895
- **Request Body:**
896
-
897
- ```json
898
- {
899
- "provider": "anthropic",
900
- "model": "claude-sonnet-4-6",
901
- "messages": [
902
- { "role": "user", "content": "Hello!" },
903
- { "role": "assistant", "content": "Hi there! How can I help?" },
904
- { "role": "user", "content": "What is 2+2?" }
905
- ],
906
- "system": "You are a helpful assistant.",
907
- "maxTokens": 512
908
- }
909
- ```
910
-
911
- **Response `200`:**
912
-
913
- ```json
914
- {
915
- "data": {
916
- "message": { "role": "assistant", "content": "2+2 equals 4." },
917
- "model": "claude-sonnet-4-6",
918
- "usage": {
919
- "prompt_tokens": 60,
920
- "completion_tokens": 12
921
- }
922
- }
923
- }
924
- ```
925
-
926
- ---
927
-
928
- ### 3.6 Agent Endpoints
929
-
930
- #### `GET /api/agents`
931
-
932
- List all defined agents (public).
933
-
934
- **Response `200`:**
935
-
936
- ```json
937
- {
938
- "data": [
939
- {
940
- "id": "uuid",
941
- "name": "Research Assistant",
942
- "description": "Searches the web and summarizes results",
943
- "provider": "openai",
944
- "model": "gpt-4o",
945
- "tools": ["web_search", "calculate"],
946
- "createdAt": "2026-03-06T12:00:00.000Z"
947
- }
948
- ]
949
- }
950
- ```
951
-
952
- ---
953
-
954
- #### `GET /api/agents/tools`
955
-
956
- List all available tools.
957
-
958
- **Response `200`:**
959
-
960
- ```json
961
- {
962
- "data": [
963
- {
964
- "name": "calculate",
965
- "description": "Safely evaluate a mathematical expression...",
966
- "parameters": {
967
- "expression": { "type": "string", "description": "A math expression" }
968
- },
969
- "required": ["expression"]
970
- }
971
- ]
972
- }
973
- ```
974
-
975
- ---
976
-
977
- #### `POST /api/agents/:id/run`
978
-
979
- Run an agent with a task input.
980
-
981
- **Request Body:**
982
-
983
- ```json
984
- {
985
- "input": "What is 18% tip on a $47.50 bill?"
986
- }
987
- ```
988
-
989
- **Response `200`:**
990
-
991
- ```json
992
- {
993
- "data": {
994
- "agentId": "uuid",
995
- "agentName": "Math Helper",
996
- "input": "What is 18% tip on a $47.50 bill?",
997
- "output": "The 18% tip on a $47.50 bill is $8.55.",
998
- "steps": [
999
- {
1000
- "type": "thinking",
1001
- "content": "I need to calculate 18% of 47.50",
1002
- "iteration": 1
1003
- },
1004
- {
1005
- "type": "tool_call",
1006
- "tool": "calculate",
1007
- "args": { "expression": "47.50 * 0.18" },
1008
- "iteration": 1
1009
- },
1010
- {
1011
- "type": "tool_result",
1012
- "tool": "calculate",
1013
- "result": "{\"expression\":\"47.50 * 0.18\",\"result\":8.55}",
1014
- "iteration": 1
1015
- },
1016
- {
1017
- "type": "final_answer",
1018
- "content": "The 18% tip on a $47.50 bill is $8.55.",
1019
- "iteration": 2
1020
- }
1021
- ],
1022
- "usage": {
1023
- "prompt_tokens": 320,
1024
- "completion_tokens": 85
1025
- }
1026
- }
1027
- }
1028
- ```
1029
-
1030
- **Errors:**
1031
-
1032
- | Code | Reason |
1033
- | ----- | ------------------------ |
1034
- | `400` | Missing or empty `input` |
1035
- | `404` | Agent not found |
1036
- | `500` | LLM provider error |
1037
-
1038
- ---
1039
-
1040
- ### 3.7 Admin Endpoints
1041
-
1042
- All admin endpoints require the `x-user-role: admin` header (automatically injected from JWT when role is `admin`).
1043
-
1044
- #### `GET /admin/api/stats`
1045
-
1046
- Dashboard statistics.
1047
-
1048
- **Response `200`:**
1049
-
1050
- ```json
1051
- {
1052
- "data": {
1053
- "models": [{ "model": "Product", "table": "products", "count": 42 }],
1054
- "users": { "count": 15 },
1055
- "files": { "count": 8, "storageMB": 12.5 },
1056
- "uptime": 3600,
1057
- "ai": {
1058
- "connectedLLMs": 2,
1059
- "enabledLLMs": 2,
1060
- "providers": [{ "id": "openai", "label": "OpenAI", "hasKey": true }],
1061
- "agentsCount": 3,
1062
- "usage": {
1063
- "totalRequests": 120,
1064
- "totalInputTokens": 45000,
1065
- "totalOutputTokens": 12000,
1066
- "byProvider": {
1067
- "openai": {
1068
- "requests": 80,
1069
- "inputTokens": 30000,
1070
- "outputTokens": 8000
1071
- }
1072
- }
1073
- }
1074
- }
1075
- }
1076
- }
1077
- ```
1078
-
1079
- ---
1080
-
1081
- #### `GET /admin/api/schema`
1082
-
1083
- Get all model definitions.
1084
-
1085
- **Response `200`:**
1086
-
1087
- ```json
1088
- {
1089
- "data": {
1090
- "Product": {
1091
- "tableName": "products",
1092
- "fields": [
1093
- { "name": "name", "type": "string", "required": true },
1094
- { "name": "price", "type": "number", "required": false }
1095
- ]
1096
- }
1097
- }
1098
- }
1099
- ```
1100
-
1101
- ---
1102
-
1103
- #### `POST /admin/api/schema`
1104
-
1105
- Create a new model. Routes become active immediately — no restart required.
1106
-
1107
- **Request Body:**
1108
-
1109
- ```json
1110
- {
1111
- "name": "Product",
1112
- "fields": {
1113
- "name": { "type": "string", "required": true },
1114
- "price": "number",
1115
- "in_stock": "boolean"
1116
- }
1117
- }
1118
- ```
1119
-
1120
- **Response `201`:**
1121
-
1122
- ```json
1123
- {
1124
- "data": { "name": "Product", "tableName": "products", "fields": [] },
1125
- "message": "Model \"Product\" is live. Routes /products are active now."
1126
- }
1127
- ```
1128
-
1129
- **Errors:**
1130
-
1131
- | Code | Reason |
1132
- | ----- | ---------------------------------------------- |
1133
- | `400` | Missing `name` or `fields`, reserved name used |
1134
- | `409` | Model with that name already exists |
1135
-
1136
- ---
1137
-
1138
- #### `DELETE /admin/api/schema/:name`
1139
-
1140
- Remove a model. Routes are deactivated immediately; the DB table is preserved.
1141
-
1142
- **Response:** `204 No Content`
1143
-
1144
- **Errors:**
1145
-
1146
- | Code | Reason |
1147
- | ----- | ---------------------------------------------- |
1148
- | `400` | Attempt to delete a system model (e.g. `User`) |
1149
- | `404` | Model not found |
1150
-
1151
- ---
1152
-
1153
- #### `GET /admin/api/logs`
1154
-
1155
- Retrieve in-memory server logs.
1156
-
1157
- **Query Parameters:**
1158
-
1159
- | Parameter | Description |
1160
- | --------- | ------------------------------------------------- |
1161
- | `after` | Unix timestamp — only return logs after this time |
1162
- | `level` | Filter by level: `info`, `warn`, `error`, `debug` |
1163
-
1164
- **Response `200`:**
1165
-
1166
- ```json
1167
- {
1168
- "logs": [
1169
- {
1170
- "ts": 1741258800000,
1171
- "level": "info",
1172
- "msg": "GET /api/products 200 12ms"
1173
- }
1174
- ]
1175
- }
1176
- ```
1177
-
1178
- ---
1179
-
1180
- #### `GET /admin/api/env`
1181
-
1182
- Read current environment/config values.
1183
-
1184
- **Response `200`:**
1185
-
1186
- ```json
1187
- {
1188
- "data": {
1189
- "JWT_SECRET": "***",
1190
- "ROTIFEX_PORT": "4994",
1191
- "ROTIFEX_CORS_ORIGIN": "*"
1192
- }
1193
- }
1194
- ```
1195
-
1196
- ---
1197
-
1198
- #### `POST /admin/api/env`
1199
-
1200
- Write environment variables to `.env`. Restart required for changes to take effect.
1201
-
1202
- **Request Body:**
1203
-
1204
- ```json
1205
- {
1206
- "vars": {
1207
- "ROTIFEX_PORT": "4000",
1208
- "ROTIFEX_CORS_ORIGIN": "https://myapp.com"
1209
- }
1210
- }
1211
- ```
1212
-
1213
- **Response `200`:**
1214
-
1215
- ```json
1216
- {
1217
- "message": "Environment saved. Restart the server for changes to take effect."
1218
- }
1219
- ```
1220
-
1221
- ---
1222
-
1223
- #### `GET /admin/api/ai/providers`
1224
-
1225
- Get all AI providers with masked API keys.
1226
-
1227
- **Response `200`:**
1228
-
1229
- ```json
1230
- {
1231
- "data": [
1232
- {
1233
- "id": "openai",
1234
- "label": "OpenAI",
1235
- "enabled": true,
1236
- "apiKey": "***abcd",
1237
- "hasKey": true,
1238
- "models": ["gpt-4o", "gpt-4o-mini"],
1239
- "defaultModel": "gpt-4o"
1240
- }
1241
- ]
1242
- }
1243
- ```
1244
-
1245
- ---
1246
-
1247
- #### `PUT /admin/api/ai/providers/:id`
1248
-
1249
- Update a provider's configuration.
1250
-
1251
- **Request Body:**
1252
-
1253
- ```json
1254
- {
1255
- "enabled": true,
1256
- "apiKey": "sk-...",
1257
- "defaultModel": "gpt-4o-mini"
1258
- }
1259
- ```
1260
-
1261
- **Response `200`:**
1262
-
1263
- ```json
1264
- {
1265
- "data": {
1266
- "id": "openai",
1267
- "label": "OpenAI",
1268
- "enabled": true,
1269
- "hasKey": true
1270
- },
1271
- "message": "Provider \"openai\" updated."
1272
- }
1273
- ```
1274
-
1275
- ---
1276
-
1277
- #### `GET /admin/api/agents`
1278
-
1279
- List all agents with full detail (including system prompt).
1280
-
1281
- #### `GET /admin/api/agents/:id`
1282
-
1283
- Get a single agent by ID.
1284
-
1285
- #### `POST /admin/api/agents`
1286
-
1287
- Create an agent.
1288
-
1289
- **Request Body:**
1290
-
1291
- ```json
1292
- {
1293
- "name": "Research Assistant",
1294
- "description": "Searches and summarizes web content",
1295
- "provider": "openai",
1296
- "model": "gpt-4o",
1297
- "systemPrompt": "You are a research assistant. Use tools to answer questions accurately.",
1298
- "tools": ["web_search", "calculate"],
1299
- "temperature": 0.7,
1300
- "maxTokens": 2048,
1301
- "maxIterations": 10
1302
- }
1303
- ```
1304
-
1305
- **Response `201`:**
9
+ **Self-hosted backend platform. Define a schema, get a full REST API instantly.**
1306
10
 
1307
- ```json
1308
- {
1309
- "data": { "id": "uuid", "name": "Research Assistant", ... },
1310
- "message": "Agent \"Research Assistant\" created."
1311
- }
1312
- ```
1313
-
1314
- #### `PUT /admin/api/agents/:id`
1315
-
1316
- Update an agent (partial patch). Same body shape as POST.
1317
-
1318
- **Response `200`:**
1319
-
1320
- ```json
1321
- {
1322
- "data": { "id": "uuid", "name": "Research Assistant", ... },
1323
- "message": "Agent \"Research Assistant\" updated."
1324
- }
1325
- ```
11
+ [Documentation](https://rotifex-docs.vercel.app/) · [Full Reference](README-final.md) · [npm](https://www.npmjs.com/package/rotifex)
1326
12
 
1327
- #### `DELETE /admin/api/agents/:id`
1328
-
1329
- Delete an agent permanently.
1330
-
1331
- **Response:** `204 No Content`
13
+ </div>
1332
14
 
1333
15
  ---
1334
16
 
1335
- ## 4. Authentication
1336
-
1337
- ### Flow
1338
-
1339
- ```
1340
- Client Rotifex Server
1341
- | |
1342
- |-- POST /auth/register ----------> | Hash password, create user
1343
- |<- { user } ---------------------- |
1344
- | |
1345
- |-- POST /auth/login -------------> | Verify password
1346
- |<- { accessToken, refreshToken }-- |
1347
- | |
1348
- |-- GET /api/products |
1349
- | Authorization: Bearer <token> > | Verify JWT, inject x-user-id/x-user-role
1350
- |<- { data: [...] } --------------- |
1351
- | |
1352
- |-- POST /auth/refresh -----------> | Verify refresh token, issue new pair
1353
- |<- { accessToken, refreshToken }-- |
1354
- ```
1355
-
1356
- ### Token Details
1357
-
1358
- | Token | Algorithm | Default TTL | TTL Env Var | Secret Env Var |
1359
- | ------------- | --------- | ----------- | ----------------------------- | -------------------- |
1360
- | Access Token | HS256 | 60 min | `ROTIFEX_ACCESS_TOKEN_TTL` | `JWT_SECRET` |
1361
- | Refresh Token | HS256 | 30 days | `ROTIFEX_REFRESH_TOKEN_TTL` | `JWT_REFRESH_SECRET` |
17
+ ## What is Rotifex?
1362
18
 
1363
- TTLs are in **minutes**. The refresh TTL must be the access TTL and 120 minutes. Both can be tuned in the admin **Settings** page or via `.env`.
19
+ Rotifex is a self-hosted backend-as-a-service that turns a JSON schema into a production-ready REST API in seconds — no code generation, no restart. It ships with JWT authentication, file storage, AI/LLM integration, and a full admin dashboard.
1364
20
 
1365
- Refresh tokens embed a unique `jti` (JWT ID) enabling individual revocation. Token rotation is enforced — each refresh token is single-use.
21
+ Think Supabase or Firebase, but running entirely on your own machine.
1366
22
 
1367
- Secrets are auto-generated and saved to `.env` on first startup if not explicitly set.
1368
-
1369
- ### Required Headers
1370
-
1371
- | Header | Value | Set By |
1372
- | --------------- | ---------------------- | ------------------------------ |
1373
- | `Authorization` | `Bearer <accessToken>` | Client |
1374
- | `x-user-id` | User UUID | JWT middleware (auto-injected) |
1375
- | `x-user-role` | `user` or `admin` | JWT middleware (auto-injected) |
1376
-
1377
- ### Permission Levels
23
+ ---
1378
24
 
1379
- | Role | Access |
1380
- | ------- | --------------------------------------------- |
1381
- | `user` | Public endpoints, own files, own data records |
1382
- | `admin` | All endpoints including `/admin/api/*` |
25
+ ## Key Features
1383
26
 
1384
- > The JWT middleware skips all `/auth/*` routes. For `/auth/me`, the token is manually verified inside the handler.
27
+ | Feature | Description |
28
+ | --------------------- | ------------------------------------------------------------------------------ |
29
+ | **Schema-driven API** | Define models in JSON or the visual editor — get 5 CRUD endpoints instantly |
30
+ | **Live updates** | Add or remove models at runtime without restarting the server |
31
+ | **JWT Auth** | Register, login, refresh tokens, role-based access, secure first-run setup |
32
+ | **File Storage** | Public and private uploads with HMAC-signed URLs |
33
+ | **AI / LLM** | Connect OpenAI, Anthropic, Gemini, or Ollama from one unified API |
34
+ | **AI Agents** | ReAct-loop agents with tools: calculator, web search, HTTP, DB query, datetime |
35
+ | **Admin Dashboard** | React SPA for managing everything — schemas, users, files, AI, logs, settings |
36
+ | **Production-ready** | Rate limiting, CORS, structured logging, SQLite, auto-rotating JWT secrets |
1385
37
 
1386
38
  ---
1387
39
 
1388
- ## 5. Models / Data Structures
40
+ ## Quick Start
1389
41
 
1390
- ### User
42
+ **Install and run:**
1391
43
 
1392
- Built-in model managed by the auth system. Not configurable via the schema engine.
1393
-
1394
- | Field | Type | Required | Notes |
1395
- | --------------- | ----------------- | -------- | ----------------------------------- |
1396
- | `id` | string (UUID) | Auto | Primary key |
1397
- | `email` | string | Yes | Unique |
1398
- | `display_name` | string | No | |
1399
- | `role` | string | Yes | `"user"` or `"admin"` |
1400
- | `password_hash` | string | Internal | bcrypt hash — never returned by API |
1401
- | `created_at` | string (ISO 8601) | Auto | |
1402
- | `updated_at` | string (ISO 8601) | Auto | |
44
+ ```bash
45
+ npm install rotifex
46
+ npm start
47
+ ```
1403
48
 
1404
- ---
49
+ **Or without installing:**
1405
50
 
1406
- ### \_files (File Metadata)
51
+ ```bash
52
+ npx rotifex start
53
+ ```
1407
54
 
1408
- Internal table managed by `StorageManager`. Not in `schema.json`.
55
+ Open `http://localhost:4994` in your browser. On first run you will see a setup screen — create your admin account and you are ready to go.
1409
56
 
1410
- | Field | Type | Notes |
1411
- | --------------- | ----------------- | ---------------------------------------- |
1412
- | `id` | string (UUID) | Primary key |
1413
- | `original_name` | string | Original filename from upload |
1414
- | `stored_name` | string | `<uuid>.<ext>` — actual filename on disk |
1415
- | `mime_type` | string | e.g. `image/jpeg` |
1416
- | `size_bytes` | integer | File size in bytes |
1417
- | `visibility` | string | `"public"` or `"private"` |
1418
- | `uploader_id` | string | UUID of the uploading user |
1419
- | `created_at` | string (ISO 8601) | |
57
+ > **Save your password.** There is no password recovery. If you ever lose access, run `rotifex reset-admin --yes` from the terminal.
1420
58
 
1421
59
  ---
1422
60
 
1423
- ### Custom Models (schema.json)
61
+ ## How It Works
1424
62
 
1425
- All custom models automatically receive `id`, `created_at`, and `updated_at`.
1426
-
1427
- **Example `schema.json`:**
63
+ **1. Define your schema** (`schema.json` or the visual editor in the dashboard):
1428
64
 
1429
65
  ```json
1430
66
  {
@@ -1432,631 +68,93 @@ All custom models automatically receive `id`, `created_at`, and `updated_at`.
1432
68
  "fields": {
1433
69
  "name": { "type": "string", "required": true },
1434
70
  "price": "number",
1435
- "in_stock": "boolean",
1436
- "sku": { "type": "string", "unique": true }
1437
- }
1438
- },
1439
- "Order": {
1440
- "fields": {
1441
- "product_id": { "type": "string", "required": true },
1442
- "quantity": { "type": "integer", "required": true },
1443
- "status": { "type": "string", "default": "pending" }
1444
- }
1445
- }
1446
- }
1447
- ```
1448
-
1449
- **Resulting routes:**
1450
-
1451
- - `Product` -> table `products` -> `/api/products`, `/api/products/:id`
1452
- - `Order` -> table `orders` -> `/api/orders`, `/api/orders/:id`
1453
-
1454
- ---
1455
-
1456
- ### Agent
1457
-
1458
- Stored in `agents.config.json`.
1459
-
1460
- | Field | Type | Default | Description |
1461
- | --------------- | ----------------- | ------- | -------------------------------------------- |
1462
- | `id` | string (UUID) | Auto | |
1463
- | `name` | string | — | Display name |
1464
- | `description` | string | `""` | Human-readable description |
1465
- | `provider` | string | — | `openai`, `anthropic`, `gemini`, or `ollama` |
1466
- | `model` | string | — | Model ID |
1467
- | `systemPrompt` | string | `""` | Agent's system instruction |
1468
- | `tools` | string[] | `[]` | List of tool names to enable |
1469
- | `temperature` | number | `0.7` | Sampling temperature |
1470
- | `maxTokens` | integer | `2048` | Max tokens per LLM call |
1471
- | `maxIterations` | integer | `10` | Max tool-call loop iterations |
1472
- | `createdAt` | string (ISO 8601) | Auto | |
1473
- | `updatedAt` | string (ISO 8601) | Auto | |
1474
-
1475
- ---
1476
-
1477
- ### AI Provider (ai.config.json)
1478
-
1479
- | Field | Type | Description |
1480
- | -------------- | -------- | ---------------------------------- |
1481
- | `label` | string | Display name |
1482
- | `apiKey` | string | API key (empty string if not set) |
1483
- | `enabled` | boolean | Whether the provider is active |
1484
- | `models` | string[] | Available model IDs |
1485
- | `defaultModel` | string | Default model ID |
1486
- | `baseUrl` | string | Only for Ollama — local server URL |
1487
-
1488
- ---
1489
-
1490
- ### Token Usage (ai.usage.json)
1491
-
1492
- ```json
1493
- {
1494
- "totalRequests": 150,
1495
- "totalInputTokens": 55000,
1496
- "totalOutputTokens": 14000,
1497
- "byProvider": {
1498
- "openai": {
1499
- "requests": 100,
1500
- "inputTokens": 40000,
1501
- "outputTokens": 10000
1502
- },
1503
- "anthropic": {
1504
- "requests": 50,
1505
- "inputTokens": 15000,
1506
- "outputTokens": 4000
71
+ "in_stock": "boolean"
1507
72
  }
1508
73
  }
1509
74
  }
1510
75
  ```
1511
76
 
1512
- ---
1513
-
1514
- ## 6. AI / LLM Integration
1515
-
1516
- ### Supported Providers
1517
-
1518
- | Provider ID | Label | Requires API Key | Default Models |
1519
- | ----------- | -------------- | ---------------- | ------------------------------------------------------------- |
1520
- | `openai` | OpenAI | Yes | gpt-4o, gpt-4o-mini, gpt-4-turbo, gpt-3.5-turbo |
1521
- | `anthropic` | Anthropic | Yes | claude-opus-4-6, claude-sonnet-4-6, claude-haiku-4-5-20251001 |
1522
- | `gemini` | Google Gemini | Yes | gemini-2.0-flash, gemini-1.5-pro, gemini-1.5-flash |
1523
- | `ollama` | Ollama (Local) | No | llama3.2, mistral, codellama, phi3 |
1524
-
1525
- ### Configuration
1526
-
1527
- Providers are configured via:
1528
-
1529
- 1. **Admin panel -> AI Integration -> Providers tab:** Enable provider, paste API key, select default model.
1530
- 2. **Direct file edit:** Edit `ai.config.json` in the project root.
1531
-
1532
- Ollama requires a running local Ollama server. Default base URL: `http://localhost:11434`.
1533
-
1534
- ### Token Usage Tracking
1535
-
1536
- Every call to `POST /api/ai/generate`, `POST /api/ai/chat`, and `POST /api/agents/:id/run` records:
1537
-
1538
- - Input token count
1539
- - Output token count
1540
- - Per-provider breakdown
1541
-
1542
- Data is persisted to `ai.usage.json` and survives server restarts. Totals are visible in the admin dashboard.
1543
-
1544
- ### AI Playground (Admin Panel)
1545
-
1546
- - **Generate mode:** Single prompt with provider, model, system prompt, temperature, max tokens controls. Shows token count after each request.
1547
- - **Chat mode:** Multi-turn conversation with scrollable history and persistent context.
1548
- - **Session totals:** Cumulative token counts for the current browser session with a reset button.
1549
-
1550
- ### Agent System
1551
-
1552
- #### ReAct Loop
1553
-
1554
- ```
1555
- User Input
1556
- |
1557
- v
1558
- LLM (receives task + tool definitions)
1559
- |
1560
- +-- Tool call requested?
1561
- | |
1562
- | +-- Execute tool
1563
- | +-- Append result to conversation
1564
- | +-- Loop back to LLM
1565
- |
1566
- +-- Final answer -> return output + all steps to caller
1567
- ```
1568
-
1569
- #### Agent Step Types
1570
-
1571
- | Type | Description |
1572
- | -------------- | ------------------------------------------- |
1573
- | `thinking` | LLM reasoning text before invoking a tool |
1574
- | `tool_call` | Name and arguments of the tool being called |
1575
- | `tool_result` | Raw output from the tool execution |
1576
- | `final_answer` | The LLM's conclusive response |
1577
-
1578
- ---
1579
-
1580
- ## 7. File Storage / Media Handling
1581
-
1582
- ### Upload Process
1583
-
1584
- 1. Client sends `POST /files/upload` as `multipart/form-data` with a `file` field and optional `visibility` field.
1585
- 2. Server reads and buffers the stream, validates MIME type against the allowed list.
1586
- 3. Checks the file size against `maxFileSizeMB` (default 10 MB).
1587
- 4. Checks the uploader's total storage against `maxStoragePerUserMB` (default 100 MB).
1588
- 5. Writes the file to disk as `<uuid><original-extension>` in the appropriate directory.
1589
- 6. Inserts metadata into the `_files` SQLite table.
1590
- 7. Returns the full file metadata record.
1591
-
1592
- ### Access URLs
1593
-
1594
- | Visibility | Download URL | Auth Required |
1595
- | ---------- | ---------------------------------------------------- | ------------------------ |
1596
- | `public` | `/files/:id/download` | No |
1597
- | `private` | `/files/:id/download?token=<hmac>&expires=<unix-ts>` | Signed URL (HMAC-SHA256) |
1598
-
1599
- Signed URLs are generated via `GET /files/:id/signed-url`. The default TTL is 1 hour, configurable via `signedUrlTTLSeconds` in config.
1600
-
1601
- ### Storage Structure
1602
-
1603
- ```
1604
- <project-root>/
1605
- storage/
1606
- public/ <- Publicly downloadable files
1607
- <uuid>.jpg
1608
- <uuid>.png
1609
- private/ <- Signed-URL-only files
1610
- <uuid>.pdf
1611
- <uuid>.docx
1612
- ```
1613
-
1614
- ### Size and Quota Limits
1615
-
1616
- | Setting | Environment Variable | Default |
1617
- | -------------------- | ----------------------------------- | ------- |
1618
- | Max file size | `ROTIFEX_STORAGE_MAX_FILE_SIZE_MB` | 10 MB |
1619
- | Max storage per user | Config only (`maxStoragePerUserMB`) | 100 MB |
1620
-
1621
- ---
1622
-
1623
- ## 8. Admin Panel Features
1624
-
1625
- ### Dashboard
1626
-
1627
- - **Stat cards:** Schemas, Total Records, Users, Files, Storage Used, Connected LLMs, Agents Created, Server Uptime, Server Status
1628
- - **Schema Overview table:** model name, table name, record count per model
1629
- - **Connected LLMs table:** provider name, request count, tokens in, tokens out, key status
1630
-
1631
- ### Database Schemas
1632
-
1633
- - View all defined models with their field names, types, and constraints
1634
- - Create a new model via a field builder UI (add field name + type pairs)
1635
- - Attempting to create a model with an existing name returns a conflict error
1636
- - Delete a model (routes deactivate immediately; underlying data table is preserved)
1637
-
1638
- ### User Management
1639
-
1640
- - List all registered users: email, display name, role, creation date
1641
- - **Create user** — "New User" modal with email, password, display name, and role
1642
- - **Reset password** — inline "Reset Password" section inside the edit modal (admin force-sets any user's password)
1643
- - Password validation enforced: minimum 8 characters, at least one letter and one number
1644
-
1645
- ### File Browser
1646
-
1647
- - Browse all files (admins see all; users see their own)
1648
- - Preview images inline
1649
- - Download any file
1650
- - Delete files (removes from disk and database)
1651
-
1652
- ### AI Integration
1653
-
1654
- **Providers tab:** Enable/disable providers, enter API keys (masked after save), set default model per provider.
1655
-
1656
- **Playground tab:**
1657
-
1658
- - Generate mode: prompt + provider/model/system/temp/maxTokens controls, token display after response
1659
- - Chat mode: multi-turn conversation with message history
1660
-
1661
- **Agents tab:**
1662
-
1663
- - List all agents with name, provider, model, tools
1664
- - Create agent form: name, description, provider, model, system prompt, tool checkboxes, temperature, max tokens, max iterations
1665
- - Edit existing agents
1666
- - Delete agents
1667
- - Run agents interactively: enter a task, see reasoning steps in real time, view final output
1668
-
1669
- **API Docs tab:** Built-in reference for all AI and agent endpoints.
1670
-
1671
- ### Server Logs
1672
-
1673
- - In-memory ring buffer of structured log entries
1674
- - Filter by level (`info`, `warn`, `error`, `debug`)
1675
- - Timestamps and log messages displayed in a table
1676
-
1677
- ### Settings
1678
-
1679
- Editable via admin panel — writes to `.env`:
1680
-
1681
- | Variable | Description |
1682
- | ----------------------------------- | --------------------------------------------------------------- |
1683
- | `ROTIFEX_ACCESS_TOKEN_TTL` | Access token lifetime in minutes (min 5, default 60) |
1684
- | `ROTIFEX_REFRESH_TOKEN_TTL` | Refresh token lifetime in minutes (min 120 and ≥ 2×access, default 43200) |
1685
- | `JWT_SECRET` | Access token signing secret |
1686
- | `JWT_REFRESH_SECRET` | Refresh token signing secret |
1687
- | `ROTIFEX_PORT` | Server port |
1688
- | `ROTIFEX_HOST` | Server bind host |
1689
- | `ROTIFEX_CORS_ORIGIN` | Allowed CORS origin(s) |
1690
- | `ROTIFEX_RATE_LIMIT_MAX` | Max requests per time window |
1691
- | `ROTIFEX_LOG_LEVEL` | Log verbosity |
1692
- | `ROTIFEX_STORAGE_MAX_FILE_SIZE_MB` | Max upload size in MB |
1693
- | `ROTIFEX_STORAGE_SIGNED_URL_SECRET` | HMAC secret for signed URLs |
1694
-
1695
- The **Token Timing** card validates constraints live: refresh TTL must be ≥ 2× access TTL and ≥ 120 minutes. The Save button is disabled until all errors are resolved.
1696
-
1697
- ---
1698
-
1699
- ## 9. Error Handling
1700
-
1701
- ### Error Response Format
1702
-
1703
- ```json
1704
- {
1705
- "error": "Error Type",
1706
- "message": "Human-readable description of what went wrong.",
1707
- "statusCode": 400
1708
- }
1709
- ```
1710
-
1711
- Validation errors may return an array for `message`:
1712
-
1713
- ```json
1714
- {
1715
- "error": "Validation Error",
1716
- "message": [{ "path": ["name"], "message": "Required" }],
1717
- "statusCode": 400
1718
- }
1719
- ```
1720
-
1721
- ### Common HTTP Error Codes
1722
-
1723
- | Code | Meaning | Common Causes |
1724
- | ----- | --------------------- | ------------------------------------------------------------------------------------- |
1725
- | `400` | Bad Request | Missing required fields, invalid types, reserved model names, no fields to update |
1726
- | `401` | Unauthorized | Missing, expired, or invalid JWT access token |
1727
- | `403` | Forbidden | Non-admin accessing `/admin/api/*`, file access without ownership or valid signed URL |
1728
- | `404` | Not Found | Unknown table name, record ID not found, agent not found, file not found |
1729
- | `409` | Conflict | Email already registered, model name already exists |
1730
- | `413` | Payload Too Large | File exceeds per-request size limit or per-user storage quota |
1731
- | `500` | Internal Server Error | Unexpected server error, LLM provider failure |
1732
-
1733
- ---
1734
-
1735
- ## 10. Environment Configuration
1736
-
1737
- ### Configuration Priority (highest to lowest)
1738
-
1739
- 1. Shell environment variables
1740
- 2. CLI flags (`--port`, `--host`, `--verbose`)
1741
- 3. `config.json` (optional user overrides)
1742
- 4. `config.default.json` (shipped defaults)
1743
- 5. `.env` file (auto-loaded at startup)
1744
-
1745
- ### Environment Variables Reference
1746
-
1747
- | Variable | Default | Description |
1748
- | ----------------------------------- | --------- | ------------------------------------------------------------------ |
1749
- | `ROTIFEX_PORT` | `4994` | TCP port (auto-tries 4994 → 4995 → 4996 if unset) |
1750
- | `ROTIFEX_HOST` | `0.0.0.0` | Bind address |
1751
- | `ROTIFEX_CORS_ORIGIN` | `*` | Allowed CORS origin |
1752
- | `ROTIFEX_RATE_LIMIT_MAX` | `100` | Max requests per rate-limit window |
1753
- | `ROTIFEX_LOG_LEVEL` | `info` | Log level (`info`, `debug`, `warn`, `error`) |
1754
- | `ROTIFEX_STORAGE_MAX_FILE_SIZE_MB` | `10` | Max upload size in MB |
1755
- | `ROTIFEX_STORAGE_SIGNED_URL_SECRET` | auto | HMAC secret for signed file URLs |
1756
- | `ROTIFEX_ACCESS_TOKEN_TTL` | `60` | Access token TTL in minutes (min 5) |
1757
- | `ROTIFEX_REFRESH_TOKEN_TTL` | `43200` | Refresh token TTL in minutes (min 120, must be ≥ 2× access TTL) |
1758
- | `JWT_SECRET` | auto | Access token signing secret |
1759
- | `JWT_REFRESH_SECRET` | auto | Refresh token signing secret |
1760
-
1761
- > `JWT_SECRET`, `JWT_REFRESH_SECRET`, and `ROTIFEX_STORAGE_SIGNED_URL_SECRET` are auto-generated on first startup if absent and saved to `.env`.
1762
-
1763
- ### Example `.env`
1764
-
1765
- ```env
1766
- ROTIFEX_PORT=4994
1767
- ROTIFEX_HOST=0.0.0.0
1768
- ROTIFEX_CORS_ORIGIN=https://myapp.com
1769
- ROTIFEX_RATE_LIMIT_MAX=200
1770
- ROTIFEX_LOG_LEVEL=info
1771
- ROTIFEX_STORAGE_MAX_FILE_SIZE_MB=25
1772
- ROTIFEX_ACCESS_TOKEN_TTL=60
1773
- ROTIFEX_REFRESH_TOKEN_TTL=43200
1774
- JWT_SECRET=replace-with-a-long-random-string
1775
- JWT_REFRESH_SECRET=replace-with-another-long-random-string
1776
- ROTIFEX_STORAGE_SIGNED_URL_SECRET=replace-with-yet-another-secret
1777
- ```
1778
-
1779
- ---
1780
-
1781
- ## 11. Deployment
1782
-
1783
- ### Requirements
1784
-
1785
- - Node.js 18 or later
1786
- - npm 9 or later
1787
-
1788
- ### Setup
1789
-
1790
- ```bash
1791
- git clone <repo-url>
1792
- cd rotifex
1793
- npm install
1794
- ```
1795
-
1796
- ### Running in Development
1797
-
1798
- ```bash
1799
- # Default port 4994 (falls back to 4995, 4996 if in use)
1800
- npx rotifex start
1801
-
1802
- # Pin a specific port (skips auto-fallback)
1803
- npx rotifex start --port 4000
1804
-
1805
- # Custom host
1806
- npx rotifex start --host 127.0.0.1
1807
-
1808
- # Verbose (debug) logging
1809
- npx rotifex start --verbose
1810
- ```
1811
-
1812
- ### Build the Admin Dashboard
1813
-
1814
- ```bash
1815
- npm run build:admin
1816
- ```
1817
-
1818
- Output is written to `admin/dist/`. The server automatically serves it at `/` when the directory exists.
1819
-
1820
- ### Production Setup
1821
-
1822
- **1. Set secrets in environment:**
1823
-
1824
- ```bash
1825
- export JWT_SECRET="$(openssl rand -hex 32)"
1826
- export JWT_REFRESH_SECRET="$(openssl rand -hex 32)"
1827
- export ROTIFEX_STORAGE_SIGNED_URL_SECRET="$(openssl rand -hex 32)"
1828
- export ROTIFEX_CORS_ORIGIN="https://yourfrontend.com"
1829
- ```
1830
-
1831
- **2. Build the admin dashboard:**
1832
-
1833
- ```bash
1834
- npm run build:admin
1835
- ```
1836
-
1837
- **3. Use a process manager:**
1838
-
1839
- ```bash
1840
- npm install -g pm2
1841
- pm2 start "npx rotifex start" --name rotifex
1842
- pm2 save
1843
- pm2 startup
1844
- ```
1845
-
1846
- **4. Reverse proxy (Nginx example):**
1847
-
1848
- ```nginx
1849
- server {
1850
- listen 80;
1851
- server_name api.yourapp.com;
1852
-
1853
- location / {
1854
- proxy_pass http://127.0.0.1:4994;
1855
- proxy_set_header Host $host;
1856
- proxy_set_header X-Real-IP $remote_addr;
1857
- proxy_set_header X-Forwarded-Proto $scheme;
1858
- }
1859
- }
1860
- ```
1861
-
1862
- **5. Backup the database:**
1863
-
1864
- ```bash
1865
- # SQLite database file — copy it regularly
1866
- cp rotifex.db rotifex.db.backup
1867
- ```
1868
-
1869
- ---
1870
-
1871
- ## 12. Example Workflows
1872
-
1873
- ### Registering a User and Logging In
1874
-
1875
- ```bash
1876
- # Register
1877
- curl -X POST http://localhost:4994/auth/register \
1878
- -H "Content-Type: application/json" \
1879
- -d '{"email":"jane@example.com","password":"secure123","display_name":"Jane"}'
1880
-
1881
- # Login — save the returned accessToken
1882
- curl -X POST http://localhost:4994/auth/login \
1883
- -H "Content-Type: application/json" \
1884
- -d '{"email":"jane@example.com","password":"secure123"}'
1885
-
1886
- # Store token
1887
- export TOKEN="<accessToken from response>"
1888
- ```
1889
-
1890
- ---
1891
-
1892
- ### Defining a Model and Using the CRUD API
77
+ **2. Instant REST API** — no restart needed:
1893
78
 
1894
79
  ```bash
1895
- # 1. Create model (admin role required)
1896
- curl -X POST http://localhost:4994/admin/api/schema \
1897
- -H "Content-Type: application/json" \
1898
- -H "Authorization: Bearer $TOKEN" \
1899
- -d '{
1900
- "name": "Product",
1901
- "fields": {
1902
- "name": {"type": "string", "required": true},
1903
- "price": "number",
1904
- "in_stock": "boolean"
1905
- }
1906
- }'
1907
-
1908
- # 2. Create a record
80
+ # Create
1909
81
  curl -X POST http://localhost:4994/api/products \
1910
82
  -H "Content-Type: application/json" \
1911
83
  -d '{"name":"Widget","price":9.99,"in_stock":true}'
1912
84
 
1913
- # 3. List with sort and filter
85
+ # List with filter + sort
1914
86
  curl "http://localhost:4994/api/products?sort=price&order=ASC&in_stock=1"
1915
-
1916
- # 4. Get one
1917
- curl http://localhost:4994/api/products/<id>
1918
-
1919
- # 5. Update
1920
- curl -X PUT http://localhost:4994/api/products/<id> \
1921
- -H "Content-Type: application/json" \
1922
- -d '{"price":14.99}'
1923
-
1924
- # 6. Delete
1925
- curl -X DELETE http://localhost:4994/api/products/<id>
1926
- ```
1927
-
1928
- ---
1929
-
1930
- ### Uploading and Downloading Files
1931
-
1932
- ```bash
1933
- # Upload a public file
1934
- curl -X POST http://localhost:4994/files/upload \
1935
- -H "Authorization: Bearer $TOKEN" \
1936
- -F "file=@/path/to/photo.jpg" \
1937
- -F "visibility=public"
1938
-
1939
- # Download public file (no auth needed)
1940
- curl http://localhost:4994/files/<id>/download -o photo.jpg
1941
-
1942
- # Upload a private file
1943
- curl -X POST http://localhost:4994/files/upload \
1944
- -H "Authorization: Bearer $TOKEN" \
1945
- -F "file=@/path/to/document.pdf" \
1946
- -F "visibility=private"
1947
-
1948
- # Get a signed URL for the private file
1949
- curl http://localhost:4994/files/<id>/signed-url \
1950
- -H "Authorization: Bearer $TOKEN"
1951
-
1952
- # Download using the signed URL
1953
- curl "<signed-url>" -o document.pdf
1954
87
  ```
1955
88
 
1956
- ---
1957
-
1958
- ### Calling an AI Model
89
+ **3. Add AI** — configure a provider in the dashboard and call it:
1959
90
 
1960
91
  ```bash
1961
- # Generate a completion
1962
92
  curl -X POST http://localhost:4994/api/ai/generate \
1963
93
  -H "Content-Type: application/json" \
1964
- -d '{
1965
- "provider": "openai",
1966
- "model": "gpt-4o",
1967
- "prompt": "Write a one-sentence tagline for a productivity app.",
1968
- "maxTokens": 60
1969
- }'
1970
-
1971
- # Multi-turn chat
1972
- curl -X POST http://localhost:4994/api/ai/chat \
1973
- -H "Content-Type: application/json" \
1974
- -d '{
1975
- "provider": "anthropic",
1976
- "model": "claude-sonnet-4-6",
1977
- "messages": [
1978
- {"role": "user", "content": "What is the capital of France?"}
1979
- ]
1980
- }'
94
+ -d '{"provider":"openai","model":"gpt-4o","prompt":"Summarize this product catalog."}'
1981
95
  ```
1982
96
 
1983
97
  ---
1984
98
 
1985
- ### Creating and Running an AI Agent
1986
-
1987
- ```bash
1988
- # 1. Create an agent (admin required)
1989
- curl -X POST http://localhost:4994/admin/api/agents \
1990
- -H "Content-Type: application/json" \
1991
- -H "Authorization: Bearer $TOKEN" \
1992
- -d '{
1993
- "name": "Math Helper",
1994
- "provider": "openai",
1995
- "model": "gpt-4o",
1996
- "systemPrompt": "You are a precise math assistant. Use the calculator tool for all computations.",
1997
- "tools": ["calculate"],
1998
- "temperature": 0.2,
1999
- "maxIterations": 5
2000
- }'
99
+ ## CLI Commands
2001
100
 
2002
- # 2. Run the agent
2003
- curl -X POST http://localhost:4994/api/agents/<id>/run \
2004
- -H "Content-Type: application/json" \
2005
- -d '{"input": "What is (144 / 12) * 7.5 plus 33?"}'
2006
- ```
101
+ | Command | Description |
102
+ | --------------------------- | ------------------------------------------------- |
103
+ | `rotifex start` | Start the server (default port `4994`) |
104
+ | `rotifex start --port 4000` | Start on a custom port |
105
+ | `rotifex start --verbose` | Enable debug logging |
106
+ | `rotifex migrate` | Run pending database migrations |
107
+ | `rotifex reset-admin --yes` | Delete admin accounts and re-run first-time setup |
2007
108
 
2008
109
  ---
2009
110
 
2010
- ### Refreshing an Expired Token
111
+ ## Architecture
2011
112
 
2012
- ```bash
2013
- curl -X POST http://localhost:4994/auth/refresh \
2014
- -H "Content-Type: application/json" \
2015
- -d '{"refreshToken":"<your-refresh-token>"}'
113
+ ```
114
+ ┌─────────────────────────────────────────┐
115
+ │ Rotifex Server │
116
+ │ │
117
+ │ Fastify HTTP · Schema Engine (live) │
118
+ │ JWT Auth · SQLite (better-sqlite3│
119
+ │ File Storage · AI / Agent System │
120
+ │ │
121
+ │ ┌───────────────────────────────────┐ │
122
+ │ │ Admin SPA (React 19 + Vite) │ │
123
+ │ └───────────────────────────────────┘ │
124
+ └─────────────────────────────────────────┘
2016
125
  ```
2017
126
 
2018
- ---
127
+ - **Runtime:** Node.js ≥ 18, Fastify v5
128
+ - **Database:** SQLite via `better-sqlite3`
129
+ - **Frontend:** React 19 + Vite, served as a static SPA at `/`
130
+ - **AI providers:** OpenAI, Anthropic, Google Gemini, Ollama (local)
2019
131
 
2020
- ## 13. Notes for Documentation Generators
132
+ ---
2021
133
 
2022
- ### Docusaurus
134
+ ## Admin Dashboard
2023
135
 
2024
- - Place this file in `docs/` as `intro.md`.
2025
- - Split by `##` heading into separate files under `docs/api/`, `docs/guides/`, `docs/features/` for large doc sites.
2026
- - Add `sidebar_label` and `sidebar_position` frontmatter to each split file.
2027
- - All code blocks use fenced syntax compatible with Docusaurus's Prism highlighter.
2028
- - Tables use standard GFM pipe syntax — no conversion needed.
136
+ The built-in SPA at `http://localhost:4994` gives you:
2029
137
 
2030
- ### Mintlify
138
+ - **Dashboard** — live stats: schemas, records, users, files, storage, uptime
139
+ - **Database Schemas** — visual model builder, create and delete models on the fly
140
+ - **User Management** — list users, create accounts, reset passwords
141
+ - **File Browser** — browse, preview, download, and delete uploads
142
+ - **AI Integration** — configure providers, playground (generate + chat), agent builder
143
+ - **Server Logs** — real-time structured log viewer with level filtering
144
+ - **Settings** — edit all environment variables without touching files
2031
145
 
2032
- - Each `##` section maps to a separate `.mdx` page.
2033
- - API endpoint documentation can be supplemented by an `openapi.yaml` file generated from §3.
2034
- - Use `<Note>`, `<Warning>`, and `<Tip>` components for callouts when converting to Mintlify MDX.
146
+ ---
2035
147
 
2036
- ### GitBook
148
+ ## Full Documentation
2037
149
 
2038
- - Import this file directly. GitBook renders all standard Markdown including tables, code blocks, and nested lists.
2039
- - Use GitBook's `{% hint style="info" %}` blocks for the notes sections if converting to native GitBook format.
150
+ For the complete API reference, all endpoint details, deployment guides, example workflows, and configuration options see:
2040
151
 
2041
- ### Swagger / OpenAPI Mapping
152
+ **[README-final.md](README-final.md)**
2042
153
 
2043
- This document contains enough information to produce a complete `openapi.yaml`. Use this mapping:
154
+ Or visit the hosted docs at [rotifex-docs.vercel.app](https://rotifex-docs.vercel.app/)
2044
155
 
2045
- | Section | OpenAPI Component |
2046
- | -------------------- | ------------------------------------------------ |
2047
- | §3.2 Auth endpoints | `paths` under `/auth/*` |
2048
- | §3.3 CRUD endpoints | `paths` under `/api/{table}` |
2049
- | §3.4 File endpoints | `paths` under `/files/*` |
2050
- | §3.5 AI endpoints | `paths` under `/api/ai/*` |
2051
- | §3.6 Agent endpoints | `paths` under `/api/agents/*` |
2052
- | §3.7 Admin endpoints | `paths` under `/admin/api/*` |
2053
- | §5 Data Structures | `components/schemas` |
2054
- | §9 Error codes | `components/responses` |
2055
- | §4 Auth headers | `components/securitySchemes` (BearerAuth, HS256) |
156
+ ---
2056
157
 
2057
- ### General Formatting Notes
158
+ ## License
2058
159
 
2059
- - All code blocks specify a language (`bash`, `json`, `nginx`) for syntax highlighting.
2060
- - Headings use a strict hierarchy (`#` -> `##` -> `###` -> `####`) for correct TOC generation.
2061
- - Internal anchor links use lowercase slugs compatible with GitHub, Docusaurus, Mintlify, and GitBook.
2062
- - No HTML tags are used — the file is pure Markdown for maximum portability.
160
+ MIT