@chidchanun/bcp 0.1.27 → 0.1.28

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,9 +2,9 @@
2
2
 
3
3
  BCP Framework is a React full-stack framework focused on file-based routing, SSR, server data loading, guarded application flows, API routes, authentication, database access, validation, logging, uploads, storage and standalone production deployment.
4
4
 
5
- > **Development target:** `0.1.27`
5
+ > **Development target:** `0.1.28`
6
6
  >
7
- > BCP is pre-1.0. The `0.1.27` source adds the Storage Ecosystem and remains an unreleased development target until local validation, RC checks, tagging and npm publication complete.
7
+ > BCP is pre-1.0. The `0.1.28` source adds Production Hardening and remains an unreleased development target until local validation, RC checks, tagging and npm publication complete.
8
8
 
9
9
  ## Current capabilities
10
10
 
@@ -25,7 +25,7 @@ BCP Framework is a React full-stack framework focused on file-based routing, SSR
25
25
  | Storage | Local + S3-compatible adapters, streaming, listing, copy/move, metadata, bulk delete and signed S3 URLs |
26
26
  | Caching | Response cache and revalidation primitives |
27
27
  | Developer tools | `doctor`, `inspect`, updater and route inspection |
28
- | Production | Standalone server build with production middleware pipeline |
28
+ | Production | Standalone build, hardening gateway, graceful shutdown, trusted-proxy controls and HTTP timeouts |
29
29
 
30
30
  ## Requirements
31
31
 
@@ -229,6 +229,8 @@ const storage =
229
229
  });
230
230
  ```
231
231
 
232
+ `create-bcp-app --storage local` creates `lib/storage.ts` plus a visible `storage/README.md` / `.gitkeep` scaffold. Runtime objects remain ignored by Git.
233
+
232
234
  ### S3 / R2 / MinIO
233
235
 
234
236
  ```ts
@@ -284,72 +286,9 @@ import {
284
286
  } from "bcp/server";
285
287
  ```
286
288
 
287
- Listing supports prefix filtering, limits and opaque cursors:
288
-
289
- ```ts
290
- const page =
291
- await listStorageObjects(
292
- storage,
293
- {
294
- prefix: "documents/",
295
- limit: 50,
296
- }
297
- );
298
- ```
299
-
300
- Copy and move use native adapter operations when available, with portable fallback behavior where practical:
301
-
302
- ```ts
303
- await copyStorageObject(
304
- storage,
305
- "incoming/report.pdf",
306
- "archive/report.pdf"
307
- );
308
-
309
- await moveStorageObject(
310
- storage,
311
- "tmp/avatar.webp",
312
- "users/42/avatar.webp"
313
- );
314
- ```
315
-
316
- Portable user metadata is string-to-string metadata and is distinct from application authorization/business state:
317
-
318
- ```ts
319
- await setStorageMetadata(
320
- storage,
321
- "archive/report.pdf",
322
- {
323
- owner: "user-42",
324
- status: "approved",
325
- }
326
- );
327
- ```
328
-
329
- S3-compatible storage also supports short-lived presigned direct-transfer URLs:
289
+ Listing supports prefix filtering, limits and opaque cursors. Copy and move use native adapter operations when available, with portable fallback behavior where practical. Portable user metadata is string-to-string metadata and is distinct from authorization/business state.
330
290
 
331
- ```ts
332
- const downloadUrl =
333
- await createStorageSignedReadUrl(
334
- storage,
335
- "videos/demo.mp4",
336
- {
337
- expiresIn: 300,
338
- }
339
- );
340
-
341
- const uploadUrl =
342
- await createStorageSignedWriteUrl(
343
- storage,
344
- "uploads/demo.mp4",
345
- {
346
- expiresIn: 300,
347
- contentType: "video/mp4",
348
- }
349
- );
350
- ```
351
-
352
- Local storage intentionally does not emulate signed URLs. Signed URLs are temporary credentials and should only be generated after application authorization.
291
+ S3-compatible storage also supports short-lived presigned direct-transfer URLs. Local storage intentionally does not emulate signed URLs.
353
292
 
354
293
  Read more: [Storage Ecosystem](docs/storage-ecosystem.md)
355
294
 
@@ -401,7 +340,38 @@ Standalone output is written under:
401
340
  └─ server.mjs
402
341
  ```
403
342
 
404
- Read more: [Deployment](docs/deployment.md)
343
+ ### Production Hardening — 0.1.28
344
+
345
+ The standalone runtime is wrapped by a public hardening gateway with configurable HTTP and shutdown behavior:
346
+
347
+ ```dotenv
348
+ BCP_REQUEST_TIMEOUT_MS=120000
349
+ BCP_HEADERS_TIMEOUT_MS=66000
350
+ BCP_KEEP_ALIVE_TIMEOUT_MS=65000
351
+ BCP_SHUTDOWN_TIMEOUT_MS=10000
352
+ BCP_TRUST_PROXY=false
353
+ ```
354
+
355
+ Standalone production handles `SIGTERM` and `SIGINT`, drains the public listener, runs application cleanup hooks and then stops internal runtime layers.
356
+
357
+ ```ts
358
+ import {
359
+ registerShutdownHook,
360
+ } from "bcp/server";
361
+
362
+ registerShutdownHook(
363
+ () => {
364
+ storage.destroy();
365
+ },
366
+ {
367
+ name: "storage",
368
+ }
369
+ );
370
+ ```
371
+
372
+ Trusted proxy mode is disabled by default. Enable `BCP_TRUST_PROXY=true` only when direct untrusted traffic cannot bypass the trusted reverse proxy/load balancer.
373
+
374
+ Read more: [Production Hardening](docs/production-hardening.md)
405
375
 
406
376
  ## Windows CLI
407
377
 
@@ -428,7 +398,7 @@ Read more: [Developer Tools](docs/developer-tools.md)
428
398
  bcp update
429
399
  bcp update --check
430
400
  bcp update --dry-run
431
- bcp update 0.1.27
401
+ bcp update 0.1.28
432
402
  bcp update next
433
403
  ```
434
404
 
@@ -468,6 +438,7 @@ Recommended starting points:
468
438
  - [Storage and File Delivery](docs/storage.md)
469
439
  - [Storage Ecosystem](docs/storage-ecosystem.md)
470
440
  - [S3-Compatible Storage](docs/s3-storage.md)
441
+ - [Production Hardening](docs/production-hardening.md)
471
442
  - [Authentication](docs/authentication.md)
472
443
  - [Database](docs/database.md)
473
444
  - [Middleware](docs/middleware.md)
@@ -487,20 +458,19 @@ Recommended starting points:
487
458
  | `0.1.25` | Storage Adapters and File Delivery |
488
459
  | `0.1.26` | S3-Compatible Storage and Production Streaming |
489
460
  | `0.1.27` | Storage Ecosystem |
461
+ | `0.1.28` | Production Hardening |
490
462
 
491
463
  ## Next direction
492
464
 
493
- After `0.1.27`, the planned milestone is **`0.1.28Production Hardening`**:
465
+ After `0.1.28`, the planned milestone is **`0.1.29Developer Experience`**:
494
466
 
495
- 1. graceful HTTP shutdown and active-request draining,
496
- 2. database/S3 resource cleanup,
497
- 3. trusted proxy and forwarded-header handling,
498
- 4. request/server timeout controls,
499
- 5. security hardening,
500
- 6. Docker and standalone-runtime regression coverage,
501
- 7. stronger startup/runtime diagnostics.
467
+ 1. route/API/middleware/migration generators,
468
+ 2. richer `doctor` and `inspect` diagnostics,
469
+ 3. clearer build/runtime error messages,
470
+ 4. improved create-app presets and automation flags,
471
+ 5. production configuration diagnostics.
502
472
 
503
- These are roadmap items, not `0.1.27` guarantees.
473
+ These are roadmap items, not `0.1.28` guarantees.
504
474
 
505
475
  ## License
506
476
 
package/docs/README.md CHANGED
@@ -1,10 +1,10 @@
1
1
  # BCP Framework Documentation Source
2
2
 
3
- The `docs/` directory is the documentation source of truth for BCP Framework and is intentionally organized for the future **`bcp-docs-web`** website.
3
+ The `docs/` directory is the documentation source of truth for BCP Framework and is intentionally organized for the **`bcp-docs-web`** website.
4
4
 
5
- > **Documentation target:** BCP Framework `0.1.27`
5
+ > **Documentation target:** BCP Framework `0.1.28`
6
6
  >
7
- > **Release state:** unreleased development target. Do not label `0.1.27` as published until RC validation and npm publication complete.
7
+ > **Release state:** unreleased development target. Do not label `0.1.28` as published until RC validation and npm publication complete.
8
8
 
9
9
  ## Documentation flow
10
10
 
@@ -88,6 +88,7 @@ Releases
88
88
  | `/docs/development-logging` | `development-logging.md` | Logging/observability |
89
89
  | `/docs/caching` | `caching.md` | Cache/revalidation |
90
90
  | `/docs/security` | `security.md` | Security/body limits |
91
+ | `/docs/production-hardening` | `production-hardening.md` | Graceful shutdown, trusted proxy and production timeouts |
91
92
 
92
93
  ### Storage & Uploads
93
94
 
@@ -117,7 +118,7 @@ bcp/middleware
117
118
  | Entrypoint | Primary guide |
118
119
  | --- | --- |
119
120
  | `bcp` | `routing.md`, `server-data-loaders.md`, `form-actions.md` |
120
- | `bcp/server` | `server-request-apis.md`, `file-upload.md`, `storage.md`, `storage-ecosystem.md`, `s3-storage.md`, `development-logging.md` |
121
+ | `bcp/server` | `server-request-apis.md`, `file-upload.md`, `storage.md`, `storage-ecosystem.md`, `s3-storage.md`, `development-logging.md`, `production-hardening.md` |
121
122
  | `bcp/auth` | `authentication.md`, `auth-route-guards.md` |
122
123
  | `bcp/database` | `database.md`, `database-migrations.md` |
123
124
  | `bcp/validation` | `validation.md` |
@@ -139,7 +140,7 @@ bcp/middleware
139
140
  - client islands / partial hydration
140
141
  - Fast Refresh
141
142
 
142
- ### Server
143
+ ### Server and production runtime
143
144
 
144
145
  - request-scoped APIs/cookies/redirects/request IDs
145
146
  - JWT sessions/authentication
@@ -147,6 +148,11 @@ bcp/middleware
147
148
  - validation/structured errors
148
149
  - structured logging
149
150
  - response caching/security gateway
151
+ - production hardening gateway
152
+ - configurable request/header/keep-alive/shutdown timeouts
153
+ - `SIGTERM` / `SIGINT` graceful shutdown
154
+ - application shutdown hooks
155
+ - trusted-proxy forwarding-header controls
150
156
 
151
157
  ### Storage & Uploads
152
158
 
@@ -163,6 +169,7 @@ bcp/middleware
163
169
  - portable user metadata
164
170
  - bulk deletion
165
171
  - S3 presigned read/write URLs
172
+ - create-app Local Server / Amazon S3 / Cloudflare R2 presets
166
173
 
167
174
  ### Database
168
175
 
@@ -182,66 +189,56 @@ bcp/middleware
182
189
  - standalone production build
183
190
  - unit/integration/E2E/package/RC checks
184
191
 
185
- ## BCP 0.1.27 documentation focus
192
+ ## BCP 0.1.28 documentation focus
186
193
 
187
- ### Storage ecosystem
194
+ ### Production hardening
188
195
 
189
- New generic helpers include:
196
+ Public cleanup API:
190
197
 
191
198
  ```ts
192
199
  import {
193
- copyStorageObject,
194
- createStorageSignedReadUrl,
195
- createStorageSignedWriteUrl,
196
- deleteStorageObjects,
197
- getStorageEcosystemCapabilities,
198
- getStorageMetadata,
199
- listStorageObjects,
200
- moveStorageObject,
201
- setStorageMetadata,
200
+ getProductionHardeningConfig,
201
+ registerShutdownHook,
202
202
  } from "bcp/server";
203
203
  ```
204
204
 
205
+ Production environment controls:
206
+
207
+ ```dotenv
208
+ BCP_REQUEST_TIMEOUT_MS=120000
209
+ BCP_HEADERS_TIMEOUT_MS=66000
210
+ BCP_KEEP_ALIVE_TIMEOUT_MS=65000
211
+ BCP_SHUTDOWN_TIMEOUT_MS=10000
212
+ BCP_TRUST_PROXY=false
213
+ ```
214
+
205
215
  Document these boundaries clearly:
206
216
 
207
- - ecosystem methods are additive and do not invalidate older `StorageAdapter` implementations,
208
- - listing cursors are opaque,
209
- - overwrite remains opt-in,
210
- - move is copy + delete rather than an atomic distributed transaction,
211
- - application metadata is portable string metadata and not authorization/business state,
212
- - `bcp_sha256` is reserved for framework checksum metadata,
213
- - local storage does not emulate signed URLs,
214
- - S3 direct-transfer URLs are temporary credentials and require application authorization before issuance.
217
+ - trusted proxy mode is off by default,
218
+ - forwarding headers are sanitized when trust is disabled,
219
+ - enable trust only behind a trusted reverse proxy/load balancer,
220
+ - Docker/process-manager `SIGTERM` triggers graceful shutdown,
221
+ - shutdown hooks are process-lifetime resource cleanup, not request cleanup,
222
+ - shutdown force-closes remaining public connections after the configured timeout.
215
223
 
216
- See `storage-ecosystem.md`.
224
+ See `production-hardening.md`.
217
225
 
218
- ### S3 ecosystem
226
+ ### Local Server generator fix
219
227
 
220
- `createS3Storage()` keeps the same public constructor and now adds:
228
+ `create-bcp-app --storage local` now generates:
221
229
 
222
230
  ```text
223
- ListObjectsV2
224
- CopyObject
225
- DeleteObjects
226
- presigned GetObject
227
- presigned PutObject
228
- application metadata
231
+ lib/storage.ts
232
+ storage/
233
+ ├─ .gitkeep
234
+ └─ README.md
229
235
  ```
230
236
 
231
- See `s3-storage.md`.
232
-
233
- ### 0.1.26 foundations remain supported
237
+ Runtime objects remain ignored while the scaffold stays visible/tracked.
234
238
 
235
- The following remain part of the storage stack:
239
+ ### 0.1.27 storage ecosystem remains supported
236
240
 
237
- ```text
238
- putStorageStream()
239
- readStorageStream()
240
- storeMultipartFile()
241
- createStorageResponse()
242
- ```
243
-
244
- `0.1.27` builds on these rather than replacing them.
241
+ The storage ecosystem continues to include listing, copy/move, portable metadata, bulk deletion and S3 signed URLs. `0.1.28` hardens deployment/runtime behavior rather than replacing those APIs.
245
242
 
246
243
  ## CLI commands
247
244
 
@@ -285,12 +282,11 @@ docs/releases/
285
282
  Recommended routes:
286
283
 
287
284
  ```text
285
+ /releases/0.1.28
288
286
  /releases/0.1.27
289
287
  /releases/0.1.26
290
288
  /releases/0.1.25
291
289
  /releases/0.1.24
292
- /releases/0.1.23
293
- /releases/0.1.22
294
290
  ```
295
291
 
296
292
  Use one of these release states:
@@ -319,8 +315,6 @@ interface DocPage {
319
315
  }
320
316
  ```
321
317
 
322
- The first `bcp-docs-web` implementation can keep this mapping in a route manifest and consume Markdown directly.
323
-
324
318
  ## Suggested website features
325
319
 
326
320
  1. Sidebar navigation from this source map.
@@ -365,16 +359,9 @@ npm run test:package
365
359
  npm run rc:check
366
360
  ```
367
361
 
368
- For `0.1.27`, package validation must also confirm the staged artifact includes:
362
+ For `0.1.28`, package validation must confirm the staged artifact includes the production hardening runtime/gateway, shutdown APIs, Local Server generator fix, and the existing S3 runtime dependencies.
369
363
 
370
- ```text
371
- @aws-sdk/client-s3
372
- @aws-sdk/lib-storage
373
- @aws-sdk/s3-request-presigner
374
- busboy
375
- ```
376
-
377
- and the ecosystem runtime/public exports.
364
+ A representative Docker app should also be built and stopped with `docker stop` to verify graceful `SIGTERM` shutdown.
378
365
 
379
366
  ## Documentation QA checklist
380
367
 
@@ -383,36 +370,24 @@ and the ecosystem runtime/public exports.
383
370
  - version/release states are current,
384
371
  - Windows direct CLI examples use `npm exec -- bcp-framework`,
385
372
  - upload/storage/auth security caveats are present,
386
- - route names match the current router,
387
373
  - release notes match the framework version,
388
374
  - S3 credentials are never shown as public browser variables,
389
375
  - signed URLs are documented as temporary credentials,
376
+ - proxy trust is documented as opt-in,
390
377
  - roadmap APIs are not presented as published guarantees.
391
378
 
392
- ## Next direction after 0.1.27
379
+ ## Next direction after 0.1.28
393
380
 
394
- Planned `0.1.28Production Hardening` focus:
381
+ Planned `0.1.29Developer Experience` focus:
395
382
 
396
- - graceful HTTP shutdown and active-request draining,
397
- - database/S3 resource cleanup,
398
- - trusted proxy and forwarded-header handling,
399
- - timeout controls,
400
- - security hardening,
401
- - Docker/standalone runtime regression coverage,
402
- - startup/runtime dependency diagnostics.
383
+ - generators for common framework files,
384
+ - richer `doctor` and `inspect`,
385
+ - improved build/runtime diagnostics,
386
+ - create-app preset improvements,
387
+ - production configuration diagnostics.
403
388
 
404
389
  These remain roadmap items until their source/tests land.
405
390
 
406
391
  ## Repository authority
407
392
 
408
- The framework repository remains authoritative for:
409
-
410
- ```text
411
- source
412
- public exports
413
- tests
414
- docs
415
- release notes
416
- ```
417
-
418
- `bcp-docs-web` is the presentation/search/navigation layer for this content.
393
+ The framework repository remains authoritative for source, public exports, tests, docs and release notes. `bcp-docs-web` is the presentation/search/navigation layer for this content.
@@ -0,0 +1,152 @@
1
+ # Production Hardening
2
+
3
+ BCP Framework `0.1.28` adds a production hardening gateway around the standalone runtime.
4
+
5
+ The gateway is the public HTTP listener. Existing security, cache, critical CSS, page/API and SSR runtimes remain bound to internal loopback ports behind it.
6
+
7
+ ## Default production behavior
8
+
9
+ The hardening gateway configures Node HTTP timeouts and graceful shutdown defaults:
10
+
11
+ ```text
12
+ Request timeout: 120000 ms
13
+ Headers timeout: 66000 ms
14
+ Keep-alive timeout: 65000 ms
15
+ Shutdown timeout: 10000 ms
16
+ Trust proxy: false
17
+ ```
18
+
19
+ Override them with server-only environment variables:
20
+
21
+ ```dotenv
22
+ BCP_REQUEST_TIMEOUT_MS=120000
23
+ BCP_HEADERS_TIMEOUT_MS=66000
24
+ BCP_KEEP_ALIVE_TIMEOUT_MS=65000
25
+ BCP_SHUTDOWN_TIMEOUT_MS=10000
26
+ BCP_TRUST_PROXY=false
27
+ ```
28
+
29
+ `BCP_HEADERS_TIMEOUT_MS` must be greater than `BCP_KEEP_ALIVE_TIMEOUT_MS`.
30
+
31
+ ## Graceful shutdown
32
+
33
+ Standalone production now handles `SIGTERM` and `SIGINT`.
34
+
35
+ This is especially important for Docker, Kubernetes and process managers that send `SIGTERM` before stopping a process.
36
+
37
+ Shutdown order:
38
+
39
+ ```text
40
+ stop accepting public traffic
41
+
42
+ drain/close public HTTP connections
43
+
44
+ run application shutdown hooks
45
+
46
+ stop internal BCP runtime layers
47
+
48
+ process exits naturally
49
+ ```
50
+
51
+ If the public server does not close before `BCP_SHUTDOWN_TIMEOUT_MS`, BCP force-closes remaining connections.
52
+
53
+ ## Application shutdown hooks
54
+
55
+ Register cleanup work through `bcp/server`:
56
+
57
+ ```ts
58
+ import {
59
+ registerShutdownHook,
60
+ } from "bcp/server";
61
+
62
+ const unregister =
63
+ registerShutdownHook(
64
+ async () => {
65
+ await closeApplicationResources();
66
+ },
67
+ {
68
+ name: "application-resources",
69
+ }
70
+ );
71
+ ```
72
+
73
+ Hooks run in reverse registration order. Calling the returned function unregisters the hook.
74
+
75
+ For S3-compatible storage, an application can register the adapter client cleanup when it owns the adapter for the lifetime of the process:
76
+
77
+ ```ts
78
+ registerShutdownHook(
79
+ () => {
80
+ storage.destroy();
81
+ },
82
+ {
83
+ name: "s3-storage",
84
+ }
85
+ );
86
+ ```
87
+
88
+ Do not register request-scoped resources as process shutdown hooks.
89
+
90
+ ## Trusted proxy mode
91
+
92
+ The hardening gateway sanitizes forwarding headers by default.
93
+
94
+ ```dotenv
95
+ BCP_TRUST_PROXY=false
96
+ ```
97
+
98
+ This prevents a direct client from spoofing headers such as:
99
+
100
+ ```text
101
+ Forwarded
102
+ X-Forwarded-For
103
+ X-Forwarded-Host
104
+ X-Forwarded-Proto
105
+ X-Real-IP
106
+ ```
107
+
108
+ When BCP is deployed only behind a trusted reverse proxy such as Nginx, Cloudflare Tunnel or a trusted load balancer, enable:
109
+
110
+ ```dotenv
111
+ BCP_TRUST_PROXY=true
112
+ ```
113
+
114
+ BCP then preserves the trusted proxy forwarding chain and appends the immediate peer address where appropriate.
115
+
116
+ Only enable trusted proxy mode when direct untrusted traffic cannot bypass the trusted proxy.
117
+
118
+ ## Docker
119
+
120
+ A production compose service can use:
121
+
122
+ ```yaml
123
+ services:
124
+ app:
125
+ init: true
126
+ restart: unless-stopped
127
+ environment:
128
+ NODE_ENV: production
129
+ BCP_HOSTNAME: 0.0.0.0
130
+ BCP_PORT: 3000
131
+ BCP_SHUTDOWN_TIMEOUT_MS: 10000
132
+ BCP_TRUST_PROXY: "true"
133
+ ```
134
+
135
+ Set `BCP_TRUST_PROXY=true` only when the container receives requests exclusively from a trusted reverse proxy/network path.
136
+
137
+ Docker's stop grace period should be longer than `BCP_SHUTDOWN_TIMEOUT_MS` so BCP gets time to drain connections and cleanup resources.
138
+
139
+ ## Local Server storage scaffold
140
+
141
+ `create-bcp-app --storage local` now creates:
142
+
143
+ ```text
144
+ lib/storage.ts
145
+ storage/
146
+ ├─ .gitkeep
147
+ └─ README.md
148
+ ```
149
+
150
+ Runtime objects inside `storage/` are ignored while the scaffold files remain tracked. The generated README explains that BCP writes objects and `.bcp-storage-meta` metadata there at runtime.
151
+
152
+ The directory therefore no longer appears as an unexplained empty folder before the first upload.