serverless-ircd 0.2.0 → 0.4.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 +2 -2
- package/.github/workflows/deploy-aws.yml +1 -3
- package/.github/workflows/deploy-cf-tcp.yml +87 -0
- package/.github/workflows/deploy-cf.yml +1 -1
- package/.node-version +1 -0
- package/.nvmrc +1 -0
- package/CHANGELOG.md +258 -18
- package/README.md +72 -30
- package/apps/aws-stack/README.md +2 -2
- package/apps/aws-stack/bin/aws.ts +7 -0
- package/apps/aws-stack/package.json +4 -4
- package/apps/aws-stack/src/aws-stack.ts +118 -6
- package/apps/aws-stack/tests/stack.test.ts +98 -3
- package/apps/cf-tcp-container/Dockerfile +69 -0
- package/apps/cf-tcp-container/package.json +34 -0
- package/apps/cf-tcp-container/src/config-loader.ts +145 -0
- package/apps/cf-tcp-container/src/container-do.ts +38 -0
- package/apps/cf-tcp-container/src/container-server.ts +363 -0
- package/apps/cf-tcp-container/src/main.ts +77 -0
- package/apps/cf-tcp-container/src/persistence.ts +144 -0
- package/apps/cf-tcp-container/src/worker.ts +41 -0
- package/apps/cf-tcp-container/terraform/provider.tf +24 -0
- package/apps/cf-tcp-container/terraform/spectrum.tf +81 -0
- package/apps/cf-tcp-container/tests/config-loader.test.ts +217 -0
- package/apps/cf-tcp-container/tests/container-server.test.ts +465 -0
- package/apps/cf-tcp-container/tests/persistence.test.ts +227 -0
- package/apps/cf-tcp-container/tests/tls-e2e.test.ts +275 -0
- package/apps/cf-tcp-container/tsconfig.build.json +17 -0
- package/apps/cf-tcp-container/tsconfig.test.json +15 -0
- package/apps/cf-tcp-container/vitest.config.ts +26 -0
- package/apps/cf-tcp-container/wrangler.toml +63 -0
- package/apps/cf-worker/package.json +1 -1
- package/apps/cf-worker/scripts/smoke.mjs +0 -0
- package/apps/cf-worker/src/worker.ts +8 -2
- package/apps/cf-worker/tests/smoke.test.ts +1 -0
- package/apps/cf-worker/wrangler.test.toml +11 -1
- package/apps/cf-worker/wrangler.toml +69 -19
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +11 -0
- package/apps/local-cli/src/main.ts +1 -1
- package/apps/local-cli/src/server.ts +46 -3
- package/apps/local-cli/tests/e2e.test.ts +52 -0
- package/package.json +19 -16
- package/packages/aws-adapter/package.json +3 -3
- package/packages/aws-adapter/src/account-store.ts +121 -0
- package/packages/aws-adapter/src/admission.ts +74 -0
- package/packages/aws-adapter/src/aws-runtime.ts +124 -34
- package/packages/aws-adapter/src/cdk-table-defs.ts +5 -1
- package/packages/aws-adapter/src/config-loader.ts +62 -0
- package/packages/aws-adapter/src/dynamo-account-store.ts +95 -0
- package/packages/aws-adapter/src/handlers/connect.ts +64 -8
- package/packages/aws-adapter/src/handlers/default.ts +43 -2
- package/packages/aws-adapter/src/handlers/disconnect.ts +27 -8
- package/packages/aws-adapter/src/handlers/index.ts +120 -9
- package/packages/aws-adapter/src/handlers/nlb-stream.ts +481 -0
- package/packages/aws-adapter/src/handlers/ping-checker.ts +1 -1
- package/packages/aws-adapter/src/index.ts +13 -0
- package/packages/aws-adapter/tests/account-store-dynamo.test.ts +182 -0
- package/packages/aws-adapter/tests/account-store.test.ts +279 -0
- package/packages/aws-adapter/tests/admission.test.ts +70 -0
- package/packages/aws-adapter/tests/aws-harness.ts +13 -1
- package/packages/aws-adapter/tests/config-loader.test.ts +75 -0
- package/packages/aws-adapter/tests/connect.test.ts +78 -0
- package/packages/aws-adapter/tests/disconnect-fanout.test.ts +343 -0
- package/packages/aws-adapter/tests/gone-exception.test.ts +31 -26
- package/packages/aws-adapter/tests/handlers.test.ts +194 -47
- package/packages/aws-adapter/tests/nlb-stream.test.ts +478 -0
- package/packages/aws-adapter/tests/ping-checker.test.ts +34 -29
- package/packages/aws-adapter/tests/sweeper.test.ts +25 -18
- package/packages/aws-adapter/tests/transactions.test.ts +25 -20
- package/packages/cf-adapter/package.json +1 -1
- package/packages/cf-adapter/src/cf-runtime.ts +40 -22
- package/packages/cf-adapter/src/channel-do.ts +40 -1
- package/packages/cf-adapter/src/channel-registry-do.ts +58 -0
- package/packages/cf-adapter/src/config-loader.ts +62 -0
- package/packages/cf-adapter/src/connection-do.ts +181 -31
- package/packages/cf-adapter/src/d1-account-store.ts +198 -0
- package/packages/cf-adapter/src/env.ts +58 -8
- package/packages/cf-adapter/src/index.ts +11 -9
- package/packages/cf-adapter/tests/cf-harness.ts +11 -1
- package/packages/cf-adapter/tests/cf-integration.test.ts +2 -1
- package/packages/cf-adapter/tests/cf-runtime.test.ts +91 -0
- package/packages/cf-adapter/tests/config-loader.test.ts +22 -0
- package/packages/cf-adapter/tests/connection-do-channel-registration.test.ts +37 -0
- package/packages/cf-adapter/tests/connection-do-no-batching-reservation.test.ts +52 -0
- package/packages/cf-adapter/tests/connection-do-sasl-d1.test.ts +166 -0
- package/packages/cf-adapter/tests/connection-do.test.ts +40 -0
- package/packages/cf-adapter/tests/d1-account-store.test.ts +226 -0
- package/packages/cf-adapter/tests/raw-modules.d.ts +11 -0
- package/packages/cf-adapter/tests/worker/main.ts +9 -1
- package/packages/cf-adapter/tests/worker/stubs/channel-stub.ts +7 -1
- package/packages/cf-adapter/wrangler.test.toml +18 -1
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/in-memory-runtime/src/in-memory-runtime.ts +14 -28
- package/packages/in-memory-runtime/tests/in-memory-runtime.test.ts +19 -0
- package/packages/irc-core/package.json +8 -2
- package/packages/irc-core/scripts/generate-build-info.mjs +31 -0
- package/packages/irc-core/src/caps/capabilities.ts +23 -2
- package/packages/irc-core/src/case-fold.ts +64 -0
- package/packages/irc-core/src/cloak.ts +1 -1
- package/packages/irc-core/src/commands/cap.ts +8 -1
- package/packages/irc-core/src/commands/index.ts +11 -0
- package/packages/irc-core/src/commands/invite.ts +6 -9
- package/packages/irc-core/src/commands/ison.ts +61 -0
- package/packages/irc-core/src/commands/isupport.ts +1 -1
- package/packages/irc-core/src/commands/join.ts +4 -3
- package/packages/irc-core/src/commands/kick.ts +4 -11
- package/packages/irc-core/src/commands/list.ts +5 -4
- package/packages/irc-core/src/commands/mode.ts +5 -9
- package/packages/irc-core/src/commands/motd-lines.ts +127 -0
- package/packages/irc-core/src/commands/motd.ts +6 -110
- package/packages/irc-core/src/commands/oper.ts +151 -0
- package/packages/irc-core/src/commands/quit.ts +12 -0
- package/packages/irc-core/src/commands/registration.ts +26 -19
- package/packages/irc-core/src/commands/sasl.ts +72 -9
- package/packages/irc-core/src/commands/server-info.ts +129 -0
- package/packages/irc-core/src/commands/tagmsg.ts +205 -0
- package/packages/irc-core/src/commands/userhost.ts +84 -0
- package/packages/irc-core/src/commands/whowas.ts +113 -0
- package/packages/irc-core/src/config.ts +84 -3
- package/packages/irc-core/src/credential-hashing.ts +124 -0
- package/packages/irc-core/src/effects.ts +6 -29
- package/packages/irc-core/src/index.ts +3 -0
- package/packages/irc-core/src/ports.ts +240 -7
- package/packages/irc-core/src/protocol/numerics.ts +14 -8
- package/packages/irc-core/src/types.ts +60 -1
- package/packages/irc-core/tests/account-store.test.ts +131 -0
- package/packages/irc-core/tests/caps/capabilities.test.ts +4 -3
- package/packages/irc-core/tests/case-fold.test.ts +80 -0
- package/packages/irc-core/tests/commands/cap.test.ts +33 -1
- package/packages/irc-core/tests/commands/ison.test.ts +166 -0
- package/packages/irc-core/tests/commands/kick.test.ts +15 -0
- package/packages/irc-core/tests/commands/oper.test.ts +257 -0
- package/packages/irc-core/tests/commands/quit.test.ts +69 -2
- package/packages/irc-core/tests/commands/registration.test.ts +256 -19
- package/packages/irc-core/tests/commands/sasl.test.ts +118 -10
- package/packages/irc-core/tests/commands/server-info.test.ts +274 -0
- package/packages/irc-core/tests/commands/tagmsg.test.ts +662 -0
- package/packages/irc-core/tests/commands/userhost.test.ts +264 -0
- package/packages/irc-core/tests/commands/who.test.ts +22 -4
- package/packages/irc-core/tests/commands/whois.test.ts +8 -1
- package/packages/irc-core/tests/commands/whowas.test.ts +312 -0
- package/packages/irc-core/tests/config.test.ts +170 -1
- package/packages/irc-core/tests/credential-hashing.test.ts +170 -0
- package/packages/irc-core/tests/effects.test.ts +0 -27
- package/packages/irc-core/tests/nick-history-store.test.ts +162 -0
- package/packages/irc-core/tests/numerics.test.ts +12 -0
- package/packages/irc-core/tests/types.test.ts +35 -1
- package/packages/irc-core/tsconfig.build.json +1 -1
- package/packages/irc-core/tsconfig.test.json +1 -1
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +182 -14
- package/packages/irc-server/src/dispatch.ts +0 -3
- package/packages/irc-server/src/index.ts +10 -2
- package/packages/irc-server/src/routing.ts +21 -0
- package/packages/irc-server/src/transport.ts +101 -0
- package/packages/irc-server/tests/actor.test.ts +617 -1
- package/packages/irc-server/tests/dispatch.test.ts +0 -17
- package/packages/irc-server/tests/routing.test.ts +7 -0
- package/packages/irc-server/tests/transport.test.ts +230 -0
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/harness.ts +44 -9
- package/packages/irc-test-support/src/in-memory-harness.ts +78 -13
- package/packages/irc-test-support/src/index.ts +3 -0
- package/packages/irc-test-support/src/scenarios.ts +132 -2
- package/packages/irc-test-support/tests/in-memory-harness.test.ts +2 -1
- package/packages/irc-test-support/tests/in-memory-scenarios.test.ts +23 -9
- package/pnpm-workspace.yaml +9 -0
- package/tools/ci-hardening/package.json +1 -1
- package/tools/package.json +4 -0
- package/tools/seed-aws-accounts.ts +79 -0
- package/tools/seed-cf-accounts.ts +104 -0
- package/tools/tcp-ws-forwarder/package.json +1 -1
- package/AGENTS.md +0 -5
- package/MOTD.txt +0 -3
- package/PLAN-FIXES.md +0 -358
- package/PLAN.md +0 -420
- package/dashboards/cloudwatch-irc.json +0 -139
- package/docs/AWS-Adapter-Architecture.md +0 -494
- package/docs/AWS-Deployment.md +0 -1107
- package/docs/Cloudflare-Deployment-Guide.md +0 -660
- package/docs/Home.md +0 -1
- package/docs/Observability.md +0 -87
- package/docs/PlanIRCv3Websocket.md +0 -489
- package/docs/PlanWebClient.md +0 -451
- package/docs/Release-Process.md +0 -440
- package/progress.md +0 -107
- package/tickets.md +0 -2485
- package/webircgateway/LICENSE +0 -201
- package/webircgateway/Makefile +0 -44
- package/webircgateway/README.md +0 -134
- package/webircgateway/config.conf.example +0 -135
- package/webircgateway/go.mod +0 -16
- package/webircgateway/go.sum +0 -89
- package/webircgateway/main.go +0 -118
- package/webircgateway/pkg/dnsbl/dnsbl.go +0 -121
- package/webircgateway/pkg/identd/identd.go +0 -86
- package/webircgateway/pkg/identd/rpcclient.go +0 -59
- package/webircgateway/pkg/irc/isupport.go +0 -56
- package/webircgateway/pkg/irc/message.go +0 -217
- package/webircgateway/pkg/irc/state.go +0 -79
- package/webircgateway/pkg/proxy/proxy.go +0 -129
- package/webircgateway/pkg/proxy/server.go +0 -237
- package/webircgateway/pkg/recaptcha/recaptcha.go +0 -59
- package/webircgateway/pkg/webircgateway/client.go +0 -741
- package/webircgateway/pkg/webircgateway/client_command_handlers.go +0 -495
- package/webircgateway/pkg/webircgateway/config.go +0 -385
- package/webircgateway/pkg/webircgateway/gateway.go +0 -278
- package/webircgateway/pkg/webircgateway/gateway_utils.go +0 -133
- package/webircgateway/pkg/webircgateway/hooks.go +0 -152
- package/webircgateway/pkg/webircgateway/letsencrypt.go +0 -41
- package/webircgateway/pkg/webircgateway/messagetags.go +0 -103
- package/webircgateway/pkg/webircgateway/transport_kiwiirc.go +0 -206
- package/webircgateway/pkg/webircgateway/transport_sockjs.go +0 -107
- package/webircgateway/pkg/webircgateway/transport_tcp.go +0 -113
- package/webircgateway/pkg/webircgateway/transport_websocket.go +0 -126
- package/webircgateway/pkg/webircgateway/utils.go +0 -147
- package/webircgateway/plugins/example/plugin.go +0 -11
- package/webircgateway/plugins/stats/plugin.go +0 -52
- package/webircgateway/staticcheck.conf +0 -1
- package/webircgateway/webircgateway.svg +0 -3
package/.github/workflows/ci.yml
CHANGED
|
@@ -39,7 +39,7 @@ jobs:
|
|
|
39
39
|
- name: Setup Node
|
|
40
40
|
uses: actions/setup-node@v4
|
|
41
41
|
with:
|
|
42
|
-
node-version:
|
|
42
|
+
node-version: 24
|
|
43
43
|
cache: pnpm
|
|
44
44
|
|
|
45
45
|
- name: Install dependencies
|
|
@@ -112,7 +112,7 @@ jobs:
|
|
|
112
112
|
- name: Setup Node
|
|
113
113
|
uses: actions/setup-node@v4
|
|
114
114
|
with:
|
|
115
|
-
node-version:
|
|
115
|
+
node-version: 24
|
|
116
116
|
cache: pnpm
|
|
117
117
|
|
|
118
118
|
- name: Install dependencies
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
name: deploy-cf-tcp
|
|
2
|
+
|
|
3
|
+
# Cloudflare TCP+TLS deploy pipeline (Spectrum + Container origin).
|
|
4
|
+
#
|
|
5
|
+
# Deploys the IRC TCP container origin (apps/cf-tcp-container) to Cloudflare
|
|
6
|
+
# Containers and applies the Spectrum TLS config (terraform). Spectrum
|
|
7
|
+
# terminates TLS on :6697 and forwards plaintext TCP to the container.
|
|
8
|
+
#
|
|
9
|
+
# This pipeline runs alongside deploy-cf.yml (the WebSocket path). The two
|
|
10
|
+
# are independent: a deployment can enable either or both transports.
|
|
11
|
+
#
|
|
12
|
+
# Secrets (configure under repo Settings → Secrets and variables → Actions):
|
|
13
|
+
# CLOUDFLARE_API_TOKEN Spectrum/Containers API token.
|
|
14
|
+
# CLOUDFLARE_ACCOUNT_ID Account ID for the target zone.
|
|
15
|
+
# CF_ZONE_ID Zone ID for the Spectrum hostname.
|
|
16
|
+
# CF_TCP_ORIGIN_ADDRESS Hostname/IP of the container origin.
|
|
17
|
+
|
|
18
|
+
on:
|
|
19
|
+
push:
|
|
20
|
+
branches: [main]
|
|
21
|
+
paths:
|
|
22
|
+
- 'apps/cf-tcp-container/**'
|
|
23
|
+
- '.github/workflows/deploy-cf-tcp.yml'
|
|
24
|
+
workflow_dispatch:
|
|
25
|
+
|
|
26
|
+
concurrency:
|
|
27
|
+
group: cf-tcp-staging
|
|
28
|
+
cancel-in-progress: false
|
|
29
|
+
|
|
30
|
+
jobs:
|
|
31
|
+
deploy-staging:
|
|
32
|
+
name: deploy cf-tcp staging
|
|
33
|
+
runs-on: ubuntu-latest
|
|
34
|
+
timeout-minutes: 15
|
|
35
|
+
steps:
|
|
36
|
+
- name: Checkout
|
|
37
|
+
uses: actions/checkout@v4
|
|
38
|
+
|
|
39
|
+
- name: Setup pnpm
|
|
40
|
+
uses: pnpm/action-setup@v4
|
|
41
|
+
|
|
42
|
+
- name: Setup Node
|
|
43
|
+
uses: actions/setup-node@v4
|
|
44
|
+
with:
|
|
45
|
+
node-version: 24
|
|
46
|
+
cache: pnpm
|
|
47
|
+
|
|
48
|
+
- name: Install dependencies
|
|
49
|
+
run: pnpm install --frozen-lockfile
|
|
50
|
+
|
|
51
|
+
- name: Build workspace
|
|
52
|
+
run: pnpm build
|
|
53
|
+
|
|
54
|
+
- name: Typecheck
|
|
55
|
+
run: pnpm typecheck
|
|
56
|
+
|
|
57
|
+
- name: Test
|
|
58
|
+
run: pnpm --filter '@serverless-ircd/cf-tcp-container' test
|
|
59
|
+
|
|
60
|
+
- name: Deploy container to staging
|
|
61
|
+
working-directory: apps/cf-tcp-container
|
|
62
|
+
env:
|
|
63
|
+
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
|
64
|
+
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
|
65
|
+
run: wrangler deploy --env staging
|
|
66
|
+
|
|
67
|
+
- name: Apply Spectrum TLS config
|
|
68
|
+
working-directory: apps/cf-tcp-container/terraform
|
|
69
|
+
env:
|
|
70
|
+
TF_VAR_zone_id: ${{ secrets.CF_ZONE_ID }}
|
|
71
|
+
TF_VAR_origin_address: ${{ secrets.CF_TCP_ORIGIN_ADDRESS }}
|
|
72
|
+
TF_VAR_hostname: irc-staging.example.com
|
|
73
|
+
run: |
|
|
74
|
+
terraform init
|
|
75
|
+
terraform apply -auto-approve
|
|
76
|
+
|
|
77
|
+
- name: Smoke e2e (irc+tls)
|
|
78
|
+
if: ${{ vars.CF_TCP_SMOKE_HOST != '' }}
|
|
79
|
+
env:
|
|
80
|
+
SMOKE_HOST: ${{ vars.CF_TCP_SMOKE_HOST }}
|
|
81
|
+
SMOKE_PORT: '6697'
|
|
82
|
+
run: |
|
|
83
|
+
# Minimal smoke: connect over TLS, register, join, privmsg, quit.
|
|
84
|
+
# Uses openssl s_client for the TLS handshake + IRC line exchange.
|
|
85
|
+
echo "NICK smoke\r\nUSER smoke 0 * :Smoke\r\nQUIT :smoke\r\n" | \
|
|
86
|
+
timeout 10 openssl s_client -connect "${SMOKE_HOST}:${SMOKE_PORT}" -quiet 2>/dev/null | \
|
|
87
|
+
grep -q "001" && echo "SMOKE PASS" || (echo "SMOKE FAIL" && exit 1)
|
package/.node-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
24
|
package/.nvmrc
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
24
|
package/CHANGELOG.md
CHANGED
|
@@ -10,6 +10,248 @@ 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.4.0] - 2026-07-31
|
|
14
|
+
|
|
15
|
+
The headline is **dual transport**: both adapters now speak a real
|
|
16
|
+
`irc+tls :6697` (TLS-over-TCP) surface for stock IRC clients in
|
|
17
|
+
production — not just the local CLI — alongside the WebSocket path. This
|
|
18
|
+
lands **SASL `EXTERNAL` via mTLS**, the **remaining standard IRC verbs**
|
|
19
|
+
(WHOWAS, VERSION, TIME, ADMIN, INFO, USERHOST, ISON), a **Cloudflare
|
|
20
|
+
D1-backed SASL account store**, config-driven server identity, and a
|
|
21
|
+
**Node 24 / pnpm 11** toolchain. Phase 7 (TLS hardening & raw TCP) is
|
|
22
|
+
complete.
|
|
23
|
+
|
|
24
|
+
### Added — Dual transport: WebSocket + `irc+tls :6697`
|
|
25
|
+
|
|
26
|
+
The wss-only transport decision (ADR-005) is superseded by **ADR-009**
|
|
27
|
+
(dual transport). The shared `ConnectionActor` now drives either
|
|
28
|
+
transport through a generalized seam; the parser/reducer/dispatch
|
|
29
|
+
pipeline is unchanged.
|
|
30
|
+
|
|
31
|
+
- **Transport seam** (`@serverless-ircd/irc-server`,
|
|
32
|
+
`@serverless-ircd/irc-core`): the `\r\n` line-framing is extracted into
|
|
33
|
+
a `Transport` abstraction. `ConnectionActor` accepts a
|
|
34
|
+
`WsTextFrameTransport` (one message per WS frame, prior behavior) or a
|
|
35
|
+
`TcpByteStreamTransport` (stateful `\r\n` framing across chunks with
|
|
36
|
+
partial-line buffering). Both produce identical reducer invocations.
|
|
37
|
+
- **Cloudflare TCP+TLS** (`@serverless-ircd/cf-tcp-container`): a new
|
|
38
|
+
workspace app — **Cloudflare Spectrum** terminates TLS at the edge and
|
|
39
|
+
forwards plaintext TCP to a stateful **Container** origin running the
|
|
40
|
+
IRC core. Ships a `Dockerfile`, config loader, deploy pipeline
|
|
41
|
+
(`.github/workflows/deploy-cf-tcp.yml`), and root
|
|
42
|
+
`deploy:cf-tcp:staging` / `deploy:cf-tcp:prod` scripts.
|
|
43
|
+
- **AWS TCP+TLS** (`@serverless-ircd/aws-adapter`, `@serverless-ircd/aws-stack`):
|
|
44
|
+
a **Network Load Balancer** with a `tls` listener terminates TLS and
|
|
45
|
+
invokes a **Lambda streaming function** (`nlb-stream.ts`) as the target.
|
|
46
|
+
Connection identity is derived from NLB flow metadata; bidirectional
|
|
47
|
+
traffic flows over the Lambda response stream, reusing `AwsRuntime`.
|
|
48
|
+
CDK constructs for NLB + target group + ACM cert are added.
|
|
49
|
+
- **Transport-parametrized contract suite** (`@serverless-ircd/irc-test-support`):
|
|
50
|
+
every scenario now runs across a `Transport` dimension
|
|
51
|
+
(in-memory+WS, in-memory+TCP, …), with TCP-specific framing scenarios
|
|
52
|
+
(chunk boundaries, mixed `\r\n`/`\n` line endings, partial sends).
|
|
53
|
+
- **Docs**: `docs/Cloudflare-TCP-Deployment.md` and
|
|
54
|
+
`docs/AWS-TCP-Deployment.md` — end-to-end :6697 deployment guides
|
|
55
|
+
(prerequisites, mTLS trust-store setup, cost/ops caveats).
|
|
56
|
+
|
|
57
|
+
### Added — SASL `EXTERNAL` via mTLS (`@serverless-ircd/irc-core`, adapters)
|
|
58
|
+
|
|
59
|
+
- **mTLS support**: an `MtlsIdentityProvider` port in `irc-core` feeds
|
|
60
|
+
the verified client-certificate subject into `AccountStore`. Cloudflare
|
|
61
|
+
uses API Shield mTLS (uploaded CA pool); AWS uses API Gateway
|
|
62
|
+
custom-domain mTLS (PEM trust store, subject in the `$connect` event).
|
|
63
|
+
Identity is plumbed via a port — no cloud deps in `irc-core`.
|
|
64
|
+
- **`EXTERNAL` now authenticates** (was a stubbed `904 ERR_SASLFAIL` in
|
|
65
|
+
v0.2.0): `AUTHENTICATE EXTERNAL` against a bound provider succeeds with
|
|
66
|
+
`900 RPL_LOGGEDIN` / `903 RPL_SASLSUCCESS` when the cert maps to an
|
|
67
|
+
account.
|
|
68
|
+
- **mTLS-conditional advertisement**: the `sasl` cap value and
|
|
69
|
+
`908 ERR_SASLMECHS` are now driven by whether an mTLS provider is bound
|
|
70
|
+
— `PLAIN` only by default, `PLAIN,EXTERNAL` when one is. `AUTHENTICATE
|
|
71
|
+
EXTERNAL` without mTLS returns `908` (mechanism not available) instead
|
|
72
|
+
of the misleading `904`. Operators enable EXTERNAL by configuring edge
|
|
73
|
+
mTLS — no code change required.
|
|
74
|
+
|
|
75
|
+
### Added — Remaining IRC query verbs (`@serverless-ircd/irc-core`)
|
|
76
|
+
|
|
77
|
+
- **`WHOWAS`**: a nick-history store (port + in-memory reference impl)
|
|
78
|
+
records `(nick, connId, username, hostname, signoffTime)` on QUIT /
|
|
79
|
+
NICK-change and purges past a TTL. `WHOWAS <nick>{,<nick>} [<count>]`
|
|
80
|
+
queries it — `314 RPL_WHOWASUSER` per match, `369 RPL_ENDOFWHOWAS`
|
|
81
|
+
terminator, `406 ERR_WASNOSUCHNICK` when no match.
|
|
82
|
+
- **`VERSION`** (`351`), **`TIME`** (`391`, uses the injected `Clock`),
|
|
83
|
+
**`ADMIN`** (`256`/`257`/`258`), **`INFO`** (`371`/`374`),
|
|
84
|
+
**`USERHOST`** (`302`), **`ISON`** (`303`) — each a reducer + a route
|
|
85
|
+
case with spec-correct numerics. Completes the standard query-verb set.
|
|
86
|
+
|
|
87
|
+
### Added — Server identity from config (`@serverless-ircd/irc-core`)
|
|
88
|
+
|
|
89
|
+
- The `002` / `003` / `004` registration replies now reflect
|
|
90
|
+
`ServerConfig.serverVersion` / `createdAt` — the hardcoded
|
|
91
|
+
`'0.1.0'` / `'Phase 1'` placeholders are gone. A build step
|
|
92
|
+
(`scripts/generate-build-info.mjs`) bakes the package version and a
|
|
93
|
+
build timestamp into `RPL_CREATED` (`003`) when the config omits them.
|
|
94
|
+
|
|
95
|
+
### Added — Cloudflare D1-backed SASL account store (`@serverless-ircd/cf-adapter`, `@serverless-ircd/irc-core`)
|
|
96
|
+
|
|
97
|
+
- Brings the CF adapter to parity with the AWS `DynamoAccountStore`. A
|
|
98
|
+
persistent `AccountStore` pre-loads scrypt-hashed rows from a new
|
|
99
|
+
**D1** database (`ACCOUNTS_DB` binding) at `ConnectionDO`
|
|
100
|
+
construction.
|
|
101
|
+
- **Shared scrypt hashing**: `hashAccountCredential` /
|
|
102
|
+
`verifyHashedPassword` are extracted to `irc-core` so both adapters use
|
|
103
|
+
one implementation (never plaintext).
|
|
104
|
+
- **Table-then-env precedence**: when the D1 table has rows it is
|
|
105
|
+
authoritative; when empty or unreachable the adapter falls back to the
|
|
106
|
+
`SASL_ACCOUNTS` env-var seed, preserving current behavior.
|
|
107
|
+
- **`tools/seed-cf-accounts.ts`**: provisioning CLI that scrypt-hashes
|
|
108
|
+
`username:password` pairs into the D1 `accounts` table (re-run
|
|
109
|
+
overwrites the prior row).
|
|
110
|
+
|
|
111
|
+
### Added — AWS max-clients admission (`@serverless-ircd/aws-adapter`)
|
|
112
|
+
|
|
113
|
+
- `$connect` now enforces `serverConfig.maxClients` (was a reserved
|
|
114
|
+
no-op — `void params.serverConfig;`). Connections over the cap are
|
|
115
|
+
rejected at accept time, matching the Cloudflare admission gate.
|
|
116
|
+
|
|
117
|
+
### Changed
|
|
118
|
+
|
|
119
|
+
- **Toolchain**: bumped from Node ≥ 20 / pnpm 9 to **Node ≥ 24 / pnpm
|
|
120
|
+
11** (aligns the Lambda and Cloudflare Workers runtimes). Re-run
|
|
121
|
+
`corepack enable` to pick up the pinned pnpm.
|
|
122
|
+
- `@serverless-ircd/irc-core`: the request/response lookup `Effect`
|
|
123
|
+
variants (`LookupNick`, `GetConnectionInfo`, `GetChannelSnapshot`) are
|
|
124
|
+
removed from `effects.ts` and `dispatch` — they were dead scaffolding
|
|
125
|
+
with no producer or consumer.
|
|
126
|
+
- `@serverless-ircd/cf-adapter`: the dead channel-registration loop in
|
|
127
|
+
`ConnectionDO.webSocketMessage` and the reserved outbound-batching
|
|
128
|
+
field are removed (the latter was a never-wired optimization).
|
|
129
|
+
|
|
130
|
+
### ⚠️ Migration required
|
|
131
|
+
|
|
132
|
+
- **Cloudflare: new D1 binding for SASL accounts.** To persist SASL
|
|
133
|
+
accounts, create the D1 database and add the `ACCOUNTS_DB` binding
|
|
134
|
+
(default + staging) to `wrangler.toml`:
|
|
135
|
+
```bash
|
|
136
|
+
wrangler d1 create serverless-ircd-accounts
|
|
137
|
+
wrangler d1 execute serverless-ircd-accounts --command \
|
|
138
|
+
"CREATE TABLE IF NOT EXISTS accounts (account TEXT PRIMARY KEY, algorithm TEXT NOT NULL, salt TEXT NOT NULL, hash TEXT NOT NULL)"
|
|
139
|
+
```
|
|
140
|
+
**No action is required to keep working** — when the D1 table is empty
|
|
141
|
+
or unreachable the adapter falls back to the `SASL_ACCOUNTS` env-var
|
|
142
|
+
seed. There is **no Durable-Object persisted-state schema change**
|
|
143
|
+
(`PERSISTED_STATE_VERSION` stays `1`); code rollback across this
|
|
144
|
+
release is safe.
|
|
145
|
+
- **Local toolchain**: Node ≥ 24 / pnpm 11 is now required. Re-run
|
|
146
|
+
`corepack enable`.
|
|
147
|
+
|
|
148
|
+
### Known limitations
|
|
149
|
+
|
|
150
|
+
- The **CF TCP path requires Cloudflare Spectrum (Enterprise tier)** plus
|
|
151
|
+
a stateful Container origin; the **AWS TCP path** uses NLB + Lambda
|
|
152
|
+
streaming and is subject to Lambda idle-timeout / stream-duration
|
|
153
|
+
limits. The WebSocket path remains the zero-extra-deps default.
|
|
154
|
+
- **mTLS (for SASL `EXTERNAL`) requires a custom domain + uploaded
|
|
155
|
+
CA/trust-store** on both platforms and is not enabled by default.
|
|
156
|
+
- Oper-gated verbs (`KILL`, `REHASH`, `CONNECT`, `SQUIT`, `TRACE`,
|
|
157
|
+
`WALLOPS`, `SETNAME`, …) still return `421 ERR_UNKNOWNCOMMAND`.
|
|
158
|
+
- `serverName` still defaults to the `irc.example.com` placeholder when
|
|
159
|
+
an adapter config omits it (pending).
|
|
160
|
+
|
|
161
|
+
### Security
|
|
162
|
+
|
|
163
|
+
- SASL `EXTERNAL` no longer returns a misleading `904 ERR_SASLFAIL` when
|
|
164
|
+
mTLS isn't configured — it returns `908 ERR_SASLMECHS` and the
|
|
165
|
+
mechanism is not advertised until a trust store is bound. SASL
|
|
166
|
+
credentials remain scrypt-hashed only (never plaintext), now via a
|
|
167
|
+
single shared implementation across both adapters.
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## [0.3.0] - 2026-07-25
|
|
172
|
+
|
|
173
|
+
### Added — Operator authentication (`@serverless-ircd/irc-core`)
|
|
174
|
+
|
|
175
|
+
- **`OPER` command reducer**: `OPER <name> <password>` authenticates the
|
|
176
|
+
caller against the deployment's configured operator credentials
|
|
177
|
+
(`OperCred[]`) and, on success, grants the `o` user mode, replying
|
|
178
|
+
`381 RPL_YOUREOPER`. Missing parameters → `461 ERR_NEEDMOREPARAMS`;
|
|
179
|
+
no credentials configured → `491 ERR_NOOPERHOST`; wrong name/password
|
|
180
|
+
→ `464 ERR_PASSWDMISMATCH`. Already an operator is an idempotent
|
|
181
|
+
no-op that re-confirms with `381`. `OPER` is the *only* path to oper
|
|
182
|
+
— user `MODE +o` is hard-rejected with `481 ERR_NOPRIVILEGES` — and
|
|
183
|
+
unlocks the `313 RPL_WHOISOPERATOR` branch of WHOIS and the `*`
|
|
184
|
+
oper flag of WHO.
|
|
185
|
+
|
|
186
|
+
### Added — IRCv3 `TAGMSG` (`@serverless-ircd/irc-core`)
|
|
187
|
+
|
|
188
|
+
- **`TAGMSG` reducer** (message-tags extension): carries tags only, no
|
|
189
|
+
text body. Gated behind the `message-tags` capability (clients that
|
|
190
|
+
did not negotiate it see `421 ERR_UNKNOWNCOMMAND`). Follows NOTICE
|
|
191
|
+
semantics — no numeric error replies on any rejection path — for both
|
|
192
|
+
channel and user targets, and is recorded so `draft/chathistory`
|
|
193
|
+
replay picks up `TAGMSG` lines.
|
|
194
|
+
|
|
195
|
+
### Added — RFC 1459 case-mapping (`@serverless-ircd/irc-core`)
|
|
196
|
+
|
|
197
|
+
- **`case-fold.ts`**: RFC 1459 case-insensitive comparison for nicks
|
|
198
|
+
and channels, advertised via `005 CASEMAPPING=rfc1459`. Replaces the
|
|
199
|
+
prior ASCII-only comparison across nick collision/registry, kick and
|
|
200
|
+
invite target lookup, and membership matching.
|
|
201
|
+
|
|
202
|
+
### Added — Registration MOTD (`@serverless-ircd/irc-core`)
|
|
203
|
+
|
|
204
|
+
- The registration welcome now renders the **real configured MOTD**
|
|
205
|
+
(`motd-lines.ts`), replacing the placeholder banner. The `MOTD`
|
|
206
|
+
command and the welcome flow share one rendering path.
|
|
207
|
+
|
|
208
|
+
### Added — Cloudflare adapter (`@serverless-ircd/cf-adapter`)
|
|
209
|
+
|
|
210
|
+
- **`ChannelRegistryDO`**: a fourth Durable Object holding the channel
|
|
211
|
+
directory (channel name → `ChannelDO` routing), alongside
|
|
212
|
+
`ConnectionDO`, `ChannelDO`, and `RegistryDO`. Wired into the Worker
|
|
213
|
+
exports, bindings, and SQLite migrations.
|
|
214
|
+
- **Cloudflare config loader**: reduces Workers `vars` to the shared
|
|
215
|
+
`ServerConfigSchema`, matching the AWS (`loadServerConfigFromLambdaEnv`)
|
|
216
|
+
and local-CLI loaders.
|
|
217
|
+
- `CfRuntime` completed — the previously stubbed `IrcRuntime` methods
|
|
218
|
+
(`getConnection`, `getChannelConnections`, `listChannels`, and
|
|
219
|
+
cross-connection `disconnect`) are now implemented.
|
|
220
|
+
|
|
221
|
+
### Added — SASL account persistence (`@serverless-ircd/aws-adapter`)
|
|
222
|
+
|
|
223
|
+
- SASL `PLAIN` accounts are now persisted in the DynamoDB `Accounts`
|
|
224
|
+
table with **scrypt** password hashes (never plaintext). The Lambda
|
|
225
|
+
cold-start path scans the table once and loads credentials into the
|
|
226
|
+
synchronous `AccountStore` port.
|
|
227
|
+
- **`tools/seed-aws-accounts.ts`**: provisioning script that scrypt-hashes
|
|
228
|
+
`username:password` pairs (from CLI args or a file) and writes them
|
|
229
|
+
to the `Accounts` table. Re-running with the same username overwrites
|
|
230
|
+
the prior row.
|
|
231
|
+
|
|
232
|
+
### Changed
|
|
233
|
+
|
|
234
|
+
- `@serverless-ircd/in-memory-runtime`: `admitConnection` now returns a
|
|
235
|
+
stable empty `recordId` when admission is disabled, dropping the
|
|
236
|
+
per-call id minting (the id was never consulted).
|
|
237
|
+
- Deploy scripts: root `pnpm deploy:aws:staging` / `deploy:aws:prod`
|
|
238
|
+
now run `pnpm build` before `cdk deploy`, so the esbuild-bundled
|
|
239
|
+
Lambda picks up fresh workspace `dist/` — matching what CI already
|
|
240
|
+
did and fixing local deploys shipping stale code.
|
|
241
|
+
|
|
242
|
+
### Known limitations
|
|
243
|
+
|
|
244
|
+
- SASL `EXTERNAL` (mTLS) and production TLS transport remain pending
|
|
245
|
+
(0.x work).
|
|
246
|
+
- Deployed stacks remain WebSocket-only; stock TCP IRC clients still
|
|
247
|
+
reach them through the local `tcp-ws-forwarder` bridge.
|
|
248
|
+
|
|
249
|
+
### Security
|
|
250
|
+
|
|
251
|
+
- None. No vulnerabilities have been reported or fixed in this release.
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
13
255
|
## [0.2.0] - 2026-07-23
|
|
14
256
|
|
|
15
257
|
### Added — Authentication & flood control (`@serverless-ircd/irc-core`)
|
|
@@ -294,31 +536,29 @@ manual per `docs/release.md` §8.3.
|
|
|
294
536
|
|
|
295
537
|
### Known limitations
|
|
296
538
|
|
|
297
|
-
- **No AWS adapter.**
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
the only deployable platform in 0.1.0.
|
|
539
|
+
- **No AWS adapter.** The CDK stack, APIGW WebSocket handlers, DynamoDB
|
|
540
|
+
transactions, EventBridge timers, deploy pipeline, and deployment doc
|
|
541
|
+
are pending. Cloudflare is the only deployable platform in 0.1.0.
|
|
301
542
|
- **No SASL.** `CAP sasl` is advertised in the capability list but
|
|
302
543
|
`AUTHENTICATE` is not wired into the dispatcher. Plain-text password
|
|
303
|
-
authentication via `PASS` is also not enforced
|
|
304
|
-
- **No flood control.** A token-bucket wrapper
|
|
305
|
-
|
|
544
|
+
authentication via `PASS` is also not enforced.
|
|
545
|
+
- **No flood control.** A token-bucket wrapper is pending. Misbehaving
|
|
546
|
+
clients can send unbounded commands.
|
|
306
547
|
- **No server-password enforcement, rate limits, cloaking, or oper
|
|
307
|
-
credentials
|
|
548
|
+
credentials.**
|
|
308
549
|
- **No config loader.** Server name, network name, MOTD, channel
|
|
309
|
-
prefixes, max clients, oper creds are hardcoded defaults
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
`[vars].MOTD_LINES`.
|
|
550
|
+
prefixes, max clients, oper creds are hardcoded defaults. The
|
|
551
|
+
`MOTD.txt` file at the repo root is informational only; the live MOTD
|
|
552
|
+
is compiled into the Worker from `[vars].MOTD_LINES`.
|
|
313
553
|
- **No CI coverage gate.** Coverage is computed and uploaded as an
|
|
314
|
-
artifact, but no threshold is enforced
|
|
554
|
+
artifact, but no threshold is enforced. Inspect
|
|
315
555
|
`packages/*/coverage/` before deploying.
|
|
316
556
|
- **No TLS / raw TCP transport.** v1 is WebSocket text frames only.
|
|
317
|
-
TLS-hardened and `irc+tls` transports are 0.x work
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
557
|
+
TLS-hardened and `irc+tls` transports are 0.x work. Stock TCP IRC
|
|
558
|
+
clients reach the server through the local `tcp-ws-forwarder` bridge.
|
|
559
|
+
- **No `chathistory` playback.** Chat-history playback is pending;
|
|
560
|
+
clients that request backlog on JOIN will see an empty batch or no
|
|
561
|
+
response.
|
|
322
562
|
|
|
323
563
|
### Security
|
|
324
564
|
|
package/README.md
CHANGED
|
@@ -4,18 +4,23 @@ A serverless IRC daemon where the IRC protocol logic lives in a pure,
|
|
|
4
4
|
platform-agnostic core, and two thin adapters run it on **Cloudflare Workers**
|
|
5
5
|
(Durable Objects) or **AWS** (API Gateway WebSockets + Lambda + DynamoDB).
|
|
6
6
|
|
|
7
|
-
One TypeScript codebase. Two serverless substrates.
|
|
7
|
+
One TypeScript codebase. Two serverless substrates.
|
|
8
8
|
|
|
9
|
-
> **Status:** **v0.
|
|
9
|
+
> **Status:** **v0.4.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
|
|
13
|
-
> deployed to staging.
|
|
14
|
-
>
|
|
15
|
-
>
|
|
16
|
-
>
|
|
17
|
-
>
|
|
18
|
-
>
|
|
13
|
+
> deployed to staging. v0.4.0 ships **dual transport**: both adapters
|
|
14
|
+
> now speak a real `irc+tls :6697` (TLS-over-TCP) surface for stock IRC
|
|
15
|
+
> clients — Cloudflare via Spectrum + a Container origin, AWS via a
|
|
16
|
+
> Network Load Balancer + Lambda streaming — alongside the WebSocket
|
|
17
|
+
> path. It also lands **SASL `EXTERNAL` via mTLS**, the remaining
|
|
18
|
+
> standard IRC verbs (`WHOWAS`, `VERSION`, `TIME`, `ADMIN`, `INFO`,
|
|
19
|
+
> `USERHOST`, `ISON`), a **Cloudflare D1-backed SASL account store**,
|
|
20
|
+
> config-driven server identity, and a **Node 24 / pnpm 11** toolchain.
|
|
21
|
+
> Remaining 0.x work: oper-gated verbs (`KILL`/`REHASH`/…), a
|
|
22
|
+
> required-`serverName` config gate, and load/compat testing. See
|
|
23
|
+
> `CHANGELOG.md` for the per-release manifests.
|
|
19
24
|
|
|
20
25
|
---
|
|
21
26
|
|
|
@@ -57,14 +62,25 @@ Hexagonal / ports-and-adapters. The core reasons about IRC; adapters move bytes.
|
|
|
57
62
|
│ packages/cf-adapter │ │ packages/aws-adapter │
|
|
58
63
|
│ ConnectionDO │ │ $connect/$disconnect/ │
|
|
59
64
|
│ ChannelDO │ │ $default Lambda handlers │
|
|
60
|
-
│
|
|
61
|
-
│
|
|
65
|
+
│ ChannelRegistryDO │ │ DynamoDB tables │
|
|
66
|
+
│ RegistryDO │ │ AwsRuntime │
|
|
67
|
+
│ CfRuntime │ │ │
|
|
62
68
|
└────────────────────────────┘ └──────────────────────────────┘
|
|
63
69
|
```
|
|
64
70
|
|
|
65
71
|
`packages/in-memory-runtime` is the reference implementation of
|
|
66
72
|
`IrcRuntime`, used by integration tests and the local CLI server.
|
|
67
73
|
|
|
74
|
+
**Two transports, one core.** Both adapters accept WebSocket text frames
|
|
75
|
+
(the serverless default) **and** a raw `irc+tls :6697` (TLS-over-TCP)
|
|
76
|
+
surface for stock IRC clients. The TCP+TLS edge is platform-specific —
|
|
77
|
+
Cloudflare **Spectrum** terminates TLS and forwards plaintext TCP to a
|
|
78
|
+
stateful **Container** origin (`apps/cf-tcp-container`); AWS terminates
|
|
79
|
+
TLS at a **Network Load Balancer** and invokes a **Lambda streaming**
|
|
80
|
+
function. Both feed the same `ConnectionActor` through a `Transport`
|
|
81
|
+
seam (`WsTextFrameTransport` vs. `TcpByteStreamTransport`); the
|
|
82
|
+
parser/reducer/dispatch pipeline is identical.
|
|
83
|
+
|
|
68
84
|
### The key idea: pure reducers + location-of-authority
|
|
69
85
|
|
|
70
86
|
Each command handler is a pure function:
|
|
@@ -106,11 +122,14 @@ ServerlessIRCd/
|
|
|
106
122
|
│ └── aws-adapter/ Lambda + DynamoDB + AwsRuntime
|
|
107
123
|
├── apps/
|
|
108
124
|
│ ├── cf-worker/ worker entry, DO migrations, bindings
|
|
109
|
-
│ ├──
|
|
125
|
+
│ ├── cf-tcp-container/ Spectrum + Container origin for irc+tls :6697
|
|
126
|
+
│ ├── aws-stack/ CDK stack (APIGW WS + NLB + Lambda streaming + DynamoDB)
|
|
110
127
|
│ └── local-cli/ runnable WS + TCP server using in-memory-runtime
|
|
111
128
|
├── tools/
|
|
112
129
|
│ ├── tcp-ws-forwarder/ local TCP↔ws/wss bridge for stock IRC clients
|
|
113
|
-
│
|
|
130
|
+
│ ├── ci-hardening/ coverage-gate + mutation-config validators
|
|
131
|
+
│ ├── seed-aws-accounts.ts scrypt-hash SASL PLAIN accounts into DynamoDB
|
|
132
|
+
│ └── seed-cf-accounts.ts scrypt-hash SASL PLAIN accounts into Cloudflare D1
|
|
114
133
|
├── pnpm-workspace.yaml turbo.json tsconfig.base.json
|
|
115
134
|
└── README.md CHANGELOG.md
|
|
116
135
|
```
|
|
@@ -121,7 +140,7 @@ ServerlessIRCd/
|
|
|
121
140
|
|
|
122
141
|
| Concern | Choice |
|
|
123
142
|
|------------------|---------------------------------------------------|
|
|
124
|
-
| Runtime | Node ≥
|
|
143
|
+
| Runtime | Node ≥ 24, ES2022+ |
|
|
125
144
|
| Package mgr | pnpm workspaces |
|
|
126
145
|
| Build cache | turbo |
|
|
127
146
|
| Language | TypeScript (strict, `exactOptionalPropertyTypes`) |
|
|
@@ -135,9 +154,12 @@ ServerlessIRCd/
|
|
|
135
154
|
|
|
136
155
|
## Getting started
|
|
137
156
|
|
|
138
|
-
Requires Node ≥
|
|
157
|
+
Requires Node ≥ 24 and pnpm 11. Enable corepack so the `packageManager`
|
|
158
|
+
pin is resolved automatically:
|
|
139
159
|
|
|
140
160
|
```bash
|
|
161
|
+
corepack enable # lets the pinned pnpm@11 run on any Node ≥ 24
|
|
162
|
+
|
|
141
163
|
pnpm install # install workspace deps
|
|
142
164
|
|
|
143
165
|
pnpm build # build all packages (turbo)
|
|
@@ -316,10 +338,16 @@ cleanly. Per-release manifests live in `CHANGELOG.md`.
|
|
|
316
338
|
- **Phase 4** — AWS adapter (APIGW WS + Lambda + DynamoDB + CDK).
|
|
317
339
|
✅ landed in **v0.2.0** (staging auto-deploy via CI; prod deploy is manual).
|
|
318
340
|
- **Phase 5** — Observability, security hardening, config, CI gates.
|
|
319
|
-
✅
|
|
320
|
-
limits, coverage + mutation gates
|
|
321
|
-
loader.
|
|
322
|
-
- **Phase 6** —
|
|
341
|
+
✅ landed across v0.2.0–v0.4.0 (logger port, server-password, cloaking,
|
|
342
|
+
admission limits, coverage + mutation gates, OPER credential auth, the
|
|
343
|
+
Cloudflare config loader, and AWS max-clients admission).
|
|
344
|
+
- **Phase 6** — TLS hardening & raw TCP transport (`irc+tls :6697`).
|
|
345
|
+
✅ landed in **v0.4.0**: generalized `ConnectionActor` transport seam,
|
|
346
|
+
Cloudflare Spectrum + Container origin, AWS NLB + Lambda streaming,
|
|
347
|
+
mTLS → SASL `EXTERNAL`, transport-parametrized contract suite, ADR-009.
|
|
348
|
+
- **Phase 7** — Load testing (10k concurrent connections per platform),
|
|
349
|
+
client compatibility sweep (WeeChat, HexChat, IRCCloud, TheLounge),
|
|
350
|
+
remaining ADRs.
|
|
323
351
|
|
|
324
352
|
---
|
|
325
353
|
|
|
@@ -327,23 +355,31 @@ cleanly. Per-release manifests live in `CHANGELOG.md`.
|
|
|
327
355
|
|
|
328
356
|
**Core (RFC 1459/2812 subset):** registration (`NICK`/`USER`/`CAP`/`PASS`),
|
|
329
357
|
`PING`/`PONG`, `QUIT`, `JOIN`, `PART`, `PRIVMSG`, `NOTICE`, `MODE` (user +
|
|
330
|
-
channel), `TOPIC`, `KICK`, `INVITE`, `NAMES`, `LIST`, `WHO`, `WHOIS`,
|
|
331
|
-
`AWAY
|
|
358
|
+
channel), `TOPIC`, `KICK`, `INVITE`, `NAMES`, `LIST`, `WHO`, `WHOIS`,
|
|
359
|
+
`WHOWAS`, `MOTD`, `AWAY`, `OPER` (credential auth → `o` user mode), plus
|
|
360
|
+
the query verbs `VERSION`, `TIME`, `ADMIN`, `INFO`, `USERHOST`, `ISON`.
|
|
361
|
+
(Oper-gated verbs — `KILL`/`REHASH`/`CONNECT`/`SQUIT`/`TRACE` — remain
|
|
362
|
+
deferred.)
|
|
332
363
|
|
|
333
364
|
**Channel modes:** `o v b i k l t n m s p`.
|
|
334
365
|
**User modes:** `i`, `o` (local only), `w`, `s`.
|
|
335
366
|
|
|
336
|
-
**IRCv3 extensions (negotiated via `CAP`):** `message-tags
|
|
337
|
-
`account-tag`, `echo-message`, `batch`,
|
|
338
|
-
`EXTERNAL`
|
|
339
|
-
|
|
367
|
+
**IRCv3 extensions (negotiated via `CAP`):** `message-tags` (incl. the
|
|
368
|
+
`TAGMSG` command), `server-time`, `account-tag`, `echo-message`, `batch`,
|
|
369
|
+
`sasl` (`PLAIN` always; `EXTERNAL` via mTLS when a client-cert trust store
|
|
370
|
+
is bound),
|
|
371
|
+
`multi-prefix`, `away-notify`, `chghost`, `invite-notify`, `extended-join`,
|
|
372
|
+
`draft/chathistory`, `safelist`. Case-insensitive nick/channel comparison
|
|
373
|
+
uses **RFC 1459** case-mapping (advertised via `005 CASEMAPPING=rfc1459`).
|
|
340
374
|
|
|
341
375
|
**Transport:** deployed stacks (Cloudflare Workers, AWS API Gateway) speak
|
|
342
|
-
WebSocket text frames
|
|
343
|
-
`\r\n`-joined frames)
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
376
|
+
WebSocket text frames by default (one IRC message per frame, with tolerance
|
|
377
|
+
for `\r\n`-joined frames), and optionally expose a raw `irc+tls :6697`
|
|
378
|
+
(TLS-over-TCP) surface — Cloudflare via **Spectrum + Container**, AWS via a
|
|
379
|
+
**Network Load Balancer + Lambda streaming** function. The local CLI
|
|
380
|
+
additionally binds a plain RFC TCP listener for direct IRC-client access,
|
|
381
|
+
and the [`tcp-ws-forwarder`](#connecting-a-tcp-irc-client-to-a-deployed-stack-tcpws-forwarder)
|
|
382
|
+
bridges a stock TCP client to a deployed WebSocket endpoint.
|
|
347
383
|
|
|
348
384
|
---
|
|
349
385
|
|
|
@@ -351,7 +387,13 @@ WebSocket endpoint through the local
|
|
|
351
387
|
|
|
352
388
|
- `CHANGELOG.md` — per-release manifests (Keep a Changelog format), including
|
|
353
389
|
the v0.2.0 work (AWS adapter, SASL, flood control, security hardening, CI
|
|
354
|
-
gates).
|
|
390
|
+
gates), v0.3.0 (OPER, TAGMSG, RFC 1459 case-mapping, real MOTD,
|
|
391
|
+
ChannelRegistryDO, SASL account persistence), and v0.4.0 (dual transport
|
|
392
|
+
`irc+tls :6697`, SASL `EXTERNAL`/mTLS, WHOWAS + remaining verbs, D1 account
|
|
393
|
+
store, Node 24/pnpm 11).
|
|
394
|
+
- `docs/Cloudflare-TCP-Deployment.md` and `docs/AWS-TCP-Deployment.md` —
|
|
395
|
+
end-to-end guides for the `:6697` TCP+TLS variants (Spectrum/Container on
|
|
396
|
+
CF, NLB + Lambda streaming on AWS), including mTLS trust-store setup.
|
|
355
397
|
- `README.md` in each `packages/*` and `apps/*` — per-package notes (e.g.
|
|
356
398
|
`packages/aws-adapter/README.md` for the DynamoDB-Local test setup,
|
|
357
399
|
`apps/aws-stack/README.md` for CDK commands and localstack validation).
|
package/apps/aws-stack/README.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# `@serverless-ircd/aws-stack`
|
|
2
2
|
|
|
3
3
|
AWS CDK v2 stack that provisions the AWS substrate for ServerlessIRCd
|
|
4
|
-
(PLAN §6.2): an API Gateway v2 WebSocket API, a single Node
|
|
4
|
+
(PLAN §6.2): an API Gateway v2 WebSocket API, a single Node 24 Lambda wired
|
|
5
5
|
to the `$connect` / `$disconnect` / `$default` routes, five DynamoDB tables
|
|
6
6
|
(`Connections`, `ChannelMeta`, `ChannelMembers`, `Nicks`, `Accounts`), and the
|
|
7
7
|
least-privilege IAM glue to let the Lambda read/write the tables and post back
|
|
@@ -12,7 +12,7 @@ The Lambda handler is currently a stub (`{ statusCode: 200 }`); the real
|
|
|
12
12
|
|
|
13
13
|
## Prerequisites
|
|
14
14
|
|
|
15
|
-
- Node.js
|
|
15
|
+
- Node.js 24+
|
|
16
16
|
- pnpm (workspace root installs this package)
|
|
17
17
|
- An AWS account with credentials configured
|
|
18
18
|
(`aws configure`, `AWS_PROFILE`, or `SSO`)
|
|
@@ -38,11 +38,18 @@ const motdLines = Array.isArray(rawMotd)
|
|
|
38
38
|
? rawMotd.split('\n')
|
|
39
39
|
: undefined;
|
|
40
40
|
|
|
41
|
+
// TCP+TLS (irc+tls :6697) is opt-in: only provisioned when this context
|
|
42
|
+
// var is supplied. When omitted, only the wss (API Gateway WebSocket)
|
|
43
|
+
// path deploys (the default documented in docs/AWS-Deployment.md). See
|
|
44
|
+
// docs/AWS-TCP-Deployment.md.
|
|
45
|
+
const tcpTlsDomainName = app.node.tryGetContext('tcpTlsDomainName') as string | undefined;
|
|
46
|
+
|
|
41
47
|
const stackProps: IrcStackProps = {
|
|
42
48
|
environmentName,
|
|
43
49
|
...(serverName !== undefined ? { serverName } : {}),
|
|
44
50
|
...(networkName !== undefined ? { networkName } : {}),
|
|
45
51
|
...(motdLines !== undefined ? { motdLines } : {}),
|
|
52
|
+
...(tcpTlsDomainName !== undefined ? { tcpTlsDomainName } : {}),
|
|
46
53
|
...(account && region ? { env: { account, region } } : {}),
|
|
47
54
|
};
|
|
48
55
|
|