serverless-ircd 0.7.0 → 0.9.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 +7 -3
- package/.gitmodules +1 -1
- package/CHANGELOG.md +518 -29
- package/README.md +221 -161
- 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-tcp-container/src/container-server.ts +21 -1
- package/apps/cf-tcp-container/tests/config-loader.test.ts +43 -0
- package/apps/cf-tcp-container/tests/container-server.test.ts +249 -1
- package/apps/cf-tcp-container/tests/persistence.test.ts +9 -0
- package/apps/cf-tcp-container/tests/tls-e2e.test.ts +24 -5
- package/apps/cf-worker/package.json +1 -1
- package/apps/cf-worker/src/worker.ts +4 -4
- package/apps/cf-worker/tests/fixtures/web-dist/webclient/index.html +18 -0
- package/apps/cf-worker/tests/smoke.test.ts +5 -5
- package/apps/cf-worker/wrangler.test.toml +6 -6
- package/apps/cf-worker/wrangler.toml +7 -5
- 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 +95 -31
- package/apps/local-cli/tests/config-loader.test.ts +14 -0
- package/apps/local-cli/tests/config-resolution.test.ts +65 -0
- package/apps/local-cli/tests/motd-file-non-error.test.ts +29 -0
- package/apps/local-cli/tests/rehash.test.ts +147 -0
- package/apps/local-cli/tests/server-helpers.test.ts +63 -0
- package/apps/local-cli/tests/tcp.test.ts +89 -0
- package/apps/local-cli/tests/ws-subprotocol.test.ts +92 -0
- package/apps/web/landing/index.html +237 -16
- package/apps/web/package.json +3 -2
- package/apps/web/scripts/build.mjs +46 -16
- package/apps/web/src/build-env.ts +1 -1
- package/apps/web/src/config-schema.ts +6 -6
- package/apps/web/src/render-docs.ts +292 -0
- package/apps/web/tests/build-smoke.test.ts +43 -14
- package/apps/web/tests/config-schema.test.ts +1 -1
- package/apps/web/tests/landing-content.test.ts +103 -0
- package/apps/web/tests/render-docs.test.ts +198 -0
- package/docs/AWS-Adapter-Architecture.md +3 -2
- package/docs/AWS-Deployment.md +21 -8
- package/docs/Cloudflare-Deployment-Guide.md +22 -6
- package/docs/PlanExtensions.md +113 -3
- package/docs/Release-Process.md +23 -13
- package/docs/Services.md +578 -0
- package/docs/WebClientGuide.md +32 -31
- package/package.json +2 -2
- package/packages/aws-adapter/package.json +1 -1
- package/packages/aws-adapter/src/aws-runtime.ts +20 -1
- 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 +25 -2
- 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/aws-runtime.test.ts +23 -1
- 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 +144 -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/global-setup.ts +28 -1
- package/packages/aws-adapter/tests/gone-exception.test.ts +21 -2
- package/packages/aws-adapter/tests/handlers.test.ts +105 -3
- package/packages/aws-adapter/tests/nlb-stream.test.ts +29 -1
- package/packages/aws-adapter/tests/sweeper.test.ts +20 -0
- 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 +130 -8
- 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/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-pure.test.ts +130 -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/scripts/generate-build-info.mjs +26 -5
- package/packages/irc-core/src/commands/account-auth.ts +200 -0
- package/packages/irc-core/src/commands/chanserv.ts +1166 -0
- package/packages/irc-core/src/commands/hostserv.ts +522 -0
- package/packages/irc-core/src/commands/index.ts +13 -0
- package/packages/irc-core/src/commands/join.ts +171 -9
- 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 +402 -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 +78 -12
- package/packages/irc-core/src/commands/sasl.ts +18 -49
- package/packages/irc-core/src/commands/service-aliases.ts +52 -0
- package/packages/irc-core/src/commands/tagmsg.ts +41 -6
- package/packages/irc-core/src/commands/topic.ts +60 -10
- 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 +38 -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/commands/chanserv.test.ts +2335 -0
- package/packages/irc-core/tests/commands/chathistory.test.ts +6 -0
- package/packages/irc-core/tests/commands/hostserv.test.ts +1006 -0
- package/packages/irc-core/tests/commands/join.test.ts +572 -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 +990 -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 +1015 -20
- package/packages/irc-core/tests/commands/sasl.test.ts +229 -12
- package/packages/irc-core/tests/commands/server-info.test.ts +9 -5
- package/packages/irc-core/tests/commands/service-aliases.test.ts +52 -0
- 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/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 +121 -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 +988 -15
- 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 +37 -8
- package/packages/irc-test-support/src/index.ts +1 -0
- package/packages/irc-test-support/src/scenarios.ts +21 -6
- package/packages/irc-test-support/tests/in-memory-harness.test.ts +51 -0
- package/packages/irc-test-support/vitest.config.ts +6 -1
- package/tools/ci-hardening/package.json +1 -1
- package/tools/load-test/package.json +1 -1
- package/tools/load-test/src/client.ts +13 -13
- package/tools/load-test/tests/client.test.ts +258 -2
- package/tools/load-test/tests/config.test.ts +39 -0
- package/tools/load-test/tests/harness.test.ts +21 -0
- package/tools/load-test/tests/metrics.test.ts +7 -0
- package/tools/tcp-ws-forwarder/package.json +1 -1
- package/tools/tcp-ws-forwarder/tests/close-error.test.ts +40 -0
- package/tools/tcp-ws-forwarder/tests/defensive-branches.test.ts +78 -0
- package/tools/tcp-ws-forwarder/tests/forwarder.test.ts +53 -2
- package/tools/tcp-ws-forwarder/tests/logger.test.ts +31 -1
- package/apps/cf-worker/tests/fixtures/web-dist/app/index.html +0 -18
- package/packages/irc-core/tests/read-marker-store.test.ts +0 -108
package/CHANGELOG.md
CHANGED
|
@@ -7,24 +7,513 @@ 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.9.0] - 2026-08-10
|
|
14
|
+
|
|
15
|
+
The main change in this release is **rounding out the integrated IRC
|
|
16
|
+
services surface** that landed in v0.8.0: ChanServ gains the prefix and
|
|
17
|
+
roster mutation verbs operators expect (`OP` / `DEOP` / `VOICE` /
|
|
18
|
+
`DEVOICE` / `KICK` / `BAN` / `UNBAN`), HostServ **auto-applies an
|
|
19
|
+
assigned vhost on identify** (no per-session `HostServ ON`), HostServ
|
|
20
|
+
HELP hides oper-only commands from non-opers, NickServ grows an `ID`
|
|
21
|
+
alias for `IDENTIFY`, and **NickServ `INFO` no longer leaks the
|
|
22
|
+
registrant's email** to non-owners. Alongside it: service **shortcut
|
|
23
|
+
verbs** (`/NICKSERV`, `/NS`, `/CS`, `/HS`, `/MS`, `/OS`, …) that the
|
|
24
|
+
daemon rewrites to the equivalent `PRIVMSG <Service>`, `echo-message`
|
|
25
|
+
reflection of `PRIVMSG` to service nicks, and the long-missing **JOIN
|
|
26
|
+
`332`/`333` topic numerics** on reconnect/rejoin (regardless of how the
|
|
27
|
+
topic came to be set). A real PASS-login bug is fixed: a client that
|
|
28
|
+
completes registration before sending `PASS <nick>:<password>` is now
|
|
29
|
+
logged in instead of receiving a silent no-op. The AWS adapter gets
|
|
30
|
+
another round of consistency hardening — every `Connections` `GetCommand`
|
|
31
|
+
now uses `ConsistentRead: true`. The web client renders the `docs/` wiki
|
|
32
|
+
to standalone HTML at `/docs/` and gains a client-specific "how to
|
|
33
|
+
connect" section on the landing page.
|
|
34
|
+
|
|
35
|
+
### Added — Services surface expansion (`@serverless-ircd/irc-core`)
|
|
36
|
+
|
|
37
|
+
- **ChanServ `OP` / `DEOP` / `VOICE` / `DEVOICE` / `KICK` / `BAN` /
|
|
38
|
+
`UNBAN`**: founder-or-`AUTOOP`-level callers can grant/remove prefix
|
|
39
|
+
modes on demand, kick through ChanServ, and manage channel bans. Each
|
|
40
|
+
verb emits the corresponding `:ChanServ!ChanServ@services MODE` /
|
|
41
|
+
`KICK` broadcast and an `ApplyChannelDelta` against the channel
|
|
42
|
+
authority (the same mechanism MLOCK reasserts use), so the roster
|
|
43
|
+
and ban list update atomically with the broadcast. A target not on
|
|
44
|
+
the channel produces a NOTICE and no state change. Privilege gating
|
|
45
|
+
reuses the existing access-list check — no new store methods.
|
|
46
|
+
- **HostServ auto-applies an assigned vhost on identify.** SASL
|
|
47
|
+
PLAIN/EXTERNAL, NickServ `IDENTIFY`, and PASS-auth now consult
|
|
48
|
+
`services.getVhost(account)` on a successful login: when an assigned
|
|
49
|
+
vhost exists, `applyVhost` runs immediately — `state.host` is set,
|
|
50
|
+
`state.vhostActive` is stamped, and any currently-joined
|
|
51
|
+
`chghost`-capable peers see the `CHGHOST` broadcast. At SASL/PASS-auth
|
|
52
|
+
time (pre-JOIN) there are no peers so no broadcast, but the vhost is
|
|
53
|
+
visible in the subsequent `001` welcome. Users no longer need to run
|
|
54
|
+
`HostServ ON` every session.
|
|
55
|
+
- **NickServ `ID` alias for `IDENTIFY`.** `PRIVMSG NickServ :ID <pw>`
|
|
56
|
+
and `ID <nick> <pw>` behave identically to `IDENTIFY` — the alias
|
|
57
|
+
is not advertised in HELP (mirrors Atheme's hidden shorthand).
|
|
58
|
+
- **Service shortcut verbs** (`@serverless-ircd/irc-server`):
|
|
59
|
+
`/NICKSERV`, `/NS`, `/CS`, `/HS`, `/MS`, `/OS` (and their long forms)
|
|
60
|
+
are rewritten at the actor to the equivalent
|
|
61
|
+
`PRIVMSG <ServiceNick> :<args>`. Clients with a dedicated
|
|
62
|
+
slash-command UX (WeeChat, HexChat, IRCCloud) now work without a
|
|
63
|
+
custom alias file. The aliases are case-insensitive and accept both
|
|
64
|
+
colon-trailing and space-joined arg shapes.
|
|
65
|
+
- **`echo-message` reflection for service PRIVMSG**: a client that
|
|
66
|
+
has negotiated `echo-message` now sees its own
|
|
67
|
+
`PRIVMSG NickServ :IDENTIFY hunter2` reflected back before the
|
|
68
|
+
NickServ NOTICE reply, matching the echo behaviour for every other
|
|
69
|
+
PRIVMSG target. The echo is emitted before the reply so labeled-
|
|
70
|
+
response batches wrap both correctly.
|
|
71
|
+
|
|
72
|
+
### Added — Web client (`apps/web`)
|
|
73
|
+
|
|
74
|
+
- **`docs/` wiki rendered to `/docs/`.** The Gitea-wiki markdown under
|
|
75
|
+
the `docs/` submodule is now rendered to standalone HTML at
|
|
76
|
+
`apps/web/dist/docs/<slug>.html` and served by the existing
|
|
77
|
+
`[assets]` binding at `/docs/<slug>.html` (with `Home.md` →
|
|
78
|
+
`/docs/index.html`). Intra-docs Markdown links are rewritten to
|
|
79
|
+
`.html`, a shared stylesheet is emitted, and an `ADR-Index` page
|
|
80
|
+
links every ADR. Requires `git submodule update --init docs` before
|
|
81
|
+
build; the build fails loudly with the recovery command if the
|
|
82
|
+
submodule is missing or empty.
|
|
83
|
+
- **Landing-page "how to connect" section.** The static landing page
|
|
84
|
+
gains a CSS-only tabbed picker with copy-paste connect snippets for
|
|
85
|
+
the common stock IRC clients (WeeChat, HexChat, irssi, IRCCloud),
|
|
86
|
+
replacing the previous per-client static guides. The picker covers
|
|
87
|
+
both the WebSocket path and the `irc+tls :6697` path.
|
|
88
|
+
|
|
89
|
+
### Changed — PASS-based account login (`@serverless-ircd/irc-core`)
|
|
90
|
+
|
|
91
|
+
- **A late `PASS <nick>:<password>` now logs the connection in.**
|
|
92
|
+
Previously, a client that completed registration (`NICK` + `USER`
|
|
93
|
+
resolving before `PASS` arrived, or any `PASS` sent after `CAP END`
|
|
94
|
+
resolved) was silently rejected — the `passReducer` short-circuited
|
|
95
|
+
at `state.registration === 'registered'` and emitted `462
|
|
96
|
+
ERR_ALREADYREGISTRED` for any post-registration `PASS`. The shared
|
|
97
|
+
verify + `applyAccountSuccess` pipeline now runs on a
|
|
98
|
+
post-registration `PASS <nick>:<password>`: on success it emits
|
|
99
|
+
`900 RPL_LOGGEDIN` + `903 RPL_SASLSUCCESS`, stamps `+r`, and runs
|
|
100
|
+
the read-marker / away / memo replay in-band, exactly like the
|
|
101
|
+
at-registration path. A bare `PASS <value>` (no colon) is a silent
|
|
102
|
+
no-op once registration has completed — a server password is
|
|
103
|
+
meaningless post-registration, and emitting `462` for it was
|
|
104
|
+
counterproductive. The pre-registration duplicate-PASS `462` (two
|
|
105
|
+
`PASS`es before `NICK`+`USER`) is preserved unchanged.
|
|
106
|
+
|
|
107
|
+
### Fixed — Channel correctness (`@serverless-ircd/irc-core`)
|
|
108
|
+
|
|
109
|
+
- **JOIN now emits `332 RPL_TOPIC` / `333 RPL_TOPICWHOISTIME` from any
|
|
110
|
+
source of the topic.** The previous JOIN reducer keyed topic
|
|
111
|
+
emission solely on the ChanServ KEEPTOPIC restore path (an empty
|
|
112
|
+
channel this join), so reconnecting clients after a deploy saw the
|
|
113
|
+
NAMES list but no topic — even when the topic survived the deploy
|
|
114
|
+
in storage. The emission now keys off `state.topic`, so a topic
|
|
115
|
+
restored from a KEEPTOPIC snapshot, a deploy snapshot with
|
|
116
|
+
KEEPTOPIC off, or an earlier joiner all produce the correct
|
|
117
|
+
numerics. The KEEPTOPIC restore path still maintains `state.topic`
|
|
118
|
+
for an empty channel; only the emission trigger changed.
|
|
119
|
+
- **No auto-op for the first joiner of a registered channel off the
|
|
120
|
+
access list.** A registered channel that had been emptied (so its
|
|
121
|
+
roster snapshot was fresh) used to auto-op the first joiner
|
|
122
|
+
unconditionally as a "founder fallback", which let any client claim
|
|
123
|
+
`+o` by joining an empty registered channel. The fallback now only
|
|
124
|
+
fires when the joiner is identified as the founder (or is on the
|
|
125
|
+
access list); an unidentified first joiner joins without prefix
|
|
126
|
+
modes.
|
|
127
|
+
|
|
128
|
+
### Fixed — AWS adapter (`@serverless-ircd/aws-adapter`)
|
|
129
|
+
|
|
130
|
+
- **Strongly-consistent `Connections` reads across every handler.**
|
|
131
|
+
`$disconnect`'s `cleanupConnection` and the NLB streaming handler's
|
|
132
|
+
per-chunk row read were eventually consistent, so under DynamoDB
|
|
133
|
+
replication lag they could miss the row that was just written (or
|
|
134
|
+
updated) — dropping the first frame of a raw `:6697` connection or
|
|
135
|
+
skipping nick release + membership fanout on `$disconnect`. Every
|
|
136
|
+
`GetCommand` against the `Connections` table in `aws-adapter/src/**`
|
|
137
|
+
now sets `ConsistentRead: true`, matching the `$default` fix from
|
|
138
|
+
v0.8.0. A unit test asserts each call site carries the flag.
|
|
139
|
+
- **Bounded `docker info` probe in the test global-setup.** On hosts
|
|
140
|
+
with Docker Desktop installed but not running, the macOS `docker`
|
|
141
|
+
CLI hangs indefinitely waiting for the daemon to spin up — and
|
|
142
|
+
`testcontainers` inherits that hang, stalling the whole `pnpm test`
|
|
143
|
+
run. The aws-adapter `globalSetup` now pre-probes `docker info`
|
|
144
|
+
with a 5-second exec timeout; if Docker is not reachable, it logs
|
|
145
|
+
and falls through to the Java ZIP fallback (or skip) instead of
|
|
146
|
+
hanging. No behaviour change when Docker is up.
|
|
147
|
+
|
|
148
|
+
### Fixed — HostServ help leak (`@serverless-ircd/irc-core`)
|
|
149
|
+
|
|
150
|
+
- **HostServ HELP hides oper-only commands from non-opers.** The
|
|
151
|
+
help/unknown NOTICE previously listed all seven HostServ commands
|
|
152
|
+
(`ON, OFF, REQUEST, SET, APPROVE, REJECT, LIST`) to every caller,
|
|
153
|
+
leaking the existence and names of oper commands. The notice
|
|
154
|
+
helpers now branch on `state.userModes.oper`: a non-oper sees only
|
|
155
|
+
`ON, OFF, REQUEST`; an oper sees the full list. The per-handler
|
|
156
|
+
oper gates are unchanged, so a non-oper who guesses an oper
|
|
157
|
+
command name still gets the existing permission-denied NOTICE.
|
|
158
|
+
|
|
159
|
+
### Fixed — NickServ INFO privacy (`@serverless-ircd/irc-core`)
|
|
160
|
+
|
|
161
|
+
- **NickServ `INFO` no longer leaks the registrant's email to
|
|
162
|
+
non-owners.** `handleInfo` previously returned the `Email:` line
|
|
163
|
+
unconditionally to any caller, identified or not — a remote,
|
|
164
|
+
unidentified user could enumerate registered nicks and harvest
|
|
165
|
+
email addresses. The `Email:` line is now gated behind
|
|
166
|
+
`state.account === rec.account` (the owner) or
|
|
167
|
+
`state.userModes.oper === true` (an oper). The owner path still
|
|
168
|
+
shows the full record; `INFO` with no target defaults to the
|
|
169
|
+
caller's own nick (owner path). The `Nick:` / `Account:` lines
|
|
170
|
+
remain visible to all callers (consistent with how a WHOIS against
|
|
171
|
+
a registered nick surfaces the account name).
|
|
172
|
+
|
|
173
|
+
### Changed — Local CLI (`apps/local-cli`)
|
|
174
|
+
|
|
175
|
+
- **`operCreds` plumbed through the config schema.** The local CLI's
|
|
176
|
+
`ServerConfig` now carries `operCreds` end-to-end (was wired ad-hoc
|
|
177
|
+
through constructor options); OPER auth against configured creds
|
|
178
|
+
works the same way it does in the cloud adapters. The transport
|
|
179
|
+
seams (WS / TCP) are also extracted into named helpers for
|
|
180
|
+
readability.
|
|
181
|
+
|
|
182
|
+
### Changed — CI / tooling
|
|
183
|
+
|
|
184
|
+
- CI workflows now explicitly checkout every submodule (`docs/`,
|
|
185
|
+
`apps/web/upstream/`) so the render-docs and Kiwi build steps run
|
|
186
|
+
deterministically on every runner, not just maintainer laptops.
|
|
187
|
+
|
|
188
|
+
### ⚠️ Migration required
|
|
189
|
+
|
|
190
|
+
- **No persisted-DO-state schema change.** `PERSISTED_STATE_VERSION`
|
|
191
|
+
stays `1`. The ChanServ roster commands, HostServ auto-vhost, and
|
|
192
|
+
the JOIN topic-numeric fix are runtime behaviour, not persisted DO
|
|
193
|
+
fields. **Code rollback across this release is safe.**
|
|
194
|
+
- **No D1 / DynamoDB schema change.** The existing services tables
|
|
195
|
+
(`nickserv_accounts`, `chanserv_channels`, …) carry the new
|
|
196
|
+
behaviour unchanged. The `cert_subjects` column flagged for the
|
|
197
|
+
in-progress services credential consolidation is **not yet
|
|
198
|
+
present** — operators do not need to run any migration command for
|
|
199
|
+
this release.
|
|
200
|
+
- **Behaviour change: late `PASS` no longer emits `462`.** A
|
|
201
|
+
post-registration `PASS <value>` used to reply
|
|
202
|
+
`462 ERR_ALREADYREGISTRED`; it is now silent. Clients that keyed
|
|
203
|
+
off the `462` to detect "you already sent PASS" will no longer see
|
|
204
|
+
it. No client is known to do this (the numeric is informational),
|
|
205
|
+
but scripted regression tests against this behaviour must be
|
|
206
|
+
updated (the in-tree `irc-test-support` scenario was updated).
|
|
207
|
+
- **Behaviour change: late `PASS <nick>:<password>` now logs in.**
|
|
208
|
+
Clients that intentionally sent the colon form post-registration
|
|
209
|
+
expecting it to be ignored will now be logged in. This is the
|
|
210
|
+
intended fix; flag it in operator runbooks if the deployment was
|
|
211
|
+
relying on the silent-reject for some access-control flow.
|
|
212
|
+
|
|
213
|
+
### Known limitations
|
|
214
|
+
|
|
215
|
+
- **The `AccountStore` / `ServicesStore` credential drift is still
|
|
216
|
+
present.** A nick registered via NickServ `REGISTER` after the
|
|
217
|
+
worker booted still cannot authenticate via SASL PLAIN/EXTERNAL or
|
|
218
|
+
`PASS <nick>:<password>` against the *fresh* password — those paths
|
|
219
|
+
consult the SASL `AccountStore` (`accounts` table), which
|
|
220
|
+
`registerNick` does not write back to. The PASS-login fix above
|
|
221
|
+
makes the at-registration and post-registration paths symmetric,
|
|
222
|
+
but the underlying cross-store drift remains tracked for a
|
|
223
|
+
follow-up release that consolidates the credential into a single
|
|
224
|
+
store. Operators who need a NickServ-registered nick to work with
|
|
225
|
+
SASL/PASS immediately should re-seed via
|
|
226
|
+
`tools/seed-cf-accounts.ts` (D1) / `tools/seed-aws-accounts.ts`
|
|
227
|
+
(DynamoDB) with the same password.
|
|
228
|
+
- Same transport limits as v0.8.0: the **CF TCP path requires
|
|
229
|
+
Cloudflare Spectrum (Enterprise tier)** plus a stateful Container
|
|
230
|
+
origin; the **AWS TCP path** uses NLB + Lambda streaming and is
|
|
231
|
+
subject to Lambda idle-timeout / stream-duration limits. The
|
|
232
|
+
WebSocket path remains the zero-extra-deps default.
|
|
233
|
+
- **mTLS (for SASL `EXTERNAL`) requires a custom domain + uploaded
|
|
234
|
+
CA/trust-store** on both platforms and is not enabled by default.
|
|
235
|
+
- **Client compatibility sweep** (WeeChat / HexChat / IRCCloud /
|
|
236
|
+
TheLounge / matrix-IRC bridge) remains pending.
|
|
237
|
+
- **Coverage hardening is partial.** `irc-core`, `irc-server`, and
|
|
238
|
+
`in-memory-runtime` sit at 100%; `aws-adapter` and `aws-stack`
|
|
239
|
+
clear the 90% gate. The remaining packages (`cf-adapter`,
|
|
240
|
+
`local-cli`, `load-test`, `cf-tcp-container`, `tcp-ws-forwarder`,
|
|
241
|
+
`irc-test-support`, `web`) are **above the gate but below 100%** —
|
|
242
|
+
follow-ups drive each to full coverage.
|
|
243
|
+
|
|
244
|
+
### Security
|
|
245
|
+
|
|
246
|
+
- **NickServ `INFO` email disclosure** (Fixed above): a remote,
|
|
247
|
+
unidentified client could harvest email addresses from every
|
|
248
|
+
registered nick. The disclosure is now gated behind owner-or-oper.
|
|
249
|
+
- **First-joiner op-takeover on empty registered channels** (Fixed
|
|
250
|
+
above): an unidentified client could claim `+o` by being the first
|
|
251
|
+
to join an empty registered channel. The auto-op fallback now
|
|
252
|
+
requires founder/access-list identification.
|
|
253
|
+
- No other auth path changed. The scrypt-hashed credential store,
|
|
254
|
+
server-password gate, and CSWSH defense are unchanged from v0.8.0.
|
|
255
|
+
|
|
256
|
+
---
|
|
257
|
+
|
|
258
|
+
## [0.8.0] - 2026-08-08
|
|
259
|
+
|
|
260
|
+
The main change in this release is **integrated IRC services**: NickServ,
|
|
261
|
+
ChanServ, HostServ, OperServ, and MemoServ ship as part of the daemon
|
|
262
|
+
itself — there is **no separate services process and no S2S link**. The
|
|
263
|
+
service nicks are reserved and routed to dedicated pure reducers backed
|
|
264
|
+
by a new `ServicesStore` port, with **persistent D1 (Cloudflare) and
|
|
265
|
+
DynamoDB (AWS) backends** so registrations survive redeploys. Alongside
|
|
266
|
+
it: the SASL `AccountStore` and NickServ accounts are **unified** under
|
|
267
|
+
one scrypt-hashed credential store, `PASS <nick>:<password>` identifies
|
|
268
|
+
at registration, the `draft/read-marker` `MARKREAD` verb lands, and a
|
|
269
|
+
round of **AWS adapter hardening** (O(1) admission counter, strongly-
|
|
270
|
+
consistent `$default` reads, WebSocket subprotocol echo fix).
|
|
271
|
+
|
|
272
|
+
### Added — Integrated IRC services (`@serverless-ircd/irc-core`, `@serverless-ircd/irc-server`)
|
|
273
|
+
|
|
274
|
+
The ten services tickets add the full
|
|
275
|
+
services surface. Service names are reserved nicks recognized at
|
|
276
|
+
`PRIVMSG` time and routed to dedicated pure reducers when a
|
|
277
|
+
`ServicesStore` is bound; when no store is bound every services command
|
|
278
|
+
replies `501` and the rest of the daemon is unaffected. See
|
|
279
|
+
`docs/Services.md` for the operator reference.
|
|
280
|
+
|
|
281
|
+
- **`ServicesStore` port + in-memory reference impl**: a synchronous,
|
|
282
|
+
injectable store holding NickServ / ChanServ / HostServ / MemoServ /
|
|
283
|
+
OperServ / read-marker state. Reducers stay pure — all side effects
|
|
284
|
+
flow through the store port. The `ReadMarkerStore` from v0.6.0 is
|
|
285
|
+
**folded into** `ServicesStore` so read-markers, `draft/pre-away`,
|
|
286
|
+
and account state share one persistence path.
|
|
287
|
+
- **NickServ** (`REGISTER` / `IDENTIFY` / `DROP` / `INFO` / `SET`):
|
|
288
|
+
registers the current nick, identifies to an account (setting the
|
|
289
|
+
read-only user mode **`+r`**, fanning `ACCOUNT` to `account-notify`
|
|
290
|
+
peers, then delivering unread MemoServ memos), and supports
|
|
291
|
+
`SET ENFORCE` — a registered nick held by a non-identified
|
|
292
|
+
connection is reclaimed on the `NICK` path (enforcement policy
|
|
293
|
+
mirrors Atheme/Anope). `IDENTIFY` falls back to SASL account
|
|
294
|
+
credentials when no NickServ row exists.
|
|
295
|
+
- **ChanServ** (`REGISTER` / `DROP` / `SET` / `INFO` / `ACCESS` /
|
|
296
|
+
`LEVELS`): channel registration with a founder, the services-only
|
|
297
|
+
channel modes **`+r`** (registered), **`+R`** (block unidentified
|
|
298
|
+
join/message), and **`+M`** (moderated-identified), `ACCESS`/`LEVELS`
|
|
299
|
+
(SOP/AOP/VOP/HOP access lists), and auto-op on `JOIN` per access
|
|
300
|
+
level. The three services modes are settable only via ChanServ, not
|
|
301
|
+
via `MODE` (`472 ERR_UNKNOWNMODE` / `501`).
|
|
302
|
+
- **HostServ** (`ON` / `OFF` / `REQUEST` + oper `SET` / `APPROVE` /
|
|
303
|
+
`REJECT` / `LIST`): per-account vhost with `CHGHOST` fanout to shared
|
|
304
|
+
channels. `REQUEST` enters an **oper approval queue**; opers `APPROVE`
|
|
305
|
+
to activate or `REJECT` to drop it.
|
|
306
|
+
- **OperServ** (`AKILL` / `JUPE` / `UNJUPE` / `RAW`): oper-gated
|
|
307
|
+
network management. `AKILL` records an `*@host` mask, `JUPE` reserves
|
|
308
|
+
a juped nick (rejected at `NICK` with `432 ERR_ERRONEUSNICKNAME`),
|
|
309
|
+
`RAW` is the escape hatch. Wired end-to-end: `PRIVMSG OperServ :…`
|
|
310
|
+
routes to the OperServ reducer.
|
|
311
|
+
- **MemoServ** (`SEND` / `LIST` / `READ` / `DEL`): per-account memos
|
|
312
|
+
with queue delivery — unread memos are replayed at identify (NickServ
|
|
313
|
+
`IDENTIFY` / SASL login / PASS-based login), the same path that
|
|
314
|
+
replays `draft/read-marker` and `draft/pre-away`.
|
|
315
|
+
|
|
316
|
+
### Added — Persistent services backends (`@serverless-ircd/cf-adapter`, `@serverless-ircd/aws-adapter`)
|
|
317
|
+
|
|
318
|
+
Both adapters ship write-behind persistence for the `ServicesStore` so
|
|
319
|
+
registrations, channel access lists, vhosts, memos, and read-markers
|
|
320
|
+
survive a redeploy/reconnect.
|
|
321
|
+
|
|
322
|
+
- **Cloudflare (D1)**: `D1ServicesStore` over the shared `ACCOUNTS_DB`
|
|
323
|
+
binding (the same D1 database used for SASL accounts since v0.4.0).
|
|
324
|
+
The ten services tables **auto-provision on cold start** (`CREATE
|
|
325
|
+
TABLE IF NOT EXISTS` inside `loadD1ServicesStore`), so a fresh deploy
|
|
326
|
+
self-provisions and an existing deploy re-runs the batch as a cheap
|
|
327
|
+
no-op. `ConnectionDO` load/flush hooks wrap each frame and teardown.
|
|
328
|
+
- **AWS (DynamoDB)**: `DynamoServicesStore` plus a new `Services`
|
|
329
|
+
table definition and handler wiring so NLB-stream / `$default`
|
|
330
|
+
invocations hydrate + flush services alongside the existing account
|
|
331
|
+
store. Enabled when the `SERVICES_TABLE` env var / table name is
|
|
332
|
+
supplied.
|
|
333
|
+
- **`PersistentServicesStore` base** (`irc-core`): the shared
|
|
334
|
+
write-behind base — mutates the inherited cache synchronously and
|
|
335
|
+
enqueues matching writes for `flush()`, so reducers stay free of IO.
|
|
336
|
+
|
|
337
|
+
### Added — Unified accounts & registration login (`@serverless-ircd/irc-core`)
|
|
338
|
+
|
|
339
|
+
- **Unified SASL + NickServ account store**: the SASL `AccountStore`
|
|
340
|
+
and NickServ `ServicesStore` now share one **scrypt-hashed**
|
|
341
|
+
credential shape. A NickServ registration *is* a SASL account —
|
|
342
|
+
SASL `PLAIN`'s services fallback verifies against a row created by
|
|
343
|
+
NickServ `REGISTER` without a second registration, and vice versa.
|
|
344
|
+
The plaintext password on `ServicesNickRow` is replaced with a
|
|
345
|
+
`HashedAccountCredential` (scrypt + random salt). `ingestAccountCredential`
|
|
346
|
+
adopts a pre-hashed SASL credential as a registered nick (idempotent;
|
|
347
|
+
existing records win).
|
|
348
|
+
- **PASS-based account login**: a client may identify to its NickServ
|
|
349
|
+
account at registration by sending `PASS <nick>:<password>` (the same
|
|
350
|
+
literal the `SASL_ACCOUNTS` seed tooling uses). On success the
|
|
351
|
+
connection is logged in — emitting `900`/`903`, stamping `+r`, and
|
|
352
|
+
running the same read-marker / away / memo replay as SASL. A bare
|
|
353
|
+
`PASS <value>` (no colon) is always treated as a server-password
|
|
354
|
+
candidate. The shared login reduction (`applyAccountSuccess`) is
|
|
355
|
+
extracted and reused from SASL.
|
|
356
|
+
- **Server-password gate with SASL short-circuit**: an optional gate
|
|
357
|
+
enforced in the registration reducer at `001` — when
|
|
358
|
+
`ServerConfig.serverPassword` is set, every connection must supply a
|
|
359
|
+
matching `PASS` (or authenticate via SASL / PASS-based account login)
|
|
360
|
+
before `RPL_WELCOME`, else `464 ERR_PASSWDMISMATCH` + disconnect.
|
|
361
|
+
Wired through all three adapters (`SERVER_PASSWORD` Worker secret /
|
|
362
|
+
Lambda env var / `--server-password` CLI flag).
|
|
363
|
+
|
|
364
|
+
### Added — `draft/read-marker` `MARKREAD` verb (`@serverless-ircd/irc-core`)
|
|
365
|
+
|
|
366
|
+
- The timestamp-based `MARKREAD` command from the read-marker draft
|
|
367
|
+
lands alongside the existing `+draft/read-marker=<msgid>` tag path.
|
|
368
|
+
`MARKREAD <target>` (GET) replies with the stored marker's timestamp;
|
|
369
|
+
`MARKREAD <target> <timestamp>` (SET) advances the marker, with
|
|
370
|
+
timestamp↔msgid translation against the message store. Failure modes
|
|
371
|
+
use `standard-replies` `FAIL MARKREAD …`. The mark fanout is
|
|
372
|
+
**scoped to the originator's account** (spec compliance), not
|
|
373
|
+
broadcast to every cap-enabled peer.
|
|
374
|
+
|
|
375
|
+
### Added — Cloudflare adapter (`@serverless-ircd/cf-adapter`)
|
|
376
|
+
|
|
377
|
+
- **Client source IP capture at WebSocket upgrade**: reads
|
|
378
|
+
`CF-Connecting-IP` (with `X-Real-IP` fallback) from the upgrade
|
|
379
|
+
request and seeds `ConnectionState.host`, so WHOIS / hostmasks carry
|
|
380
|
+
a real `@host` segment instead of an `undefined` fallback.
|
|
381
|
+
- **Ghost-member pruning via alarm**: when a WebSocket `close` event is
|
|
382
|
+
lost (client drop, edge hiccup), the DO's alarm prunes the ghost
|
|
383
|
+
member from its channels on the next sweep, preventing stale roster
|
|
384
|
+
entries.
|
|
385
|
+
|
|
386
|
+
### Changed — AWS adapter (`@serverless-ircd/aws-adapter`, `@serverless-ircd/aws-stack`)
|
|
387
|
+
|
|
388
|
+
- **O(1) admission counter**: `$connect`'s full-table `Scan COUNT` is
|
|
389
|
+
replaced with an atomic increment/decrement on a single meta row,
|
|
390
|
+
dropping admission cost from O(connections) to O(1). Reservations
|
|
391
|
+
roll back on rejection or a failed `Put`; the sweeper and stats
|
|
392
|
+
exclude the meta row.
|
|
393
|
+
- **Strongly-consistent `$default` reads + observability**: `$default`
|
|
394
|
+
now reads the `Connections` row strongly consistent (closes a
|
|
395
|
+
read-after-write `410` window), warns on a missing row, logs
|
|
396
|
+
`postToConnection` failures (previously silently swallowed), and
|
|
397
|
+
emits a summary log per invocation.
|
|
398
|
+
- **`ServerName` / `NetworkName` as CFN parameters**: server identity
|
|
399
|
+
is now a CloudFormation parameter (was env-var-only), and the
|
|
400
|
+
sweeper Lambda's `loadServerConfigFromLambdaEnv` is wired with the
|
|
401
|
+
server-identity env vars it requires. `SweeperScheduleExpression`,
|
|
402
|
+
`PingCheckerScheduleExpression`, and `ApiGatewayLoggingLevel` are
|
|
403
|
+
also exposed as parameters.
|
|
404
|
+
- **WebSocket subprotocol echo fixed**: API Gateway WebSocket
|
|
405
|
+
`IntegrationResponse` uses `route.response.header.*` (not
|
|
406
|
+
`method.response.header.*`, which is REST API only), so the
|
|
407
|
+
`Sec-WebSocket-Protocol` header was not being echoed back to clients.
|
|
408
|
+
AWS now correctly negotiates the IRCv3 subprotocol. Subprotocol
|
|
409
|
+
advertisement is forced text-only (`text.ircv3.net`) to match the
|
|
410
|
+
Lambda response-stream path.
|
|
411
|
+
- **APIGW logging role corrected** and Biome lint/format applied.
|
|
412
|
+
|
|
413
|
+
### Changed
|
|
414
|
+
|
|
415
|
+
- **`MODE +o` echo on oper-up**: the connection that grants oper now
|
|
416
|
+
receives a `MODE` echo, matching the cross-connection oper-up path.
|
|
417
|
+
- **NICK change broadcast fix**: nick changes now broadcast to every
|
|
418
|
+
shared-channel peer (a code path that dropped peers under certain
|
|
419
|
+
roster shapes is corrected).
|
|
420
|
+
- **Server version from `package.json`**: the default `serverVersion`
|
|
421
|
+
advertised in `002`/`003` is derived from the workspace
|
|
422
|
+
`package.json` at build time (was a hardcoded placeholder when the
|
|
423
|
+
config omitted it).
|
|
424
|
+
- **Web client SPA path `/app/` → `/webclient/`**: the vendored Kiwi
|
|
425
|
+
IRC SPA is now served at `/webclient/` (the `[assets]` publicPath
|
|
426
|
+
and config are repointed). Operators with custom Kiwi configs or
|
|
427
|
+
bookmarks must update to `/webclient/`.
|
|
428
|
+
- **Source / issue links migrated to Gitea**; landing-page copy
|
|
429
|
+
tightened and the upstream-install guard hardened.
|
|
430
|
+
|
|
431
|
+
### Changed — CI / tooling
|
|
432
|
+
|
|
433
|
+
- `actions/setup-java` bumped to v5; pnpm pinned to 11.20.0. CI jobs
|
|
434
|
+
are bounded by explicit timeouts and the unreachable pnpm cache step
|
|
435
|
+
is dropped.
|
|
436
|
+
|
|
437
|
+
### ⚠️ Migration required
|
|
438
|
+
|
|
439
|
+
- **Cloudflare: services are enabled when `ACCOUNTS_DB` is bound.** The
|
|
440
|
+
D1 binding is the same one introduced in v0.4.0 for SASL accounts;
|
|
441
|
+
the ten services tables **self-provision on cold start** (no manual
|
|
442
|
+
`wrangler d1 execute` needed). If `ACCOUNTS_DB` is already bound,
|
|
443
|
+
services are now live after deploy — no action. If it is unset,
|
|
444
|
+
services commands reply `501` and the daemon is otherwise unchanged.
|
|
445
|
+
- **AWS: new `Services` DynamoDB table + `SERVICES_TABLE`.** To enable
|
|
446
|
+
services, supply a services table name (`SERVICES_TABLE` env var /
|
|
447
|
+
CDK `servicesTableName` prop); the table is created by the CDK stack.
|
|
448
|
+
Without it, services are unbound.
|
|
449
|
+
- **NickServ / SASL credentials are now scrypt-hashed at rest.** A
|
|
450
|
+
NickServ `REGISTER` creates the same hashed credential shape SASL
|
|
451
|
+
uses; the `password` column on the NickServ row is replaced by
|
|
452
|
+
`algorithm` / `salt` / `hash`. Existing seeded SASL accounts are
|
|
453
|
+
adopted as registered nicks idempotently. Operators re-seeding via
|
|
454
|
+
`tools/seed-*-accounts.ts` see no change (the tools already hashed).
|
|
455
|
+
- **Web client SPA moved to `/webclient/`.** Update any bookmarks,
|
|
456
|
+
custom Kiwi configs, or `WEB_ORIGINS` entries pointing at `/app/`.
|
|
457
|
+
- **No persisted-DO-state schema change.** `PERSISTED_STATE_VERSION`
|
|
458
|
+
stays `1`. The new read-only user mode `r` (registered) and the
|
|
459
|
+
services channel modes `r`/`R`/`M` are runtime state, not persisted
|
|
460
|
+
DO fields; the `ReadMarkerStore` fold moves read-markers into the
|
|
461
|
+
services backend (D1/DynamoDB), out of DO storage. **Code rollback
|
|
462
|
+
across this release is safe.**
|
|
463
|
+
- No toolchain change. Node ≥ 24 / pnpm 11 (from v0.4.0) still apply.
|
|
464
|
+
|
|
465
|
+
### Known limitations
|
|
466
|
+
|
|
467
|
+
- **Services are not binary-compatible with Atheme or Anope.** There is
|
|
468
|
+
no S2S protocol, so an existing Atheme/Anope services process cannot
|
|
469
|
+
connect, and existing services databases cannot be imported.
|
|
470
|
+
Operators migrating from a services-aware network must re-register
|
|
471
|
+
nicks and channels. See `docs/Services.md` §12.
|
|
472
|
+
- **The web client remains partial.** The Playwright headless-browser
|
|
473
|
+
e2e against the deployed staging frontend is still pending; the SPA
|
|
474
|
+
is exercised via the WS smoke (`scripts/smoke.mjs`) and the `apps/web`
|
|
475
|
+
unit / build-smoke suite. The web client remains Cloudflare-only (the
|
|
476
|
+
`[assets]` binding is a Worker feature).
|
|
477
|
+
- Same transport limits as v0.7.0: the **CF TCP path requires
|
|
478
|
+
Cloudflare Spectrum (Enterprise tier)** plus a stateful Container
|
|
479
|
+
origin; the **AWS TCP path** uses NLB + Lambda streaming and is
|
|
480
|
+
subject to Lambda idle-timeout / stream-duration limits. The
|
|
481
|
+
WebSocket path remains the zero-extra-deps default.
|
|
482
|
+
- **mTLS (for SASL `EXTERNAL`) requires a custom domain + uploaded
|
|
483
|
+
CA/trust-store** on both platforms and is not enabled by default.
|
|
484
|
+
- **Client compatibility sweep** (WeeChat / HexChat / IRCCloud /
|
|
485
|
+
TheLounge / matrix-IRC bridge) remains pending.
|
|
486
|
+
- **Coverage hardening is partial.** `aws-adapter` and `aws-stack`
|
|
487
|
+
clear the 90% gate; the remaining packages (`cf-adapter`,
|
|
488
|
+
`local-cli`, `load-test`, `cf-tcp-container`, `tcp-ws-forwarder`,
|
|
489
|
+
`irc-test-support`, `web`) are **above the gate but below 100%** —
|
|
490
|
+
follow-ups drive each to 100%.
|
|
491
|
+
|
|
492
|
+
### Security
|
|
493
|
+
|
|
494
|
+
- **NickServ / SASL credentials are now uniformly scrypt-hashed.** The
|
|
495
|
+
unified account store means a NickServ-registered nick and a SASL
|
|
496
|
+
account share one hashed credential — never plaintext, in memory or
|
|
497
|
+
at rest. The server-password gate and CSWSH defense are unchanged
|
|
498
|
+
from v0.7.0; no other auth path changed.
|
|
499
|
+
|
|
500
|
+
---
|
|
501
|
+
|
|
13
502
|
## [0.7.0] - 2026-08-04
|
|
14
503
|
|
|
15
|
-
The
|
|
504
|
+
The main change in this release is the **web client**: a vendored **Kiwi IRC**
|
|
16
505
|
SPA served at `/app/` and a static project landing page served at `/`
|
|
17
506
|
directly by the Cloudflare Worker, with **Cross-Site WebSocket
|
|
18
507
|
Hijacking (CSWSH) defense** wired into the WS upgrade path. The browser
|
|
19
|
-
opens a native `wss://`
|
|
20
|
-
endpoint — no proxy, gateway, or transport adaptation in the
|
|
508
|
+
opens a native `wss://` directly to the IRC-over-WebSocket
|
|
509
|
+
endpoint — no proxy, gateway, or transport adaptation in the request path.
|
|
21
510
|
Alongside it: a round of **test hardening** (mutation-killing tests for
|
|
22
511
|
`irc-core`'s parser / batch / numerics, and CF-harness / scenario fixes
|
|
23
512
|
for slow transports).
|
|
24
513
|
|
|
25
514
|
### Added — Web client (`@serverless-ircd/web`, `@serverless-ircd/cf-worker`)
|
|
26
515
|
|
|
27
|
-
Six
|
|
516
|
+
Six tickets add the project's browser surface. The Worker
|
|
28
517
|
becomes the single origin for the landing page, the SPA, **and** the
|
|
29
518
|
WebSocket. Same-origin by default — which is what makes the CSWSH
|
|
30
519
|
defense almost zero-config.
|
|
@@ -67,7 +556,7 @@ defense almost zero-config.
|
|
|
67
556
|
with a `200 OK` (the WS endpoint and the landing page share path
|
|
68
557
|
`/`; only the Worker can tell them apart).
|
|
69
558
|
- **Static landing page** (`apps/web/landing/index.html`): framework-
|
|
70
|
-
free HTML + inline CSS, no JavaScript. The project's
|
|
559
|
+
free HTML + inline CSS, no JavaScript. The project's entry page —
|
|
71
560
|
explains what ServerlessIRCd is, links to `/app/` (the IRC client),
|
|
72
561
|
the docs, and the source repo. The build copies it verbatim to
|
|
73
562
|
`dist/index.html`. Renders without JS; responsive; the build-smoke
|
|
@@ -182,7 +671,7 @@ contributor can deploy a staging SPA following only this doc.
|
|
|
182
671
|
|
|
183
672
|
### Known limitations
|
|
184
673
|
|
|
185
|
-
- **
|
|
674
|
+
- **The web client is partial.** The Playwright headless-browser e2e
|
|
186
675
|
against the deployed staging frontend remains pending
|
|
187
676
|
— the SPA is exercised via the existing WS smoke
|
|
188
677
|
(`scripts/smoke.mjs`) and the new `apps/web` unit / build-smoke
|
|
@@ -206,7 +695,7 @@ contributor can deploy a staging SPA following only this doc.
|
|
|
206
695
|
(`cf-adapter`, `local-cli`, `load-test`, `cf-tcp-container`,
|
|
207
696
|
`tcp-ws-forwarder`, `irc-test-support`, and now `web`) are **above
|
|
208
697
|
the gate but below 100%** — follow-ups drive each to 100%.
|
|
209
|
-
- The IRC services
|
|
698
|
+
- The IRC services work (NickServ / ChanServ / HostServ /
|
|
210
699
|
OperServ / MemoServ) is pending; `draft/read-marker` and
|
|
211
700
|
`draft/pre-away` ship with in-memory reference stores, so marker /
|
|
212
701
|
away persistence does not survive an adapter restart until a
|
|
@@ -226,20 +715,20 @@ contributor can deploy a staging SPA following only this doc.
|
|
|
226
715
|
|
|
227
716
|
## [0.6.0] - 2026-08-03
|
|
228
717
|
|
|
229
|
-
The
|
|
718
|
+
The main change in this release is the **IRCv3 extension sweep**: ten negotiated
|
|
230
719
|
caps and the user-mode-`S` TLS indicator land in one release, taking the
|
|
231
720
|
negotiated capability surface from "modern baseline" to "draft-leading-
|
|
232
721
|
edge". Alongside it: the **required-`serverName` config gate** (the
|
|
233
|
-
last open v0.5.0 follow-up), a new **`tools/load-test`** client
|
|
234
|
-
(
|
|
722
|
+
last open v0.5.0 follow-up), a new **`tools/load-test`** client pool
|
|
723
|
+
(the 10k-connection harness), and the **coverage / CI unblocks**
|
|
235
724
|
that lift `aws-adapter` over the 90% gate and switch `cf-adapter` /
|
|
236
725
|
`cf-worker` to istanbul (clearing the v0.5.0 workerd-incompatibility
|
|
237
726
|
caveat). Deploy workflows are now **manual-only** (`workflow_dispatch`).
|
|
238
727
|
|
|
239
728
|
### Added — IRCv3 extensions (`@serverless-ircd/irc-core`, `@serverless-ircd/irc-server`)
|
|
240
729
|
|
|
241
|
-
The ten
|
|
242
|
-
surface, and the two non-cap
|
|
730
|
+
The ten cap tickets add the remaining negotiated-cap
|
|
731
|
+
surface, and the two non-cap tickets (ISUPPORT tokens and
|
|
243
732
|
user mode `S`) round out the wire-visible advertisement. Each cap
|
|
244
733
|
ships a pure reducer (where applicable), an actor route case,
|
|
245
734
|
advertisement in `SUPPORTED_CAPABILITIES` (or conditional
|
|
@@ -349,10 +838,10 @@ with a readable, field-listing error.
|
|
|
349
838
|
|
|
350
839
|
### Added — Load-test harness (`@serverless-ircd/load-test`)
|
|
351
840
|
|
|
352
|
-
PLAN §7
|
|
353
|
-
client
|
|
841
|
+
The PLAN §7 10k-connection harness. A synthetic WebSocket IRC
|
|
842
|
+
client pool: opens N connections, registers (NICK/USER), joins a
|
|
354
843
|
channel, and optionally chats. Captures **p50 / p95 / p99 latency per
|
|
355
|
-
|
|
844
|
+
stage** (connect / register / join / message) plus the **drop rate**,
|
|
356
845
|
and prints a markdown report to stdout (or `--report <path>`).
|
|
357
846
|
|
|
358
847
|
```bash
|
|
@@ -467,11 +956,11 @@ resolves it.
|
|
|
467
956
|
`irc-test-support`) are **above the gate but below 100%** —
|
|
468
957
|
follow-ups drive each to 100%.
|
|
469
958
|
- The IRC services (NickServ / ChanServ / HostServ / OperServ /
|
|
470
|
-
MemoServ)
|
|
959
|
+
MemoServ) work is pending; `draft/read-marker` and
|
|
471
960
|
`draft/pre-away` ship with in-memory reference stores, so marker /
|
|
472
961
|
away persistence does not survive an adapter restart until a
|
|
473
962
|
services backend lands.
|
|
474
|
-
- The web client
|
|
963
|
+
- The web client work (vendor Kiwi IRC, serve SPA from the
|
|
475
964
|
Worker, WS Origin allowlist) is pending.
|
|
476
965
|
|
|
477
966
|
### Security
|
|
@@ -486,8 +975,8 @@ resolves it.
|
|
|
486
975
|
|
|
487
976
|
## [0.5.0] - 2026-08-02
|
|
488
977
|
|
|
489
|
-
The
|
|
490
|
-
|
|
978
|
+
The main change in this release is **protocol completion**: the deferred IRC verbs land,
|
|
979
|
+
and the S2S / obsolete RFC 2812 verbs are **formally
|
|
491
980
|
dropped** rather than left "deferred". With this release every standard
|
|
492
981
|
IRC verb a modern client expects is implemented — the only remaining
|
|
493
982
|
`421 ERR_UNKNOWNCOMMAND` paths are the deliberately-unimplemented S2S
|
|
@@ -497,10 +986,10 @@ client compatibility matrix) and the required-`serverName` config gate.
|
|
|
497
986
|
|
|
498
987
|
### Added — Deferred IRC verbs (`@serverless-ircd/irc-core`, `@serverless-ircd/irc-server`)
|
|
499
988
|
|
|
500
|
-
Six
|
|
989
|
+
Six tickets add the remaining command surface. Each ships
|
|
501
990
|
a pure reducer, an actor route case (no more `421`), new numerics in
|
|
502
991
|
`protocol/numerics.ts`, and unit + actor coverage. (The remaining two
|
|
503
|
-
|
|
992
|
+
tickets are drops — see below.)
|
|
504
993
|
|
|
505
994
|
- **`KILL`** (oper-gated force-disconnect): `KILL <nick>
|
|
506
995
|
<comment>` resolves the target via the nick registry + a live
|
|
@@ -554,7 +1043,7 @@ Phase 11 tickets are drops — see below.)
|
|
|
554
1043
|
channel-scoped). The originator is always excluded (skip-self,
|
|
555
1044
|
matching NOTICE/PRIVMSG semantics). Empty message → `461`. The `+w`
|
|
556
1045
|
user mode was already accepted by `MODE` and advertised in `004
|
|
557
|
-
RPL_MYINFO`; this
|
|
1046
|
+
RPL_MYINFO`; this enables the consumer.
|
|
558
1047
|
- **`SETNAME`** (realname change): `SETNAME :<realname>`
|
|
559
1048
|
updates `state.realname` so a subsequent `WHOIS` reflects the new
|
|
560
1049
|
value in `311 RPL_WHOISUSER`. No broadcast is emitted in v1 (there
|
|
@@ -564,7 +1053,7 @@ Phase 11 tickets are drops — see below.)
|
|
|
564
1053
|
|
|
565
1054
|
### Changed — S2S & obsolete verbs formally dropped (`@serverless-ircd/irc-core`)
|
|
566
1055
|
|
|
567
|
-
The remaining two
|
|
1056
|
+
The remaining two tickets convert the "deferred" framing into
|
|
568
1057
|
an explicit **drop**. There is no behaviour change on the wire — the verbs
|
|
569
1058
|
already returned `421 ERR_UNKNOWNCOMMAND` — but the framing, comments,
|
|
570
1059
|
and reserved numerics are cleaned up so the codebase no longer carries
|
|
@@ -661,13 +1150,13 @@ dead scaffolding.
|
|
|
661
1150
|
|
|
662
1151
|
## [0.4.0] - 2026-07-31
|
|
663
1152
|
|
|
664
|
-
The
|
|
1153
|
+
The main change in this release is **dual transport**: both adapters now speak a real
|
|
665
1154
|
`irc+tls :6697` (TLS-over-TCP) surface for stock IRC clients in
|
|
666
1155
|
production — not just the local CLI — alongside the WebSocket path. This
|
|
667
1156
|
lands **SASL `EXTERNAL` via mTLS**, the **remaining standard IRC verbs**
|
|
668
1157
|
(WHOWAS, VERSION, TIME, ADMIN, INFO, USERHOST, ISON), a **Cloudflare
|
|
669
1158
|
D1-backed SASL account store**, config-driven server identity, and a
|
|
670
|
-
**Node 24 / pnpm 11** toolchain.
|
|
1159
|
+
**Node 24 / pnpm 11** toolchain. TLS hardening & raw TCP is
|
|
671
1160
|
complete.
|
|
672
1161
|
|
|
673
1162
|
### Added — Dual transport: WebSocket + `irc+tls :6697`
|
|
@@ -737,7 +1226,7 @@ pipeline is unchanged.
|
|
|
737
1226
|
|
|
738
1227
|
- The `002` / `003` / `004` registration replies now reflect
|
|
739
1228
|
`ServerConfig.serverVersion` / `createdAt` — the hardcoded
|
|
740
|
-
`'0.1.0'` /
|
|
1229
|
+
`'0.1.0'` version / placeholder strings are gone. A build step
|
|
741
1230
|
(`scripts/generate-build-info.mjs`) bakes the package version and a
|
|
742
1231
|
build timestamp into `RPL_CREATED` (`003`) when the config omits them.
|
|
743
1232
|
|
|
@@ -1005,7 +1494,7 @@ AWS are now both deployable.
|
|
|
1005
1494
|
### Added — Shared test harness (`@serverless-ircd/irc-test-support`)
|
|
1006
1495
|
|
|
1007
1496
|
- New workspace package owning the parametrized IRC scenario suite
|
|
1008
|
-
(`runIrcScenarios`, 31
|
|
1497
|
+
(`runIrcScenarios`, 31 scenarios) and the
|
|
1009
1498
|
`IrcHarnessFactory` / `IrcHarness` / `ClientHarness` seam each
|
|
1010
1499
|
adapter implements. Ships the reference `inMemoryHarnessFactory`.
|
|
1011
1500
|
cf-adapter and aws-adapter integration tests both consume it, so the
|
|
@@ -1036,7 +1525,7 @@ AWS are now both deployable.
|
|
|
1036
1525
|
end-to-end rather than advertised-only.
|
|
1037
1526
|
- The local CLI now binds a **second RFC-style TCP listener** by default
|
|
1038
1527
|
(`--tcp-port`, default `<port> + 1`) alongside the WebSocket listener,
|
|
1039
|
-
so stock IRC clients can
|
|
1528
|
+
so stock IRC clients can connect directly without the forwarder.
|
|
1040
1529
|
Pass `--no-tcp` for WebSocket-only. `--motd-file` loads the MOTD from
|
|
1041
1530
|
disk.
|
|
1042
1531
|
|