serverless-ircd 0.5.0 → 0.7.0

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 (394) hide show
  1. package/.github/workflows/ci.yml +40 -3
  2. package/.github/workflows/deploy-aws.yml +1 -2
  3. package/.github/workflows/deploy-cf-tcp.yml +1 -5
  4. package/.github/workflows/deploy-cf.yml +1 -2
  5. package/.gitmodules +3 -0
  6. package/CHANGELOG.md +474 -0
  7. package/README.md +219 -22
  8. package/apps/aws-stack/README.md +33 -0
  9. package/apps/aws-stack/package.json +1 -1
  10. package/apps/aws-stack/src/aws-stack.ts +74 -9
  11. package/apps/aws-stack/tests/coverage-command.test.ts +155 -0
  12. package/apps/aws-stack/tests/stack.test.ts +2 -2
  13. package/apps/aws-stack/tests/synth-no-bundle.test.ts +129 -0
  14. package/apps/aws-stack/tests/vitest.setup.ts +18 -0
  15. package/apps/aws-stack/vitest.config.ts +3 -0
  16. package/apps/cf-tcp-container/package.json +1 -1
  17. package/apps/cf-tcp-container/src/config-loader.ts +6 -7
  18. package/apps/cf-tcp-container/src/container-server.ts +3 -0
  19. package/apps/cf-tcp-container/tests/config-loader.test.ts +10 -3
  20. package/apps/cf-tcp-container/wrangler.toml +1 -1
  21. package/apps/cf-worker/package.json +2 -1
  22. package/apps/cf-worker/src/origin-allowlist.ts +99 -0
  23. package/apps/cf-worker/src/worker.ts +89 -9
  24. package/apps/cf-worker/tests/fixtures/web-dist/app/index.html +18 -0
  25. package/apps/cf-worker/tests/fixtures/web-dist/index.html +18 -0
  26. package/apps/cf-worker/tests/origin-allowlist.test.ts +101 -0
  27. package/apps/cf-worker/tests/smoke.test.ts +28 -2
  28. package/apps/cf-worker/tests/ws-origin.test.ts +89 -0
  29. package/apps/cf-worker/vitest.config.ts +1 -1
  30. package/apps/cf-worker/wrangler.test.toml +23 -0
  31. package/apps/cf-worker/wrangler.toml +59 -3
  32. package/apps/local-cli/package.json +1 -1
  33. package/apps/local-cli/src/config-loader.ts +6 -6
  34. package/apps/local-cli/src/main.ts +20 -0
  35. package/apps/local-cli/src/server.ts +9 -7
  36. package/apps/local-cli/tests/e2e.test.ts +112 -28
  37. package/apps/local-cli/tests/tcp.test.ts +71 -19
  38. package/apps/local-cli/tests/ws-subprotocol.test.ts +12 -7
  39. package/apps/web/landing/index.html +217 -0
  40. package/apps/web/package.json +28 -0
  41. package/apps/web/scripts/build.mjs +125 -0
  42. package/apps/web/src/build-env.ts +43 -0
  43. package/apps/web/src/config-schema.ts +138 -0
  44. package/apps/web/static/config.json +28 -0
  45. package/apps/web/static/config.prod.json +28 -0
  46. package/apps/web/static/config.staging.json +28 -0
  47. package/apps/web/tests/build-env.test.ts +63 -0
  48. package/apps/web/tests/build-smoke.test.ts +103 -0
  49. package/apps/web/tests/config-schema.test.ts +432 -0
  50. package/apps/web/tests/workspace.test.ts +12 -0
  51. package/apps/web/tsconfig.json +10 -0
  52. package/apps/web/upstream/.browserslistrc +3 -0
  53. package/apps/web/upstream/.editorconfig +9 -0
  54. package/apps/web/upstream/.eslintignore +3 -0
  55. package/apps/web/upstream/.eslintrc.js +90 -0
  56. package/apps/web/upstream/.github/FUNDING.yml +12 -0
  57. package/apps/web/upstream/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  58. package/apps/web/upstream/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
  59. package/apps/web/upstream/.github/workflows/push.yml +26 -0
  60. package/apps/web/upstream/.github/workflows/release.yml +33 -0
  61. package/apps/web/upstream/.github/workflows/release_manual.yml +55 -0
  62. package/apps/web/upstream/.prettierrc.js +9 -0
  63. package/apps/web/upstream/.stylelintignore +5 -0
  64. package/apps/web/upstream/.stylelintrc.js +21 -0
  65. package/apps/web/upstream/LICENSE +201 -0
  66. package/apps/web/upstream/README.md +76 -0
  67. package/apps/web/upstream/babel.config.js +25 -0
  68. package/apps/web/upstream/crowdin.yml +3 -0
  69. package/apps/web/upstream/index.html +35 -0
  70. package/apps/web/upstream/jest.config.js +7 -0
  71. package/apps/web/upstream/jsdoc.json +22 -0
  72. package/apps/web/upstream/package.json +80 -0
  73. package/apps/web/upstream/scripts/pre-commit +43 -0
  74. package/apps/web/upstream/src/components/App.vue +516 -0
  75. package/apps/web/upstream/src/components/AppSettings.vue +587 -0
  76. package/apps/web/upstream/src/components/AutoComplete.vue +284 -0
  77. package/apps/web/upstream/src/components/Avatar.vue +105 -0
  78. package/apps/web/upstream/src/components/AwayStatusIndicator.vue +77 -0
  79. package/apps/web/upstream/src/components/BufferKey.vue +121 -0
  80. package/apps/web/upstream/src/components/BufferSettings.vue +97 -0
  81. package/apps/web/upstream/src/components/Captcha.vue +85 -0
  82. package/apps/web/upstream/src/components/ChannelBanlist.vue +96 -0
  83. package/apps/web/upstream/src/components/ChannelInfo.vue +159 -0
  84. package/apps/web/upstream/src/components/ChannelInvitelist.vue +276 -0
  85. package/apps/web/upstream/src/components/ChannelList.vue +345 -0
  86. package/apps/web/upstream/src/components/Container.vue +401 -0
  87. package/apps/web/upstream/src/components/ContainerHeader.vue +522 -0
  88. package/apps/web/upstream/src/components/ControlInput.vue +1016 -0
  89. package/apps/web/upstream/src/components/LoadingAnimation.vue +104 -0
  90. package/apps/web/upstream/src/components/MediaViewer.vue +199 -0
  91. package/apps/web/upstream/src/components/MessageInfo.vue +192 -0
  92. package/apps/web/upstream/src/components/MessageList.vue +1028 -0
  93. package/apps/web/upstream/src/components/MessageListAvatar.vue +20 -0
  94. package/apps/web/upstream/src/components/MessageListMessageCompact.vue +361 -0
  95. package/apps/web/upstream/src/components/MessageListMessageInline.vue +257 -0
  96. package/apps/web/upstream/src/components/MessageListMessageModern.vue +430 -0
  97. package/apps/web/upstream/src/components/NetworkSettings.vue +528 -0
  98. package/apps/web/upstream/src/components/Nicklist.vue +309 -0
  99. package/apps/web/upstream/src/components/NicklistUser.vue +181 -0
  100. package/apps/web/upstream/src/components/NotConnected.vue +271 -0
  101. package/apps/web/upstream/src/components/SelfUser.vue +283 -0
  102. package/apps/web/upstream/src/components/ServerSelector.vue +162 -0
  103. package/apps/web/upstream/src/components/ServerView.vue +126 -0
  104. package/apps/web/upstream/src/components/SettingsAdvanced.vue +232 -0
  105. package/apps/web/upstream/src/components/SettingsAliases.vue +121 -0
  106. package/apps/web/upstream/src/components/Sidebar.vue +429 -0
  107. package/apps/web/upstream/src/components/SidebarAboutBuffer.vue +288 -0
  108. package/apps/web/upstream/src/components/SidebarState.vue +145 -0
  109. package/apps/web/upstream/src/components/StartupError.vue +35 -0
  110. package/apps/web/upstream/src/components/StateBrowser.vue +420 -0
  111. package/apps/web/upstream/src/components/StateBrowserBuffer.vue +124 -0
  112. package/apps/web/upstream/src/components/StateBrowserNetwork.vue +823 -0
  113. package/apps/web/upstream/src/components/StateBrowserUsermenu.vue +162 -0
  114. package/apps/web/upstream/src/components/TypingStatusIndicator.vue +34 -0
  115. package/apps/web/upstream/src/components/TypingUsersList.vue +56 -0
  116. package/apps/web/upstream/src/components/UrlEmbed.vue +149 -0
  117. package/apps/web/upstream/src/components/UserBox.vue +728 -0
  118. package/apps/web/upstream/src/components/inputtools/Emoji.vue +67 -0
  119. package/apps/web/upstream/src/components/inputtools/TextStyle.vue +142 -0
  120. package/apps/web/upstream/src/components/startups/CommonLayout.vue +208 -0
  121. package/apps/web/upstream/src/components/startups/CustomServer.vue +495 -0
  122. package/apps/web/upstream/src/components/startups/KiwiBnc.vue +361 -0
  123. package/apps/web/upstream/src/components/startups/Personal.vue +317 -0
  124. package/apps/web/upstream/src/components/startups/Welcome.vue +544 -0
  125. package/apps/web/upstream/src/components/startups/ZncLogin.vue +229 -0
  126. package/apps/web/upstream/src/components/utils/InputConfirm.vue +68 -0
  127. package/apps/web/upstream/src/components/utils/InputPrompt.vue +118 -0
  128. package/apps/web/upstream/src/components/utils/InputText.vue +172 -0
  129. package/apps/web/upstream/src/components/utils/IrcInput.vue +579 -0
  130. package/apps/web/upstream/src/components/utils/PluginWrapper.vue +26 -0
  131. package/apps/web/upstream/src/components/utils/TabbedView.vue +149 -0
  132. package/apps/web/upstream/src/components/utils/TransitionExpand.vue +97 -0
  133. package/apps/web/upstream/src/helpers/Colours.js +128 -0
  134. package/apps/web/upstream/src/helpers/IrcdDiffs.js +26 -0
  135. package/apps/web/upstream/src/helpers/Md5.js +193 -0
  136. package/apps/web/upstream/src/helpers/Misc.js +401 -0
  137. package/apps/web/upstream/src/helpers/TextFormatting.js +237 -0
  138. package/apps/web/upstream/src/libs/AliasRewriter.js +157 -0
  139. package/apps/web/upstream/src/libs/AudioManager.js +60 -0
  140. package/apps/web/upstream/src/libs/BouncerMiddleware.js +247 -0
  141. package/apps/web/upstream/src/libs/BouncerProvider.js +551 -0
  142. package/apps/web/upstream/src/libs/ChathistoryMiddleware.js +153 -0
  143. package/apps/web/upstream/src/libs/ConfigLoader.js +94 -0
  144. package/apps/web/upstream/src/libs/EmojiProvider.js +46 -0
  145. package/apps/web/upstream/src/libs/GlobalApi.js +346 -0
  146. package/apps/web/upstream/src/libs/IPC.js +51 -0
  147. package/apps/web/upstream/src/libs/InputHandler.js +921 -0
  148. package/apps/web/upstream/src/libs/IrcClient.js +1506 -0
  149. package/apps/web/upstream/src/libs/Logger.js +71 -0
  150. package/apps/web/upstream/src/libs/Message.js +169 -0
  151. package/apps/web/upstream/src/libs/MessageFormatter.js +364 -0
  152. package/apps/web/upstream/src/libs/MessageParser.js +249 -0
  153. package/apps/web/upstream/src/libs/Notifications.js +78 -0
  154. package/apps/web/upstream/src/libs/ServerConnection.js +270 -0
  155. package/apps/web/upstream/src/libs/ServerSession.js +102 -0
  156. package/apps/web/upstream/src/libs/SoundBleep.js +20 -0
  157. package/apps/web/upstream/src/libs/StatePersistence.js +90 -0
  158. package/apps/web/upstream/src/libs/ThemeManager.js +128 -0
  159. package/apps/web/upstream/src/libs/TypingMiddleware.js +105 -0
  160. package/apps/web/upstream/src/libs/WindowTitle.js +63 -0
  161. package/apps/web/upstream/src/libs/batchedAdd.js +74 -0
  162. package/apps/web/upstream/src/libs/bufferTools.js +177 -0
  163. package/apps/web/upstream/src/libs/polyfill/Element.closest.js +18 -0
  164. package/apps/web/upstream/src/libs/renderers/Html.js +113 -0
  165. package/apps/web/upstream/src/libs/settingTools.js +31 -0
  166. package/apps/web/upstream/src/libs/state/BufferState.js +703 -0
  167. package/apps/web/upstream/src/libs/state/NetworkState.js +158 -0
  168. package/apps/web/upstream/src/libs/state/UserState.js +89 -0
  169. package/apps/web/upstream/src/libs/state/common.js +26 -0
  170. package/apps/web/upstream/src/libs/state.js +961 -0
  171. package/apps/web/upstream/src/libs/storage/Local.js +51 -0
  172. package/apps/web/upstream/src/main.js +566 -0
  173. package/apps/web/upstream/src/res/autocompleteCommands.js +31 -0
  174. package/apps/web/upstream/src/res/configTemplates.js +373 -0
  175. package/apps/web/upstream/src/res/globalStyle.css +277 -0
  176. package/apps/web/upstream/src/res/kiwiLoadingLogo.png +0 -0
  177. package/apps/web/upstream/src/res/locales/app.af-ZA.po +1145 -0
  178. package/apps/web/upstream/src/res/locales/app.ar-SA.po +1145 -0
  179. package/apps/web/upstream/src/res/locales/app.bg-BG.po +1145 -0
  180. package/apps/web/upstream/src/res/locales/app.bs-BA.po +1145 -0
  181. package/apps/web/upstream/src/res/locales/app.ca-ES.po +1145 -0
  182. package/apps/web/upstream/src/res/locales/app.cs-CZ.po +1145 -0
  183. package/apps/web/upstream/src/res/locales/app.da-DK.po +1145 -0
  184. package/apps/web/upstream/src/res/locales/app.de-DE.po +1145 -0
  185. package/apps/web/upstream/src/res/locales/app.dev.po +1246 -0
  186. package/apps/web/upstream/src/res/locales/app.el-GR.po +1145 -0
  187. package/apps/web/upstream/src/res/locales/app.en-US.po +1145 -0
  188. package/apps/web/upstream/src/res/locales/app.es-419.po +1145 -0
  189. package/apps/web/upstream/src/res/locales/app.es-AR.po +1145 -0
  190. package/apps/web/upstream/src/res/locales/app.es-EM.po +536 -0
  191. package/apps/web/upstream/src/res/locales/app.es-ES.po +1145 -0
  192. package/apps/web/upstream/src/res/locales/app.es-US.po +1145 -0
  193. package/apps/web/upstream/src/res/locales/app.eu-ES.po +1145 -0
  194. package/apps/web/upstream/src/res/locales/app.fi-FI.po +1145 -0
  195. package/apps/web/upstream/src/res/locales/app.fr-FR.po +1145 -0
  196. package/apps/web/upstream/src/res/locales/app.gl-ES.po +1145 -0
  197. package/apps/web/upstream/src/res/locales/app.he-IL.po +1145 -0
  198. package/apps/web/upstream/src/res/locales/app.hi-IN.po +1145 -0
  199. package/apps/web/upstream/src/res/locales/app.hu-HU.po +1145 -0
  200. package/apps/web/upstream/src/res/locales/app.id-ID.po +1145 -0
  201. package/apps/web/upstream/src/res/locales/app.it-IT.po +1145 -0
  202. package/apps/web/upstream/src/res/locales/app.ja-JP.po +1145 -0
  203. package/apps/web/upstream/src/res/locales/app.ko-KR.po +1145 -0
  204. package/apps/web/upstream/src/res/locales/app.nl-NL.po +1145 -0
  205. package/apps/web/upstream/src/res/locales/app.no-NO.po +1145 -0
  206. package/apps/web/upstream/src/res/locales/app.pl-PL.po +1145 -0
  207. package/apps/web/upstream/src/res/locales/app.pt-BR.po +1145 -0
  208. package/apps/web/upstream/src/res/locales/app.pt-PT.po +1145 -0
  209. package/apps/web/upstream/src/res/locales/app.ro-RO.po +1145 -0
  210. package/apps/web/upstream/src/res/locales/app.ru-RU.po +1145 -0
  211. package/apps/web/upstream/src/res/locales/app.sl-SI.po +1145 -0
  212. package/apps/web/upstream/src/res/locales/app.sq-AL.po +1145 -0
  213. package/apps/web/upstream/src/res/locales/app.sr-SP.po +1145 -0
  214. package/apps/web/upstream/src/res/locales/app.sv-SE.po +1145 -0
  215. package/apps/web/upstream/src/res/locales/app.tr-TR.po +1145 -0
  216. package/apps/web/upstream/src/res/locales/app.uk-UA.po +1145 -0
  217. package/apps/web/upstream/src/res/locales/app.vi-VN.po +1145 -0
  218. package/apps/web/upstream/src/res/locales/app.zh-CN.po +1145 -0
  219. package/apps/web/upstream/src/res/locales/app.zh-TW.po +1145 -0
  220. package/apps/web/upstream/src/res/localesList.json +27 -0
  221. package/apps/web/upstream/src/res/logo.png +0 -0
  222. package/apps/web/upstream/src/thirdparty/about.html +43 -0
  223. package/apps/web/upstream/src/thirdparty/index.js +5 -0
  224. package/apps/web/upstream/src/thirdparty/kiwiirccom.vue +185 -0
  225. package/apps/web/upstream/static/config.json +28 -0
  226. package/apps/web/upstream/static/emoticons/smile.png +0 -0
  227. package/apps/web/upstream/static/favicon.png +0 -0
  228. package/apps/web/upstream/static/highlight.mp3 +0 -0
  229. package/apps/web/upstream/static/highlight.ogg +0 -0
  230. package/apps/web/upstream/static/locales/.gitignore +2 -0
  231. package/apps/web/upstream/static/plugins/customise.html.example +10 -0
  232. package/apps/web/upstream/static/themes/coffee/theme.css +145 -0
  233. package/apps/web/upstream/static/themes/common/base.css +1064 -0
  234. package/apps/web/upstream/static/themes/dark/theme.css +196 -0
  235. package/apps/web/upstream/static/themes/default/theme.css +92 -0
  236. package/apps/web/upstream/static/themes/elite/theme.css +248 -0
  237. package/apps/web/upstream/static/themes/grayfox/theme.css +143 -0
  238. package/apps/web/upstream/static/themes/nightswatch/theme.css +367 -0
  239. package/apps/web/upstream/static/themes/osprey/theme.css +115 -0
  240. package/apps/web/upstream/static/themes/radioactive/theme.css +1211 -0
  241. package/apps/web/upstream/static/themes/sky/theme.css +107 -0
  242. package/apps/web/upstream/tests/unit/BatchAdd.spec.js +174 -0
  243. package/apps/web/upstream/tests/unit/MessageParser.spec.js +125 -0
  244. package/apps/web/upstream/tests/unit/Misc.spec.js +24 -0
  245. package/apps/web/upstream/tests/unit/NetworkState.spec.js +58 -0
  246. package/apps/web/upstream/tests/unit/StartupError.spec.js +20 -0
  247. package/apps/web/upstream/vue.config.js +150 -0
  248. package/apps/web/upstream/yarn.lock +9767 -0
  249. package/apps/web/vitest.config.ts +13 -0
  250. package/biome.json +2 -0
  251. package/docs/ADR-001-pure-reducers-and-effect-system.md +74 -0
  252. package/docs/ADR-002-location-of-authority.md +82 -0
  253. package/docs/ADR-003-durable-object-sharding.md +110 -0
  254. package/docs/ADR-004-dynamodb-schema.md +96 -0
  255. package/docs/ADR-005-wss-only-transport-v1.md +83 -0
  256. package/docs/ADR-006-sasl-mechanism-scope.md +96 -0
  257. package/docs/ADR-007-deterministic-ports.md +82 -0
  258. package/docs/ADR-008-monorepo-tooling.md +60 -0
  259. package/docs/ADR-009-dual-transport-wss-and-irc-tls.md +138 -0
  260. package/docs/ADR-Index.md +15 -0
  261. package/docs/AWS-Adapter-Architecture.md +496 -0
  262. package/docs/AWS-Deployment.md +1275 -0
  263. package/docs/AWS-TCP-Deployment.md +410 -0
  264. package/docs/Cloudflare-Deployment-Guide.md +719 -0
  265. package/docs/Cloudflare-TCP-Deployment.md +510 -0
  266. package/docs/Home.md +16 -0
  267. package/docs/Observability.md +87 -0
  268. package/docs/PlanExtensions.md +812 -0
  269. package/docs/PlanIRCv3Websocket.md +503 -0
  270. package/docs/PlanWebClient.md +451 -0
  271. package/docs/Release-Process.md +443 -0
  272. package/docs/WebClientGuide.md +535 -0
  273. package/package.json +2 -2
  274. package/packages/aws-adapter/package.json +1 -1
  275. package/packages/aws-adapter/src/handlers/default.ts +3 -0
  276. package/packages/aws-adapter/src/handlers/nlb-stream.ts +9 -0
  277. package/packages/aws-adapter/src/serialize.ts +29 -1
  278. package/packages/aws-adapter/tests/account-store-dynamo.test.ts +49 -8
  279. package/packages/aws-adapter/tests/account-store.test.ts +4 -7
  280. package/packages/aws-adapter/tests/aws-harness.ts +5 -2
  281. package/packages/aws-adapter/tests/aws-integration.test.ts +8 -4
  282. package/packages/aws-adapter/tests/aws-runtime.test.ts +79 -0
  283. package/packages/aws-adapter/tests/config-loader.test.ts +11 -2
  284. package/packages/aws-adapter/tests/connect.test.ts +9 -9
  285. package/packages/aws-adapter/tests/disconnect-fanout.test.ts +3 -4
  286. package/packages/aws-adapter/tests/global-setup.ts +55 -30
  287. package/packages/aws-adapter/tests/gone-exception.test.ts +4 -9
  288. package/packages/aws-adapter/tests/handlers.test.ts +90 -4
  289. package/packages/aws-adapter/tests/message-store.test.ts +6 -5
  290. package/packages/aws-adapter/tests/nlb-stream.test.ts +60 -8
  291. package/packages/cf-adapter/package.json +2 -1
  292. package/packages/cf-adapter/src/connection-do.ts +3 -0
  293. package/packages/cf-adapter/src/serialize.ts +25 -4
  294. package/packages/cf-adapter/tests/cf-harness.ts +12 -1
  295. package/packages/cf-adapter/tests/cf-runtime.test.ts +269 -0
  296. package/packages/cf-adapter/tests/channel-do.test.ts +117 -0
  297. package/packages/cf-adapter/tests/config-loader.test.ts +49 -0
  298. package/packages/cf-adapter/tests/connection-do-pure.test.ts +91 -0
  299. package/packages/cf-adapter/tests/d1-account-store.test.ts +48 -0
  300. package/packages/cf-adapter/tests/registry-do.test.ts +104 -0
  301. package/packages/cf-adapter/tests/serialize.test.ts +308 -0
  302. package/packages/cf-adapter/vitest.config.ts +1 -1
  303. package/packages/in-memory-runtime/package.json +1 -1
  304. package/packages/irc-core/package.json +1 -1
  305. package/packages/irc-core/reports/mutation/mutation.html +342 -0
  306. package/packages/irc-core/src/caps/capabilities.ts +37 -3
  307. package/packages/irc-core/src/caps/index.ts +1 -0
  308. package/packages/irc-core/src/caps/sts.ts +84 -0
  309. package/packages/irc-core/src/commands/account-notify.ts +53 -0
  310. package/packages/irc-core/src/commands/away.ts +9 -3
  311. package/packages/irc-core/src/commands/cap.ts +23 -5
  312. package/packages/irc-core/src/commands/chathistory.ts +40 -11
  313. package/packages/irc-core/src/commands/index.ts +34 -0
  314. package/packages/irc-core/src/commands/isupport.ts +53 -0
  315. package/packages/irc-core/src/commands/mode.ts +8 -1
  316. package/packages/irc-core/src/commands/monitor.ts +327 -0
  317. package/packages/irc-core/src/commands/multiline.ts +256 -0
  318. package/packages/irc-core/src/commands/pre-away.ts +112 -0
  319. package/packages/irc-core/src/commands/privmsg.ts +19 -12
  320. package/packages/irc-core/src/commands/read-marker.ts +108 -0
  321. package/packages/irc-core/src/commands/registration.ts +8 -0
  322. package/packages/irc-core/src/commands/sasl.ts +24 -1
  323. package/packages/irc-core/src/commands/tagmsg.ts +71 -1
  324. package/packages/irc-core/src/commands/whois.ts +5 -0
  325. package/packages/irc-core/src/config.ts +65 -10
  326. package/packages/irc-core/src/effects.ts +14 -0
  327. package/packages/irc-core/src/ports.ts +389 -0
  328. package/packages/irc-core/src/protocol/index.ts +14 -0
  329. package/packages/irc-core/src/protocol/labeled-response.ts +133 -0
  330. package/packages/irc-core/src/protocol/numerics.ts +15 -0
  331. package/packages/irc-core/src/protocol/outbound.ts +16 -1
  332. package/packages/irc-core/src/protocol/standard-replies.ts +277 -0
  333. package/packages/irc-core/src/state/connection.ts +32 -1
  334. package/packages/irc-core/src/types.ts +112 -0
  335. package/packages/irc-core/stryker.commands.conf.json +1 -2
  336. package/packages/irc-core/tests/away-store.test.ts +73 -0
  337. package/packages/irc-core/tests/batch.test.ts +15 -0
  338. package/packages/irc-core/tests/caps/capabilities.test.ts +119 -2
  339. package/packages/irc-core/tests/caps/sts.test.ts +123 -0
  340. package/packages/irc-core/tests/commands/account-notify.test.ts +171 -0
  341. package/packages/irc-core/tests/commands/away.test.ts +85 -2
  342. package/packages/irc-core/tests/commands/cap.test.ts +165 -0
  343. package/packages/irc-core/tests/commands/chathistory.test.ts +148 -1
  344. package/packages/irc-core/tests/commands/echo-message.test.ts +9 -3
  345. package/packages/irc-core/tests/commands/isupport.test.ts +80 -0
  346. package/packages/irc-core/tests/commands/mode.test.ts +72 -0
  347. package/packages/irc-core/tests/commands/monitor.test.ts +411 -0
  348. package/packages/irc-core/tests/commands/multiline.test.ts +428 -0
  349. package/packages/irc-core/tests/commands/pre-away.test.ts +241 -0
  350. package/packages/irc-core/tests/commands/privmsg.test.ts +154 -3
  351. package/packages/irc-core/tests/commands/read-marker.test.ts +211 -0
  352. package/packages/irc-core/tests/commands/registration.test.ts +64 -0
  353. package/packages/irc-core/tests/commands/sasl.test.ts +116 -2
  354. package/packages/irc-core/tests/commands/tagmsg.test.ts +357 -0
  355. package/packages/irc-core/tests/commands/whois.test.ts +34 -0
  356. package/packages/irc-core/tests/config.test.ts +103 -13
  357. package/packages/irc-core/tests/labeled-response.test.ts +181 -0
  358. package/packages/irc-core/tests/message-tags.test.ts +114 -0
  359. package/packages/irc-core/tests/monitor-store.test.ts +200 -0
  360. package/packages/irc-core/tests/numerics.test.ts +13 -0
  361. package/packages/irc-core/tests/parser.test.ts +109 -0
  362. package/packages/irc-core/tests/read-marker-store.test.ts +108 -0
  363. package/packages/irc-core/tests/standard-replies.test.ts +412 -0
  364. package/packages/irc-core/tests/types.test.ts +27 -0
  365. package/packages/irc-server/package.json +1 -1
  366. package/packages/irc-server/src/actor.ts +450 -3
  367. package/packages/irc-server/src/dispatch.ts +108 -16
  368. package/packages/irc-server/tests/actor.test.ts +1432 -39
  369. package/packages/irc-server/tests/dispatch.test.ts +263 -2
  370. package/packages/irc-test-support/package.json +1 -1
  371. package/packages/irc-test-support/src/index.ts +6 -0
  372. package/packages/irc-test-support/src/scenarios.ts +24 -2
  373. package/packages/irc-test-support/src/test-config.ts +54 -0
  374. package/packages/irc-test-support/tests/test-config.test.ts +51 -0
  375. package/tools/ci-hardening/package.json +1 -1
  376. package/tools/ci-hardening/tests/ddb-local.test.ts +82 -0
  377. package/tools/load-test/package.json +33 -0
  378. package/tools/load-test/src/client.ts +351 -0
  379. package/tools/load-test/src/config.ts +313 -0
  380. package/tools/load-test/src/harness.ts +116 -0
  381. package/tools/load-test/src/main.ts +120 -0
  382. package/tools/load-test/src/metrics.ts +168 -0
  383. package/tools/load-test/src/report.ts +106 -0
  384. package/tools/load-test/tests/client.test.ts +212 -0
  385. package/tools/load-test/tests/config.test.ts +152 -0
  386. package/tools/load-test/tests/framing.test.ts +37 -0
  387. package/tools/load-test/tests/harness.test.ts +165 -0
  388. package/tools/load-test/tests/metrics.test.ts +174 -0
  389. package/tools/load-test/tests/report.test.ts +161 -0
  390. package/tools/load-test/tests/smoke.test.ts +67 -0
  391. package/tools/load-test/tsconfig.build.json +12 -0
  392. package/tools/load-test/tsconfig.test.json +10 -0
  393. package/tools/load-test/vitest.config.ts +29 -0
  394. package/tools/tcp-ws-forwarder/package.json +1 -1
@@ -42,6 +42,32 @@ jobs:
42
42
  node-version: 24
43
43
  cache: pnpm
44
44
 
45
+ # DynamoDB Local capability for `aws-adapter` coverage.
46
+ #
47
+ # The `aws-adapter` package's vitest `globalSetup` boots a real
48
+ # DynamoDB Local instance so the integration suite (handlers,
49
+ # aws-runtime, transactions, …) can be exercised under coverage.
50
+ # Without it, 171 of 294 tests `describe.skipIf` away and the
51
+ # package drops to ~42% coverage, blowing the 90% gate.
52
+ #
53
+ # The self-hosted Gitea runner image (`ubuntu-latest`) ships with
54
+ # neither Docker (testcontainers' Ryuk reaper fails with "Failed
55
+ # to connect to Reaper" without a `docker.sock` mount or
56
+ # privileged mode) nor a JRE. We install a JRE here via the
57
+ # canonical `setup-java` action so the globalSetup's ZIP fallback
58
+ # can fire — this is the only strategy the runner image can
59
+ # support without host-level privileged-mode configuration.
60
+ #
61
+ # `DDB_LOCAL_STRATEGY=zip` is pinned on the coverage step below
62
+ # so the globalSetup skips the testcontainers probe entirely
63
+ # (otherwise it blocks for ~60s waiting for Ryuk before falling
64
+ # back, which is flaky and wastes CI minutes).
65
+ - name: Setup Java (JRE for DynamoDB Local ZIP fallback)
66
+ uses: actions/setup-java@v4
67
+ with:
68
+ distribution: temurin
69
+ java-version: '11'
70
+
45
71
  - name: Install dependencies
46
72
  run: pnpm install --frozen-lockfile
47
73
 
@@ -52,6 +78,12 @@ jobs:
52
78
  run: pnpm typecheck
53
79
 
54
80
  - name: Test with coverage
81
+ # `DDB_LOCAL_STRATEGY=zip` pins the DynamoDB Local boot to the
82
+ # Java ZIP fallback (see the `Setup Java` step above for why).
83
+ # This skips the testcontainers probe so the runner does not
84
+ # block ~60s waiting for Ryuk before falling back.
85
+ env:
86
+ DDB_LOCAL_STRATEGY: zip
55
87
  run: pnpm coverage
56
88
 
57
89
  - name: Coverage gate (coverage-thresholds enforced per package)
@@ -90,7 +122,7 @@ jobs:
90
122
 
91
123
  - name: Upload coverage
92
124
  if: always()
93
- uses: actions/upload-artifact@v4
125
+ uses: actions/upload-artifact@v3
94
126
  with:
95
127
  name: coverage
96
128
  path: |
@@ -119,7 +151,12 @@ jobs:
119
151
  run: pnpm install --frozen-lockfile
120
152
 
121
153
  - name: Build workspace (irc-core mutation targets)
122
- run: pnpm build
154
+ # Scope the build to irc-core and its workspace deps only. The
155
+ # mutation spot-check mutates irc-core/protocol and irc-core/commands,
156
+ # so building the rest of the workspace (notably @serverless-ircd/web,
157
+ # which spawns `yarn` to build the Kiwi upstream SPA) is unnecessary
158
+ # and fails on runner images that don't ship yarn.
159
+ run: pnpm turbo run build --filter=@serverless-ircd/irc-core...
123
160
 
124
161
  - name: Stryker spot-check on irc-core/protocol
125
162
  # Mutation score threshold >=80% is enforced by the
@@ -135,7 +172,7 @@ jobs:
135
172
 
136
173
  - name: Upload mutation report
137
174
  if: always()
138
- uses: actions/upload-artifact@v4
175
+ uses: actions/upload-artifact@v3
139
176
  with:
140
177
  name: mutation-report
141
178
  path: packages/irc-core/reports/mutation
@@ -38,8 +38,7 @@ name: deploy-aws
38
38
  # behaviour).
39
39
 
40
40
  on:
41
- push:
42
- branches: [main]
41
+ # Automatic push trigger disabled; deploy only runs manually.
43
42
  workflow_dispatch:
44
43
 
45
44
  # OIDC web identity requires id-token write; contents:read for checkout.
@@ -16,11 +16,7 @@ name: deploy-cf-tcp
16
16
  # CF_TCP_ORIGIN_ADDRESS Hostname/IP of the container origin.
17
17
 
18
18
  on:
19
- push:
20
- branches: [main]
21
- paths:
22
- - 'apps/cf-tcp-container/**'
23
- - '.github/workflows/deploy-cf-tcp.yml'
19
+ # Automatic push trigger disabled; deploy only runs manually.
24
20
  workflow_dispatch:
25
21
 
26
22
  concurrency:
@@ -25,8 +25,7 @@ name: deploy-cf
25
25
  # custom routes are wired.
26
26
 
27
27
  on:
28
- push:
29
- branches: [main]
28
+ # Automatic push trigger disabled; deploy only runs manually.
30
29
  workflow_dispatch:
31
30
 
32
31
  # Prevent two deploys racing the same DO namespace.
package/.gitmodules CHANGED
@@ -1,3 +1,6 @@
1
1
  [submodule "docs"]
2
2
  path = docs
3
3
  url = https://gitea.com/fowlmouth/ServerlessIRCd.wiki.git
4
+ [submodule "apps/web/upstream"]
5
+ path = apps/web/upstream
6
+ url = https://github.com/kiwiirc/kiwiirc.git
package/CHANGELOG.md CHANGED
@@ -10,6 +10,480 @@ For the release process itself — versioning policy, pre-release checklist,
10
10
  cutting a tag, rolling back — see [`docs/release.md`](docs/release.md).
11
11
  Cross-reference `progress.md` / `tickets.md` for per-ticket detail.
12
12
 
13
+ ## [0.7.0] - 2026-08-04
14
+
15
+ The headline is the **web client (Phase 14)**: a vendored **Kiwi IRC**
16
+ SPA served at `/app/` and a static project landing page served at `/`
17
+ directly by the Cloudflare Worker, with **Cross-Site WebSocket
18
+ Hijacking (CSWSH) defense** wired into the WS upgrade path. The browser
19
+ opens a native `wss://` straight to the existing IRC-over-WebSocket
20
+ endpoint — no proxy, gateway, or transport adaptation in the hot path.
21
+ Alongside it: a round of **test hardening** (mutation-killing tests for
22
+ `irc-core`'s parser / batch / numerics, and CF-harness / scenario fixes
23
+ for slow transports).
24
+
25
+ ### Added — Web client (`@serverless-ircd/web`, `@serverless-ircd/cf-worker`)
26
+
27
+ Six Phase 14 tickets light up the project's browser surface. The Worker
28
+ becomes the single origin for the landing page, the SPA, **and** the
29
+ WebSocket. Same-origin by default — which is what makes the CSWSH
30
+ defense almost zero-config.
31
+
32
+ - **`apps/web` workspace package (`@serverless-ircd/web`)**: a new
33
+ pnpm workspace package owning the Kiwi build orchestration and config
34
+ schema. The package layout reserves `dist/app/` for the Kiwi SPA
35
+ (served at `/app/`) and `dist/index.html` for the landing page
36
+ (served at `/`). No `@serverless-ircd/*` runtime dependency —
37
+ Kiwi talks to the Worker over plain WebSocket.
38
+ - **Kiwi IRC vendored as a git submodule** (`apps/web/upstream/`,
39
+ tag-pinned): `scripts/build.mjs` patches the upstream `publicPath`
40
+ to `/app/`, runs `yarn build` inside the submodule, copies
41
+ `upstream/dist/` → `apps/web/dist/app/`, then layers our validated
42
+ `static/config.json` over the upstream default. The patch runs in
43
+ `finally` so the submodule stays clean; every hashed asset URL comes
44
+ out `/app/static/...` instead of root-relative. `.gitmodules` updated;
45
+ `biome.json` ignores `apps/web/upstream/**`. `git submodule update
46
+ --init` is now part of the contributor quickstart.
47
+ - **Per-env config matrix + Zod schema** (`apps/web/src/config-schema.ts`,
48
+ `apps/web/src/build-env.ts`): `static/config.json`,
49
+ `static/config.staging.json`, `static/config.prod.json` are validated
50
+ against a Zod schema **before** being baked into
51
+ `dist/app/static/config.json`. `pnpm --filter web build` accepts a
52
+ `--env <staging|prod>` flag (unit-tested in isolation from the slow
53
+ Kiwi build). A malformed config (missing `server`, out-of-range
54
+ `port`, non-`#` channel, `direct: false`, …) aborts the build with
55
+ an aggregated, field-listing error rather than shipping a broken SPA.
56
+ The Kiwi `startupOptions.server` uses the `{{hostname}}` template
57
+ (resolved by the browser to `window.location.hostname` at load time),
58
+ so **one build artifact targets staging, prod, and preview deploys
59
+ without a per-env hostname**.
60
+ - **Cloudflare Worker `[assets]` binding** (`apps/cf-worker/wrangler.toml`,
61
+ `apps/cf-worker/src/worker.ts`): the non-WS, non-`/health` branch of
62
+ the worker's `fetch` falls through to `env.ASSETS`, which serves
63
+ `apps/web/dist`. Per-env (`[env.staging.assets]`) blocks ship so
64
+ `wrangler deploy --env staging` deploys the Worker and the SPA in
65
+ one command. `run_worker_first = true` is set so WS upgrades aren't
66
+ intercepted by the asset platform serving `dist/index.html` at `/`
67
+ with a `200 OK` (the WS endpoint and the landing page share path
68
+ `/`; only the Worker can tell them apart).
69
+ - **Static landing page** (`apps/web/landing/index.html`): framework-
70
+ free HTML + inline CSS, no JavaScript. The project's front door —
71
+ explains what ServerlessIRCd is, links to `/app/` (the IRC client),
72
+ the docs, and the source repo. The build copies it verbatim to
73
+ `dist/index.html`. Renders without JS; responsive; the build-smoke
74
+ suite asserts the `/app/` link, the docs link, and the viewport meta
75
+ tag so they cannot regress.
76
+ - **`/health` is an explicit route**: the previous catch-all plaintext
77
+ liveness string now lives at `GET /health` so `/` is free for the
78
+ landing page. WS upgrades still route to `ConnectionDO`.
79
+
80
+ ### Added — CSWSH defense (`@serverless-ircd/cf-worker`)
81
+
82
+ WebSocket upgrades do **not** follow the same-origin policy. A
83
+ malicious page can `new WebSocket('wss://irc.example.com/')` from the
84
+ victim's browser and drive an IRC session with the victim's
85
+ credentials. The Worker now rejects upgrades whose `Origin` is not
86
+ allowed — see `apps/cf-worker/src/origin-allowlist.ts` and
87
+ `docs/WebClientGuide.md` §5.
88
+
89
+ - **Two enforcement modes, evaluated in order:**
90
+ 1. **Explicit allowlist** — when the `WEB_ORIGINS` var is set, only
91
+ listed origins proceed. Use this for cross-origin deployments
92
+ (SPA on a different domain than the Worker, e.g. Cloudflare
93
+ Pages).
94
+ 2. **Auto-derive (same-origin)** — when `WEB_ORIGINS` is unset/empty
95
+ (the default), the Worker compares the browser's `Origin` against
96
+ the request's own origin (`${protocol}//${host}`). If they match,
97
+ the page opening the WS is the same origin as the server → safe.
98
+ - **`WEB_ORIGINS` is optional.** Because mode 2 is the default and
99
+ requires zero per-env configuration, most deployments never need to
100
+ set it: the SPA and the Worker are on the same origin. It works for
101
+ `*.workers.dev`, custom domains, and preview URLs alike.
102
+ - **Non-browser clients pass through unchanged.** curl, WeeChat, the
103
+ `tcp-ws-forwarder`, and scripted test harnesses never send `Origin`;
104
+ their upgrades are allowed in both modes so existing tooling keeps
105
+ working.
106
+ - **No fail-open path.** A browser-sent `Origin` that is not allowed
107
+ → `403 Forbidden`. The extreme edge case (no allowlist AND
108
+ `Host` undeterminable, which never fires on Cloudflare) is
109
+ fail-closed `403`. The pure policy is exhaustively unit-tested in
110
+ `apps/cf-worker/tests/origin-allowlist.test.ts`; the worker
111
+ integration is covered by `tests/ws-origin.test.ts`.
112
+
113
+ ### Added — Documentation (`docs/WebClientGuide.md`)
114
+
115
+ End-to-end contributor/operator doc covering: how the SPA, landing
116
+ page, and WS endpoint fit on one origin; prerequisites (incl. `yarn`
117
+ for the Kiwi build only); the quickstart (`git submodule update --init`
118
+ → `pnpm --filter web build:staging` → `pnpm deploy:cf:staging`); a
119
+ walkthrough of the build pipeline and the per-env config matrix; the
120
+ CSWSH rationale and the optional `WEB_ORIGINS` var; local dev
121
+ (`wrangler dev` serves everything on `http://localhost:8787`); an
122
+ optional Cloudflare Pages alternative for cross-origin prod deploys;
123
+ the optional `webircgateway` SockJS fallback (strictly opt-in, not
124
+ needed for normal operation); landing-page customisation; and a
125
+ troubleshooting section. The acceptance criterion is that a new
126
+ contributor can deploy a staging SPA following only this doc.
127
+
128
+ ### Added — Test hardening (`@serverless-ircd/irc-core`, `@serverless-ircd/cf-adapter`, `@serverless-ircd/irc-test-support`)
129
+
130
+ - **Mutation-killing tests for `irc-core`**: the parser, batch, and
131
+ numerics suites were expanded to kill surviving Stryker mutants —
132
+ command-token anchors, strict source-prefix shape, malformed-input
133
+ diagnostic text, middle-param whitespace collapsing,
134
+ `unescapeTagValue` boundary conditions; empty-batch framing metadata
135
+ surviving elision; and `numericToName` round-trip parity against the
136
+ registry. A generated Stryker mutation report is committed under
137
+ `packages/irc-core/reports/mutation/`.
138
+ - **CF harness / scenario fixes for slow transports**: the CF test
139
+ harness now waits for the end of the registration burst (`376` /
140
+ `422`) instead of `001`, so late MOTD frames do not pollute
141
+ `received` after a test clears it. The NOTICE scenario now only
142
+ rejects NOTICE-relevant error numerics (rather than any numeric),
143
+ preventing delayed welcome / MOTD lines from false-positiving the
144
+ RFC no-error-replies check. No production code changed.
145
+
146
+ ### Changed
147
+
148
+ - **Cloudflare Worker routing.** `/health` is the new liveness route;
149
+ the root `/` now serves the landing page from the `[assets]`
150
+ binding. WS upgrades still route to `ConnectionDO`. Operators who
151
+ scraped the root `/` for the plaintext health string must update
152
+ to `/health`.
153
+
154
+ ### ⚠️ Migration required
155
+
156
+ - **New optional runtime var: `WEB_ORIGINS`.** Optional — same-origin
157
+ auto-derive is the default. Set it only for cross-origin SPA
158
+ deployments (SPA on Cloudflare Pages, Worker serving the WS only,
159
+ etc.):
160
+ ```toml
161
+ # apps/cf-worker/wrangler.toml
162
+ [vars]
163
+ WEB_ORIGINS = "https://irc.example.com,https://web.example.com"
164
+ ```
165
+ - **New build prerequisite for the SPA.** Building `apps/web` needs
166
+ the Kiwi submodule (`git submodule update --init apps/web/upstream`)
167
+ and `yarn` (provided by `corepack enable`). The CF deploy workflow
168
+ runs `pnpm build` (which builds the SPA + landing page) before
169
+ `wrangler deploy`. If you deploy manually and skip the SPA build,
170
+ the Worker still works but `/app/` and `/` return 404 from the asset
171
+ layer.
172
+ - **`/health` is the new liveness route.** Operators who scraped `/`
173
+ for the plaintext health string must update to `/health`.
174
+ - **New workspace package.** `apps/web` is a new private package
175
+ (`@serverless-ircd/web`); `pnpm install` picks it up automatically
176
+ from the existing `pnpm-workspace.yaml` glob.
177
+ - **No persisted-state schema change.** `PERSISTED_STATE_VERSION`
178
+ stays `1`. **Code rollback across this release is safe** — the
179
+ web-client work adds the `[assets]` binding and the `/health` route
180
+ but touches no Durable Object state.
181
+ - No toolchain change. Node ≥ 24 / pnpm 11 (from v0.4.0) still apply.
182
+
183
+ ### Known limitations
184
+
185
+ - **Phase 14 is partial.** The Playwright headless-browser e2e
186
+ against the deployed staging frontend remains pending
187
+ — the SPA is exercised via the existing WS smoke
188
+ (`scripts/smoke.mjs`) and the new `apps/web` unit / build-smoke
189
+ suite, but no browser-driven end-to-end test runs in CI yet.
190
+ - **The web client is Cloudflare-only.** The `[assets]` binding is a
191
+ Cloudflare Worker feature; AWS deployments continue to require an
192
+ external static host for the SPA (and must add the SPA's origin to
193
+ the Worker-style `WEB_ORIGINS` equivalent if/when an AWS-side
194
+ CSWSH check lands).
195
+ - Same transport limits as v0.6.0: the **CF TCP path requires
196
+ Cloudflare Spectrum (Enterprise tier)** plus a stateful Container
197
+ origin; the **AWS TCP path** uses NLB + Lambda streaming and is
198
+ subject to Lambda idle-timeout / stream-duration limits. The
199
+ WebSocket path remains the zero-extra-deps default.
200
+ - **mTLS (for SASL `EXTERNAL`) requires a custom domain + uploaded
201
+ CA/trust-store** on both platforms and is not enabled by default.
202
+ - **Client compatibility sweep** (WeeChat / HexChat / IRCCloud /
203
+ TheLounge / matrix-IRC bridge) remains pending.
204
+ - **Coverage hardening is partial.** `aws-adapter` and `aws-stack`
205
+ clear the 90% gate (v0.6.0); the remaining packages
206
+ (`cf-adapter`, `local-cli`, `load-test`, `cf-tcp-container`,
207
+ `tcp-ws-forwarder`, `irc-test-support`, and now `web`) are **above
208
+ the gate but below 100%** — follow-ups drive each to 100%.
209
+ - The IRC services Phase 13 work (NickServ / ChanServ / HostServ /
210
+ OperServ / MemoServ) is pending; `draft/read-marker` and
211
+ `draft/pre-away` ship with in-memory reference stores, so marker /
212
+ away persistence does not survive an adapter restart until a
213
+ services backend lands.
214
+
215
+ ### Security
216
+
217
+ - **CSWSH defense added.** WebSocket upgrades from browsers now
218
+ require an allowed `Origin`. Same-origin auto-derive is the default
219
+ (zero config); explicit `WEB_ORIGINS` allowlist for cross-origin
220
+ setups. Non-browser clients (no `Origin`) pass through unchanged.
221
+ There is no fail-open path. See `docs/WebClientGuide.md` §5. No
222
+ other auth path changed (SASL `PLAIN` / `EXTERNAL`, oper gate,
223
+ admission limits, and cloaking are unchanged from v0.6.0).
224
+
225
+ ---
226
+
227
+ ## [0.6.0] - 2026-08-03
228
+
229
+ The headline is the **IRCv3 extension sweep (Phase 12)**: ten negotiated
230
+ caps and the user-mode-`S` TLS indicator land in one release, taking the
231
+ negotiated capability surface from "modern baseline" to "draft-leading-
232
+ edge". Alongside it: the **required-`serverName` config gate** (the
233
+ last open v0.5.0 follow-up), a new **`tools/load-test`** client farm
234
+ (Phase 7's 10k-connection harness), and the **coverage / CI unblocks**
235
+ that lift `aws-adapter` over the 90% gate and switch `cf-adapter` /
236
+ `cf-worker` to istanbul (clearing the v0.5.0 workerd-incompatibility
237
+ caveat). Deploy workflows are now **manual-only** (`workflow_dispatch`).
238
+
239
+ ### Added — IRCv3 extensions (`@serverless-ircd/irc-core`, `@serverless-ircd/irc-server`)
240
+
241
+ The ten Phase 12 cap tickets light up the remaining negotiated-cap
242
+ surface, and the two non-cap Phase 12 tickets (ISUPPORT tokens and
243
+ user mode `S`) round out the wire-visible advertisement. Each cap
244
+ ships a pure reducer (where applicable), an actor route case,
245
+ advertisement in `SUPPORTED_CAPABILITIES` (or conditional
246
+ advertisement for `sts`), and unit + actor coverage. The
247
+ pure-reducer / dispatch pipeline is unchanged — every cap is plumbed
248
+ through `ctx` and emitted `Effect[]`, never performed in the handler.
249
+
250
+ - **`account-notify`**: pushes `:nick ACCOUNT <name|*>`
251
+ to peers in shared channels (excluding the sender unless
252
+ `echo-message`) on SASL login / logout. Closes the
253
+ `account-tag` / `extended-join` / `account-notify` triple modern
254
+ clients render fully.
255
+ - **`msgid`**: every PRIVMSG / NOTICE / TAGMSG fanned out
256
+ to a cap-enabled peer carries `@+msgid=<id>` from
257
+ `IdFactory.nonce()`. The nonce is **shared between the live fanout
258
+ and the `draft/chathistory` record** so a peer sees the same id live
259
+ and on replay. Legacy peers see no `msgid` tag.
260
+ - **`standard-replies`**: a data-driven
261
+ `numericToStandardReply` table replaces a curated subset of bare
262
+ numerics (`461`, `403`, `432`, `433`, SASL failures, …) with
263
+ `FAIL`/`WARN`/`NOTE COMMAND code context :description` for
264
+ cap-enabled clients. Legacy clients still receive the unchanged
265
+ numeric. Numeric-only replies (MOTD lines, NAMES, …) stay on their
266
+ numeric form.
267
+ - **`MONITOR`**: the modern `ISON` replacement. Implements
268
+ `MONITOR + nick{,nick}` / `- nick` / `C` / `L` / `S` with the
269
+ `730 RPL_MONONLINE` / `731 RPL_MONOFFLINE` / `732 RPL_MONLIST` /
270
+ `734 ERR_MONLISTFULL` numerics. Online/offline transitions push
271
+ `730`/`731` **asynchronously** via the Registry's nick reserve /
272
+ release hooks (a new `MonitorStore` port + push callback). The
273
+ ceiling is config-driven (`MONITOR=<n>`, default 30).
274
+ - **`labeled-response`**: a client-sent `+label=<id>` is
275
+ plumbed through `ctx` and every reply caused by that message is
276
+ wrapped in a single `BATCH +id labeled-response … BATCH -id`. A
277
+ command that produces no reply emits an empty labeled batch (spec-
278
+ mandated). The label travels on every reducer's emitted effects,
279
+ so multi-reply commands accumulate under one batch.
280
+ - **`sts`**: Strict Transport Security. The cap is
281
+ **conditionally advertised** — only when `ServerConfig.sts`
282
+ (`{ duration, port, preload? }`) is supplied. On a plaintext
283
+ listener a compliant client upgrades; on the TLS listener the
284
+ duration is still sent to lock in the policy. Defaults:
285
+ `duration = 30 days`, `port = 6697`.
286
+ - **`draft/typing`**: `TAGMSG <target> +draft/typing=
287
+ active|paused|done` broadcasts to channel members (or routes to a
288
+ nick target), excluding the sender unless `echo-message`. The
289
+ `+draft/typing=*` client tag is whitelisted for clients without
290
+ `message-tags` (per the spec's client-tag exception). Broadcast
291
+ delivery uses **OR-semantics**: a recipient qualifies if it
292
+ negotiated `message-tags` **or** `draft/typing` (a new `caps` field
293
+ on `BroadcastEffect`).
294
+ - **`draft/multiline`**: a cap-enabled
295
+ client sends `BATCH +id draft/multiline :target` + N PRIVMSG/NOTICE
296
+ lines + `BATCH -id`. The server concatenates per spec (with `\n`),
297
+ enforces a configurable byte budget (`multilineMaxBytes`, default
298
+ 4096), and broadcasts the batched replay to multiline peers / a
299
+ single joined PRIVMSG (carrying `+draft/multiline`) to legacy
300
+ peers. **Nick-target routing**, **chathistory recording** (one
301
+ record per batch carrying the joined body, assigned `msgid`, and
302
+ the `+draft/multiline` client tag), and **tag gating** (the
303
+ `+draft/multiline` tag is stripped for peers without
304
+ `message-tags`) all land in the completion ticket.
305
+ - **`draft/read-marker`**: persists the per-`(account,
306
+ channel)` last-read marker in a new `ReadMarkerStore` port,
307
+ propagates updates via `+draft/read-marker=<msgid>` tags, and
308
+ seeds a fresh connection's marker from the persisted value at
309
+ identify time. `CHATHISTORY BEFORE` honours the marker when no
310
+ explicit pivot is supplied.
311
+ - **`draft/pre-away`**: persists the away reason in a
312
+ new `AwayStore` port keyed by account. On identify (or SASL login)
313
+ the server re-applies the away state and emits the `AWAY` line +
314
+ `306 RPL_NOWAWAY` as if the user had sent `AWAY :reason`
315
+ themselves. `AWAY` (unset) clears the persisted reason.
316
+ - **ISUPPORT tokens for the new caps**: `005
317
+ RPL_ISUPPORT` now advertises `STATUSMSG=@+`, `EXTBAN=<prefix>,q`,
318
+ `ACCOUNTEXTBAN=a` (services-gated), `TYPING`, and `MULTILINE`.
319
+ `MONITOR=<n>` honours `serverConfig.monitorLimit`. Tokens are
320
+ config-derived where applicable.
321
+ - **User mode `S` (TLS connected)**: the transport's
322
+ TLS fact is surfaced end-to-end as a read-only user mode. Set
323
+ once by the registration reducer from `ConnectionState.secure`;
324
+ emitted in `221 … +S`; rejected by `MODE nick +/-S` with `501
325
+ ERR_UMODEUNKNOWNFLAG`; surfaced in WHOIS via the new `276
326
+ RPL_WHOISSECURE`. Each adapter asserts `secure: true` where the
327
+ platform terminates TLS (API Gateway, CF edge, Spectrum, NLB TLS
328
+ listener).
329
+
330
+ ### Changed — Required server identity
331
+
332
+ The last open v0.5.0 follow-up closes. The `irc.example.com`
333
+ placeholder `DEFAULT_SERVER_CONFIG` is **removed** from
334
+ `irc-core`; the shared `ServerConfigSchema` rejects a missing
335
+ `serverName` / `networkName` so every adapter **fails fast at boot**
336
+ with a readable, field-listing error.
337
+
338
+ - **`@serverless-ircd/irc-core`**: `DEFAULT_SERVER_CONFIG` removed.
339
+ - **`@serverless-ircd/irc-test-support`**: ships a clearly test-scoped
340
+ `makeTestServerConfig()` baseline (`irc.test.local`) so unit tests
341
+ construct a `ServerConfig` without re-introducing a production
342
+ default.
343
+ - `apps/cf-worker` + `apps/cf-tcp-container`: loaders require
344
+ `SERVER_NAME`; `wrangler.toml` samples switched to
345
+ `irc.your-domain.invalid`.
346
+ - `apps/aws-stack` + `apps/local-cli`: keep obviously-local fallbacks
347
+ (`irc.localhost`) documented as *"production MUST override"*;
348
+ `local-cli` gains `--server-name` / `--network-name` flags.
349
+
350
+ ### Added — Load-test harness (`@serverless-ircd/load-test`)
351
+
352
+ PLAN §7 / Phase 7's 10k-connection harness. A synthetic WebSocket IRC
353
+ client farm: opens N connections, registers (NICK/USER), joins a
354
+ channel, and optionally chats. Captures **p50 / p95 / p99 latency per
355
+ phase** (connect / register / join / message) plus the **drop rate**,
356
+ and prints a markdown report to stdout (or `--report <path>`).
357
+
358
+ ```bash
359
+ pnpm --filter load-test start -- \
360
+ --target wss://irc.staging.example.com/ \
361
+ --connections 10000 --concurrency 200 --ramp-ms 60000 \
362
+ --channel '#loadtest' --messages 3 --platform cf-staging \
363
+ --report reports/cf-staging-2026-08-03.md
364
+ ```
365
+
366
+ The package ships its own vitest suite (config parsing, framing,
367
+ harness, metrics, report) at ≥90% coverage; no production source in
368
+ the irc-core / irc-server packages changed to support it.
369
+
370
+ ### Added — Coverage / CI unblocks
371
+
372
+ - **`aws-adapter` clears the 90% gate.** The self-hosted runner image
373
+ ships with neither Docker nor a JRE, so the package's
374
+ testcontainers / Java-ZIP DynamoDB Local boot fell through to
375
+ strategy 3 and 171 of 294 integration tests `describe.skipIf`-ed
376
+ away — dropping coverage to ~42% and blowing the gate. CI now
377
+ installs Temurin JRE 11 via `actions/setup-java@v4` and pins
378
+ `DDB_LOCAL_STRATEGY=zip` on the coverage step so the ZIP fallback
379
+ fires deterministically. The full aws-adapter suite now runs on
380
+ every coverage job. New integration tests cover
381
+ `broadcastWallops` fan-out, `reserveNick` non-conditional error
382
+ propagation, `refreshChannel` empty-members / topic-preservation
383
+ paths, NLB header fallbacks, scan-row type guards, and
384
+ `CREATED_AT` overflow handling. `tools/ci-hardening` gained a
385
+ self-check asserting the JRE install + strategy pin remain wired.
386
+ - **`aws-stack` coverage command repaired.** The CDK `NodejsFunction`
387
+ in the test synth re-bundled the Lambda asset via esbuild ~80
388
+ times per coverage run and OOM-ed CI before vitest could print a
389
+ report. A new `makeLambda()` factory switches between
390
+ `NodejsFunction` (production synth) and an inline-zip `Function`
391
+ stub (tests) via the `IRC_AWS_STACK_TEST_NO_BUNDLE` env var. The
392
+ stub preserves every CloudFormation shape the suite asserts on
393
+ while making bundling a no-op. New
394
+ `tests/synth-no-bundle.test.ts` + `tests/coverage-command.test.ts`
395
+ lock both branches and assert the `pnpm coverage` run prints an
396
+ `All files` row.
397
+ - **`cf-adapter` / `cf-worker` switch to istanbul coverage.** This
398
+ **resolves the v0.5.0 known limitation**: `@vitest/coverage-v8`
399
+ v4.1.x imports `node:inspector/promises`, which the
400
+ `@cloudflare/vitest-pool-workers` (workerd) pool cannot resolve.
401
+ Both packages now use `@vitest/coverage-istanbul` so `pnpm
402
+ coverage` loads under workerd. Edge-case suites were expanded
403
+ (serialize/deserialize/migrate, connection-do pure helpers,
404
+ `broadcastWallops` fan-out, `changeNick` shard routing,
405
+ ChannelDO broadcast/cache, RegistryDO `listEntries`, SASL seed
406
+ parsing).
407
+
408
+ ### Changed — Deploy workflows are now manual-only
409
+
410
+ `deploy-cf.yml`, `deploy-cf-tcp.yml`, and `deploy-aws.yml` dropped
411
+ their `push: branches: [main]` triggers and now run on
412
+ `workflow_dispatch` only. Staging deploys are no longer fired
413
+ automatically on every merge to `main`; a maintainer kicks them off
414
+ by hand. The release runbook (`docs/Release-Process.md`) is the
415
+ canonical reference either way — there was never a CI auto-deploy to
416
+ production.
417
+
418
+ ### Changed — `@vitest/coverage-istanbul` peer
419
+
420
+ The root lockfile (`pnpm-lock.yaml`) registers the
421
+ `@vitest/coverage-istanbul` peer introduced by the cf-adapter /
422
+ cf-worker coverage switch. No operator action; `pnpm install`
423
+ resolves it.
424
+
425
+ ### ⚠️ Migration required
426
+
427
+ - **`serverName` is now required at boot.** Operators who relied on
428
+ the silent `irc.example.com` placeholder must supply an explicit
429
+ value via their adapter's config knob:
430
+ - **Cloudflare Worker / TCP container**: set `SERVER_NAME` (and
431
+ `NETWORK_NAME` if desired) in `wrangler.toml` `[vars]` or via
432
+ `wrangler secret put`. The samples switched to
433
+ `irc.your-domain.invalid` — replace with your real hostname.
434
+ - **AWS**: the CDK stack's `serverName` prop / `SERVER_NAME` env
435
+ var. The local fallback is `irc.localhost` and is explicitly
436
+ documented as "production MUST override".
437
+ - **`local-cli`**: new `--server-name <h>` / `--network-name <n>`
438
+ flags (default `irc.localhost` / `LocalNet`).
439
+ A missing value now throws an aggregated `"Invalid server
440
+ config:"` error at boot listing every offending field.
441
+ - **No persisted-state schema change.** `PERSISTED_STATE_VERSION`
442
+ stays `1`. The user-mode-`S` work adds a `secure` field and a
443
+ `userModes.tls` field to the persisted shape, but both are purely
444
+ **additive with `false` defaults** and the cf-adapter `migrate()`
445
+ path backfills them on pre-feature records. **Code rollback across
446
+ this release is safe.**
447
+ - No toolchain change. Node ≥ 24 / pnpm 11 (from v0.4.0) still apply.
448
+
449
+ ### Known limitations
450
+
451
+ - Same transport limits as v0.5.0: the **CF TCP path requires
452
+ Cloudflare Spectrum (Enterprise tier)** plus a stateful Container
453
+ origin; the **AWS TCP path** uses NLB + Lambda streaming and is
454
+ subject to Lambda idle-timeout / stream-duration limits. The
455
+ WebSocket path remains the zero-extra-deps default.
456
+ - **mTLS (for SASL `EXTERNAL`) requires a custom domain + uploaded
457
+ CA/trust-store** on both platforms and is not enabled by default.
458
+ - `STATS` ships the `u` + `l` query letters; the full RFC matrix
459
+ (`k`, `K`, `q`, `g`, `x`, `z`, …) is intentionally out of scope for
460
+ v1 — unknown letters get the charybdis-style empty body + `219`.
461
+ - **Client compatibility sweep** (WeeChat / HexChat / IRCCloud /
462
+ TheLounge / matrix-IRC bridge) remains pending.
463
+ - **Coverage hardening is partial.** `aws-adapter` and `aws-stack`
464
+ now clear the 90% gate, and `cf-adapter` / `cf-worker` run under
465
+ istanbul; the remaining packages (`cf-adapter`, `local-cli`,
466
+ `load-test`, `cf-tcp-container`, `tcp-ws-forwarder`,
467
+ `irc-test-support`) are **above the gate but below 100%** —
468
+ follow-ups drive each to 100%.
469
+ - The IRC services (NickServ / ChanServ / HostServ / OperServ /
470
+ MemoServ) Phase 13 work is pending; `draft/read-marker` and
471
+ `draft/pre-away` ship with in-memory reference stores, so marker /
472
+ away persistence does not survive an adapter restart until a
473
+ services backend lands.
474
+ - The web client Phase 14 work (vendor Kiwi IRC, serve SPA from the
475
+ Worker, WS Origin allowlist) is pending.
476
+
477
+ ### Security
478
+
479
+ - None. No vulnerabilities have been reported or fixed in this
480
+ release. The new caps add surface area but ship no new auth path
481
+ (the SASL `PLAIN` / `EXTERNAL` paths and the oper gate are
482
+ unchanged); the required-`serverName` change rejects misconfigured
483
+ deploys at boot rather than advertising a placeholder identity.
484
+
485
+ ---
486
+
13
487
  ## [0.5.0] - 2026-08-02
14
488
 
15
489
  The headline is **protocol completion**: the deferred IRC verbs land