serverless-ircd 0.9.0 → 0.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/.github/workflows/deploy-aws.yml +156 -32
  2. package/.github/workflows/deploy-cf-tcp.yml +11 -9
  3. package/.github/workflows/deploy-cf.yml +14 -14
  4. package/CHANGELOG.md +305 -0
  5. package/README.md +134 -41
  6. package/apps/aws-stack/README.md +3 -5
  7. package/apps/aws-stack/bin/aws.ts +82 -9
  8. package/apps/aws-stack/cdk.json +0 -3
  9. package/apps/aws-stack/package.json +3 -4
  10. package/apps/aws-stack/src/aws-stack.ts +177 -52
  11. package/apps/aws-stack/src/static-site.ts +323 -0
  12. package/apps/aws-stack/tests/smoke-helpers.test.ts +1 -1
  13. package/apps/aws-stack/tests/stack.test.ts +267 -92
  14. package/apps/aws-stack/tests/static-site.test.ts +491 -0
  15. package/apps/aws-stack/tests/synth-no-bundle.test.ts +0 -1
  16. package/apps/cf-tcp-container/package.json +2 -3
  17. package/apps/cf-tcp-container/src/container-server.ts +12 -9
  18. package/apps/cf-tcp-container/wrangler.toml +1 -10
  19. package/apps/cf-worker/package.json +3 -4
  20. package/apps/cf-worker/wrangler.toml +12 -71
  21. package/apps/local-cli/package.json +1 -1
  22. package/apps/local-cli/src/server.ts +21 -17
  23. package/apps/web/landing/favicon.ico +0 -0
  24. package/apps/web/landing/index.html +1 -0
  25. package/apps/web/package.json +2 -2
  26. package/apps/web/scripts/build.mjs +66 -4
  27. package/apps/web/src/build-env.ts +125 -4
  28. package/apps/web/src/config-schema.ts +20 -6
  29. package/apps/web/static/{config.staging.json → config.prod-aws.json} +3 -2
  30. package/apps/web/tests/build-env.test.ts +210 -9
  31. package/apps/web/tests/build-smoke.test.ts +2 -2
  32. package/apps/web/tests/config-schema.test.ts +149 -25
  33. package/docs/AWS-Deployment.md +670 -96
  34. package/docs/AWS-TCP-Deployment.md +20 -45
  35. package/docs/Cloudflare-Deployment-Guide.md +87 -113
  36. package/docs/Cloudflare-TCP-Deployment.md +25 -49
  37. package/docs/Release-Process.md +27 -23
  38. package/docs/Services.md +69 -22
  39. package/docs/WebClientGuide.md +35 -26
  40. package/package.json +7 -10
  41. package/packages/aws-adapter/package.json +1 -1
  42. package/packages/aws-adapter/src/cdk-table-defs.ts +6 -11
  43. package/packages/aws-adapter/src/config-loader.ts +19 -2
  44. package/packages/aws-adapter/src/dynamo-services-store.ts +7 -0
  45. package/packages/aws-adapter/src/handlers/connect.ts +26 -0
  46. package/packages/aws-adapter/src/handlers/default.ts +190 -123
  47. package/packages/aws-adapter/src/handlers/index.ts +67 -23
  48. package/packages/aws-adapter/src/handlers/nlb-stream.ts +3 -6
  49. package/packages/aws-adapter/src/index.ts +5 -7
  50. package/packages/aws-adapter/src/origin-allowlist.ts +94 -0
  51. package/packages/aws-adapter/src/serialize.ts +15 -0
  52. package/packages/aws-adapter/src/tables.ts +2 -12
  53. package/packages/aws-adapter/tests/aws-harness.ts +0 -1
  54. package/packages/aws-adapter/tests/config-loader.test.ts +66 -0
  55. package/packages/aws-adapter/tests/connect.test.ts +124 -1
  56. package/packages/aws-adapter/tests/default-occ.test.ts +219 -0
  57. package/packages/aws-adapter/tests/dynamo-services-store-unit.test.ts +11 -0
  58. package/packages/aws-adapter/tests/handlers.test.ts +117 -11
  59. package/packages/aws-adapter/tests/migrate-accounts-to-services.test.ts +164 -0
  60. package/packages/aws-adapter/tests/origin-allowlist.test.ts +110 -0
  61. package/packages/aws-adapter/tests/ping-checker.test.ts +0 -1
  62. package/packages/aws-adapter/tests/stats.test.ts +0 -3
  63. package/packages/aws-adapter/tests/sweeper.test.ts +0 -1
  64. package/packages/aws-adapter/tests/tables.test.ts +1 -8
  65. package/packages/aws-adapter/tests/transactions.test.ts +0 -1
  66. package/packages/cf-adapter/package.json +1 -5
  67. package/packages/cf-adapter/src/cf-runtime.ts +59 -8
  68. package/packages/cf-adapter/src/channel-do.ts +13 -3
  69. package/packages/cf-adapter/src/connection-do.ts +266 -109
  70. package/packages/cf-adapter/src/d1-services-store.ts +63 -26
  71. package/packages/cf-adapter/src/env.ts +11 -10
  72. package/packages/cf-adapter/src/index.ts +0 -6
  73. package/packages/cf-adapter/tests/cf-runtime.test.ts +101 -1
  74. package/packages/cf-adapter/tests/channel-do.test.ts +118 -1
  75. package/packages/cf-adapter/tests/connection-do-coverage.test.ts +460 -0
  76. package/packages/cf-adapter/tests/connection-do-pure.test.ts +95 -54
  77. package/packages/cf-adapter/tests/connection-do-sasl-d1.test.ts +62 -38
  78. package/packages/cf-adapter/tests/d1-services-store.test.ts +53 -2
  79. package/packages/cf-adapter/tests/serialize.test.ts +25 -0
  80. package/packages/in-memory-runtime/package.json +1 -1
  81. package/packages/irc-core/package.json +1 -1
  82. package/packages/irc-core/src/account-migration.ts +140 -0
  83. package/packages/irc-core/src/commands/account-auth.ts +16 -19
  84. package/packages/irc-core/src/commands/memoserv.ts +1 -1
  85. package/packages/irc-core/src/commands/nickserv.ts +122 -11
  86. package/packages/irc-core/src/commands/registration.ts +2 -2
  87. package/packages/irc-core/src/commands/sasl.ts +22 -31
  88. package/packages/irc-core/src/config.ts +35 -9
  89. package/packages/irc-core/src/credential-hashing.ts +11 -54
  90. package/packages/irc-core/src/index.ts +1 -0
  91. package/packages/irc-core/src/ports.ts +159 -179
  92. package/packages/irc-core/src/types.ts +38 -10
  93. package/packages/irc-core/tests/account-migration.test.ts +133 -0
  94. package/packages/irc-core/tests/commands/markread.test.ts +54 -0
  95. package/packages/irc-core/tests/commands/memoserv.test.ts +19 -0
  96. package/packages/irc-core/tests/commands/nickserv.test.ts +237 -1
  97. package/packages/irc-core/tests/commands/oper.test.ts +15 -0
  98. package/packages/irc-core/tests/commands/registration.test.ts +140 -133
  99. package/packages/irc-core/tests/commands/sasl.test.ts +159 -178
  100. package/packages/irc-core/tests/commands/unified-account.test.ts +102 -84
  101. package/packages/irc-core/tests/credential-hashing.test.ts +0 -78
  102. package/packages/irc-core/tests/message-store.test.ts +5 -0
  103. package/packages/irc-core/tests/persistent-services-store.test.ts +71 -12
  104. package/packages/irc-core/tests/ports.test.ts +71 -0
  105. package/packages/irc-core/tests/services-store.test.ts +204 -0
  106. package/packages/irc-core/vitest.config.ts +6 -1
  107. package/packages/irc-server/package.json +1 -1
  108. package/packages/irc-server/src/actor.ts +0 -14
  109. package/packages/irc-server/tests/actor.test.ts +19 -47
  110. package/packages/irc-test-support/package.json +1 -1
  111. package/pnpm-workspace.yaml +1 -0
  112. package/scripts/__tests__/deploy-web-aws.test.ts +491 -0
  113. package/scripts/deploy-web-aws.mjs +290 -0
  114. package/scripts/package.json +23 -0
  115. package/scripts/tsconfig.test.json +12 -0
  116. package/scripts/vitest.config.ts +19 -0
  117. package/tools/ci-hardening/package.json +1 -1
  118. package/tools/ci-hardening/src/index.ts +2 -0
  119. package/tools/ci-hardening/src/validate.ts +57 -0
  120. package/tools/ci-hardening/tests/deploy-aws-oidc.test.ts +96 -0
  121. package/tools/ci-hardening/tests/validate.test.ts +42 -0
  122. package/tools/load-test/package.json +1 -1
  123. package/tools/migrate-accounts-to-services.ts +270 -0
  124. package/tools/package.json +2 -1
  125. package/tools/seed-aws-accounts.ts +35 -10
  126. package/tools/seed-cf-accounts.ts +42 -9
  127. package/tools/tcp-ws-forwarder/package.json +1 -1
  128. package/packages/aws-adapter/src/account-store.ts +0 -121
  129. package/packages/aws-adapter/src/dynamo-account-store.ts +0 -95
  130. package/packages/aws-adapter/tests/account-store-dynamo.test.ts +0 -223
  131. package/packages/aws-adapter/tests/account-store.test.ts +0 -276
  132. package/packages/cf-adapter/src/d1-account-store.ts +0 -198
  133. package/packages/cf-adapter/tests/d1-account-store.test.ts +0 -274
  134. package/packages/irc-core/tests/account-store.test.ts +0 -131
@@ -12,7 +12,7 @@ CDK stack 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 staging instance following only this doc.
15
+ their own TCP+TLS instance following only this doc.
16
16
 
17
17
  Cross-reference: `apps/aws-stack/src/aws-stack.ts` (CDK), the wss path
18
18
  (`AWS-Deployment.md`), TICKET-054 (mTLS), ADR-006 (SASL scope).
@@ -102,7 +102,6 @@ The `IrcAwsStack` construct accepts an optional prop
102
102
 
103
103
  ```ts
104
104
  export interface IrcStackProps extends StackProps {
105
- readonly environmentName?: string;
106
105
  readonly serverName?: string;
107
106
  readonly networkName?: string;
108
107
  readonly motdLines?: string[];
@@ -130,21 +129,20 @@ deploys — the existing pipeline stays green.
130
129
 
131
130
  ---
132
131
 
133
- ## 4. Deploy staging (TCP+TLS)
132
+ ## 4. Deploy (TCP+TLS)
134
133
 
135
134
  ```bash
136
135
  # From the repo root:
137
- pnpm deploy:aws:staging -- \
138
- -c environmentName=staging \
139
- -c tcpTlsDomainName=irc-staging.example.com
136
+ pnpm deploy:aws -- \
137
+ -c tcpTlsDomainName=irc.example.com
140
138
  ```
141
139
 
142
140
  This is the same `cdk deploy --all` the wss path uses
143
- (`apps/aws-stack/package.json` `deploy:staging`), just with the extra
141
+ (`apps/aws-stack/package.json` `deploy`), just with the extra
144
142
  context var. The deploy will:
145
143
 
146
- 1. Create the ACM certificate for `irc-staging.example.com` (DNS
147
- validation via Route 53; if the hosted zone is not in this account,
144
+ 1. Create the ACM certificate for `irc.example.com` (DNS
145
+ validation via Route 53; if the hosted zone is not in this account,
148
146
  validate out-of-band and import the cert ARN — see §9.2).
149
147
  2. Provision `NlbVpc`, the internet-facing NLB, the `TLS` listener on
150
148
  `:6697`, and the `IrcNlbHandler` Lambda target group.
@@ -165,15 +163,15 @@ unchanged** — enabling TCP does not disable wss.
165
163
  The first deploy can take several minutes while ACM validates the
166
164
  certificate. `cdk deploy` waits on the certificate resource; do not
167
165
  interrupt it. If validation times out, confirm the hosted zone for the
168
- domain is in this account and Route 53 can write the `_acme-challenge`
166
+ domain is in this account and Route 53 can write the `_acme-challenge`
169
167
  CNAME.
170
168
 
171
169
  ### 4.2 Point a DNS record at the NLB (recommended)
172
170
 
173
171
  The NLB prints a long `.elb.amazonaws.com` DNS name. For a stable,
174
- owned hostname, create an **alias A record** (Route 53) or a CNAME
175
- pointing `irc-staging.example.com` → the NLB DNS name. Clients then
176
- connect to `irc-staging.example.com:6697`.
172
+ owned hostname, create an **alias A record** (Route 53) or a CNAME
173
+ pointing `irc.example.com` → the NLB DNS name. Clients then
174
+ connect to `irc.example.com:6697`.
177
175
 
178
176
  (You can connect directly to the raw NLB DNS, but the cert is issued for
179
177
  your domain, so TLS SNI/hostname verification wants the owned name.)
@@ -182,28 +180,12 @@ your domain, so TLS SNI/hostname verification wants the owned name.)
182
180
 
183
181
  ```bash
184
182
  printf 'NICK smoke\r\nUSER smoke 0 * :Smoke\r\nQUIT :smoke\r\n' | \
185
- timeout 10 openssl s_client -connect irc-staging.example.com:6697 -quiet | \
183
+ timeout 10 openssl s_client -connect irc.example.com:6697 -quiet | \
186
184
  grep -q '001' && echo "SMOKE PASS" || echo "SMOKE FAIL"
187
185
  ```
188
186
 
189
187
  ---
190
188
 
191
- ## 5. Deploy production
192
-
193
- ```bash
194
- pnpm deploy:aws:prod -- \
195
- -c environmentName=production \
196
- -c tcpTlsDomainName=irc.example.com
197
- ```
198
-
199
- Production deploys are manual. The staging CI workflow
200
- (`deploy-aws.yml`) deploys only the wss path (`-c environmentName=staging`
201
- without `tcpTlsDomainName`); there is **no auto-deploy for the TCP path
202
- yet** — add a `deploy-aws-tcp.yml` workflow (mirroring `deploy-cf-tcp.yml`)
203
- as a follow-up when the TCP path is ready for continuous staging.
204
-
205
- ---
206
-
207
189
  ## 6. Operational model & limits
208
190
 
209
191
  The NLB + Lambda streaming model has hard platform limits the wss path
@@ -240,17 +222,16 @@ TCP-specific ones:
240
222
  | Knob (context var / prop) | Purpose | Default when omitted |
241
223
  |-------------------------------|--------------------------------------------------|----------------------------|
242
224
  | `tcpTlsDomainName` | Domain for the ACM cert + NLB TLS listener. | *(TCP path not provisioned)* |
243
- | `environmentName` | Env prefix for table names + stack id. | `staging` |
244
225
 
245
226
  The NLB handler Lambda inherits the **same** identity/table knobs as the
246
- wss handler (`serverName`, `networkName`, `motd`, `environmentName`),
247
- injected in `aws-stack.ts`:
227
+ wss handler (`serverName`, `networkName`, `motd`), injected in
228
+ `aws-stack.ts`:
248
229
 
249
230
  - `SERVER_NAME`, `NETWORK_NAME`, `MOTD` (read by the shared
250
231
  `config-loader.ts`).
251
232
  - `MANAGEMENT_URL` — the wss stage's callback URL, so cross-connection
252
233
  fanout reaches recipients on either transport.
253
- - `<TABLE>_TABLE` for each of the five tables (prefixed by environment).
234
+ - `<TABLE>_TABLE` for each of the five tables (bare logical ids).
254
235
 
255
236
  The Lambda handler entry is the same file
256
237
  (`packages/aws-adapter/src/handlers/index.ts`); esbuild tree-shakes to
@@ -372,25 +353,19 @@ RFC-compliant clients that need it.
372
353
  ## 11. Quick reference
373
354
 
374
355
  ```bash
375
- # Staging (TCP+TLS added alongside wss)
376
- pnpm deploy:aws:staging -- \
377
- -c environmentName=staging \
378
- -c tcpTlsDomainName=irc-staging.example.com
356
+ # Deploy (TCP+TLS added alongside wss)
357
+ pnpm deploy:aws -- \
358
+ -c tcpTlsDomainName=irc.example.com
379
359
 
380
360
  # Read the TCP endpoint from stack outputs
381
361
  aws cloudformation describe-stacks \
382
- --stack-name IrcAwsStack-staging \
362
+ --stack-name IrcAwsStack \
383
363
  --query 'Stacks[0].Outputs[?OutputKey==`TcpConnectUrl`].OutputValue' \
384
364
  --output text
385
365
 
386
366
  # Smoke over TLS
387
367
  printf 'NICK smoke\r\nUSER smoke 0 * :Smoke\r\nQUIT :smoke\r\n' | \
388
- openssl s_client -connect irc-staging.example.com:6697 -quiet | grep '001'
389
-
390
- # Production (manual)
391
- pnpm deploy:aws:prod -- \
392
- -c environmentName=production \
393
- -c tcpTlsDomainName=irc.example.com
368
+ openssl s_client -connect irc.example.com:6697 -quiet | grep '001'
394
369
 
395
370
  # Tests (synth-time assertions for the NLB path)
396
371
  pnpm --filter @serverless-ircd/aws-stack test
@@ -2,15 +2,15 @@
2
2
 
3
3
  End-to-end guide for deploying ServerlessIRCd to Cloudflare Workers
4
4
  (Phase 3 of `PLAN.md`). Covers prerequisites, first-time account setup,
5
- local development, staging deploy, production deploy, configuration,
6
- secrets, sharding, CI/CD, cost, and troubleshooting.
5
+ local development, deploy, configuration, secrets, sharding, CI/CD,
6
+ cost, and troubleshooting.
7
7
 
8
8
  Cross-reference: `PLAN.md` §6.1 (CF mapping), `apps/cf-worker/wrangler.toml`,
9
9
  `packages/cf-adapter/`, `.github/workflows/deploy-cf.yml`. For the AWS
10
- equivalent, see `docs/deployment-aws.md` (TICKET-044).
10
+ equivalent, see `docs/AWS-Deployment.md`.
11
11
 
12
12
  **Acceptance criterion (TICKET-038):** a new contributor can deploy their
13
- own staging instance following only this doc.
13
+ own instance following only this doc.
14
14
 
15
15
  ---
16
16
 
@@ -52,8 +52,10 @@ connection's `ConnectionDO`. All IRC protocol logic lives in the shared
52
52
  `irc-core` reducers, run by `ConnectionActor` inside the DO. No state is
53
53
  held in the Worker process itself.
54
54
 
55
- The same artifacts serve both staging and production; the only difference
56
- is the `--env` flag and the `[env.staging]` block in `wrangler.toml`.
55
+ There is a single deploy target the staging/prod split was collapsed.
56
+ The same `wrangler.toml` ships to whichever Cloudflare account the
57
+ deploy credentials target; staging vs production isolation is by
58
+ account, not by a `[env.staging]` block.
57
59
 
58
60
  ---
59
61
 
@@ -63,15 +65,15 @@ is the `--env` flag and the `[env.staging]` block in `wrangler.toml`.
63
65
  |-----------------------|--------------------------------------------------------|
64
66
  | Node.js | ≥ 20 (matches CI; `engines.node` in root `package.json`)|
65
67
  | pnpm | 9.x (`packageManager: pnpm@9.15.9` in root `package.json`)|
66
- | Cloudflare account | Free tier is enough for staging. Workers Paid plan |
67
- | | (USD $5/mo) is recommended for production — see §10. |
68
+ | Cloudflare account | Free tier is enough to bring up the Worker. Workers Paid |
69
+ | | plan (USD $5/mo) is recommended for real traffic — see §10.|
68
70
  | `wrangler` CLI | Comes from `apps/cf-worker/devDependencies`; no global install needed. |
69
- | Git checkout | Clean working tree on `main` for production deploys. |
71
+ | Git checkout | Clean working tree on `main` for deploys. |
70
72
 
71
- A **Workers Paid plan** is not strictly required for staging, but
72
- Durable Objects only run on the Paid plan in any meaningful production
73
- scenario. The free tier caps DO requests and disables hibernation
74
- budget headroom. See §10 (Cost).
73
+ A **Workers Paid plan** is not strictly required to bring the Worker
74
+ up, but Durable Objects only run on the Paid plan in any meaningful
75
+ production scenario. The free tier caps DO requests and disables
76
+ hibernation budget headroom. See §10 (Cost).
75
77
 
76
78
  Confirm the local environment:
77
79
 
@@ -136,9 +138,8 @@ export CLOUDFLARE_ACCOUNT_ID=...
136
138
  ```
137
139
 
138
140
  When `CLOUDFLARE_API_TOKEN` is set in the environment, `wrangler` uses
139
- it directly and skips the OAuth store. The deploy commands in
140
- `apps/cf-worker/package.json` (`deploy:staging`, `deploy:prod`) honor
141
- both.
141
+ it directly and skips the OAuth store. The deploy command in
142
+ `apps/cf-worker/package.json` (`deploy`) honors both.
142
143
 
143
144
  ---
144
145
 
@@ -172,7 +173,7 @@ Expected output (success):
172
173
  The smoke script (`scripts/smoke.mjs`) replays
173
174
  CONNECT → NICK/USER → JOIN #smoke → PRIVMSG → QUIT and asserts the
174
175
  server emits `001`, `376`, `353`, `366`, and closes the socket. It is
175
- the exact same script CI runs against the deployed staging URL.
176
+ the exact same script CI runs against the deployed URL.
176
177
 
177
178
  The unit + integration suites live in `packages/cf-adapter/tests/` and
178
179
  `apps/cf-worker/tests/`; both run under `@cloudflare/vitest-pool-workers`
@@ -185,29 +186,29 @@ pnpm --filter @serverless-ircd/cf-worker test
185
186
 
186
187
  ---
187
188
 
188
- ## 5. Deploy staging
189
+ ## 5. Deploy
189
190
 
190
- Staging is the environment CI deploys on every push to `main`; you can
191
- also deploy it manually from a clean checkout.
191
+ The deploy is `workflow_dispatch`-only (a maintainer triggers it by
192
+ hand); you can also deploy manually from a clean checkout.
192
193
 
193
194
  ### 5.1 Deploy from your machine
194
195
 
195
196
  ```bash
196
197
  # From the repo root:
197
- pnpm deploy:cf:staging
198
+ pnpm deploy:cf
198
199
  # Equivalent to:
199
- # pnpm --filter @serverless-ircd/cf-worker deploy:staging
200
- # → wrangler deploy --env staging
200
+ # pnpm --filter @serverless-ircd/cf-worker run deploy
201
+ # → wrangler deploy
201
202
  ```
202
203
 
203
204
  The first deploy of a given account will:
204
205
 
205
- 1. Create the Worker `serverless-ircd-staging`.
206
- 2. Apply the `[[env.staging.migrations]]` block (`tag = "v1"`,
207
- `new_classes = [ConnectionDO, RegistryDO, ChannelDO]`). This is what
208
- provisions the three DO namespaces.
206
+ 1. Create the Worker `serverless-ircd`.
207
+ 2. Apply the `[[migrations]]` block (`tag = "v1"`,
208
+ `new_sqlite_classes = [ConnectionDO, RegistryDO, ChannelDO,
209
+ ChannelRegistryDO]`). This is what provisions the DO namespaces.
209
210
  3. Print the deployed URL, e.g.
210
- `https://serverless-ircd-staging.example.workers.dev`.
211
+ `https://serverless-ircd.example.workers.dev`.
211
212
 
212
213
  If you see `Migration tag has already been applied`, the namespaces
213
214
  already exist; subsequent deploys just update the Worker code.
@@ -216,7 +217,7 @@ already exist; subsequent deploys just update the Worker code.
216
217
 
217
218
  ```bash
218
219
  node apps/cf-worker/scripts/smoke.mjs \
219
- --url wss://serverless-ircd-staging.example.workers.dev
220
+ --url wss://serverless-ircd.example.workers.dev
220
221
  ```
221
222
 
222
223
  The same JSON `level: "info"` line on stdout means the full
@@ -228,7 +229,7 @@ Point any RFC-compliant WebSocket-aware IRC client at the deployed URL.
228
229
  For WeeChat:
229
230
 
230
231
  ```
231
- /server add ircd serverless-ircd-staging.example.workers.dev/443
232
+ /server add ircd serverless-ircd.example.workers.dev/443
232
233
  /set irc.server.ircd.ssl on
233
234
  /connect ircd
234
235
  /join #test
@@ -241,18 +242,14 @@ section.
241
242
 
242
243
  ---
243
244
 
244
- ## 6. Deploy production
245
+ ## 6. Production hardening
245
246
 
246
- Production is the default `wrangler.toml` environment (no `--env`
247
- flag). It deploys the Worker named `serverless-ircd` (no `-staging`
248
- suffix) with its own DO namespaces.
247
+ There is a single deploy target per platform (`pnpm deploy:cf`). The
248
+ staging/prod split was collapsed there is no `[env.staging]` block
249
+ and no `--env` flag. The Worker is always named `serverless-ircd`.
249
250
 
250
- ```bash
251
- # From the repo root:
252
- pnpm deploy:cf:prod
253
- ```
254
-
255
- Before your first production deploy, edit `apps/cf-worker/wrangler.toml`:
251
+ Before your first deploy against a real account, edit
252
+ `apps/cf-worker/wrangler.toml`:
256
253
 
257
254
  1. Set `[vars].SERVER_NAME` to the public hostname clients should see
258
255
  in numerics (`001`, `005`, etc.) — e.g. `irc.your-domain.com`.
@@ -263,29 +260,27 @@ Before your first production deploy, edit `apps/cf-worker/wrangler.toml`:
263
260
  3. Set `[vars].MOTD_LINES` to your message-of-the-day (newline-delimited).
264
261
  4. (Optional) Add a custom-domain Route under `[[routes]]` — see §8.4.
265
262
 
266
- **First-time production migration:** the default `[[migrations]]` block
267
- applies on the first `deploy:cf:prod` and creates the production DO
268
- classes. This is one-way: once a DO namespace has data, you cannot
269
- rename or delete a class without an explicit migration entry (see
270
- §9.2).
263
+ **First-time migration:** the `[[migrations]]` block applies on the
264
+ first deploy and creates the DO classes. This is one-way: once a DO
265
+ namespace has data, you cannot rename or delete a class without an
266
+ explicit migration entry (see §9.2).
271
267
 
272
- There is **no CI auto-deploy to production** — production deploys are
273
- always manual (`workflow_dispatch` only is intentionally not wired; see
274
- `.github/workflows/deploy-cf.yml`). Staging deploys on every push to
275
- `main`.
268
+ Staging vs production isolation is by **Cloudflare account** point a
269
+ separate account's `CLOUDFLARE_API_TOKEN` at the deploy to ship to a
270
+ different Worker namespace.
276
271
 
277
272
  ---
278
273
 
279
274
  ## 7. Configuration reference
280
275
 
281
276
  All deployment knobs live in `apps/cf-worker/wrangler.toml`. The file
282
- is the single source of truth for both environments.
277
+ is the single source of truth for the Worker.
283
278
 
284
279
  ### 7.1 Top-level / `[vars]`
285
280
 
286
281
  | Var | Purpose | Default |
287
282
  |-----------------|----------------------------------------------------|--------------------------------------|
288
- | `name` | Worker name. Staging overrides to `...-staging`. | `serverless-ircd` |
283
+ | `name` | Worker name. | `serverless-ircd` |
289
284
  | `main` | Worker entry. | `src/worker.ts` |
290
285
  | `compatibility_date` | Pins Workers runtime behavior. | `2024-11-01` |
291
286
  | `compatibility_flags` | `nodejs_compat` enables Node-style APIs. | `["nodejs_compat"]` |
@@ -298,23 +293,7 @@ These are read by `ConnectionDO` via `Env` (see
298
293
  `connection-do.ts:337`). They are **not** secrets — they ship in the
299
294
  bundle and are visible in the dashboard.
300
295
 
301
- ### 7.2 `[env.staging]`
302
-
303
- The staging block overrides `name` and `[vars]` only. The DO bindings
304
- and migrations must be redeclared per-environment (wrangler requires
305
- explicit per-env blocks — they do not inherit):
306
-
307
- ```toml
308
- [env.staging]
309
- name = "serverless-ircd-staging"
310
-
311
- [env.staging.vars]
312
- SERVER_NAME = "irc-staging.example.com"
313
- NETWORK_NAME = "ServerlessIRCd (staging)"
314
- MOTD_LINES = "..."
315
- ```
316
-
317
- ### 7.3 Durable Object bindings
296
+ ### 7.2 Durable Object bindings
318
297
 
319
298
  Three DO classes are exported from `src/worker.ts` (re-exported from
320
299
  `@serverless-ircd/cf-adapter`) and bound in `wrangler.toml`:
@@ -328,12 +307,12 @@ Three DO classes are exported from `src/worker.ts` (re-exported from
328
307
  The class names in `wrangler.toml`'s `class_name` field MUST match the
329
308
  re-exports at the top of `apps/cf-worker/src/worker.ts:29`.
330
309
 
331
- ### 7.4 Migrations
310
+ ### 7.3 Migrations
332
311
 
333
312
  ```toml
334
313
  [[migrations]]
335
314
  tag = "v1"
336
- new_classes = ["ConnectionDO", "RegistryDO", "ChannelDO"]
315
+ new_sqlite_classes = ["ConnectionDO", "RegistryDO", "ChannelDO", "ChannelRegistryDO"]
337
316
  ```
338
317
 
339
318
  `tag` is the migration id (string, monotonically tracked by wrangler).
@@ -342,10 +321,10 @@ new_classes = ["ConnectionDO", "RegistryDO", "ChannelDO"]
342
321
  block with `tag = "v2"` (etc.) and a `renamed_classes` or `deleted_classes`
343
322
  entry. See <https://developers.cloudflare.com/durable-objects/reference/durable-objects-migrations/>.
344
323
 
345
- Both the default and staging environments carry their own migration
346
- list. Keep them in sync unless you intentionally diverge.
324
+ There is a single migration list the `[env.staging]` block was
325
+ removed when staging/prod collapsed into one target.
347
326
 
348
- ### 7.5 Custom domain (optional)
327
+ ### 7.4 Custom domain (optional)
349
328
 
350
329
  To serve the Worker on `irc.example.com` instead of the workers.dev
351
330
  subdomain, add a route and bind a Cloudflare-managed DNS record:
@@ -357,9 +336,9 @@ zone_name = "example.com"
357
336
  custom_domain = false # true if you'd rather use a Worker Custom Domain
358
337
  ```
359
338
 
360
- Custom domains are optional for staging and recommended for production
361
- (many IRC clients expect a stable, owned hostname). DNS records are
362
- managed separately in the dashboard.
339
+ Custom domains are optional for a first deploy and recommended for
340
+ real traffic (many IRC clients expect a stable, owned hostname). DNS
341
+ records are managed separately in the dashboard.
363
342
 
364
343
  ---
365
344
 
@@ -372,7 +351,7 @@ disabled (the default). Configure it as a wrangler secret, never a
372
351
  plaintext `[vars]` entry:
373
352
 
374
353
  ```bash
375
- wrangler secret put SERVER_PASSWORD --env staging
354
+ wrangler secret put SERVER_PASSWORD
376
355
  # → prompts for the value; stores it in the Workers secret store,
377
356
  # NOT in wrangler.toml or the repo.
378
357
  ```
@@ -463,8 +442,7 @@ node --import tsx tools/seed-cf-accounts.ts \
463
442
  --accounts alice:change-me bob:also-change-me
464
443
  ```
465
444
 
466
- For staging, add `--env staging` and use the staging database name. The
467
- seed CLI hashes each password locally and executes the `INSERT OR REPLACE`
445
+ The seed CLI hashes each password locally and executes the `INSERT OR REPLACE`
468
446
  via `wrangler d1 execute`.
469
447
 
470
448
  > The `SASL_ACCOUNTS` env var remains the quick-start path (no D1 needed):
@@ -548,9 +526,9 @@ the public list price as of 2024-11; verify current pricing at
548
526
 
549
527
  Practical numbers:
550
528
 
551
- - **Workers Free plan**: sufficient to bring up staging, exercise the
529
+ - **Workers Free plan**: sufficient to bring up the Worker, exercise the
552
530
  smoke e2e, and validate a handful of concurrent connections. Not
553
- suitable for production traffic (request caps, no reserved DO
531
+ suitable for sustained traffic (request caps, no reserved DO
554
532
  compute).
555
533
  - **Workers Paid plan** (USD $5/mo base + usage): enough headroom for a
556
534
  small production network. The dominant cost driver at scale is
@@ -572,10 +550,10 @@ mitigations (batched fanout, per-channel send-list cache).
572
550
 
573
551
  ## 11. CI/CD
574
552
 
575
- The GitHub Actions workflow at `.github/workflows/deploy-cf.yml`
576
- deploys staging on every push to `main` and replays the smoke e2e. It
577
- is the canonical deploy path manual `pnpm deploy:cf:staging` is for
578
- iteration only.
553
+ The GitHub Actions workflow at `.github/workflows/deploy-cf.yml` is
554
+ `workflow_dispatch`-only (a maintainer triggers it by hand); there is
555
+ no automatic push-to-main deploy. It deploys the single Worker target
556
+ and replays the smoke e2e.
579
557
 
580
558
  Steps performed by the workflow (in order):
581
559
 
@@ -584,13 +562,13 @@ Steps performed by the workflow (in order):
584
562
  3. `pnpm install --frozen-lockfile`.
585
563
  4. `pnpm build` — builds all workspace packages.
586
564
  5. `pnpm typecheck` and `pnpm test` — gate the deploy.
587
- 6. `pnpm deploy:cf:staging` — `wrangler deploy --env staging`.
565
+ 6. `pnpm deploy:cf` — `wrangler deploy`.
588
566
  7. Resolve the smoke URL from the `CF_SMOKE_URL` repo variable. If
589
567
  unset, the smoke step is skipped with a warning.
590
568
  8. Run `node scripts/smoke.mjs --url "$SMOKE_URL"`. Failure fails the
591
569
  build.
592
570
 
593
- Concurrency is serialized via `concurrency.group: cf-staging` so two
571
+ Concurrency is serialized via `concurrency.group: cf-deploy` so two
594
572
  deploys cannot race the same DO namespace.
595
573
 
596
574
  Required repo configuration (under Settings → Secrets and variables →
@@ -599,12 +577,12 @@ Actions):
599
577
  - **Secret `CLOUDFLARE_API_TOKEN`** — required.
600
578
  - **Secret `CLOUDFLARE_ACCOUNT_ID`** — required if the token's account
601
579
  context is ambiguous.
602
- - **Variable `CF_SMOKE_URL`** — set to the staging Worker's `wss://`
580
+ - **Variable `CF_SMOKE_URL`** — set to the Worker's `wss://`
603
581
  URL. Without it, the smoke step silently no-ops.
604
582
 
605
- There is **no auto-deploy to production**. Production deploys are
606
- intentionally manual (`pnpm deploy:cf:prod` from a clean checkout on
607
- `main`).
583
+ There is a single deploy target the staging/prod split was collapsed,
584
+ so the same workflow + account ships to whichever Worker namespace the
585
+ credentials target.
608
586
 
609
587
  ---
610
588
 
@@ -619,10 +597,10 @@ the `[[migrations]]` block, wrangler refuses to proceed.
619
597
  - If you intentionally changed the DO schema, append a new
620
598
  `[[migrations]]` block with `tag = "v2"` (and `renamed_classes` /
621
599
  `deleted_classes` as needed). Never edit an already-applied `tag`.
622
- - If the staging namespace has stale state from an aborted experiment,
600
+ - If the namespace has stale state from an aborted experiment,
623
601
  you can destroy and recreate it from the dashboard (Workers & Pages →
624
- Durable Objects → namespaces). **Production namespaces must never be
625
- deleted** — that data is gone permanently.
602
+ Durable Objects → namespaces). **Namespaces carrying real traffic must
603
+ never be deleted** — that data is gone permanently.
626
604
 
627
605
  ### 12.2 `Cannot find module '@serverless-ircd/cf-adapter'`
628
606
 
@@ -636,10 +614,10 @@ The connection upgrade succeeded but registration didn't complete.
636
614
  Common causes:
637
615
 
638
616
  - The deployed Worker is reachable but the DO threw on the first frame.
639
- Tail the logs: `wrangler tail --env staging` and reproduce.
617
+ Tail the logs: `wrangler tail` and reproduce.
640
618
  - The `MOTD_LINES` or `SERVER_NAME` var was deleted — `ConnectionDO`'s
641
619
  `serverConfig()` falls back to defaults, but a malformed value can
642
- break parsing. Check the `[env.staging.vars]` block.
620
+ break parsing. Check the `[vars]` block.
643
621
  - Client connected over `ws://` to a Worker that requires `wss://`.
644
622
  Use the `wss://` URL the deploy step printed.
645
623
 
@@ -649,8 +627,8 @@ The DO alarm fired but the handler threw. The most likely cause is a
649
627
  stale persisted-state version after a code change; the serializer in
650
628
  `packages/cf-adapter/src/serialize.ts` carries `PERSISTED_STATE_VERSION`
651
629
  and will reject incompatible blobs. If this happens after an upgrade,
652
- ship a `deserialize` migration that upgrades the old version (or, on
653
- staging, clear the DO storage from the dashboard).
630
+ ship a `deserialize` migration that upgrades the old version (or, on a
631
+ throwaway deployment, clear the DO storage from the dashboard).
654
632
 
655
633
  ### 12.5 Nick reservation always fails
656
634
 
@@ -658,9 +636,9 @@ A client's `NICK` command always returns `433 ERR_NICKNAMEINUSE`, even
658
636
  for nicknames that should be free. This happens when two deployments
659
637
  share a `RegistryDO` namespace but were supposed to be separate —
660
638
  e.g., staging and production point at the same DO class name. Verify
661
- that each environment has its own DO namespace (the Worker name
662
- differs: `serverless-ircd` vs `serverless-ircd-staging`, and wrangler
663
- allocates a distinct namespace per Worker).
639
+ that each deployment has its own DO namespace (a distinct Worker name
640
+ per Cloudflare account means wrangler allocates a distinct namespace
641
+ per Worker).
664
642
 
665
643
  ### 12.6 High request cost / DO request spike
666
644
 
@@ -687,9 +665,9 @@ appears lost, either:
687
665
  - The persisted-state version changed without a deserialize migration
688
666
  (see 12.4).
689
667
 
690
- For staging, the fastest recovery is usually to clear DO storage and
691
- re-test. For production, treat any migration tag change as a release
692
- blocker requiring a runbook entry.
668
+ For a throwaway deployment, the fastest recovery is usually to clear DO
669
+ storage and re-test. For real traffic, treat any migration tag change
670
+ as a release blocker requiring a runbook entry.
693
671
 
694
672
  ---
695
673
 
@@ -705,15 +683,11 @@ pnpm build
705
683
  pnpm --filter @serverless-ircd/cf-worker dev # http://localhost:8787
706
684
  node apps/cf-worker/scripts/smoke.mjs # smoke against localhost
707
685
 
708
- # Staging
709
- pnpm deploy:cf:staging
686
+ # Deploy (single target — staging vs prod is which account you point at)
687
+ pnpm deploy:cf
710
688
  node apps/cf-worker/scripts/smoke.mjs \
711
- --url wss://serverless-ircd-staging.<subdomain>.workers.dev
712
- npx wrangler tail --env staging # live logs
713
-
714
- # Production (manual only)
715
- pnpm deploy:cf:prod
716
- npx wrangler tail # default env = production
689
+ --url wss://serverless-ircd.<subdomain>.workers.dev
690
+ npx wrangler tail # live logs
717
691
 
718
692
  # Tests / lint
719
693
  pnpm test # full workspace
@@ -732,4 +706,4 @@ Key files:
732
706
  | `packages/cf-adapter/src/` | `ConnectionDO`, `RegistryDO`, `ChannelDO`, `CfRuntime`, sharding. |
733
707
  | `packages/cf-adapter/src/sharding.ts` | Shard function + `DEFAULT_REGISTRY_SHARDS`. |
734
708
  | `packages/cf-adapter/src/env.ts` | `Env` interface (binding contract). |
735
- | `.github/workflows/deploy-cf.yml` | Staging deploy + smoke e2e CI. |
709
+ | `.github/workflows/deploy-cf.yml` | Deploy + smoke e2e CI (manual dispatch). |