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.
Files changed (135) hide show
  1. package/.github/workflows/ci.yml +3 -3
  2. package/.gitmodules +1 -1
  3. package/CHANGELOG.md +273 -29
  4. package/README.md +155 -55
  5. package/apps/aws-stack/package.json +1 -1
  6. package/apps/aws-stack/src/aws-stack.ts +186 -18
  7. package/apps/aws-stack/tests/stack.test.ts +400 -56
  8. package/apps/cf-tcp-container/package.json +1 -1
  9. package/apps/cf-worker/package.json +1 -1
  10. package/apps/cf-worker/src/worker.ts +4 -4
  11. package/apps/cf-worker/tests/fixtures/web-dist/webclient/index.html +18 -0
  12. package/apps/cf-worker/tests/smoke.test.ts +5 -5
  13. package/apps/cf-worker/wrangler.test.toml +6 -6
  14. package/apps/cf-worker/wrangler.toml +7 -5
  15. package/apps/local-cli/package.json +1 -1
  16. package/apps/local-cli/src/config-loader.ts +8 -0
  17. package/apps/local-cli/src/main.ts +16 -0
  18. package/apps/local-cli/src/server.ts +1 -0
  19. package/apps/local-cli/tests/config-loader.test.ts +14 -0
  20. package/apps/web/landing/index.html +14 -16
  21. package/apps/web/package.json +2 -2
  22. package/apps/web/scripts/build.mjs +23 -16
  23. package/apps/web/src/build-env.ts +1 -1
  24. package/apps/web/src/config-schema.ts +6 -6
  25. package/apps/web/tests/build-smoke.test.ts +14 -14
  26. package/apps/web/tests/config-schema.test.ts +1 -1
  27. package/docs/AWS-Deployment.md +21 -8
  28. package/docs/Cloudflare-Deployment-Guide.md +22 -6
  29. package/docs/PlanExtensions.md +113 -3
  30. package/docs/Release-Process.md +23 -13
  31. package/docs/Services.md +546 -0
  32. package/docs/WebClientGuide.md +32 -31
  33. package/package.json +2 -2
  34. package/packages/aws-adapter/package.json +1 -1
  35. package/packages/aws-adapter/src/aws-runtime.ts +5 -0
  36. package/packages/aws-adapter/src/cdk-table-defs.ts +6 -0
  37. package/packages/aws-adapter/src/config-loader.ts +11 -0
  38. package/packages/aws-adapter/src/connection-counter.ts +89 -0
  39. package/packages/aws-adapter/src/dynamo-services-store.ts +649 -0
  40. package/packages/aws-adapter/src/handlers/connect.ts +55 -51
  41. package/packages/aws-adapter/src/handlers/default.ts +36 -4
  42. package/packages/aws-adapter/src/handlers/index.ts +15 -0
  43. package/packages/aws-adapter/src/handlers/nlb-stream.ts +15 -0
  44. package/packages/aws-adapter/src/handlers/sweeper.ts +5 -1
  45. package/packages/aws-adapter/src/index.ts +4 -0
  46. package/packages/aws-adapter/src/stats.ts +6 -1
  47. package/packages/aws-adapter/src/tables.ts +34 -4
  48. package/packages/aws-adapter/tests/aws-harness.ts +3 -0
  49. package/packages/aws-adapter/tests/config-loader.test.ts +8 -0
  50. package/packages/aws-adapter/tests/connect.test.ts +158 -32
  51. package/packages/aws-adapter/tests/connection-counter.test.ts +127 -0
  52. package/packages/aws-adapter/tests/dynamo-services-store-dynamo.test.ts +183 -0
  53. package/packages/aws-adapter/tests/dynamo-services-store-unit.test.ts +568 -0
  54. package/packages/aws-adapter/tests/handlers.test.ts +105 -3
  55. package/packages/aws-adapter/tests/tables.test.ts +6 -1
  56. package/packages/cf-adapter/package.json +1 -1
  57. package/packages/cf-adapter/src/config-loader.ts +11 -0
  58. package/packages/cf-adapter/src/connection-do.ts +112 -2
  59. package/packages/cf-adapter/src/d1-services-store.ts +703 -0
  60. package/packages/cf-adapter/src/env.ts +8 -0
  61. package/packages/cf-adapter/src/index.ts +5 -0
  62. package/packages/cf-adapter/tests/config-loader.test.ts +19 -0
  63. package/packages/cf-adapter/tests/connection-do-nickserv-d1.test.ts +128 -0
  64. package/packages/cf-adapter/tests/connection-do.test.ts +150 -2
  65. package/packages/cf-adapter/tests/d1-services-store.test.ts +582 -0
  66. package/packages/cf-adapter/tests/serialize.test.ts +1 -0
  67. package/packages/in-memory-runtime/package.json +1 -1
  68. package/packages/irc-core/package.json +1 -1
  69. package/packages/irc-core/scripts/generate-build-info.mjs +26 -5
  70. package/packages/irc-core/src/commands/account-auth.ts +172 -0
  71. package/packages/irc-core/src/commands/chanserv.ts +882 -0
  72. package/packages/irc-core/src/commands/hostserv.ts +487 -0
  73. package/packages/irc-core/src/commands/index.ts +12 -0
  74. package/packages/irc-core/src/commands/join.ts +164 -8
  75. package/packages/irc-core/src/commands/markread.ts +202 -0
  76. package/packages/irc-core/src/commands/memoserv.ts +319 -0
  77. package/packages/irc-core/src/commands/mode.ts +96 -4
  78. package/packages/irc-core/src/commands/nickserv.ts +390 -0
  79. package/packages/irc-core/src/commands/oper.ts +18 -1
  80. package/packages/irc-core/src/commands/operserv.ts +346 -0
  81. package/packages/irc-core/src/commands/pre-away.ts +3 -1
  82. package/packages/irc-core/src/commands/privmsg.ts +42 -0
  83. package/packages/irc-core/src/commands/read-marker.ts +8 -8
  84. package/packages/irc-core/src/commands/registration.ts +61 -6
  85. package/packages/irc-core/src/commands/sasl.ts +18 -49
  86. package/packages/irc-core/src/commands/tagmsg.ts +41 -6
  87. package/packages/irc-core/src/commands/topic.ts +37 -0
  88. package/packages/irc-core/src/config.ts +36 -5
  89. package/packages/irc-core/src/effects.ts +56 -1
  90. package/packages/irc-core/src/ports.ts +1653 -84
  91. package/packages/irc-core/src/protocol/numerics.ts +8 -0
  92. package/packages/irc-core/src/state/channel.ts +21 -1
  93. package/packages/irc-core/src/state/connection.ts +25 -1
  94. package/packages/irc-core/src/types.ts +48 -12
  95. package/packages/irc-core/tests/commands/chanserv.test.ts +1668 -0
  96. package/packages/irc-core/tests/commands/chathistory.test.ts +6 -0
  97. package/packages/irc-core/tests/commands/hostserv.test.ts +935 -0
  98. package/packages/irc-core/tests/commands/join.test.ts +393 -1
  99. package/packages/irc-core/tests/commands/markread.test.ts +361 -0
  100. package/packages/irc-core/tests/commands/memoserv.test.ts +654 -0
  101. package/packages/irc-core/tests/commands/mode.test.ts +381 -2
  102. package/packages/irc-core/tests/commands/nickserv.test.ts +807 -0
  103. package/packages/irc-core/tests/commands/oper.test.ts +13 -0
  104. package/packages/irc-core/tests/commands/operserv.test.ts +656 -0
  105. package/packages/irc-core/tests/commands/privmsg.test.ts +147 -0
  106. package/packages/irc-core/tests/commands/read-marker.test.ts +28 -28
  107. package/packages/irc-core/tests/commands/registration.test.ts +788 -14
  108. package/packages/irc-core/tests/commands/sasl.test.ts +185 -12
  109. package/packages/irc-core/tests/commands/server-info.test.ts +9 -5
  110. package/packages/irc-core/tests/commands/tagmsg.test.ts +73 -33
  111. package/packages/irc-core/tests/commands/topic.test.ts +94 -2
  112. package/packages/irc-core/tests/commands/unified-account.test.ts +416 -0
  113. package/packages/irc-core/tests/config.test.ts +49 -5
  114. package/packages/irc-core/tests/effects.test.ts +19 -0
  115. package/packages/irc-core/tests/message-store.test.ts +63 -0
  116. package/packages/irc-core/tests/persistent-services-store.test.ts +582 -0
  117. package/packages/irc-core/tests/services-store.test.ts +1289 -0
  118. package/packages/irc-core/tests/state/channel.test.ts +3 -0
  119. package/packages/irc-server/package.json +1 -1
  120. package/packages/irc-server/src/actor.ts +71 -16
  121. package/packages/irc-server/src/dispatch.ts +94 -7
  122. package/packages/irc-server/src/routing.ts +19 -0
  123. package/packages/irc-server/tests/actor.test.ts +623 -12
  124. package/packages/irc-server/tests/dispatch.test.ts +270 -2
  125. package/packages/irc-server/tests/routing.test.ts +6 -0
  126. package/packages/irc-test-support/package.json +1 -1
  127. package/packages/irc-test-support/src/in-memory-harness.ts +29 -3
  128. package/packages/irc-test-support/src/index.ts +1 -0
  129. package/packages/irc-test-support/tests/in-memory-harness.test.ts +32 -0
  130. package/tools/ci-hardening/package.json +1 -1
  131. package/tools/load-test/package.json +1 -1
  132. package/tools/tcp-ws-forwarder/package.json +1 -1
  133. package/tools/tcp-ws-forwarder/tests/forwarder.test.ts +2 -2
  134. package/apps/cf-worker/tests/fixtures/web-dist/app/index.html +0 -18
  135. package/packages/irc-core/tests/read-marker-store.test.ts +0 -108
@@ -253,9 +253,30 @@ connection is not identified as. The dispatch layer resolves it against
253
253
  Already-present seams the services reuse:
254
254
 
255
255
  - `AccountStore.verify('PLAIN', ...)` (TICKET-026 ✅) → a successful
256
- NickServ `IDENTIFY` and a SASL PLAIN login both reduce to "this
257
- connection is now account X", setting the `+r` user mode and
258
- emitting `ACCOUNT` to `account-notify`-capable peers.
256
+ NickServ `IDENTIFY`, a SASL PLAIN login, and a PASS-based account login
257
+ (TICKET-152 ✅) all reduce to "this connection is now account X". The
258
+ shared `applyAccountSuccess(state, ctx, account)` helper stamps the
259
+ account, sets the `+r` user mode, restores per-account state (read
260
+ markers, persisted away, queued memos), and emits `900 RPL_LOGGEDIN` +
261
+ `903 RPL_SASLSUCCESS` plus the `ACCOUNT` broadcast to
262
+ `account-notify`-capable peers.
263
+ - **PASS-based account auth (TICKET-152 ✅)** — a third account-login
264
+ entry point alongside SASL and NickServ `IDENTIFY`. A client that sends
265
+ `NICK <nick>` then `PASS <nick>:<password>` (then `USER`) is identified
266
+ to `ctx.accounts` at registration completion without speaking SASL. The
267
+ `<nick>:<password>` literal matches the `SASL_ACCOUNTS` seed format. The
268
+ payload nick (left of the first `:`) is the account name and MUST equal
269
+ `state.nick` at registration completion. Order of precedence at
270
+ registration is: (1) already identified (SASL/PASS-auth) → gate
271
+ satisfied; (2) `<nick>:<password>` verifies against `ctx.accounts` →
272
+ account set; (3) bare `PASS` matches `serverPassword` → gate satisfied;
273
+ (4) else `464 ERR_PASSWDMISMATCH` + disconnect. A bare `PASS <value>`
274
+ (no colon) is always a server-password candidate, never an account
275
+ credential; when `ctx.accounts` is unbound the `<nick>:<password>` form
276
+ is ignored and behaviour collapses to the server-password gate. Like
277
+ SASL, PASS-auth runs through `applyAccountSuccess` and therefore stamps
278
+ `+r`; a failed verify reveals nothing — unknown nick and wrong password
279
+ are indistinguishable (a fixed dummy verify equalises the failure path).
259
280
  - `MessageStore` (TICKET-063 ✅) → MemoServ's `SEND` is a record into
260
281
  a per-account memo queue; delivery is a fanout at identify time.
261
282
  - `clock` / `IdFactory` ports → memo ids, enforcement grace timers.
@@ -464,6 +485,52 @@ follows strict TDD (Red → Green → Refactor) per `CLAUDE.md` /
464
485
  `seedReadMarkers`; `CHATHISTORY BEFORE` with no pivot falls back to the
465
486
  connection's marker. TICKET-122 should fold this store into the unified
466
487
  services `UserStore` when it lands.
488
+ - **Divergence vs. spec compliance (TICKET-150 follow-up, shipped):**
489
+ TICKET-118 shipped **channel-wide** mark TAGMSG fanout for pragmatic parity
490
+ with the `draft/typing` precedent — every `message-tags` OR
491
+ `draft/read-marker` peer in the channel received the mark. The ircv3
492
+ `draft/read-marker` spec, however, mandates that a read marker **MUST NOT be
493
+ disclosed to other users**: a client's own mark is private to its account.
494
+ The Phase 14 follow-up (account-scoped `draft/read-marker` fanout) restored
495
+ spec compliance with two behaviour changes:
496
+
497
+ 1. **Identified originator → account-scoped fanout.** The
498
+ `tagmsgChannelReducer` now annotates the mark `TAGMSG` Broadcast effect
499
+ with the originator's `account`. The dispatch layer's cap-aware
500
+ `broadcast()` restricts the recipient set to channel members
501
+ authenticated as that same account (RFC 1459 case-folded comparison),
502
+ still honouring `except` (originator skip) and the OR-semantics `caps`
503
+ filter (`message-tags` OR `draft/read-marker`). Other accounts in the
504
+ channel never receive the mark; the originator's other live sessions on
505
+ the same account (multi-device sync, when they share the channel)
506
+ receive it. `filterClientTags` and its `+draft/read-marker` whitelist
507
+ are unchanged — only the *recipient set* changes.
508
+ 2. **Unidentified originator → no fanout (session-local).** An
509
+ unidentified connection (no account) emits **no** Broadcast for a read-
510
+ marker mark. Its mark stays session-local (the in-memory
511
+ `lastReadMarkers` still advances; persistence is a no-op without an
512
+ account, unchanged from TICKET-118). The originator's own client still
513
+ observes its mark via the `echo-message` self-echo. This matches the
514
+ spec's privacy default: a read marker never reaches *other* users, and
515
+ an unidentified connection has no account to share a mark with.
516
+
517
+ **Recipient-resolution primitive ("or equivalent" index).** The ticket's
518
+ description floated an `AccountConnections` index on the runtime. The
519
+ shipped implementation takes the spec-permitted "(or equivalent)" path:
520
+ there is **no separate index structure**. The recipient set is resolved at
521
+ dispatch time by walking the channel roster and consulting each member's
522
+ live `ConnectionState.account` via the existing `getConnectionInfo` port
523
+ method (the same per-recipient resolution already used for cap gating).
524
+ This is always fresh by construction — disconnected connections leave the
525
+ roster, and account changes are live references — so the staleness
526
+ concerns an explicit index would need (re-sync on `ACCOUNT`/SASL/
527
+ disconnect) do not apply. Global cross-channel account fanout (reaching an
528
+ account's sessions that are *not* in the marked channel) is a documented
529
+ follow-up, not needed for spec privacy: the mark is channel-specific, and
530
+ the persistence layer (`ServicesStore.setLastReadMarker`) already syncs a
531
+ session's marker on reconnect/rejoin via `seedReadMarkers`. 100% coverage
532
+ on the account-scoped fanout + dispatch filter (irc-core and irc-server
533
+ both enforce a 100% gate).
467
534
 
468
535
  #### TICKET-119 — IRCv3 `draft/pre-away` (persist away status)
469
536
  - **Phase:** 12
@@ -613,6 +680,49 @@ follows strict TDD (Red → Green → Refactor) per `CLAUDE.md` /
613
680
  - **TDD Outline:** Red on REGISTER→`+r`; green; then `+R`/`+M`;
614
681
  then KEEPTOPIC; then MLOCK.
615
682
 
683
+ Implementation notes (post-land):
684
+ - Three new boolean channel modes added to `ChannelModes`:
685
+ `registered` (`+r`), `blockUnidentified` (`+R`), `moderatedIdent`
686
+ (`+M`). They appear in the `324 RPL_CHANNELMODEIS` reply when set,
687
+ but `channelModeReducer` rejects `+r`/`+R`/`+M` via `MODE` with `472
688
+ ERR_UNKNOWNMODE` — only ChanServ may toggle them via
689
+ `ApplyChannelDelta` effects.
690
+ - `519 ERR_ALLMUSTREGISTER` added to `Numerics` for the `+R` JOIN gate.
691
+ - `ServicesStore` extended: `getChannel`, `setChannelFounder`,
692
+ `setChannelMlock`/`getChannelMlock`, `setChannelRestricted`,
693
+ `setChannelKeepTopic`, `setChannelTopic`/`getChannelTopic`. The
694
+ `RegisteredChannel` record now carries `mlock` / `restricted` /
695
+ `keepTopic` / `topic?`. The in-memory store returns defensive
696
+ copies (callers cannot mutate the store by editing returned
697
+ records).
698
+ - `chanservReducer` (new, `commands/chanserv.ts`) handles
699
+ `PRIVMSG ChanServ :<command>`. Subcommands: `REGISTER`, `DROP`,
700
+ `INFO`, `SET FOUNDER/MLOCK/RESTRICTED/KEEPTOPIC`. Channel
701
+ mutations are emitted as `ApplyChannelDelta` effects so the actor
702
+ applies them to the authoritative `ChannelState`.
703
+ - `SET RESTRICTED ON|OFF` toggles the `+R` channel mode (and stores
704
+ the preference). `SET KEEPTOPIC ON|OFF` toggles topic persistence
705
+ and drops any previously-stored snapshot when turned off.
706
+ - JOIN reducer consults services on the first join after empty-recreate
707
+ to restore a KEEPTOPIC snapshot (emits `332`/`333` to the joiner).
708
+ `+R` JOIN gate rejects unidentified clients with `519`.
709
+ - TOPIC reducer persists the snapshot on every change (set or clear)
710
+ when KEEPTOPIC is on.
711
+ - PRIVMSG/NOTICE channel reducers enforce `+M` (op/voice/identified
712
+ pass) and `+R` (identified pass) with `404`; NOTICE is silent.
713
+ - `channelModeReducer` reapplies MLOCK after every applied MODE: any
714
+ positively-locked boolean mode that ended up unset (whether by this
715
+ MODE or by prior state) is re-asserted via a ChanServ-emitted
716
+ `ApplyChannelDelta` + `Broadcast` (`:ChanServ!ChanServ@services
717
+ MODE #chan +…`). Defence in depth — services-only modes (`+r`) are
718
+ also defended, so the registered mark survives even if a future
719
+ code path applies an `ApplyChannelDelta` clearing it.
720
+ - Actor routes `PRIVMSG ChanServ` (case-insensitive) to
721
+ `reducers.chanserv`; NOTICE and unbound-services cases fall through
722
+ to the normal user-target reducer (RFC: NOTICE is silent).
723
+ - 100% coverage on `chanserv.ts`, `mode.ts` (reapply branch), and the
724
+ new join/privmsg/topic branches.
725
+
616
726
  #### TICKET-126 — ChanServ: ACCESS / LEVELS (SOP/AOP/VOP/HOP)
617
727
  - **Phase:** 13
618
728
  - **Points:** 5
@@ -15,8 +15,8 @@ opening a PR that bumps versions and updates `CHANGELOG.md`; the
15
15
  maintainer team cuts the tag.
16
16
 
17
17
  Cross-reference: `README.md` (Roadmap), `progress.md` / `tickets.md`
18
- (release content), `docs/deployment-cf.md` (prod deploy step §6),
19
- `.github/workflows/ci.yml`, `.github/workflows/deploy-cf.yml`.
18
+ (release content), `docs/Cloudflare-Deployment-Guide.md` (prod deploy
19
+ step §6), `.github/workflows/ci.yml`, `.github/workflows/deploy-cf.yml`.
20
20
 
21
21
  ---
22
22
 
@@ -37,7 +37,7 @@ this repo itself) should pin exact versions.
37
37
 
38
38
  ### 1.1 Workspace version lock
39
39
 
40
- All eight `package.json` files (root + 7 workspace packages) carry the
40
+ All fifteen `package.json` files (root + 14 workspace packages) carry the
41
41
  **same** version string. They are bumped together in the release
42
42
  commit. The pre-release checklist in §4 enforces this.
43
43
 
@@ -132,17 +132,20 @@ node apps/cf-worker/scripts/smoke.mjs \
132
132
  If any box fails, **stop**. Fix forward on `main` and restart the
133
133
  checklist — do not tag over a broken tip.
134
134
 
135
- ### 4.1 What is explicitly out of scope for a 0.x release
135
+ ### 4.1 What is explicitly out of scope for the 0.x line
136
136
 
137
- - AWS adapter (TICKET-039 through TICKET-044).
138
- - SASL authentication (TICKET-026).
139
- - Flood control (TICKET-028).
140
- - Server password / rate limits / cloaking (TICKET-046).
141
- - Config loader (TICKET-047).
137
+ Items still pending in `progress.md` that are **not** blockers for a
138
+ given release they ship when ready and are called out under "Known
139
+ limitations" in the GitHub Release body:
142
140
 
143
- These belong in a later 0.x release. Their absence is **not** a
144
- blocker and should be called out under "Known limitations" in the
145
- GitHub Release body.
141
+ - Client compatibility sweep (WeeChat / HexChat / IRCCloud / TheLounge /
142
+ matrix-IRC bridge) `progress.md` TICKET-050.
143
+ - Playwright headless-browser e2e for the web client — TICKET-138.
144
+ - Driving the sub-100% packages to full coverage — TICKET-143 through
145
+ TICKET-148.
146
+
147
+ Their absence is **not** a blocker. If a release intentionally defers an
148
+ in-progress ticket, name it under "Known limitations".
146
149
 
147
150
  ---
148
151
 
@@ -184,7 +187,7 @@ understand what changed and what they need to do.
184
187
 
185
188
  ## 6. Bumping versions
186
189
 
187
- There are eight `package.json` files. They must end up at the same
190
+ There are fifteen `package.json` files. They must end up at the same
188
191
  version:
189
192
 
190
193
  ```
@@ -192,10 +195,17 @@ version:
192
195
  packages/irc-core/package.json
193
196
  packages/irc-server/package.json
194
197
  packages/in-memory-runtime/package.json
198
+ packages/irc-test-support/package.json
195
199
  packages/cf-adapter/package.json
200
+ packages/aws-adapter/package.json
196
201
  apps/local-cli/package.json
197
202
  apps/cf-worker/package.json
203
+ apps/cf-tcp-container/package.json
204
+ apps/aws-stack/package.json
205
+ apps/web/package.json
198
206
  tools/tcp-ws-forwarder/package.json
207
+ tools/load-test/package.json
208
+ tools/ci-hardening/package.json
199
209
  ```
200
210
 
201
211
  To cut `v0.X.Y`: