serverless-ircd 0.6.0 → 0.8.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.
- package/.github/workflows/ci.yml +9 -4
- package/.gitmodules +4 -1
- package/CHANGELOG.md +480 -22
- package/README.md +244 -57
- package/apps/aws-stack/package.json +1 -1
- package/apps/aws-stack/src/aws-stack.ts +186 -18
- package/apps/aws-stack/tests/stack.test.ts +400 -56
- package/apps/cf-tcp-container/package.json +1 -1
- package/apps/cf-worker/package.json +1 -1
- package/apps/cf-worker/src/origin-allowlist.ts +99 -0
- package/apps/cf-worker/src/worker.ts +89 -9
- package/apps/cf-worker/tests/fixtures/web-dist/index.html +18 -0
- package/apps/cf-worker/tests/fixtures/web-dist/webclient/index.html +18 -0
- package/apps/cf-worker/tests/origin-allowlist.test.ts +101 -0
- package/apps/cf-worker/tests/smoke.test.ts +28 -2
- package/apps/cf-worker/tests/ws-origin.test.ts +89 -0
- package/apps/cf-worker/wrangler.test.toml +23 -0
- package/apps/cf-worker/wrangler.toml +56 -0
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +8 -0
- package/apps/local-cli/src/main.ts +16 -0
- package/apps/local-cli/src/server.ts +1 -0
- package/apps/local-cli/tests/config-loader.test.ts +14 -0
- package/apps/web/landing/index.html +215 -0
- package/apps/web/package.json +28 -0
- package/apps/web/scripts/build.mjs +132 -0
- package/apps/web/src/build-env.ts +43 -0
- package/apps/web/src/config-schema.ts +138 -0
- package/apps/web/static/config.json +28 -0
- package/apps/web/static/config.prod.json +28 -0
- package/apps/web/static/config.staging.json +28 -0
- package/apps/web/tests/build-env.test.ts +63 -0
- package/apps/web/tests/build-smoke.test.ts +103 -0
- package/apps/web/tests/config-schema.test.ts +432 -0
- package/apps/web/tests/workspace.test.ts +12 -0
- package/apps/web/tsconfig.json +10 -0
- package/apps/web/upstream/.browserslistrc +3 -0
- package/apps/web/upstream/.editorconfig +9 -0
- package/apps/web/upstream/.eslintignore +3 -0
- package/apps/web/upstream/.eslintrc.js +90 -0
- package/apps/web/upstream/.github/FUNDING.yml +12 -0
- package/apps/web/upstream/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
- package/apps/web/upstream/.github/ISSUE_TEMPLATE/feature_request.md +20 -0
- package/apps/web/upstream/.github/workflows/push.yml +26 -0
- package/apps/web/upstream/.github/workflows/release.yml +33 -0
- package/apps/web/upstream/.github/workflows/release_manual.yml +55 -0
- package/apps/web/upstream/.prettierrc.js +9 -0
- package/apps/web/upstream/.stylelintignore +5 -0
- package/apps/web/upstream/.stylelintrc.js +21 -0
- package/apps/web/upstream/LICENSE +201 -0
- package/apps/web/upstream/README.md +76 -0
- package/apps/web/upstream/babel.config.js +25 -0
- package/apps/web/upstream/crowdin.yml +3 -0
- package/apps/web/upstream/index.html +35 -0
- package/apps/web/upstream/jest.config.js +7 -0
- package/apps/web/upstream/jsdoc.json +22 -0
- package/apps/web/upstream/package.json +80 -0
- package/apps/web/upstream/scripts/pre-commit +43 -0
- package/apps/web/upstream/src/components/App.vue +516 -0
- package/apps/web/upstream/src/components/AppSettings.vue +587 -0
- package/apps/web/upstream/src/components/AutoComplete.vue +284 -0
- package/apps/web/upstream/src/components/Avatar.vue +105 -0
- package/apps/web/upstream/src/components/AwayStatusIndicator.vue +77 -0
- package/apps/web/upstream/src/components/BufferKey.vue +121 -0
- package/apps/web/upstream/src/components/BufferSettings.vue +97 -0
- package/apps/web/upstream/src/components/Captcha.vue +85 -0
- package/apps/web/upstream/src/components/ChannelBanlist.vue +96 -0
- package/apps/web/upstream/src/components/ChannelInfo.vue +159 -0
- package/apps/web/upstream/src/components/ChannelInvitelist.vue +276 -0
- package/apps/web/upstream/src/components/ChannelList.vue +345 -0
- package/apps/web/upstream/src/components/Container.vue +401 -0
- package/apps/web/upstream/src/components/ContainerHeader.vue +522 -0
- package/apps/web/upstream/src/components/ControlInput.vue +1016 -0
- package/apps/web/upstream/src/components/LoadingAnimation.vue +104 -0
- package/apps/web/upstream/src/components/MediaViewer.vue +199 -0
- package/apps/web/upstream/src/components/MessageInfo.vue +192 -0
- package/apps/web/upstream/src/components/MessageList.vue +1028 -0
- package/apps/web/upstream/src/components/MessageListAvatar.vue +20 -0
- package/apps/web/upstream/src/components/MessageListMessageCompact.vue +361 -0
- package/apps/web/upstream/src/components/MessageListMessageInline.vue +257 -0
- package/apps/web/upstream/src/components/MessageListMessageModern.vue +430 -0
- package/apps/web/upstream/src/components/NetworkSettings.vue +528 -0
- package/apps/web/upstream/src/components/Nicklist.vue +309 -0
- package/apps/web/upstream/src/components/NicklistUser.vue +181 -0
- package/apps/web/upstream/src/components/NotConnected.vue +271 -0
- package/apps/web/upstream/src/components/SelfUser.vue +283 -0
- package/apps/web/upstream/src/components/ServerSelector.vue +162 -0
- package/apps/web/upstream/src/components/ServerView.vue +126 -0
- package/apps/web/upstream/src/components/SettingsAdvanced.vue +232 -0
- package/apps/web/upstream/src/components/SettingsAliases.vue +121 -0
- package/apps/web/upstream/src/components/Sidebar.vue +429 -0
- package/apps/web/upstream/src/components/SidebarAboutBuffer.vue +288 -0
- package/apps/web/upstream/src/components/SidebarState.vue +145 -0
- package/apps/web/upstream/src/components/StartupError.vue +35 -0
- package/apps/web/upstream/src/components/StateBrowser.vue +420 -0
- package/apps/web/upstream/src/components/StateBrowserBuffer.vue +124 -0
- package/apps/web/upstream/src/components/StateBrowserNetwork.vue +823 -0
- package/apps/web/upstream/src/components/StateBrowserUsermenu.vue +162 -0
- package/apps/web/upstream/src/components/TypingStatusIndicator.vue +34 -0
- package/apps/web/upstream/src/components/TypingUsersList.vue +56 -0
- package/apps/web/upstream/src/components/UrlEmbed.vue +149 -0
- package/apps/web/upstream/src/components/UserBox.vue +728 -0
- package/apps/web/upstream/src/components/inputtools/Emoji.vue +67 -0
- package/apps/web/upstream/src/components/inputtools/TextStyle.vue +142 -0
- package/apps/web/upstream/src/components/startups/CommonLayout.vue +208 -0
- package/apps/web/upstream/src/components/startups/CustomServer.vue +495 -0
- package/apps/web/upstream/src/components/startups/KiwiBnc.vue +361 -0
- package/apps/web/upstream/src/components/startups/Personal.vue +317 -0
- package/apps/web/upstream/src/components/startups/Welcome.vue +544 -0
- package/apps/web/upstream/src/components/startups/ZncLogin.vue +229 -0
- package/apps/web/upstream/src/components/utils/InputConfirm.vue +68 -0
- package/apps/web/upstream/src/components/utils/InputPrompt.vue +118 -0
- package/apps/web/upstream/src/components/utils/InputText.vue +172 -0
- package/apps/web/upstream/src/components/utils/IrcInput.vue +579 -0
- package/apps/web/upstream/src/components/utils/PluginWrapper.vue +26 -0
- package/apps/web/upstream/src/components/utils/TabbedView.vue +149 -0
- package/apps/web/upstream/src/components/utils/TransitionExpand.vue +97 -0
- package/apps/web/upstream/src/helpers/Colours.js +128 -0
- package/apps/web/upstream/src/helpers/IrcdDiffs.js +26 -0
- package/apps/web/upstream/src/helpers/Md5.js +193 -0
- package/apps/web/upstream/src/helpers/Misc.js +401 -0
- package/apps/web/upstream/src/helpers/TextFormatting.js +237 -0
- package/apps/web/upstream/src/libs/AliasRewriter.js +157 -0
- package/apps/web/upstream/src/libs/AudioManager.js +60 -0
- package/apps/web/upstream/src/libs/BouncerMiddleware.js +247 -0
- package/apps/web/upstream/src/libs/BouncerProvider.js +551 -0
- package/apps/web/upstream/src/libs/ChathistoryMiddleware.js +153 -0
- package/apps/web/upstream/src/libs/ConfigLoader.js +94 -0
- package/apps/web/upstream/src/libs/EmojiProvider.js +46 -0
- package/apps/web/upstream/src/libs/GlobalApi.js +346 -0
- package/apps/web/upstream/src/libs/IPC.js +51 -0
- package/apps/web/upstream/src/libs/InputHandler.js +921 -0
- package/apps/web/upstream/src/libs/IrcClient.js +1506 -0
- package/apps/web/upstream/src/libs/Logger.js +71 -0
- package/apps/web/upstream/src/libs/Message.js +169 -0
- package/apps/web/upstream/src/libs/MessageFormatter.js +364 -0
- package/apps/web/upstream/src/libs/MessageParser.js +249 -0
- package/apps/web/upstream/src/libs/Notifications.js +78 -0
- package/apps/web/upstream/src/libs/ServerConnection.js +270 -0
- package/apps/web/upstream/src/libs/ServerSession.js +102 -0
- package/apps/web/upstream/src/libs/SoundBleep.js +20 -0
- package/apps/web/upstream/src/libs/StatePersistence.js +90 -0
- package/apps/web/upstream/src/libs/ThemeManager.js +128 -0
- package/apps/web/upstream/src/libs/TypingMiddleware.js +105 -0
- package/apps/web/upstream/src/libs/WindowTitle.js +63 -0
- package/apps/web/upstream/src/libs/batchedAdd.js +74 -0
- package/apps/web/upstream/src/libs/bufferTools.js +177 -0
- package/apps/web/upstream/src/libs/polyfill/Element.closest.js +18 -0
- package/apps/web/upstream/src/libs/renderers/Html.js +113 -0
- package/apps/web/upstream/src/libs/settingTools.js +31 -0
- package/apps/web/upstream/src/libs/state/BufferState.js +703 -0
- package/apps/web/upstream/src/libs/state/NetworkState.js +158 -0
- package/apps/web/upstream/src/libs/state/UserState.js +89 -0
- package/apps/web/upstream/src/libs/state/common.js +26 -0
- package/apps/web/upstream/src/libs/state.js +961 -0
- package/apps/web/upstream/src/libs/storage/Local.js +51 -0
- package/apps/web/upstream/src/main.js +566 -0
- package/apps/web/upstream/src/res/autocompleteCommands.js +31 -0
- package/apps/web/upstream/src/res/configTemplates.js +373 -0
- package/apps/web/upstream/src/res/globalStyle.css +277 -0
- package/apps/web/upstream/src/res/kiwiLoadingLogo.png +0 -0
- package/apps/web/upstream/src/res/locales/app.af-ZA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ar-SA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.bg-BG.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.bs-BA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ca-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.cs-CZ.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.da-DK.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.de-DE.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.dev.po +1246 -0
- package/apps/web/upstream/src/res/locales/app.el-GR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.en-US.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-419.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-AR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-EM.po +536 -0
- package/apps/web/upstream/src/res/locales/app.es-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.es-US.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.eu-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.fi-FI.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.fr-FR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.gl-ES.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.he-IL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.hi-IN.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.hu-HU.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.id-ID.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.it-IT.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ja-JP.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ko-KR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.nl-NL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.no-NO.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.pl-PL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.pt-BR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.pt-PT.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ro-RO.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.ru-RU.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sl-SI.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sq-AL.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sr-SP.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.sv-SE.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.tr-TR.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.uk-UA.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.vi-VN.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.zh-CN.po +1145 -0
- package/apps/web/upstream/src/res/locales/app.zh-TW.po +1145 -0
- package/apps/web/upstream/src/res/localesList.json +27 -0
- package/apps/web/upstream/src/res/logo.png +0 -0
- package/apps/web/upstream/src/thirdparty/about.html +43 -0
- package/apps/web/upstream/src/thirdparty/index.js +5 -0
- package/apps/web/upstream/src/thirdparty/kiwiirccom.vue +185 -0
- package/apps/web/upstream/static/config.json +28 -0
- package/apps/web/upstream/static/emoticons/smile.png +0 -0
- package/apps/web/upstream/static/favicon.png +0 -0
- package/apps/web/upstream/static/highlight.mp3 +0 -0
- package/apps/web/upstream/static/highlight.ogg +0 -0
- package/apps/web/upstream/static/locales/.gitignore +2 -0
- package/apps/web/upstream/static/plugins/customise.html.example +10 -0
- package/apps/web/upstream/static/themes/coffee/theme.css +145 -0
- package/apps/web/upstream/static/themes/common/base.css +1064 -0
- package/apps/web/upstream/static/themes/dark/theme.css +196 -0
- package/apps/web/upstream/static/themes/default/theme.css +92 -0
- package/apps/web/upstream/static/themes/elite/theme.css +248 -0
- package/apps/web/upstream/static/themes/grayfox/theme.css +143 -0
- package/apps/web/upstream/static/themes/nightswatch/theme.css +367 -0
- package/apps/web/upstream/static/themes/osprey/theme.css +115 -0
- package/apps/web/upstream/static/themes/radioactive/theme.css +1211 -0
- package/apps/web/upstream/static/themes/sky/theme.css +107 -0
- package/apps/web/upstream/tests/unit/BatchAdd.spec.js +174 -0
- package/apps/web/upstream/tests/unit/MessageParser.spec.js +125 -0
- package/apps/web/upstream/tests/unit/Misc.spec.js +24 -0
- package/apps/web/upstream/tests/unit/NetworkState.spec.js +58 -0
- package/apps/web/upstream/tests/unit/StartupError.spec.js +20 -0
- package/apps/web/upstream/vue.config.js +150 -0
- package/apps/web/upstream/yarn.lock +9767 -0
- package/apps/web/vitest.config.ts +13 -0
- package/biome.json +1 -0
- package/docs/AWS-Deployment.md +21 -8
- package/docs/Cloudflare-Deployment-Guide.md +22 -6
- package/docs/Home.md +1 -0
- package/docs/PlanExtensions.md +113 -3
- package/docs/Release-Process.md +23 -13
- package/docs/Services.md +546 -0
- package/docs/WebClientGuide.md +536 -0
- package/package.json +2 -2
- package/packages/aws-adapter/package.json +1 -1
- package/packages/aws-adapter/src/aws-runtime.ts +5 -0
- package/packages/aws-adapter/src/cdk-table-defs.ts +6 -0
- package/packages/aws-adapter/src/config-loader.ts +11 -0
- package/packages/aws-adapter/src/connection-counter.ts +89 -0
- package/packages/aws-adapter/src/dynamo-services-store.ts +649 -0
- package/packages/aws-adapter/src/handlers/connect.ts +55 -51
- package/packages/aws-adapter/src/handlers/default.ts +36 -4
- package/packages/aws-adapter/src/handlers/index.ts +15 -0
- package/packages/aws-adapter/src/handlers/nlb-stream.ts +15 -0
- package/packages/aws-adapter/src/handlers/sweeper.ts +5 -1
- package/packages/aws-adapter/src/index.ts +4 -0
- package/packages/aws-adapter/src/stats.ts +6 -1
- package/packages/aws-adapter/src/tables.ts +34 -4
- package/packages/aws-adapter/tests/aws-harness.ts +3 -0
- package/packages/aws-adapter/tests/config-loader.test.ts +8 -0
- package/packages/aws-adapter/tests/connect.test.ts +158 -32
- package/packages/aws-adapter/tests/connection-counter.test.ts +127 -0
- package/packages/aws-adapter/tests/dynamo-services-store-dynamo.test.ts +183 -0
- package/packages/aws-adapter/tests/dynamo-services-store-unit.test.ts +568 -0
- package/packages/aws-adapter/tests/handlers.test.ts +105 -3
- package/packages/aws-adapter/tests/tables.test.ts +6 -1
- package/packages/cf-adapter/package.json +1 -1
- package/packages/cf-adapter/src/config-loader.ts +11 -0
- package/packages/cf-adapter/src/connection-do.ts +112 -2
- package/packages/cf-adapter/src/d1-services-store.ts +703 -0
- package/packages/cf-adapter/src/env.ts +8 -0
- package/packages/cf-adapter/src/index.ts +5 -0
- package/packages/cf-adapter/tests/cf-harness.ts +12 -1
- package/packages/cf-adapter/tests/config-loader.test.ts +19 -0
- package/packages/cf-adapter/tests/connection-do-nickserv-d1.test.ts +128 -0
- package/packages/cf-adapter/tests/connection-do.test.ts +150 -2
- package/packages/cf-adapter/tests/d1-services-store.test.ts +582 -0
- package/packages/cf-adapter/tests/serialize.test.ts +1 -0
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/irc-core/package.json +1 -1
- package/packages/irc-core/reports/mutation/mutation.html +342 -0
- package/packages/irc-core/scripts/generate-build-info.mjs +26 -5
- package/packages/irc-core/src/commands/account-auth.ts +172 -0
- package/packages/irc-core/src/commands/chanserv.ts +882 -0
- package/packages/irc-core/src/commands/hostserv.ts +487 -0
- package/packages/irc-core/src/commands/index.ts +12 -0
- package/packages/irc-core/src/commands/join.ts +164 -8
- package/packages/irc-core/src/commands/markread.ts +202 -0
- package/packages/irc-core/src/commands/memoserv.ts +319 -0
- package/packages/irc-core/src/commands/mode.ts +96 -4
- package/packages/irc-core/src/commands/nickserv.ts +390 -0
- package/packages/irc-core/src/commands/oper.ts +18 -1
- package/packages/irc-core/src/commands/operserv.ts +346 -0
- package/packages/irc-core/src/commands/pre-away.ts +3 -1
- package/packages/irc-core/src/commands/privmsg.ts +42 -0
- package/packages/irc-core/src/commands/read-marker.ts +8 -8
- package/packages/irc-core/src/commands/registration.ts +61 -6
- package/packages/irc-core/src/commands/sasl.ts +18 -49
- package/packages/irc-core/src/commands/tagmsg.ts +41 -6
- package/packages/irc-core/src/commands/topic.ts +37 -0
- package/packages/irc-core/src/config.ts +36 -5
- package/packages/irc-core/src/effects.ts +56 -1
- package/packages/irc-core/src/ports.ts +1653 -84
- package/packages/irc-core/src/protocol/numerics.ts +8 -0
- package/packages/irc-core/src/state/channel.ts +21 -1
- package/packages/irc-core/src/state/connection.ts +25 -1
- package/packages/irc-core/src/types.ts +48 -12
- package/packages/irc-core/tests/batch.test.ts +15 -0
- package/packages/irc-core/tests/commands/chanserv.test.ts +1668 -0
- package/packages/irc-core/tests/commands/chathistory.test.ts +6 -0
- package/packages/irc-core/tests/commands/hostserv.test.ts +935 -0
- package/packages/irc-core/tests/commands/join.test.ts +393 -1
- package/packages/irc-core/tests/commands/markread.test.ts +361 -0
- package/packages/irc-core/tests/commands/memoserv.test.ts +654 -0
- package/packages/irc-core/tests/commands/mode.test.ts +381 -2
- package/packages/irc-core/tests/commands/nickserv.test.ts +807 -0
- package/packages/irc-core/tests/commands/oper.test.ts +13 -0
- package/packages/irc-core/tests/commands/operserv.test.ts +656 -0
- package/packages/irc-core/tests/commands/privmsg.test.ts +147 -0
- package/packages/irc-core/tests/commands/read-marker.test.ts +28 -28
- package/packages/irc-core/tests/commands/registration.test.ts +788 -14
- package/packages/irc-core/tests/commands/sasl.test.ts +185 -12
- package/packages/irc-core/tests/commands/server-info.test.ts +9 -5
- package/packages/irc-core/tests/commands/tagmsg.test.ts +73 -33
- package/packages/irc-core/tests/commands/topic.test.ts +94 -2
- package/packages/irc-core/tests/commands/unified-account.test.ts +416 -0
- package/packages/irc-core/tests/config.test.ts +49 -5
- package/packages/irc-core/tests/effects.test.ts +19 -0
- package/packages/irc-core/tests/message-store.test.ts +63 -0
- package/packages/irc-core/tests/numerics.test.ts +13 -0
- package/packages/irc-core/tests/parser.test.ts +109 -0
- package/packages/irc-core/tests/persistent-services-store.test.ts +582 -0
- package/packages/irc-core/tests/services-store.test.ts +1289 -0
- package/packages/irc-core/tests/state/channel.test.ts +3 -0
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +71 -16
- package/packages/irc-server/src/dispatch.ts +94 -7
- package/packages/irc-server/src/routing.ts +19 -0
- package/packages/irc-server/tests/actor.test.ts +623 -12
- package/packages/irc-server/tests/dispatch.test.ts +270 -2
- package/packages/irc-server/tests/routing.test.ts +6 -0
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/in-memory-harness.ts +29 -3
- package/packages/irc-test-support/src/index.ts +1 -0
- package/packages/irc-test-support/src/scenarios.ts +24 -2
- package/packages/irc-test-support/tests/in-memory-harness.test.ts +32 -0
- package/tools/ci-hardening/package.json +1 -1
- package/tools/load-test/package.json +1 -1
- package/tools/tcp-ws-forwarder/package.json +1 -1
- package/tools/tcp-ws-forwarder/tests/forwarder.test.ts +2 -2
- package/packages/irc-core/tests/read-marker-store.test.ts +0 -108
package/.github/workflows/ci.yml
CHANGED
|
@@ -29,6 +29,7 @@ jobs:
|
|
|
29
29
|
verify:
|
|
30
30
|
name: typecheck / lint / test / coverage
|
|
31
31
|
runs-on: ubuntu-latest
|
|
32
|
+
timeout-minutes: 30
|
|
32
33
|
steps:
|
|
33
34
|
- name: Checkout
|
|
34
35
|
uses: actions/checkout@v4
|
|
@@ -40,7 +41,6 @@ jobs:
|
|
|
40
41
|
uses: actions/setup-node@v4
|
|
41
42
|
with:
|
|
42
43
|
node-version: 24
|
|
43
|
-
cache: pnpm
|
|
44
44
|
|
|
45
45
|
# DynamoDB Local capability for `aws-adapter` coverage.
|
|
46
46
|
#
|
|
@@ -63,7 +63,7 @@ jobs:
|
|
|
63
63
|
# (otherwise it blocks for ~60s waiting for Ryuk before falling
|
|
64
64
|
# back, which is flaky and wastes CI minutes).
|
|
65
65
|
- name: Setup Java (JRE for DynamoDB Local ZIP fallback)
|
|
66
|
-
uses: actions/setup-java@
|
|
66
|
+
uses: actions/setup-java@v5
|
|
67
67
|
with:
|
|
68
68
|
distribution: temurin
|
|
69
69
|
java-version: '11'
|
|
@@ -134,6 +134,7 @@ jobs:
|
|
|
134
134
|
runs-on: ubuntu-latest
|
|
135
135
|
# Stryker is the slowest single task; it is independent of the
|
|
136
136
|
# typecheck/lint/coverage chain and runs in parallel.
|
|
137
|
+
timeout-minutes: 45
|
|
137
138
|
steps:
|
|
138
139
|
- name: Checkout
|
|
139
140
|
uses: actions/checkout@v4
|
|
@@ -145,13 +146,17 @@ jobs:
|
|
|
145
146
|
uses: actions/setup-node@v4
|
|
146
147
|
with:
|
|
147
148
|
node-version: 24
|
|
148
|
-
cache: pnpm
|
|
149
149
|
|
|
150
150
|
- name: Install dependencies
|
|
151
151
|
run: pnpm install --frozen-lockfile
|
|
152
152
|
|
|
153
153
|
- name: Build workspace (irc-core mutation targets)
|
|
154
|
-
|
|
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...
|
|
155
160
|
|
|
156
161
|
- name: Stryker spot-check on irc-core/protocol
|
|
157
162
|
# Mutation score threshold >=80% is enforced by the
|
package/.gitmodules
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -7,25 +7,483 @@ and this project adheres to [Semantic Versioning](https://semver.org/) with
|
|
|
7
7
|
the 0.x convention (any release may change anything).
|
|
8
8
|
|
|
9
9
|
For the release process itself — versioning policy, pre-release checklist,
|
|
10
|
-
cutting a tag, rolling back — see [`docs/
|
|
10
|
+
cutting a tag, rolling back — see [`docs/Release-Process.md`](docs/Release-Process.md).
|
|
11
11
|
Cross-reference `progress.md` / `tickets.md` for per-ticket detail.
|
|
12
12
|
|
|
13
|
+
## [0.8.0] - 2026-08-08
|
|
14
|
+
|
|
15
|
+
The main change in this release is **integrated IRC services**: NickServ,
|
|
16
|
+
ChanServ, HostServ, OperServ, and MemoServ ship as part of the daemon
|
|
17
|
+
itself — there is **no separate services process and no S2S link**. The
|
|
18
|
+
service nicks are reserved and routed to dedicated pure reducers backed
|
|
19
|
+
by a new `ServicesStore` port, with **persistent D1 (Cloudflare) and
|
|
20
|
+
DynamoDB (AWS) backends** so registrations survive redeploys. Alongside
|
|
21
|
+
it: the SASL `AccountStore` and NickServ accounts are **unified** under
|
|
22
|
+
one scrypt-hashed credential store, `PASS <nick>:<password>` identifies
|
|
23
|
+
at registration, the `draft/read-marker` `MARKREAD` verb lands, and a
|
|
24
|
+
round of **AWS adapter hardening** (O(1) admission counter, strongly-
|
|
25
|
+
consistent `$default` reads, WebSocket subprotocol echo fix).
|
|
26
|
+
|
|
27
|
+
### Added — Integrated IRC services (`@serverless-ircd/irc-core`, `@serverless-ircd/irc-server`)
|
|
28
|
+
|
|
29
|
+
The ten services tickets add the full
|
|
30
|
+
services surface. Service names are reserved nicks recognized at
|
|
31
|
+
`PRIVMSG` time and routed to dedicated pure reducers when a
|
|
32
|
+
`ServicesStore` is bound; when no store is bound every services command
|
|
33
|
+
replies `501` and the rest of the daemon is unaffected. See
|
|
34
|
+
`docs/Services.md` for the operator reference.
|
|
35
|
+
|
|
36
|
+
- **`ServicesStore` port + in-memory reference impl**: a synchronous,
|
|
37
|
+
injectable store holding NickServ / ChanServ / HostServ / MemoServ /
|
|
38
|
+
OperServ / read-marker state. Reducers stay pure — all side effects
|
|
39
|
+
flow through the store port. The `ReadMarkerStore` from v0.6.0 is
|
|
40
|
+
**folded into** `ServicesStore` so read-markers, `draft/pre-away`,
|
|
41
|
+
and account state share one persistence path.
|
|
42
|
+
- **NickServ** (`REGISTER` / `IDENTIFY` / `DROP` / `INFO` / `SET`):
|
|
43
|
+
registers the current nick, identifies to an account (setting the
|
|
44
|
+
read-only user mode **`+r`**, fanning `ACCOUNT` to `account-notify`
|
|
45
|
+
peers, then delivering unread MemoServ memos), and supports
|
|
46
|
+
`SET ENFORCE` — a registered nick held by a non-identified
|
|
47
|
+
connection is reclaimed on the `NICK` path (enforcement policy
|
|
48
|
+
mirrors Atheme/Anope). `IDENTIFY` falls back to SASL account
|
|
49
|
+
credentials when no NickServ row exists.
|
|
50
|
+
- **ChanServ** (`REGISTER` / `DROP` / `SET` / `INFO` / `ACCESS` /
|
|
51
|
+
`LEVELS`): channel registration with a founder, the services-only
|
|
52
|
+
channel modes **`+r`** (registered), **`+R`** (block unidentified
|
|
53
|
+
join/message), and **`+M`** (moderated-identified), `ACCESS`/`LEVELS`
|
|
54
|
+
(SOP/AOP/VOP/HOP access lists), and auto-op on `JOIN` per access
|
|
55
|
+
level. The three services modes are settable only via ChanServ, not
|
|
56
|
+
via `MODE` (`472 ERR_UNKNOWNMODE` / `501`).
|
|
57
|
+
- **HostServ** (`ON` / `OFF` / `REQUEST` + oper `SET` / `APPROVE` /
|
|
58
|
+
`REJECT` / `LIST`): per-account vhost with `CHGHOST` fanout to shared
|
|
59
|
+
channels. `REQUEST` enters an **oper approval queue**; opers `APPROVE`
|
|
60
|
+
to activate or `REJECT` to drop it.
|
|
61
|
+
- **OperServ** (`AKILL` / `JUPE` / `UNJUPE` / `RAW`): oper-gated
|
|
62
|
+
network management. `AKILL` records an `*@host` mask, `JUPE` reserves
|
|
63
|
+
a juped nick (rejected at `NICK` with `432 ERR_ERRONEUSNICKNAME`),
|
|
64
|
+
`RAW` is the escape hatch. Wired end-to-end: `PRIVMSG OperServ :…`
|
|
65
|
+
routes to the OperServ reducer.
|
|
66
|
+
- **MemoServ** (`SEND` / `LIST` / `READ` / `DEL`): per-account memos
|
|
67
|
+
with queue delivery — unread memos are replayed at identify (NickServ
|
|
68
|
+
`IDENTIFY` / SASL login / PASS-based login), the same path that
|
|
69
|
+
replays `draft/read-marker` and `draft/pre-away`.
|
|
70
|
+
|
|
71
|
+
### Added — Persistent services backends (`@serverless-ircd/cf-adapter`, `@serverless-ircd/aws-adapter`)
|
|
72
|
+
|
|
73
|
+
Both adapters ship write-behind persistence for the `ServicesStore` so
|
|
74
|
+
registrations, channel access lists, vhosts, memos, and read-markers
|
|
75
|
+
survive a redeploy/reconnect.
|
|
76
|
+
|
|
77
|
+
- **Cloudflare (D1)**: `D1ServicesStore` over the shared `ACCOUNTS_DB`
|
|
78
|
+
binding (the same D1 database used for SASL accounts since v0.4.0).
|
|
79
|
+
The ten services tables **auto-provision on cold start** (`CREATE
|
|
80
|
+
TABLE IF NOT EXISTS` inside `loadD1ServicesStore`), so a fresh deploy
|
|
81
|
+
self-provisions and an existing deploy re-runs the batch as a cheap
|
|
82
|
+
no-op. `ConnectionDO` load/flush hooks wrap each frame and teardown.
|
|
83
|
+
- **AWS (DynamoDB)**: `DynamoServicesStore` plus a new `Services`
|
|
84
|
+
table definition and handler wiring so NLB-stream / `$default`
|
|
85
|
+
invocations hydrate + flush services alongside the existing account
|
|
86
|
+
store. Enabled when the `SERVICES_TABLE` env var / table name is
|
|
87
|
+
supplied.
|
|
88
|
+
- **`PersistentServicesStore` base** (`irc-core`): the shared
|
|
89
|
+
write-behind base — mutates the inherited cache synchronously and
|
|
90
|
+
enqueues matching writes for `flush()`, so reducers stay free of IO.
|
|
91
|
+
|
|
92
|
+
### Added — Unified accounts & registration login (`@serverless-ircd/irc-core`)
|
|
93
|
+
|
|
94
|
+
- **Unified SASL + NickServ account store**: the SASL `AccountStore`
|
|
95
|
+
and NickServ `ServicesStore` now share one **scrypt-hashed**
|
|
96
|
+
credential shape. A NickServ registration *is* a SASL account —
|
|
97
|
+
SASL `PLAIN`'s services fallback verifies against a row created by
|
|
98
|
+
NickServ `REGISTER` without a second registration, and vice versa.
|
|
99
|
+
The plaintext password on `ServicesNickRow` is replaced with a
|
|
100
|
+
`HashedAccountCredential` (scrypt + random salt). `ingestAccountCredential`
|
|
101
|
+
adopts a pre-hashed SASL credential as a registered nick (idempotent;
|
|
102
|
+
existing records win).
|
|
103
|
+
- **PASS-based account login**: a client may identify to its NickServ
|
|
104
|
+
account at registration by sending `PASS <nick>:<password>` (the same
|
|
105
|
+
literal the `SASL_ACCOUNTS` seed tooling uses). On success the
|
|
106
|
+
connection is logged in — emitting `900`/`903`, stamping `+r`, and
|
|
107
|
+
running the same read-marker / away / memo replay as SASL. A bare
|
|
108
|
+
`PASS <value>` (no colon) is always treated as a server-password
|
|
109
|
+
candidate. The shared login reduction (`applyAccountSuccess`) is
|
|
110
|
+
extracted and reused from SASL.
|
|
111
|
+
- **Server-password gate with SASL short-circuit**: an optional gate
|
|
112
|
+
enforced in the registration reducer at `001` — when
|
|
113
|
+
`ServerConfig.serverPassword` is set, every connection must supply a
|
|
114
|
+
matching `PASS` (or authenticate via SASL / PASS-based account login)
|
|
115
|
+
before `RPL_WELCOME`, else `464 ERR_PASSWDMISMATCH` + disconnect.
|
|
116
|
+
Wired through all three adapters (`SERVER_PASSWORD` Worker secret /
|
|
117
|
+
Lambda env var / `--server-password` CLI flag).
|
|
118
|
+
|
|
119
|
+
### Added — `draft/read-marker` `MARKREAD` verb (`@serverless-ircd/irc-core`)
|
|
120
|
+
|
|
121
|
+
- The timestamp-based `MARKREAD` command from the read-marker draft
|
|
122
|
+
lands alongside the existing `+draft/read-marker=<msgid>` tag path.
|
|
123
|
+
`MARKREAD <target>` (GET) replies with the stored marker's timestamp;
|
|
124
|
+
`MARKREAD <target> <timestamp>` (SET) advances the marker, with
|
|
125
|
+
timestamp↔msgid translation against the message store. Failure modes
|
|
126
|
+
use `standard-replies` `FAIL MARKREAD …`. The mark fanout is
|
|
127
|
+
**scoped to the originator's account** (spec compliance), not
|
|
128
|
+
broadcast to every cap-enabled peer.
|
|
129
|
+
|
|
130
|
+
### Added — Cloudflare adapter (`@serverless-ircd/cf-adapter`)
|
|
131
|
+
|
|
132
|
+
- **Client source IP capture at WebSocket upgrade**: reads
|
|
133
|
+
`CF-Connecting-IP` (with `X-Real-IP` fallback) from the upgrade
|
|
134
|
+
request and seeds `ConnectionState.host`, so WHOIS / hostmasks carry
|
|
135
|
+
a real `@host` segment instead of an `undefined` fallback.
|
|
136
|
+
- **Ghost-member pruning via alarm**: when a WebSocket `close` event is
|
|
137
|
+
lost (client drop, edge hiccup), the DO's alarm prunes the ghost
|
|
138
|
+
member from its channels on the next sweep, preventing stale roster
|
|
139
|
+
entries.
|
|
140
|
+
|
|
141
|
+
### Changed — AWS adapter (`@serverless-ircd/aws-adapter`, `@serverless-ircd/aws-stack`)
|
|
142
|
+
|
|
143
|
+
- **O(1) admission counter**: `$connect`'s full-table `Scan COUNT` is
|
|
144
|
+
replaced with an atomic increment/decrement on a single meta row,
|
|
145
|
+
dropping admission cost from O(connections) to O(1). Reservations
|
|
146
|
+
roll back on rejection or a failed `Put`; the sweeper and stats
|
|
147
|
+
exclude the meta row.
|
|
148
|
+
- **Strongly-consistent `$default` reads + observability**: `$default`
|
|
149
|
+
now reads the `Connections` row strongly consistent (closes a
|
|
150
|
+
read-after-write `410` window), warns on a missing row, logs
|
|
151
|
+
`postToConnection` failures (previously silently swallowed), and
|
|
152
|
+
emits a summary log per invocation.
|
|
153
|
+
- **`ServerName` / `NetworkName` as CFN parameters**: server identity
|
|
154
|
+
is now a CloudFormation parameter (was env-var-only), and the
|
|
155
|
+
sweeper Lambda's `loadServerConfigFromLambdaEnv` is wired with the
|
|
156
|
+
server-identity env vars it requires. `SweeperScheduleExpression`,
|
|
157
|
+
`PingCheckerScheduleExpression`, and `ApiGatewayLoggingLevel` are
|
|
158
|
+
also exposed as parameters.
|
|
159
|
+
- **WebSocket subprotocol echo fixed**: API Gateway WebSocket
|
|
160
|
+
`IntegrationResponse` uses `route.response.header.*` (not
|
|
161
|
+
`method.response.header.*`, which is REST API only), so the
|
|
162
|
+
`Sec-WebSocket-Protocol` header was not being echoed back to clients.
|
|
163
|
+
AWS now correctly negotiates the IRCv3 subprotocol. Subprotocol
|
|
164
|
+
advertisement is forced text-only (`text.ircv3.net`) to match the
|
|
165
|
+
Lambda response-stream path.
|
|
166
|
+
- **APIGW logging role corrected** and Biome lint/format applied.
|
|
167
|
+
|
|
168
|
+
### Changed
|
|
169
|
+
|
|
170
|
+
- **`MODE +o` echo on oper-up**: the connection that grants oper now
|
|
171
|
+
receives a `MODE` echo, matching the cross-connection oper-up path.
|
|
172
|
+
- **NICK change broadcast fix**: nick changes now broadcast to every
|
|
173
|
+
shared-channel peer (a code path that dropped peers under certain
|
|
174
|
+
roster shapes is corrected).
|
|
175
|
+
- **Server version from `package.json`**: the default `serverVersion`
|
|
176
|
+
advertised in `002`/`003` is derived from the workspace
|
|
177
|
+
`package.json` at build time (was a hardcoded placeholder when the
|
|
178
|
+
config omitted it).
|
|
179
|
+
- **Web client SPA path `/app/` → `/webclient/`**: the vendored Kiwi
|
|
180
|
+
IRC SPA is now served at `/webclient/` (the `[assets]` publicPath
|
|
181
|
+
and config are repointed). Operators with custom Kiwi configs or
|
|
182
|
+
bookmarks must update to `/webclient/`.
|
|
183
|
+
- **Source / issue links migrated to Gitea**; landing-page copy
|
|
184
|
+
tightened and the upstream-install guard hardened.
|
|
185
|
+
|
|
186
|
+
### Changed — CI / tooling
|
|
187
|
+
|
|
188
|
+
- `actions/setup-java` bumped to v5; pnpm pinned to 11.20.0. CI jobs
|
|
189
|
+
are bounded by explicit timeouts and the unreachable pnpm cache step
|
|
190
|
+
is dropped.
|
|
191
|
+
|
|
192
|
+
### ⚠️ Migration required
|
|
193
|
+
|
|
194
|
+
- **Cloudflare: services are enabled when `ACCOUNTS_DB` is bound.** The
|
|
195
|
+
D1 binding is the same one introduced in v0.4.0 for SASL accounts;
|
|
196
|
+
the ten services tables **self-provision on cold start** (no manual
|
|
197
|
+
`wrangler d1 execute` needed). If `ACCOUNTS_DB` is already bound,
|
|
198
|
+
services are now live after deploy — no action. If it is unset,
|
|
199
|
+
services commands reply `501` and the daemon is otherwise unchanged.
|
|
200
|
+
- **AWS: new `Services` DynamoDB table + `SERVICES_TABLE`.** To enable
|
|
201
|
+
services, supply a services table name (`SERVICES_TABLE` env var /
|
|
202
|
+
CDK `servicesTableName` prop); the table is created by the CDK stack.
|
|
203
|
+
Without it, services are unbound.
|
|
204
|
+
- **NickServ / SASL credentials are now scrypt-hashed at rest.** A
|
|
205
|
+
NickServ `REGISTER` creates the same hashed credential shape SASL
|
|
206
|
+
uses; the `password` column on the NickServ row is replaced by
|
|
207
|
+
`algorithm` / `salt` / `hash`. Existing seeded SASL accounts are
|
|
208
|
+
adopted as registered nicks idempotently. Operators re-seeding via
|
|
209
|
+
`tools/seed-*-accounts.ts` see no change (the tools already hashed).
|
|
210
|
+
- **Web client SPA moved to `/webclient/`.** Update any bookmarks,
|
|
211
|
+
custom Kiwi configs, or `WEB_ORIGINS` entries pointing at `/app/`.
|
|
212
|
+
- **No persisted-DO-state schema change.** `PERSISTED_STATE_VERSION`
|
|
213
|
+
stays `1`. The new read-only user mode `r` (registered) and the
|
|
214
|
+
services channel modes `r`/`R`/`M` are runtime state, not persisted
|
|
215
|
+
DO fields; the `ReadMarkerStore` fold moves read-markers into the
|
|
216
|
+
services backend (D1/DynamoDB), out of DO storage. **Code rollback
|
|
217
|
+
across this release is safe.**
|
|
218
|
+
- No toolchain change. Node ≥ 24 / pnpm 11 (from v0.4.0) still apply.
|
|
219
|
+
|
|
220
|
+
### Known limitations
|
|
221
|
+
|
|
222
|
+
- **Services are not binary-compatible with Atheme or Anope.** There is
|
|
223
|
+
no S2S protocol, so an existing Atheme/Anope services process cannot
|
|
224
|
+
connect, and existing services databases cannot be imported.
|
|
225
|
+
Operators migrating from a services-aware network must re-register
|
|
226
|
+
nicks and channels. See `docs/Services.md` §12.
|
|
227
|
+
- **The web client remains partial.** The Playwright headless-browser
|
|
228
|
+
e2e against the deployed staging frontend is still pending; the SPA
|
|
229
|
+
is exercised via the WS smoke (`scripts/smoke.mjs`) and the `apps/web`
|
|
230
|
+
unit / build-smoke suite. The web client remains Cloudflare-only (the
|
|
231
|
+
`[assets]` binding is a Worker feature).
|
|
232
|
+
- Same transport limits as v0.7.0: the **CF TCP path requires
|
|
233
|
+
Cloudflare Spectrum (Enterprise tier)** plus a stateful Container
|
|
234
|
+
origin; the **AWS TCP path** uses NLB + Lambda streaming and is
|
|
235
|
+
subject to Lambda idle-timeout / stream-duration limits. The
|
|
236
|
+
WebSocket path remains the zero-extra-deps default.
|
|
237
|
+
- **mTLS (for SASL `EXTERNAL`) requires a custom domain + uploaded
|
|
238
|
+
CA/trust-store** on both platforms and is not enabled by default.
|
|
239
|
+
- **Client compatibility sweep** (WeeChat / HexChat / IRCCloud /
|
|
240
|
+
TheLounge / matrix-IRC bridge) remains pending.
|
|
241
|
+
- **Coverage hardening is partial.** `aws-adapter` and `aws-stack`
|
|
242
|
+
clear the 90% gate; the remaining packages (`cf-adapter`,
|
|
243
|
+
`local-cli`, `load-test`, `cf-tcp-container`, `tcp-ws-forwarder`,
|
|
244
|
+
`irc-test-support`, `web`) are **above the gate but below 100%** —
|
|
245
|
+
follow-ups drive each to 100%.
|
|
246
|
+
|
|
247
|
+
### Security
|
|
248
|
+
|
|
249
|
+
- **NickServ / SASL credentials are now uniformly scrypt-hashed.** The
|
|
250
|
+
unified account store means a NickServ-registered nick and a SASL
|
|
251
|
+
account share one hashed credential — never plaintext, in memory or
|
|
252
|
+
at rest. The server-password gate and CSWSH defense are unchanged
|
|
253
|
+
from v0.7.0; no other auth path changed.
|
|
254
|
+
|
|
255
|
+
---
|
|
256
|
+
|
|
257
|
+
## [0.7.0] - 2026-08-04
|
|
258
|
+
|
|
259
|
+
The main change in this release is the **web client**: a vendored **Kiwi IRC**
|
|
260
|
+
SPA served at `/app/` and a static project landing page served at `/`
|
|
261
|
+
directly by the Cloudflare Worker, with **Cross-Site WebSocket
|
|
262
|
+
Hijacking (CSWSH) defense** wired into the WS upgrade path. The browser
|
|
263
|
+
opens a native `wss://` directly to the IRC-over-WebSocket
|
|
264
|
+
endpoint — no proxy, gateway, or transport adaptation in the request path.
|
|
265
|
+
Alongside it: a round of **test hardening** (mutation-killing tests for
|
|
266
|
+
`irc-core`'s parser / batch / numerics, and CF-harness / scenario fixes
|
|
267
|
+
for slow transports).
|
|
268
|
+
|
|
269
|
+
### Added — Web client (`@serverless-ircd/web`, `@serverless-ircd/cf-worker`)
|
|
270
|
+
|
|
271
|
+
Six tickets add the project's browser surface. The Worker
|
|
272
|
+
becomes the single origin for the landing page, the SPA, **and** the
|
|
273
|
+
WebSocket. Same-origin by default — which is what makes the CSWSH
|
|
274
|
+
defense almost zero-config.
|
|
275
|
+
|
|
276
|
+
- **`apps/web` workspace package (`@serverless-ircd/web`)**: a new
|
|
277
|
+
pnpm workspace package owning the Kiwi build orchestration and config
|
|
278
|
+
schema. The package layout reserves `dist/app/` for the Kiwi SPA
|
|
279
|
+
(served at `/app/`) and `dist/index.html` for the landing page
|
|
280
|
+
(served at `/`). No `@serverless-ircd/*` runtime dependency —
|
|
281
|
+
Kiwi talks to the Worker over plain WebSocket.
|
|
282
|
+
- **Kiwi IRC vendored as a git submodule** (`apps/web/upstream/`,
|
|
283
|
+
tag-pinned): `scripts/build.mjs` patches the upstream `publicPath`
|
|
284
|
+
to `/app/`, runs `yarn build` inside the submodule, copies
|
|
285
|
+
`upstream/dist/` → `apps/web/dist/app/`, then layers our validated
|
|
286
|
+
`static/config.json` over the upstream default. The patch runs in
|
|
287
|
+
`finally` so the submodule stays clean; every hashed asset URL comes
|
|
288
|
+
out `/app/static/...` instead of root-relative. `.gitmodules` updated;
|
|
289
|
+
`biome.json` ignores `apps/web/upstream/**`. `git submodule update
|
|
290
|
+
--init` is now part of the contributor quickstart.
|
|
291
|
+
- **Per-env config matrix + Zod schema** (`apps/web/src/config-schema.ts`,
|
|
292
|
+
`apps/web/src/build-env.ts`): `static/config.json`,
|
|
293
|
+
`static/config.staging.json`, `static/config.prod.json` are validated
|
|
294
|
+
against a Zod schema **before** being baked into
|
|
295
|
+
`dist/app/static/config.json`. `pnpm --filter web build` accepts a
|
|
296
|
+
`--env <staging|prod>` flag (unit-tested in isolation from the slow
|
|
297
|
+
Kiwi build). A malformed config (missing `server`, out-of-range
|
|
298
|
+
`port`, non-`#` channel, `direct: false`, …) aborts the build with
|
|
299
|
+
an aggregated, field-listing error rather than shipping a broken SPA.
|
|
300
|
+
The Kiwi `startupOptions.server` uses the `{{hostname}}` template
|
|
301
|
+
(resolved by the browser to `window.location.hostname` at load time),
|
|
302
|
+
so **one build artifact targets staging, prod, and preview deploys
|
|
303
|
+
without a per-env hostname**.
|
|
304
|
+
- **Cloudflare Worker `[assets]` binding** (`apps/cf-worker/wrangler.toml`,
|
|
305
|
+
`apps/cf-worker/src/worker.ts`): the non-WS, non-`/health` branch of
|
|
306
|
+
the worker's `fetch` falls through to `env.ASSETS`, which serves
|
|
307
|
+
`apps/web/dist`. Per-env (`[env.staging.assets]`) blocks ship so
|
|
308
|
+
`wrangler deploy --env staging` deploys the Worker and the SPA in
|
|
309
|
+
one command. `run_worker_first = true` is set so WS upgrades aren't
|
|
310
|
+
intercepted by the asset platform serving `dist/index.html` at `/`
|
|
311
|
+
with a `200 OK` (the WS endpoint and the landing page share path
|
|
312
|
+
`/`; only the Worker can tell them apart).
|
|
313
|
+
- **Static landing page** (`apps/web/landing/index.html`): framework-
|
|
314
|
+
free HTML + inline CSS, no JavaScript. The project's entry page —
|
|
315
|
+
explains what ServerlessIRCd is, links to `/app/` (the IRC client),
|
|
316
|
+
the docs, and the source repo. The build copies it verbatim to
|
|
317
|
+
`dist/index.html`. Renders without JS; responsive; the build-smoke
|
|
318
|
+
suite asserts the `/app/` link, the docs link, and the viewport meta
|
|
319
|
+
tag so they cannot regress.
|
|
320
|
+
- **`/health` is an explicit route**: the previous catch-all plaintext
|
|
321
|
+
liveness string now lives at `GET /health` so `/` is free for the
|
|
322
|
+
landing page. WS upgrades still route to `ConnectionDO`.
|
|
323
|
+
|
|
324
|
+
### Added — CSWSH defense (`@serverless-ircd/cf-worker`)
|
|
325
|
+
|
|
326
|
+
WebSocket upgrades do **not** follow the same-origin policy. A
|
|
327
|
+
malicious page can `new WebSocket('wss://irc.example.com/')` from the
|
|
328
|
+
victim's browser and drive an IRC session with the victim's
|
|
329
|
+
credentials. The Worker now rejects upgrades whose `Origin` is not
|
|
330
|
+
allowed — see `apps/cf-worker/src/origin-allowlist.ts` and
|
|
331
|
+
`docs/WebClientGuide.md` §5.
|
|
332
|
+
|
|
333
|
+
- **Two enforcement modes, evaluated in order:**
|
|
334
|
+
1. **Explicit allowlist** — when the `WEB_ORIGINS` var is set, only
|
|
335
|
+
listed origins proceed. Use this for cross-origin deployments
|
|
336
|
+
(SPA on a different domain than the Worker, e.g. Cloudflare
|
|
337
|
+
Pages).
|
|
338
|
+
2. **Auto-derive (same-origin)** — when `WEB_ORIGINS` is unset/empty
|
|
339
|
+
(the default), the Worker compares the browser's `Origin` against
|
|
340
|
+
the request's own origin (`${protocol}//${host}`). If they match,
|
|
341
|
+
the page opening the WS is the same origin as the server → safe.
|
|
342
|
+
- **`WEB_ORIGINS` is optional.** Because mode 2 is the default and
|
|
343
|
+
requires zero per-env configuration, most deployments never need to
|
|
344
|
+
set it: the SPA and the Worker are on the same origin. It works for
|
|
345
|
+
`*.workers.dev`, custom domains, and preview URLs alike.
|
|
346
|
+
- **Non-browser clients pass through unchanged.** curl, WeeChat, the
|
|
347
|
+
`tcp-ws-forwarder`, and scripted test harnesses never send `Origin`;
|
|
348
|
+
their upgrades are allowed in both modes so existing tooling keeps
|
|
349
|
+
working.
|
|
350
|
+
- **No fail-open path.** A browser-sent `Origin` that is not allowed
|
|
351
|
+
→ `403 Forbidden`. The extreme edge case (no allowlist AND
|
|
352
|
+
`Host` undeterminable, which never fires on Cloudflare) is
|
|
353
|
+
fail-closed `403`. The pure policy is exhaustively unit-tested in
|
|
354
|
+
`apps/cf-worker/tests/origin-allowlist.test.ts`; the worker
|
|
355
|
+
integration is covered by `tests/ws-origin.test.ts`.
|
|
356
|
+
|
|
357
|
+
### Added — Documentation (`docs/WebClientGuide.md`)
|
|
358
|
+
|
|
359
|
+
End-to-end contributor/operator doc covering: how the SPA, landing
|
|
360
|
+
page, and WS endpoint fit on one origin; prerequisites (incl. `yarn`
|
|
361
|
+
for the Kiwi build only); the quickstart (`git submodule update --init`
|
|
362
|
+
→ `pnpm --filter web build:staging` → `pnpm deploy:cf:staging`); a
|
|
363
|
+
walkthrough of the build pipeline and the per-env config matrix; the
|
|
364
|
+
CSWSH rationale and the optional `WEB_ORIGINS` var; local dev
|
|
365
|
+
(`wrangler dev` serves everything on `http://localhost:8787`); an
|
|
366
|
+
optional Cloudflare Pages alternative for cross-origin prod deploys;
|
|
367
|
+
the optional `webircgateway` SockJS fallback (strictly opt-in, not
|
|
368
|
+
needed for normal operation); landing-page customisation; and a
|
|
369
|
+
troubleshooting section. The acceptance criterion is that a new
|
|
370
|
+
contributor can deploy a staging SPA following only this doc.
|
|
371
|
+
|
|
372
|
+
### Added — Test hardening (`@serverless-ircd/irc-core`, `@serverless-ircd/cf-adapter`, `@serverless-ircd/irc-test-support`)
|
|
373
|
+
|
|
374
|
+
- **Mutation-killing tests for `irc-core`**: the parser, batch, and
|
|
375
|
+
numerics suites were expanded to kill surviving Stryker mutants —
|
|
376
|
+
command-token anchors, strict source-prefix shape, malformed-input
|
|
377
|
+
diagnostic text, middle-param whitespace collapsing,
|
|
378
|
+
`unescapeTagValue` boundary conditions; empty-batch framing metadata
|
|
379
|
+
surviving elision; and `numericToName` round-trip parity against the
|
|
380
|
+
registry. A generated Stryker mutation report is committed under
|
|
381
|
+
`packages/irc-core/reports/mutation/`.
|
|
382
|
+
- **CF harness / scenario fixes for slow transports**: the CF test
|
|
383
|
+
harness now waits for the end of the registration burst (`376` /
|
|
384
|
+
`422`) instead of `001`, so late MOTD frames do not pollute
|
|
385
|
+
`received` after a test clears it. The NOTICE scenario now only
|
|
386
|
+
rejects NOTICE-relevant error numerics (rather than any numeric),
|
|
387
|
+
preventing delayed welcome / MOTD lines from false-positiving the
|
|
388
|
+
RFC no-error-replies check. No production code changed.
|
|
389
|
+
|
|
390
|
+
### Changed
|
|
391
|
+
|
|
392
|
+
- **Cloudflare Worker routing.** `/health` is the new liveness route;
|
|
393
|
+
the root `/` now serves the landing page from the `[assets]`
|
|
394
|
+
binding. WS upgrades still route to `ConnectionDO`. Operators who
|
|
395
|
+
scraped the root `/` for the plaintext health string must update
|
|
396
|
+
to `/health`.
|
|
397
|
+
|
|
398
|
+
### ⚠️ Migration required
|
|
399
|
+
|
|
400
|
+
- **New optional runtime var: `WEB_ORIGINS`.** Optional — same-origin
|
|
401
|
+
auto-derive is the default. Set it only for cross-origin SPA
|
|
402
|
+
deployments (SPA on Cloudflare Pages, Worker serving the WS only,
|
|
403
|
+
etc.):
|
|
404
|
+
```toml
|
|
405
|
+
# apps/cf-worker/wrangler.toml
|
|
406
|
+
[vars]
|
|
407
|
+
WEB_ORIGINS = "https://irc.example.com,https://web.example.com"
|
|
408
|
+
```
|
|
409
|
+
- **New build prerequisite for the SPA.** Building `apps/web` needs
|
|
410
|
+
the Kiwi submodule (`git submodule update --init apps/web/upstream`)
|
|
411
|
+
and `yarn` (provided by `corepack enable`). The CF deploy workflow
|
|
412
|
+
runs `pnpm build` (which builds the SPA + landing page) before
|
|
413
|
+
`wrangler deploy`. If you deploy manually and skip the SPA build,
|
|
414
|
+
the Worker still works but `/app/` and `/` return 404 from the asset
|
|
415
|
+
layer.
|
|
416
|
+
- **`/health` is the new liveness route.** Operators who scraped `/`
|
|
417
|
+
for the plaintext health string must update to `/health`.
|
|
418
|
+
- **New workspace package.** `apps/web` is a new private package
|
|
419
|
+
(`@serverless-ircd/web`); `pnpm install` picks it up automatically
|
|
420
|
+
from the existing `pnpm-workspace.yaml` glob.
|
|
421
|
+
- **No persisted-state schema change.** `PERSISTED_STATE_VERSION`
|
|
422
|
+
stays `1`. **Code rollback across this release is safe** — the
|
|
423
|
+
web-client work adds the `[assets]` binding and the `/health` route
|
|
424
|
+
but touches no Durable Object state.
|
|
425
|
+
- No toolchain change. Node ≥ 24 / pnpm 11 (from v0.4.0) still apply.
|
|
426
|
+
|
|
427
|
+
### Known limitations
|
|
428
|
+
|
|
429
|
+
- **The web client is partial.** The Playwright headless-browser e2e
|
|
430
|
+
against the deployed staging frontend remains pending
|
|
431
|
+
— the SPA is exercised via the existing WS smoke
|
|
432
|
+
(`scripts/smoke.mjs`) and the new `apps/web` unit / build-smoke
|
|
433
|
+
suite, but no browser-driven end-to-end test runs in CI yet.
|
|
434
|
+
- **The web client is Cloudflare-only.** The `[assets]` binding is a
|
|
435
|
+
Cloudflare Worker feature; AWS deployments continue to require an
|
|
436
|
+
external static host for the SPA (and must add the SPA's origin to
|
|
437
|
+
the Worker-style `WEB_ORIGINS` equivalent if/when an AWS-side
|
|
438
|
+
CSWSH check lands).
|
|
439
|
+
- Same transport limits as v0.6.0: the **CF TCP path requires
|
|
440
|
+
Cloudflare Spectrum (Enterprise tier)** plus a stateful Container
|
|
441
|
+
origin; the **AWS TCP path** uses NLB + Lambda streaming and is
|
|
442
|
+
subject to Lambda idle-timeout / stream-duration limits. The
|
|
443
|
+
WebSocket path remains the zero-extra-deps default.
|
|
444
|
+
- **mTLS (for SASL `EXTERNAL`) requires a custom domain + uploaded
|
|
445
|
+
CA/trust-store** on both platforms and is not enabled by default.
|
|
446
|
+
- **Client compatibility sweep** (WeeChat / HexChat / IRCCloud /
|
|
447
|
+
TheLounge / matrix-IRC bridge) remains pending.
|
|
448
|
+
- **Coverage hardening is partial.** `aws-adapter` and `aws-stack`
|
|
449
|
+
clear the 90% gate (v0.6.0); the remaining packages
|
|
450
|
+
(`cf-adapter`, `local-cli`, `load-test`, `cf-tcp-container`,
|
|
451
|
+
`tcp-ws-forwarder`, `irc-test-support`, and now `web`) are **above
|
|
452
|
+
the gate but below 100%** — follow-ups drive each to 100%.
|
|
453
|
+
- The IRC services work (NickServ / ChanServ / HostServ /
|
|
454
|
+
OperServ / MemoServ) is pending; `draft/read-marker` and
|
|
455
|
+
`draft/pre-away` ship with in-memory reference stores, so marker /
|
|
456
|
+
away persistence does not survive an adapter restart until a
|
|
457
|
+
services backend lands.
|
|
458
|
+
|
|
459
|
+
### Security
|
|
460
|
+
|
|
461
|
+
- **CSWSH defense added.** WebSocket upgrades from browsers now
|
|
462
|
+
require an allowed `Origin`. Same-origin auto-derive is the default
|
|
463
|
+
(zero config); explicit `WEB_ORIGINS` allowlist for cross-origin
|
|
464
|
+
setups. Non-browser clients (no `Origin`) pass through unchanged.
|
|
465
|
+
There is no fail-open path. See `docs/WebClientGuide.md` §5. No
|
|
466
|
+
other auth path changed (SASL `PLAIN` / `EXTERNAL`, oper gate,
|
|
467
|
+
admission limits, and cloaking are unchanged from v0.6.0).
|
|
468
|
+
|
|
469
|
+
---
|
|
470
|
+
|
|
13
471
|
## [0.6.0] - 2026-08-03
|
|
14
472
|
|
|
15
|
-
The
|
|
473
|
+
The main change in this release is the **IRCv3 extension sweep**: ten negotiated
|
|
16
474
|
caps and the user-mode-`S` TLS indicator land in one release, taking the
|
|
17
475
|
negotiated capability surface from "modern baseline" to "draft-leading-
|
|
18
476
|
edge". Alongside it: the **required-`serverName` config gate** (the
|
|
19
|
-
last open v0.5.0 follow-up), a new **`tools/load-test`** client
|
|
20
|
-
(
|
|
477
|
+
last open v0.5.0 follow-up), a new **`tools/load-test`** client pool
|
|
478
|
+
(the 10k-connection harness), and the **coverage / CI unblocks**
|
|
21
479
|
that lift `aws-adapter` over the 90% gate and switch `cf-adapter` /
|
|
22
480
|
`cf-worker` to istanbul (clearing the v0.5.0 workerd-incompatibility
|
|
23
481
|
caveat). Deploy workflows are now **manual-only** (`workflow_dispatch`).
|
|
24
482
|
|
|
25
483
|
### Added — IRCv3 extensions (`@serverless-ircd/irc-core`, `@serverless-ircd/irc-server`)
|
|
26
484
|
|
|
27
|
-
The ten
|
|
28
|
-
surface, and the two non-cap
|
|
485
|
+
The ten cap tickets add the remaining negotiated-cap
|
|
486
|
+
surface, and the two non-cap tickets (ISUPPORT tokens and
|
|
29
487
|
user mode `S`) round out the wire-visible advertisement. Each cap
|
|
30
488
|
ships a pure reducer (where applicable), an actor route case,
|
|
31
489
|
advertisement in `SUPPORTED_CAPABILITIES` (or conditional
|
|
@@ -135,10 +593,10 @@ with a readable, field-listing error.
|
|
|
135
593
|
|
|
136
594
|
### Added — Load-test harness (`@serverless-ircd/load-test`)
|
|
137
595
|
|
|
138
|
-
PLAN §7
|
|
139
|
-
client
|
|
596
|
+
The PLAN §7 10k-connection harness. A synthetic WebSocket IRC
|
|
597
|
+
client pool: opens N connections, registers (NICK/USER), joins a
|
|
140
598
|
channel, and optionally chats. Captures **p50 / p95 / p99 latency per
|
|
141
|
-
|
|
599
|
+
stage** (connect / register / join / message) plus the **drop rate**,
|
|
142
600
|
and prints a markdown report to stdout (or `--report <path>`).
|
|
143
601
|
|
|
144
602
|
```bash
|
|
@@ -253,11 +711,11 @@ resolves it.
|
|
|
253
711
|
`irc-test-support`) are **above the gate but below 100%** —
|
|
254
712
|
follow-ups drive each to 100%.
|
|
255
713
|
- The IRC services (NickServ / ChanServ / HostServ / OperServ /
|
|
256
|
-
MemoServ)
|
|
714
|
+
MemoServ) work is pending; `draft/read-marker` and
|
|
257
715
|
`draft/pre-away` ship with in-memory reference stores, so marker /
|
|
258
716
|
away persistence does not survive an adapter restart until a
|
|
259
717
|
services backend lands.
|
|
260
|
-
- The web client
|
|
718
|
+
- The web client work (vendor Kiwi IRC, serve SPA from the
|
|
261
719
|
Worker, WS Origin allowlist) is pending.
|
|
262
720
|
|
|
263
721
|
### Security
|
|
@@ -272,8 +730,8 @@ resolves it.
|
|
|
272
730
|
|
|
273
731
|
## [0.5.0] - 2026-08-02
|
|
274
732
|
|
|
275
|
-
The
|
|
276
|
-
|
|
733
|
+
The main change in this release is **protocol completion**: the deferred IRC verbs land,
|
|
734
|
+
and the S2S / obsolete RFC 2812 verbs are **formally
|
|
277
735
|
dropped** rather than left "deferred". With this release every standard
|
|
278
736
|
IRC verb a modern client expects is implemented — the only remaining
|
|
279
737
|
`421 ERR_UNKNOWNCOMMAND` paths are the deliberately-unimplemented S2S
|
|
@@ -283,10 +741,10 @@ client compatibility matrix) and the required-`serverName` config gate.
|
|
|
283
741
|
|
|
284
742
|
### Added — Deferred IRC verbs (`@serverless-ircd/irc-core`, `@serverless-ircd/irc-server`)
|
|
285
743
|
|
|
286
|
-
Six
|
|
744
|
+
Six tickets add the remaining command surface. Each ships
|
|
287
745
|
a pure reducer, an actor route case (no more `421`), new numerics in
|
|
288
746
|
`protocol/numerics.ts`, and unit + actor coverage. (The remaining two
|
|
289
|
-
|
|
747
|
+
tickets are drops — see below.)
|
|
290
748
|
|
|
291
749
|
- **`KILL`** (oper-gated force-disconnect): `KILL <nick>
|
|
292
750
|
<comment>` resolves the target via the nick registry + a live
|
|
@@ -340,7 +798,7 @@ Phase 11 tickets are drops — see below.)
|
|
|
340
798
|
channel-scoped). The originator is always excluded (skip-self,
|
|
341
799
|
matching NOTICE/PRIVMSG semantics). Empty message → `461`. The `+w`
|
|
342
800
|
user mode was already accepted by `MODE` and advertised in `004
|
|
343
|
-
RPL_MYINFO`; this
|
|
801
|
+
RPL_MYINFO`; this enables the consumer.
|
|
344
802
|
- **`SETNAME`** (realname change): `SETNAME :<realname>`
|
|
345
803
|
updates `state.realname` so a subsequent `WHOIS` reflects the new
|
|
346
804
|
value in `311 RPL_WHOISUSER`. No broadcast is emitted in v1 (there
|
|
@@ -350,7 +808,7 @@ Phase 11 tickets are drops — see below.)
|
|
|
350
808
|
|
|
351
809
|
### Changed — S2S & obsolete verbs formally dropped (`@serverless-ircd/irc-core`)
|
|
352
810
|
|
|
353
|
-
The remaining two
|
|
811
|
+
The remaining two tickets convert the "deferred" framing into
|
|
354
812
|
an explicit **drop**. There is no behaviour change on the wire — the verbs
|
|
355
813
|
already returned `421 ERR_UNKNOWNCOMMAND` — but the framing, comments,
|
|
356
814
|
and reserved numerics are cleaned up so the codebase no longer carries
|
|
@@ -447,13 +905,13 @@ dead scaffolding.
|
|
|
447
905
|
|
|
448
906
|
## [0.4.0] - 2026-07-31
|
|
449
907
|
|
|
450
|
-
The
|
|
908
|
+
The main change in this release is **dual transport**: both adapters now speak a real
|
|
451
909
|
`irc+tls :6697` (TLS-over-TCP) surface for stock IRC clients in
|
|
452
910
|
production — not just the local CLI — alongside the WebSocket path. This
|
|
453
911
|
lands **SASL `EXTERNAL` via mTLS**, the **remaining standard IRC verbs**
|
|
454
912
|
(WHOWAS, VERSION, TIME, ADMIN, INFO, USERHOST, ISON), a **Cloudflare
|
|
455
913
|
D1-backed SASL account store**, config-driven server identity, and a
|
|
456
|
-
**Node 24 / pnpm 11** toolchain.
|
|
914
|
+
**Node 24 / pnpm 11** toolchain. TLS hardening & raw TCP is
|
|
457
915
|
complete.
|
|
458
916
|
|
|
459
917
|
### Added — Dual transport: WebSocket + `irc+tls :6697`
|
|
@@ -523,7 +981,7 @@ pipeline is unchanged.
|
|
|
523
981
|
|
|
524
982
|
- The `002` / `003` / `004` registration replies now reflect
|
|
525
983
|
`ServerConfig.serverVersion` / `createdAt` — the hardcoded
|
|
526
|
-
`'0.1.0'` /
|
|
984
|
+
`'0.1.0'` version / placeholder strings are gone. A build step
|
|
527
985
|
(`scripts/generate-build-info.mjs`) bakes the package version and a
|
|
528
986
|
build timestamp into `RPL_CREATED` (`003`) when the config omits them.
|
|
529
987
|
|
|
@@ -791,7 +1249,7 @@ AWS are now both deployable.
|
|
|
791
1249
|
### Added — Shared test harness (`@serverless-ircd/irc-test-support`)
|
|
792
1250
|
|
|
793
1251
|
- New workspace package owning the parametrized IRC scenario suite
|
|
794
|
-
(`runIrcScenarios`, 31
|
|
1252
|
+
(`runIrcScenarios`, 31 scenarios) and the
|
|
795
1253
|
`IrcHarnessFactory` / `IrcHarness` / `ClientHarness` seam each
|
|
796
1254
|
adapter implements. Ships the reference `inMemoryHarnessFactory`.
|
|
797
1255
|
cf-adapter and aws-adapter integration tests both consume it, so the
|
|
@@ -822,7 +1280,7 @@ AWS are now both deployable.
|
|
|
822
1280
|
end-to-end rather than advertised-only.
|
|
823
1281
|
- The local CLI now binds a **second RFC-style TCP listener** by default
|
|
824
1282
|
(`--tcp-port`, default `<port> + 1`) alongside the WebSocket listener,
|
|
825
|
-
so stock IRC clients can
|
|
1283
|
+
so stock IRC clients can connect directly without the forwarder.
|
|
826
1284
|
Pass `--no-tcp` for WebSocket-only. `--motd-file` loads the MOTD from
|
|
827
1285
|
disk.
|
|
828
1286
|
|