serverless-ircd 0.5.0 → 0.6.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 +34 -2
- package/.github/workflows/deploy-aws.yml +1 -2
- package/.github/workflows/deploy-cf-tcp.yml +1 -5
- package/.github/workflows/deploy-cf.yml +1 -2
- package/CHANGELOG.md +260 -0
- package/README.md +132 -22
- package/apps/aws-stack/README.md +33 -0
- package/apps/aws-stack/package.json +1 -1
- package/apps/aws-stack/src/aws-stack.ts +74 -9
- package/apps/aws-stack/tests/coverage-command.test.ts +155 -0
- package/apps/aws-stack/tests/stack.test.ts +2 -2
- package/apps/aws-stack/tests/synth-no-bundle.test.ts +129 -0
- package/apps/aws-stack/tests/vitest.setup.ts +18 -0
- package/apps/aws-stack/vitest.config.ts +3 -0
- package/apps/cf-tcp-container/package.json +1 -1
- package/apps/cf-tcp-container/src/config-loader.ts +6 -7
- package/apps/cf-tcp-container/src/container-server.ts +3 -0
- package/apps/cf-tcp-container/tests/config-loader.test.ts +10 -3
- package/apps/cf-tcp-container/wrangler.toml +1 -1
- package/apps/cf-worker/package.json +2 -1
- package/apps/cf-worker/vitest.config.ts +1 -1
- package/apps/cf-worker/wrangler.toml +5 -3
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +6 -6
- package/apps/local-cli/src/main.ts +20 -0
- package/apps/local-cli/src/server.ts +9 -7
- package/apps/local-cli/tests/e2e.test.ts +112 -28
- package/apps/local-cli/tests/tcp.test.ts +71 -19
- package/apps/local-cli/tests/ws-subprotocol.test.ts +12 -7
- package/biome.json +1 -0
- package/docs/ADR-001-pure-reducers-and-effect-system.md +74 -0
- package/docs/ADR-002-location-of-authority.md +82 -0
- package/docs/ADR-003-durable-object-sharding.md +110 -0
- package/docs/ADR-004-dynamodb-schema.md +96 -0
- package/docs/ADR-005-wss-only-transport-v1.md +83 -0
- package/docs/ADR-006-sasl-mechanism-scope.md +96 -0
- package/docs/ADR-007-deterministic-ports.md +82 -0
- package/docs/ADR-008-monorepo-tooling.md +60 -0
- package/docs/ADR-009-dual-transport-wss-and-irc-tls.md +138 -0
- package/docs/ADR-Index.md +15 -0
- package/docs/AWS-Adapter-Architecture.md +496 -0
- package/docs/AWS-Deployment.md +1275 -0
- package/docs/AWS-TCP-Deployment.md +410 -0
- package/docs/Cloudflare-Deployment-Guide.md +719 -0
- package/docs/Cloudflare-TCP-Deployment.md +510 -0
- package/docs/Home.md +15 -0
- package/docs/Observability.md +87 -0
- package/docs/PlanExtensions.md +812 -0
- package/docs/PlanIRCv3Websocket.md +503 -0
- package/docs/PlanWebClient.md +451 -0
- package/docs/Release-Process.md +443 -0
- package/package.json +2 -2
- package/packages/aws-adapter/package.json +1 -1
- package/packages/aws-adapter/src/handlers/default.ts +3 -0
- package/packages/aws-adapter/src/handlers/nlb-stream.ts +9 -0
- package/packages/aws-adapter/src/serialize.ts +29 -1
- package/packages/aws-adapter/tests/account-store-dynamo.test.ts +49 -8
- package/packages/aws-adapter/tests/account-store.test.ts +4 -7
- package/packages/aws-adapter/tests/aws-harness.ts +5 -2
- package/packages/aws-adapter/tests/aws-integration.test.ts +8 -4
- package/packages/aws-adapter/tests/aws-runtime.test.ts +79 -0
- package/packages/aws-adapter/tests/config-loader.test.ts +11 -2
- package/packages/aws-adapter/tests/connect.test.ts +9 -9
- package/packages/aws-adapter/tests/disconnect-fanout.test.ts +3 -4
- package/packages/aws-adapter/tests/global-setup.ts +55 -30
- package/packages/aws-adapter/tests/gone-exception.test.ts +4 -9
- package/packages/aws-adapter/tests/handlers.test.ts +90 -4
- package/packages/aws-adapter/tests/message-store.test.ts +6 -5
- package/packages/aws-adapter/tests/nlb-stream.test.ts +60 -8
- package/packages/cf-adapter/package.json +2 -1
- package/packages/cf-adapter/src/connection-do.ts +3 -0
- package/packages/cf-adapter/src/serialize.ts +25 -4
- package/packages/cf-adapter/tests/cf-runtime.test.ts +269 -0
- package/packages/cf-adapter/tests/channel-do.test.ts +117 -0
- package/packages/cf-adapter/tests/config-loader.test.ts +49 -0
- package/packages/cf-adapter/tests/connection-do-pure.test.ts +91 -0
- package/packages/cf-adapter/tests/d1-account-store.test.ts +48 -0
- package/packages/cf-adapter/tests/registry-do.test.ts +104 -0
- package/packages/cf-adapter/tests/serialize.test.ts +308 -0
- package/packages/cf-adapter/vitest.config.ts +1 -1
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/irc-core/package.json +1 -1
- package/packages/irc-core/src/caps/capabilities.ts +37 -3
- package/packages/irc-core/src/caps/index.ts +1 -0
- package/packages/irc-core/src/caps/sts.ts +84 -0
- package/packages/irc-core/src/commands/account-notify.ts +53 -0
- package/packages/irc-core/src/commands/away.ts +9 -3
- package/packages/irc-core/src/commands/cap.ts +23 -5
- package/packages/irc-core/src/commands/chathistory.ts +40 -11
- package/packages/irc-core/src/commands/index.ts +34 -0
- package/packages/irc-core/src/commands/isupport.ts +53 -0
- package/packages/irc-core/src/commands/mode.ts +8 -1
- package/packages/irc-core/src/commands/monitor.ts +327 -0
- package/packages/irc-core/src/commands/multiline.ts +256 -0
- package/packages/irc-core/src/commands/pre-away.ts +112 -0
- package/packages/irc-core/src/commands/privmsg.ts +19 -12
- package/packages/irc-core/src/commands/read-marker.ts +108 -0
- package/packages/irc-core/src/commands/registration.ts +8 -0
- package/packages/irc-core/src/commands/sasl.ts +24 -1
- package/packages/irc-core/src/commands/tagmsg.ts +71 -1
- package/packages/irc-core/src/commands/whois.ts +5 -0
- package/packages/irc-core/src/config.ts +65 -10
- package/packages/irc-core/src/effects.ts +14 -0
- package/packages/irc-core/src/ports.ts +389 -0
- package/packages/irc-core/src/protocol/index.ts +14 -0
- package/packages/irc-core/src/protocol/labeled-response.ts +133 -0
- package/packages/irc-core/src/protocol/numerics.ts +15 -0
- package/packages/irc-core/src/protocol/outbound.ts +16 -1
- package/packages/irc-core/src/protocol/standard-replies.ts +277 -0
- package/packages/irc-core/src/state/connection.ts +32 -1
- package/packages/irc-core/src/types.ts +112 -0
- package/packages/irc-core/stryker.commands.conf.json +1 -2
- package/packages/irc-core/tests/away-store.test.ts +73 -0
- package/packages/irc-core/tests/caps/capabilities.test.ts +119 -2
- package/packages/irc-core/tests/caps/sts.test.ts +123 -0
- package/packages/irc-core/tests/commands/account-notify.test.ts +171 -0
- package/packages/irc-core/tests/commands/away.test.ts +85 -2
- package/packages/irc-core/tests/commands/cap.test.ts +165 -0
- package/packages/irc-core/tests/commands/chathistory.test.ts +148 -1
- package/packages/irc-core/tests/commands/echo-message.test.ts +9 -3
- package/packages/irc-core/tests/commands/isupport.test.ts +80 -0
- package/packages/irc-core/tests/commands/mode.test.ts +72 -0
- package/packages/irc-core/tests/commands/monitor.test.ts +411 -0
- package/packages/irc-core/tests/commands/multiline.test.ts +428 -0
- package/packages/irc-core/tests/commands/pre-away.test.ts +241 -0
- package/packages/irc-core/tests/commands/privmsg.test.ts +154 -3
- package/packages/irc-core/tests/commands/read-marker.test.ts +211 -0
- package/packages/irc-core/tests/commands/registration.test.ts +64 -0
- package/packages/irc-core/tests/commands/sasl.test.ts +116 -2
- package/packages/irc-core/tests/commands/tagmsg.test.ts +357 -0
- package/packages/irc-core/tests/commands/whois.test.ts +34 -0
- package/packages/irc-core/tests/config.test.ts +103 -13
- package/packages/irc-core/tests/labeled-response.test.ts +181 -0
- package/packages/irc-core/tests/message-tags.test.ts +114 -0
- package/packages/irc-core/tests/monitor-store.test.ts +200 -0
- package/packages/irc-core/tests/read-marker-store.test.ts +108 -0
- package/packages/irc-core/tests/standard-replies.test.ts +412 -0
- package/packages/irc-core/tests/types.test.ts +27 -0
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +450 -3
- package/packages/irc-server/src/dispatch.ts +108 -16
- package/packages/irc-server/tests/actor.test.ts +1432 -39
- package/packages/irc-server/tests/dispatch.test.ts +263 -2
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/index.ts +6 -0
- package/packages/irc-test-support/src/test-config.ts +54 -0
- package/packages/irc-test-support/tests/test-config.test.ts +51 -0
- package/tools/ci-hardening/package.json +1 -1
- package/tools/ci-hardening/tests/ddb-local.test.ts +82 -0
- package/tools/load-test/package.json +33 -0
- package/tools/load-test/src/client.ts +351 -0
- package/tools/load-test/src/config.ts +313 -0
- package/tools/load-test/src/harness.ts +116 -0
- package/tools/load-test/src/main.ts +120 -0
- package/tools/load-test/src/metrics.ts +168 -0
- package/tools/load-test/src/report.ts +106 -0
- package/tools/load-test/tests/client.test.ts +212 -0
- package/tools/load-test/tests/config.test.ts +152 -0
- package/tools/load-test/tests/framing.test.ts +37 -0
- package/tools/load-test/tests/harness.test.ts +165 -0
- package/tools/load-test/tests/metrics.test.ts +174 -0
- package/tools/load-test/tests/report.test.ts +161 -0
- package/tools/load-test/tests/smoke.test.ts +67 -0
- package/tools/load-test/tsconfig.build.json +12 -0
- package/tools/load-test/tsconfig.test.json +10 -0
- package/tools/load-test/vitest.config.ts +29 -0
- package/tools/tcp-ws-forwarder/package.json +1 -1
package/.github/workflows/ci.yml
CHANGED
|
@@ -42,6 +42,32 @@ jobs:
|
|
|
42
42
|
node-version: 24
|
|
43
43
|
cache: pnpm
|
|
44
44
|
|
|
45
|
+
# DynamoDB Local capability for `aws-adapter` coverage.
|
|
46
|
+
#
|
|
47
|
+
# The `aws-adapter` package's vitest `globalSetup` boots a real
|
|
48
|
+
# DynamoDB Local instance so the integration suite (handlers,
|
|
49
|
+
# aws-runtime, transactions, …) can be exercised under coverage.
|
|
50
|
+
# Without it, 171 of 294 tests `describe.skipIf` away and the
|
|
51
|
+
# package drops to ~42% coverage, blowing the 90% gate.
|
|
52
|
+
#
|
|
53
|
+
# The self-hosted Gitea runner image (`ubuntu-latest`) ships with
|
|
54
|
+
# neither Docker (testcontainers' Ryuk reaper fails with "Failed
|
|
55
|
+
# to connect to Reaper" without a `docker.sock` mount or
|
|
56
|
+
# privileged mode) nor a JRE. We install a JRE here via the
|
|
57
|
+
# canonical `setup-java` action so the globalSetup's ZIP fallback
|
|
58
|
+
# can fire — this is the only strategy the runner image can
|
|
59
|
+
# support without host-level privileged-mode configuration.
|
|
60
|
+
#
|
|
61
|
+
# `DDB_LOCAL_STRATEGY=zip` is pinned on the coverage step below
|
|
62
|
+
# so the globalSetup skips the testcontainers probe entirely
|
|
63
|
+
# (otherwise it blocks for ~60s waiting for Ryuk before falling
|
|
64
|
+
# back, which is flaky and wastes CI minutes).
|
|
65
|
+
- name: Setup Java (JRE for DynamoDB Local ZIP fallback)
|
|
66
|
+
uses: actions/setup-java@v4
|
|
67
|
+
with:
|
|
68
|
+
distribution: temurin
|
|
69
|
+
java-version: '11'
|
|
70
|
+
|
|
45
71
|
- name: Install dependencies
|
|
46
72
|
run: pnpm install --frozen-lockfile
|
|
47
73
|
|
|
@@ -52,6 +78,12 @@ jobs:
|
|
|
52
78
|
run: pnpm typecheck
|
|
53
79
|
|
|
54
80
|
- name: Test with coverage
|
|
81
|
+
# `DDB_LOCAL_STRATEGY=zip` pins the DynamoDB Local boot to the
|
|
82
|
+
# Java ZIP fallback (see the `Setup Java` step above for why).
|
|
83
|
+
# This skips the testcontainers probe so the runner does not
|
|
84
|
+
# block ~60s waiting for Ryuk before falling back.
|
|
85
|
+
env:
|
|
86
|
+
DDB_LOCAL_STRATEGY: zip
|
|
55
87
|
run: pnpm coverage
|
|
56
88
|
|
|
57
89
|
- name: Coverage gate (coverage-thresholds enforced per package)
|
|
@@ -90,7 +122,7 @@ jobs:
|
|
|
90
122
|
|
|
91
123
|
- name: Upload coverage
|
|
92
124
|
if: always()
|
|
93
|
-
uses: actions/upload-artifact@
|
|
125
|
+
uses: actions/upload-artifact@v3
|
|
94
126
|
with:
|
|
95
127
|
name: coverage
|
|
96
128
|
path: |
|
|
@@ -135,7 +167,7 @@ jobs:
|
|
|
135
167
|
|
|
136
168
|
- name: Upload mutation report
|
|
137
169
|
if: always()
|
|
138
|
-
uses: actions/upload-artifact@
|
|
170
|
+
uses: actions/upload-artifact@v3
|
|
139
171
|
with:
|
|
140
172
|
name: mutation-report
|
|
141
173
|
path: packages/irc-core/reports/mutation
|
|
@@ -16,11 +16,7 @@ name: deploy-cf-tcp
|
|
|
16
16
|
# CF_TCP_ORIGIN_ADDRESS Hostname/IP of the container origin.
|
|
17
17
|
|
|
18
18
|
on:
|
|
19
|
-
push
|
|
20
|
-
branches: [main]
|
|
21
|
-
paths:
|
|
22
|
-
- 'apps/cf-tcp-container/**'
|
|
23
|
-
- '.github/workflows/deploy-cf-tcp.yml'
|
|
19
|
+
# Automatic push trigger disabled; deploy only runs manually.
|
|
24
20
|
workflow_dispatch:
|
|
25
21
|
|
|
26
22
|
concurrency:
|
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,266 @@ For the release process itself — versioning policy, pre-release checklist,
|
|
|
10
10
|
cutting a tag, rolling back — see [`docs/release.md`](docs/release.md).
|
|
11
11
|
Cross-reference `progress.md` / `tickets.md` for per-ticket detail.
|
|
12
12
|
|
|
13
|
+
## [0.6.0] - 2026-08-03
|
|
14
|
+
|
|
15
|
+
The headline is the **IRCv3 extension sweep (Phase 12)**: ten negotiated
|
|
16
|
+
caps and the user-mode-`S` TLS indicator land in one release, taking the
|
|
17
|
+
negotiated capability surface from "modern baseline" to "draft-leading-
|
|
18
|
+
edge". Alongside it: the **required-`serverName` config gate** (the
|
|
19
|
+
last open v0.5.0 follow-up), a new **`tools/load-test`** client farm
|
|
20
|
+
(Phase 7's 10k-connection harness), and the **coverage / CI unblocks**
|
|
21
|
+
that lift `aws-adapter` over the 90% gate and switch `cf-adapter` /
|
|
22
|
+
`cf-worker` to istanbul (clearing the v0.5.0 workerd-incompatibility
|
|
23
|
+
caveat). Deploy workflows are now **manual-only** (`workflow_dispatch`).
|
|
24
|
+
|
|
25
|
+
### Added — IRCv3 extensions (`@serverless-ircd/irc-core`, `@serverless-ircd/irc-server`)
|
|
26
|
+
|
|
27
|
+
The ten Phase 12 cap tickets light up the remaining negotiated-cap
|
|
28
|
+
surface, and the two non-cap Phase 12 tickets (ISUPPORT tokens and
|
|
29
|
+
user mode `S`) round out the wire-visible advertisement. Each cap
|
|
30
|
+
ships a pure reducer (where applicable), an actor route case,
|
|
31
|
+
advertisement in `SUPPORTED_CAPABILITIES` (or conditional
|
|
32
|
+
advertisement for `sts`), and unit + actor coverage. The
|
|
33
|
+
pure-reducer / dispatch pipeline is unchanged — every cap is plumbed
|
|
34
|
+
through `ctx` and emitted `Effect[]`, never performed in the handler.
|
|
35
|
+
|
|
36
|
+
- **`account-notify`**: pushes `:nick ACCOUNT <name|*>`
|
|
37
|
+
to peers in shared channels (excluding the sender unless
|
|
38
|
+
`echo-message`) on SASL login / logout. Closes the
|
|
39
|
+
`account-tag` / `extended-join` / `account-notify` triple modern
|
|
40
|
+
clients render fully.
|
|
41
|
+
- **`msgid`**: every PRIVMSG / NOTICE / TAGMSG fanned out
|
|
42
|
+
to a cap-enabled peer carries `@+msgid=<id>` from
|
|
43
|
+
`IdFactory.nonce()`. The nonce is **shared between the live fanout
|
|
44
|
+
and the `draft/chathistory` record** so a peer sees the same id live
|
|
45
|
+
and on replay. Legacy peers see no `msgid` tag.
|
|
46
|
+
- **`standard-replies`**: a data-driven
|
|
47
|
+
`numericToStandardReply` table replaces a curated subset of bare
|
|
48
|
+
numerics (`461`, `403`, `432`, `433`, SASL failures, …) with
|
|
49
|
+
`FAIL`/`WARN`/`NOTE COMMAND code context :description` for
|
|
50
|
+
cap-enabled clients. Legacy clients still receive the unchanged
|
|
51
|
+
numeric. Numeric-only replies (MOTD lines, NAMES, …) stay on their
|
|
52
|
+
numeric form.
|
|
53
|
+
- **`MONITOR`**: the modern `ISON` replacement. Implements
|
|
54
|
+
`MONITOR + nick{,nick}` / `- nick` / `C` / `L` / `S` with the
|
|
55
|
+
`730 RPL_MONONLINE` / `731 RPL_MONOFFLINE` / `732 RPL_MONLIST` /
|
|
56
|
+
`734 ERR_MONLISTFULL` numerics. Online/offline transitions push
|
|
57
|
+
`730`/`731` **asynchronously** via the Registry's nick reserve /
|
|
58
|
+
release hooks (a new `MonitorStore` port + push callback). The
|
|
59
|
+
ceiling is config-driven (`MONITOR=<n>`, default 30).
|
|
60
|
+
- **`labeled-response`**: a client-sent `+label=<id>` is
|
|
61
|
+
plumbed through `ctx` and every reply caused by that message is
|
|
62
|
+
wrapped in a single `BATCH +id labeled-response … BATCH -id`. A
|
|
63
|
+
command that produces no reply emits an empty labeled batch (spec-
|
|
64
|
+
mandated). The label travels on every reducer's emitted effects,
|
|
65
|
+
so multi-reply commands accumulate under one batch.
|
|
66
|
+
- **`sts`**: Strict Transport Security. The cap is
|
|
67
|
+
**conditionally advertised** — only when `ServerConfig.sts`
|
|
68
|
+
(`{ duration, port, preload? }`) is supplied. On a plaintext
|
|
69
|
+
listener a compliant client upgrades; on the TLS listener the
|
|
70
|
+
duration is still sent to lock in the policy. Defaults:
|
|
71
|
+
`duration = 30 days`, `port = 6697`.
|
|
72
|
+
- **`draft/typing`**: `TAGMSG <target> +draft/typing=
|
|
73
|
+
active|paused|done` broadcasts to channel members (or routes to a
|
|
74
|
+
nick target), excluding the sender unless `echo-message`. The
|
|
75
|
+
`+draft/typing=*` client tag is whitelisted for clients without
|
|
76
|
+
`message-tags` (per the spec's client-tag exception). Broadcast
|
|
77
|
+
delivery uses **OR-semantics**: a recipient qualifies if it
|
|
78
|
+
negotiated `message-tags` **or** `draft/typing` (a new `caps` field
|
|
79
|
+
on `BroadcastEffect`).
|
|
80
|
+
- **`draft/multiline`**: a cap-enabled
|
|
81
|
+
client sends `BATCH +id draft/multiline :target` + N PRIVMSG/NOTICE
|
|
82
|
+
lines + `BATCH -id`. The server concatenates per spec (with `\n`),
|
|
83
|
+
enforces a configurable byte budget (`multilineMaxBytes`, default
|
|
84
|
+
4096), and broadcasts the batched replay to multiline peers / a
|
|
85
|
+
single joined PRIVMSG (carrying `+draft/multiline`) to legacy
|
|
86
|
+
peers. **Nick-target routing**, **chathistory recording** (one
|
|
87
|
+
record per batch carrying the joined body, assigned `msgid`, and
|
|
88
|
+
the `+draft/multiline` client tag), and **tag gating** (the
|
|
89
|
+
`+draft/multiline` tag is stripped for peers without
|
|
90
|
+
`message-tags`) all land in the completion ticket.
|
|
91
|
+
- **`draft/read-marker`**: persists the per-`(account,
|
|
92
|
+
channel)` last-read marker in a new `ReadMarkerStore` port,
|
|
93
|
+
propagates updates via `+draft/read-marker=<msgid>` tags, and
|
|
94
|
+
seeds a fresh connection's marker from the persisted value at
|
|
95
|
+
identify time. `CHATHISTORY BEFORE` honours the marker when no
|
|
96
|
+
explicit pivot is supplied.
|
|
97
|
+
- **`draft/pre-away`**: persists the away reason in a
|
|
98
|
+
new `AwayStore` port keyed by account. On identify (or SASL login)
|
|
99
|
+
the server re-applies the away state and emits the `AWAY` line +
|
|
100
|
+
`306 RPL_NOWAWAY` as if the user had sent `AWAY :reason`
|
|
101
|
+
themselves. `AWAY` (unset) clears the persisted reason.
|
|
102
|
+
- **ISUPPORT tokens for the new caps**: `005
|
|
103
|
+
RPL_ISUPPORT` now advertises `STATUSMSG=@+`, `EXTBAN=<prefix>,q`,
|
|
104
|
+
`ACCOUNTEXTBAN=a` (services-gated), `TYPING`, and `MULTILINE`.
|
|
105
|
+
`MONITOR=<n>` honours `serverConfig.monitorLimit`. Tokens are
|
|
106
|
+
config-derived where applicable.
|
|
107
|
+
- **User mode `S` (TLS connected)**: the transport's
|
|
108
|
+
TLS fact is surfaced end-to-end as a read-only user mode. Set
|
|
109
|
+
once by the registration reducer from `ConnectionState.secure`;
|
|
110
|
+
emitted in `221 … +S`; rejected by `MODE nick +/-S` with `501
|
|
111
|
+
ERR_UMODEUNKNOWNFLAG`; surfaced in WHOIS via the new `276
|
|
112
|
+
RPL_WHOISSECURE`. Each adapter asserts `secure: true` where the
|
|
113
|
+
platform terminates TLS (API Gateway, CF edge, Spectrum, NLB TLS
|
|
114
|
+
listener).
|
|
115
|
+
|
|
116
|
+
### Changed — Required server identity
|
|
117
|
+
|
|
118
|
+
The last open v0.5.0 follow-up closes. The `irc.example.com`
|
|
119
|
+
placeholder `DEFAULT_SERVER_CONFIG` is **removed** from
|
|
120
|
+
`irc-core`; the shared `ServerConfigSchema` rejects a missing
|
|
121
|
+
`serverName` / `networkName` so every adapter **fails fast at boot**
|
|
122
|
+
with a readable, field-listing error.
|
|
123
|
+
|
|
124
|
+
- **`@serverless-ircd/irc-core`**: `DEFAULT_SERVER_CONFIG` removed.
|
|
125
|
+
- **`@serverless-ircd/irc-test-support`**: ships a clearly test-scoped
|
|
126
|
+
`makeTestServerConfig()` baseline (`irc.test.local`) so unit tests
|
|
127
|
+
construct a `ServerConfig` without re-introducing a production
|
|
128
|
+
default.
|
|
129
|
+
- `apps/cf-worker` + `apps/cf-tcp-container`: loaders require
|
|
130
|
+
`SERVER_NAME`; `wrangler.toml` samples switched to
|
|
131
|
+
`irc.your-domain.invalid`.
|
|
132
|
+
- `apps/aws-stack` + `apps/local-cli`: keep obviously-local fallbacks
|
|
133
|
+
(`irc.localhost`) documented as *"production MUST override"*;
|
|
134
|
+
`local-cli` gains `--server-name` / `--network-name` flags.
|
|
135
|
+
|
|
136
|
+
### Added — Load-test harness (`@serverless-ircd/load-test`)
|
|
137
|
+
|
|
138
|
+
PLAN §7 / Phase 7's 10k-connection harness. A synthetic WebSocket IRC
|
|
139
|
+
client farm: opens N connections, registers (NICK/USER), joins a
|
|
140
|
+
channel, and optionally chats. Captures **p50 / p95 / p99 latency per
|
|
141
|
+
phase** (connect / register / join / message) plus the **drop rate**,
|
|
142
|
+
and prints a markdown report to stdout (or `--report <path>`).
|
|
143
|
+
|
|
144
|
+
```bash
|
|
145
|
+
pnpm --filter load-test start -- \
|
|
146
|
+
--target wss://irc.staging.example.com/ \
|
|
147
|
+
--connections 10000 --concurrency 200 --ramp-ms 60000 \
|
|
148
|
+
--channel '#loadtest' --messages 3 --platform cf-staging \
|
|
149
|
+
--report reports/cf-staging-2026-08-03.md
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
The package ships its own vitest suite (config parsing, framing,
|
|
153
|
+
harness, metrics, report) at ≥90% coverage; no production source in
|
|
154
|
+
the irc-core / irc-server packages changed to support it.
|
|
155
|
+
|
|
156
|
+
### Added — Coverage / CI unblocks
|
|
157
|
+
|
|
158
|
+
- **`aws-adapter` clears the 90% gate.** The self-hosted runner image
|
|
159
|
+
ships with neither Docker nor a JRE, so the package's
|
|
160
|
+
testcontainers / Java-ZIP DynamoDB Local boot fell through to
|
|
161
|
+
strategy 3 and 171 of 294 integration tests `describe.skipIf`-ed
|
|
162
|
+
away — dropping coverage to ~42% and blowing the gate. CI now
|
|
163
|
+
installs Temurin JRE 11 via `actions/setup-java@v4` and pins
|
|
164
|
+
`DDB_LOCAL_STRATEGY=zip` on the coverage step so the ZIP fallback
|
|
165
|
+
fires deterministically. The full aws-adapter suite now runs on
|
|
166
|
+
every coverage job. New integration tests cover
|
|
167
|
+
`broadcastWallops` fan-out, `reserveNick` non-conditional error
|
|
168
|
+
propagation, `refreshChannel` empty-members / topic-preservation
|
|
169
|
+
paths, NLB header fallbacks, scan-row type guards, and
|
|
170
|
+
`CREATED_AT` overflow handling. `tools/ci-hardening` gained a
|
|
171
|
+
self-check asserting the JRE install + strategy pin remain wired.
|
|
172
|
+
- **`aws-stack` coverage command repaired.** The CDK `NodejsFunction`
|
|
173
|
+
in the test synth re-bundled the Lambda asset via esbuild ~80
|
|
174
|
+
times per coverage run and OOM-ed CI before vitest could print a
|
|
175
|
+
report. A new `makeLambda()` factory switches between
|
|
176
|
+
`NodejsFunction` (production synth) and an inline-zip `Function`
|
|
177
|
+
stub (tests) via the `IRC_AWS_STACK_TEST_NO_BUNDLE` env var. The
|
|
178
|
+
stub preserves every CloudFormation shape the suite asserts on
|
|
179
|
+
while making bundling a no-op. New
|
|
180
|
+
`tests/synth-no-bundle.test.ts` + `tests/coverage-command.test.ts`
|
|
181
|
+
lock both branches and assert the `pnpm coverage` run prints an
|
|
182
|
+
`All files` row.
|
|
183
|
+
- **`cf-adapter` / `cf-worker` switch to istanbul coverage.** This
|
|
184
|
+
**resolves the v0.5.0 known limitation**: `@vitest/coverage-v8`
|
|
185
|
+
v4.1.x imports `node:inspector/promises`, which the
|
|
186
|
+
`@cloudflare/vitest-pool-workers` (workerd) pool cannot resolve.
|
|
187
|
+
Both packages now use `@vitest/coverage-istanbul` so `pnpm
|
|
188
|
+
coverage` loads under workerd. Edge-case suites were expanded
|
|
189
|
+
(serialize/deserialize/migrate, connection-do pure helpers,
|
|
190
|
+
`broadcastWallops` fan-out, `changeNick` shard routing,
|
|
191
|
+
ChannelDO broadcast/cache, RegistryDO `listEntries`, SASL seed
|
|
192
|
+
parsing).
|
|
193
|
+
|
|
194
|
+
### Changed — Deploy workflows are now manual-only
|
|
195
|
+
|
|
196
|
+
`deploy-cf.yml`, `deploy-cf-tcp.yml`, and `deploy-aws.yml` dropped
|
|
197
|
+
their `push: branches: [main]` triggers and now run on
|
|
198
|
+
`workflow_dispatch` only. Staging deploys are no longer fired
|
|
199
|
+
automatically on every merge to `main`; a maintainer kicks them off
|
|
200
|
+
by hand. The release runbook (`docs/Release-Process.md`) is the
|
|
201
|
+
canonical reference either way — there was never a CI auto-deploy to
|
|
202
|
+
production.
|
|
203
|
+
|
|
204
|
+
### Changed — `@vitest/coverage-istanbul` peer
|
|
205
|
+
|
|
206
|
+
The root lockfile (`pnpm-lock.yaml`) registers the
|
|
207
|
+
`@vitest/coverage-istanbul` peer introduced by the cf-adapter /
|
|
208
|
+
cf-worker coverage switch. No operator action; `pnpm install`
|
|
209
|
+
resolves it.
|
|
210
|
+
|
|
211
|
+
### ⚠️ Migration required
|
|
212
|
+
|
|
213
|
+
- **`serverName` is now required at boot.** Operators who relied on
|
|
214
|
+
the silent `irc.example.com` placeholder must supply an explicit
|
|
215
|
+
value via their adapter's config knob:
|
|
216
|
+
- **Cloudflare Worker / TCP container**: set `SERVER_NAME` (and
|
|
217
|
+
`NETWORK_NAME` if desired) in `wrangler.toml` `[vars]` or via
|
|
218
|
+
`wrangler secret put`. The samples switched to
|
|
219
|
+
`irc.your-domain.invalid` — replace with your real hostname.
|
|
220
|
+
- **AWS**: the CDK stack's `serverName` prop / `SERVER_NAME` env
|
|
221
|
+
var. The local fallback is `irc.localhost` and is explicitly
|
|
222
|
+
documented as "production MUST override".
|
|
223
|
+
- **`local-cli`**: new `--server-name <h>` / `--network-name <n>`
|
|
224
|
+
flags (default `irc.localhost` / `LocalNet`).
|
|
225
|
+
A missing value now throws an aggregated `"Invalid server
|
|
226
|
+
config:"` error at boot listing every offending field.
|
|
227
|
+
- **No persisted-state schema change.** `PERSISTED_STATE_VERSION`
|
|
228
|
+
stays `1`. The user-mode-`S` work adds a `secure` field and a
|
|
229
|
+
`userModes.tls` field to the persisted shape, but both are purely
|
|
230
|
+
**additive with `false` defaults** and the cf-adapter `migrate()`
|
|
231
|
+
path backfills them on pre-feature records. **Code rollback across
|
|
232
|
+
this release is safe.**
|
|
233
|
+
- No toolchain change. Node ≥ 24 / pnpm 11 (from v0.4.0) still apply.
|
|
234
|
+
|
|
235
|
+
### Known limitations
|
|
236
|
+
|
|
237
|
+
- Same transport limits as v0.5.0: the **CF TCP path requires
|
|
238
|
+
Cloudflare Spectrum (Enterprise tier)** plus a stateful Container
|
|
239
|
+
origin; the **AWS TCP path** uses NLB + Lambda streaming and is
|
|
240
|
+
subject to Lambda idle-timeout / stream-duration limits. The
|
|
241
|
+
WebSocket path remains the zero-extra-deps default.
|
|
242
|
+
- **mTLS (for SASL `EXTERNAL`) requires a custom domain + uploaded
|
|
243
|
+
CA/trust-store** on both platforms and is not enabled by default.
|
|
244
|
+
- `STATS` ships the `u` + `l` query letters; the full RFC matrix
|
|
245
|
+
(`k`, `K`, `q`, `g`, `x`, `z`, …) is intentionally out of scope for
|
|
246
|
+
v1 — unknown letters get the charybdis-style empty body + `219`.
|
|
247
|
+
- **Client compatibility sweep** (WeeChat / HexChat / IRCCloud /
|
|
248
|
+
TheLounge / matrix-IRC bridge) remains pending.
|
|
249
|
+
- **Coverage hardening is partial.** `aws-adapter` and `aws-stack`
|
|
250
|
+
now clear the 90% gate, and `cf-adapter` / `cf-worker` run under
|
|
251
|
+
istanbul; the remaining packages (`cf-adapter`, `local-cli`,
|
|
252
|
+
`load-test`, `cf-tcp-container`, `tcp-ws-forwarder`,
|
|
253
|
+
`irc-test-support`) are **above the gate but below 100%** —
|
|
254
|
+
follow-ups drive each to 100%.
|
|
255
|
+
- The IRC services (NickServ / ChanServ / HostServ / OperServ /
|
|
256
|
+
MemoServ) Phase 13 work is pending; `draft/read-marker` and
|
|
257
|
+
`draft/pre-away` ship with in-memory reference stores, so marker /
|
|
258
|
+
away persistence does not survive an adapter restart until a
|
|
259
|
+
services backend lands.
|
|
260
|
+
- The web client Phase 14 work (vendor Kiwi IRC, serve SPA from the
|
|
261
|
+
Worker, WS Origin allowlist) is pending.
|
|
262
|
+
|
|
263
|
+
### Security
|
|
264
|
+
|
|
265
|
+
- None. No vulnerabilities have been reported or fixed in this
|
|
266
|
+
release. The new caps add surface area but ship no new auth path
|
|
267
|
+
(the SASL `PLAIN` / `EXTERNAL` paths and the oper gate are
|
|
268
|
+
unchanged); the required-`serverName` change rejects misconfigured
|
|
269
|
+
deploys at boot rather than advertising a placeholder identity.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
13
273
|
## [0.5.0] - 2026-08-02
|
|
14
274
|
|
|
15
275
|
The headline is **protocol completion**: the deferred IRC verbs land
|
package/README.md
CHANGED
|
@@ -6,7 +6,7 @@ platform-agnostic core, and two thin adapters run it on **Cloudflare Workers**
|
|
|
6
6
|
|
|
7
7
|
One TypeScript codebase. Two serverless substrates.
|
|
8
8
|
|
|
9
|
-
> **Status:** **v0.
|
|
9
|
+
> **Status:** **v0.6.0 (preview).** The pure protocol core, the
|
|
10
10
|
> `IrcRuntime` port + in-memory runtime, a runnable local CLI server,
|
|
11
11
|
> the **Cloudflare Workers** adapter, and the **AWS** (API Gateway
|
|
12
12
|
> WebSocket + Lambda + DynamoDB + CDK) adapter are all functional and
|
|
@@ -15,15 +15,25 @@ One TypeScript codebase. Two serverless substrates.
|
|
|
15
15
|
> clients — Cloudflare via Spectrum + a Container origin, AWS via a
|
|
16
16
|
> Network Load Balancer + Lambda streaming — alongside the WebSocket
|
|
17
17
|
> path, plus **SASL `EXTERNAL` via mTLS** and a **Cloudflare D1-backed
|
|
18
|
-
> SASL account store**. v0.5.0
|
|
18
|
+
> SASL account store**. v0.5.0 completed the **protocol surface**: the
|
|
19
19
|
> deferred IRC verbs (`KILL`, `REHASH`, `LUSERS` + `STATS`, `TRACE`,
|
|
20
20
|
> `WALLOPS`, `SETNAME`) all land, and the S2S (`CONNECT` / `SQUIT` /
|
|
21
21
|
> `LINKS`) and obsolete RFC 2812 (`SERVICE` / `SUMMON` / `USERS`) verbs
|
|
22
|
-
> are formally dropped
|
|
23
|
-
>
|
|
24
|
-
>
|
|
25
|
-
>
|
|
26
|
-
>
|
|
22
|
+
> are formally dropped. **v0.6.0 lands the IRCv3 extension sweep
|
|
23
|
+
> (Phase 12)** — ten caps (`account-notify`, `msgid`,
|
|
24
|
+
> `standard-replies`, `MONITOR`, `labeled-response`, `sts`,
|
|
25
|
+
> `draft/typing`, `draft/multiline`, `draft/read-marker`,
|
|
26
|
+
> `draft/pre-away`), the ISUPPORT tokens for the new caps, and the
|
|
27
|
+
> read-only user mode `S` (TLS connected) — closes the
|
|
28
|
+
> required-`serverName` config gate, ships a `tools/load-test`
|
|
29
|
+
> 10k-connection harness, and lifts `aws-adapter` / `aws-stack` over
|
|
30
|
+
> the 90% coverage gate (CF packages moved to istanbul coverage,
|
|
31
|
+
> clearing the v0.5.0 workerd-incompatibility caveat). Remaining 0.x
|
|
32
|
+
> work: the client compatibility sweep (WeeChat / HexChat / IRCCloud /
|
|
33
|
+
> TheLounge), IRC services (NickServ / ChanServ / HostServ /
|
|
34
|
+
> OperServ / MemoServ), a web client (vendored Kiwi IRC), and driving
|
|
35
|
+
> the remaining sub-100% packages to full coverage. See
|
|
36
|
+
> `CHANGELOG.md` for the per-release manifests.
|
|
27
37
|
|
|
28
38
|
---
|
|
29
39
|
|
|
@@ -130,6 +140,7 @@ ServerlessIRCd/
|
|
|
130
140
|
│ └── local-cli/ runnable WS + TCP server using in-memory-runtime
|
|
131
141
|
├── tools/
|
|
132
142
|
│ ├── tcp-ws-forwarder/ local TCP↔ws/wss bridge for stock IRC clients
|
|
143
|
+
│ ├── load-test/ synthetic WebSocket IRC client farm (10k conns, p50/p95/p99, drop rate)
|
|
133
144
|
│ ├── ci-hardening/ coverage-gate + mutation-config validators
|
|
134
145
|
│ ├── seed-aws-accounts.ts scrypt-hash SASL PLAIN accounts into DynamoDB
|
|
135
146
|
│ └── seed-cf-accounts.ts scrypt-hash SASL PLAIN accounts into Cloudflare D1
|
|
@@ -230,6 +241,8 @@ On startup it logs a JSON line like:
|
|
|
230
241
|
| `--tcp-port <n>` | `<port> + 1` | TCP port for RFC-style IRC clients. |
|
|
231
242
|
| `--no-tcp` | | Disable the TCP listener (WebSocket only). |
|
|
232
243
|
| `--motd-file <p>` | built-in | Read MOTD lines from this file (one per line). |
|
|
244
|
+
| `--server-name <h>`| `irc.localhost` | Server hostname advertised in `001`/`005`. Production MUST override. |
|
|
245
|
+
| `--network-name <n>`| `LocalNet` | Network name advertised in `005 NETWORK=…`. |
|
|
233
246
|
| `-h, --help` | | Show help and exit. |
|
|
234
247
|
|
|
235
248
|
Pass `--host 0.0.0.0` to expose the server on all interfaces. `SIGINT` /
|
|
@@ -302,6 +315,55 @@ against any line-oriented WebSocket endpoint.
|
|
|
302
315
|
|
|
303
316
|
---
|
|
304
317
|
|
|
318
|
+
## Load testing (`tools/load-test`)
|
|
319
|
+
|
|
320
|
+
A synthetic WebSocket IRC client farm (PLAN §7, Phase 7). It opens N
|
|
321
|
+
connections to a `ws://` / `wss://` target, registers each (NICK/USER),
|
|
322
|
+
joins a channel, and optionally chats. Per-phase **p50 / p95 / p99
|
|
323
|
+
latency** (connect / register / join / message) and the **drop rate**
|
|
324
|
+
are captured, then printed as a markdown report to stdout (and written
|
|
325
|
+
to `--report <path>` when given).
|
|
326
|
+
|
|
327
|
+
Build the workspace, then start a run (from the repo root):
|
|
328
|
+
|
|
329
|
+
```bash
|
|
330
|
+
pnpm build
|
|
331
|
+
|
|
332
|
+
pnpm --filter load-test start -- \
|
|
333
|
+
--target wss://irc.staging.example.com/ \
|
|
334
|
+
--connections 10000 --concurrency 200 --ramp-ms 60000 \
|
|
335
|
+
--channel '#loadtest' --messages 3 --platform cf-staging \
|
|
336
|
+
--report reports/cf-staging-2026-08-03.md
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
The harness drives the WebSocket transport only (the serverless default);
|
|
340
|
+
a TCP IRC client is not what a serverless load test exercises. Each
|
|
341
|
+
connection is independent — the package ships no `@serverless-ircd/*`
|
|
342
|
+
runtime dependency (only the IRC line framing shared with the
|
|
343
|
+
forwarder), so it can be pointed at any WebSocket IRC endpoint.
|
|
344
|
+
|
|
345
|
+
### CLI flags
|
|
346
|
+
|
|
347
|
+
| Flag | Default | Description |
|
|
348
|
+
|-------------------------|-------------|----------------------------------------------------------|
|
|
349
|
+
| `--target <url>` | (required) | Upstream `ws://` or `wss://` URL to load test. |
|
|
350
|
+
| `--connections <n>` | `1000` | Total connections to open. |
|
|
351
|
+
| `--concurrency <n>` | `50` | Concurrent in-flight connection attempts during ramp-up. |
|
|
352
|
+
| `--ramp-ms <ms>` | `30000` | Ramp-up duration over which connections are opened. |
|
|
353
|
+
| `--channel <c>` | `#loadtest` | Channel every client JOINs. |
|
|
354
|
+
| `--messages <n>` | `0` | PRIVMSGs each client sends after JOIN (`0` = connect only). |
|
|
355
|
+
| `--echo-message` | | Negotiate `echo-message` so sent PRIVMSGs round-trip. |
|
|
356
|
+
| `--platform <name>` | `unknown` | Label baked into the report header. |
|
|
357
|
+
| `--report <path>` | | Write the markdown report to this path. |
|
|
358
|
+
| `-h, --help` | | Show help and exit. |
|
|
359
|
+
|
|
360
|
+
`SIGINT` / `SIGTERM` cancel an in-flight run gracefully (drains live
|
|
361
|
+
sockets, prints the partial report). The package ships its own vitest
|
|
362
|
+
suite at ≥90% coverage (config parsing, framing, harness, metrics,
|
|
363
|
+
report).
|
|
364
|
+
|
|
365
|
+
---
|
|
366
|
+
|
|
305
367
|
## WebSocket transport (IRCv3 subprotocols)
|
|
306
368
|
|
|
307
369
|
The WebSocket entry points (the local CLI, the Cloudflare Worker, and the
|
|
@@ -373,21 +435,23 @@ cleanly. Per-release manifests live in `CHANGELOG.md`.
|
|
|
373
435
|
- **Phase 2** — `IrcRuntime` port, in-memory runtime, `ConnectionActor`,
|
|
374
436
|
local CLI, parametrized contract suite. ✅ landed in **v0.1.0**.
|
|
375
437
|
- **Phase 3** — Cloudflare adapter (ConnectionDO / ChannelDO / RegistryDO).
|
|
376
|
-
✅ landed in **v0.1.0** (staging
|
|
438
|
+
✅ landed in **v0.1.0** (staging deploy via CI; prod deploy is manual).
|
|
377
439
|
- **Phase 4** — AWS adapter (APIGW WS + Lambda + DynamoDB + CDK).
|
|
378
|
-
✅ landed in **v0.2.0** (staging
|
|
440
|
+
✅ landed in **v0.2.0** (staging deploy via CI; prod deploy is manual).
|
|
379
441
|
- **Phase 5** — Observability, security hardening, config, CI gates.
|
|
380
442
|
✅ landed across v0.2.0–v0.4.0 (logger port, server-password, cloaking,
|
|
381
443
|
admission limits, coverage + mutation gates, OPER credential auth, the
|
|
382
|
-
Cloudflare config loader, and AWS max-clients admission)
|
|
444
|
+
Cloudflare config loader, and AWS max-clients admission); the
|
|
445
|
+
required-`serverName` gate closed in **v0.6.0**.
|
|
383
446
|
- **Phase 6** — TLS hardening & raw TCP transport (`irc+tls :6697`).
|
|
384
447
|
✅ landed in **v0.4.0**: generalized `ConnectionActor` transport seam,
|
|
385
448
|
Cloudflare Spectrum + Container origin, AWS NLB + Lambda streaming,
|
|
386
449
|
mTLS → SASL `EXTERNAL`, transport-parametrized contract suite, ADR-009.
|
|
387
|
-
- **Phase 7
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
450
|
+
- **Phase 7 — Load testing & compatibility sweep.** 🔄 partial as of
|
|
451
|
+
**v0.6.0**: the `tools/load-test` 10k-connection harness is in-tree;
|
|
452
|
+
the formal 10k-connection report per platform and the client
|
|
453
|
+
compatibility matrix (WeeChat / HexChat / IRCCloud / TheLounge) are
|
|
454
|
+
still pending.
|
|
391
455
|
- **Phase 8 — PLAN-FIXES remediation.** ✅ landed across v0.3.0–v0.4.0
|
|
392
456
|
(MessageStore wired into all three adapters, AccountStore end-to-end,
|
|
393
457
|
the DynamoDB `Accounts` table decided, CF enumeration RPCs, AWS QUIT
|
|
@@ -406,6 +470,28 @@ cleanly. Per-release manifests live in `CHANGELOG.md`.
|
|
|
406
470
|
the formal drop of the S2S (`CONNECT`/`SQUIT`/`LINKS`) and obsolete
|
|
407
471
|
RFC 2812 (`SERVICE`/`SUMMON`/`USERS`) verbs. The protocol surface is
|
|
408
472
|
feature-complete for modern clients.
|
|
473
|
+
- **Phase 12 — IRCv3 extension sweep.** ✅ landed in **v0.6.0**: ten
|
|
474
|
+
negotiated caps (`account-notify`, `msgid`, `standard-replies`,
|
|
475
|
+
`MONITOR`, `labeled-response`, `sts`, `draft/typing`,
|
|
476
|
+
`draft/multiline`, `draft/read-marker`, `draft/pre-away`), the
|
|
477
|
+
ISUPPORT tokens for the new caps (`MONITOR`/`MULTILINE`/`TYPING`/
|
|
478
|
+
`STATUSMSG`/`EXTBAN`/`ACCOUNTEXTBAN`), the read-only user mode `S`
|
|
479
|
+
(TLS connected), and the async push fanout seam (Registry hooks →
|
|
480
|
+
`MONITOR` online/offline, `BroadcastWallops`-style caps-gated
|
|
481
|
+
delivery).
|
|
482
|
+
- **Phase 13 — IRC services.** ⬳ pending: `ServicesStore` port +
|
|
483
|
+
NickServ / ChanServ / HostServ / OperServ / MemoServ. Adapter
|
|
484
|
+
backends (D1 / DynamoDB) ship alongside; until then
|
|
485
|
+
`draft/read-marker` and `draft/pre-away` use in-memory reference
|
|
486
|
+
stores and do not survive an adapter restart.
|
|
487
|
+
- **Phase 14 — Web client.** ⬳ pending: vendored Kiwi IRC SPA served
|
|
488
|
+
from the Worker, WS Origin allowlist (CSWSH defense), landing page,
|
|
489
|
+
headless browser e2e.
|
|
490
|
+
- **Coverage hardening.** 🔄 partial as of **v0.6.0**: `aws-adapter`
|
|
491
|
+
and `aws-stack` now clear the 90% gate (CI boots DynamoDB Local via
|
|
492
|
+
a JRE; the CDK synth no longer re-bundles under coverage), and the
|
|
493
|
+
CF packages moved to istanbul to clear the v0.5.0 workerd-incompat.
|
|
494
|
+
Follow-ups drive each remaining sub-100% package to 100%.
|
|
409
495
|
|
|
410
496
|
---
|
|
411
497
|
|
|
@@ -416,6 +502,8 @@ cleanly. Per-release manifests live in `CHANGELOG.md`.
|
|
|
416
502
|
channel), `TOPIC`, `KICK`, `INVITE`, `NAMES`, `LIST`, `WHO`, `WHOIS`,
|
|
417
503
|
`WHOWAS`, `MOTD`, `AWAY`, `OPER` (credential auth → `o` user mode), the
|
|
418
504
|
query verbs `VERSION`, `TIME`, `ADMIN`, `INFO`, `USERHOST`, `ISON`, the
|
|
505
|
+
modern-online-presence verb `MONITOR` (`+`/`-`/`C`/`L`/`S` with the
|
|
506
|
+
`730`/`731`/`732`/`734` numerics and async online/offline push), the
|
|
419
507
|
network-statistics verbs `LUSERS` and `STATS` (`u` uptime + `l` link-info
|
|
420
508
|
letters; unknown letters get the charybdis-style empty body), the
|
|
421
509
|
routing-diagnostic verb `TRACE` (oper-gated per-connection detail), the
|
|
@@ -429,15 +517,31 @@ v0.5.0 — they return `421 ERR_UNKNOWNCOMMAND`, and their reserved
|
|
|
429
517
|
numerics have been removed. S2S linking is a PLAN non-goal.)
|
|
430
518
|
|
|
431
519
|
**Channel modes:** `o v b i k l t n m s p`.
|
|
432
|
-
**User modes:** `i`, `o` (local only), `w`, `s
|
|
520
|
+
**User modes:** `i`, `o` (local only), `w`, `s`, and the read-only `S`
|
|
521
|
+
(TLS connected — set by the transport at registration, surfaced in WHOIS
|
|
522
|
+
via `276 RPL_WHOISSECURE`, not settable via `MODE`).
|
|
433
523
|
|
|
434
524
|
**IRCv3 extensions (negotiated via `CAP`):** `message-tags` (incl. the
|
|
435
|
-
`TAGMSG` command), `server-time`, `account-tag`, `
|
|
525
|
+
`TAGMSG` command), `server-time`, `account-tag`, `account-notify`
|
|
526
|
+
(pushes `ACCOUNT` on SASL login/logout), `echo-message`, `batch`,
|
|
436
527
|
`sasl` (`PLAIN` always; `EXTERNAL` via mTLS when a client-cert trust store
|
|
437
|
-
is bound),
|
|
438
|
-
`
|
|
439
|
-
`draft/chathistory
|
|
440
|
-
|
|
528
|
+
is bound), `multi-prefix`, `away-notify`, `chghost`, `invite-notify`,
|
|
529
|
+
`extended-join`, `msgid` (`@+msgid=<id>` on every PRIVMSG/NOTICE/TAGMSG,
|
|
530
|
+
shared between live and `draft/chathistory` replay), `standard-replies`
|
|
531
|
+
(`FAIL`/`WARN`/`NOTE` replacements for a curated numeric subset),
|
|
532
|
+
`MONITOR=<n>` (config-driven ceiling, default 30), `labeled-response`
|
|
533
|
+
(`+label=<id>` → `BATCH +id labeled-response … BATCH -id` wrapping),
|
|
534
|
+
`sts` (Strict Transport Security — conditionally advertised with
|
|
535
|
+
`duration`/`port`/optional `preload` from `ServerConfig` when configured,
|
|
536
|
+
so compliant clients upgrade from plaintext to TLS and pin the secure
|
|
537
|
+
listener), `draft/chathistory`, `safelist`, `draft/typing` (typing-indicator
|
|
538
|
+
broadcast), `draft/multiline=<n>` (multi-line `BATCH`, default 4096-byte
|
|
539
|
+
budget), `draft/read-marker` (per-account persisted last-read), and
|
|
540
|
+
`draft/pre-away` (per-account persisted away reason, replayed at
|
|
541
|
+
identify). Case-insensitive nick/channel comparison uses **RFC 1459**
|
|
542
|
+
case-mapping (advertised via `005 CASEMAPPING=rfc1459`). The new
|
|
543
|
+
`MONITOR` / `MULTILINE` / `TYPING` / `STATUSMSG` / `EXTBAN` /
|
|
544
|
+
`ACCOUNTEXTBAN` ISUPPORT tokens are advertised from `ServerConfig`.
|
|
441
545
|
|
|
442
546
|
**Transport:** deployed stacks (Cloudflare Workers, AWS API Gateway) speak
|
|
443
547
|
WebSocket text frames by default (one IRC message per frame, with tolerance
|
|
@@ -457,9 +561,15 @@ bridges a stock TCP client to a deployed WebSocket endpoint.
|
|
|
457
561
|
gates), v0.3.0 (OPER, TAGMSG, RFC 1459 case-mapping, real MOTD,
|
|
458
562
|
ChannelRegistryDO, SASL account persistence), v0.4.0 (dual transport
|
|
459
563
|
`irc+tls :6697`, SASL `EXTERNAL`/mTLS, WHOWAS + remaining verbs, D1 account
|
|
460
|
-
store, Node 24/pnpm 11),
|
|
564
|
+
store, Node 24/pnpm 11), v0.5.0 (deferred IRC verbs `KILL` / `REHASH`
|
|
461
565
|
/ `LUSERS` + `STATS` / `TRACE` / `WALLOPS` / `SETNAME`, plus the formal
|
|
462
|
-
drop of the S2S and obsolete RFC 2812 verbs).
|
|
566
|
+
drop of the S2S and obsolete RFC 2812 verbs), and **v0.6.0** (the IRCv3
|
|
567
|
+
extension sweep — `account-notify`, `msgid`, `standard-replies`,
|
|
568
|
+
`MONITOR`, `labeled-response`, `sts`, `draft/typing`, `draft/multiline`,
|
|
569
|
+
`draft/read-marker`, `draft/pre-away`, plus user mode `S`; the
|
|
570
|
+
required-`serverName` gate; `tools/load-test`; aws-adapter / aws-stack
|
|
571
|
+
coverage unblocks; CF packages moved to istanbul; deploys now
|
|
572
|
+
manual-only).
|
|
463
573
|
- `docs/Cloudflare-TCP-Deployment.md` and `docs/AWS-TCP-Deployment.md` —
|
|
464
574
|
end-to-end guides for the `:6697` TCP+TLS variants (Spectrum/Container on
|
|
465
575
|
CF, NLB + Lambda streaming on AWS), including mTLS trust-store setup.
|
package/apps/aws-stack/README.md
CHANGED
|
@@ -60,6 +60,39 @@ export AWS_DEFAULT_REGION=us-east-1
|
|
|
60
60
|
LOCALSTACK=1 pnpm test
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
+
## Test-mode bundle stub (why coverage does not re-bundle)
|
|
64
|
+
|
|
65
|
+
The stack ships four `NodejsFunction` constructs (the wss `IrcHandler`,
|
|
66
|
+
`IrcSweeper`, `IrcPingChecker`, and the optional NLB `IrcNlbHandler`).
|
|
67
|
+
Each one invokes esbuild at synth time to bundle the shared handler
|
|
68
|
+
entry. Repeated across the ~80 synth assertions in `tests/stack.test.ts`,
|
|
69
|
+
that meant the bundler ran **dozens to hundreds of times per coverage
|
|
70
|
+
invocation**, exhausting CI memory before vitest could print its
|
|
71
|
+
coverage table — the failure mode presented as a wall of
|
|
72
|
+
`Bundling asset TestStack/IrcHandler/Code/Stage…` lines followed by
|
|
73
|
+
`[ELIFECYCLE] Command failed with exit code N` and no report.
|
|
74
|
+
|
|
75
|
+
The fix is a single env-var toggle consumed by `makeLambda()` in
|
|
76
|
+
`src/aws-stack.ts`:
|
|
77
|
+
|
|
78
|
+
| Env var | Effect on the synth |
|
|
79
|
+
|-----------------------------------|-----------------------------------------------------------------------|
|
|
80
|
+
| `IRC_AWS_STACK_TEST_NO_BUNDLE=1` | Every Lambda is emitted as an inline `Code.fromInline('void 0')` zip; esbuild is not invoked at all. CloudFormation shape (runtime, env vars, IAM grants, route wiring, …) is identical to the production synth. |
|
|
81
|
+
| _(unset)_ | Production path: every Lambda is a real `NodejsFunction` whose asset is esbuild-bundled at synth time. This is what `cdk synth` / `cdk deploy` run. |
|
|
82
|
+
|
|
83
|
+
`tests/vitest.setup.ts` sets the flag once per test file so the entire
|
|
84
|
+
`pnpm test` / `pnpm coverage` run stays on the stub path. Production
|
|
85
|
+
synth (`bin/aws.ts` via the CDK CLI) never loads vitest, so it cannot
|
|
86
|
+
accidentally inherit the flag.
|
|
87
|
+
|
|
88
|
+
A small `tests/synth-no-bundle.test.ts` locks both paths in place: it
|
|
89
|
+
asserts the stub emits `Code: { ZipFile }` when the flag is set, and
|
|
90
|
+
exercises the production `NodejsFunction` return once (with the flag
|
|
91
|
+
explicitly unset) so the production branch stays covered. The
|
|
92
|
+
end-to-end guard lives in `tests/coverage-command.test.ts`, which
|
|
93
|
+
spawns `pnpm --filter @serverless-ircd/aws-stack coverage` in a
|
|
94
|
+
subprocess and asserts the report prints an `All files` row.
|
|
95
|
+
|
|
63
96
|
## Stack outputs
|
|
64
97
|
|
|
65
98
|
| Output | Description |
|