serverless-ircd 0.8.0 → 0.10.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 +4 -0
- package/.github/workflows/deploy-aws.yml +156 -32
- package/.github/workflows/deploy-cf-tcp.yml +11 -9
- package/.github/workflows/deploy-cf.yml +14 -14
- package/CHANGELOG.md +550 -0
- package/README.md +275 -222
- package/apps/aws-stack/README.md +3 -5
- package/apps/aws-stack/bin/aws.ts +82 -9
- package/apps/aws-stack/cdk.json +0 -3
- package/apps/aws-stack/package.json +3 -4
- package/apps/aws-stack/src/aws-stack.ts +177 -52
- package/apps/aws-stack/src/static-site.ts +323 -0
- package/apps/aws-stack/tests/smoke-helpers.test.ts +1 -1
- package/apps/aws-stack/tests/stack.test.ts +267 -92
- package/apps/aws-stack/tests/static-site.test.ts +491 -0
- package/apps/aws-stack/tests/synth-no-bundle.test.ts +0 -1
- package/apps/cf-tcp-container/package.json +2 -3
- package/apps/cf-tcp-container/src/container-server.ts +33 -10
- package/apps/cf-tcp-container/tests/config-loader.test.ts +43 -0
- package/apps/cf-tcp-container/tests/container-server.test.ts +249 -1
- package/apps/cf-tcp-container/tests/persistence.test.ts +9 -0
- package/apps/cf-tcp-container/tests/tls-e2e.test.ts +24 -5
- package/apps/cf-tcp-container/wrangler.toml +1 -10
- package/apps/cf-worker/package.json +3 -4
- package/apps/cf-worker/wrangler.toml +12 -71
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/server.ts +115 -48
- package/apps/local-cli/tests/config-resolution.test.ts +65 -0
- package/apps/local-cli/tests/motd-file-non-error.test.ts +29 -0
- package/apps/local-cli/tests/rehash.test.ts +147 -0
- package/apps/local-cli/tests/server-helpers.test.ts +63 -0
- package/apps/local-cli/tests/tcp.test.ts +89 -0
- package/apps/local-cli/tests/ws-subprotocol.test.ts +92 -0
- package/apps/web/landing/favicon.ico +0 -0
- package/apps/web/landing/index.html +227 -3
- package/apps/web/package.json +3 -2
- package/apps/web/scripts/build.mjs +91 -6
- package/apps/web/src/build-env.ts +125 -4
- package/apps/web/src/config-schema.ts +20 -6
- package/apps/web/src/render-docs.ts +292 -0
- package/apps/web/static/{config.staging.json → config.prod-aws.json} +3 -2
- package/apps/web/tests/build-env.test.ts +210 -9
- package/apps/web/tests/build-smoke.test.ts +33 -4
- package/apps/web/tests/config-schema.test.ts +149 -25
- package/apps/web/tests/landing-content.test.ts +103 -0
- package/apps/web/tests/render-docs.test.ts +198 -0
- package/docs/AWS-Adapter-Architecture.md +3 -2
- package/docs/AWS-Deployment.md +670 -96
- package/docs/AWS-TCP-Deployment.md +20 -45
- package/docs/Cloudflare-Deployment-Guide.md +87 -113
- package/docs/Cloudflare-TCP-Deployment.md +25 -49
- package/docs/Release-Process.md +27 -23
- package/docs/Services.md +102 -23
- package/docs/WebClientGuide.md +35 -26
- package/package.json +7 -10
- package/packages/aws-adapter/package.json +1 -1
- package/packages/aws-adapter/src/aws-runtime.ts +15 -1
- package/packages/aws-adapter/src/cdk-table-defs.ts +6 -11
- package/packages/aws-adapter/src/config-loader.ts +19 -2
- package/packages/aws-adapter/src/dynamo-services-store.ts +7 -0
- package/packages/aws-adapter/src/handlers/connect.ts +26 -0
- package/packages/aws-adapter/src/handlers/default.ts +190 -123
- package/packages/aws-adapter/src/handlers/index.ts +67 -23
- package/packages/aws-adapter/src/handlers/nlb-stream.ts +13 -8
- package/packages/aws-adapter/src/index.ts +5 -7
- package/packages/aws-adapter/src/origin-allowlist.ts +94 -0
- package/packages/aws-adapter/src/serialize.ts +15 -0
- package/packages/aws-adapter/src/tables.ts +2 -12
- package/packages/aws-adapter/tests/aws-harness.ts +0 -1
- package/packages/aws-adapter/tests/aws-runtime.test.ts +23 -1
- package/packages/aws-adapter/tests/config-loader.test.ts +66 -0
- package/packages/aws-adapter/tests/connect.test.ts +124 -1
- package/packages/aws-adapter/tests/connection-counter.test.ts +17 -0
- package/packages/aws-adapter/tests/default-occ.test.ts +219 -0
- package/packages/aws-adapter/tests/dynamo-services-store-unit.test.ts +11 -0
- package/packages/aws-adapter/tests/global-setup.ts +28 -1
- package/packages/aws-adapter/tests/gone-exception.test.ts +21 -2
- package/packages/aws-adapter/tests/handlers.test.ts +117 -11
- package/packages/aws-adapter/tests/migrate-accounts-to-services.test.ts +164 -0
- package/packages/aws-adapter/tests/nlb-stream.test.ts +29 -1
- package/packages/aws-adapter/tests/origin-allowlist.test.ts +110 -0
- package/packages/aws-adapter/tests/ping-checker.test.ts +0 -1
- package/packages/aws-adapter/tests/stats.test.ts +0 -3
- package/packages/aws-adapter/tests/sweeper.test.ts +20 -1
- package/packages/aws-adapter/tests/tables.test.ts +1 -8
- package/packages/aws-adapter/tests/transactions.test.ts +0 -1
- package/packages/cf-adapter/package.json +1 -5
- package/packages/cf-adapter/src/cf-runtime.ts +59 -8
- package/packages/cf-adapter/src/channel-do.ts +13 -3
- package/packages/cf-adapter/src/connection-do.ts +284 -115
- package/packages/cf-adapter/src/d1-services-store.ts +63 -26
- package/packages/cf-adapter/src/env.ts +11 -10
- package/packages/cf-adapter/src/index.ts +0 -6
- package/packages/cf-adapter/tests/cf-runtime.test.ts +101 -1
- package/packages/cf-adapter/tests/channel-do.test.ts +118 -1
- package/packages/cf-adapter/tests/connection-do-coverage.test.ts +460 -0
- package/packages/cf-adapter/tests/connection-do-pure.test.ts +222 -51
- package/packages/cf-adapter/tests/connection-do-sasl-d1.test.ts +62 -38
- package/packages/cf-adapter/tests/d1-services-store.test.ts +53 -2
- package/packages/cf-adapter/tests/serialize.test.ts +25 -0
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/irc-core/package.json +1 -1
- package/packages/irc-core/src/account-migration.ts +140 -0
- package/packages/irc-core/src/commands/account-auth.ts +60 -35
- package/packages/irc-core/src/commands/chanserv.ts +288 -4
- package/packages/irc-core/src/commands/hostserv.ts +38 -3
- package/packages/irc-core/src/commands/index.ts +1 -0
- package/packages/irc-core/src/commands/join.ts +41 -35
- package/packages/irc-core/src/commands/memoserv.ts +1 -1
- package/packages/irc-core/src/commands/nickserv.ts +138 -15
- package/packages/irc-core/src/commands/registration.ts +28 -17
- package/packages/irc-core/src/commands/sasl.ts +22 -31
- package/packages/irc-core/src/commands/service-aliases.ts +52 -0
- package/packages/irc-core/src/commands/topic.ts +23 -10
- package/packages/irc-core/src/config.ts +35 -9
- package/packages/irc-core/src/credential-hashing.ts +11 -54
- package/packages/irc-core/src/index.ts +1 -0
- package/packages/irc-core/src/ports.ts +159 -179
- package/packages/irc-core/src/state/channel.ts +17 -0
- package/packages/irc-core/src/types.ts +38 -10
- package/packages/irc-core/tests/account-migration.test.ts +133 -0
- package/packages/irc-core/tests/commands/chanserv.test.ts +668 -1
- package/packages/irc-core/tests/commands/hostserv.test.ts +71 -0
- package/packages/irc-core/tests/commands/join.test.ts +179 -0
- package/packages/irc-core/tests/commands/markread.test.ts +54 -0
- package/packages/irc-core/tests/commands/memoserv.test.ts +19 -0
- package/packages/irc-core/tests/commands/nickserv.test.ts +422 -3
- package/packages/irc-core/tests/commands/oper.test.ts +15 -0
- package/packages/irc-core/tests/commands/registration.test.ts +336 -108
- package/packages/irc-core/tests/commands/sasl.test.ts +194 -169
- package/packages/irc-core/tests/commands/service-aliases.test.ts +52 -0
- package/packages/irc-core/tests/commands/unified-account.test.ts +102 -84
- package/packages/irc-core/tests/credential-hashing.test.ts +0 -78
- package/packages/irc-core/tests/message-store.test.ts +5 -0
- package/packages/irc-core/tests/persistent-services-store.test.ts +71 -12
- package/packages/irc-core/tests/ports.test.ts +71 -0
- package/packages/irc-core/tests/services-store.test.ts +204 -0
- package/packages/irc-core/vitest.config.ts +6 -1
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +80 -44
- package/packages/irc-server/tests/actor.test.ts +384 -50
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/in-memory-harness.ts +8 -5
- package/packages/irc-test-support/src/scenarios.ts +21 -6
- package/packages/irc-test-support/tests/in-memory-harness.test.ts +19 -0
- package/packages/irc-test-support/vitest.config.ts +6 -1
- package/pnpm-workspace.yaml +1 -0
- package/scripts/__tests__/deploy-web-aws.test.ts +491 -0
- package/scripts/deploy-web-aws.mjs +290 -0
- package/scripts/package.json +23 -0
- package/scripts/tsconfig.test.json +12 -0
- package/scripts/vitest.config.ts +19 -0
- package/tools/ci-hardening/package.json +1 -1
- package/tools/ci-hardening/src/index.ts +2 -0
- package/tools/ci-hardening/src/validate.ts +57 -0
- package/tools/ci-hardening/tests/deploy-aws-oidc.test.ts +96 -0
- package/tools/ci-hardening/tests/validate.test.ts +42 -0
- package/tools/load-test/package.json +1 -1
- package/tools/load-test/src/client.ts +13 -13
- package/tools/load-test/tests/client.test.ts +258 -2
- package/tools/load-test/tests/config.test.ts +39 -0
- package/tools/load-test/tests/harness.test.ts +21 -0
- package/tools/load-test/tests/metrics.test.ts +7 -0
- package/tools/migrate-accounts-to-services.ts +270 -0
- package/tools/package.json +2 -1
- package/tools/seed-aws-accounts.ts +35 -10
- package/tools/seed-cf-accounts.ts +42 -9
- package/tools/tcp-ws-forwarder/package.json +1 -1
- package/tools/tcp-ws-forwarder/tests/close-error.test.ts +40 -0
- package/tools/tcp-ws-forwarder/tests/defensive-branches.test.ts +78 -0
- package/tools/tcp-ws-forwarder/tests/forwarder.test.ts +51 -0
- package/tools/tcp-ws-forwarder/tests/logger.test.ts +31 -1
- package/packages/aws-adapter/src/account-store.ts +0 -121
- package/packages/aws-adapter/src/dynamo-account-store.ts +0 -95
- package/packages/aws-adapter/tests/account-store-dynamo.test.ts +0 -223
- package/packages/aws-adapter/tests/account-store.test.ts +0 -276
- package/packages/cf-adapter/src/d1-account-store.ts +0 -198
- package/packages/cf-adapter/tests/d1-account-store.test.ts +0 -274
- package/packages/irc-core/tests/account-store.test.ts +0 -131
package/README.md
CHANGED
|
@@ -6,48 +6,68 @@ 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:** **
|
|
10
|
-
>
|
|
11
|
-
>
|
|
9
|
+
> **Status:** **preview.** The pure protocol core, the `IrcRuntime`
|
|
10
|
+
> port + in-memory runtime, a runnable local CLI server, the
|
|
11
|
+
> **Cloudflare Workers** adapter, and the **AWS** (API Gateway
|
|
12
12
|
> WebSocket + Lambda + DynamoDB + CDK) adapter are all functional and
|
|
13
|
-
> deployed to staging.
|
|
14
|
-
>
|
|
15
|
-
>
|
|
16
|
-
>
|
|
17
|
-
>
|
|
18
|
-
>
|
|
19
|
-
>
|
|
20
|
-
>
|
|
21
|
-
>
|
|
22
|
-
>
|
|
23
|
-
>
|
|
24
|
-
> `
|
|
25
|
-
> `
|
|
26
|
-
>
|
|
27
|
-
>
|
|
28
|
-
>
|
|
29
|
-
>
|
|
30
|
-
>
|
|
31
|
-
>
|
|
32
|
-
>
|
|
33
|
-
>
|
|
34
|
-
>
|
|
35
|
-
>
|
|
36
|
-
>
|
|
37
|
-
>
|
|
38
|
-
>
|
|
39
|
-
>
|
|
40
|
-
> (
|
|
41
|
-
>
|
|
42
|
-
>
|
|
43
|
-
> `
|
|
44
|
-
>
|
|
45
|
-
>
|
|
46
|
-
> `
|
|
47
|
-
>
|
|
48
|
-
>
|
|
49
|
-
>
|
|
50
|
-
>
|
|
13
|
+
> deployed to staging.
|
|
14
|
+
>
|
|
15
|
+
> **Dual transport.** Both adapters speak a real `irc+tls :6697`
|
|
16
|
+
> (TLS-over-TCP) surface for stock IRC clients — Cloudflare via
|
|
17
|
+
> Spectrum + a Container origin, AWS via a Network Load Balancer +
|
|
18
|
+
> Lambda streaming — alongside the WebSocket path, plus **SASL
|
|
19
|
+
> `EXTERNAL` via mTLS** and a **Cloudflare D1-backed SASL account
|
|
20
|
+
> store**.
|
|
21
|
+
>
|
|
22
|
+
> **Complete protocol surface.** The deferred IRC verbs (`KILL`,
|
|
23
|
+
> `REHASH`, `LUSERS` + `STATS`, `TRACE`, `WALLOPS`, `SETNAME`) are
|
|
24
|
+
> implemented, and the S2S (`CONNECT` / `SQUIT` / `LINKS`) and obsolete
|
|
25
|
+
> RFC 2812 (`SERVICE` / `SUMMON` / `USERS`) verbs are formally dropped.
|
|
26
|
+
>
|
|
27
|
+
> **IRCv3 extensions.** Negotiated caps include `account-notify`,
|
|
28
|
+
> `msgid`, `standard-replies`, `MONITOR`, `labeled-response`, `sts`,
|
|
29
|
+
> `draft/typing`, `draft/multiline`, `draft/read-marker`, and
|
|
30
|
+
> `draft/pre-away`, plus the ISUPPORT tokens for each and the
|
|
31
|
+
> read-only user mode `S` (TLS connected).
|
|
32
|
+
>
|
|
33
|
+
> **Web client.** A vendored **Kiwi IRC** SPA is served at
|
|
34
|
+
> `/webclient/` and a static landing page at `/` directly by the
|
|
35
|
+
> Cloudflare Worker (via its `[assets]` binding) **or** by AWS via an
|
|
36
|
+
> **S3 + CloudFront + OAC** `StaticSite` construct (opt-in via CDK
|
|
37
|
+
> context). One `apps/web/dist/` artifact ships on both platforms
|
|
38
|
+
> unchanged. The `docs/` wiki is rendered to standalone HTML at
|
|
39
|
+
> `/docs/`. Both platforms wire **Cross-Site WebSocket Hijacking
|
|
40
|
+
> (CSWSH) defense** into the WS upgrade path: the CF Worker uses
|
|
41
|
+
> same-origin auto-derive by default with an optional `WEB_ORIGINS`
|
|
42
|
+
> allowlist for cross-origin deploys; the AWS `$connect` Lambda uses
|
|
43
|
+
> an explicit `WEB_ORIGINS` allowlist only (the SPA and the API
|
|
44
|
+
> Gateway wss endpoint are on different origins, so auto-derive does
|
|
45
|
+
> not apply). AWS deploys are **stack-output-driven** (a two-phase
|
|
46
|
+
> `scripts/deploy-web-aws.mjs` bakes the real `ConnectUrl` into the
|
|
47
|
+
> SPA config, then `s3 sync` + CloudFront invalidation).
|
|
48
|
+
>
|
|
49
|
+
> **Deploy security.** The AWS CI deploy is **OIDC-only** — the
|
|
50
|
+
> GitHub Actions workflow assumes an IAM role via GitHub OIDC web
|
|
51
|
+
> identity (no long-lived access keys, with an in-workflow assertion
|
|
52
|
+
> that the assumed role matches `AWS_DEPLOY_ROLE_ARN` before any
|
|
53
|
+
> `cdk deploy`). API Gateway full-frame body logging
|
|
54
|
+
> (`DataTraceEnabled`) is **off by default and hard-locked**, so IRC
|
|
55
|
+
> frames (`PASS`, `AUTHENTICATE <SASL-PLAIN>`, channel keys,
|
|
56
|
+
> `PRIVMSG`/`NOTICE`) are never written to CloudWatch; the only way
|
|
57
|
+
> back on is an explicit two-flag sandbox escape hatch.
|
|
58
|
+
>
|
|
59
|
+
> **Integrated IRC services.** NickServ, ChanServ, HostServ,
|
|
60
|
+
> OperServ, and MemoServ run inside the daemon (no separate services
|
|
61
|
+
> process, no S2S link) backed by a `ServicesStore` port with
|
|
62
|
+
> **persistent D1 (Cloudflare) and DynamoDB (AWS) backends**
|
|
63
|
+
> (write-behind, surviving redeploys). The `ServicesStore` is the
|
|
64
|
+
> **single credential home**: SASL PLAIN, SASL EXTERNAL (CertFP),
|
|
65
|
+
> NickServ `IDENTIFY`, and `PASS <nick>:<password>` all verify through
|
|
66
|
+
> the same scrypt-hashed `verifyNick` / `verifyCertFP` surface, and
|
|
67
|
+
> `draft/read-marker` / `draft/pre-away` persist through the
|
|
68
|
+
> `ServicesStore`.
|
|
69
|
+
>
|
|
70
|
+
> See `CHANGELOG.md` for the per-release manifests.
|
|
51
71
|
|
|
52
72
|
---
|
|
53
73
|
|
|
@@ -160,6 +180,8 @@ ServerlessIRCd/
|
|
|
160
180
|
│ ├── ci-hardening/ coverage-gate + mutation-config validators
|
|
161
181
|
│ ├── seed-aws-accounts.ts scrypt-hash SASL PLAIN accounts into DynamoDB
|
|
162
182
|
│ └── seed-cf-accounts.ts scrypt-hash SASL PLAIN accounts into Cloudflare D1
|
|
183
|
+
├── scripts/
|
|
184
|
+
│ └── deploy-web-aws.mjs stack-output-driven AWS web client deploy (describe → bake → s3 sync → invalidate)
|
|
163
185
|
├── pnpm-workspace.yaml turbo.json tsconfig.base.json
|
|
164
186
|
└── README.md CHANGELOG.md
|
|
165
187
|
```
|
|
@@ -295,27 +317,33 @@ treats `state.account !== undefined` as already authorised, so a
|
|
|
295
317
|
deployment with both `SERVER_PASSWORD` and configured SASL accounts
|
|
296
318
|
does not need to hand the shared password to identified users. The
|
|
297
319
|
server-wide gate is a shared deployment secret (never log it); per-user
|
|
298
|
-
credentials still go through the
|
|
299
|
-
(`
|
|
320
|
+
credentials still go through the unified services credential store
|
|
321
|
+
(`ServicesStore.verifyNick` — D1 `nickserv_accounts` / DynamoDB
|
|
322
|
+
`Services`), which is unaffected.
|
|
300
323
|
|
|
301
324
|
**PASS-based account login:** in addition to SASL, a client may identify
|
|
302
|
-
to its NickServ account
|
|
303
|
-
|
|
304
|
-
`
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
325
|
+
to its NickServ account by sending `PASS <nick>:<password>` (the same
|
|
326
|
+
`<nick>:<password>` literal the `SASL_ACCOUNTS` seed tooling uses).
|
|
327
|
+
When a `ServicesStore` is configured, `NICK alice` + `PASS alice:hunter2`
|
|
328
|
+
+ `USER …` verifies the credentials via `services.verifyNick` and, on
|
|
329
|
+
success, logs the connection in as `alice` — emitting `900 RPL_LOGGEDIN`
|
|
330
|
+
+ `903 RPL_SASLSUCCESS` (the same "account is set" numerics SASL uses)
|
|
331
|
+
before `001 RPL_WELCOME`, stamping user mode `+r`, and running the same
|
|
332
|
+
read-marker / away / memo replay as a SASL login. The login is also
|
|
333
|
+
honoured when `PASS <nick>:<password>` arrives **after** registration
|
|
334
|
+
has completed (e.g. a client that sent `NICK`+`USER` before `PASS`, or
|
|
335
|
+
any `PASS` arriving after `CAP END` resolved): the same verify +
|
|
336
|
+
`applyAccountSuccess` pipeline runs, emitting `900`/`903` and stamping
|
|
337
|
+
`+r` in-band. The nick left of the `:` must match the connection's
|
|
338
|
+
current nick; a bare `PASS <value>` with no colon is always treated as
|
|
339
|
+
a server-password candidate (silent no-op once registration has
|
|
340
|
+
completed), never as account credentials. A wrong password or unknown
|
|
341
|
+
nick is indistinguishable (no `904`/`464` from the auth path) and the
|
|
342
|
+
connection simply proceeds un-identified when no server password is
|
|
343
|
+
set. This composes with the gate above by precedence: an
|
|
344
|
+
already-identified connection (SASL or PASS-auth) satisfies the gate;
|
|
345
|
+
a `<nick>:<password>` that fails verify never matches a bare shared
|
|
346
|
+
secret, so a server with both configured still rejects it with `464`.
|
|
319
347
|
|
|
320
348
|
### Connecting
|
|
321
349
|
|
|
@@ -386,7 +414,7 @@ against any line-oriented WebSocket endpoint.
|
|
|
386
414
|
|
|
387
415
|
## Load testing (`tools/load-test`)
|
|
388
416
|
|
|
389
|
-
A synthetic WebSocket IRC client pool
|
|
417
|
+
A synthetic WebSocket IRC client pool. It opens N
|
|
390
418
|
connections to a `ws://` / `wss://` target, registers each (NICK/USER),
|
|
391
419
|
joins a channel, and optionally chats. Per-stage **p50 / p95 / p99
|
|
392
420
|
latency** (connect / register / join / message) and the **drop rate**
|
|
@@ -436,47 +464,104 @@ report).
|
|
|
436
464
|
## Web client (`apps/web`)
|
|
437
465
|
|
|
438
466
|
A vendored **Kiwi IRC** SPA served at `/webclient/` and a static project
|
|
439
|
-
landing page served at `/`, both baked out of `apps/web/dist
|
|
440
|
-
served
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
467
|
+
landing page served at `/`, both baked out of `apps/web/dist/`. The same
|
|
468
|
+
artifact is served by **both** platforms:
|
|
469
|
+
|
|
470
|
+
| Platform | HTTP/SPA origin | WebSocket origin | Same-origin? |
|
|
471
|
+
|-------------|----------------------------------------------------|---------------------------------------------------|--------------|
|
|
472
|
+
| Cloudflare | the Worker (`*.workers.dev` / custom domain) | the same Worker | **Yes** — one domain serves both |
|
|
473
|
+
| AWS | CloudFront (`*.cloudfront.net` / custom domain) | API Gateway (`*.execute-api.*.amazonaws.com`) | **No** — two different endpoints |
|
|
474
|
+
|
|
475
|
+
On Cloudflare the Worker's `[assets]` binding serves the SPA and the WS
|
|
476
|
+
upgrade on one origin — the browser opens a native `wss://` directly to
|
|
477
|
+
the Worker, no proxy or gateway. On AWS the SPA is fronted by an
|
|
478
|
+
**S3 + CloudFront + OAC** `StaticSite` construct
|
|
479
|
+
(`apps/aws-stack/src/static-site.ts`, opt-in via CDK context) and a
|
|
480
|
+
two-phase deploy bakes the real `ConnectUrl` into the SPA config (see
|
|
481
|
+
"Deploying on AWS" below). See `docs/WebClientGuide.md` for the CF
|
|
482
|
+
end-to-end guide and `docs/AWS-Deployment.md` §16 for the AWS path.
|
|
445
483
|
|
|
446
484
|
Build the SPA + landing page (needs the submodule + `yarn`, provided by
|
|
447
485
|
`corepack enable`):
|
|
448
486
|
|
|
449
487
|
```bash
|
|
450
488
|
git submodule update --init apps/web/upstream # one-time per clone
|
|
451
|
-
|
|
489
|
+
git submodule update --init docs # one-time per clone (docs site)
|
|
490
|
+
pnpm --filter web build # or build:prod (CF) / build:prod-aws (AWS)
|
|
452
491
|
# → apps/web/dist/index.html (landing page, served at /)
|
|
453
492
|
# → apps/web/dist/webclient/index.html (Kiwi SPA, served at /webclient/)
|
|
454
493
|
# → apps/web/dist/webclient/static/config.json (baked, env-specific)
|
|
494
|
+
# → apps/web/dist/docs/<slug>.html (rendered docs/, served at /docs/)
|
|
455
495
|
```
|
|
456
496
|
|
|
457
|
-
|
|
458
|
-
|
|
497
|
+
The build also renders the `docs/` submodule (the Gitea wiki, 23 GFM
|
|
498
|
+
markdown files) to standalone HTML under `apps/web/dist/docs/` so the
|
|
499
|
+
existing `[assets]` binding serves them at `/docs/<slug>.html` (and
|
|
500
|
+
`/docs/` for the index — `Home.md` becomes `index.html`). Requires the
|
|
501
|
+
`docs/` submodule checkout; the build fails loudly with the recovery
|
|
502
|
+
command if it is missing or empty (mirroring the Kiwi `upstream/` guard).
|
|
503
|
+
`Home.md` is the docs landing page; `ADR-Index.md` lists every ADR.
|
|
504
|
+
|
|
505
|
+
### Deploying on Cloudflare
|
|
506
|
+
|
|
507
|
+
Run everything locally (Worker serves SPA + landing page + WS on one
|
|
508
|
+
origin):
|
|
459
509
|
|
|
460
510
|
```bash
|
|
461
511
|
pnpm build # workspace packages
|
|
462
512
|
pnpm --filter @serverless-ircd/cf-worker dev # http://localhost:8787
|
|
463
513
|
# / → landing page
|
|
464
514
|
# /webclient/ → Kiwi SPA (opens ws://localhost:8787/)
|
|
515
|
+
# /docs/ → rendered docs/ (Home → index, per-page HTML)
|
|
465
516
|
# /health → plaintext liveness
|
|
466
517
|
```
|
|
467
518
|
|
|
468
|
-
Deploy
|
|
519
|
+
Deploy the Worker (Worker + assets in one command):
|
|
520
|
+
|
|
521
|
+
```bash
|
|
522
|
+
pnpm deploy:cf # wrangler deploy
|
|
523
|
+
```
|
|
524
|
+
|
|
525
|
+
### Deploying on AWS
|
|
526
|
+
|
|
527
|
+
The web client is **opt-in**: provision the `StaticSite` construct by
|
|
528
|
+
passing `webSite*` CDK context on the stack deploy, and (once the SPA
|
|
529
|
+
ships) set `webOrigins` for the CSWSH defence:
|
|
530
|
+
|
|
531
|
+
```bash
|
|
532
|
+
pnpm deploy:aws -- \
|
|
533
|
+
-c webSiteCustomDomain=app.example.com \
|
|
534
|
+
-c webSiteCertificateArn=arn:aws:acm:us-east-1:... \
|
|
535
|
+
-c webSiteHostedZoneName=example.com. -c webSiteHostedZoneId=... \
|
|
536
|
+
-c webOrigins=https://app.example.com
|
|
537
|
+
```
|
|
538
|
+
|
|
539
|
+
Then bake + ship the SPA (stack-output-driven — reads `ConnectUrl`,
|
|
540
|
+
`WebsiteBucketName`, `WebsiteDistributionId` from the stack outputs,
|
|
541
|
+
bakes the real wss URL into `config.json`, `s3 sync`s the build, and
|
|
542
|
+
invalidates the CloudFront edge cache):
|
|
469
543
|
|
|
470
544
|
```bash
|
|
471
|
-
|
|
545
|
+
node scripts/deploy-web-aws.mjs
|
|
472
546
|
```
|
|
473
547
|
|
|
548
|
+
The ACM certificate **must** be in `us-east-1` (CloudFront requirement).
|
|
549
|
+
See `docs/AWS-Deployment.md` §16 for the full two-phase flow, the
|
|
550
|
+
`server` / `direct_path` config split (irc-framework prepends `wss://`
|
|
551
|
+
itself, so the host is baked scheme-less), and the custom-domain setup.
|
|
552
|
+
|
|
474
553
|
### WebSocket Origin policy (CSWSH defense)
|
|
475
554
|
|
|
476
|
-
WebSocket upgrades do **not** follow the same-origin policy, so
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
555
|
+
WebSocket upgrades do **not** follow the same-origin policy, so a
|
|
556
|
+
malicious page can open a WebSocket to the IRC server from a victim's
|
|
557
|
+
browser and drive the session with their credentials (Cross-Site
|
|
558
|
+
WebSocket Hijacking, CSWSH). Both adapters enforce an `Origin` policy
|
|
559
|
+
on the upgrade — but the **modes differ**, because the CF Worker serves
|
|
560
|
+
the SPA and the wss endpoint on the same origin while AWS serves them
|
|
561
|
+
on different origins:
|
|
562
|
+
|
|
563
|
+
**Cloudflare** — see `apps/cf-worker/src/origin-allowlist.ts` and the
|
|
564
|
+
SPA guide §5. Two modes, evaluated in order:
|
|
480
565
|
|
|
481
566
|
1. **Explicit allowlist** — set `WEB_ORIGINS` (comma-separated) for
|
|
482
567
|
cross-origin deploys (SPA on a different domain than the Worker,
|
|
@@ -485,11 +570,27 @@ modes, evaluated in order:
|
|
|
485
570
|
unset/empty, the Worker compares the browser's `Origin` against the
|
|
486
571
|
request's own origin. Match → proceed; mismatch → `403 Forbidden`.
|
|
487
572
|
|
|
488
|
-
`WEB_ORIGINS` is **optional** — same-origin auto-derive needs
|
|
489
|
-
per-env config and works for `*.workers.dev`, custom domains, and
|
|
490
|
-
preview URLs alike.
|
|
491
|
-
|
|
492
|
-
|
|
573
|
+
`WEB_ORIGINS` is **optional** on CF — same-origin auto-derive needs
|
|
574
|
+
zero per-env config and works for `*.workers.dev`, custom domains, and
|
|
575
|
+
preview URLs alike.
|
|
576
|
+
|
|
577
|
+
**AWS** — see `packages/aws-adapter/src/origin-allowlist.ts` and
|
|
578
|
+
`docs/AWS-Deployment.md` §8.2. **Explicit allowlist only** (no
|
|
579
|
+
auto-derive): the SPA is on a CloudFront origin and the wss endpoint is
|
|
580
|
+
on an API Gateway origin, so the request's own host is never the SPA's
|
|
581
|
+
origin. The defence is **opt-in** — unset `WEB_ORIGINS` skips the check
|
|
582
|
+
entirely (existing bare-IRC deployments without a web frontend are
|
|
583
|
+
unchanged on upgrade); set it to the SPA's origin(s) once the web client
|
|
584
|
+
ships:
|
|
585
|
+
|
|
586
|
+
```bash
|
|
587
|
+
pnpm deploy:aws -- -c webOrigins=https://app.example.com
|
|
588
|
+
# or as a stack prop: webOrigins: 'https://app.example.com,https://staging.app.example.com'
|
|
589
|
+
```
|
|
590
|
+
|
|
591
|
+
Non-browser clients (curl, WeeChat, the `tcp-ws-forwarder`, scripted
|
|
592
|
+
harnesses) never send `Origin` and pass through unchanged on both
|
|
593
|
+
platforms.
|
|
493
594
|
|
|
494
595
|
---
|
|
495
596
|
|
|
@@ -552,92 +653,33 @@ port and an `IdFactory` port are injected so tests are fully deterministic.
|
|
|
552
653
|
|
|
553
654
|
## Roadmap
|
|
554
655
|
|
|
555
|
-
A v1 ships when
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
- **
|
|
567
|
-
|
|
568
|
-
- **
|
|
569
|
-
|
|
570
|
-
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
mTLS → SASL `EXTERNAL`, transport-parametrized contract suite, ADR-009.
|
|
579
|
-
- **Load testing & compatibility sweep.** 🔄 partial as of
|
|
580
|
-
**v0.6.0**: the `tools/load-test` 10k-connection harness is in-tree;
|
|
581
|
-
the formal 10k-connection report per platform and the client
|
|
582
|
-
compatibility matrix (WeeChat / HexChat / IRCCloud / TheLounge) are
|
|
583
|
-
still pending.
|
|
584
|
-
- **PLAN-FIXES remediation.** ✅ landed across v0.3.0–v0.4.0
|
|
585
|
-
(MessageStore wired into all three adapters, AccountStore end-to-end,
|
|
586
|
-
the DynamoDB `Accounts` table decided, CF enumeration RPCs, AWS QUIT
|
|
587
|
-
fanout, `OPER` / `TAGMSG` / `WHOWAS`, RFC 1459 case-mapping, real
|
|
588
|
-
MOTD, config-driven server identity, AWS max-clients admission,
|
|
589
|
-
CF D1 SASL store, and a sweep of dead-code / stale-comment cleanup).
|
|
590
|
-
- **Runtime & tooling baseline.** ✅ landed in **v0.4.0**
|
|
591
|
-
(Node ≥ 24 / pnpm 11).
|
|
592
|
-
- **IRCv3 WebSocket extension.** ✅ landed in **v0.4.0**:
|
|
593
|
-
`text.ircv3.net` / `binary.ircv3.net` subprotocol negotiation +
|
|
594
|
-
per-message framing across the CF Worker, AWS APIGW, local CLI, and
|
|
595
|
-
`tcp-ws-forwarder` (510-byte budget, `1009`/`1003` close codes,
|
|
596
|
-
legacy fallback).
|
|
597
|
-
- **Deferred IRC verbs.** ✅ landed in **v0.5.0**: `KILL`,
|
|
598
|
-
`REHASH`, `LUSERS` + `STATS`, `TRACE`, `WALLOPS`, `SETNAME`, plus
|
|
599
|
-
the formal drop of the S2S (`CONNECT`/`SQUIT`/`LINKS`) and obsolete
|
|
600
|
-
RFC 2812 (`SERVICE`/`SUMMON`/`USERS`) verbs. The protocol surface is
|
|
601
|
-
feature-complete for modern clients.
|
|
602
|
-
- **IRCv3 extension sweep.** ✅ landed in **v0.6.0**: ten
|
|
603
|
-
negotiated caps (`account-notify`, `msgid`, `standard-replies`,
|
|
604
|
-
`MONITOR`, `labeled-response`, `sts`, `draft/typing`,
|
|
605
|
-
`draft/multiline`, `draft/read-marker`, `draft/pre-away`), the
|
|
606
|
-
ISUPPORT tokens for the new caps (`MONITOR`/`MULTILINE`/`TYPING`/
|
|
607
|
-
`STATUSMSG`/`EXTBAN`/`ACCOUNTEXTBAN`), the read-only user mode `S`
|
|
608
|
-
(TLS connected), and the async push fanout seam (Registry hooks →
|
|
609
|
-
`MONITOR` online/offline, `BroadcastWallops`-style caps-gated
|
|
610
|
-
delivery).
|
|
611
|
-
- **IRC services.** ✅ landed in **v0.8.0**: `ServicesStore` port
|
|
612
|
-
+ integrated NickServ (`REGISTER`/`IDENTIFY`/`DROP`/`INFO`/`SET
|
|
613
|
-
ENFORCE` + nick enforcement), ChanServ (`REGISTER`/`DROP`/`SET`/
|
|
614
|
-
`ACCESS`/`LEVELS` + `+r`/`+R`/`+M`), HostServ
|
|
615
|
-
(`ON`/`OFF`/`REQUEST` + oper `SET`/`APPROVE`), OperServ
|
|
616
|
-
(`AKILL`/`JUPE`/`RAW`), MemoServ (`SEND`/`LIST`/`READ`/`DEL`).
|
|
617
|
-
Persistent backends ship alongside — **D1** on Cloudflare,
|
|
618
|
-
**DynamoDB** on AWS — both write-behind so registrations survive
|
|
619
|
-
redeploys. The SASL `AccountStore` and NickServ accounts are
|
|
620
|
-
**unified** under one scrypt-hashed credential store, `PASS
|
|
621
|
-
<nick>:<password>` identifies at registration, and `draft/read-marker`
|
|
622
|
-
/ `draft/pre-away` now persist through the `ServicesStore` (no longer
|
|
623
|
-
in-memory-only). See `docs/Services.md`.
|
|
624
|
-
- **Web client.** 🔄 partial as of **v0.7.0**: a vendored
|
|
625
|
-
Kiwi IRC SPA is served at `/webclient/` and a static landing page at `/`
|
|
626
|
-
by the Cloudflare Worker via an `[assets]` binding, with a Zod-
|
|
627
|
-
validated per-env config matrix and **CSWSH defense** (same-origin
|
|
628
|
-
auto-derive + optional `WEB_ORIGINS` allowlist). The Playwright
|
|
629
|
-
headless-browser e2e is the remaining open ticket.
|
|
630
|
-
See `docs/WebClientGuide.md` for the contributor/operator guide.
|
|
631
|
-
- **Coverage hardening.** 🔄 partial as of **v0.7.0**: `aws-adapter`
|
|
632
|
-
and `aws-stack` clear the 90% gate (v0.6.0); the CF packages run
|
|
633
|
-
under istanbul (v0.6.0). The remaining packages (`cf-adapter`,
|
|
634
|
-
`local-cli`, `load-test`, `cf-tcp-container`, `tcp-ws-forwarder`,
|
|
635
|
-
`irc-test-support`, `web`) are **above the gate but below 100%** —
|
|
636
|
-
follow-ups drive each to 100%.
|
|
656
|
+
A v1 ships when the parametrized contract suite passes against every
|
|
657
|
+
runtime, the client compatibility matrix is signed off, and every
|
|
658
|
+
package clears its coverage gate. Per-release manifests live in
|
|
659
|
+
`CHANGELOG.md`.
|
|
660
|
+
|
|
661
|
+
Active follow-ups:
|
|
662
|
+
|
|
663
|
+
- **Client compatibility sweep** — formal sign-off on ≥3 reference
|
|
664
|
+
clients (WeeChat / HexChat / IRCCloud / TheLounge) against both
|
|
665
|
+
deployed adapters, on both the WebSocket and `irc+tls :6697`
|
|
666
|
+
surfaces.
|
|
667
|
+
- **Web client e2e** — Playwright headless-browser e2e exercising the
|
|
668
|
+
vendored Kiwi IRC SPA against a deployed stack.
|
|
669
|
+
- **Coverage hardening** — `aws-adapter` and `aws-stack` clear the 90%
|
|
670
|
+
gate; the CF packages run under istanbul. The remaining packages
|
|
671
|
+
(`cf-adapter`, `local-cli`, `load-test`, `cf-tcp-container`,
|
|
672
|
+
`tcp-ws-forwarder`, `irc-test-support`, `web`) sit above the gate
|
|
673
|
+
but below 100%; follow-ups drive each to full coverage. `irc-core`,
|
|
674
|
+
`irc-server`, and `in-memory-runtime` are at 100%.
|
|
675
|
+
- **Persistent ChanServ ban list** — ban masks currently live on
|
|
676
|
+
`ChannelState.banMasks` and do not survive an empty-recreate of a
|
|
677
|
+
channel. Extending `ServicesStore` with a persistent ban list is the
|
|
678
|
+
documented next step.
|
|
637
679
|
|
|
638
680
|
---
|
|
639
681
|
|
|
640
|
-
## Protocol scope
|
|
682
|
+
## Protocol scope
|
|
641
683
|
|
|
642
684
|
**Core (RFC 1459/2812 subset):** registration (`NICK`/`USER`/`CAP`/`PASS`),
|
|
643
685
|
`PING`/`PONG`, `QUIT`, `JOIN`, `PART`, `PRIVMSG`, `NOTICE`, `MODE` (user +
|
|
@@ -654,9 +696,9 @@ disconnect), `REHASH` (config reload via `IrcRuntime.reloadConfig()`),
|
|
|
654
696
|
`WALLOPS` (global broadcast to every `+w` user).
|
|
655
697
|
|
|
656
698
|
(Out of scope: the S2S verbs `CONNECT`/`SQUIT`/`LINKS` and the obsolete
|
|
657
|
-
RFC 2812 verbs `SERVICE`/`SUMMON`/`USERS` are **formally dropped**
|
|
658
|
-
|
|
659
|
-
|
|
699
|
+
RFC 2812 verbs `SERVICE`/`SUMMON`/`USERS` are **formally dropped** —
|
|
700
|
+
they return `421 ERR_UNKNOWNCOMMAND`, and their reserved numerics have
|
|
701
|
+
been removed. S2S linking is a stated non-goal.)
|
|
660
702
|
|
|
661
703
|
**Channel modes:** `o v b i k l t n m s p`, plus the
|
|
662
704
|
services-derived modes `r` (registered), `R` (block unidentified
|
|
@@ -668,24 +710,48 @@ via `276 RPL_WHOISSECURE`, not settable via `MODE`) and the read-only
|
|
|
668
710
|
`r` (registered — set by NickServ `IDENTIFY` / SASL login, surfaced as
|
|
669
711
|
`+r`, not settable via `MODE`).
|
|
670
712
|
|
|
671
|
-
**Integrated IRC services
|
|
672
|
-
`
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
`
|
|
688
|
-
`
|
|
713
|
+
**Integrated IRC services:** the service nicks `NickServ`, `ChanServ`,
|
|
714
|
+
`HostServ`, `OperServ`, and `MemoServ` are reserved and routed to
|
|
715
|
+
dedicated pure reducers when a `ServicesStore` is bound — there is **no
|
|
716
|
+
separate services process and no S2S link**. Each service also accepts a
|
|
717
|
+
**shortcut verb** (`/NICKSERV`, `/NS`, `/CS`, `/HS`, `/MS`, `/OS`, …)
|
|
718
|
+
that the daemon rewrites to the equivalent `PRIVMSG <Service> :<args>`
|
|
719
|
+
for clients with a dedicated slash-command UX. Reached via `PRIVMSG
|
|
720
|
+
<Service> :<subcommand>`:
|
|
721
|
+
|
|
722
|
+
- **NickServ** — `REGISTER` / `IDENTIFY` (alias: `ID`) / `DROP` / `INFO`
|
|
723
|
+
(owner + oper only for the `Email:` line) / `SET ENFORCE` + nick
|
|
724
|
+
enforcement on the `NICK` path / `SET PASSWORD` (self-service password
|
|
725
|
+
change; requires the current password re-supplied). `INFO <nick>` with
|
|
726
|
+
no target resolves to the caller's own account.
|
|
727
|
+
- **ChanServ** — `REGISTER` / `DROP` / `SET` (`FOUNDER` / `MLOCK` /
|
|
728
|
+
`RESTRICTED` / `KEEPTOPIC`) / `INFO` / `ACCESS` (`SOP` / `AOP` / `HOP`
|
|
729
|
+
/ `VOP`, each `ADD` / `DEL` / `LIST`) / `LEVELS`, plus the prefix /
|
|
730
|
+
roster mutation verbs `OP`, `DEOP`, `VOICE`, `DEVOICE`, `KICK`, and
|
|
731
|
+
`BAN` / `UNBAN` (each emits the corresponding `:ChanServ MODE` /
|
|
732
|
+
`KICK` broadcast and `ApplyChannelDelta` against the channel
|
|
733
|
+
authority). Auto-op / auto-voice on `JOIN` follows the access list;
|
|
734
|
+
the first joiner of a registered channel no longer gets auto-opped
|
|
735
|
+
merely for being first.
|
|
736
|
+
- **HostServ** — `ON` / `OFF` / `REQUEST` + oper `SET` / `APPROVE` /
|
|
737
|
+
`ACTIVATE` / `REJECT` / `LIST`, with `CHGHOST` fanout. An assigned
|
|
738
|
+
vhost is **auto-applied on identify** (SASL, NickServ `IDENTIFY`, or
|
|
739
|
+
PASS-auth): `state.host` is set, `state.vhostActive` is stamped, and
|
|
740
|
+
any currently-joined `chghost`-capable peers see the broadcast.
|
|
741
|
+
Oper-only subcommands are hidden from non-opers in the help NOTICE.
|
|
742
|
+
- **OperServ** — `AKILL` / `JUPE` / `UNJUPE` / `RAW` (oper-only).
|
|
743
|
+
- **MemoServ** — `SEND` / `LIST` / `READ` / `DEL` with queue delivery
|
|
744
|
+
at identify.
|
|
745
|
+
|
|
746
|
+
The `ServicesStore` is the **single credential home**: SASL PLAIN, SASL
|
|
747
|
+
EXTERNAL (CertFP), NickServ `IDENTIFY`, and `PASS <nick>:<password>` all
|
|
748
|
+
verify through the same scrypt-hashed `verifyNick` / `verifyCertFP`
|
|
749
|
+
surface, so a registered nick is also a SASL login and vice versa.
|
|
750
|
+
Backends: D1 on Cloudflare, DynamoDB on AWS, in-memory for the
|
|
751
|
+
local CLI / tests (all write-behind; registrations survive redeploys).
|
|
752
|
+
When no store is bound, services commands reply `501` and the rest of
|
|
753
|
+
the daemon is unaffected. See `docs/Services.md` for the full
|
|
754
|
+
reference.
|
|
689
755
|
|
|
690
756
|
**IRCv3 extensions (negotiated via `CAP`):** `message-tags` (incl. the
|
|
691
757
|
`TAGMSG` command), `server-time`, `account-tag`, `account-notify`
|
|
@@ -724,42 +790,29 @@ bridges a stock TCP client to a deployed WebSocket endpoint.
|
|
|
724
790
|
|
|
725
791
|
## Further reading
|
|
726
792
|
|
|
727
|
-
- `CHANGELOG.md` — per-release manifests
|
|
728
|
-
the v0.2.0 work (AWS adapter, SASL, flood control, security hardening, CI
|
|
729
|
-
gates), v0.3.0 (OPER, TAGMSG, RFC 1459 case-mapping, real MOTD,
|
|
730
|
-
ChannelRegistryDO, SASL account persistence), v0.4.0 (dual transport
|
|
731
|
-
`irc+tls :6697`, SASL `EXTERNAL`/mTLS, WHOWAS + remaining verbs, D1 account
|
|
732
|
-
store, Node 24/pnpm 11), v0.5.0 (deferred IRC verbs `KILL` / `REHASH`
|
|
733
|
-
/ `LUSERS` + `STATS` / `TRACE` / `WALLOPS` / `SETNAME`, plus the formal
|
|
734
|
-
drop of the S2S and obsolete RFC 2812 verbs), v0.6.0 (the IRCv3
|
|
735
|
-
extension sweep — `account-notify`, `msgid`, `standard-replies`,
|
|
736
|
-
`MONITOR`, `labeled-response`, `sts`, `draft/typing`, `draft/multiline`,
|
|
737
|
-
`draft/read-marker`, `draft/pre-away`, plus user mode `S`; the
|
|
738
|
-
required-`serverName` gate; `tools/load-test`; aws-adapter / aws-stack
|
|
739
|
-
coverage unblocks; CF packages moved to istanbul; deploys now
|
|
740
|
-
manual-only), and **v0.7.0** (the web client — vendored Kiwi IRC SPA
|
|
741
|
-
at `/webclient/`, static landing page at `/`, Cloudflare Worker
|
|
742
|
-
`[assets]` binding, per-env Zod-validated config matrix, and CSWSH
|
|
743
|
-
defense via same-origin auto-derive + optional `WEB_ORIGINS`;
|
|
744
|
-
`/health` becomes an explicit route; mutation-killing tests for
|
|
745
|
-
`irc-core`'s parser / batch / numerics), and **v0.8.0** (integrated
|
|
746
|
-
IRC services — NickServ / ChanServ / HostServ / OperServ / MemoServ
|
|
747
|
-
backed by a `ServicesStore` port with persistent D1 + DynamoDB
|
|
748
|
-
backends; unified SASL + NickServ scrypt-hashed account store;
|
|
749
|
-
`PASS <nick>:<password>` registration login; the `draft/read-marker`
|
|
750
|
-
`MARKREAD` verb).
|
|
793
|
+
- `CHANGELOG.md` — per-release manifests in Keep a Changelog format.
|
|
751
794
|
- `docs/Services.md` — operator and contributor reference for the
|
|
752
795
|
integrated IRC services: architecture (integrated vs. pseudo-client),
|
|
753
|
-
the per-service command tables
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
796
|
+
the per-service command tables (including ChanServ `OP`/`DEOP`/
|
|
797
|
+
`VOICE`/`DEVOICE`/`KICK`/`BAN`/`UNBAN`, HostServ oper approval flow,
|
|
798
|
+
the `+r`/`+R`/`+M` modes, nick enforcement, the unified scrypt
|
|
799
|
+
account store, service shortcut verbs), adapter backends, and an
|
|
800
|
+
end-to-end registration walkthrough.
|
|
801
|
+
- `docs/WebClientGuide.md` — end-to-end contributor/operator doc for
|
|
802
|
+
the **Cloudflare** web client: build pipeline, per-env config matrix,
|
|
803
|
+
CSWSH rationale and the optional `WEB_ORIGINS` var, local dev,
|
|
804
|
+
optional Cloudflare Pages alternative, and troubleshooting.
|
|
805
|
+
- `docs/AWS-Deployment.md` — end-to-end AWS deploy guide: first-time
|
|
806
|
+
setup, CDK reference, DynamoDB capacity planning, region strategy,
|
|
807
|
+
cost notes, **OIDC-only CI** (§17), **APIGW `DataTraceEnabled` hard-
|
|
808
|
+
lock** (§7.6), and the **S3 + CloudFront + OAC web client** (§16,
|
|
809
|
+
including the two-phase stack-output-driven deploy and the
|
|
810
|
+
`webOrigins` CSWSH knob).
|
|
811
|
+
- `docs/Cloudflare-TCP-Deployment.md` and `docs/AWS-TCP-Deployment.md`
|
|
812
|
+
— end-to-end guides for the `:6697` TCP+TLS variants
|
|
813
|
+
(Spectrum/Container on CF, NLB + Lambda streaming on AWS), including
|
|
814
|
+
mTLS trust-store setup.
|
|
815
|
+
- `README.md` in each `packages/*` and `apps/*` — per-package notes
|
|
816
|
+
(e.g. `packages/aws-adapter/README.md` for the DynamoDB-Local test
|
|
817
|
+
setup, `apps/aws-stack/README.md` for CDK commands and localstack
|
|
818
|
+
validation).
|