zyket 1.2.19 → 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.
@@ -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]`).
package/README.md CHANGED
@@ -1,279 +1,175 @@
1
- # Zyket
2
-
3
- Zyket is a Node.js framework designed to simplify the development of real-time applications with Socket.IO and Express. Inspired by the structured approach of frameworks like Symfony, Zyket provides a robust, service-oriented architecture for building scalable and maintainable server-side applications.
4
-
5
- Upon initial boot, Zyket automatically scaffolds a default project structure, including handlers, routes, and configuration files, allowing you to get started immediately.
6
-
7
- ## Getting Started
8
-
9
- ### Quick Start (Recommended)
10
-
11
- The easiest way to get started with Zyket is to use the CLI initialization command:
12
-
13
- ```bash
14
- # Create a new directory for your project
15
- mkdir my-zyket-app
16
- cd my-zyket-app
17
-
18
- # Install zyket
19
- npm install zyket
20
-
21
- # Initialize the project (one command!)
22
- npx zyket init
23
-
24
- # Start your application
25
- npm run dev
26
- ```
27
-
28
- The `init` command will:
29
- - Create an `index.js` file with the kernel boilerplate
30
- - Generate a `.env` file with sensible defaults
31
- - Create a `package.json` if one doesn't exist
32
- - Set up your project ready to run
33
-
34
- You can also run `npx zyket` without arguments for an interactive menu with more options.
35
-
36
- ### Manual Setup (Alternative)
37
-
38
- If you prefer to set up manually, install Zyket in your project:
39
-
40
- ```bash
41
- npm i zyket
42
- ```
43
-
44
- Then, create an `index.js` file and boot the Zyket Kernel:
45
-
46
- ```javascript
47
- // index.js
48
- const { Kernel } = require("zyket");
49
-
50
- // Instantiate the kernel
51
- const kernel = new Kernel();
52
-
53
- // Boot the kernel to start all services
54
- kernel.boot().then(() => {
55
- console.log("Kernel booted successfully!");
56
- });
57
- ```
58
-
59
- When you run this for the first time, Zyket will create a default `.env` file and a `src` directory containing boilerplate for routes, handlers, guards, and middlewares.
60
-
61
- ## Core Concepts
62
-
63
- Zyket is built around a few key architectural concepts:
64
-
65
- * **Socket.IO Handlers & Guards**: For managing real-time WebSocket events and their authorization.
66
- * **Express Routes & Middlewares**: For handling traditional RESTful API endpoints.
67
- * **Services**: Reusable components that are managed by a dependency injection container.
68
- * **CLI**: A command-line tool to scaffold features from templates.
69
-
70
- ### Socket.IO Development
71
-
72
- #### Handlers
73
-
74
- Handlers are classes that process incoming Socket.IO events. The name of the handler file (e.g., `message.js`) determines the event it listens to (`message`).
75
-
76
- ```javascript
77
- // src/handlers/message.js
78
- const { Handler } = require("zyket");
79
-
80
- module.exports = class MessageHandler extends Handler {
81
- // Array of guard names to execute before the handler
82
- guards = ["default"];
83
-
84
- async handle({ container, socket, data, io }) {
85
- const logger = container.get("logger");
86
- logger.info(`Received message: ${JSON.stringify(data)}`);
87
- socket.emit("response", { received: data });
88
- }
89
- };
90
- ```
91
-
92
- #### Guards
93
-
94
- Guards are used to protect Socket.IO handlers or the initial connection. They run before the handler's `handle` method and are ideal for authorization logic.
95
-
96
- ```javascript
97
- // src/guards/default.js
98
- const { Guard } = require("zyket");
99
-
100
- module.exports = class DefaultGuard extends Guard {
101
- async handle({ container, socket, io }) {
102
- container.get("logger").info(`Executing default guard for socket ${socket.id}`);
103
-
104
- // Example: Check for an auth token. If invalid, disconnect the user.
105
- // if (!socket.token) {
106
- // socket.disconnect();
107
- // }
108
- }
109
- };
110
- ```
111
-
112
- ### Express API Development
113
-
114
- #### Routes
115
-
116
- Routes handle HTTP requests. The file path maps directly to the URL endpoint. For example, `src/routes/index.js` handles requests to `/`, and `src/routes/[test]/message.js` handles requests to `/:test/message`.
117
-
118
- ```javascript
119
- // src/routes/index.js
120
- const { Route } = require("zyket");
121
- const DefaultMiddleware = require("../middlewares/default");
122
-
123
- module.exports = class DefaultRoute extends Route {
124
- // Apply middlewares to specific HTTP methods
125
- middlewares = {
126
- get: [ new DefaultMiddleware() ],
127
- post: [ new DefaultMiddleware() ]
128
- }
129
-
130
- async get({ container, request }) {
131
- container.get("logger").info("Default route GET");
132
- return { test: "Hello World GET!" };
133
- }
134
-
135
- async post({ container, request }) {
136
- container.get("logger").info("Default route POST");
137
- return { test: "Hello World POST!" };
138
- }
139
- };
140
- ```
141
-
142
- #### Middlewares
143
-
144
- Middlewares process the request before it reaches the route handler. They follow the standard Express middleware pattern.
145
-
146
- ```javascript
147
- // src/middlewares/default.js
148
- const { Middleware } = require("zyket");
149
-
150
- module.exports = class DefaultMiddleware extends Middleware {
151
- async handle({ container, request, response, next }) {
152
- container.get("logger").info("Default Express middleware executing");
153
- next(); // Pass control to the next middleware or route handler
154
- }
155
- };
156
- ```
157
-
158
- ## Services
159
-
160
- Services are the cornerstone of Zyket's architecture, providing reusable functionality across your application. Zyket includes several default services and allows you to register your own.
161
-
162
- ### Custom Services
163
-
164
- You can create your own services by extending the `Service` class and registering it with the Kernel.
165
-
166
- ```javascript
167
- // src/services/MyCustomService.js
168
- const { Service } = require("zyket");
169
-
170
- module.exports = class MyCustomService extends Service {
171
- constructor() {
172
- super("my-custom-service");
173
- }
174
-
175
- async boot() {
176
- console.log("MyCustomService has been booted!");
177
- }
178
-
179
- doSomething() {
180
- return "Something was done.";
181
- }
182
- }
183
- ```
184
-
185
- Register the service in your main `index.js` file:
186
-
187
- ```javascript
188
- // index.js
189
- const { Kernel } = require("zyket");
190
- const MyCustomService = require("./src/services/MyCustomService");
191
-
192
- const kernel = new Kernel({
193
- services: [
194
- // [name, class, [constructor_args]]
195
- ["my-service", MyCustomService, []]
196
- ]
197
- });
198
-
199
- kernel.boot();
200
- ```
201
- Services are reusable components specified in the kernel configuration. Each service must include a boot() function that is executed when the kernel starts.
202
-
203
- ```javascript
204
- module.exports = class LoggerService {
205
- this.#container;
206
-
207
- boot(container, enableLogging = true) {
208
- this.#container = container;
209
- console.log("LoggerService Booted");
210
- }
211
-
212
- info(message) {
213
- if(!enableLogging) return;
214
- console.log(`[INFO]: ${message}`);
215
- }
216
- };
217
- ```
218
-
219
- Then, when booting the kernel, specify the service:
220
-
221
- ```javascript
222
- const { Kernel } = require("zyket");
223
- const LoggerService = require("./LoggerService");
224
-
225
- const kernel = new Kernel({
226
- services: [["logger", LoggerService, ['@service_container', true]],
227
- });
228
-
229
- kernel.boot().then(() => console.log(`Kernel Booted`));
230
- ```
231
-
232
- ## Default Services
233
- Zyket includes some default services that provide essential functionality. These services can be overridden or extended if needed.
234
-
235
- #### Cache Services
236
- - **Name** `cache`
237
- - **Description** Provides caching functionality using a Redis adapter.
238
- - **Configuration** Add `CACHE_URL` in your `.env` file to activate caching.
239
-
240
- #### Database Service
241
- - **Name** `database`
242
- - **Description** Manages database connections using a MariaDB/Sequelize adapter.
243
- - **Configuration** Add `DATABASE_URL` in your `.env` file to enable the database connection.
244
-
245
- #### S3 Service
246
- - **Name** `s3`
247
- - **Description** Provides S3-compatible object storage using MinIO.
248
- - **Configuration** Add the following variables in your `.env` file to enable the service
249
- - `S3_ENDPOINT`
250
- - `S3_PORT`
251
- - `S3_USE_SSL`
252
- - `S3_ACCESS_KEY`
253
- - `S3_SECRET_KEY`
254
-
255
- #### Logger Service
256
- - **Name** `logger`
257
- - **Description** Handles logging for the application.
258
- - **Configuration**
259
- - Change `LOG_DIRECTORY` in `.env` file to set a custom log directory.
260
- - Set `DEBUG` in `.env` file to enable or disable debug logging.
261
-
262
- #### Socket.io Service
263
- - **Name** `socketio`
264
- - **Description** Manages the WebSocket server.
265
- - **Configuration** Add `PORT` in your `.env` file to define the listening port for Socket.io.
266
-
267
-
268
- ## Contributing
269
-
270
- We welcome contributions from the community! If you'd like to improve Zyket, feel free to:
271
-
272
- - Report issues and suggest features on GitHub Issues
273
-
274
- - Submit pull requests with bug fixes or enhancements
275
-
276
- - Improve the documentation
277
-
278
- Let's build a better framework together! 🚀
279
-
1
+ # Zyket
2
+
3
+ Zyket is a service-oriented Node.js framework for building real-time and REST applications with **Express 5**, **Socket.IO**, **better-auth**, and **Sequelize/BullMQ/MinIO**. Inspired by Symfony, it pairs a dependency-injection container with filesystem-based auto-discovery: drop a file in the right folder and the framework wires it up.
4
+
5
+ > **Building with an AI agent?** See **[AGENTS.md](AGENTS.md)** for a complete, machine-readable reference, and **[SECURITY.md](SECURITY.md)** for hardening guidance.
6
+
7
+ ## Getting started
8
+
9
+ ```bash
10
+ mkdir my-app && cd my-app
11
+ npm install zyket
12
+
13
+ # Initialize from a template (interactive picker)
14
+ npx zyket init
15
+ # …or pick one directly:
16
+ npx zyket init api-rest # default | api-rest | saas-multitenant | realtime-chat
17
+ ```
18
+
19
+ `init` scaffolds the template, generates a `.env`, writes `package.json` with the right dependencies, and installs them. Templates that ship authentication need their tables created once:
20
+
21
+ ```bash
22
+ npx @better-auth/cli migrate
23
+ node index.js
24
+ ```
25
+
26
+ ### Templates
27
+
28
+ | Template | What you get |
29
+ |----------|--------------|
30
+ | `default` | General starter (backend + optional React frontend) |
31
+ | `api-rest` | Backend-only REST API with auth and a protected CRUD resource |
32
+ | `saas-multitenant` | Multi-tenant SaaS (organizations, roles) + React dashboard |
33
+ | `realtime-chat` | Authenticated real-time chat (Socket.IO) + React UI |
34
+
35
+ ### Manual boot
36
+
37
+ ```js
38
+ // index.js
39
+ const { Kernel } = require("zyket");
40
+
41
+ const kernel = new Kernel({
42
+ services: [
43
+ ["auth", require("./src/services/auth"), ["@service_container"]],
44
+ ],
45
+ });
46
+
47
+ kernel.boot().then(() => console.log("Kernel booted!"));
48
+ ```
49
+
50
+ On first run Zyket creates a `.env` and a `src/` directory with boilerplate.
51
+
52
+ ## Core concepts
53
+
54
+ - **Routes & Middlewares** — REST endpoints via Express, auto-discovered from `src/routes`.
55
+ - **Handlers & Guards** — Socket.IO events and their authorization, from `src/handlers` / `src/guards`.
56
+ - **Services** — reusable units managed by a DI container (`logger`, `database`, `cache`, `s3`, `events`, `scheduler`, `bullmq`, `socketio`, `express`, `auth`).
57
+ - **Extensions** — post-boot plugins (e.g. BullBoard, interactive storage).
58
+ - **Templates & CLI** — scaffold whole projects or individual components.
59
+
60
+ ### Routes
61
+
62
+ File path maps to the URL: `src/routes/index.js` → `/`, `src/routes/users/[id].js` → `/users/:id`. Methods: `get`, `post`, `put`, `delete`.
63
+
64
+ ```js
65
+ const { Route, RequireAuthMiddleware } = require("zyket");
66
+
67
+ module.exports = class extends Route {
68
+ middlewares = { post: [new RequireAuthMiddleware()] };
69
+
70
+ async get({ container, request }) {
71
+ return { items: [] }; // → { success: true, items: [] }
72
+ }
73
+ async post({ container, request }) {
74
+ if (!request.body.name) return { success: false, message: "name required", status: 400 };
75
+ return { created: true, status: 201 };
76
+ }
77
+ };
78
+ ```
79
+
80
+ Return an object (wrapped as `{ success: true, ... }`), set `status` to change the HTTP code, return a `Buffer` for a download, or `new RedirectResponse(url)` to redirect.
81
+
82
+ ### Middlewares
83
+
84
+ ```js
85
+ const { Middleware } = require("zyket");
86
+
87
+ module.exports = class extends Middleware {
88
+ async handle({ container, request, response, next }) {
89
+ next(); // respond to block, or next() to continue
90
+ }
91
+ };
92
+ ```
93
+
94
+ ### Socket.IO handlers & guards
95
+
96
+ ```js
97
+ // src/handlers/message.js → "message" event
98
+ const { Handler } = require("zyket");
99
+
100
+ module.exports = class extends Handler {
101
+ guards = ["auth"];
102
+ async handle({ container, socket, data, io }) {
103
+ io.emit("message", data);
104
+ return { ok: true };
105
+ }
106
+ };
107
+ ```
108
+
109
+ ```js
110
+ // src/guards/auth.js — reuse the built-in session guard
111
+ module.exports = require("zyket").AuthGuard;
112
+ ```
113
+
114
+ ## Authentication
115
+
116
+ Zyket ships first-class auth via [better-auth](https://better-auth.com) mounted at `/api/auth/*`, with the admin, bearer, and (optional) organization plugins. Customize by subclassing `AuthService`; protect routes/sockets with `RequireAuthMiddleware`, `RequireAdminMiddleware`, and `AuthGuard`.
117
+
118
+ ```js
119
+ const { Route, RequireAdminMiddleware } = require("zyket");
120
+
121
+ module.exports = class extends Route {
122
+ middlewares = { get: [new RequireAdminMiddleware()] };
123
+ async get() { return { secret: "admins only" }; }
124
+ };
125
+ ```
126
+
127
+ Cookies are environment-aware: `sameSite=lax` in local dev (works over `http://localhost`), and `sameSite=none; secure` when `AUTH_CROSS_DOMAIN=true` for cross-domain HTTPS deployments. See [AGENTS.md §6](AGENTS.md#6-authentication-better-auth).
128
+
129
+ ## Services
130
+
131
+ Default services activate from environment variables (see [AGENTS.md §3–4](AGENTS.md#3-environment-variables)). Register your own in the Kernel:
132
+
133
+ ```js
134
+ const { Service } = require("zyket");
135
+
136
+ module.exports = class MyService extends Service {
137
+ #container;
138
+ constructor(container) { super("my-service"); this.#container = container; }
139
+ async boot() { /* init */ }
140
+ doThing() { return "done"; }
141
+ };
142
+ ```
143
+
144
+ ```js
145
+ const kernel = new Kernel({
146
+ services: [["my-service", MyService, ["@service_container"]]],
147
+ });
148
+ ```
149
+
150
+ Default services and their activation:
151
+
152
+ | Service | Env to enable |
153
+ |---------|---------------|
154
+ | `database` | `DATABASE_URL` (+ `DATABASE_DIALECT`, default `sqlite`) |
155
+ | `cache` | always (`CACHE_URL=redis://…` for Redis, else in-memory) |
156
+ | `s3` | `S3_ENDPOINT` + `S3_ACCESS_KEY` + `S3_SECRET_KEY` (MinIO) |
157
+ | `socketio` | `DISABLE_SOCKET=false` |
158
+ | `bullmq` | `DISABLE_BULLMQ=false` + `QUEUES=...` |
159
+ | `scheduler` | `DISABLE_SCHEDULER=false` |
160
+ | `events` | `DISABLE_EVENTS=false` |
161
+ | `vite` | `VITE_ROOT` + `DISABLE_VITE=false` |
162
+ | `auth` | manual registration (requires `sqlite`/`postgresql`) |
163
+
164
+ ## Security
165
+
166
+ Zyket bakes in several defaults (random per-project `AUTH_SECRET`, fail-closed BullBoard, configurable payload limits, path-traversal guards on storage). Review **[SECURITY.md](SECURITY.md)** before going to production — notably rate limiting and `helmet` are not bundled.
167
+
168
+ ## Tooling for AI agents
169
+
170
+ - **[AGENTS.md](AGENTS.md)** full framework reference.
171
+ - **Claude Code plugin** (`plugin/`) — skills to scaffold and use Zyket (`generate`, `build`).
172
+
173
+ ## Contributing
174
+
175
+ Issues and pull requests are welcome — bug fixes, features, and documentation improvements. Let's build a better framework together. 🚀
package/bin/cli.js CHANGED
@@ -118,21 +118,49 @@ function depsForTemplate(template) {
118
118
  return deps;
119
119
  }
120
120
 
121
+ // `node-dependency-injection` loads @typescript-eslint/typescript-estree@5 at
122
+ // require time, which reads `ts.SyntaxKind` off whatever `typescript` npm
123
+ // hoisted. TypeScript 7 dropped that CJS shape, so an unpinned tree dies on
124
+ // boot with "Cannot read properties of undefined (reading 'BarBarToken')".
125
+ // zyket depends on typescript ^5 for this reason; the override is what keeps an
126
+ // already-resolved lockfile from pulling 7.x back in.
127
+ const TYPESCRIPT_PIN = ZYKET_PKG.dependencies?.typescript || '^5.9.3';
128
+
129
+ function projectName() {
130
+ const raw = path.basename(process.cwd()).toLowerCase().replace(/[^a-z0-9._-]+/g, '-').replace(/^[._-]+/, '');
131
+ return raw || 'zyket-app';
132
+ }
133
+
134
+ // `npm install zyket` already leaves a package.json behind, so merging into an
135
+ // existing file is the normal path, not the exception. Only fill in what's
136
+ // missing — never clobber versions or scripts the user already chose.
121
137
  function ensurePackageJson(template) {
122
138
  const packageJsonPath = path.join(process.cwd(), 'package.json');
123
- if (fs.existsSync(packageJsonPath)) return;
124
- const packageJson = {
125
- name: path.basename(process.cwd()),
139
+ let packageJson = {};
140
+ if (fs.existsSync(packageJsonPath)) {
141
+ try {
142
+ packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8')) || {};
143
+ } catch {
144
+ console.log('[ZYKET] package.json is not valid JSON — leaving it untouched.');
145
+ return;
146
+ }
147
+ }
148
+ const defaults = {
149
+ name: projectName(),
126
150
  version: '1.0.0',
127
151
  description: 'Zyket application',
128
152
  main: 'index.js',
129
- scripts: { dev: 'node index.js' },
130
153
  keywords: [],
131
154
  author: '',
132
155
  license: 'ISC',
133
- dependencies: depsForTemplate(template),
134
156
  };
135
- fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 2));
157
+ for (const [key, value] of Object.entries(defaults)) {
158
+ if (packageJson[key] === undefined) packageJson[key] = value;
159
+ }
160
+ packageJson.scripts = { dev: 'node index.js', ...packageJson.scripts };
161
+ packageJson.dependencies = { ...depsForTemplate(template), ...packageJson.dependencies };
162
+ packageJson.overrides = { typescript: TYPESCRIPT_PIN, ...packageJson.overrides };
163
+ fs.writeFileSync(packageJsonPath, `${JSON.stringify(packageJson, null, 2)}\n`);
136
164
  }
137
165
 
138
166
  function npmInstall() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zyket",
3
- "version": "1.2.19",
3
+ "version": "1.2.20",
4
4
  "main": "index.js",
5
5
  "scripts": {
6
6
  "test": "echo \"Error: no test specified\" && exit 1"
@@ -44,6 +44,7 @@
44
44
  "swagger-jsdoc": "^6.2.8",
45
45
  "swagger-ui-express": "^5.0.1",
46
46
  "tailwindcss": "^4.2.2",
47
+ "typescript": "^5.9.3",
47
48
  "umzug": "^3.8.2",
48
49
  "vite": "^8.0.2",
49
50
  "zustand": "^5.0.12"
@@ -53,6 +54,7 @@
53
54
  "ioredis": "^5.6.1"
54
55
  },
55
56
  "overrides": {
56
- "ajv": "^8.17.1"
57
+ "ajv": "^8.17.1",
58
+ "typescript": "^5.9.3"
57
59
  }
58
60
  }
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "zyket",
3
- "description": "Claude Code plugin for the Zyket framework scaffold handlers, guards, routes, middlewares, services, events, schedulers, workers, and extensions.",
4
- "version": "1.0.0",
3
+ "description": "Claude Code plugin for the Zyket framework. `build`: start, configure, and run a Zyket app (templates, services, auth, security). `generate`: scaffold handlers, guards, routes, middlewares, services, events, schedulers, workers, and extensions.",
4
+ "version": "1.1.0",
5
5
  "author": {
6
6
  "name": "OnchainTech"
7
7
  }
@@ -0,0 +1,88 @@
1
+ ---
2
+ name: build
3
+ description: Build, configure, and run applications with the Zyket framework — start a project from a template, enable services, wire authentication, secure the app, and add features. Use whenever the user wants to use Zyket, create or set up a Zyket project/app, or understand how Zyket works. For scaffolding a single component, use the `generate` skill instead.
4
+ allowed-tools: Read, Write, Edit, Grep, Glob, Bash
5
+ ---
6
+
7
+ # Build with Zyket
8
+
9
+ Use this skill to take a user from zero to a running Zyket application, or to extend an existing one. Zyket is a service-oriented Node.js framework (Express 5 + Socket.IO + better-auth + Sequelize/BullMQ/MinIO) with filesystem auto-discovery and a DI container.
10
+
11
+ ## Golden rules
12
+
13
+ 1. **Read the reference.** After install, the full, authoritative API lives in `node_modules/zyket/AGENTS.md`. Read it before relying on any API; confirm base classes against `node_modules/zyket/` source. Do not invent APIs.
14
+ 2. **Filename = identity.** Routes, handlers, guards, events, schedulers, workers, and models are auto-discovered from `src/<folder>/`; the filename determines the URL/event/name. Use kebab-case and `module.exports = class extends Base`. Never add constructor params to these.
15
+ 3. **Services activate from `.env`.** A feature that needs sockets/queues/db must have the matching `DISABLE_*=false` / `DATABASE_URL` set.
16
+ 4. **Secure by default.** Protect non-public routes/sockets with `RequireAuthMiddleware` / `AuthGuard`. Never weaken cookie/auth settings to "make it work" — use `AUTH_CROSS_DOMAIN` instead.
17
+
18
+ ## Workflow
19
+
20
+ ### A. Starting a new project
21
+
22
+ 1. **Pick a template** by intent:
23
+
24
+ | User wants… | Template |
25
+ |-------------|----------|
26
+ | A clean REST API | `api-rest` |
27
+ | Multi-tenant SaaS (orgs, roles, dashboard) | `saas-multitenant` |
28
+ | Real-time / chat / live updates | `realtime-chat` |
29
+ | A general starter / unsure | `default` |
30
+
31
+ 2. **Scaffold:**
32
+ ```bash
33
+ npm install zyket
34
+ npx zyket init <template>
35
+ ```
36
+ This copies the template, merges its `.env.example` onto sensible defaults, writes `package.json` with the right dependencies, and installs them.
37
+
38
+ 3. **If the template uses auth** (`src/services/auth` exists), create the tables once:
39
+ ```bash
40
+ npx @better-auth/cli migrate
41
+ ```
42
+
43
+ 4. **Run:** `node index.js`. The API is on `PORT` (3000); a frontend, if any, on `VITE_PORT` (5173).
44
+
45
+ ### B. Enabling a capability in an existing project
46
+
47
+ Flip the env flag, then add the component. Confirm details in `AGENTS.md §3–4`.
48
+
49
+ | Capability | `.env` | Then add |
50
+ |-----------|--------|----------|
51
+ | HTTP routes | `DISABLE_EXPRESS=false` | `src/routes/**.js` |
52
+ | Real-time | `DISABLE_SOCKET=false` | `src/handlers/*.js`, `src/guards/*.js` |
53
+ | Database | `DATABASE_URL=...`, `DATABASE_DIALECT=...` | `src/models/*.js` |
54
+ | Background jobs | `DISABLE_BULLMQ=false`, `QUEUES=emails` | `src/workers/*.js` |
55
+ | Cron | `DISABLE_SCHEDULER=false` | `src/schedulers/*.js` |
56
+ | Cache | `CACHE_URL=redis://…` (or in-memory) | use `container.get('cache')` |
57
+ | Object storage | `S3_ENDPOINT`, `S3_ACCESS_KEY`, `S3_SECRET_KEY` | use `container.get('s3')` |
58
+ | Frontend | `DISABLE_VITE=false`, `VITE_ROOT=./frontend` | `frontend/` React app |
59
+
60
+ ### C. Adding authentication
61
+
62
+ 1. Register the auth service in `index.js`: `["auth", require("./src/services/auth"), ["@service_container"]]`.
63
+ 2. Subclass `AuthService` (see `AGENTS.md §6`) to set `organizationEnabled`, `requireEmailVerification`, email senders, etc.
64
+ 3. Require `DATABASE_DIALECT` = `sqlite` or `postgresql`; run `npx @better-auth/cli migrate`.
65
+ 4. Protect routes with `new RequireAuthMiddleware()` (or `['admin']` / `RequireAdminMiddleware`) and sockets with the `AuthGuard` (`module.exports = require('zyket').AuthGuard` in `src/guards/auth.js`; enforce connect-time auth inside `src/handlers/connection.js` via `container.get('auth').getSession(socket.handshake.headers)`).
66
+
67
+ ### D. Building a feature (recipe)
68
+
69
+ 1. Model → `src/models/Thing.js` (Sequelize factory). Create tables with `container.get('database').sync()` after boot, or migrations.
70
+ 2. Routes → `src/routes/things/index.js` (GET/POST) and `src/routes/things/[id].js` (GET/PUT/DELETE), gated by `RequireAuthMiddleware`.
71
+ 3. Read data via `container.get('database').models.Thing`.
72
+ 4. Optional realtime/jobs per the table in B.
73
+ 5. Run and verify.
74
+
75
+ Delegate individual component scaffolding to the **`generate`** skill (e.g. `/zyket:generate route things/[id]`).
76
+
77
+ ## Common pitfalls
78
+
79
+ - **PATCH routes don't dispatch** — only `get/post/put/delete` methods are wired.
80
+ - **Connection guards don't block** — do connect-time auth inside `connection.js` (throw to disconnect).
81
+ - **`bullmq`/`scheduler`/`events` default ON when the `DISABLE_*` var is absent**, but the generated `.env` ships them as `true`. Set them to `false` explicitly to enable.
82
+ - **Auth fails to boot** if `AUTH_SECRET` is missing or a known placeholder — let the framework generate it.
83
+ - **Frontend can't resolve `vite`/`react`/`better-auth`** — the project (not just zyket) must declare those deps; `npx zyket init` does this automatically for templates with a frontend.
84
+ - **Boot crashes with `Cannot read properties of undefined (reading 'BarBarToken')`** — a TypeScript 7 got hoisted into the tree, which `node-dependency-injection`'s bundled `typescript-estree@5` can't read. Zyket pins `typescript` to `^5` and `npx zyket init` writes a matching `overrides` block; on a project predating that, add `"overrides": { "typescript": "^5.9.3" }` to `package.json` and reinstall.
85
+
86
+ ## Security pass before shipping
87
+
88
+ Run through `SECURITY.md`. Minimum: every private route/socket guarded; `BULLBOARD_ADMIN_PASSWORD` set (or the dashboard won't mount); `SWAGGER_PASSWORD` or `DISABLE_SWAGGER=true`; `TRUSTED_ORIGINS` locked; add rate limiting + `helmet` (not bundled).