serverless-ircd 0.7.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 +3 -3
- package/.gitmodules +1 -1
- package/CHANGELOG.md +273 -29
- package/README.md +155 -55
- 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/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 +1 -0
- package/apps/local-cli/tests/config-loader.test.ts +14 -0
- package/apps/web/landing/index.html +14 -16
- package/apps/web/package.json +2 -2
- package/apps/web/scripts/build.mjs +23 -16
- package/apps/web/src/build-env.ts +1 -1
- package/apps/web/src/config-schema.ts +6 -6
- package/apps/web/tests/build-smoke.test.ts +14 -14
- package/apps/web/tests/config-schema.test.ts +1 -1
- 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 +546 -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 +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/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/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/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/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/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/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/.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,7 +146,6 @@ 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
|
package/.gitmodules
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -7,24 +7,268 @@ 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
|
+
|
|
13
257
|
## [0.7.0] - 2026-08-04
|
|
14
258
|
|
|
15
|
-
The
|
|
259
|
+
The main change in this release is the **web client**: a vendored **Kiwi IRC**
|
|
16
260
|
SPA served at `/app/` and a static project landing page served at `/`
|
|
17
261
|
directly by the Cloudflare Worker, with **Cross-Site WebSocket
|
|
18
262
|
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
|
|
263
|
+
opens a native `wss://` directly to the IRC-over-WebSocket
|
|
264
|
+
endpoint — no proxy, gateway, or transport adaptation in the request path.
|
|
21
265
|
Alongside it: a round of **test hardening** (mutation-killing tests for
|
|
22
266
|
`irc-core`'s parser / batch / numerics, and CF-harness / scenario fixes
|
|
23
267
|
for slow transports).
|
|
24
268
|
|
|
25
269
|
### Added — Web client (`@serverless-ircd/web`, `@serverless-ircd/cf-worker`)
|
|
26
270
|
|
|
27
|
-
Six
|
|
271
|
+
Six tickets add the project's browser surface. The Worker
|
|
28
272
|
becomes the single origin for the landing page, the SPA, **and** the
|
|
29
273
|
WebSocket. Same-origin by default — which is what makes the CSWSH
|
|
30
274
|
defense almost zero-config.
|
|
@@ -67,7 +311,7 @@ defense almost zero-config.
|
|
|
67
311
|
with a `200 OK` (the WS endpoint and the landing page share path
|
|
68
312
|
`/`; only the Worker can tell them apart).
|
|
69
313
|
- **Static landing page** (`apps/web/landing/index.html`): framework-
|
|
70
|
-
free HTML + inline CSS, no JavaScript. The project's
|
|
314
|
+
free HTML + inline CSS, no JavaScript. The project's entry page —
|
|
71
315
|
explains what ServerlessIRCd is, links to `/app/` (the IRC client),
|
|
72
316
|
the docs, and the source repo. The build copies it verbatim to
|
|
73
317
|
`dist/index.html`. Renders without JS; responsive; the build-smoke
|
|
@@ -182,7 +426,7 @@ contributor can deploy a staging SPA following only this doc.
|
|
|
182
426
|
|
|
183
427
|
### Known limitations
|
|
184
428
|
|
|
185
|
-
- **
|
|
429
|
+
- **The web client is partial.** The Playwright headless-browser e2e
|
|
186
430
|
against the deployed staging frontend remains pending
|
|
187
431
|
— the SPA is exercised via the existing WS smoke
|
|
188
432
|
(`scripts/smoke.mjs`) and the new `apps/web` unit / build-smoke
|
|
@@ -206,7 +450,7 @@ contributor can deploy a staging SPA following only this doc.
|
|
|
206
450
|
(`cf-adapter`, `local-cli`, `load-test`, `cf-tcp-container`,
|
|
207
451
|
`tcp-ws-forwarder`, `irc-test-support`, and now `web`) are **above
|
|
208
452
|
the gate but below 100%** — follow-ups drive each to 100%.
|
|
209
|
-
- The IRC services
|
|
453
|
+
- The IRC services work (NickServ / ChanServ / HostServ /
|
|
210
454
|
OperServ / MemoServ) is pending; `draft/read-marker` and
|
|
211
455
|
`draft/pre-away` ship with in-memory reference stores, so marker /
|
|
212
456
|
away persistence does not survive an adapter restart until a
|
|
@@ -226,20 +470,20 @@ contributor can deploy a staging SPA following only this doc.
|
|
|
226
470
|
|
|
227
471
|
## [0.6.0] - 2026-08-03
|
|
228
472
|
|
|
229
|
-
The
|
|
473
|
+
The main change in this release is the **IRCv3 extension sweep**: ten negotiated
|
|
230
474
|
caps and the user-mode-`S` TLS indicator land in one release, taking the
|
|
231
475
|
negotiated capability surface from "modern baseline" to "draft-leading-
|
|
232
476
|
edge". Alongside it: the **required-`serverName` config gate** (the
|
|
233
|
-
last open v0.5.0 follow-up), a new **`tools/load-test`** client
|
|
234
|
-
(
|
|
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**
|
|
235
479
|
that lift `aws-adapter` over the 90% gate and switch `cf-adapter` /
|
|
236
480
|
`cf-worker` to istanbul (clearing the v0.5.0 workerd-incompatibility
|
|
237
481
|
caveat). Deploy workflows are now **manual-only** (`workflow_dispatch`).
|
|
238
482
|
|
|
239
483
|
### Added — IRCv3 extensions (`@serverless-ircd/irc-core`, `@serverless-ircd/irc-server`)
|
|
240
484
|
|
|
241
|
-
The ten
|
|
242
|
-
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
|
|
243
487
|
user mode `S`) round out the wire-visible advertisement. Each cap
|
|
244
488
|
ships a pure reducer (where applicable), an actor route case,
|
|
245
489
|
advertisement in `SUPPORTED_CAPABILITIES` (or conditional
|
|
@@ -349,10 +593,10 @@ with a readable, field-listing error.
|
|
|
349
593
|
|
|
350
594
|
### Added — Load-test harness (`@serverless-ircd/load-test`)
|
|
351
595
|
|
|
352
|
-
PLAN §7
|
|
353
|
-
client
|
|
596
|
+
The PLAN §7 10k-connection harness. A synthetic WebSocket IRC
|
|
597
|
+
client pool: opens N connections, registers (NICK/USER), joins a
|
|
354
598
|
channel, and optionally chats. Captures **p50 / p95 / p99 latency per
|
|
355
|
-
|
|
599
|
+
stage** (connect / register / join / message) plus the **drop rate**,
|
|
356
600
|
and prints a markdown report to stdout (or `--report <path>`).
|
|
357
601
|
|
|
358
602
|
```bash
|
|
@@ -467,11 +711,11 @@ resolves it.
|
|
|
467
711
|
`irc-test-support`) are **above the gate but below 100%** —
|
|
468
712
|
follow-ups drive each to 100%.
|
|
469
713
|
- The IRC services (NickServ / ChanServ / HostServ / OperServ /
|
|
470
|
-
MemoServ)
|
|
714
|
+
MemoServ) work is pending; `draft/read-marker` and
|
|
471
715
|
`draft/pre-away` ship with in-memory reference stores, so marker /
|
|
472
716
|
away persistence does not survive an adapter restart until a
|
|
473
717
|
services backend lands.
|
|
474
|
-
- The web client
|
|
718
|
+
- The web client work (vendor Kiwi IRC, serve SPA from the
|
|
475
719
|
Worker, WS Origin allowlist) is pending.
|
|
476
720
|
|
|
477
721
|
### Security
|
|
@@ -486,8 +730,8 @@ resolves it.
|
|
|
486
730
|
|
|
487
731
|
## [0.5.0] - 2026-08-02
|
|
488
732
|
|
|
489
|
-
The
|
|
490
|
-
|
|
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
|
|
491
735
|
dropped** rather than left "deferred". With this release every standard
|
|
492
736
|
IRC verb a modern client expects is implemented — the only remaining
|
|
493
737
|
`421 ERR_UNKNOWNCOMMAND` paths are the deliberately-unimplemented S2S
|
|
@@ -497,10 +741,10 @@ client compatibility matrix) and the required-`serverName` config gate.
|
|
|
497
741
|
|
|
498
742
|
### Added — Deferred IRC verbs (`@serverless-ircd/irc-core`, `@serverless-ircd/irc-server`)
|
|
499
743
|
|
|
500
|
-
Six
|
|
744
|
+
Six tickets add the remaining command surface. Each ships
|
|
501
745
|
a pure reducer, an actor route case (no more `421`), new numerics in
|
|
502
746
|
`protocol/numerics.ts`, and unit + actor coverage. (The remaining two
|
|
503
|
-
|
|
747
|
+
tickets are drops — see below.)
|
|
504
748
|
|
|
505
749
|
- **`KILL`** (oper-gated force-disconnect): `KILL <nick>
|
|
506
750
|
<comment>` resolves the target via the nick registry + a live
|
|
@@ -554,7 +798,7 @@ Phase 11 tickets are drops — see below.)
|
|
|
554
798
|
channel-scoped). The originator is always excluded (skip-self,
|
|
555
799
|
matching NOTICE/PRIVMSG semantics). Empty message → `461`. The `+w`
|
|
556
800
|
user mode was already accepted by `MODE` and advertised in `004
|
|
557
|
-
RPL_MYINFO`; this
|
|
801
|
+
RPL_MYINFO`; this enables the consumer.
|
|
558
802
|
- **`SETNAME`** (realname change): `SETNAME :<realname>`
|
|
559
803
|
updates `state.realname` so a subsequent `WHOIS` reflects the new
|
|
560
804
|
value in `311 RPL_WHOISUSER`. No broadcast is emitted in v1 (there
|
|
@@ -564,7 +808,7 @@ Phase 11 tickets are drops — see below.)
|
|
|
564
808
|
|
|
565
809
|
### Changed — S2S & obsolete verbs formally dropped (`@serverless-ircd/irc-core`)
|
|
566
810
|
|
|
567
|
-
The remaining two
|
|
811
|
+
The remaining two tickets convert the "deferred" framing into
|
|
568
812
|
an explicit **drop**. There is no behaviour change on the wire — the verbs
|
|
569
813
|
already returned `421 ERR_UNKNOWNCOMMAND` — but the framing, comments,
|
|
570
814
|
and reserved numerics are cleaned up so the codebase no longer carries
|
|
@@ -661,13 +905,13 @@ dead scaffolding.
|
|
|
661
905
|
|
|
662
906
|
## [0.4.0] - 2026-07-31
|
|
663
907
|
|
|
664
|
-
The
|
|
908
|
+
The main change in this release is **dual transport**: both adapters now speak a real
|
|
665
909
|
`irc+tls :6697` (TLS-over-TCP) surface for stock IRC clients in
|
|
666
910
|
production — not just the local CLI — alongside the WebSocket path. This
|
|
667
911
|
lands **SASL `EXTERNAL` via mTLS**, the **remaining standard IRC verbs**
|
|
668
912
|
(WHOWAS, VERSION, TIME, ADMIN, INFO, USERHOST, ISON), a **Cloudflare
|
|
669
913
|
D1-backed SASL account store**, config-driven server identity, and a
|
|
670
|
-
**Node 24 / pnpm 11** toolchain.
|
|
914
|
+
**Node 24 / pnpm 11** toolchain. TLS hardening & raw TCP is
|
|
671
915
|
complete.
|
|
672
916
|
|
|
673
917
|
### Added — Dual transport: WebSocket + `irc+tls :6697`
|
|
@@ -737,7 +981,7 @@ pipeline is unchanged.
|
|
|
737
981
|
|
|
738
982
|
- The `002` / `003` / `004` registration replies now reflect
|
|
739
983
|
`ServerConfig.serverVersion` / `createdAt` — the hardcoded
|
|
740
|
-
`'0.1.0'` /
|
|
984
|
+
`'0.1.0'` version / placeholder strings are gone. A build step
|
|
741
985
|
(`scripts/generate-build-info.mjs`) bakes the package version and a
|
|
742
986
|
build timestamp into `RPL_CREATED` (`003`) when the config omits them.
|
|
743
987
|
|
|
@@ -1005,7 +1249,7 @@ AWS are now both deployable.
|
|
|
1005
1249
|
### Added — Shared test harness (`@serverless-ircd/irc-test-support`)
|
|
1006
1250
|
|
|
1007
1251
|
- New workspace package owning the parametrized IRC scenario suite
|
|
1008
|
-
(`runIrcScenarios`, 31
|
|
1252
|
+
(`runIrcScenarios`, 31 scenarios) and the
|
|
1009
1253
|
`IrcHarnessFactory` / `IrcHarness` / `ClientHarness` seam each
|
|
1010
1254
|
adapter implements. Ships the reference `inMemoryHarnessFactory`.
|
|
1011
1255
|
cf-adapter and aws-adapter integration tests both consume it, so the
|
|
@@ -1036,7 +1280,7 @@ AWS are now both deployable.
|
|
|
1036
1280
|
end-to-end rather than advertised-only.
|
|
1037
1281
|
- The local CLI now binds a **second RFC-style TCP listener** by default
|
|
1038
1282
|
(`--tcp-port`, default `<port> + 1`) alongside the WebSocket listener,
|
|
1039
|
-
so stock IRC clients can
|
|
1283
|
+
so stock IRC clients can connect directly without the forwarder.
|
|
1040
1284
|
Pass `--no-tcp` for WebSocket-only. `--motd-file` loads the MOTD from
|
|
1041
1285
|
disk.
|
|
1042
1286
|
|