@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 +49 -79
- package/docs/README.md +55 -80
- package/docs/production-hardening.md +152 -0
- package/docs/releases/0.1.28.md +134 -0
- package/package.json +1 -1
- package/packages/client/src/server.ts +9 -0
- package/packages/server/src/hardening-proxy.ts +393 -0
- package/packages/server/src/production-hardening.ts +374 -0
- package/packages/server/src/standalone-production-runtime-v7.ts +299 -0
- package/packages/server/src/standalone-production-server.ts +1 -1
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.
|
|
5
|
+
> **Development target:** `0.1.28`
|
|
6
6
|
>
|
|
7
|
-
> BCP is pre-1.0. The `0.1.
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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.
|
|
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.
|
|
465
|
+
After `0.1.28`, the planned milestone is **`0.1.29 — Developer Experience`**:
|
|
494
466
|
|
|
495
|
-
1.
|
|
496
|
-
2.
|
|
497
|
-
3.
|
|
498
|
-
4.
|
|
499
|
-
5.
|
|
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.
|
|
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
|
|
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.
|
|
5
|
+
> **Documentation target:** BCP Framework `0.1.28`
|
|
6
6
|
>
|
|
7
|
-
> **Release state:** unreleased development target. Do not label `0.1.
|
|
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.
|
|
192
|
+
## BCP 0.1.28 documentation focus
|
|
186
193
|
|
|
187
|
-
###
|
|
194
|
+
### Production hardening
|
|
188
195
|
|
|
189
|
-
|
|
196
|
+
Public cleanup API:
|
|
190
197
|
|
|
191
198
|
```ts
|
|
192
199
|
import {
|
|
193
|
-
|
|
194
|
-
|
|
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
|
-
-
|
|
208
|
-
-
|
|
209
|
-
-
|
|
210
|
-
-
|
|
211
|
-
-
|
|
212
|
-
-
|
|
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 `
|
|
224
|
+
See `production-hardening.md`.
|
|
217
225
|
|
|
218
|
-
###
|
|
226
|
+
### Local Server generator fix
|
|
219
227
|
|
|
220
|
-
`
|
|
228
|
+
`create-bcp-app --storage local` now generates:
|
|
221
229
|
|
|
222
230
|
```text
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
presigned PutObject
|
|
228
|
-
application metadata
|
|
231
|
+
lib/storage.ts
|
|
232
|
+
storage/
|
|
233
|
+
├─ .gitkeep
|
|
234
|
+
└─ README.md
|
|
229
235
|
```
|
|
230
236
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
### 0.1.26 foundations remain supported
|
|
237
|
+
Runtime objects remain ignored while the scaffold stays visible/tracked.
|
|
234
238
|
|
|
235
|
-
|
|
239
|
+
### 0.1.27 storage ecosystem remains supported
|
|
236
240
|
|
|
237
|
-
|
|
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.
|
|
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
|
-
|
|
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.
|
|
379
|
+
## Next direction after 0.1.28
|
|
393
380
|
|
|
394
|
-
Planned `0.1.
|
|
381
|
+
Planned `0.1.29 — Developer Experience` focus:
|
|
395
382
|
|
|
396
|
-
-
|
|
397
|
-
-
|
|
398
|
-
-
|
|
399
|
-
-
|
|
400
|
-
-
|
|
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.
|