@plumbus/core 0.2.2 → 0.2.4

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 (42) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/ai/__tests__/ai-service.test.js +27 -0
  3. package/dist/ai/__tests__/ai-service.test.js.map +1 -1
  4. package/dist/ai/__tests__/validation.test.js +33 -0
  5. package/dist/ai/__tests__/validation.test.js.map +1 -1
  6. package/dist/ai/ai-service.d.ts.map +1 -1
  7. package/dist/ai/ai-service.js +87 -77
  8. package/dist/ai/ai-service.js.map +1 -1
  9. package/dist/ai/validation.d.ts.map +1 -1
  10. package/dist/ai/validation.js +7 -1
  11. package/dist/ai/validation.js.map +1 -1
  12. package/dist/api/route-generator.d.ts.map +1 -1
  13. package/dist/api/route-generator.js +8 -0
  14. package/dist/api/route-generator.js.map +1 -1
  15. package/dist/cli/commands/dev.d.ts.map +1 -1
  16. package/dist/cli/commands/dev.js +3 -0
  17. package/dist/cli/commands/dev.js.map +1 -1
  18. package/dist/cli/commands/start.d.ts.map +1 -1
  19. package/dist/cli/commands/start.js +3 -0
  20. package/dist/cli/commands/start.js.map +1 -1
  21. package/dist/execution/__tests__/context-factory.test.js +16 -0
  22. package/dist/execution/__tests__/context-factory.test.js.map +1 -1
  23. package/dist/execution/context-factory.d.ts +2 -1
  24. package/dist/execution/context-factory.d.ts.map +1 -1
  25. package/dist/execution/context-factory.js +4 -0
  26. package/dist/execution/context-factory.js.map +1 -1
  27. package/dist/server/__tests__/bootstrap.test.js +13 -0
  28. package/dist/server/__tests__/bootstrap.test.js.map +1 -1
  29. package/dist/server/bootstrap.d.ts +2 -0
  30. package/dist/server/bootstrap.d.ts.map +1 -1
  31. package/dist/server/bootstrap.js +2 -1
  32. package/dist/server/bootstrap.js.map +1 -1
  33. package/dist/testing/context.d.ts.map +1 -1
  34. package/dist/testing/context.js +15 -0
  35. package/dist/testing/context.js.map +1 -1
  36. package/dist/types/context.d.ts +22 -0
  37. package/dist/types/context.d.ts.map +1 -1
  38. package/dist/types/index.d.ts +1 -1
  39. package/dist/types/index.d.ts.map +1 -1
  40. package/dist/types/index.js.map +1 -1
  41. package/instructions/deployment.md +304 -68
  42. package/package.json +1 -1
@@ -1,6 +1,6 @@
1
1
  # Deployment
2
2
 
3
- Plumbus applications deploy as up to three services — a **backend** (Fastify API server), a **frontend** (Next.js), and optionally an **admin dashboard** (Next.js) — backed by PostgreSQL and Redis. This document covers production configuration, containerization, and operational readiness.
3
+ Plumbus applications deploy as up to three services — a **backend** (Fastify API server), a **frontend** (Next.js), and optionally an **admin dashboard** (Next.js) — backed by PostgreSQL and Redis.
4
4
 
5
5
  ## Service Topology
6
6
 
@@ -19,14 +19,116 @@ Plumbus applications deploy as up to three services — a **backend** (Fastify A
19
19
  +-------+ +-------+
20
20
  ```
21
21
 
22
- - **Frontend** and **admin** proxy API calls to the backend via server-side route handlers (`app/api/[...path]/route.ts`). Browsers never connect directly to the backend.
22
+ - **Frontend** and **admin** proxy API calls to the backend server-side. Browsers never connect directly to the backend.
23
23
  - **Backend** connects to PostgreSQL (data) and Redis (job queue, event dispatch).
24
24
 
25
+ ---
26
+
27
+ ## Docker Rules — MUST follow all of these
28
+
29
+ These rules are critical. Violating any one causes build failures or bloated images.
30
+
31
+ ### Rule 1: Generate `.plumbus/` inside Docker
32
+
33
+ - **DO**: Run `plumbus generate` in a Docker build stage.
34
+ - **DO NOT**: Copy `.plumbus/` from the host. It may not exist in CI/fresh clones.
35
+
36
+ ### Rule 2: Always copy `.npmrc` into Docker images
37
+
38
+ - **DO**: `COPY .npmrc ./` before `pnpm install`.
39
+ - **DO NOT**: Skip `.npmrc`. It has `public-hoist-pattern` entries required for `next build` to find `next`, `react`, `tailwindcss`.
40
+ - **Symptom if violated**: `Cannot find module 'next'` during build.
41
+
42
+ ### Rule 3: Never copy `pnpm-workspace.yaml` with local dev overrides
43
+
44
+ - **DO**: Copy the clean `pnpm-workspace.yaml` (lists only `frontend` and `frontend-admin`).
45
+ - **DO NOT**: Copy a workspace yaml that contains `link:../Plumbus/...` dev overrides.
46
+ - **TIP**: If your project uses `dev:link`/`dev:unlink` scripts to swap workspace configs, make sure the clean version is active before building Docker images.
47
+
48
+ ### Rule 4: Use `FROM deps AS build` (preserve pnpm symlinks in build stage)
49
+
50
+ - **DO**: `FROM deps AS build` — inherits the filesystem with intact symlinks.
51
+ - **DO NOT**: `FROM base AS build` with `COPY --from=deps /app/node_modules ./node_modules`. Docker COPY breaks pnpm symlinks.
52
+
53
+ ### Rule 5: Use `pnpm deploy` to isolate frontend dependencies
54
+
55
+ - **DO**: Run `pnpm --filter {./frontend} deploy --legacy /app/deployed` to create a self-contained directory with flat `node_modules` containing ONLY the frontend's dependencies.
56
+ - **DO NOT**: Build Next.js from the workspace root with all monorepo dependencies visible. pnpm's symlinked store breaks Next.js file tracing, and hoisted layout includes every workspace package.
57
+ - **Why**: `pnpm deploy` creates isolated flat `node_modules` with just the target package's deps. Next.js standalone traces only what the frontend actually needs (~30-40 MB vs ~600+ MB).
58
+
59
+ ### Rule 6: Never copy full `node_modules` into frontend runner stages
60
+
61
+ - **DO**: Copy ONLY `.next/standalone` and `.next/static` into the runner.
62
+ - **DO NOT**: `COPY --from=deps /app/node_modules ./node_modules` in the runner stage.
63
+ - **Why**: Standalone output already includes all traced modules (~60-80 MB). Copying full `node_modules` adds ~700 MB for no reason.
64
+ - **Target image size**: Frontend/admin images should be ~150-200 MB total, not 800 MB+.
65
+
66
+ ### Rule 7: Strip frontend-only packages from backend prod deps
67
+
68
+ - **DO**: Create a separate `proddeps` stage that removes `@plumbus/ui` from `package.json` before `npm install --omit=dev`.
69
+ - **DO NOT**: Copy the full `node_modules` (including `@plumbus/ui`) into the backend runner.
70
+ - **Why**: `@plumbus/ui` pulls in `next`, `react`, `react-dom`, `tailwindcss`, `lucide-react` (~300 MB). The backend only needs `@plumbus/core` at runtime.
71
+ - **Target image size**: Backend should be ~500-700 MB, not 1.5 GB+.
72
+
73
+ ### Rule 8: Use `ENV PATH` instead of `npx` for frontend builds
74
+
75
+ - **DO**: `ENV PATH="/app/deployed/node_modules/.bin:$PATH"` then `RUN next build`.
76
+ - **DO NOT**: `RUN npx next build`.
77
+ - **Why**: With `pnpm deploy`, binaries are in the deployed directory's `node_modules/.bin/`. Adding it to PATH ensures `next` resolves correctly.
78
+
79
+ ### Rule 9: Never run migrations in the entrypoint
80
+
81
+ - **DO**: Run `npx plumbus migrate apply` as a separate CI/CD step or one-off job.
82
+ - **DO NOT**: Put migration commands in `entrypoint.sh` or Docker init containers.
83
+ - **Why**: In multi-replica deployments, concurrent migrations corrupt data.
84
+
85
+ ### Rule 10: Use dynamic environment detection in `app.config.ts`
86
+
87
+ - **DO**: `environment: (process.env.NODE_ENV === 'production' ? 'production' : 'development')`
88
+ - **DO NOT**: Hardcode `environment: 'development'`. This causes `plumbus start` to skip production safeguards in Docker.
89
+
90
+ ### Rule 11: Regenerate client types before building frontends
91
+
92
+ - **DO**: Run `plumbus ui generate` before `next build` if capability schemas changed.
93
+ - **DO NOT**: Build frontends with stale `frontend/lib/client.ts`.
94
+
95
+ ### Rule 12: Set `output: "standalone"` in all `next.config.mjs` files
96
+
97
+ - **DO**: Add `output: "standalone"` to `next.config.mjs` for every Next.js app (frontend, admin).
98
+ - **DO NOT**: Deploy without standalone output. The app would need full `node_modules` at runtime.
99
+
100
+ ### Rule 13: Add `outputFileTracingExcludes` in `next.config.mjs`
101
+
102
+ - **DO**: Exclude build/dev tools that are transitive deps of `@plumbus/ui` but not needed at runtime:
103
+ ```js
104
+ outputFileTracingExcludes: {
105
+ "**": [
106
+ "./node_modules/typescript/**",
107
+ "./node_modules/@swc/core*/**",
108
+ ],
109
+ },
110
+ ```
111
+ - **Why**: Even with `pnpm deploy`, some transitive deps (typescript, @swc/core) are present via `@plumbus/ui` but unnecessary at runtime. Excluding them saves ~50 MB.
112
+
113
+ ### Rule 14: Clean up test/build tools from backend `node_modules`
114
+
115
+ - **DO**: After `npm install --omit=dev` in the backend `proddeps` stage, delete transitive deps only needed for testing/building:
116
+ ```dockerfile
117
+ RUN npm install --omit=dev && \
118
+ rm -rf node_modules/@vitest node_modules/vitest \
119
+ node_modules/playwright-core node_modules/playwright \
120
+ node_modules/@esbuild node_modules/esbuild \
121
+ node_modules/typescript
122
+ ```
123
+ - **Why**: `@plumbus/core` includes vitest, playwright, esbuild, and typescript as dependencies. The backend doesn't need them at runtime.
124
+
125
+ ---
126
+
25
127
  ## Backend Deployment
26
128
 
27
129
  ### Production Command
28
130
 
29
- Use `plumbus start` for production. It forces production environment, binds to `0.0.0.0`, and requires `AUTH_SECRET`:
131
+ Use `plumbus start` for production:
30
132
 
31
133
  | Behavior | `plumbus dev` | `plumbus start` |
32
134
  |----------|---------------|------------------|
@@ -43,7 +145,7 @@ npx plumbus start --port 3000
43
145
 
44
146
  ### Runtime File Requirements
45
147
 
46
- The backend uses dynamic resource discovery at startup — it scans the source tree for capabilities, entities, flows, events, and prompts. The following directories **must be present** in the production image:
148
+ These directories **must be present** in the production image:
47
149
 
48
150
  | Directory | Purpose |
49
151
  |-----------|---------|
@@ -56,18 +158,20 @@ The backend uses dynamic resource discovery at startup — it scans the source t
56
158
 
57
159
  ### Entrypoint Pattern
58
160
 
59
- **CRITICAL**: Do **not** run migrations automatically in the container entrypoint. In multi-replica deployments (Kubernetes, ECS), concurrent migration runs can corrupt data or cause extended downtime. Run migrations as a separate, deliberate step before deploying new containers.
60
-
61
161
  ```sh
62
- # Run migrations BEFORE deploying new containers (CI/CD step or one-off job):
63
- npx plumbus migrate apply
64
-
65
- # Then deploy. Container entrypoint only starts the server:
162
+ # entrypoint.sh — only starts the server. No migrations here.
66
163
  #!/bin/sh
67
164
  set -e
68
165
  exec npx plumbus start --port 3000
69
166
  ```
70
167
 
168
+ Migrations run separately:
169
+
170
+ ```sh
171
+ # Run BEFORE deploying new containers (CI/CD step or one-off job):
172
+ npx plumbus migrate apply
173
+ ```
174
+
71
175
  ### Health Endpoints
72
176
 
73
177
  | Endpoint | Purpose | Healthy response |
@@ -77,11 +181,13 @@ exec npx plumbus start --port 3000
77
181
 
78
182
  Use `/ready` for container health checks and load balancer probes.
79
183
 
184
+ ---
185
+
80
186
  ## Frontend Deployment
81
187
 
82
188
  ### Standalone Output
83
189
 
84
- **CRITICAL**: Add `output: "standalone"` to `next.config.mjs` for containerized or self-hosted deployment. Without this, the build requires the full `node_modules` at runtime.
190
+ Add to `next.config.mjs`:
85
191
 
86
192
  ```js
87
193
  const nextConfig = {
@@ -97,18 +203,12 @@ const nextConfig = {
97
203
  npx next build
98
204
 
99
205
  # Run standalone server
100
- node .next/standalone/<app-dir>/server.js
101
- ```
102
-
103
- Set `PORT` and `HOSTNAME` environment variables to control the listening address:
104
-
105
- ```bash
106
206
  PORT=3001 HOSTNAME=0.0.0.0 node .next/standalone/frontend/server.js
107
207
  ```
108
208
 
109
209
  ### Static Assets
110
210
 
111
- After a standalone build, static files live in `.next/static/`. Copy them to the correct path in your deployment:
211
+ After a standalone build, copy static files alongside the server:
112
212
 
113
213
  ```
114
214
  .next/standalone/ # Server code
@@ -117,9 +217,11 @@ After a standalone build, static files live in `.next/static/`. Copy them to the
117
217
 
118
218
  ### Environment Variables
119
219
 
120
- - `NEXT_PUBLIC_*` variables are inlined at **build time**. Set them before `next build`, not at runtime.
121
- - `API_BASE_URL` (server-only) controls the backend URL for server-side proxy routes. Set this at runtime to the internal Docker network address (e.g., `http://backend:3000`).
122
- - `AUTH_SECRET` is required for cookie signing.
220
+ - `NEXT_PUBLIC_*` — inlined at **build time**. Set before `next build`, not at runtime.
221
+ - `API_BASE_URL` — set at **runtime**. Internal Docker network address (e.g., `http://backend:3000`).
222
+ - `AUTH_SECRET` — required for cookie signing.
223
+
224
+ ---
123
225
 
124
226
  ## Environment Variables Reference
125
227
 
@@ -140,6 +242,7 @@ After a standalone build, static files live in `.next/static/`. Copy them to the
140
242
  | `AI_OPENAI_API_KEY` | If using OpenAI | — | OpenAI API key |
141
243
  | `AI_OPENAI_MODEL` | No | `gpt-4o-mini` | Default OpenAI model |
142
244
  | `AI_DEFAULT_MODEL` | No | `gpt-4o` | Fallback model |
245
+ | `TRUST_PROXY` | No | — | Set to `true` behind a reverse proxy so `request.ip` reflects `X-Forwarded-For` |
143
246
 
144
247
  ### Frontend / Admin
145
248
 
@@ -148,89 +251,147 @@ After a standalone build, static files live in `.next/static/`. Copy them to the
148
251
  | `NEXT_PUBLIC_API_BASE_URL` | Build | Public API URL (empty string if using server-side proxy) |
149
252
  | `API_BASE_URL` | Runtime | Backend URL for server-side proxy (e.g., `http://backend:3000`) |
150
253
  | `AUTH_SECRET` | Runtime | Cookie signing secret |
254
+ | `AUTH_COOKIE_SECURE` | Runtime | Set to `"true"` for TLS, `"false"` for HTTP-only deployments |
255
+
256
+ ---
151
257
 
152
258
  ## Docker
153
259
 
154
- ### Backend Dockerfile (multi-stage)
260
+ ### Expected Image Sizes
261
+
262
+ | Image | Target Size | Too Large If |
263
+ |-------|-------------|--------------|
264
+ | Frontend | ~150-200 MB | > 400 MB |
265
+ | Admin | ~150-200 MB | > 400 MB |
266
+ | Backend | ~500-700 MB | > 1 GB |
267
+
268
+ If images exceed these sizes, you are likely not using `pnpm deploy` (Rule 5) or not stripping backend deps (Rule 7, Rule 14).
269
+
270
+ ### Backend Dockerfile
155
271
 
156
272
  ```dockerfile
273
+ # ==============================================================
274
+ # BACKEND DOCKERFILE — Reference template
275
+ # See "Docker Rules" section above for the rationale behind each step.
276
+ # ==============================================================
277
+
157
278
  # ---- Base ----
158
279
  FROM node:22-alpine AS base
159
280
  RUN corepack enable && corepack prepare pnpm@latest --activate
160
281
  WORKDIR /app
161
282
 
162
- # ---- Dependencies ----
283
+ # ---- Full dependencies (needed for plumbus generate and pnpm build) ----
163
284
  FROM base AS deps
164
- # Do NOT copy pnpm-workspace.yaml — it may contain local dev overrides
165
- # (link:../Plumbus/...) that don't exist in Docker. Packages resolve from npm.
285
+ # DO NOT copy pnpm-workspace.yaml — it may have local dev overrides.
166
286
  COPY package.json pnpm-lock.yaml ./
167
287
  RUN pnpm install
168
288
 
289
+ # ---- Production dependencies only ----
290
+ # Strip @plumbus/ui and devDependencies — backend doesn't need them at runtime.
291
+ # This saves ~300 MB (next, react, tailwindcss, etc. are excluded).
292
+ # After install, remove test/build tools from @plumbus/core transitive deps.
293
+ FROM node:22-alpine AS proddeps
294
+ WORKDIR /app
295
+ COPY package.json ./
296
+ ENV PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD=1
297
+ RUN node -e "\
298
+ const p = JSON.parse(require('fs').readFileSync('package.json','utf8'));\
299
+ for (const k of ['@plumbus/ui'])\
300
+ delete (p.dependencies||{})[k];\
301
+ delete p.devDependencies;\
302
+ require('fs').writeFileSync('package.json', JSON.stringify(p, null, 2));" && \
303
+ npm install --omit=dev && \
304
+ rm -rf node_modules/@vitest node_modules/vitest \
305
+ node_modules/playwright-core node_modules/playwright \
306
+ node_modules/@esbuild node_modules/esbuild \
307
+ node_modules/typescript
308
+
309
+ # ---- Generate .plumbus/ ----
310
+ # DO: Generate inside Docker. DO NOT: Copy from host.
311
+ FROM deps AS generate
312
+ COPY app/ ./app/
313
+ COPY config/ ./config/
314
+ COPY tsconfig.json ./
315
+ RUN npx plumbus generate
316
+
169
317
  # ---- Build ----
170
- FROM base AS build
171
- COPY --from=deps /app/node_modules ./node_modules
318
+ # Uses "FROM deps" to preserve pnpm symlinks (Rule 4).
319
+ FROM deps AS build
320
+ COPY --from=generate /app/.plumbus/ ./.plumbus/
172
321
  COPY package.json tsconfig.json ./
173
322
  COPY app/ ./app/
174
323
  COPY config/ ./config/
175
- COPY .plumbus/ ./.plumbus/
176
324
  COPY drizzle/ ./drizzle/
177
325
  RUN pnpm build
178
326
 
179
327
  # ---- Runner ----
180
328
  FROM node:22-alpine AS runner
181
- RUN corepack enable && corepack prepare pnpm@latest --activate
182
329
  WORKDIR /app
183
330
  ENV NODE_ENV=production
184
331
  RUN addgroup --system --gid 1001 plumbus && \
185
332
  adduser --system --uid 1001 plumbus
186
- COPY --from=deps /app/node_modules ./node_modules
333
+ # Production-only node_modules from proddeps (no @plumbus/ui, no devDeps).
334
+ COPY --from=proddeps /app/node_modules ./node_modules
187
335
  COPY --from=build /app/dist ./dist
336
+ COPY --from=generate /app/.plumbus/ ./.plumbus/
188
337
  COPY package.json ./
189
338
  COPY app/ ./app/
190
339
  COPY config/ ./config/
191
- COPY .plumbus/ ./.plumbus/
192
340
  COPY drizzle/ ./drizzle/
193
- # Entrypoint starts the production server
194
341
  USER plumbus
195
342
  EXPOSE 3000
196
- HEALTHCHECK CMD curl -f http://localhost:3000/ready || exit 1
343
+ HEALTHCHECK CMD wget -q -O /dev/null http://localhost:3000/ready || exit 1
197
344
  ENTRYPOINT ["./entrypoint.sh"]
198
345
  ```
199
346
 
200
- ### Frontend Dockerfile (Next.js standalone)
347
+ ### Frontend Dockerfile
348
+
349
+ For the **admin dashboard**, replace `{./frontend}` with `{./frontend-admin}`, change port 3001 to 3002, and adjust the package filter accordingly.
201
350
 
202
351
  ```dockerfile
352
+ # ==============================================================
353
+ # FRONTEND DOCKERFILE — Reference template
354
+ # Uses pnpm deploy to isolate frontend deps from the monorepo.
355
+ # See "Docker Rules" section above for the rationale behind each step.
356
+ # ==============================================================
357
+
203
358
  FROM node:22-alpine AS base
204
359
  RUN corepack enable && corepack prepare pnpm@latest --activate
205
360
  WORKDIR /app
206
361
 
362
+ # ---- Install full workspace (needed for pnpm deploy to resolve deps) ----
207
363
  FROM base AS deps
208
- COPY package.json pnpm-lock.yaml ./
364
+ COPY .npmrc package.json pnpm-lock.yaml pnpm-workspace.yaml ./
209
365
  COPY frontend/package.json ./frontend/
210
- # Generate a clean workspace config — don't copy the host's pnpm-workspace.yaml
211
- RUN printf 'packages:\n - "frontend"\n' > pnpm-workspace.yaml
366
+ COPY frontend-admin/package.json ./frontend-admin/
212
367
  RUN pnpm install
213
368
 
214
- FROM base AS build
215
- COPY --from=deps /app/node_modules ./node_modules
216
- COPY --from=deps /app/frontend/node_modules ./frontend/node_modules
369
+ # ---- Deploy + Build ----
370
+ # pnpm deploy creates a self-contained directory with flat node_modules
371
+ # containing ONLY the frontend's dependencies — no backend/root workspace deps.
372
+ FROM deps AS build
217
373
  COPY frontend/ ./frontend/
218
- WORKDIR /app/frontend
219
- RUN npx next build
374
+ RUN pnpm --filter {./frontend} deploy --legacy /app/deployed
375
+ ENV PATH="/app/deployed/node_modules/.bin:$PATH"
376
+ WORKDIR /app/deployed
377
+ RUN next build
220
378
 
379
+ # ---- Runner ----
380
+ # Standalone output only — includes server.js + traced node_modules (~30-40 MB).
381
+ # DO NOT copy node_modules from deps — standalone has everything it needs.
221
382
  FROM node:22-alpine AS runner
222
383
  WORKDIR /app
223
384
  ENV NODE_ENV=production
224
385
  RUN addgroup --system --gid 1001 nextjs && \
225
386
  adduser --system --uid 1001 nextjs
226
- COPY --from=build /app/frontend/.next/standalone ./
227
- COPY --from=build /app/frontend/.next/static ./frontend/.next/static
387
+ COPY --from=build /app/deployed/.next/standalone ./
388
+ COPY --from=build /app/deployed/.next/static ./deployed/.next/static
228
389
  USER nextjs
229
390
  EXPOSE 3001
230
- CMD ["node", "frontend/server.js"]
391
+ CMD ["node", "deployed/server.js"]
231
392
  ```
232
393
 
233
- ### Docker Compose Topology
394
+ ### Docker Compose
234
395
 
235
396
  ```yaml
236
397
  services:
@@ -251,18 +412,66 @@ services:
251
412
  depends_on: [backend]
252
413
  ```
253
414
 
254
- ### Key Docker Considerations
415
+ ### .dockerignore
255
416
 
256
- - **Do NOT copy `pnpm-workspace.yaml`** into Docker images. It may contain local dev overrides (`link:../Plumbus/...`) that don't exist in the build context. The backend Dockerfile doesn't need it at all. Frontend Dockerfiles generate a minimal workspace config inline (`printf 'packages: ["frontend"]' > pnpm-workspace.yaml`) to declare the sub-package.
257
- - **Dependency resolution**: `@plumbus/core` and `@plumbus/ui` are declared as normal dependencies in `package.json`. Without the workspace overrides, `pnpm install` resolves them from the npm registry.
258
- - **Volumes**: Mount persistent volumes for PostgreSQL data and the `uploads/` directory (user-generated media and exports).
259
- - **Internal networking**: Frontends connect to backend via Docker service name (e.g., `http://backend:3000`). Set `API_BASE_URL` accordingly.
260
- - **`NEXT_PUBLIC_API_BASE_URL`**: Set to empty string (`""`) if the frontend uses server-side proxy routes. The proxy route handler reads `API_BASE_URL` at runtime.
261
- - **File layout**: Place all Docker files (Dockerfiles, compose, entrypoint, env template) in a `docker/` subdirectory. The `.dockerignore` stays at the project root (the build context). Set `context: ..` (parent) in compose `build:` sections so COPY paths resolve from the project root.
417
+ Place at the project root. Must include at minimum:
262
418
 
263
- ## Kubernetes / Helm
419
+ ```
420
+ node_modules
421
+ .next
422
+ dist
423
+ .turbo
424
+ .git
425
+ uploads
426
+ **/e2e
427
+ docs
428
+ ```
429
+
430
+ ### File Layout
431
+
432
+ Place all Docker files in a `docker/` subdirectory:
433
+
434
+ ```
435
+ docker/
436
+ Dockerfile.backend
437
+ Dockerfile.frontend
438
+ Dockerfile.admin
439
+ docker-compose.yml
440
+ entrypoint-backend.sh
441
+ .env.example
442
+ .dockerignore # at project root (the build context)
443
+ ```
264
444
 
265
- The Docker images are self-contained and orchestrator-agnostic. When deploying to Kubernetes:
445
+ Set `context: ..` in compose `build:` sections so COPY paths resolve from the project root.
446
+
447
+ ---
448
+
449
+ ## HTTP Deployments Without TLS
450
+
451
+ `plumbus start` sets `secure: true` on auth cookies by default. If you deploy over plain HTTP (no TLS), browsers silently drop `Secure` cookies, causing infinite login redirect loops.
452
+
453
+ ### Workaround
454
+
455
+ Set `AUTH_COOKIE_SECURE=false` in the environment. Frontend auth route handlers must read it:
456
+
457
+ ```ts
458
+ // app/api/auth/login/route.ts
459
+ const isSecure = process.env.AUTH_COOKIE_SECURE === "true";
460
+
461
+ cookies().set("auth_token", token, {
462
+ httpOnly: true,
463
+ secure: isSecure,
464
+ sameSite: isSecure ? "strict" : "lax",
465
+ path: "/",
466
+ maxAge: 60 * 60 * 24 * 7,
467
+ });
468
+ ```
469
+
470
+ **Security trade-off** — always use TLS in production and set `AUTH_COOKIE_SECURE=true`.
471
+
472
+ ---
473
+
474
+ ## Kubernetes / Helm
266
475
 
267
476
  ### Required Resources
268
477
 
@@ -276,11 +485,9 @@ The Docker images are self-contained and orchestrator-agnostic. When deploying t
276
485
  | `Secret` | `DB_PASSWORD`, `AUTH_SECRET`, AI provider keys |
277
486
  | `ConfigMap` | Non-sensitive config: `DB_HOST`, `AI_DEFAULT_PROVIDER`, etc. |
278
487
 
279
- ### Migrations in Kubernetes — Critical
488
+ ### Migrations — Critical
280
489
 
281
- **NEVER** run migrations in the container entrypoint or as an init container that runs on every pod replica. In a rolling deployment with multiple pods starting concurrently, parallel migration runs against the same database can corrupt data or cause extended downtime.
282
-
283
- Run migrations as a **separate Kubernetes Job** before rolling out new pods:
490
+ **NEVER** run migrations in the entrypoint or in init containers that run per-replica. Run as a **separate Job** before rolling out new pods:
284
491
 
285
492
  ```yaml
286
493
  apiVersion: batch/v1
@@ -312,21 +519,50 @@ readinessProbe:
312
519
  periodSeconds: 10
313
520
  ```
314
521
 
315
- ### Scaling Notes
522
+ ### Scaling
316
523
 
317
524
  - **Backend**: Stateless — safe to scale. All replicas must share the same `AUTH_SECRET`.
318
525
  - **Frontends**: Stateless — scale freely behind a load balancer.
319
- - **Uploads**: Replace the local `uploads/` directory with object storage (S3, GCS) or a `ReadWriteMany` PersistentVolumeClaim.
320
- - **PostgreSQL / Redis**: Use managed services (RDS, ElastiCache, etc.) or official Helm charts — not the Docker Compose images.
526
+ - **Uploads**: Replace local `uploads/` with object storage (S3, GCS) or a `ReadWriteMany` PVC.
527
+ - **PostgreSQL / Redis**: Use managed services (RDS, ElastiCache) — not the Docker Compose images.
528
+
529
+ ---
321
530
 
322
531
  ## Production Checklist
323
532
 
324
- - [ ] `plumbus start` used as production server command
325
- - [ ] `AUTH_SECRET` set to a strong random value (32+ characters)
533
+ ### Server
534
+
535
+ - [ ] `plumbus start` used as production command
536
+ - [ ] `AUTH_SECRET` set to strong random value (32+ chars)
326
537
  - [ ] Database SSL enabled (automatic with `plumbus start`)
327
- - [ ] `plumbus migrate apply` run as a separate step before deployment (NOT in entrypoint)
328
- - [ ] Health checks configured for `/ready` endpoint
538
+ - [ ] Migrations run separately — NOT in entrypoint
539
+ - [ ] Health checks on `/ready`
540
+ - [ ] `config/app.config.ts` uses dynamic environment detection
541
+ - [ ] AI provider API keys configured
542
+ - [ ] Redis available for job queue
543
+
544
+ ### Docker — Image Size
545
+
329
546
  - [ ] `output: "standalone"` in all `next.config.mjs` files
547
+ - [ ] Frontend/admin runner copies ONLY `.next/standalone` + `.next/static`
548
+ - [ ] Frontend/admin runner does NOT contain `node_modules` from deps stage
549
+ - [ ] Backend runner uses production-only `node_modules` with `@plumbus/ui` stripped
550
+ - [ ] Image sizes: frontends ~150-200 MB, backend ~500-700 MB
551
+
552
+ ### Docker — Build Correctness
553
+
554
+ - [ ] `.npmrc` copied into all Docker images
555
+ - [ ] `pnpm deploy --legacy` used to isolate frontend deps (not hoisted full-workspace install)
556
+ - [ ] `ENV PATH="/app/deployed/node_modules/.bin:$PATH"` set before `next build`
557
+ - [ ] `outputFileTracingExcludes` configured in `next.config.mjs` (typescript, @swc/core)
558
+ - [ ] Backend `proddeps` cleans up vitest/playwright/esbuild/typescript after npm install
559
+ - [ ] `.plumbus/` generated inside Docker (not copied from host)
560
+ - [ ] `plumbus ui generate` run before building frontends
561
+ - [ ] `.dockerignore` excludes `node_modules/`, `dist/`, `.git/`, `uploads/`, `**/e2e/`
562
+
563
+ ### Volumes and Networking
564
+
330
565
  - [ ] Persistent volumes for PostgreSQL data and uploads
331
- - [ ] AI provider API keys configured
332
- - [ ] Redis available for job queue and event dispatch
566
+ - [ ] `API_BASE_URL` set to Docker service name (e.g., `http://backend:3000`)
567
+ - [ ] `NEXT_PUBLIC_API_BASE_URL` set to empty string `""` (for server-side proxy)
568
+ - [ ] `AUTH_COOKIE_SECURE` set to `"true"` (or `"false"` only for HTTP-only — security trade-off)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plumbus/core",
3
- "version": "0.2.2",
3
+ "version": "0.2.4",
4
4
  "description": "Plumbus framework core — types, SDK, runtime, CLI, test utilities",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",