serverless-ircd 0.9.0 → 0.11.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 +28 -0
- package/.github/workflows/deploy-aws.yml +156 -32
- package/.github/workflows/deploy-cf-tcp.yml +35 -9
- package/.github/workflows/deploy-cf.yml +40 -14
- package/CHANGELOG.md +594 -0
- package/README.md +286 -60
- package/apps/aws-stack/README.md +3 -5
- package/apps/aws-stack/bin/aws.ts +118 -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 +398 -67
- 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 +714 -105
- 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/Dockerfile +37 -5
- package/apps/cf-tcp-container/package.json +7 -3
- package/apps/cf-tcp-container/src/config-loader.ts +113 -2
- package/apps/cf-tcp-container/src/container-server.ts +267 -87
- package/apps/cf-tcp-container/src/main.ts +22 -7
- package/apps/cf-tcp-container/src/proxy-protocol.ts +112 -0
- package/apps/cf-tcp-container/terraform/spectrum.tf +40 -11
- package/apps/cf-tcp-container/tests/config-loader.test.ts +170 -0
- package/apps/cf-tcp-container/tests/container-server-tls.test.ts +382 -0
- package/apps/cf-tcp-container/tests/container-server.test.ts +358 -31
- package/apps/cf-tcp-container/tests/dockerfile.test.ts +110 -0
- package/apps/cf-tcp-container/tests/proxy-protocol.test.ts +187 -0
- package/apps/cf-tcp-container/tests/spectrum-terraform.test.ts +135 -0
- package/apps/cf-tcp-container/tests/tls-e2e.test.ts +5 -1
- package/apps/cf-tcp-container/wrangler.toml +18 -14
- package/apps/cf-worker/package.json +3 -4
- package/apps/cf-worker/src/worker.ts +77 -5
- package/apps/cf-worker/tests/raw-modules.d.ts +11 -0
- package/apps/cf-worker/tests/smoke.test.ts +4 -0
- package/apps/cf-worker/tests/wrangler-config.test.ts +47 -0
- package/apps/cf-worker/tests/ws-admission.test.ts +112 -0
- package/apps/cf-worker/tests/ws-rate-limit.test.ts +133 -0
- package/apps/cf-worker/wrangler.test.toml +15 -1
- package/apps/cf-worker/wrangler.toml +95 -77
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +14 -2
- package/apps/local-cli/src/line-scanner.ts +26 -0
- package/apps/local-cli/src/server.ts +44 -19
- package/apps/local-cli/tests/line-scanner.test.ts +64 -0
- package/apps/local-cli/tests/tcp.test.ts +29 -0
- package/apps/web/landing/favicon.ico +0 -0
- package/apps/web/landing/index.html +1 -0
- package/apps/web/package.json +2 -2
- package/apps/web/scripts/build.mjs +66 -4
- package/apps/web/src/build-env.ts +125 -4
- package/apps/web/src/config-schema.ts +20 -6
- 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 +2 -2
- package/apps/web/tests/config-schema.test.ts +149 -25
- package/docs/AWS-Deployment.md +793 -118
- package/docs/AWS-TCP-Deployment.md +57 -47
- package/docs/Chat-History.md +55 -0
- package/docs/Cloudflare-Deployment-Guide.md +95 -114
- package/docs/Cloudflare-TCP-Deployment.md +160 -101
- package/docs/Release-Process.md +27 -23
- package/docs/SASL-EXTERNAL.md +175 -0
- package/docs/Services.md +69 -22
- package/docs/WebClientGuide.md +35 -26
- package/package.json +7 -10
- package/packages/aws-adapter/package.json +1 -1
- package/packages/aws-adapter/src/admission.ts +28 -13
- package/packages/aws-adapter/src/aws-runtime.ts +30 -3
- package/packages/aws-adapter/src/cdk-table-defs.ts +39 -16
- package/packages/aws-adapter/src/config-loader.ts +153 -8
- package/packages/aws-adapter/src/dynamo-services-store.ts +19 -0
- package/packages/aws-adapter/src/handlers/connect.ts +73 -1
- package/packages/aws-adapter/src/handlers/default.ts +279 -123
- package/packages/aws-adapter/src/handlers/index.ts +98 -25
- package/packages/aws-adapter/src/handlers/nlb-stream.ts +135 -14
- package/packages/aws-adapter/src/index.ts +5 -7
- package/packages/aws-adapter/src/ip-admission.ts +79 -0
- package/packages/aws-adapter/src/origin-allowlist.ts +94 -0
- package/packages/aws-adapter/src/serialize.ts +23 -0
- package/packages/aws-adapter/src/tables.ts +11 -12
- package/packages/aws-adapter/tests/admission.test.ts +60 -2
- package/packages/aws-adapter/tests/aws-harness.ts +23 -2
- package/packages/aws-adapter/tests/aws-runtime.test.ts +64 -0
- package/packages/aws-adapter/tests/config-loader.test.ts +217 -0
- package/packages/aws-adapter/tests/connect.test.ts +323 -3
- package/packages/aws-adapter/tests/default-frame-limit.test.ts +231 -0
- package/packages/aws-adapter/tests/default-occ.test.ts +226 -0
- package/packages/aws-adapter/tests/dynamo-services-store-unit.test.ts +134 -1
- package/packages/aws-adapter/tests/handlers.test.ts +174 -12
- package/packages/aws-adapter/tests/migrate-accounts-to-services.test.ts +164 -0
- package/packages/aws-adapter/tests/nlb-secure.test.ts +362 -0
- package/packages/aws-adapter/tests/nlb-stream.test.ts +628 -9
- 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 +0 -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 +100 -10
- package/packages/cf-adapter/src/channel-do.ts +13 -3
- package/packages/cf-adapter/src/config-loader.ts +133 -8
- package/packages/cf-adapter/src/connection-do.ts +406 -116
- package/packages/cf-adapter/src/counter-do.ts +142 -0
- package/packages/cf-adapter/src/d1-services-store.ts +105 -26
- package/packages/cf-adapter/src/env.ts +99 -10
- package/packages/cf-adapter/src/index.ts +17 -7
- package/packages/cf-adapter/src/rate-limit-do.ts +87 -0
- package/packages/cf-adapter/tests/cf-runtime.test.ts +205 -16
- package/packages/cf-adapter/tests/channel-do.test.ts +118 -1
- package/packages/cf-adapter/tests/config-loader.test.ts +159 -0
- package/packages/cf-adapter/tests/connection-do-counter.test.ts +165 -0
- package/packages/cf-adapter/tests/connection-do-coverage.test.ts +460 -0
- package/packages/cf-adapter/tests/connection-do-frame-limit.test.ts +177 -0
- package/packages/cf-adapter/tests/connection-do-pure.test.ts +164 -54
- package/packages/cf-adapter/tests/connection-do-sasl-d1.test.ts +62 -38
- package/packages/cf-adapter/tests/connection-do-ws-spec-contract.test.ts +7 -4
- package/packages/cf-adapter/tests/counter-do.test.ts +181 -0
- package/packages/cf-adapter/tests/d1-services-store.test.ts +245 -3
- package/packages/cf-adapter/tests/rate-limit-do.test.ts +160 -0
- package/packages/cf-adapter/tests/serialize.test.ts +25 -0
- package/packages/cf-adapter/tests/worker/main.ts +4 -0
- 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 +25 -0
- package/packages/in-memory-runtime/tests/in-memory-runtime.test.ts +74 -0
- package/packages/irc-core/package.json +1 -1
- package/packages/irc-core/src/account-migration.ts +140 -0
- package/packages/irc-core/src/caps/capabilities.ts +20 -10
- package/packages/irc-core/src/certfp.ts +178 -0
- package/packages/irc-core/src/commands/account-auth.ts +16 -19
- package/packages/irc-core/src/commands/cap.ts +10 -2
- package/packages/irc-core/src/commands/chanserv.ts +117 -14
- package/packages/irc-core/src/commands/chathistory.ts +13 -5
- package/packages/irc-core/src/commands/hostserv.ts +84 -8
- package/packages/irc-core/src/commands/index.ts +2 -1
- package/packages/irc-core/src/commands/invite.ts +1 -7
- package/packages/irc-core/src/commands/join.ts +1 -16
- package/packages/irc-core/src/commands/kick.ts +1 -8
- package/packages/irc-core/src/commands/list.ts +1 -8
- package/packages/irc-core/src/commands/memoserv.ts +1 -1
- package/packages/irc-core/src/commands/mode.ts +1 -8
- package/packages/irc-core/src/commands/multiline.ts +4 -10
- package/packages/irc-core/src/commands/names.ts +53 -13
- package/packages/irc-core/src/commands/nickserv.ts +161 -11
- package/packages/irc-core/src/commands/oper.ts +361 -8
- package/packages/irc-core/src/commands/part.ts +4 -10
- package/packages/irc-core/src/commands/privmsg.ts +8 -4
- package/packages/irc-core/src/commands/registration.ts +148 -4
- package/packages/irc-core/src/commands/sasl.ts +154 -46
- package/packages/irc-core/src/commands/topic.ts +10 -12
- package/packages/irc-core/src/commands/who.ts +1 -8
- package/packages/irc-core/src/config.ts +424 -25
- package/packages/irc-core/src/credential-hashing.ts +11 -54
- package/packages/irc-core/src/effects.ts +24 -0
- package/packages/irc-core/src/flood-control.ts +10 -10
- package/packages/irc-core/src/frame-rate-limit.ts +82 -0
- package/packages/irc-core/src/index.ts +9 -0
- package/packages/irc-core/src/oper-hashing.ts +43 -0
- package/packages/irc-core/src/oper-lockout.ts +87 -0
- package/packages/irc-core/src/ports.ts +529 -190
- package/packages/irc-core/src/protocol/bytes.ts +65 -0
- package/packages/irc-core/src/protocol/channel-name.ts +37 -0
- package/packages/irc-core/src/protocol/index.ts +12 -1
- package/packages/irc-core/src/protocol/outbound.ts +43 -10
- package/packages/irc-core/src/protocol/parser.ts +79 -10
- package/packages/irc-core/src/state/connection.ts +13 -0
- package/packages/irc-core/src/types.ts +266 -23
- package/packages/irc-core/src/ws-framing.ts +5 -4
- package/packages/irc-core/tests/account-migration.test.ts +133 -0
- package/packages/irc-core/tests/bytes.test.ts +89 -0
- package/packages/irc-core/tests/certfp.test.ts +117 -0
- package/packages/irc-core/tests/commands/cap.test.ts +76 -2
- package/packages/irc-core/tests/commands/chanserv.test.ts +166 -0
- package/packages/irc-core/tests/commands/chathistory.test.ts +140 -0
- package/packages/irc-core/tests/commands/hostserv.test.ts +316 -0
- package/packages/irc-core/tests/commands/join.test.ts +78 -1
- 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/names.test.ts +193 -0
- package/packages/irc-core/tests/commands/nickserv.test.ts +419 -3
- package/packages/irc-core/tests/commands/oper.test.ts +574 -1
- package/packages/irc-core/tests/commands/privmsg.test.ts +16 -0
- package/packages/irc-core/tests/commands/registration.test.ts +602 -133
- package/packages/irc-core/tests/commands/sasl.test.ts +742 -172
- package/packages/irc-core/tests/commands/topic.test.ts +137 -2
- package/packages/irc-core/tests/commands/unified-account.test.ts +104 -84
- package/packages/irc-core/tests/config.test.ts +534 -2
- package/packages/irc-core/tests/credential-hashing.test.ts +0 -78
- package/packages/irc-core/tests/effects.test.ts +14 -0
- package/packages/irc-core/tests/flood-control.test.ts +29 -1
- package/packages/irc-core/tests/frame-rate-limit.test.ts +98 -0
- package/packages/irc-core/tests/message-store.test.ts +5 -0
- package/packages/irc-core/tests/oper-hashing.test.ts +60 -0
- package/packages/irc-core/tests/oper-lockout.test.ts +74 -0
- package/packages/irc-core/tests/outbound.test.ts +148 -0
- package/packages/irc-core/tests/parser.test.ts +287 -5
- package/packages/irc-core/tests/persistent-services-store.test.ts +212 -12
- package/packages/irc-core/tests/ports.test.ts +170 -7
- package/packages/irc-core/tests/services-store.test.ts +567 -1
- package/packages/irc-core/tests/ws-framing.test.ts +45 -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 +123 -22
- package/packages/irc-server/src/dispatch.ts +1 -0
- package/packages/irc-server/src/index.ts +7 -0
- package/packages/irc-server/src/redact.ts +159 -0
- package/packages/irc-server/src/runtime.ts +14 -0
- package/packages/irc-server/src/transport.ts +28 -1
- package/packages/irc-server/tests/actor.test.ts +563 -54
- package/packages/irc-server/tests/dispatch.test.ts +31 -0
- package/packages/irc-server/tests/redact.test.ts +198 -0
- package/packages/irc-server/tests/runtime.test.ts +2 -0
- package/packages/irc-server/tests/transport.test.ts +66 -0
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/in-memory-harness.ts +4 -0
- 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 +2 -2
- package/tools/ci-hardening/src/cf-deploy-cli.ts +3 -0
- package/tools/ci-hardening/src/cf-deploy.ts +118 -0
- package/tools/ci-hardening/src/deploy-hostname.ts +118 -0
- package/tools/ci-hardening/src/env-var-drift.ts +192 -0
- package/tools/ci-hardening/src/hostname-guard.ts +11 -0
- package/tools/ci-hardening/src/index.ts +19 -0
- package/tools/ci-hardening/src/validate.ts +57 -0
- package/tools/ci-hardening/tests/__wrangler_missing__.toml +2 -0
- package/tools/ci-hardening/tests/__wrangler_placeholder__.toml +3 -0
- package/tools/ci-hardening/tests/__wrangler_real__.toml +3 -0
- package/tools/ci-hardening/tests/cf-deploy.test.ts +200 -0
- package/tools/ci-hardening/tests/deploy-aws-oidc.test.ts +96 -0
- package/tools/ci-hardening/tests/deploy-hostname.test.ts +348 -0
- package/tools/ci-hardening/tests/env-var-drift.test.ts +284 -0
- package/tools/ci-hardening/tests/validate.test.ts +42 -0
- package/tools/ci-hardening/vitest.config.ts +5 -1
- package/tools/hash-oper-cred.ts +85 -0
- package/tools/load-test/package.json +1 -1
- 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/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
|
@@ -12,7 +12,7 @@ the two are independent and a deployment can enable either or both.
|
|
|
12
12
|
> This doc covers the *how*.
|
|
13
13
|
|
|
14
14
|
**Acceptance criterion (TICKET-058):** a new contributor can deploy
|
|
15
|
-
their own TCP+TLS
|
|
15
|
+
their own TCP+TLS instance following only this doc.
|
|
16
16
|
|
|
17
17
|
Cross-reference: `apps/cf-tcp-container/` (origin),
|
|
18
18
|
`apps/cf-tcp-container/wrangler.toml` (container config),
|
|
@@ -36,22 +36,32 @@ path adds two stateful pieces in front of the shared IRC core:
|
|
|
36
36
|
┌──────────────────────────────────────────────────────────────┐
|
|
37
37
|
│ Cloudflare Spectrum (Enterprise) │
|
|
38
38
|
│ • terminates TLS on :6697 │
|
|
39
|
-
│ •
|
|
39
|
+
│ • re-encrypts to the origin (tls_mode = "full") │
|
|
40
40
|
│ • PROXY protocol v1 carries the real client IP │
|
|
41
41
|
└──────────────────────────┬───────────────────────────────────┘
|
|
42
|
-
│
|
|
42
|
+
│ TLS (:6697, origin cert)
|
|
43
43
|
▼
|
|
44
44
|
┌──────────────────────────────────────────────────────────────┐
|
|
45
45
|
│ Cloudflare Container (apps/cf-tcp-container) │
|
|
46
|
-
│ • node:24 process, EXPOSE
|
|
46
|
+
│ • node:24 process, EXPOSE 6697 — terminates TLS itself │
|
|
47
|
+
│ (TLS_CERT_PATH / TLS_KEY_PATH, mounted at /data/certs) │
|
|
47
48
|
│ • ConnectionActor + InMemoryRuntime + TcpByteStream- │
|
|
48
49
|
│ Transport (the shared transport seam, TICKET-053) │
|
|
49
50
|
│ • persistence snapshot at /data/state.json │
|
|
50
51
|
│ (channel topology survives restart; live connections │
|
|
51
52
|
│ do not — clients reconnect) │
|
|
53
|
+
│ • plaintext :6667 = localhost-only debug listener │
|
|
54
|
+
│ (DEBUG_PLAINTEXT=localhost; never routed, never in │
|
|
55
|
+
│ prod — a prod-classified boot refuses to start │
|
|
56
|
+
│ without TLS material) │
|
|
52
57
|
└──────────────────────────────────────────────────────────────┘
|
|
53
58
|
```
|
|
54
59
|
|
|
60
|
+
TLS is **end-to-end**: no plaintext IRC bytes ever cross the origin
|
|
61
|
+
network, so an attacker who can reach the origin address directly can
|
|
62
|
+
neither read `PASS`/`AUTHENTICATE`/`PRIVMSG` off the wire nor spoof
|
|
63
|
+
them.
|
|
64
|
+
|
|
55
65
|
The **parser → reducer → dispatch pipeline is shared verbatim** with the
|
|
56
66
|
wss path. Only the line-framing transport differs: a `TcpByteStreamTransport`
|
|
57
67
|
reassembles `\r\n`-terminated IRC lines across arbitrary TCP chunks
|
|
@@ -61,8 +71,8 @@ Two artifacts are deployed and configured:
|
|
|
61
71
|
|
|
62
72
|
| Artifact | Tool | What it does |
|
|
63
73
|
|---------------------------------------|----------------|------------------------------------------------|
|
|
64
|
-
| `sirc-tcp-origin` Cloudflare Container| `wrangler` | Builds + runs the IRC core container image.
|
|
65
|
-
| `cloudflare_spectrum_application` | `terraform` | TLS
|
|
74
|
+
| `sirc-tcp-origin` Cloudflare Container| `wrangler` | Builds + runs the IRC core container image (TLS origin). |
|
|
75
|
+
| `cloudflare_spectrum_application` | `terraform` | TLS on :6697 + re-encrypted origin forwarding (`tls_mode = "full"`). |
|
|
66
76
|
|
|
67
77
|
---
|
|
68
78
|
|
|
@@ -77,7 +87,7 @@ Two artifacts are deployed and configured:
|
|
|
77
87
|
| **Cloudflare Enterprise**| **Spectrum is an Enterprise-tier feature.** Required for the TCP path. |
|
|
78
88
|
| Containers beta access | Cloudflare Containers is in beta; request access in the dashboard. |
|
|
79
89
|
| A Cloudflare zone | The public hostname (e.g. `irc.example.com`) must live in a zone you control. |
|
|
80
|
-
| Git checkout | Clean working tree on `main` for
|
|
90
|
+
| Git checkout | Clean working tree on `main` for deploys. |
|
|
81
91
|
|
|
82
92
|
> The wss path runs on the Workers Free/Paid plan. The TCP path does
|
|
83
93
|
> **not** — Spectrum mandates Enterprise. If you do not have Enterprise,
|
|
@@ -133,17 +143,19 @@ export CLOUDFLARE_ACCOUNT_ID=...
|
|
|
133
143
|
|
|
134
144
|
## 4. Local development
|
|
135
145
|
|
|
136
|
-
Run the container origin locally without Spectrum
|
|
137
|
-
|
|
138
|
-
`
|
|
146
|
+
Run the container origin locally without Spectrum. The plaintext TCP
|
|
147
|
+
listener is **debug-only and localhost-only**: it starts solely when
|
|
148
|
+
`DEBUG_PLAINTEXT=localhost` is set (the config loader then forces the
|
|
149
|
+
bind to `127.0.0.1` — `TCP_HOST` cannot widen it). This exercises the
|
|
150
|
+
exact `ConnectionActor` + `TcpByteStreamTransport` pipeline.
|
|
139
151
|
|
|
140
152
|
```bash
|
|
141
153
|
# Build the workspace + the container package.
|
|
142
154
|
pnpm build
|
|
143
155
|
|
|
144
|
-
# Run the origin
|
|
145
|
-
|
|
146
|
-
--
|
|
156
|
+
# Run the origin with the localhost-only plaintext debug listener.
|
|
157
|
+
DEBUG_PLAINTEXT=localhost SERVER_NAME=irc.local TCP_PORT=6667 \
|
|
158
|
+
pnpm --filter @serverless-ircd/cf-tcp-container start
|
|
147
159
|
```
|
|
148
160
|
|
|
149
161
|
Connect a plaintext client (no TLS) to `localhost:6667`:
|
|
@@ -156,12 +168,19 @@ JOIN #test
|
|
|
156
168
|
PRIVMSG #test :hello over plaintext TCP
|
|
157
169
|
```
|
|
158
170
|
|
|
159
|
-
For a TLS
|
|
160
|
-
|
|
171
|
+
For a TLS local loop that mirrors production (TLS terminating *at the
|
|
172
|
+
origin*), point the origin at a local cert pair instead — no debug
|
|
173
|
+
flag needed:
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
TLS_CERT_PATH=./certs/tls.pem TLS_KEY_PATH=./certs/tls.key SERVER_NAME=irc.local \
|
|
177
|
+
pnpm --filter @serverless-ircd/cf-tcp-container start
|
|
178
|
+
# → TLS listener on :6697; plaintext stays closed.
|
|
179
|
+
```
|
|
161
180
|
|
|
162
181
|
The unit + the TLS e2e suites live in `apps/cf-tcp-container/tests/`
|
|
163
|
-
(including `tls-e2e.test.ts`, which
|
|
164
|
-
origin). Run them with:
|
|
182
|
+
(including `tls-e2e.test.ts` and `container-server-tls.test.ts`, which
|
|
183
|
+
run real TLS handshakes against the origin). Run them with:
|
|
165
184
|
|
|
166
185
|
```bash
|
|
167
186
|
pnpm --filter @serverless-ircd/cf-tcp-container test
|
|
@@ -169,27 +188,58 @@ pnpm --filter @serverless-ircd/cf-tcp-container test
|
|
|
169
188
|
|
|
170
189
|
---
|
|
171
190
|
|
|
172
|
-
## 5. Deploy
|
|
191
|
+
## 5. Deploy
|
|
173
192
|
|
|
174
|
-
|
|
175
|
-
|
|
193
|
+
The deploy is `workflow_dispatch`-only (a maintainer triggers it by
|
|
194
|
+
hand); you can also deploy manually.
|
|
176
195
|
|
|
177
196
|
### 5.1 Deploy the container origin
|
|
178
197
|
|
|
179
198
|
```bash
|
|
180
199
|
# From the repo root:
|
|
181
|
-
pnpm deploy:cf-tcp
|
|
200
|
+
pnpm deploy:cf-tcp
|
|
182
201
|
# Equivalent to:
|
|
183
|
-
# pnpm build && pnpm --filter @serverless-ircd/cf-tcp-container deploy
|
|
184
|
-
# → wrangler deploy
|
|
202
|
+
# pnpm build && pnpm --filter @serverless-ircd/cf-tcp-container run deploy
|
|
203
|
+
# → wrangler deploy (inside apps/cf-tcp-container)
|
|
185
204
|
```
|
|
186
205
|
|
|
187
206
|
This builds the container image from `apps/cf-tcp-container/Dockerfile`
|
|
188
|
-
and deploys the `sirc-tcp-origin
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
### 5.2
|
|
207
|
+
and deploys the `sirc-tcp-origin` Container. The first deploy prints
|
|
208
|
+
the **origin address** — copy it into `CF_TCP_ORIGIN_ADDRESS` (used by
|
|
209
|
+
the Spectrum step next).
|
|
210
|
+
|
|
211
|
+
### 5.2 Provision the origin certificate
|
|
212
|
+
|
|
213
|
+
`tls_mode = "full"` means the **origin terminates TLS itself**, so it
|
|
214
|
+
needs a PEM cert/key pair. Mount it at `/data/certs/` (the image's
|
|
215
|
+
documented default: `TLS_CERT_PATH=/data/certs/tls.pem`,
|
|
216
|
+
`TLS_KEY_PATH=/data/certs/tls.key`; override via env if the pair lives
|
|
217
|
+
elsewhere). Cert files must be readable by UID 1000 (`node`) — the
|
|
218
|
+
container drops privileges before reading them. Any of these work:
|
|
219
|
+
|
|
220
|
+
| Option | How | Notes |
|
|
221
|
+
|------------------------|----------------------------------------------------------------|-------|
|
|
222
|
+
| **Cloudflare Origin Cert** | Dashboard → SSL/TLS → Origin Server → Create Certificate. | Free, long-lived, issued for your zone; the easiest option when the origin is only reached via Spectrum. Not publicly trusted (fine — clients validate the *edge* cert). |
|
|
223
|
+
| **Let's Encrypt** | `certbot certonly --dns-plugin … -d origin.example.com`, then copy `fullchain.pem`/`privkey.pem` to `/data/certs/tls.{pem,key}` and renew on a cron/sidecar. | Publicly trusted; needs automation for the 90-day rotation. |
|
|
224
|
+
| **BYO cert** | Any PEM pair from your CA. | Your compliance rules apply. |
|
|
225
|
+
|
|
226
|
+
> A prod-classified boot (`NODE_ENV=production`, set by the Dockerfile)
|
|
227
|
+
> **refuses to start** without `TLS_CERT_PATH`/`TLS_KEY_PATH` — there is
|
|
228
|
+
> no silent plaintext fallback. A missing/unreadable cert file also
|
|
229
|
+
> fails fast at boot with the offending path in the error.
|
|
230
|
+
|
|
231
|
+
### 5.3 Lock origin ingress down to Cloudflare IPs
|
|
232
|
+
|
|
233
|
+
Spectrum is the only thing that should ever reach the origin's :6697.
|
|
234
|
+
On any platform with a firewall / security-group primitive, restrict
|
|
235
|
+
ingress to Cloudflare's published ranges (https://www.cloudflare.com/ips/)
|
|
236
|
+
— otherwise a direct-reach attacker skips the edge entirely.
|
|
237
|
+
`spectrum.tf` ships a commented `cloudflare_list` reference for keeping
|
|
238
|
+
a managed list of the current ranges; Cloudflare Containers origins
|
|
239
|
+
rely on Spectrum `ip_firewall` + the platform's own network isolation,
|
|
240
|
+
but external-compute origins must apply the firewall on their side.
|
|
241
|
+
|
|
242
|
+
### 5.4 Apply the Spectrum TLS config
|
|
193
243
|
|
|
194
244
|
```bash
|
|
195
245
|
cd apps/cf-tcp-container/terraform
|
|
@@ -197,28 +247,29 @@ terraform init
|
|
|
197
247
|
terraform apply \
|
|
198
248
|
-var zone_id=$CF_ZONE_ID \
|
|
199
249
|
-var origin_address=$CF_TCP_ORIGIN_ADDRESS \
|
|
200
|
-
-var hostname=irc
|
|
250
|
+
-var hostname=irc.example.com
|
|
201
251
|
```
|
|
202
252
|
|
|
203
253
|
This creates the `cloudflare_spectrum_application.irc_tls` resource:
|
|
204
|
-
TLS on :6697
|
|
254
|
+
TLS on :6697 at the edge, **re-encrypted** to the container origin's
|
|
255
|
+
TLS listener on :6697 (`tls_mode = "full"` — the secure default).
|
|
205
256
|
|
|
206
|
-
### 5.
|
|
257
|
+
### 5.5 Smoke test over TLS
|
|
207
258
|
|
|
208
259
|
```bash
|
|
209
260
|
# Minimal smoke: register + quit over the real TLS endpoint.
|
|
210
261
|
printf 'NICK smoke\r\nUSER smoke 0 * :Smoke\r\nQUIT :smoke\r\n' | \
|
|
211
|
-
timeout 10 openssl s_client -connect irc
|
|
262
|
+
timeout 10 openssl s_client -connect irc.example.com:6697 -quiet | \
|
|
212
263
|
grep -q '001' && echo "SMOKE PASS" || echo "SMOKE FAIL"
|
|
213
264
|
```
|
|
214
265
|
|
|
215
266
|
CI does the same — see `.github/workflows/deploy-cf-tcp.yml` step
|
|
216
267
|
"Smoke e2e (irc+tls)", gated on the `CF_TCP_SMOKE_HOST` repo variable.
|
|
217
268
|
|
|
218
|
-
### 5.
|
|
269
|
+
### 5.6 Connect a real IRC client
|
|
219
270
|
|
|
220
271
|
```
|
|
221
|
-
/server add ircd irc
|
|
272
|
+
/server add ircd irc.example.com/6697
|
|
222
273
|
/set irc.server.ircd.ssl on
|
|
223
274
|
/connect ircd
|
|
224
275
|
/join #test
|
|
@@ -226,27 +277,6 @@ CI does the same — see `.github/workflows/deploy-cf-tcp.yml` step
|
|
|
226
277
|
|
|
227
278
|
---
|
|
228
279
|
|
|
229
|
-
## 6. Deploy production
|
|
230
|
-
|
|
231
|
-
Production is the default `wrangler.toml` environment (no `--env` flag).
|
|
232
|
-
|
|
233
|
-
```bash
|
|
234
|
-
# 1. Edit wrangler.toml [vars] for production identity.
|
|
235
|
-
# 2. Deploy the container:
|
|
236
|
-
pnpm deploy:cf-tcp:prod
|
|
237
|
-
# 3. Apply Spectrum config for the production hostname:
|
|
238
|
-
cd apps/cf-tcp-container/terraform
|
|
239
|
-
terraform apply -var zone_id=$CF_ZONE_ID \
|
|
240
|
-
-var origin_address=$CF_TCP_ORIGIN_ADDRESS \
|
|
241
|
-
-var hostname=irc.example.com
|
|
242
|
-
```
|
|
243
|
-
|
|
244
|
-
There is **no CI auto-deploy to production** — production deploys are
|
|
245
|
-
manual. Staging deploys on every push to `main` that touches the
|
|
246
|
-
container path.
|
|
247
|
-
|
|
248
|
-
---
|
|
249
|
-
|
|
250
280
|
## 7. Configuration reference
|
|
251
281
|
|
|
252
282
|
### 7.1 Container — `apps/cf-tcp-container/wrangler.toml`
|
|
@@ -261,8 +291,13 @@ Top-level `[vars]` map directly to the container config loader
|
|
|
261
291
|
| `SERVER_VERSION` | Version string in `002`. | (from package version) |
|
|
262
292
|
| `CREATED_AT` | Creation text/time in `003`. | build/deploy timestamp |
|
|
263
293
|
| `MOTD_LINES` | Message-of-the-day, `\n`-delimited. | welcome banner |
|
|
264
|
-
| `
|
|
265
|
-
| `
|
|
294
|
+
| `NODE_ENV` | `production` classifies the boot as prod: the loader then refuses to start without TLS material and rejects `DEBUG_PLAINTEXT`. The Dockerfile sets it. | (unset = dev) |
|
|
295
|
+
| `TLS_CERT_PATH` | PEM certificate the origin TLS listener presents. **Required in prod** (with `TLS_KEY_PATH`). | image: `/data/certs/tls.pem` |
|
|
296
|
+
| `TLS_KEY_PATH` | PEM private key for the origin TLS listener. **Required in prod** (with `TLS_CERT_PATH`). | image: `/data/certs/tls.key` |
|
|
297
|
+
| `TLS_PORT` | Origin TLS listener port. | `6697` |
|
|
298
|
+
| `TCP_PORT` | Plaintext debug-listener port — only used when `DEBUG_PLAINTEXT=localhost` is set. | `6667` |
|
|
299
|
+
| `TCP_HOST` | Bind address. Forced to `127.0.0.1` whenever the plaintext debug listener is enabled. | `0.0.0.0` |
|
|
300
|
+
| `DEBUG_PLAINTEXT` | Exact value `localhost` opts into the localhost-only plaintext debug listener. Any other value is rejected; forbidden in prod. | (unset = TLS-only) |
|
|
266
301
|
| `PERSISTENCE_PATH` | Snapshot file path; enables channel-state survival. | `/data/state.json` |
|
|
267
302
|
| `SNAPSHOT_INTERVAL_MS`| Auto-snapshot cadence (ms). | `60000` |
|
|
268
303
|
| `OPER_USER` / `OPER_PASSWORD` | Oper credentials for `OPER`. | — |
|
|
@@ -294,10 +329,10 @@ sleep_after = "2h"
|
|
|
294
329
|
|-------------------|---------------------------------------------------|---------------------|
|
|
295
330
|
| `zone_id` | Cloudflare zone for the hostname. | *(required)* |
|
|
296
331
|
| `origin_address` | Hostname/IP of the container origin. | *(required)* |
|
|
297
|
-
| `origin_port` | Port the origin
|
|
332
|
+
| `origin_port` | Port the origin's **TLS** listener serves. | `6697` |
|
|
298
333
|
| `hostname` | Public hostname clients connect to. | `irc.example.com` |
|
|
299
334
|
| `spectrum_port` | Public port Spectrum listens on. | `6697` |
|
|
300
|
-
| `tls_mode` | `
|
|
335
|
+
| `tls_mode` | `full` (TLS end-to-end — the secure default) / `strict` (edge also validates the origin cert). | `full` |
|
|
301
336
|
|
|
302
337
|
`ip_firewall = true` (Cloudflare's L3/L4 DDoS) and
|
|
303
338
|
`argo_smart_routing = true` are on by default. `proxy_protocol = "v1"`
|
|
@@ -332,24 +367,39 @@ path (Durable Objects) does not have. Two layers of survival:
|
|
|
332
367
|
|
|
333
368
|
---
|
|
334
369
|
|
|
335
|
-
## 9. mTLS (SASL EXTERNAL)
|
|
370
|
+
## 9. mTLS (SASL EXTERNAL) — not supported on this transport
|
|
371
|
+
|
|
372
|
+
SASL `EXTERNAL` requires the verified client certificate to reach the
|
|
373
|
+
application layer. On the TCP container path it cannot: Spectrum
|
|
374
|
+
forwards to the origin with **PROXY protocol v1**, which carries no
|
|
375
|
+
client-cert subject, and although `tls_mode = "full"` terminates TLS at
|
|
376
|
+
the origin (`TLS_CERT_PATH`/`TLS_KEY_PATH`), no client-certificate
|
|
377
|
+
trust pool is wired through to the IRC core — there is no
|
|
378
|
+
`MtlsIdentityProvider` to feed, in either TLS mode.
|
|
336
379
|
|
|
337
|
-
|
|
338
|
-
|
|
380
|
+
The container therefore pins the mechanism off explicitly via the
|
|
381
|
+
`sasl.externalUnsupportedMessage` transport override:
|
|
339
382
|
|
|
340
|
-
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
- **Full mode:** set `tls_mode = "full"` and configure the origin to
|
|
345
|
-
present its own certificate; configure Spectrum/API Shield mTLS with
|
|
346
|
-
your CA pool. The verified client-cert subject is then exposed to the
|
|
347
|
-
origin, feeding the `MtlsIdentityProvider` port → `AccountStore` →
|
|
348
|
-
`AUTHENTICATE EXTERNAL` succeeds (`903`) or fails (`904`).
|
|
383
|
+
- the `sasl` capability advertises `PLAIN` only, and
|
|
384
|
+
- a client sending `AUTHENTICATE EXTERNAL` is rejected up front with
|
|
385
|
+
`904 ERR_SASLFAIL` carrying the stable message
|
|
386
|
+
`SASL EXTERNAL not supported on this transport`.
|
|
349
387
|
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
`Cloudflare-Deployment-Guide.md`)
|
|
388
|
+
The refusal does not count toward the per-connection SASL failure
|
|
389
|
+
lockout. Clients that need certificate-backed SASL should use the wss
|
|
390
|
+
path (CF API Shield mTLS — see `Cloudflare-Deployment-Guide.md`) or the
|
|
391
|
+
AWS NLB+TLS path (API Gateway custom-domain mTLS — see
|
|
392
|
+
`AWS-TCP-Deployment.md`). Forwarding the client-cert subject via
|
|
393
|
+
PROXY-protocol-v2 TLVs (which Spectrum can emit) is the documented
|
|
394
|
+
follow-up if demand materialises; see `SASL-EXTERNAL.md`.
|
|
395
|
+
|
|
396
|
+
- **Flexible mode (explicit downgrade):** Spectrum terminates TLS and
|
|
397
|
+
forwards plaintext TCP to the origin. EXTERNAL is equally unavailable
|
|
398
|
+
there, and plaintext origin traffic reintroduces the direct-reach
|
|
399
|
+
exposure this topology closed. Avoid it.
|
|
400
|
+
|
|
401
|
+
Cross-reference: ADR-006 (SASL scope), `SASL-EXTERNAL.md` (mechanism
|
|
402
|
+
scope + transport availability).
|
|
353
403
|
|
|
354
404
|
---
|
|
355
405
|
|
|
@@ -360,7 +410,7 @@ and SASL credentials ship as `[vars]` (`OPER_*`, `SASL_ACCOUNTS`). When
|
|
|
360
410
|
strong-secret handling lands, set them via:
|
|
361
411
|
|
|
362
412
|
```bash
|
|
363
|
-
wrangler secret put OPER_PASSWORD
|
|
413
|
+
wrangler secret put OPER_PASSWORD
|
|
364
414
|
```
|
|
365
415
|
|
|
366
416
|
CI uses these **GitHub Actions secrets/variables** (configure under
|
|
@@ -384,19 +434,20 @@ Settings → Secrets and variables → Actions):
|
|
|
384
434
|
(the wss path). The two are independent — a deployment can enable
|
|
385
435
|
either or both.
|
|
386
436
|
|
|
387
|
-
Triggers:
|
|
388
|
-
|
|
437
|
+
Triggers: `workflow_dispatch` only (a maintainer triggers it by hand;
|
|
438
|
+
there is no automatic push-to-main deploy). Steps (in order):
|
|
389
439
|
|
|
390
440
|
1. Checkout → pnpm 11 + Node 24 → `pnpm install --frozen-lockfile`.
|
|
391
441
|
2. `pnpm build`, `pnpm typecheck`, `pnpm --filter cf-tcp-container test`.
|
|
392
|
-
3. `wrangler deploy
|
|
442
|
+
3. `wrangler deploy` (container).
|
|
393
443
|
4. `terraform init && terraform apply -auto-approve` (Spectrum).
|
|
394
444
|
5. TLS smoke (`openssl s_client`), gated on `CF_TCP_SMOKE_HOST`.
|
|
395
445
|
|
|
396
|
-
Concurrency is serialized via `concurrency.group: cf-tcp-
|
|
446
|
+
Concurrency is serialized via `concurrency.group: cf-tcp-deploy`.
|
|
397
447
|
|
|
398
|
-
There is
|
|
399
|
-
|
|
448
|
+
There is a single deploy target — the staging/prod split was collapsed,
|
|
449
|
+
so the same workflow + account ships to whichever Container namespace
|
|
450
|
+
the credentials target.
|
|
400
451
|
|
|
401
452
|
---
|
|
402
453
|
|
|
@@ -409,7 +460,7 @@ because of the stateful origin:
|
|
|
409
460
|
|------------------------|------------------------------------|--------------------------------------------|
|
|
410
461
|
| **Plan tier** | Workers Paid ($5/mo) suffices. | **Enterprise required** (Spectrum). |
|
|
411
462
|
| **Idle connections** | Hibernation → ~zero memory cost. | Origin holds every socket; `sleep_after` only helps when *all* connections are idle. |
|
|
412
|
-
| **Compute** | Per-request (WebSocket frames). | **GB-second** of running container time.
|
|
463
|
+
| **Compute** | Per-request (WebSocket frames). | **GB-second** of running container time. `tls_mode = "full"` moves TLS termination into the origin container, so it now pays the per-connection handshake CPU — budget handshakes/sec (each reconnect = one handshake) against the container's compute allocation when sizing `instance_type`. |
|
|
413
464
|
| **State durability** | DO storage (managed). | Container volume snapshot (self-managed). |
|
|
414
465
|
| **DDoS** | Cloudflare built-in. | Spectrum `ip_firewall` + Argo. |
|
|
415
466
|
|
|
@@ -437,10 +488,19 @@ respond. Causes:
|
|
|
437
488
|
- `CF_TCP_ORIGIN_ADDRESS` points at the wrong origin. It must match the
|
|
438
489
|
address printed by `wrangler deploy` (§5.1).
|
|
439
490
|
- The origin crashed on boot. Check `wrangler tail` for the container
|
|
440
|
-
(or container logs in the dashboard).
|
|
441
|
-
|
|
491
|
+
(or container logs in the dashboard). Common causes: the cert pair is
|
|
492
|
+
missing/unreadable at `TLS_CERT_PATH`/`TLS_KEY_PATH` (prod boots
|
|
493
|
+
refuse to start without it — the error names the offending path), an
|
|
494
|
+
invalid `MOTD_LINES`, or a malformed `SASL_ACCOUNTS` line.
|
|
495
|
+
|
|
496
|
+
### 13.3 Origin refuses to boot: "refusing to start a prod-classified origin without TLS"
|
|
442
497
|
|
|
443
|
-
|
|
498
|
+
`NODE_ENV=production` is set (the Dockerfile sets it by default) but
|
|
499
|
+
`TLS_CERT_PATH`/`TLS_KEY_PATH` are not. Provision a cert pair (§5.2),
|
|
500
|
+
mount it at `/data/certs/`, and restart. There is deliberately no
|
|
501
|
+
plaintext fallback in prod.
|
|
502
|
+
|
|
503
|
+
### 13.4 Channel topic/modes lost after a restart
|
|
444
504
|
|
|
445
505
|
`PERSISTENCE_PATH` is unset or the volume is not attached. Confirm
|
|
446
506
|
`PERSISTENCE_PATH=/data/state.json` in `[vars]` and that the `/data`
|
|
@@ -448,13 +508,13 @@ volume (`VOLUME ["/data"]` in the `Dockerfile`) is mounted on the
|
|
|
448
508
|
container platform. Without it the snapshot is written to ephemeral
|
|
449
509
|
storage and lost.
|
|
450
510
|
|
|
451
|
-
### 13.
|
|
511
|
+
### 13.5 Client IP seen as Spectrum's address
|
|
452
512
|
|
|
453
513
|
`proxy_protocol = "v1"` must be set in `spectrum.tf` (it is by default).
|
|
454
514
|
If you overrode it, the origin's per-IP admission gate sees Spectrum's
|
|
455
515
|
egress IP for every client and will wrongly throttle/cap.
|
|
456
516
|
|
|
457
|
-
### 13.
|
|
517
|
+
### 13.6 Messages split across chunks get lost
|
|
458
518
|
|
|
459
519
|
This is a transport-layer bug, not a config issue — the
|
|
460
520
|
`TcpByteStreamTransport` is contract-tested for chunk boundaries
|
|
@@ -462,7 +522,7 @@ This is a transport-layer bug, not a config issue — the
|
|
|
462
522
|
`pnpm --filter @serverless-ircd/cf-tcp-container test` and the shared
|
|
463
523
|
transport suite; a regression there will fail.
|
|
464
524
|
|
|
465
|
-
### 13.
|
|
525
|
+
### 13.7 `wrangler deploy` fails: `Containers beta not enabled`
|
|
466
526
|
|
|
467
527
|
Cloudflare Containers is in beta. Request access in the dashboard
|
|
468
528
|
(Workers & Pages → Containers). Until granted, only the local Docker
|
|
@@ -477,20 +537,19 @@ path (§4) and the wss transport work.
|
|
|
477
537
|
npx wrangler login # or set CLOUDFLARE_API_TOKEN
|
|
478
538
|
terraform version # >= 1.5
|
|
479
539
|
|
|
480
|
-
# Local dev (
|
|
540
|
+
# Local dev (TLS origin on :6697 with a local cert pair)
|
|
481
541
|
pnpm build
|
|
482
|
-
|
|
542
|
+
TLS_CERT_PATH=./certs/tls.pem TLS_KEY_PATH=./certs/tls.key \
|
|
543
|
+
pnpm --filter @serverless-ircd/cf-tcp-container start
|
|
544
|
+
# …or the localhost-only plaintext debug listener:
|
|
545
|
+
DEBUG_PLAINTEXT=localhost pnpm --filter @serverless-ircd/cf-tcp-container start
|
|
483
546
|
|
|
484
|
-
#
|
|
485
|
-
pnpm deploy:cf-tcp
|
|
547
|
+
# Deploy (single target — staging vs prod is which account you point at)
|
|
548
|
+
pnpm deploy:cf-tcp # container
|
|
486
549
|
cd apps/cf-tcp-container/terraform && \
|
|
487
550
|
terraform apply -var zone_id=$CF_ZONE_ID \
|
|
488
551
|
-var origin_address=$CF_TCP_ORIGIN_ADDRESS \
|
|
489
|
-
-var hostname=irc
|
|
490
|
-
|
|
491
|
-
# Production (manual)
|
|
492
|
-
pnpm deploy:cf-tcp:prod
|
|
493
|
-
cd apps/cf-tcp-container/terraform && terraform apply ... # prod hostname
|
|
552
|
+
-var hostname=irc.example.com # Spectrum (tls_mode=full)
|
|
494
553
|
|
|
495
554
|
# Tests
|
|
496
555
|
pnpm --filter @serverless-ircd/cf-tcp-container test
|
|
@@ -501,10 +560,10 @@ Key files:
|
|
|
501
560
|
| Path | What |
|
|
502
561
|
|-------------------------------------------------|--------------------------------------------|
|
|
503
562
|
| `apps/cf-tcp-container/src/main.ts` | Container entry point. |
|
|
504
|
-
| `apps/cf-tcp-container/src/container-server.ts` | TCP
|
|
505
|
-
| `apps/cf-tcp-container/src/config-loader.ts` | Env → `ServerConfig` + container knobs.
|
|
563
|
+
| `apps/cf-tcp-container/src/container-server.ts` | TCP/TLS listeners + actor wiring + persistence. |
|
|
564
|
+
| `apps/cf-tcp-container/src/config-loader.ts` | Env → `ServerConfig` + container knobs (TLS + plaintext-debug gates). |
|
|
506
565
|
| `apps/cf-tcp-container/src/persistence.ts` | Channel-topology snapshot/restore. |
|
|
507
566
|
| `apps/cf-tcp-container/wrangler.toml` | Container deploy config. |
|
|
508
|
-
| `apps/cf-tcp-container/Dockerfile` | Image (node:24, `/data` volume, :
|
|
509
|
-
| `apps/cf-tcp-container/terraform/spectrum.tf` | Spectrum TLS :6697 → origin :
|
|
567
|
+
| `apps/cf-tcp-container/Dockerfile` | Image (node:24, `/data` volume, :6697, UID 1000). |
|
|
568
|
+
| `apps/cf-tcp-container/terraform/spectrum.tf` | Spectrum TLS :6697 → origin TLS :6697 (`full`). |
|
|
510
569
|
| `.github/workflows/deploy-cf-tcp.yml` | Staging deploy + Spectrum + smoke CI. |
|
package/docs/Release-Process.md
CHANGED
|
@@ -37,7 +37,7 @@ this repo itself) should pin exact versions.
|
|
|
37
37
|
|
|
38
38
|
### 1.1 Workspace version lock
|
|
39
39
|
|
|
40
|
-
All
|
|
40
|
+
All sixteen `package.json` files (root + 15 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
|
|
|
@@ -63,8 +63,8 @@ not always possible.
|
|
|
63
63
|
| Push to `main` | Maintainers | Merging the release PR. |
|
|
64
64
|
| Push tags (`v*`) | Maintainers | `git push origin v0.x.0`. |
|
|
65
65
|
| Create GitHub Release | Maintainers | Publishing the release notes. |
|
|
66
|
-
| `CLOUDFLARE_API_TOKEN` secret | Repo Settings → Secrets |
|
|
67
|
-
| `CF_SMOKE_URL` variable | Repo Settings → Variables |
|
|
66
|
+
| `CLOUDFLARE_API_TOKEN` secret | Repo Settings → Secrets | Manual + CI deploys (single target). |
|
|
67
|
+
| `CF_SMOKE_URL` variable | Repo Settings → Variables | Smoke e2e in CI (manual dispatch). |
|
|
68
68
|
|
|
69
69
|
There is no separate "release engineer" role. The maintainer who cuts
|
|
70
70
|
the tag owns the release end-to-end, including the production deploy
|
|
@@ -101,7 +101,7 @@ git pull --ff-only origin main
|
|
|
101
101
|
# 2. CI is green on the tip of main.
|
|
102
102
|
# Open: https://github.com/<owner>/<repo>/actions
|
|
103
103
|
# - CI workflow: lint / typecheck / coverage all green.
|
|
104
|
-
# - deploy-cf workflow:
|
|
104
|
+
# - deploy-cf workflow: deploy + smoke e2e green (manual dispatch).
|
|
105
105
|
|
|
106
106
|
# 3. Local full verify.
|
|
107
107
|
pnpm install --frozen-lockfile
|
|
@@ -120,9 +120,9 @@ pnpm build # every package produces dist/
|
|
|
120
120
|
# be green (step 2), so locally re-run `pnpm coverage` as a
|
|
121
121
|
# belt-and-braces check before tagging.
|
|
122
122
|
|
|
123
|
-
# 5.
|
|
123
|
+
# 5. Smoke e2e passes against the deployed tip-of-main.
|
|
124
124
|
node apps/cf-worker/scripts/smoke.mjs \
|
|
125
|
-
--url wss://serverless-ircd
|
|
125
|
+
--url wss://serverless-ircd.<subdomain>.workers.dev
|
|
126
126
|
|
|
127
127
|
# 6. CHANGELOG.md is complete and accurate (§5).
|
|
128
128
|
# 7. All package.json versions are bumped and lock-step (§6).
|
|
@@ -187,8 +187,9 @@ understand what changed and what they need to do.
|
|
|
187
187
|
|
|
188
188
|
## 6. Bumping versions
|
|
189
189
|
|
|
190
|
-
There are
|
|
191
|
-
version
|
|
190
|
+
There are sixteen `package.json` files (root + 15 workspace packages).
|
|
191
|
+
They must end up at the same version (`tools/package.json` is a parent
|
|
192
|
+
container with no `version` field and is not bumped):
|
|
192
193
|
|
|
193
194
|
```
|
|
194
195
|
./package.json (workspace root)
|
|
@@ -206,6 +207,7 @@ apps/web/package.json
|
|
|
206
207
|
tools/tcp-ws-forwarder/package.json
|
|
207
208
|
tools/load-test/package.json
|
|
208
209
|
tools/ci-hardening/package.json
|
|
210
|
+
scripts/package.json (deploy helpers)
|
|
209
211
|
```
|
|
210
212
|
|
|
211
213
|
To cut `v0.X.Y`:
|
|
@@ -217,8 +219,8 @@ git checkout -b release/v0.X.Y
|
|
|
217
219
|
# bump each file. The version field is the only change.
|
|
218
220
|
# (Optional helper: `pnpm -r exec -- node -e '...'` if you script it.)
|
|
219
221
|
|
|
220
|
-
# verify all
|
|
221
|
-
rg '"version":' package.json packages/*/package.json apps/*/package.json tools/*/package.json
|
|
222
|
+
# verify all files agree:
|
|
223
|
+
rg '"version":' package.json packages/*/package.json apps/*/package.json tools/*/package.json scripts/package.json
|
|
222
224
|
```
|
|
223
225
|
|
|
224
226
|
The lockfile (`pnpm-lock.yaml`) is unaffected — none of the workspace
|
|
@@ -270,7 +272,7 @@ git pull --ff-only origin main
|
|
|
270
272
|
git log -1 --oneline # expect: chore(release): v0.X.Y
|
|
271
273
|
|
|
272
274
|
# Confirm versions are at the new release.
|
|
273
|
-
rg '"version":' package.json packages/*/package.json apps/*/package.json tools/*/package.json
|
|
275
|
+
rg '"version":' package.json packages/*/package.json apps/*/package.json tools/*/package.json scripts/package.json
|
|
274
276
|
```
|
|
275
277
|
|
|
276
278
|
### 8.1 Tag and push
|
|
@@ -299,8 +301,10 @@ configured (`git config tag.gpgSign true`).
|
|
|
299
301
|
|
|
300
302
|
### 8.3 Production deploy (manual)
|
|
301
303
|
|
|
302
|
-
There is
|
|
303
|
-
prod
|
|
304
|
+
There is a single deploy target per platform (`pnpm deploy:cf`); the
|
|
305
|
+
staging/prod split was collapsed, so the same command ships to whichever
|
|
306
|
+
Cloudflare account the credentials target. The maintainer runs the
|
|
307
|
+
deploy by hand, from a clean checkout on the tag:
|
|
304
308
|
|
|
305
309
|
```bash
|
|
306
310
|
git checkout v0.X.Y
|
|
@@ -313,17 +317,17 @@ pnpm build
|
|
|
313
317
|
pnpm typecheck
|
|
314
318
|
pnpm test
|
|
315
319
|
|
|
316
|
-
# Deploy the Cloudflare Worker
|
|
317
|
-
pnpm deploy:cf
|
|
318
|
-
# → wrangler deploy
|
|
320
|
+
# Deploy the Cloudflare Worker.
|
|
321
|
+
pnpm deploy:cf
|
|
322
|
+
# → wrangler deploy
|
|
319
323
|
|
|
320
|
-
# Tail
|
|
324
|
+
# Tail logs while the first connections arrive.
|
|
321
325
|
npx wrangler tail
|
|
322
326
|
|
|
323
|
-
# Smoke-check
|
|
324
|
-
# You must point it at the
|
|
327
|
+
# Smoke-check the deploy with the same script CI uses.
|
|
328
|
+
# You must point it at the wss:// URL:
|
|
325
329
|
node apps/cf-worker/scripts/smoke.mjs \
|
|
326
|
-
--url wss://<
|
|
330
|
+
--url wss://<workers-subdomain>.workers.dev
|
|
327
331
|
```
|
|
328
332
|
|
|
329
333
|
If smoke fails on prod:
|
|
@@ -355,7 +359,7 @@ To redeploy a previous release:
|
|
|
355
359
|
git checkout v0.W.Z # the previous tag
|
|
356
360
|
pnpm install --frozen-lockfile
|
|
357
361
|
pnpm build
|
|
358
|
-
pnpm deploy:cf
|
|
362
|
+
pnpm deploy:cf
|
|
359
363
|
```
|
|
360
364
|
|
|
361
365
|
This works as long as the previous release's code is compatible with
|
|
@@ -430,7 +434,7 @@ pnpm lint && pnpm typecheck && pnpm test && pnpm build
|
|
|
430
434
|
|
|
431
435
|
# 2. Branch, bump versions + CHANGELOG, open PR (§6, §7).
|
|
432
436
|
git checkout -b release/v0.X.Y
|
|
433
|
-
# ... edit
|
|
437
|
+
# ... edit 16 × package.json + CHANGELOG.md ...
|
|
434
438
|
git commit -am "chore(release): v0.X.Y"
|
|
435
439
|
git push -u origin release/v0.X.Y
|
|
436
440
|
# ... open PR, get it merged ...
|
|
@@ -445,7 +449,7 @@ git push origin v0.X.Y
|
|
|
445
449
|
# 5. Prod deploy (§8.3).
|
|
446
450
|
git checkout v0.X.Y
|
|
447
451
|
pnpm install --frozen-lockfile && pnpm build
|
|
448
|
-
pnpm deploy:cf
|
|
452
|
+
pnpm deploy:cf
|
|
449
453
|
node apps/cf-worker/scripts/smoke.mjs --url wss://<prod>.workers.dev
|
|
450
454
|
```
|
|
451
455
|
|