@nodefony/http 10.0.0-alpha.8 → 10.0.0-beta.1

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 (98) hide show
  1. package/README.md +7 -0
  2. package/dist/_virtual/{_@oxc-project_runtime@0.150.0 → _@oxc-project_runtime@0.151.0}/helpers/esm/decorate.js +1 -1
  3. package/dist/_virtual/{_@oxc-project_runtime@0.150.0 → _@oxc-project_runtime@0.151.0}/helpers/esm/decorateMetadata.js +1 -1
  4. package/dist/_virtual/{_@oxc-project_runtime@0.150.0 → _@oxc-project_runtime@0.151.0}/helpers/esm/decorateParam.js +1 -1
  5. package/dist/index.js +6 -4
  6. package/dist/nodefony/command/assetsPublishCommand.js +1 -1
  7. package/dist/nodefony/command/certificatesCommand.js +1 -1
  8. package/dist/nodefony/command/networkCommand.js +1 -1
  9. package/dist/nodefony/command/proxyGenerateCommand.js +1 -1
  10. package/dist/nodefony/config/config.js +22 -1
  11. package/dist/nodefony/interfaces/IRouting.js +1 -0
  12. package/dist/nodefony/interfaces/ISecurity.js +1 -0
  13. package/dist/nodefony/service/ProfilerAdminApi.js +2 -1
  14. package/dist/nodefony/service/audit-logger.js +16 -13
  15. package/dist/nodefony/service/certificates.js +25 -13
  16. package/dist/nodefony/service/error-renderer.js +118 -5
  17. package/dist/nodefony/service/http-kernel.js +342 -165
  18. package/dist/nodefony/service/pretty-request-logger.js +3 -10
  19. package/dist/nodefony/service/servers/clientError.js +2 -2
  20. package/dist/nodefony/service/servers/server-http.js +23 -23
  21. package/dist/nodefony/service/servers/server-https.js +41 -42
  22. package/dist/nodefony/service/servers/server-static.js +13 -17
  23. package/dist/nodefony/service/servers/server-websocket-secure.js +11 -10
  24. package/dist/nodefony/service/servers/server-websocket.js +11 -10
  25. package/dist/nodefony/service/servers/wsHeartbeat.js +2 -2
  26. package/dist/nodefony/service/sessions/sessions-service.js +25 -17
  27. package/dist/nodefony/service/trace.js +1 -1
  28. package/dist/nodefony/service/upload/upload-service.js +11 -10
  29. package/dist/nodefony/src/assets/prebuiltUi.js +1 -1
  30. package/dist/nodefony/src/context/Context.js +34 -19
  31. package/dist/nodefony/src/context/forwarded.js +4 -4
  32. package/dist/nodefony/src/context/http/HttpContext.js +133 -88
  33. package/dist/nodefony/src/context/http/Request.js +93 -44
  34. package/dist/nodefony/src/context/http/Response.js +107 -51
  35. package/dist/nodefony/src/context/http/parser.js +9 -10
  36. package/dist/nodefony/src/context/http/urlFastPath.js +1 -2
  37. package/dist/nodefony/src/context/http2/Response.js +53 -25
  38. package/dist/nodefony/src/context/metaData.js +1 -1
  39. package/dist/nodefony/src/context/trustProxy.js +4 -3
  40. package/dist/nodefony/src/context/websocket/Response.js +13 -6
  41. package/dist/nodefony/src/context/websocket/WebsocketContext.js +59 -26
  42. package/dist/nodefony/src/context/websocket/wsCloseReason.js +41 -0
  43. package/dist/nodefony/src/cookies/cookie.js +3 -3
  44. package/dist/nodefony/src/errors/httpError.js +6 -6
  45. package/dist/nodefony/src/errors/thrownMessage.js +27 -0
  46. package/dist/nodefony/src/profiler/FrameProfile.js +4 -3
  47. package/dist/nodefony/src/profiler/Profiler.js +4 -4
  48. package/dist/nodefony/src/servers/kernelServers.js +29 -0
  49. package/dist/nodefony/src/servers/portBinder.js +8 -21
  50. package/dist/nodefony/src/session/session.js +18 -10
  51. package/dist/nodefony/src/session/sessionStoreFailure.js +2 -1
  52. package/dist/nodefony/src/session/storage/RevocationGuardStorage.js +24 -9
  53. package/dist/types/index.d.ts +16 -0
  54. package/dist/types/nodefony/config/config.d.ts +22 -0
  55. package/dist/types/nodefony/interfaces/IContext.d.ts +24 -7
  56. package/dist/types/nodefony/interfaces/ICookie.d.ts +18 -18
  57. package/dist/types/nodefony/interfaces/IHttpKernel.d.ts +10 -4
  58. package/dist/types/nodefony/interfaces/IResponse.d.ts +1 -1
  59. package/dist/types/nodefony/interfaces/IRouting.d.ts +87 -0
  60. package/dist/types/nodefony/interfaces/ISecurity.d.ts +45 -0
  61. package/dist/types/nodefony/interfaces/ISession.d.ts +8 -8
  62. package/dist/types/nodefony/interfaces/IUpload.d.ts +9 -9
  63. package/dist/types/nodefony/interfaces/index.d.ts +2 -0
  64. package/dist/types/nodefony/service/audit-logger.d.ts +14 -10
  65. package/dist/types/nodefony/service/certificates.d.ts +10 -10
  66. package/dist/types/nodefony/service/error-renderer.d.ts +14 -0
  67. package/dist/types/nodefony/service/http-kernel.d.ts +192 -42
  68. package/dist/types/nodefony/service/servers/server-http.d.ts +4 -1
  69. package/dist/types/nodefony/service/servers/server-https.d.ts +4 -1
  70. package/dist/types/nodefony/service/servers/server-websocket-secure.d.ts +4 -1
  71. package/dist/types/nodefony/service/servers/server-websocket.d.ts +4 -1
  72. package/dist/types/nodefony/service/sessions/sessions-service.d.ts +6 -2
  73. package/dist/types/nodefony/service/upload/upload-service.d.ts +1 -1
  74. package/dist/types/nodefony/src/context/Context.d.ts +21 -10
  75. package/dist/types/nodefony/src/context/forwarded.d.ts +3 -3
  76. package/dist/types/nodefony/src/context/http/HttpContext.d.ts +75 -18
  77. package/dist/types/nodefony/src/context/http/Request.d.ts +39 -2
  78. package/dist/types/nodefony/src/context/http/Response.d.ts +47 -7
  79. package/dist/types/nodefony/src/context/http/parser.d.ts +1 -1
  80. package/dist/types/nodefony/src/context/http2/Response.d.ts +32 -2
  81. package/dist/types/nodefony/src/context/websocket/Response.d.ts +4 -4
  82. package/dist/types/nodefony/src/context/websocket/WebsocketContext.d.ts +11 -2
  83. package/dist/types/nodefony/src/context/websocket/wsCloseReason.d.ts +21 -0
  84. package/dist/types/nodefony/src/cookies/cookie.d.ts +9 -9
  85. package/dist/types/nodefony/src/errors/httpError.d.ts +7 -7
  86. package/dist/types/nodefony/src/errors/thrownMessage.d.ts +12 -0
  87. package/dist/types/nodefony/src/profiler/Profiler.d.ts +28 -28
  88. package/dist/types/nodefony/src/servers/kernelServers.d.ts +20 -0
  89. package/dist/types/nodefony/src/servers/portBinder.d.ts +6 -19
  90. package/dist/types/nodefony/src/session/session.d.ts +12 -12
  91. package/dist/types/nodefony/src/session/storage/RevocationGuardStorage.d.ts +1 -1
  92. package/docs/cookies.md +25 -25
  93. package/docs/observabilite.md +30 -30
  94. package/docs/rate-limit.md +22 -22
  95. package/docs/servers.md +61 -61
  96. package/docs/session.md +82 -77
  97. package/docs/upload.md +43 -37
  98. package/package.json +11 -11
package/docs/servers.md CHANGED
@@ -122,18 +122,18 @@ les probes.
122
122
 
123
123
  **Un port TLS qui parle deux protocoles.** Quand `servers.https.protocol` vaut `"2.0"` (le défaut),
124
124
  Nodefony crée un serveur HTTP/2 sécurisé avec `allowHTTP1: true` (`ServerHttps.createServerH2()`,
125
- `server-https.ts:174`) : les clients modernes négocient `h2` par ALPN, les autres restent en HTTP/1.1
125
+ `server-https.ts:187`) : les clients modernes négocient `h2` par ALPN, les autres restent en HTTP/1.1
126
126
  **sur le même port**. Le protocole effectif est relu socket par socket pour taguer le contexte
127
127
  (`server-https.ts:222`).
128
128
 
129
129
  **Le WebSocket n'est jamais un citoyen de seconde zone.** Il est adossé au serveur HTTP porteur
130
130
  (`server-websocket.ts:80`), passe par le **même** rate-limit d'IP que les requêtes HTTP — un upgrade
131
- _est_ une requête HTTP (`HttpKernel.onWebsocketRequest()`, `http-kernel.ts:1540`) —, hérite de la même
131
+ _est_ une requête HTTP (`HttpKernel.onWebsocketRequest()`, `http-kernel.ts:1619`) —, hérite de la même
132
132
  session et du même firewall, et se ferme avec le même soin qu'une réponse HTTP.
133
133
 
134
134
  > [!NOTE]
135
135
  > Le serveur HTTP/3 (QUIC) est **réservé, pas implémenté** : la clé `http3` existe dans le schéma,
136
- > marquée `reserved` (`config.ts:1035`). Elle ne fait rien aujourd'hui.
136
+ > marquée `reserved` (`config.ts:1050`). Elle ne fait rien aujourd'hui.
137
137
 
138
138
  ## 🚀 Démarrage rapide
139
139
 
@@ -249,7 +249,7 @@ export default PingController;
249
249
 
250
250
  ### 4. Ce qu'on observe au boot
251
251
 
252
- Le kernel démarre les serveurs à la phase `onReady` (`Kernel.ts:1214`), puis affiche les URL réellement
252
+ Le kernel démarre les serveurs à la phase `onReady` (`Kernel.ts:1274`), puis affiche les URL réellement
253
253
  en écoute — le récap de développement liste HTTP, HTTP/2, WS et WSS dans cet ordre
254
254
  (`BootReporter.ts:389`) :
255
255
 
@@ -264,7 +264,7 @@ en écoute — le récap de développement liste HTTP, HTTP/2, WS et WSS dans ce
264
264
  ```
265
265
 
266
266
  Hors écran animé (production, CI, `--debug`), ce sont les bannières par serveur qui sortent
267
- (`ServerHttp.showBanner()`, `server-http.ts:226`, appelées par le kernel — `Kernel.ts:545`) :
267
+ (`ServerHttp.showBanner()`, `server-http.ts:242`, appelées par le kernel — `Kernel.ts:561`) :
268
268
 
269
269
  ```text
270
270
  Server Listen on http://127.0.0.1:5151 Family: IPv4 Protocol : 1.1
@@ -327,34 +327,34 @@ Choisir en cinq secondes :
327
327
  | `server-websocket-secure` | `wss://` — 5152 | `server-https` actif | WebSocket adossé au serveur TLS. |
328
328
  | `server-static` | (aucun port propre) | toujours enregistré | Fichiers statiques, en **repli** après le routing. |
329
329
 
330
- L'assemblage est fait par `HttpKernel.initServers()` (`http-kernel.ts:1033`) : chaque serveur est
331
- consulté sur son drapeau `active`, un serveur désactivé est **sauté**, pas créé (`http-kernel.ts:235`).
330
+ L'assemblage est fait par `HttpKernel.initServers()` (`http-kernel.ts:1065`) : chaque serveur est
331
+ consulté sur son drapeau `active`, un serveur désactivé est **sauté**, pas créé (`http-kernel.ts:1062`).
332
332
  Les serveurs WebSocket ne sont montés que si leur porteur l'a été.
333
333
 
334
334
  ### `server-http` — HTTP/1.1 en clair
335
335
 
336
336
  Le plus simple, et celui qui porte le trafic en cloud-native. `ServerHttp.createServer()`
337
- (`server-http.ts:68`) crée le `http.Server`, applique les réglages de transport, branche
337
+ (`server-http.ts:83`) crée le `http.Server`, applique les réglages de transport, branche
338
338
  `onHttpRequest`, puis écoute selon la politique de port.
339
339
 
340
340
  - **Actif** si `servers.http` n'est pas `false` — la décision est prise dès le constructeur
341
341
  (`server-http.ts:56`), et le port est lu de la config d'app (`ServerHttp.setPort()`,
342
- `server-http.ts:61`).
342
+ `server-http.ts:74`).
343
343
  - **Réglages appliqués** : `requestTimeout` (anti slow-loris), `maxHeadersCount`, `timeout` de socket
344
344
  (qui émet un événement `onTimeout`), `keepAliveTimeout`.
345
345
  - **Erreurs de protocole** : l'événement `clientError` est traité explicitement
346
- (`server-http.ts:167`) — voir la section Résilience, c'est un piège Node à part entière.
346
+ (`server-http.ts:186`) — voir la section Résilience, c'est un piège Node à part entière.
347
347
 
348
348
  ### `server-https` — TLS, et HTTP/2 par défaut
349
349
 
350
- Deux branches dans un seul service, choisies sur `servers.https.protocol` (`server-https.ts:88`) :
350
+ Deux branches dans un seul service, choisies sur `servers.https.protocol` (`server-https.ts:100`) :
351
351
 
352
352
  - **`"2.0"` (défaut)** → `http2.createSecureServer` avec `allowHTTP1: true`
353
- (`ServerHttps.createServerH2()`, `server-https.ts:174`). Les bornes anti-DoS HTTP/2 ne sont posées
353
+ (`ServerHttps.createServerH2()`, `server-https.ts:187`). Les bornes anti-DoS HTTP/2 ne sont posées
354
354
  **que si elles sont configurées**, pour ne pas écraser les défauts de Node
355
- (`maxSessionMemory`, `server-https.ts:197`).
355
+ (`maxSessionMemory`, `server-https.ts:215`).
356
356
  Les erreurs de session et de flux sont journalisées sans tuer le serveur
357
- (`sessionError`, `server-https.ts:274`).
357
+ (`sessionError`, `server-https.ts:296`).
358
358
  - **`"1.1"`** → `https.createServer` classique.
359
359
 
360
360
  Le certificat vient du service `certificates`, lu au moment de la création via
@@ -375,7 +375,7 @@ automatiquement un éventuel décalage de port.
375
375
  réglages **forcés** par Nodefony : `server` et `clientTracking: true`, requis par `broadcast()` et par
376
376
  le battement de cœur (`server-websocket.ts:80`).
377
377
  - **Keep-alive** armé à la création (`startHeartbeat()`, `server-websocket.ts:87`) et par connexion
378
- (`trackPong()`, `server-websocket.ts:107`).
378
+ (`trackPong()`, `server-websocket.ts:103`).
379
379
  - **Arrêt** : il s'inscrit en tête des écouteurs de terminaison
380
380
  (`prependOnceListener`, `server-websocket.ts:91`) — l'ordre
381
381
  compte, voir la section Arrêt gracieux.
@@ -400,7 +400,7 @@ C'est la distinction la plus utile de cette page, et celle qu'on rate le plus so
400
400
  | Question | Où ça se règle | Source |
401
401
  | ----------------------------------------- | ------------------------------ | --------------------------------------------------------- |
402
402
  | **Quels** serveurs, sur **quels ports** ? | `servers` (config d'app) | `serversSchema` (`src/nodefony/src/config/schema.ts:149`) |
403
- | **Comment** ces serveurs se comportent ? | `use("@nodefony/http", { … })` | `httpConfigSchema` (`config.ts:953`) |
403
+ | **Comment** ces serveurs se comportent ? | `use("@nodefony/http", { … })` | `httpConfigSchema` (`config.ts:989`) |
404
404
 
405
405
  Autrement dit : la **topologie** est une propriété du déploiement (elle change entre le poste du dev,
406
406
  la CI et le cluster) ; le **réglage** est une propriété de l'application.
@@ -421,19 +421,19 @@ Défauts matérialisés dans `defaultAppConfig` (`src/nodefony/src/config/defaul
421
421
 
422
422
  ### Niveau 2 — le transport HTTP / HTTPS
423
423
 
424
- Table dérivée de `httpServerSchema` (`config.ts:257`) ; la section `https` reprend les mêmes clés et en
424
+ Table dérivée de `httpServerSchema` (`config.ts:272`) ; la section `https` reprend les mêmes clés et en
425
425
  ajoute une (`httpsServerSchema`, `config.ts:336`).
426
426
 
427
- | Option | Type | Défaut | Effet |
428
- | ---------------------------- | ----- | -------- | -------------------------------------------------------------------------------- |
429
- | `maxHeadersCount` | int | `2000` | Nombre maximum d'en-têtes par requête — anti _header flooding_. |
430
- | `keepAliveTimeout` | ms | `5000` | Délai de réutilisation de la socket TCP entre deux requêtes. |
431
- | `timeout` | ms | `120000` | Timeout global de socket. `0` = désactivé. |
432
- | `requestTimeout` | ms | `30000` | Délai de réception de la requête complète — **anti slow-loris**. |
433
- | `responseTimeout` | ms | `30000` | Délai d'envoi de la réponse complète (couche pipeline). |
434
- | `shutdownTimeout` | ms | `5000` | Drain au shutdown avant destruction forcée. Garder < grâce orchestrateur. |
435
- | `headers` | objet | `null` | En-têtes ajoutés à toutes les réponses. |
436
- | `rejectUnauthorized` (https) | bool | `false` | Rejette les certificats invalides. `false` en dev (auto-signés), `true` en prod. |
427
+ | Option | Type | Défaut | Effet |
428
+ | ---------------------------- | ----- | ------- | -------------------------------------------------------------------------------- |
429
+ | `maxHeadersCount` | int | `2000` | Nombre maximum d'en-têtes par requête — anti _header flooding_. |
430
+ | `keepAliveTimeout` | ms | `5000` | Délai de réutilisation de la socket TCP entre deux requêtes. |
431
+ | `timeout` | ms | `30000` | Timeout global de socket, aligné sur `responseTimeout`. `0` = désactivé. |
432
+ | `requestTimeout` | ms | `30000` | Délai de réception de la requête complète — **anti slow-loris**. |
433
+ | `responseTimeout` | ms | `30000` | Délai d'envoi de la réponse complète (couche pipeline). |
434
+ | `shutdownTimeout` | ms | `5000` | Drain au shutdown avant destruction forcée. Garder < grâce orchestrateur. |
435
+ | `headers` | objet | `null` | En-têtes ajoutés à toutes les réponses. |
436
+ | `rejectUnauthorized` (https) | bool | `false` | Rejette les certificats invalides. `false` en dev (auto-signés), `true` en prod. |
437
437
 
438
438
  Ces deux sections sont **permissives** (`z.looseObject`) : toute option supplémentaire de
439
439
  `http.Server` / `net.Server` / TLS (`insecureHTTPParser`, `ciphers`, `minVersion`…) est transmise telle
@@ -442,7 +442,7 @@ quelle à Node. C'est délibéré — un schéma strict effacerait silencieuseme
442
442
  ### Niveau 2 — HTTP/2
443
443
 
444
444
  Depuis `http2Schema` (`config.ts:353`), appliqué seulement si défini
445
- (`maxSessionMemory`, `server-https.ts:197`).
445
+ (`maxSessionMemory`, `server-https.ts:215`).
446
446
 
447
447
  | Option | Type | Défaut | Effet |
448
448
  | ---------------------- | ---- | ------ | ----------------------------------------------------------------------------- |
@@ -452,7 +452,7 @@ Depuis `http2Schema` (`config.ts:353`), appliqué seulement si défini
452
452
  ### Niveau 2 — WebSocket (`websocket` et `websocketSecure`)
453
453
 
454
454
  Depuis `websocketSchema` (`config.ts:517`). Les deux sections partagent la forme et les défauts ; le WSS
455
- lit `websocketSecure` (`config.ts:1043`).
455
+ lit `websocketSecure` (`config.ts:1058`).
456
456
 
457
457
  | Option | Type | Défaut | Effet |
458
458
  | ------------------------ | ------------------- | ------- | ---------------------------------------------------------------------------------- |
@@ -484,7 +484,7 @@ panne, c'est une nuisance.
484
484
 
485
485
  ### La règle
486
486
 
487
- `resolvePortPolicy()` (`portBinder.ts:74`) tranche selon l'environnement, et la valeur explicite gagne
487
+ `resolvePortPolicy()` (cœur, `src/nodefony/src/service/dev/devProcess.ts:1730` — la même règle sert `nodefony doctor`) tranche selon l'environnement, et la valeur explicite gagne
488
488
  toujours :
489
489
 
490
490
  | Environnement | Défaut | Pourquoi |
@@ -507,17 +507,17 @@ détails d'implémentation valent d'être connus, parce qu'ils expliquent des co
507
507
  1. **On retente au `listen()`, jamais après une sonde.** Demander « le port est-il libre ? » puis
508
508
  binder est une course : entre la réponse et le bind, un autre processus peut prendre le port. Le
509
509
  `listen()` est atomique — on retente donc sur l'échec réel (`bindWithFallback()`,
510
- `portBinder.ts:142`).
510
+ `portBinder.ts:263`).
511
511
  2. **Le port de l'autre serveur est réservé.** Si HTTP est chassé de 5151, incrémenter naïvement le
512
512
  ferait voler 5152 à HTTPS, qui se décalerait à son tour. Les ports convoités par les autres serveurs
513
- sont sautés d'emblée (`buildBindPlan()`, `portBinder.ts:104`, réservation `portBinder.ts:111`).
513
+ sont sautés d'emblée (`buildBindPlan()`, `portBinder.ts:105`, réservation `portBinder.ts:105`).
514
514
  3. **Le gestionnaire d'erreur durable est posé APRÈS le bind.** Attaché avant, il verrait passer les
515
515
  `EADDRINUSE` de repli et terminerait le kernel en croyant à une panne
516
- (`ServerHttp.attachErrorHandler()`, `server-http.ts:186`).
516
+ (`ServerHttp.attachErrorHandler()`, `server-http.ts:199`).
517
517
 
518
518
  Quand le bind échoue pour de bon — `strict`, ou tous les replis épuisés, ou une erreur qui n'est pas un
519
519
  conflit de port — c'est **fatal** : message explicite puis terminaison du processus
520
- (`ServerHttp.reportBindError()`, `server-http.ts:199`). Un serveur qui n'écoute pas ne doit jamais
520
+ (`ServerHttp.reportBindError()`, `server-http.ts:212`). Un serveur qui n'écoute pas ne doit jamais
521
521
  laisser un processus se croire démarré.
522
522
 
523
523
  ### Le corollaire : les ports effectifs sont publiés
@@ -537,12 +537,12 @@ serveur qui, lui, écoute très bien.
537
537
  **Générer un certificat est un confort de développement, pas une fonction de production.** Nodefony
538
538
  n'est pas une autorité de certification : en production, on fournit un vrai certificat (Let's Encrypt,
539
539
  ingress k8s, reverse-proxy). Le service crie un avertissement si ce n'est pas le cas
540
- (`Certificate.resolveStrategy()`, `certificates.ts:418`).
540
+ (`Certificate.resolveStrategy()`, `certificates.ts:407`).
541
541
 
542
542
  ### Les quatre stratégies
543
543
 
544
544
  Réglées par `certificates.strategy` (`certificatesSchema`, `config.ts:475`), résolues par
545
- `Certificate.resolveStrategy()` (`certificates.ts:379`).
545
+ `Certificate.resolveStrategy()` (`certificates.ts:407`).
546
546
 
547
547
  | Stratégie | Quand l'utiliser | Ce qui se passe |
548
548
  | --------------- | ------------------------------------------- | --------------------------------------------------------------------------- |
@@ -577,8 +577,8 @@ export default defineConfig(() => ({
577
577
  ### Ce que le chemin `explicit` évite
578
578
 
579
579
  `node-forge` est une grosse dépendance. Elle est chargée **paresseusement**, uniquement sur le chemin
580
- de génération (`Certificate.loadForge()`, `certificates.ts:227`) : en production avec un certificat
581
- fourni, elle n'entre jamais dans le processus (`certificates.ts:227`).
580
+ de génération (`Certificate.loadForge()`, `certificates.ts:238`) : en production avec un certificat
581
+ fourni, elle n'entre jamais dans le processus (`certificates.ts:238`).
582
582
 
583
583
  ### Conformité de l'auto-signé
584
584
 
@@ -588,7 +588,7 @@ invalid »). Celui de Nodefony respecte les règles qui comptent :
588
588
  | Exigence | Norme | Mise en œuvre |
589
589
  | --------------------------------------- | -------------------- | --------------------------------------------------------- |
590
590
  | Signature SHA-256, **jamais** SHA-1 | RFC 5280, CA/B Forum | `selfSigned.hash` par défaut `sha256` (`config.ts:400`) |
591
- | Numéro de série aléatoire 128 bits | RFC 5280 §4.1.2.2 | `Certificate.generateSerialHex()` (`certificates.ts:264`) |
591
+ | Numéro de série aléatoire 128 bits | RFC 5280 §4.1.2.2 | `Certificate.generateSerialHex()` (`certificates.ts:302`) |
592
592
  | Le SAN fait foi, pas le CN | RFC 6125 | SAN dérivé du kernel si non fourni (`config.ts:444`) |
593
593
  | `notBefore` reculé (décalage d'horloge) | pratique | `selfSigned.backdateMinutes`, défaut 5 (`config.ts:415`) |
594
594
  | Clé privée non lisible par tous | hygiène | `privateKeyMode` `0600` (`config.ts:499`) |
@@ -596,7 +596,7 @@ invalid »). Celui de Nodefony respecte les règles qui comptent :
596
596
  ### Régénération automatique
597
597
 
598
598
  Un certificat présent sur disque n'est pas forcément **adéquat**. `Certificate.isCertAdequate()`
599
- (`certificates.ts:542`) le régénère s'il est expiré, s'il est signé en SHA-1, ou si son SAN ne couvre
599
+ (`certificates.ts:579`) le régénère s'il est expiré, s'il est signé en SHA-1, ou si son SAN ne couvre
600
600
  plus les noms requis — le dernier cas est celui qui sauve : changer le domaine d'écoute sans ce
601
601
  contrôle laisserait un certificat obsolète en place indéfiniment.
602
602
 
@@ -618,7 +618,7 @@ sont ignorés** (`config.ts:965`), et l'IP retenue est celle de la socket réell
618
618
  | `"loopback"`, `"linklocal"`, `"uniquelocal"` | Préréglages de plages privées. |
619
619
 
620
620
  La politique est compilée **une seule fois** au premier usage (`HttpKernel.getTrustProxyChecker()`,
621
- `http-kernel.ts:538`, via `buildTrustProxy()`, `trustProxy.ts:100`) : aucune structure allouée par
621
+ `http-kernel.ts:562`, via `buildTrustProxy()`, `trustProxy.ts:100`) : aucune structure allouée par
622
622
  requête. La résolution de l'IP cliente remonte la chaîne **de droite à gauche** depuis la socket réelle
623
623
  (`resolveForwarded()`, `forwarded.ts:253`) — conforme RFC 7239 et à la recommandation OWASP.
624
624
 
@@ -626,7 +626,7 @@ requête. La résolution de l'IP cliente remonte la chaîne **de droite à gauch
626
626
 
627
627
  Barrière testée **avant le routage**, contre l'injection d'en-tête `Host`. Le domaine canonique du
628
628
  kernel est toujours accepté, plus le loopback en développement (`HttpKernel.compileAlias()`,
629
- `http-kernel.ts:936`). `false` (défaut) = ce socle seul ; une liste ajoute des vhosts (exact ou joker
629
+ `http-kernel.ts:964`). `false` (défaut) = ce socle seul ; une liste ajoute des vhosts (exact ou joker
630
630
  d'un seul niveau, `*.cdn.example.com`) ; `true` désactive la barrière — à réserver au cas où le proxy
631
631
  filtre déjà le `Host` (`config.ts:974`).
632
632
 
@@ -635,13 +635,13 @@ filtre déjà le `Host` (`config.ts:974`).
635
635
  Les navigateurs **n'appliquent pas CORS aux WebSockets**. Sans contrôle, une page tierce peut ouvrir un
636
636
  WS authentifié par le cookie de session de la victime (CSWSH, OWASP WSTG-CLNT-10). Nodefony exige donc
637
637
  par défaut que l'`Origin` du handshake corresponde au `Host` servi
638
- (`HttpKernel.checkWebsocketOrigin()`, `http-kernel.ts:599`), avec tolérance loopback en développement
638
+ (`HttpKernel.checkWebsocketOrigin()`, `http-kernel.ts:621`), avec tolérance loopback en développement
639
639
  et allowlist explicite pour les SPA cross-origine. Un refus se solde par une fermeture **1008 (Policy
640
640
  Violation)**, jamais par un code HTTP.
641
641
 
642
642
  > [!NOTE]
643
643
  > Une requête **sans** `Origin` (client non navigateur : script, agent, test) est acceptée
644
- > (`http-kernel.ts:551`). Ce n'est pas un trou : un attaquant non navigateur n'a aucun besoin de CSWSH,
644
+ > (`http-kernel.ts:630`). Ce n'est pas un trou : un attaquant non navigateur n'a aucun besoin de CSWSH,
645
645
  > il se connecte directement. Le contrôle protège les **utilisateurs**, pas le port.
646
646
 
647
647
  ## Probes de santé — `/livez` et `/readyz`
@@ -658,13 +658,13 @@ redémarrer le pod **en plein drain** par le kubelet, et casserait précisément
658
658
  protéger.
659
659
 
660
660
  Implémentation : court-circuit **total** du pipeline dans `HttpKernel.onHttpRequest()`
661
- (`http-kernel.ts:944`) — pas de contexte, pas de portée DI, pas de session, pas de journal par sonde,
662
- réponses pré-allouées (`HttpKernel.#respondHealth()`, `http-kernel.ts:473`). Et surtout : **avant le
661
+ (`http-kernel.ts:972`) — pas de contexte, pas de portée DI, pas de session, pas de journal par sonde,
662
+ réponses pré-allouées (`HttpKernel.#respondHealth()`, `http-kernel.ts:497`). Et surtout : **avant le
663
663
  rate-limit**. Un kubelet qui reçoit un `429` croit le pod mort → cascade de redémarrages.
664
664
 
665
665
  | Option | Type | Défaut | Effet |
666
666
  | --------------- | ------ | --------- | ---------------------------------------------------------------------- |
667
- | `enabled` | bool | `true` | Expose les probes (`healthSchema`, `config.ts:925`). |
667
+ | `enabled` | bool | `true` | Expose les probes (`healthSchema`, `config.ts:940`). |
668
668
  | `livenessPath` | string | `/livez` | Chemin de la sonde de vie (`livenessProbe.httpGet.path` k8s). |
669
669
  | `readinessPath` | string | `/readyz` | Chemin de la sonde de disponibilité. |
670
670
  | `shutdownDelay` | ms | `0` | Délai entre la bascule `503` et le début du drain (propagation du LB). |
@@ -732,8 +732,8 @@ inséré **en tête** et donc s'exécuter **en premier** (`http-kernel.ts:515`).
732
732
  load balancer cesse d'envoyer du trafic, et `shutdownDelay` laisse à cette information le temps de se
733
733
  propager.
734
734
 
735
- **Étape 2 — les clients WebSocket sont prévenus.** `Websocket.terminate()` (`server-websocket.ts:117`)
736
- envoie un message applicatif puis une **frame Close 1001 « Going Away »** (`server-websocket.ts:134`).
735
+ **Étape 2 — les clients WebSocket sont prévenus.** `Websocket.terminate()` (`server-websocket.ts:113`)
736
+ envoie un message applicatif puis une **frame Close 1001 « Going Away »** (`server-websocket.ts:113`).
737
737
  Sans elle, la coupure TCP ferait voir un **1006** au client — code réservé, jamais émis sur le fil,
738
738
  indiscernable d'une panne réseau. Avec 1001, le client sait qu'il doit simplement se reconnecter.
739
739
 
@@ -745,7 +745,7 @@ réponses en cours, sockets inactives fermées, destruction forcée au-delà de
745
745
  > [!IMPORTANT]
746
746
  > L'ordre WS-avant-HTTP n'est pas cosmétique. Les serveurs WebSocket s'inscrivent en
747
747
  > `prependOnceListener` (`server-websocket.ts:91`), les serveurs HTTP en `once`
748
- > (`server-http.ts:151`). Inversé, le terminator détruirait les sockets déjà upgradées **sans** frame
748
+ > (`server-http.ts:170`). Inversé, le terminator détruirait les sockets déjà upgradées **sans** frame
749
749
  > Close : retour du 1006 pour tous les clients temps réel.
750
750
 
751
751
  Deux garde-fous encadrent la séquence : `shutdownTimeout` **par serveur** (le drain nominal) et
@@ -784,13 +784,13 @@ processus à l'arrêt.
784
784
 
785
785
  L'upgrade WebSocket **est** une requête HTTP : il passe donc par le **même** compteur de rate-limit par
786
786
  IP que les requêtes ordinaires, vérifié avant toute allocation de contexte
787
- (`HttpKernel.onWebsocketRequest()`, `http-kernel.ts:1540`). Le `101` étant déjà émis par `ws`, un `429`
787
+ (`HttpKernel.onWebsocketRequest()`, `http-kernel.ts:1619`). Le `101` étant déjà émis par `ws`, un `429`
788
788
  est impossible → la connexion est fermée en **1013 « Try Again Later »**
789
- (`rateLimiter`, `http-kernel.ts:287`), sans
789
+ (`rateLimiter`, `http-kernel.ts:1644`), sans
790
790
  journalisation (un journal par handshake rejeté serait lui-même un amplificateur sous flood).
791
791
 
792
792
  Un second plafond, **désactivé par défaut**, borne le nombre de connexions **simultanées** par IP :
793
- `wsMaxConnectionsPerIp` (`config.ts:1052`). En cloud-native, laisser `null` et déléguer à l'edge —
793
+ `wsMaxConnectionsPerIp` (`config.ts:1067`). En cloud-native, laisser `null` et déléguer à l'edge —
794
794
  nginx `limit_conn`, HAProxy `sc_conn_cur` — qui voit tout le trafic, rejette avant le coût du
795
795
  descripteur et du TLS, et couvre tous les pods. Ne l'activer que sur une machine sans ingress.
796
796
 
@@ -809,7 +809,7 @@ par seconde. Les choix visibles dans le code :
809
809
  - **Aucun timer par connexion** — un `setInterval` par serveur WebSocket, `unref`, et deux `number` par
810
810
  socket (`wsHeartbeat.ts:69`).
811
811
  - **Rien de compilé par requête** — la politique de trust-proxy, celle des `Origin` WS et les motifs de
812
- `trustedHosts` sont compilés une fois et mémoïsés (`http-kernel.ts:937`, `http-kernel.ts:489`).
812
+ `trustedHosts` sont compilés une fois et mémoïsés (`http-kernel.ts:407`, `http-kernel.ts:407`).
813
813
  - **Rejets avant allocation** — rate-limit HTTP et bornes WS sont vérifiés avant le contexte, la portée
814
814
  DI et l'ALS : un flood coûte une recherche dans une table de hachage.
815
815
  - **Probes hors pipeline** — réponses pré-allouées, aucun objet créé, aucun journal
@@ -834,18 +834,18 @@ demande le backplane realtime.
834
834
 
835
835
  | Domaine | Norme | Ancrage |
836
836
  | ------------------------------------- | ------------------ | -------------------------------------------------------------------------- |
837
- | HTTP/1.1 (sémantique, message) | RFC 9110, 9112 | `node:http` + pipeline `HttpKernel.onHttpRequest()` (`http-kernel.ts:819`) |
838
- | HTTP/2 | RFC 9113 | `ServerHttps.createServerH2()` (`server-https.ts:174`) |
837
+ | HTTP/1.1 (sémantique, message) | RFC 9110, 9112 | `node:http` + pipeline `HttpKernel.onHttpRequest()` (`http-kernel.ts:972`) |
838
+ | HTTP/2 | RFC 9113 | `ServerHttps.createServerH2()` (`server-https.ts:187`) |
839
839
  | HTTP/2 Rapid Reset | CVE-2023-44487 | `maxConcurrentStreams` (`config.ts:355`) |
840
- | En-têtes trop volumineux → 431 | RFC 6585 §5 | `handleClientError()` (`clientError.ts:25`) |
840
+ | En-têtes trop volumineux → 431 | RFC 6585 §5 | `handleClientError()` (`clientError.ts:15`) |
841
841
  | WebSocket — protocole | RFC 6455 | `ws@8` + options (`config.ts:496`) |
842
- | WebSocket — Close 1001 « Going Away » | RFC 6455 §7.4.1 | `Websocket.terminate()` (`server-websocket.ts:134`) |
842
+ | WebSocket — Close 1001 « Going Away » | RFC 6455 §7.4.1 | `Websocket.terminate()` (`server-websocket.ts:113`) |
843
843
  | WebSocket — 1009 « Message Too Big » | RFC 6455 §7.4.1 | `maxPayload` (`config.ts:543`) |
844
844
  | WebSocket — validation UTF-8 | RFC 6455 §8.1 | `skipUTF8Validation` (`config.ts:623`) |
845
845
  | WebSocket — compression | RFC 7692 | `perMessageDeflate` (`config.ts:567`) |
846
- | CSWSH (Origin au handshake) | OWASP WSTG-CLNT-10 | `HttpKernel.checkWebsocketOrigin()` (`http-kernel.ts:599`) |
846
+ | CSWSH (Origin au handshake) | OWASP WSTG-CLNT-10 | `HttpKernel.checkWebsocketOrigin()` (`http-kernel.ts:621`) |
847
847
  | En-têtes forwarded | RFC 7239 | `resolveForwarded()` (`forwarded.ts:253`) |
848
- | Certificat — série, SAN, extensions | RFC 5280 | `Certificate.generateSerialHex()` (`certificates.ts:264`) |
848
+ | Certificat — série, SAN, extensions | RFC 5280 | `Certificate.generateSerialHex()` (`certificates.ts:302`) |
849
849
  | Certificat — identité par le SAN | RFC 6125 | `sanSchema` (`config.ts:442`) |
850
850
 
851
851
  ## ⚠️ Pièges (symptôme → cause → correction)
@@ -886,7 +886,7 @@ l'origine du transport.
886
886
 
887
887
  `proxy:generate` mérite un mot : la configuration nginx/HAProxy est **dérivée** des domaines de
888
888
  confiance, des ports effectifs et des dossiers statiques montés — donc elle ne diverge pas du code. Le
889
- résumé de certificat vient de `Certificate.describe()` (`certificates.ts:853`), source unique partagée
889
+ résumé de certificat vient de `Certificate.describe()` (`certificates.ts:862`), source unique partagée
890
890
  par la commande, le boot et un futur écran d'administration.
891
891
 
892
892
  **Runtime.** `nodefony status` et `nodefony stop` lisent les ports effectifs publiés au boot ; ils