zyket 1.2.18 → 1.2.20

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 (74) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/AGENTS.md +407 -0
  3. package/README.md +175 -279
  4. package/SECURITY.md +40 -0
  5. package/bin/cli.js +310 -202
  6. package/index.js +4 -3
  7. package/package.json +7 -1
  8. package/plugin/.claude-plugin/plugin.json +2 -2
  9. package/plugin/skills/build/SKILL.md +88 -0
  10. package/src/extensions/bullboard/index.js +14 -3
  11. package/src/extensions/interactive-storage/index.js +25 -9
  12. package/src/extensions/interactive-storage/routes/delete-folder.js +13 -1
  13. package/src/extensions/interactive-storage/routes/delete.js +14 -3
  14. package/src/extensions/interactive-storage/routes/download.js +22 -3
  15. package/src/extensions/interactive-storage/routes/info.js +13 -0
  16. package/src/services/auth/index.js +46 -28
  17. package/src/services/express/Express.js +32 -15
  18. package/src/services/express/RequireAdminMiddleware.js +14 -0
  19. package/src/services/express/RequireAuthMiddleware.js +46 -0
  20. package/src/services/express/index.js +7 -5
  21. package/src/services/socketio/AuthGuard.js +33 -0
  22. package/src/services/socketio/SocketIO.js +3 -1
  23. package/src/services/socketio/index.js +2 -1
  24. package/src/services/template-manager/index.js +1 -0
  25. package/src/templates/api-rest/.env.example +24 -0
  26. package/src/templates/api-rest/README.md +50 -0
  27. package/src/templates/api-rest/index.js +18 -0
  28. package/src/templates/api-rest/src/models/Task.js +18 -0
  29. package/src/templates/api-rest/src/routes/tasks/[id].js +42 -0
  30. package/src/templates/api-rest/src/routes/tasks/index.js +26 -0
  31. package/src/templates/api-rest/src/services/auth/auth.js +9 -0
  32. package/src/templates/api-rest/src/services/auth/index.js +23 -0
  33. package/src/templates/realtime-chat/.env.example +26 -0
  34. package/src/templates/realtime-chat/README.md +38 -0
  35. package/src/templates/realtime-chat/frontend/.env.example +3 -0
  36. package/src/templates/realtime-chat/frontend/index.html +12 -0
  37. package/src/templates/realtime-chat/frontend/main.jsx +18 -0
  38. package/src/templates/realtime-chat/frontend/src/hooks/useAuth.jsx +27 -0
  39. package/src/templates/realtime-chat/frontend/src/hooks/useChatSocket.jsx +29 -0
  40. package/src/templates/realtime-chat/frontend/src/middlewares/LoggedMiddleware.jsx +12 -0
  41. package/src/templates/realtime-chat/frontend/src/middlewares/NotLoggedMiddleware.jsx +12 -0
  42. package/src/templates/realtime-chat/frontend/src/store/storeAuth.jsx +11 -0
  43. package/src/templates/realtime-chat/frontend/src/views/AuthView.jsx +70 -0
  44. package/src/templates/realtime-chat/frontend/src/views/ChatView.jsx +69 -0
  45. package/src/templates/realtime-chat/frontend/styles.css +1 -0
  46. package/src/templates/realtime-chat/frontend/vite.config.js +7 -0
  47. package/src/templates/realtime-chat/index.js +14 -0
  48. package/src/templates/realtime-chat/src/guards/auth.js +3 -0
  49. package/src/templates/realtime-chat/src/handlers/connection.js +23 -0
  50. package/src/templates/realtime-chat/src/handlers/message.js +29 -0
  51. package/src/templates/realtime-chat/src/services/auth/auth.js +8 -0
  52. package/src/templates/realtime-chat/src/services/auth/index.js +19 -0
  53. package/src/templates/saas-multitenant/.env.example +22 -0
  54. package/src/templates/saas-multitenant/README.md +71 -0
  55. package/src/templates/saas-multitenant/frontend/.env.example +3 -0
  56. package/src/templates/saas-multitenant/frontend/index.html +12 -0
  57. package/src/templates/saas-multitenant/frontend/main.jsx +18 -0
  58. package/src/templates/saas-multitenant/frontend/src/hooks/useAuth.jsx +27 -0
  59. package/src/templates/saas-multitenant/frontend/src/hooks/useProjects.jsx +41 -0
  60. package/src/templates/saas-multitenant/frontend/src/middlewares/LoggedMiddleware.jsx +12 -0
  61. package/src/templates/saas-multitenant/frontend/src/middlewares/NotLoggedMiddleware.jsx +12 -0
  62. package/src/templates/saas-multitenant/frontend/src/store/storeAuth.jsx +13 -0
  63. package/src/templates/saas-multitenant/frontend/src/views/AuthView.jsx +70 -0
  64. package/src/templates/saas-multitenant/frontend/src/views/DashboardView.jsx +131 -0
  65. package/src/templates/saas-multitenant/frontend/styles.css +1 -0
  66. package/src/templates/saas-multitenant/frontend/vite.config.js +7 -0
  67. package/src/templates/saas-multitenant/index.js +14 -0
  68. package/src/templates/saas-multitenant/src/middlewares/RequireOrganization.js +22 -0
  69. package/src/templates/saas-multitenant/src/models/Project.js +17 -0
  70. package/src/templates/saas-multitenant/src/routes/admin/stats.js +15 -0
  71. package/src/templates/saas-multitenant/src/routes/projects/index.js +34 -0
  72. package/src/templates/saas-multitenant/src/services/auth/auth.js +8 -0
  73. package/src/templates/saas-multitenant/src/services/auth/index.js +43 -0
  74. package/src/utils/EnvManager.js +23 -0
@@ -10,8 +10,8 @@
10
10
  {
11
11
  "name": "zyket",
12
12
  "source": "./plugin",
13
- "description": "Generate Zyket framework components handlers, guards, routes, middlewares, services, events, schedulers, workers, and extensions.",
14
- "version": "1.0.0"
13
+ "description": "Build and scaffold Zyket appsinitialize from templates, wire services/auth/security (build), and generate components like routes, handlers, guards, services, and workers (generate).",
14
+ "version": "1.1.0"
15
15
  }
16
16
  ]
17
17
  }
package/AGENTS.md ADDED
@@ -0,0 +1,407 @@
1
+ # AGENTS.md — Zyket Framework Reference
2
+
3
+ > A complete, accurate reference for AI agents building applications with **Zyket**.
4
+ > Zyket is a service-oriented Node.js framework (Express 5 + Socket.IO + better-auth + Sequelize/BullMQ/MinIO) inspired by Symfony's architecture. Components are auto-discovered from the filesystem; services are wired through a dependency-injection container.
5
+
6
+ This document is the source of truth. When an installed copy exists, read `node_modules/zyket/AGENTS.md`. Always confirm a base class API against the installed source before relying on it.
7
+
8
+ ---
9
+
10
+ ## 1. Quick start
11
+
12
+ ```bash
13
+ mkdir my-app && cd my-app
14
+ npm install zyket
15
+ npx zyket init # interactive: pick a template
16
+ # or non-interactive:
17
+ npx zyket init api-rest # default | api-rest | saas-multitenant | realtime-chat
18
+ ```
19
+
20
+ `init` scaffolds the chosen template's files, generates a `.env` (defaults merged with the template's `.env.example`), writes a `package.json` with the right dependencies, and runs `npm install`. Templates that ship auth need their tables created once:
21
+
22
+ ```bash
23
+ npx @better-auth/cli migrate
24
+ node index.js
25
+ ```
26
+
27
+ Minimal manual boot:
28
+
29
+ ```js
30
+ // index.js
31
+ const { Kernel } = require("zyket");
32
+
33
+ const kernel = new Kernel({
34
+ services: [
35
+ ["auth", require("./src/services/auth"), ["@service_container"]],
36
+ ],
37
+ });
38
+
39
+ kernel.boot().then(() => console.log("Booted")).catch(console.error);
40
+ ```
41
+
42
+ ---
43
+
44
+ ## 2. The Kernel
45
+
46
+ ```js
47
+ new Kernel({ services: [], extensions: [] })
48
+ kernel.boot(clearConsole = true, secretsPath = "<cwd>/.env")
49
+ ```
50
+
51
+ Boot order:
52
+ 1. Load `.env` (creates one with defaults if missing).
53
+ 2. Start the HTTP server on `PORT` (default 3000).
54
+ 3. Register **core services** (auto-selected from env, see §4) then your `services`.
55
+ 4. Call `boot({ httpServer })` on every service in order.
56
+ 5. Load each `extensions` instance via `extension.load(container)` (after services).
57
+
58
+ `kernel.container` exposes the DI container (`container.get('name')`, `container.has('name')`).
59
+
60
+ **Service registration tuple:** `[name, ClassOrFactory, args]`. Special arg placeholders:
61
+
62
+ | Placeholder | Injected value |
63
+ |-------------|----------------|
64
+ | `"@service_container"` | the DI container |
65
+ | `"@onConnection"` | the kernel's socket-connection callback |
66
+
67
+ Auth is **not** a core service — register it yourself (templates do).
68
+
69
+ ---
70
+
71
+ ## 3. Environment variables
72
+
73
+ The generated `.env` (`src/utils/EnvManager.js`) plus auth/vite variables:
74
+
75
+ | Variable | Default | Purpose |
76
+ |----------|---------|---------|
77
+ | `DEBUG` | `true` | Enable debug-level logs |
78
+ | `PORT` | `3000` | HTTP/WS port |
79
+ | `NODE_ENV` | — | `production` enables secure cookies, Vite preview build |
80
+ | `HTTP_JSON_LIMIT` | `10mb` | Express JSON body limit |
81
+ | `SOCKET_MAX_HTTP_BUFFER_SIZE` | `10485760` | Socket.IO max payload bytes |
82
+ | `DISABLE_EXPRESS` | `false` | Turn off the HTTP/REST service |
83
+ | `DISABLE_SOCKET` | `true` | Turn off Socket.IO |
84
+ | `DISABLE_VITE` | `true` | Turn off the Vite frontend |
85
+ | `DISABLE_EVENTS` | `true` | Turn off the events service |
86
+ | `DISABLE_BULLMQ` | `true` | Turn off BullMQ queues/workers |
87
+ | `DISABLE_SCHEDULER` | `true` | Turn off the cron scheduler |
88
+ | `DISABLE_LOGGER` | `false` | Silence the logger |
89
+ | `DISABLE_SWAGGER` | — | `true` removes the `/docs` UI |
90
+ | `SWAGGER_PASSWORD` / `SWAGGER_USER` | — / `admin` | HTTP Basic auth for `/docs` |
91
+ | `SWAGGER_PATH` | `/docs` | Swagger UI mount path |
92
+ | `DATABASE_URL` | `./database.sqlite` | DB connection / sqlite path. Activates the `database` service when set |
93
+ | `DATABASE_DIALECT` | `sqlite` | `sqlite`, `postgresql`, `mariadb`… (auth supports only `sqlite`/`postgresql`) |
94
+ | `CACHE_URL` | `` | `redis://…` for Redis, empty/`memory` for in-memory |
95
+ | `REDIS_URL` | — | Enables the Socket.IO Redis adapter (multi-instance) |
96
+ | `S3_ENDPOINT` `S3_PORT` `S3_USE_SSL` `S3_ACCESS_KEY` `S3_SECRET_KEY` | — | MinIO/S3. The `s3` service activates when endpoint+keys are set |
97
+ | `S3_PUBLIC_BUCKETS` / `S3_PRIVATE_BUCKETS` | — | Comma lists; public buckets get a read-all policy |
98
+ | `LOG_DIRECTORY` | `./logs` | Log file directory |
99
+ | `QUEUES` | `` | Comma list of BullMQ queue names to create |
100
+ | `VITE_ROOT` | `./frontend` | Frontend project root |
101
+ | `VITE_PORT` | `5173` | Vite dev/preview port |
102
+ | `VITE_API_BASE` | `http://localhost:3000` | Frontend → backend base URL |
103
+ | **Auth** (added by the auth service) | | |
104
+ | `AUTH_SECRET` | random 32-byte hex | Session signing secret. **Boot fails if missing/insecure** |
105
+ | `BETTER_AUTH_URL` | `http://localhost:3000` | Public base URL of the auth server |
106
+ | `TRUSTED_ORIGINS` | `http://localhost:5173,http://localhost:3000` | Comma list of allowed origins (CORS + CSRF) |
107
+ | `AUTH_CROSS_DOMAIN` | `false` | `true` → cross-site cookies (`sameSite=none; secure`, needs HTTPS) |
108
+ | `BULLBOARD_ADMIN_PASSWORD` | — | Required to expose the BullBoard dashboard extension |
109
+
110
+ ---
111
+
112
+ ## 4. Services
113
+
114
+ Core services are auto-registered based on env (`src/services/index.js`). Access any with `container.get('<name>')`.
115
+
116
+ | Name | Activated when | Key API |
117
+ |------|----------------|---------|
118
+ | `logger` | always (silent if `DISABLE_LOGGER=true`) | `info/warn/error/debug(msg)` |
119
+ | `template-manager` | always | internal (templates) |
120
+ | `events` | `DISABLE_EVENTS !== 'true'` | `emit(name, payload)`, `emitAsync(name, payload, timeout)` |
121
+ | `database` | `DATABASE_URL` set | `models`, `sequelize`, `Op`, `Sequelize`, `sync()`, `runMigrations(path)`, `loadModel(fn)` |
122
+ | `cache` | always | `get/set/del/keys/expire`, `client` (redis or null) |
123
+ | `s3` | `S3_ENDPOINT` + access + secret | `saveFile/getFile/removeFile/ensureBucket/listBuckets`, `client` (MinIO) |
124
+ | `scheduler` | `DISABLE_SCHEDULER !== 'true'` | discovers `src/schedulers/*` |
125
+ | `bullmq` | `DISABLE_BULLMQ !== 'true'` | `queues`, `addJob(queue, name, data, opts, waitForCompletion)` |
126
+ | `socketio` | `DISABLE_SOCKET !== 'true'` | `io`, `sockets` |
127
+ | `vite` | `VITE_ROOT` set + `DISABLE_VITE !== 'true'` | serves `frontend/` |
128
+ | `express` | `DISABLE_EXPRESS !== 'true'` | `app()`, `registerRoutes(routes)`, `regiterRawAllRoutes(path, handler)` |
129
+ | `auth` | **manual registration** | `client` (better-auth), `getSession(headers)` |
130
+
131
+ > Note: `bullmq`, `scheduler`, `events` default to ON when their `DISABLE_*` var is absent, but the generated `.env` ships them as `true` (off). Set the relevant `DISABLE_*=false` to enable.
132
+
133
+ ### Custom service
134
+
135
+ ```js
136
+ const { Service } = require("zyket");
137
+
138
+ module.exports = class MyService extends Service {
139
+ #container;
140
+ constructor(container) { super("my-service"); this.#container = container; }
141
+ async boot({ httpServer } = {}) { /* init */ }
142
+ doThing() { /* ... */ }
143
+ };
144
+ ```
145
+
146
+ Register: `services: [["my-service", MyService, ["@service_container"]]]`.
147
+
148
+ ---
149
+
150
+ ## 5. Auto-discovered components
151
+
152
+ These are discovered from folders under `src/` — the **filename is the identity**. Use kebab-case, `module.exports = class extends Base`, and do not add constructor params (the loader passes only the derived name).
153
+
154
+ | Component | Folder | Base | Identity from filename |
155
+ |-----------|--------|------|------------------------|
156
+ | Route | `src/routes/` | `Route` | URL path |
157
+ | Middleware | `src/middlewares/` | `Middleware` | referenced manually |
158
+ | Handler | `src/handlers/` | `Handler` | socket event name |
159
+ | Guard | `src/guards/` | `Guard` | guard name |
160
+ | Event | `src/events/` | `Event` | event name |
161
+ | Scheduler | `src/schedulers/` | `Schedule` | label |
162
+ | Worker | `src/workers/` | `Worker` | label |
163
+ | Model | `src/models/` | factory fn | model name |
164
+
165
+ ### Routes (`src/routes/**.js`)
166
+
167
+ File path → URL: `index.js`→`/`, `users.js`→`/users`, `users/[id].js`→`/users/:id`. Methods: **`get`, `post`, `put`, `delete`** (PATCH is not dispatched).
168
+
169
+ ```js
170
+ const { Route, RequireAuthMiddleware } = require("zyket");
171
+
172
+ module.exports = class extends Route {
173
+ middlewares = { post: [new RequireAuthMiddleware()] }; // per-method
174
+
175
+ async get({ container, request, response }) {
176
+ return { items: [] }; // → { success: true, items: [] }
177
+ }
178
+ async post({ container, request }) {
179
+ if (!request.body.name) return { success: false, message: "name required", status: 400 };
180
+ return { created: true, status: 201 };
181
+ }
182
+ };
183
+ ```
184
+
185
+ Return conventions: object → `{ success: true, ...obj }` JSON; `status` key sets HTTP code; `Buffer` → file download; `new RedirectResponse(url)` → redirect. Thrown errors → `500 { success:false, message:"Internal Server Error" }` (details only in logs).
186
+
187
+ ### Middlewares (`src/middlewares/*.js`)
188
+
189
+ ```js
190
+ const { Middleware } = require("zyket");
191
+ module.exports = class extends Middleware {
192
+ async handle({ container, request, response, next }) {
193
+ // respond to block, or call next() to continue
194
+ next();
195
+ }
196
+ };
197
+ ```
198
+
199
+ ### Socket.IO handlers (`src/handlers/*.js`) & guards (`src/guards/*.js`)
200
+
201
+ ```js
202
+ // src/handlers/message.js → listens to the "message" event
203
+ const { Handler } = require("zyket");
204
+ module.exports = class extends Handler {
205
+ guards = ["auth"]; // run before handle; throw to block
206
+ async handle({ container, socket, data, io }) {
207
+ io.to("room").emit("message", data);
208
+ return { ok: true }; // returned to the client ack callback
209
+ }
210
+ };
211
+ ```
212
+
213
+ ```js
214
+ // src/guards/auth.js
215
+ const { Guard } = require("zyket");
216
+ module.exports = class extends Guard {
217
+ async handle({ container, socket, io }) {
218
+ if (!ok) throw new Error("Unauthorized"); // blocks the event
219
+ }
220
+ };
221
+ ```
222
+
223
+ > `connection.js` is special (runs on connect). **Connection-level guards are not awaited and do not reliably block** — enforce connect-time auth inside `connection.js` itself (throwing there disconnects the socket).
224
+
225
+ ### Events (`src/events/*.js`)
226
+
227
+ ```js
228
+ const { Event } = require("zyket");
229
+ module.exports = class extends Event {
230
+ async handle({ container, payload }) { /* ... */ }
231
+ };
232
+ // elsewhere: container.get('events').emit('user-registered', { id })
233
+ ```
234
+
235
+ ### Scheduler (`src/schedulers/*.js`)
236
+
237
+ ```js
238
+ const { Schedule } = require("zyket");
239
+ module.exports = class extends Schedule {
240
+ time = "*/5 * * * *"; // node-cron expression
241
+ async handle({ container }) { /* ... */ }
242
+ };
243
+ ```
244
+
245
+ ### Worker (`src/workers/*.js`) — BullMQ
246
+
247
+ ```js
248
+ const { Worker } = require("zyket");
249
+ module.exports = class extends Worker {
250
+ queueName = "emails"; // must match a name in QUEUES
251
+ instances = 1; // number | array | async fn
252
+ options = { concurrency: 5 }; // BullMQ worker options | fn
253
+ async handle({ container, job, instance, index }) { /* process job.data */ }
254
+ };
255
+ // enqueue: container.get('bullmq').addJob('emails', 'welcome', { to })
256
+ ```
257
+
258
+ ### Model (`src/models/*.js`) — Sequelize factory
259
+
260
+ ```js
261
+ module.exports = ({ sequelize, Sequelize, container }) => {
262
+ const User = sequelize.define("User", {
263
+ email: { type: Sequelize.STRING, allowNull: false },
264
+ });
265
+ User.associate = (models) => { /* User.hasMany(models.Post) */ };
266
+ return User;
267
+ };
268
+ // access: container.get('database').models.User ; create tables: .sync()
269
+ ```
270
+
271
+ ---
272
+
273
+ ## 6. Authentication (better-auth)
274
+
275
+ The `auth` service mounts better-auth at `/api/auth/*` and provides sessions, the **admin** plugin, the **bearer** plugin, and (optionally) the **organization** plugin. Customize by subclassing `AuthService`:
276
+
277
+ ```js
278
+ const { AuthService } = require("zyket");
279
+
280
+ module.exports = class CustomAuthService extends AuthService {
281
+ get organizationEnabled() { return true; } // multi-tenant
282
+ get requireEmailVerification() { return false; }
283
+ get plugins() { return []; } // extra better-auth plugins
284
+ get socialProviders() { return {}; }
285
+ get userAdditionalFields() { return {}; }
286
+ get organizationAdditionalFields() { return {}; }
287
+ async allowUserToCreateOrganization(user) { return true; }
288
+ async sendVerificationEmail({ user, url, token }) { /* email */ }
289
+ async sendResetPasswordEmail({ user, url, token }) { /* email */ }
290
+ async sendInvitationEmail(data) { /* org invite */ }
291
+ };
292
+ ```
293
+
294
+ Requirements: `DATABASE_DIALECT` must be `sqlite` or `postgresql`. Run `npx @better-auth/cli migrate` to create auth tables (it reads `src/services/auth/auth.js`).
295
+
296
+ ### Protecting routes & sockets
297
+
298
+ | Helper | Use |
299
+ |--------|-----|
300
+ | `RequireAuthMiddleware` | route middleware; requires a session. `new RequireAuthMiddleware(['admin'])` to gate by role. Sets `request.user` / `request.session` |
301
+ | `RequireAdminMiddleware` | route middleware; requires `role === 'admin'` |
302
+ | `AuthGuard` | socket guard; rejects events without a session. Use `module.exports = require('zyket').AuthGuard` in `src/guards/auth.js` |
303
+ | `container.get('auth').getSession(headers)` | resolve a session from raw headers (e.g. `socket.handshake.headers`) without importing better-auth |
304
+
305
+ ### Cookies (environment-aware)
306
+
307
+ Default: `sameSite=lax`, `secure` only in production → works over `http://localhost` when frontend (`:5173`) and API (`:3000`) are same-site. For front/back on **different domains**, set `AUTH_CROSS_DOMAIN=true` (HTTPS required) → `sameSite=none; secure` + cross-subdomain cookies.
308
+
309
+ ### Frontend auth client
310
+
311
+ ```js
312
+ import { createAuthClient } from "better-auth/react";
313
+ import { organizationClient, adminClient } from "better-auth/client/plugins";
314
+
315
+ export const client = createAuthClient({
316
+ baseURL: `${import.meta.env.VITE_API_BASE || "http://localhost:3000"}/api/auth/`,
317
+ plugins: [organizationClient(), adminClient()],
318
+ });
319
+ ```
320
+
321
+ ---
322
+
323
+ ## 7. Extensions
324
+
325
+ Post-boot plugins with full container access. Register instances in the Kernel.
326
+
327
+ ```js
328
+ const { Kernel, BullBoardExtension, InteractiveStorageExtension } = require("zyket");
329
+
330
+ new Kernel({
331
+ extensions: [
332
+ new BullBoardExtension({ path: "/bullboard", middlewares: [] }), // needs BULLBOARD_ADMIN_PASSWORD or middlewares, else it won't mount
333
+ new InteractiveStorageExtension({ path: "/storage", bucketName: "dropbox", middlewares: [/* auth */], maxDeleteBatch: 100 }),
334
+ ],
335
+ });
336
+ ```
337
+
338
+ Custom extension:
339
+
340
+ ```js
341
+ const { Extension } = require("zyket");
342
+ module.exports = class extends Extension {
343
+ constructor(options = {}) { super("MyExtension"); this.options = options; }
344
+ async load(container) {
345
+ if (!container.get("express")) return container.get("logger").warn("no express");
346
+ container.get("express").registerRoutes([/* Route instances */]);
347
+ }
348
+ };
349
+ ```
350
+
351
+ ---
352
+
353
+ ## 8. Frontend (Vite + React)
354
+
355
+ When `VITE_ROOT` is set and `DISABLE_VITE=false`, Zyket runs Vite (dev server, or a built preview server in production) for the `frontend/` app. Stack: React 19 + react-router-dom + zustand + Tailwind v4 + better-auth client. The frontend talks to the backend at `VITE_API_BASE`. A project with a frontend must declare its own frontend dependencies (the CLI does this automatically per template).
356
+
357
+ ---
358
+
359
+ ## 9. Templates
360
+
361
+ | Template | Stack | Use for |
362
+ |----------|-------|---------|
363
+ | `default` | backend + (optional) React frontend | general starter |
364
+ | `api-rest` | backend only, auth + CRUD (`Task`) | clean REST APIs |
365
+ | `saas-multitenant` | orgs/roles + React dashboard, tenant-scoped `Project` | multi-tenant SaaS |
366
+ | `realtime-chat` | Socket.IO + React chat UI, session auth | real-time apps |
367
+
368
+ Each template is self-contained (`index.js`, `.env.example`, `README.md`, and frontend where relevant). `npx zyket init <template>` scaffolds it.
369
+
370
+ ---
371
+
372
+ ## 10. CLI
373
+
374
+ ```bash
375
+ npx zyket # interactive menu
376
+ npx zyket init # choose a template, scaffold + install + (default) run
377
+ npx zyket init <template> # scaffold a specific template
378
+ ```
379
+
380
+ `init` keeps existing files (non-destructive). For non-default templates it prints next steps (auth migrate + run) instead of auto-starting.
381
+
382
+ ---
383
+
384
+ ## 11. Security checklist
385
+
386
+ See `SECURITY.md` for the full audit. Essentials before production:
387
+
388
+ - Keep `.env` out of git; never reuse `AUTH_SECRET` (it's generated per project; boot fails on a known/empty value).
389
+ - Protect every non-public route/socket with `RequireAuthMiddleware` / `AuthGuard`.
390
+ - Set `BULLBOARD_ADMIN_PASSWORD` (or pass `middlewares`) — the dashboard won't mount without auth.
391
+ - Set `SWAGGER_PASSWORD` (or `DISABLE_SWAGGER=true`) in production.
392
+ - Lock `TRUSTED_ORIGINS`; set `AUTH_CROSS_DOMAIN=true` only for real cross-domain HTTPS deployments.
393
+ - Add rate limiting and `helmet` (not bundled).
394
+ - For the storage extension, always pass auth `middlewares`; tune `maxFileSize` / `maxDeleteBatch`.
395
+
396
+ ---
397
+
398
+ ## 12. Build-a-feature recipe
399
+
400
+ 1. **Model** — `src/models/Thing.js` (Sequelize factory). Create tables with `container.get('database').sync()` in `index.js` after boot, or migrations.
401
+ 2. **Routes** — `src/routes/things/index.js` (GET list, POST create) and `src/routes/things/[id].js` (GET/PUT/DELETE). Gate with `new RequireAuthMiddleware()`.
402
+ 3. **Access data** — `const { Thing } = container.get('database').models;`.
403
+ 4. **Realtime (optional)** — `src/handlers/<event>.js` with `guards = ["auth"]`; broadcast with `io`.
404
+ 5. **Background (optional)** — add the queue to `QUEUES`, `DISABLE_BULLMQ=false`, add `src/workers/<name>.js`, enqueue with `container.get('bullmq').addJob(...)`.
405
+ 6. **Run** — `npx @better-auth/cli migrate` (first time) then `node index.js`.
406
+
407
+ To scaffold any component quickly, use the `generate` skill (`/zyket:generate route things/[id]`).