@nodefony/http 10.0.0-alpha.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 (155) hide show
  1. package/LICENSE +544 -0
  2. package/README.md +77 -0
  3. package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorate.js +9 -0
  4. package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateMetadata.js +6 -0
  5. package/dist/_virtual/_@oxc-project_runtime@0.148.0/helpers/esm/decorateParam.js +8 -0
  6. package/dist/index.js +108 -0
  7. package/dist/nodefony/command/assetsPublishCommand.js +102 -0
  8. package/dist/nodefony/command/certificatesCommand.js +47 -0
  9. package/dist/nodefony/command/networkCommand.js +27 -0
  10. package/dist/nodefony/command/proxyGenerateCommand.js +66 -0
  11. package/dist/nodefony/config/config.js +335 -0
  12. package/dist/nodefony/config/defineModuleConfig.js +93 -0
  13. package/dist/nodefony/interfaces/IContext.js +1 -0
  14. package/dist/nodefony/interfaces/ICookie.js +1 -0
  15. package/dist/nodefony/interfaces/IErrorRenderer.js +1 -0
  16. package/dist/nodefony/interfaces/IHttpConfig.js +1 -0
  17. package/dist/nodefony/interfaces/IHttpKernel.js +1 -0
  18. package/dist/nodefony/interfaces/IRequest.js +1 -0
  19. package/dist/nodefony/interfaces/IRequestLogger.js +1 -0
  20. package/dist/nodefony/interfaces/IResponse.js +1 -0
  21. package/dist/nodefony/interfaces/ISession.js +1 -0
  22. package/dist/nodefony/interfaces/IUpload.js +1 -0
  23. package/dist/nodefony/interfaces/index.js +1 -0
  24. package/dist/nodefony/service/HttpAdminApi.js +376 -0
  25. package/dist/nodefony/service/ProfilerAdminApi.js +73 -0
  26. package/dist/nodefony/service/audit-logger.js +159 -0
  27. package/dist/nodefony/service/certificates.js +545 -0
  28. package/dist/nodefony/service/error-renderer.js +320 -0
  29. package/dist/nodefony/service/http-kernel.js +948 -0
  30. package/dist/nodefony/service/pretty-request-logger.js +72 -0
  31. package/dist/nodefony/service/request-logger.js +54 -0
  32. package/dist/nodefony/service/servers/clientError.js +20 -0
  33. package/dist/nodefony/service/servers/server-http.js +135 -0
  34. package/dist/nodefony/service/servers/server-https.js +204 -0
  35. package/dist/nodefony/service/servers/server-static.js +192 -0
  36. package/dist/nodefony/service/servers/server-websocket-secure.js +104 -0
  37. package/dist/nodefony/service/servers/server-websocket.js +104 -0
  38. package/dist/nodefony/service/servers/serverShutdown.js +31 -0
  39. package/dist/nodefony/service/servers/wsHeartbeat.js +64 -0
  40. package/dist/nodefony/service/sessions/sessions-service.js +580 -0
  41. package/dist/nodefony/service/trace.js +72 -0
  42. package/dist/nodefony/service/upload/upload-service.js +171 -0
  43. package/dist/nodefony/src/assets/collectAssets.js +34 -0
  44. package/dist/nodefony/src/assets/prebuiltUi.js +125 -0
  45. package/dist/nodefony/src/context/Context.js +415 -0
  46. package/dist/nodefony/src/context/domainMatcher.js +88 -0
  47. package/dist/nodefony/src/context/forwarded.js +185 -0
  48. package/dist/nodefony/src/context/http/HttpContext.js +309 -0
  49. package/dist/nodefony/src/context/http/Request.js +543 -0
  50. package/dist/nodefony/src/context/http/Response.js +368 -0
  51. package/dist/nodefony/src/context/http/parser.js +188 -0
  52. package/dist/nodefony/src/context/http/urlFastPath.js +103 -0
  53. package/dist/nodefony/src/context/http2/Request.js +29 -0
  54. package/dist/nodefony/src/context/http2/Response.js +97 -0
  55. package/dist/nodefony/src/context/metaData.js +47 -0
  56. package/dist/nodefony/src/context/requestId.js +41 -0
  57. package/dist/nodefony/src/context/trustProxy.js +167 -0
  58. package/dist/nodefony/src/context/websocket/Response.js +181 -0
  59. package/dist/nodefony/src/context/websocket/WebsocketContext.js +389 -0
  60. package/dist/nodefony/src/context/websocket/wsBackpressure.js +56 -0
  61. package/dist/nodefony/src/context/websocket/wsLogContent.js +68 -0
  62. package/dist/nodefony/src/cookies/cookie.js +258 -0
  63. package/dist/nodefony/src/errors/httpError.js +69 -0
  64. package/dist/nodefony/src/profiler/FrameProfile.js +95 -0
  65. package/dist/nodefony/src/profiler/Profiler.js +139 -0
  66. package/dist/nodefony/src/proxy/generateProxyConfig.js +157 -0
  67. package/dist/nodefony/src/rateLimit/IRateLimitStore.js +1 -0
  68. package/dist/nodefony/src/rateLimit/MemoryRateLimitStore.js +146 -0
  69. package/dist/nodefony/src/rateLimit/WsConnectionCounter.js +64 -0
  70. package/dist/nodefony/src/rateLimit/rateLimitFilters.js +20 -0
  71. package/dist/nodefony/src/servers/portBinder.js +114 -0
  72. package/dist/nodefony/src/session/session.js +390 -0
  73. package/dist/nodefony/src/session/storage/MemorySessionStorage.js +185 -0
  74. package/dist/nodefony/src/session/storage/RevocationGuardStorage.js +137 -0
  75. package/dist/nodefony/src/session/storage/sessionFilters.js +83 -0
  76. package/dist/nodefony/src/session/storage/sessionSort.js +53 -0
  77. package/dist/types/index.d.ts +83 -0
  78. package/dist/types/nodefony/command/assetsPublishCommand.d.ts +23 -0
  79. package/dist/types/nodefony/command/certificatesCommand.d.ts +17 -0
  80. package/dist/types/nodefony/command/networkCommand.d.ts +8 -0
  81. package/dist/types/nodefony/command/proxyGenerateCommand.d.ts +19 -0
  82. package/dist/types/nodefony/config/config.d.ts +197 -0
  83. package/dist/types/nodefony/config/defineModuleConfig.d.ts +39 -0
  84. package/dist/types/nodefony/interfaces/IContext.d.ts +138 -0
  85. package/dist/types/nodefony/interfaces/ICookie.d.ts +47 -0
  86. package/dist/types/nodefony/interfaces/IErrorRenderer.d.ts +55 -0
  87. package/dist/types/nodefony/interfaces/IHttpConfig.d.ts +12 -0
  88. package/dist/types/nodefony/interfaces/IHttpKernel.d.ts +10 -0
  89. package/dist/types/nodefony/interfaces/IRequest.d.ts +35 -0
  90. package/dist/types/nodefony/interfaces/IRequestLogger.d.ts +31 -0
  91. package/dist/types/nodefony/interfaces/IResponse.d.ts +39 -0
  92. package/dist/types/nodefony/interfaces/ISession.d.ts +283 -0
  93. package/dist/types/nodefony/interfaces/IUpload.d.ts +66 -0
  94. package/dist/types/nodefony/interfaces/index.d.ts +7 -0
  95. package/dist/types/nodefony/service/HttpAdminApi.d.ts +18 -0
  96. package/dist/types/nodefony/service/ProfilerAdminApi.d.ts +23 -0
  97. package/dist/types/nodefony/service/audit-logger.d.ts +143 -0
  98. package/dist/types/nodefony/service/certificates.d.ts +246 -0
  99. package/dist/types/nodefony/service/error-renderer.d.ts +74 -0
  100. package/dist/types/nodefony/service/http-kernel.d.ts +377 -0
  101. package/dist/types/nodefony/service/pretty-request-logger.d.ts +25 -0
  102. package/dist/types/nodefony/service/request-logger.d.ts +18 -0
  103. package/dist/types/nodefony/service/servers/clientError.d.ts +14 -0
  104. package/dist/types/nodefony/service/servers/server-http.d.ts +42 -0
  105. package/dist/types/nodefony/service/servers/server-https.d.ts +41 -0
  106. package/dist/types/nodefony/service/servers/server-static.d.ts +62 -0
  107. package/dist/types/nodefony/service/servers/server-websocket-secure.d.ts +29 -0
  108. package/dist/types/nodefony/service/servers/server-websocket.d.ts +29 -0
  109. package/dist/types/nodefony/service/servers/serverShutdown.d.ts +27 -0
  110. package/dist/types/nodefony/service/servers/wsHeartbeat.d.ts +46 -0
  111. package/dist/types/nodefony/service/sessions/sessions-service.d.ts +218 -0
  112. package/dist/types/nodefony/service/trace.d.ts +39 -0
  113. package/dist/types/nodefony/service/upload/upload-service.d.ts +61 -0
  114. package/dist/types/nodefony/src/assets/collectAssets.d.ts +35 -0
  115. package/dist/types/nodefony/src/assets/prebuiltUi.d.ts +99 -0
  116. package/dist/types/nodefony/src/context/Context.d.ts +195 -0
  117. package/dist/types/nodefony/src/context/domainMatcher.d.ts +67 -0
  118. package/dist/types/nodefony/src/context/forwarded.d.ts +95 -0
  119. package/dist/types/nodefony/src/context/http/HttpContext.d.ts +85 -0
  120. package/dist/types/nodefony/src/context/http/Request.d.ts +203 -0
  121. package/dist/types/nodefony/src/context/http/Response.d.ts +68 -0
  122. package/dist/types/nodefony/src/context/http/parser.d.ts +65 -0
  123. package/dist/types/nodefony/src/context/http/urlFastPath.d.ts +52 -0
  124. package/dist/types/nodefony/src/context/http2/Request.d.ts +14 -0
  125. package/dist/types/nodefony/src/context/http2/Response.d.ts +20 -0
  126. package/dist/types/nodefony/src/context/metaData.d.ts +58 -0
  127. package/dist/types/nodefony/src/context/requestId.d.ts +28 -0
  128. package/dist/types/nodefony/src/context/trustProxy.d.ts +77 -0
  129. package/dist/types/nodefony/src/context/websocket/Response.d.ts +53 -0
  130. package/dist/types/nodefony/src/context/websocket/WebsocketContext.d.ts +125 -0
  131. package/dist/types/nodefony/src/context/websocket/wsBackpressure.d.ts +73 -0
  132. package/dist/types/nodefony/src/context/websocket/wsLogContent.d.ts +37 -0
  133. package/dist/types/nodefony/src/cookies/cookie.d.ts +88 -0
  134. package/dist/types/nodefony/src/errors/httpError.d.ts +15 -0
  135. package/dist/types/nodefony/src/profiler/FrameProfile.d.ts +110 -0
  136. package/dist/types/nodefony/src/profiler/Profiler.d.ts +192 -0
  137. package/dist/types/nodefony/src/proxy/generateProxyConfig.d.ts +76 -0
  138. package/dist/types/nodefony/src/rateLimit/IRateLimitStore.d.ts +98 -0
  139. package/dist/types/nodefony/src/rateLimit/MemoryRateLimitStore.d.ts +40 -0
  140. package/dist/types/nodefony/src/rateLimit/WsConnectionCounter.d.ts +37 -0
  141. package/dist/types/nodefony/src/rateLimit/rateLimitFilters.d.ts +18 -0
  142. package/dist/types/nodefony/src/servers/portBinder.d.ts +102 -0
  143. package/dist/types/nodefony/src/session/session.d.ts +171 -0
  144. package/dist/types/nodefony/src/session/storage/MemorySessionStorage.d.ts +77 -0
  145. package/dist/types/nodefony/src/session/storage/RevocationGuardStorage.d.ts +81 -0
  146. package/dist/types/nodefony/src/session/storage/sessionFilters.d.ts +102 -0
  147. package/dist/types/nodefony/src/session/storage/sessionSort.d.ts +45 -0
  148. package/docs/cookies.md +365 -0
  149. package/docs/index.md +163 -0
  150. package/docs/observabilite.md +460 -0
  151. package/docs/rate-limit.md +372 -0
  152. package/docs/servers.md +935 -0
  153. package/docs/session.md +768 -0
  154. package/docs/upload.md +460 -0
  155. package/package.json +101 -0
@@ -0,0 +1,365 @@
1
+ ---
2
+ title: "Cookies — attributs, signature, parsing, HTTP et WebSocket"
3
+ navTitle: Cookies
4
+ lang: fr
5
+ module: "@nodefony/http"
6
+ topic: cookies
7
+ section: "CƓur runtime"
8
+ audience: [developer]
9
+ tags:
10
+ [
11
+ cookies,
12
+ set-cookie,
13
+ samesite,
14
+ secure,
15
+ httponly,
16
+ __host-,
17
+ signature,
18
+ hmac,
19
+ rfc6265bis,
20
+ websocket,
21
+ ]
22
+ version: "doc"
23
+ status: stable
24
+ updated: 2026-07-21
25
+ source: "src/packages/@nodefony/http/docs/cookies.md"
26
+ coverageModule: http
27
+ coverageFiles: cookies/cookie.ts,context/Context.ts,context/http/Response.ts,context/websocket/Response.ts
28
+ ---
29
+
30
+ # Cookies — attributs, signature, parsing, HTTP et WebSocket
31
+
32
+ > Un cookie est le seul état que le serveur peut coller au navigateur du visiteur : une petite étiquette
33
+ > renvoyĂ©e Ă  chaque requĂȘte. Cette page dĂ©crit la classe `Cookie` de Nodefony — comment on la construit,
34
+ > quels attributs de sécurité elle applique (et **force**), comment les cookies entrants sont lus, comment
35
+ > on lit et écrit un cookie depuis un contrÎleur, et ce qui change cÎté WebSocket. Le cookie **de session**
36
+ > a sa propre page : [Sessions](session.md). Chaque fait est ancré sur le code.
37
+
38
+ 📍 [Documentation](../../../../../docs/index.md) â€ș [@nodefony/http](index.md) â€ș **Cookies**
39
+
40
+ ## 🧠 Le modĂšle mental — deux sens, une Ă©tiquette
41
+
42
+ Un cookie voyage dans **deux en-tĂȘtes diffĂ©rents**, et Nodefony traite les deux sens sĂ©parĂ©ment :
43
+
44
+ - **Entrant** — le navigateur renvoie ses cookies dans l'en-tĂȘte `Cookie:`. Le pipeline le parse une fois
45
+ et range chaque cookie dans `context.cookies` (lecture seule cÎté contrÎleur).
46
+ - **Sortant** — le contrĂŽleur crĂ©e un `Cookie`, le pose sur la rĂ©ponse ; Ă  l'envoi, chaque cookie est
47
+ **sérialisé** en une ligne `Set-Cookie:` avec ses attributs.
48
+
49
+ ```mermaid
50
+ flowchart TD
51
+ REQ["RequĂȘte<br/>en-tĂȘte Cookie: a=1; b=2"] --> PARSE["cookiesParser(context)<br/>parse + un Cookie par entrĂ©e"]
52
+ PARSE --> STORE["context.cookies<br/>Record&lt;nom, Cookie&gt; (lecture)"]
53
+ STORE --> CTRL["ContrĂŽleur<br/>context.getRequestCookies(nom)"]
54
+ CTRL --> NEW["new Cookie(nom, valeur, options)"]
55
+ NEW --> SET["context.setCookie(cookie)<br/>→ response.addCookie"]
56
+ SET --> SER["Cookie.serialize()<br/>attributs + préfixes forcés"]
57
+ SER --> OUT["RĂ©ponse<br/>en-tĂȘte Set-Cookie: 
"]
58
+ ```
59
+
60
+ Trois idées portent tout le reste :
61
+
62
+ 1. **Lire et écrire ne sont pas symétriques.** On lit dans `context.cookies` (rempli par le parseur) ; on
63
+ écrit en posant un `Cookie` sur la **réponse**. Un cookie entrant modifié en mémoire ne repart pas tout
64
+ seul — il faut le (re)poser sur la rĂ©ponse.
65
+ 2. **La classe applique des défauts sûrs, et les fait respecter.** `Secure`, `HttpOnly`, `SameSite=Lax` par
66
+ défaut ; les préfixes `__Host-`/`__Secure-` **imposent** leurs contraintes à la sérialisation.
67
+ 3. **Le cookie de session est un cas particulier**, gĂ©rĂ© par le gestionnaire de sessions — dĂ©crit dans
68
+ [Sessions](session.md), pas ici.
69
+
70
+ ## 📖 Lexique
71
+
72
+ | Terme | Sens |
73
+ | --------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
74
+ | `Cookie:` (en-tĂȘte) | En-tĂȘte **de requĂȘte** : le navigateur y renvoie tous les cookies qu'il dĂ©tient pour le domaine. |
75
+ | `Set-Cookie:` | En-tĂȘte **de rĂ©ponse** : le serveur y dĂ©pose un cookie (nom, valeur, attributs). Une ligne par cookie. |
76
+ | `HttpOnly` | Attribut : le cookie est **invisible** à `document.cookie` (JavaScript) → un XSS ne peut pas le voler. |
77
+ | `Secure` | Attribut : le cookie n'est renvoyé que sur une connexion **chiffrée** (HTTPS/WSS). |
78
+ | `SameSite` | Attribut anti-CSRF : `Strict` / `Lax` / `None` — dit si le cookie part sur une requĂȘte **inter-site**. |
79
+ | `Path` / `Domain` | Portée du cookie : le préfixe d'URL et le domaine pour lesquels le navigateur le renvoie. |
80
+ | `Max-Age` / `Expires` | Durée de vie : en secondes (`Max-Age`) ou date absolue (`Expires`). Absents = **cookie de session** (mort à la fermeture). |
81
+ | `__Host-` | PrĂ©fixe RFC 6265bis : le navigateur **exige** `Secure` + `Path=/` + **aucun** `Domain` — sinon il rejette le cookie. |
82
+ | `__Secure-` | Préfixe RFC 6265bis : le navigateur exige seulement `Secure`. |
83
+ | Cookie **signĂ©** | Cookie dont la valeur porte un HMAC → le serveur dĂ©tecte toute altĂ©ration cĂŽtĂ© client (intĂ©gritĂ©, pas confidentialitĂ©). |
84
+ | HMAC | _Hash-based Message Authentication Code_ : empreinte clĂ©-dĂ©pendante (ici `HMAC-SHA256`) — infalsifiable sans le secret. |
85
+ | base64url | Variante base64 **sûre en URL/cookie** (pas de `+`, `/`, `=`). |
86
+ | Timing-safe | Comparaison à temps constant (`crypto.timingSafeEqual`) : ne fuit pas la signature attendue par la durée de la comparaison. |
87
+ | XSS | _Cross-Site Scripting_ : du JS injectĂ© s'exĂ©cute dans la page victime — voler un cookie non `HttpOnly` en est le premier but. |
88
+ | CSRF | _Cross-Site Request Forgery_ : un site tiers dĂ©clenche une requĂȘte authentifiĂ©e Ă  l'insu de la victime — bloquĂ© par `SameSite`. |
89
+ | Session fixation | L'attaquant impose un identifiant de session connu de lui Ă  la victime — `__Host-` empĂȘche l'injection cross-sous-domaine. |
90
+
91
+ ## Qu'est-ce qu'un cookie, ici ?
92
+
93
+ Un cookie, c'est un **badge vestiaire** : le serveur remet un ticket au navigateur, qui le représente à
94
+ chaque passage. Le serveur reconnaĂźt le porteur sans rien retenir de coĂ»teux — juste la valeur du ticket.
95
+ Le badge peut porter des mentions : « ne pas montrer à un script » (`HttpOnly`), « seulement au guichet
96
+ sécurisé » (`Secure`), « pas valable si un autre site t'envoie » (`SameSite`).
97
+
98
+ Un cookie n'est pas neutre : c'est une **surface d'attaque**. Les attributs sont lĂ  pour la refermer.
99
+
100
+ - **`HttpOnly` bloque le vol par XSS.** Sans lui, un script injecté lit `document.cookie` et exfiltre le
101
+ cookie de session. Avec lui, le cookie est hors de portée du JavaScript de la page.
102
+ - **`SameSite` bloque le CSRF.** Sans lui, une balise image (ou un formulaire) hébergée sur un site pirate
103
+ dĂ©clenche une requĂȘte vers ta banque qui part **avec** le cookie de la victime. `Lax` (le dĂ©faut
104
+ Nodefony) coupe cet envoi sur les requĂȘtes inter-site dangereuses.
105
+ - **`__Host-` bloque la session fixation cross-sous-domaine.** Un sous-domaine compromis
106
+ (`evil.example.com`) ne peut pas écrire un cookie qui remonterait vers `example.com` : le préfixe interdit
107
+ `Domain` et impose `Path=/`.
108
+
109
+ ## La vision Nodefony
110
+
111
+ Nodefony ne se contente pas de proposer ces attributs : il **choisit des défauts sûrs** et **fait respecter
112
+ les invariants** que le navigateur exigerait de toute façon — pour que l'erreur ne parte pas sur le fil.
113
+
114
+ **Le défaut est fermé.** Un cookie créé sans options est `Secure` + `HttpOnly` + `SameSite=Lax`
115
+ (`cookieDefaultSettings`, `cookie.ts:43`). Il faut **choisir** d'ouvrir (ex. `httpOnly: false` pour un
116
+ cookie lu en JS), jamais choisir de fermer.
117
+
118
+ **Les préfixes sont forcés, pas espérés.** Nommer un cookie `__Host-
` ne suffit pas : `serialize()`
119
+ (`cookie.ts:383`) **réécrit** la sortie — `Secure` ajoutĂ©, `Path=/` imposĂ©, `Domain` retirĂ© — pour que le
120
+ navigateur ne rejette jamais le cookie en silence. Idem `__Secure-` (Secure imposé) et `SameSite=None` (qui
121
+ impose `Secure`, `cookie.ts:393`).
122
+
123
+ **La signature refuse le secret prévisible.** Un cookie `signed: true` sans secret configuré **jette**
124
+ (`setValue()`, `cookie.ts:211`) : signer avec le secret public par défaut ne protégerait rien
125
+ (fail-closed). La vĂ©rification est **timing-safe** (`unsign()` → `crypto.timingSafeEqual`, `cookie.ts:380`).
126
+
127
+ **`SameSite` retombe toujours sur `Lax`, jamais sur `None`.** Toute valeur inconnue est normalisée vers
128
+ `Lax` (`setSameSite()`, `cookie.ts:250`) : `None` désactive la protection CSRF, ce n'est jamais un défaut.
129
+
130
+ **HTTP et WebSocket lisent les mĂȘmes cookies.** Le parseur tourne pour les deux transports ; en revanche la
131
+ poignĂ©e de main WebSocket **ne peut pas** Ă©crire de cookie (limite de la bibliothĂšque `ws`) — voir plus bas.
132
+
133
+ Liens utiles : [RFC 6265bis](https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-rfc6265bis) ·
134
+ cookie de session → [Sessions](session.md) · en-tĂȘtes de sĂ©curitĂ© → [En-tĂȘtes](../../security/docs/headers.md).
135
+
136
+ ## 🚀 DĂ©marrage rapide
137
+
138
+ Dans une application générée par `nodefony create app`, il n'y a **rien à configurer** pour les cookies
139
+ applicatifs : on construit un `Cookie`, on le lit ou on le pose depuis le contexte du contrĂŽleur. Le
140
+ pipeline a déjà parsé les cookies entrants avant que ton action ne s'exécute.
141
+
142
+ ### Un contrÎleur qui lit et écrit un cookie
143
+
144
+ ```typescript
145
+ // nodefony/controller/PreferencesController.ts — complet, compile tel quel
146
+ import { Controller, controller, Get } from "@nodefony/framework";
147
+ import { Cookie } from "@nodefony/http";
148
+ import type { Context } from "@nodefony/http";
149
+
150
+ @controller("/prefs")
151
+ class PreferencesController extends Controller {
152
+ constructor(context: Context) {
153
+ super("PreferencesController", context);
154
+ }
155
+
156
+ // LECTURE — le pipeline a dĂ©jĂ  parsĂ© l'en-tĂȘte `Cookie:` dans context.cookies.
157
+ @Get("/")
158
+ async read() {
159
+ const theme = this.context?.getRequestCookies("theme");
160
+ return this.renderJson({
161
+ theme: theme instanceof Cookie ? theme.value : null,
162
+ });
163
+ }
164
+
165
+ // ÉCRITURE — construire puis poser sur la rĂ©ponse ; l'attribut Secure/HttpOnly
166
+ // /SameSite=Lax est appliqué par défaut. Ici on OUVRE volontairement en JS.
167
+ @Get("/set")
168
+ async write() {
169
+ const cookie = new Cookie("theme", "dark", {
170
+ maxAge: 30 * 24 * 60 * 60, // 30 jours, EN SECONDES
171
+ sameSite: "Lax",
172
+ httpOnly: false, // prĂ©fĂ©rence non sensible → lisible cĂŽtĂ© client
173
+ });
174
+ this.context?.setCookie(cookie);
175
+ return this.renderJson({ set: cookie.serialize() });
176
+ }
177
+ }
178
+
179
+ export default PreferencesController;
180
+ ```
181
+
182
+ ### Ce qu'on observe au terminal
183
+
184
+ ```bash
185
+ # Poser le cookie — la ligne Set-Cookie porte les attributs sĂ©rialisĂ©s
186
+ curl -si http://127.0.0.1:5151/prefs/set | grep -i set-cookie
187
+ # set-cookie: theme=dark; Max-Age=2592000; Path=/; SameSite=Lax; Expires=

188
+
189
+ # Le renvoyer et le lire
190
+ curl -s --cookie "theme=dark" http://127.0.0.1:5151/prefs
191
+ # {"theme":"dark"}
192
+ ```
193
+
194
+ > [!NOTE]
195
+ > Le défaut `Secure` fait qu'un cookie posé en HTTP clair (`5151`) n'est stocké par le navigateur **que**
196
+ > sur `localhost` (tolérance des navigateurs). En production, on sert en HTTPS et `Secure` est correct.
197
+
198
+ ### Un cookie signé (intégrité)
199
+
200
+ Pour qu'une valeur ne puisse pas ĂȘtre **falsifiĂ©e** cĂŽtĂ© client (sans la chiffrer), signe-la — il faut un
201
+ secret **configuré** (le secret par défaut est refusé) :
202
+
203
+ ```typescript ignore
204
+ // fragment — un secret prĂ©visible est refusĂ© (fail-closed)
205
+ const c = new Cookie("pref", "v1", {
206
+ signed: true,
207
+ secret: process.env.COOKIE_SECRET!,
208
+ });
209
+ // c.value === "s:v1.<hmac base64url>" ; c.unsign() renvoie "v1" ou false si altéré
210
+ ```
211
+
212
+ ## 🧰 API publique
213
+
214
+ Les signatures exactes vivent dans `.ai/symbols.json` et les types gĂ©nĂ©rĂ©s — jamais recopiĂ©es ici. Ce qui
215
+ suit montre l'**usage réel**. La classe s'importe `import { Cookie } from "@nodefony/http"` ; les interfaces
216
+ `ICookie`, `ICookieOptions`, `IWsCookie`, `SameSiteType` en `import type`.
217
+
218
+ ### Construire un cookie et ses attributs
219
+
220
+ Le constructeur accepte `(nom, valeur, options?)` ou **un cookie Ă  copier** (surcharge,
221
+ `cookie.ts:149`). Les options fusionnent avec les défauts sûrs.
222
+
223
+ | Option | Type | Défaut | Effet |
224
+ | ---------- | -------------------------- | ------------ | ---------------------------------------------------------------------- |
225
+ | `maxAge` | `number` (secondes) | `0` | Durée de vie. `0` = cookie de session (ni `Max-Age` ni `Expires`). |
226
+ | `expires` | `Date \| string \| number` | — | Date d'expiration absolue (`setExpires()`, `cookie.ts:264`). |
227
+ | `path` | `string` | `/` | Préfixe d'URL de portée. |
228
+ | `domain` | `string` | `undefined` | Domaine de portée (retiré si nom `__Host-`). |
229
+ | `secure` | `boolean` | `true` | HTTPS only. Forcé si `SameSite=None` ou préfixe `__Host-`/`__Secure-`. |
230
+ | `httpOnly` | `boolean` | `true` | Invisible Ă  `document.cookie` (anti-XSS). |
231
+ | `sameSite` | `SameSiteType` | `Lax` | `Strict`/`Lax`/`None`. Toute autre valeur retombe sur `Lax`. |
232
+ | `signed` | `boolean` | `false` | Signe la valeur (HMAC). Exige `secret` configuré, sinon **jette**. |
233
+ | `secret` | `string` | (par défaut) | Clé HMAC. Le secret par défaut est **refusé** pour signer. |
234
+ | `priority` | `High \| Medium \| Low` | `undefined` | Attribut `Priority` (`setPriority()`, `cookie.ts:297`). |
235
+
236
+ Les défauts sont matérialisés dans `cookieDefaultSettings` (`cookie.ts:43`) ; la fusion se fait dans le
237
+ constructeur de `Cookie` (`cookie.ts:130`).
238
+
239
+ ### Sérialiser : `serialize()` et `serializeWebSocket()`
240
+
241
+ - `serialize()` (`cookie.ts:383`) produit la **ligne `Set-Cookie`** complĂšte (attributs dans l'ordre,
242
+ préfixes forcés, `Max-Age` seulement s'il est positif).
243
+ - `serializeWebSocket()` (`cookie.ts:425`) produit un **objet** `IWsCookie` (mĂȘmes invariants de sĂ©curitĂ©)
244
+ — utilisĂ© quand un cookie doit ĂȘtre dĂ©crit hors en-tĂȘte HTTP.
245
+ - `toString()` (`cookie.ts:317`) ne rend que `nom=valeurEncodée` (sans attributs).
246
+
247
+ ### Signer / vérifier : `sign()` et `unsign()`
248
+
249
+ - `sign(val, secret)` (`cookie.ts:331`) → `val.base64url(HMAC-SHA256)` — la valeur d'origine est
250
+ **préservée** (récupérable), la signature garantit l'intégrité.
251
+ - `unsign(val?, secret?)` (`cookie.ts:355`) → la valeur en clair si la signature est valide, sinon `false`.
252
+ Comparaison **timing-safe** (`cookie.ts:380`) ; tolÚre le préfixe marqueur `s:`.
253
+
254
+ ### Lire et écrire depuis le contexte
255
+
256
+ | Besoin | Appel | OĂč |
257
+ | ------------------------------ | ----------------------------------------------------- | ---------------------------------------- |
258
+ | Lire un cookie entrant | `context.getRequestCookies("nom")` → `Cookie \| null` | `getRequestCookies()` (`Context.ts:660`) |
259
+ | Lire tous les cookies entrants | `context.cookies` → `Record<string, Cookie>` | `cookies` (`Context.ts:193`) |
260
+ | Écrire un cookie sortant | `context.setCookie(new Cookie(
))` | `setCookie()` (`Context.ts:667`) |
261
+ | Supprimer un cookie sortant | `response.deleteCookieByName("nom")` | `http/Response.ts:119` |
262
+
263
+ CÎté réponse HTTP, `addCookie()` (`http/Response.ts:101`) enregistre le cookie, et `setCookies()`
264
+ (`http/Response.ts:107`) Ă©met **une ligne `Set-Cookie` par cookie** — un tableau passĂ© Ă  Node, jamais une
265
+ boucle de `setHeader` (qui écraserait tout sauf le dernier). Pour expirer un cookie chez le client :
266
+ `clearCookie()` (`cookie.ts:198`) recule `Expires` à l'époque.
267
+
268
+ ### Parsing des cookies entrants
269
+
270
+ `cookiesParser(context)` (`cookie.ts:91`) lit l'en-tĂȘte `Cookie:` (via la bibliothĂšque `cookie`,
271
+ `parser()` `cookie.ts:54`), crée un `Cookie` par entrée et l'ajoute au contexte avec `addRequestCookie()`
272
+ (`Context.ts:650`). Il est déclenché automatiquement par le pipeline : `parseCookies()` est appelé à
273
+ l'initialisation du contexte HTTP (`HttpContext.ts:190`) **et** WebSocket (`WebsocketContext.ts:170`).
274
+
275
+ ### CĂŽtĂ© WebSocket — lecture oui, Ă©criture non
276
+
277
+ Les cookies **entrants** sont lus au handshake, exactement comme en HTTP (mĂȘme parseur). Mais la **poignĂ©e
278
+ de main WebSocket ne peut pas poser de cookie** : `setCookie()` et `setCookies()` de la réponse WS sont des
279
+ **no-op** (`websocket/Response.ts:295`), une limite de la bibliothĂšque `ws`. Le cookie de session, lui, est
280
+ posé pendant la **phase HTTP** qui précÚde l'upgrade. La forme d'un cookie décrit pour le WS est
281
+ `IWsCookie` (`ICookie.ts:19`), produite par `serializeWebSocket()`.
282
+
283
+ ## ⚙ Configuration
284
+
285
+ Les cookies **applicatifs** ne se configurent pas par schéma : on les construit dans le code, avec les
286
+ défauts sûrs de `cookieDefaultSettings` (`cookie.ts:43`). Le seul cookie **piloté par la config** est celui
287
+ de la **session** — bloc Zod `sessionCookieSchema` (`config.ts:727`), avec notamment `hostPrefix`
288
+ (`config.ts:730`) qui décide du préfixe `__Host-`. Tout cela est documenté dans [Sessions](session.md) :
289
+ cette page ne le duplique pas.
290
+
291
+ Le nom effectif du cookie de session (avec ou sans `__Host-` selon le transport) est calculé par
292
+ `getSessionCookieName()` (`Context.ts:714`) — encore un dĂ©tail qui appartient Ă  la page Sessions.
293
+
294
+ ## đŸ›Ąïž DĂ©fenses par attribut
295
+
296
+ | Attribut / mécanisme | Faille bloquée | Comment Nodefony l'applique |
297
+ | -------------------------- | --------------------------------------- | ------------------------------------------------------------------------ |
298
+ | `HttpOnly` (défaut `true`) | Vol de cookie par **XSS** | Défaut fermé (`cookieDefaultSettings`, `cookie.ts:43`) |
299
+ | `SameSite=Lax` (défaut) | **CSRF** inter-site | Fallback toujours `Lax`, jamais `None` (`setSameSite()` `cookie.ts:250`) |
300
+ | `Secure` (défaut `true`) | Interception en clair | Forcé aussi par `None`/préfixes (`serialize()` `cookie.ts:393`) |
301
+ | Préfixe `__Host-` | **Session fixation** cross-sous-domaine | `Domain` retiré + `Path=/` imposés (`serialize()` `cookie.ts:399`) |
302
+ | Cookie signé (HMAC) | Altération de la valeur cÎté client | `sign()`/`unsign()` timing-safe (`cookie.ts:331`, `cookie.ts:380`) |
303
+ | Refus du secret par défaut | Signature « fantÎme » sans protection | Fail-closed à la signature (`setValue()` `cookie.ts:199`) |
304
+
305
+ ## 📜 Normes appliquĂ©es
306
+
307
+ | Domaine | Norme | Ancrage |
308
+ | ---------------------------------- | -------------------- | ------------------------------------------------------------------ |
309
+ | Cookies — syntaxe `Set-Cookie` | RFC 6265bis | `serialize()` (`cookie.ts:383`) |
310
+ | `SameSite` — 3 valeurs canoniques | RFC 6265bis §5.4.7 | `SameSiteType` (`ICookie.ts:3`), `setSameSite()` (`cookie.ts:250`) |
311
+ | Préfixes `__Host-` / `__Secure-` | RFC 6265bis §4.1.3 | `serialize()` force les contraintes (`cookie.ts:390`) |
312
+ | `SameSite=None` impose `Secure` | RFC 6265bis | `serialize()` (`cookie.ts:393`) |
313
+ | IntĂ©gritĂ© — HMAC-SHA256, base64url | RFC 2104 / RFC 4648 | `sign()` (`cookie.ts:331`) |
314
+ | VĂ©rification Ă  temps constant | bonne pratique OWASP | `unsign()` → `timingSafeEqual` (`cookie.ts:380`) |
315
+
316
+ ## ⚠ PiĂšges (symptĂŽme → cause → correction)
317
+
318
+ | SymptĂŽme | Cause | Correction |
319
+ | --------------------------------------------------------- | ------------------------------------------------------------------------- | ------------------------------------------------------------------------ |
320
+ | Le cookie posĂ© en HTTP clair n'est pas stockĂ© | DĂ©faut `Secure: true` → le navigateur exige HTTPS (sauf `localhost`) | Servir en HTTPS, ou `secure: false` en dev hors localhost |
321
+ | Un cookie `__Host-` ignore mon `Domain`/`Path` | `serialize()` **force** les contraintes du prĂ©fixe | Attendu (RFC 6265bis) — renommer sans prĂ©fixe si tu veux un `Domain` |
322
+ | `new Cookie(..., { signed: true })` **jette** | Aucun `secret` configurĂ© → refus du secret prĂ©visible (fail-closed) | Passer un `secret` rĂ©el (`{ signed: true, secret: 
 }`) |
323
+ | Modifier un cookie entrant ne change rien cÎté client | Lecture (`context.cookies`) et écriture (réponse) ne sont pas symétriques | (Re)poser le cookie sur la réponse : `context.setCookie(new Cookie(
))` |
324
+ | Le cookie WS posé au handshake n'arrive jamais | `setCookie`/`setCookies` de la réponse WS sont des no-op (`ws`) | Poser le cookie pendant la phase HTTP avant l'upgrade (cf session) |
325
+ | Deux `Set-Cookie` s'écrasent, un seul survit | Un `setHeader('Set-Cookie', str)` remplace le précédent | Déjà géré : `setCookies()` passe un **tableau** (`http/Response.ts:117`) |
326
+ | `SameSite` mal orthographiĂ© devient `Lax` silencieusement | Fallback fail-safe sur `Lax` | Attendu — vĂ©rifier la casse ; `Strict`/`Lax`/`None` seulement |
327
+ | `maxAge` interprété en millisecondes | `maxAge` est en **secondes** (comme `Set-Cookie` Max-Age) | Passer des secondes (`30*24*60*60`), pas des ms |
328
+
329
+ ## 📡 ObservabilitĂ©
330
+
331
+ Il n'y a pas d'écran Studio dédié aux cookies applicatifs (le cookie **de session** est surfacé dans
332
+ l'écran **Sessions**). En développement, chaque écriture de cookie est journalisée en `DEBUG` par la
333
+ rĂ©ponse HTTP (`ADD COOKIE ==> 
`, `setCookie()` `http/Response.ts:126`) — visible via le skill
334
+ `nodefony-tail-error-logs` ou le Suivi de requĂȘte. Sur le fil, un `curl -i` montre les lignes `Set-Cookie`.
335
+
336
+ ## đŸ§Ș Tests & couverture
337
+
338
+ Les chiffres exacts vivent dans la carte de tests de cette page (régénérée depuis vitest, jamais figés dans
339
+ le Markdown).
340
+
341
+ | Type | OĂč |
342
+ | ---------------------- | ----------------------------------------------------------------------------------------------------- |
343
+ | Unitaire (dĂ©diĂ©) | `tests/unit/Cookie.test.ts` — constructeur, copie, `toString`, `serialize`, `clearCookie`, `setValue` |
344
+ | Unitaire — RFC 6265bis | `Cookie.test.ts` — `SameSite`/`__Host-`/`__Secure-`, `None ⇒ Secure`, `serializeWebSocket` |
345
+ | Unitaire — signature | `Cookie.test.ts` — `sign`/`unsign` (round-trip, altĂ©ration, mauvais secret, `s:`, fail-closed) |
346
+ | Unitaire — rĂ©gression | `Cookie.test.ts` — overflow `Max-Age`/`Expires` (`maxAge=0/3600/86400/undefined`) |
347
+ | Adjacent (rĂ©ponse) | `tests/unit/Response.test.ts` — `addCookie`/`setCookies` cĂŽtĂ© `HttpResponse` |
348
+
349
+ Ce qui **manque** aujourd'hui : le round-trip `Set-Cookie` → navigateur → `Cookie:` d'un cookie
350
+ **applicatif** arbitraire n'a pas de test d'intégration dédié (il est exercé indirectement par les tests de
351
+ session et de CSRF, qui posent et relisent des cookies sur serveur réel) ; et il n'y a pas de test
352
+ d'attaque `*.attack.test.ts` centré cookies (l'altération de valeur signée est couverte unitairement).
353
+
354
+ Suites : `npm test` (unitaires — la classe `Cookie` s'y teste sans serveur). Couverture : `npm run
355
+ coverage` dans `@nodefony/http` — le pourcentage vit dans le rapport vitest, jamais figĂ© ici. Skill
356
+ associé : `nodefony-security-review` (revue des attributs de sécurité d'un cookie dans un diff).
357
+
358
+ ## 🔗 Pour aller plus loin
359
+
360
+ - âŹ†ïž **Retour au hub** : [@nodefony/http — vue du module](index.md) · [Toute la documentation](../../../../../docs/index.md)
361
+ - 🧭 **Pages sƓurs** : [Sessions](session.md) (le cookie de session, sa config `hostPrefix`, sa rĂ©vocation) · [Serveurs](servers.md)
362
+ - Le trajet complet d'une requĂȘte (oĂč le parsing des cookies s'insĂšre) → [pipeline-requete](../../../../../docs/architecture/pipeline-requete.md)
363
+ - En-tĂȘtes de sĂ©curitĂ© applicatifs (CSP, Referrer-Policy
) → [En-tĂȘtes](../../security/docs/headers.md)
364
+ - Le pare-feu et CSRF par-dessus le transport → [Firewall](../../security/docs/firewall.md)
365
+ - Configuration d'application (`defineConfig`, `use`, env) → [configuration](../../../../../docs/guides/configuration.md)
package/docs/index.md ADDED
@@ -0,0 +1,163 @@
1
+ ---
2
+ title: "@nodefony/http — la couche transport"
3
+ navTitle: "@nodefony/http"
4
+ lang: fr
5
+ module: "@nodefony/http"
6
+ topic: http
7
+ section: "CƓur runtime"
8
+ audience: [developer, devops]
9
+ tags:
10
+ [http, https, http2, websocket, wss, context, serveurs, sessions, transport]
11
+ version: "doc"
12
+ status: stable
13
+ updated: 2026-07-19
14
+ source: "src/packages/@nodefony/http/docs/index.md"
15
+ coverageModule: http
16
+ ---
17
+
18
+ # @nodefony/http — la couche transport
19
+
20
+ > Les portes d'entrĂ©e du processus. Ce module ouvre les sockets, accepte les connexions — web **et**
21
+ > temps rĂ©el — et construit le **contexte de requĂȘte** que tout le reste du framework consomme. Sa
22
+ > particularité tient en une phrase : HTTP et WebSocket ne sont pas deux mondes, ce sont **deux entrées
23
+ > du mĂȘme pipeline**. C'est de lĂ  que vient le diffĂ©renciateur de Nodefony.
24
+
25
+ 📍 [Documentation](../../../../../docs/index.md) â€ș **@nodefony/http**
26
+
27
+ ## 🧭 Par oĂč commencer
28
+
29
+ Trois parcours selon ce que tu viens faire. L'ordre compte : chaque étape suppose la précédente.
30
+
31
+ **Je dĂ©couvre le module** — comprendre avant de configurer.
32
+
33
+ 1. [Serveurs](servers.md) — ce qui Ă©coute, sur quels ports, et comment ça dĂ©marre et s'arrĂȘte.
34
+ 2. [Pipeline de requĂȘte](../../../../../docs/architecture/pipeline-requete.md) — le trajet complet
35
+ d'une requĂȘte, du socket jusqu'Ă  ton contrĂŽleur. **La page qui relie tout.**
36
+ 3. [Sessions](session.md) — le premier Ă©tat serveur que rencontre une application rĂ©elle.
37
+ 4. [Routage et contrîleurs](../../framework/docs/index.md) — la suite du voyage, dans `@nodefony/framework`.
38
+
39
+ **Je mets en production** — ce qu'un serveur exposĂ© doit tenir.
40
+
41
+ 1. [Serveurs](servers.md) — TLS, certificats, politique de port, arrĂȘt gracieux, sondes de vie.
42
+ 2. [Sessions](session.md) — choisir un store partagĂ© : sans lui, deux pods ne partagent aucune session.
43
+ 3. [Pipeline de requĂȘte](../../../../../docs/architecture/pipeline-requete.md) — oĂč se branchent
44
+ rate-limit, en-tĂȘtes et firewall.
45
+ 4. [Rate-limit](rate-limit.md) — plafonner le dĂ©bit par client avant que la charge n'atteigne le contrĂŽleur.
46
+ 5. [ObservabilitĂ©](observabilite.md) — corrĂ©ler les logs par `requestId` pour diagnostiquer Ă  chaud.
47
+ 6. [SĂ©curitĂ©](../../security/docs/index.md) — le pare-feu applicatif se pose par-dessus ce module.
48
+
49
+ **Je fais du temps rĂ©el** — WebSocket dans le mĂȘme contexte que le web.
50
+
51
+ 1. [Serveurs](servers.md) — le WS n'a pas de port à lui : il se greffe sur son porteur HTTP.
52
+ 2. [Sessions](session.md) — la session cĂŽtĂ© WebSocket, et pourquoi elle passe par l'ALS.
53
+ 3. [La socket Nodefony](../../realtime/docs/index.md) — la couche au-dessus,
54
+ qui multiplexe N canaux sur une connexion.
55
+
56
+ ## đŸ—‚ïž Les briques du module
57
+
58
+ Le tableau pour choisir vite ; les cards en dessous pour savoir ce qu'on y trouve.
59
+
60
+ <!-- prettier-ignore -->
61
+ | Brique | Ce qu'elle résout | Tu en as besoin quand
 |
62
+ | --- | --- | --- |
63
+ | [Serveurs](servers.md) | ouvrir, rĂ©gler, transmettre, fermer proprement | toujours — c'est la fondation |
64
+ | [Sessions](session.md) | de l'état serveur rattaché à un visiteur | login, panier, préférences, WS authentifié |
65
+ | [Cookies](cookies.md) | lire/écrire des cookies sûrs (SameSite, signés) | tu poses un état cÎté client hors session |
66
+ | [Upload & corps](upload.md) | parser le corps et recevoir des fichiers | formulaires, imports multipart, API JSON |
67
+ | [Rate-limit](rate-limit.md) | plafonner le débit par client (429) | protéger une API d'un flood ou d'un abus |
68
+ | [ObservabilitĂ©](observabilite.md) | tracer et journaliser chaque requĂȘte | dĂ©bugger en prod, corrĂ©ler des logs |
69
+ | [Pipeline de requĂȘte](../../../../../docs/architecture/pipeline-requete.md) | l'ordre exact des Ă©tapes, HTTP comme WS | tu dĂ©bugges « pourquoi ça passe / ça bloque ici » |
70
+
71
+ ```nodefony-cards
72
+ [
73
+ { "icon": "🔌", "title": "servers", "href": "servers.md",
74
+ "desc": "Deux ports, quatre serveurs, un seul pipeline : politique de port, certificats et TLS de dĂ©veloppement, rĂ©glage du transport, sondes de liveness/readiness, arrĂȘt gracieux, dĂ©fenses de bordure (slow-loris, floods, zombies WebSocket).",
75
+ "meta": "commence ici — tout le reste suppose un serveur qui Ă©coute" },
76
+ { "icon": "đŸ—ïž", "title": "session", "href": "session.md",
77
+ "desc": "Cycle de vie complet, cookie opaque, les quatre stores (memory, drizzle, redis, mongoose) et comment auto en choisit un, les délais NIST, la révocation, la session cÎté WebSocket.",
78
+ "meta": "la brique oĂč un choix de dev (memory) devient un bug de prod" },
79
+ { "icon": "đŸȘ", "title": "cookies", "href": "cookies.md",
80
+ "desc": "Lire et écrire des cookies : attributs SameSite, Secure, HttpOnly, Path, Domain, Max-Age/Expires, parsing des cookies entrants, signature HMAC, cookies cÎté WebSocket. Le cookie de session a sa propre page.",
81
+ "meta": "dÚs que tu poses un état cÎté client hors session" },
82
+ { "icon": "📎", "title": "upload", "href": "upload.md",
83
+ "desc": "RĂ©ception du corps de requĂȘte (JSON, urlencoded, multipart, brut) et upload de fichiers : accĂšs aux champs et fichiers, API UploadedFile (taille, type, move), bornes de payload (413) et sĂ»retĂ© du nom de fichier.",
84
+ "meta": "formulaires, imports de fichiers, API JSON" },
85
+ { "icon": "⏳", "title": "rate-limit", "href": "rate-limit.md",
86
+ "desc": "Limiter le dĂ©bit par client : fenĂȘtre et quota configurables, rĂ©ponse 429 avec en-tĂȘtes X-RateLimit-* et Retry-After, store pluggable, limites cĂŽtĂ© WebSocket (handshake + connexions concurrentes), introspection admin.",
87
+ "meta": "protéger une API d'un flood ou d'un abus" },
88
+ { "icon": "📈", "title": "observabilite", "href": "observabilite.md",
89
+ "desc": "Observer les requĂȘtes : lignes de log (pretty ou JSON), requestId de corrĂ©lation, W3C Trace Context, trace des frames WebSocket, redaction et sampling d'audit. OĂč partent les logs est traitĂ© par la page Syslog du cƓur.",
90
+ "meta": "dĂ©bugger en prod, corrĂ©ler les logs par requĂȘte" },
91
+ { "icon": "🔀", "title": "pipeline-requete", "href": "../../../../../docs/architecture/pipeline-requete.md",
92
+ "desc": "OĂč ce module s'arrĂȘte et oĂč le framework prend le relais, et dans quel ordre s'enchaĂźnent contexte, rate-limit, routage, session, CSRF et firewall.",
93
+ "meta": "page transverse — celle qui relie tout" }
94
+ ]
95
+ ```
96
+
97
+ > [!NOTE]
98
+ > **Deux briques n'ont pas encore leur page dédiée** : les fichiers statiques et les certificats. Ils
99
+ > sont implémentés et testés ; en attendant, leur configuration vit dans les blocs Zod de
100
+ > `nodefony/config/config.ts` et leur comportement est décrit dans la page [Serveurs](servers.md)
101
+ > (repli statique, stratégies de certificats TLS).
102
+
103
+ ## đŸ›ïž Place dans le framework
104
+
105
+ ```mermaid
106
+ flowchart TD
107
+ N["node:http · node:https · node:http2 · ws"] --> SRV["serveurs<br/>http · https/h2 · ws · wss"]
108
+ SRV --> HK["HttpKernel<br/>orchestrateur du pipeline"]
109
+ HK --> CTX["Context<br/>HttpContext · WebsocketContext"]
110
+ CTX --> SESS["sessions · cookies · upload"]
111
+ CTX --> FW["@nodefony/security<br/>firewall, CSRF, CORS"]
112
+ FW --> FRW["@nodefony/framework<br/>routage → contrîleur"]
113
+ ```
114
+
115
+ `@nodefony/http` ne connaüt ni les routes ni les contrîleurs — il ne peut pas importer
116
+ `@nodefony/framework` (ce serait un cycle). Il expose un contexte ; le framework s'y branche.
117
+
118
+ ## 🧰 Surface publique
119
+
120
+ Depuis une application : `Context`, `HttpContext`, `WebsocketContext`, `Session`, `SessionsService`,
121
+ les services de serveurs, `cookie`, `httpError`, le profiler. Les signatures exactes vivent dans
122
+ `.ai/symbols.json` et dans les types gĂ©nĂ©rĂ©s — jamais recopiĂ©es Ă  la main dans cette page, oĂč elles
123
+ se périmeraient en silence.
124
+
125
+ ## ⚙ Configuration
126
+
127
+ Tout se déclare dans `nodefony.config.ts` via `use("@nodefony/http", { 
 })`. Les blocs Zod
128
+ (`nodefony/config/config.ts`) couvrent : `servers` (ports, transport, TLS, HTTP/2), `session` et
129
+ `cookie`, `trustProxy`, `certificates`, `upload`, le rate-limit et les fichiers statiques. Chaque page
130
+ de brique détaille son bloc et ses défauts réels.
131
+
132
+ ## 📜 Normes appliquĂ©es
133
+
134
+ RFC 9110/9111/9112 (sémantique HTTP, cache, HTTP/1.1), RFC 9113 (HTTP/2), RFC 6455 (WebSocket et ses
135
+ codes de fermeture), RFC 6265bis (cookies), RFC 6585 (429), RFC 6125 (identité des certificats),
136
+ WHATWG Fetch (CORS), W3C Trace Context (`traceparent`).
137
+
138
+ ## 📡 ObservabilitĂ© — Studio
139
+
140
+ Le profiler mesure les phases d'une requĂȘte et alimente le data plane admin (`HttpAdminApi`). Les
141
+ sessions sont surfacées dans l'écran **Sessions** (`/nodefony/sessions`), les corrélations par
142
+ `traceparent` dans l'écran **Traces** (`/nodefony/logs/trace/{requestId}`), et l'état des serveurs dans la carte du module.
143
+
144
+ ## đŸ§Ș Tests & couverture
145
+
146
+ Le module porte la plus grosse couverture du dĂ©pĂŽt — les chiffres exacts vivent dans la carte de
147
+ l'aperçu, régénérée depuis vitest, jamais figés dans la prose.
148
+
149
+ | Type | OĂč | Ce qui est prouvĂ© |
150
+ | ---------------- | ------------------------------------- | ----------------------------------------------------- |
151
+ | Unitaire | `tests/unit/**` | cookies, session, erreurs, trust-proxy, requestId |
152
+ | Intégration | `tests/{http,integration,routing}/**` | pipeline réel sur serveur vivant, TLS, statiques |
153
+ | WebSocket | `tests/websockets/**` | handshake, protocoles, binaire, broadcast, sessions |
154
+ | Contrat | `tests/support/*Contract.ts` | un store tiers respecte le contrat attendu |
155
+ | Charge / mémoire | `tests/load/**` + `memory.test.ts` | seuils de heap, connexions soutenues, débit de frames |
156
+
157
+ ## 🔗 Pour aller plus loin
158
+
159
+ - Le trajet d'une requĂȘte → [pipeline-requete](../../../../../docs/architecture/pipeline-requete.md)
160
+ - Routage et contrîleurs → [@nodefony/framework](../../framework/docs/index.md)
161
+ - Le pare-feu par-dessus → [@nodefony/security](../../security/docs/index.md)
162
+ - Choisir un store de sessions → [session-storage](../../../../../docs/guides/session-storage.md)
163
+ - Vue d'ensemble du framework → [vue-ensemble](../../../../../docs/architecture/vue-ensemble.md)