serverless-ircd 0.1.0 → 0.3.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 +96 -2
- package/.github/workflows/deploy-aws.yml +129 -0
- package/.github/workflows/deploy-cf.yml +0 -2
- package/.gitmodules +3 -0
- package/CHANGELOG.md +436 -0
- package/README.md +127 -84
- package/apps/aws-stack/README.md +73 -0
- package/apps/aws-stack/bin/aws.ts +49 -0
- package/apps/aws-stack/cdk.json +10 -0
- package/apps/aws-stack/package.json +41 -0
- package/apps/aws-stack/scripts/smoke-helpers.d.mts +22 -0
- package/apps/aws-stack/scripts/smoke-helpers.mjs +89 -0
- package/apps/aws-stack/scripts/smoke.mjs +142 -0
- package/apps/aws-stack/src/aws-stack.ts +263 -0
- package/apps/aws-stack/src/tables.ts +10 -0
- package/apps/aws-stack/tests/localstack.test.ts +46 -0
- package/apps/aws-stack/tests/smoke-helpers.test.ts +98 -0
- package/apps/aws-stack/tests/stack.test.ts +464 -0
- package/apps/aws-stack/tsconfig.build.json +11 -0
- package/apps/aws-stack/tsconfig.test.json +8 -0
- package/apps/aws-stack/vitest.config.ts +20 -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 +5 -1
- package/apps/cf-worker/wrangler.toml +66 -17
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +57 -0
- package/apps/local-cli/src/main.ts +1 -1
- package/apps/local-cli/src/server.ts +267 -32
- package/apps/local-cli/tests/config-loader.test.ts +107 -0
- package/apps/local-cli/tests/e2e.test.ts +126 -0
- package/apps/local-cli/tests/security-e2e.test.ts +239 -0
- package/biome.json +3 -1
- package/docs/ADR-001-pure-reducers-and-effect-system.md +74 -0
- package/docs/ADR-002-location-of-authority.md +82 -0
- package/docs/ADR-003-durable-object-sharding.md +93 -0
- package/docs/ADR-004-dynamodb-schema.md +96 -0
- package/docs/ADR-005-wss-only-transport-v1.md +83 -0
- package/docs/ADR-006-sasl-mechanism-scope.md +86 -0
- package/docs/ADR-007-deterministic-ports.md +82 -0
- package/docs/ADR-008-monorepo-tooling.md +60 -0
- package/docs/AWS-Adapter-Architecture.md +496 -0
- package/docs/AWS-Deployment.md +1186 -0
- package/docs/Cloudflare-Deployment-Guide.md +660 -0
- package/docs/Home.md +11 -0
- package/docs/Observability.md +87 -0
- package/docs/PlanIRCv3Websocket.md +489 -0
- package/docs/PlanWebClient.md +451 -0
- package/docs/Release-Process.md +443 -0
- package/package.json +19 -14
- package/packages/aws-adapter/README.md +35 -0
- package/packages/aws-adapter/package.json +52 -0
- package/packages/aws-adapter/src/account-store.ts +121 -0
- package/packages/aws-adapter/src/aws-runtime.ts +871 -0
- package/packages/aws-adapter/src/cdk-table-defs.ts +73 -0
- package/packages/aws-adapter/src/config-loader.ts +126 -0
- package/packages/aws-adapter/src/dynamo-account-store.ts +156 -0
- package/packages/aws-adapter/src/dynamo.ts +61 -0
- package/packages/aws-adapter/src/handlers/connect.ts +44 -0
- package/packages/aws-adapter/src/handlers/default.ts +330 -0
- package/packages/aws-adapter/src/handlers/disconnect.ts +48 -0
- package/packages/aws-adapter/src/handlers/index.ts +293 -0
- package/packages/aws-adapter/src/handlers/ping-checker.ts +217 -0
- package/packages/aws-adapter/src/handlers/state.ts +13 -0
- package/packages/aws-adapter/src/handlers/sweeper.ts +84 -0
- package/packages/aws-adapter/src/index.ts +74 -0
- package/packages/aws-adapter/src/message-store.ts +34 -0
- package/packages/aws-adapter/src/serialize.ts +283 -0
- package/packages/aws-adapter/src/tables.ts +53 -0
- package/packages/aws-adapter/tests/account-store-dynamo.test.ts +171 -0
- package/packages/aws-adapter/tests/account-store.test.ts +280 -0
- package/packages/aws-adapter/tests/aws-harness.ts +408 -0
- package/packages/aws-adapter/tests/aws-integration.test.ts +17 -0
- package/packages/aws-adapter/tests/aws-runtime.test.ts +654 -0
- package/packages/aws-adapter/tests/config-loader.test.ts +213 -0
- package/packages/aws-adapter/tests/disconnect-fanout.test.ts +336 -0
- package/packages/aws-adapter/tests/dynamo.test.ts +57 -0
- package/packages/aws-adapter/tests/global-setup.ts +301 -0
- package/packages/aws-adapter/tests/gone-exception.test.ts +271 -0
- package/packages/aws-adapter/tests/handlers.test.ts +473 -0
- package/packages/aws-adapter/tests/message-store.test.ts +55 -0
- package/packages/aws-adapter/tests/ping-checker.test.ts +571 -0
- package/packages/aws-adapter/tests/serialize.test.ts +249 -0
- package/packages/aws-adapter/tests/smoke.test.ts +48 -0
- package/packages/aws-adapter/tests/sweeper.test.ts +420 -0
- package/packages/aws-adapter/tests/tables.test.ts +74 -0
- package/packages/aws-adapter/tests/transactions.test.ts +446 -0
- package/packages/aws-adapter/tsconfig.build.json +16 -0
- package/packages/aws-adapter/tsconfig.test.json +14 -0
- package/packages/aws-adapter/vitest.config.ts +23 -0
- package/packages/cf-adapter/package.json +2 -1
- package/packages/cf-adapter/src/cf-runtime.ts +42 -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 +135 -0
- package/packages/cf-adapter/src/connection-do.ts +119 -0
- package/packages/cf-adapter/src/env.ts +27 -1
- package/packages/cf-adapter/src/index.ts +2 -1
- package/packages/cf-adapter/tests/cf-harness.ts +2 -2
- package/packages/cf-adapter/tests/cf-integration.test.ts +2 -2
- package/packages/cf-adapter/tests/cf-runtime.test.ts +91 -0
- package/packages/cf-adapter/tests/config-loader.test.ts +149 -0
- package/packages/cf-adapter/tests/connection-do.test.ts +82 -0
- package/packages/cf-adapter/tests/worker/main.ts +2 -1
- package/packages/cf-adapter/tests/worker/stubs/channel-stub.ts +7 -1
- package/packages/cf-adapter/wrangler.test.toml +10 -1
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/in-memory-runtime/src/in-memory-runtime.ts +107 -16
- package/packages/in-memory-runtime/src/index.ts +1 -1
- package/packages/in-memory-runtime/tests/in-memory-runtime.test.ts +134 -0
- package/packages/irc-core/package.json +13 -2
- package/packages/irc-core/src/admission.ts +216 -0
- package/packages/irc-core/src/caps/capabilities.ts +1 -0
- package/packages/irc-core/src/case-fold.ts +64 -0
- package/packages/irc-core/src/cloak.ts +81 -0
- package/packages/irc-core/src/commands/cap.ts +1 -2
- package/packages/irc-core/src/commands/chathistory.ts +305 -0
- package/packages/irc-core/src/commands/index.ts +9 -0
- package/packages/irc-core/src/commands/invite.ts +6 -9
- package/packages/irc-core/src/commands/isupport.ts +1 -1
- package/packages/irc-core/src/commands/join.ts +63 -10
- 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/privmsg.ts +24 -0
- package/packages/irc-core/src/commands/registration.ts +79 -21
- package/packages/irc-core/src/commands/sasl.ts +251 -0
- package/packages/irc-core/src/commands/tagmsg.ts +205 -0
- package/packages/irc-core/src/config.ts +270 -0
- package/packages/irc-core/src/flood-control.ts +175 -0
- package/packages/irc-core/src/index.ts +7 -0
- package/packages/irc-core/src/ports.ts +719 -0
- package/packages/irc-core/src/protocol/base64.ts +16 -0
- package/packages/irc-core/src/protocol/index.ts +2 -1
- package/packages/irc-core/src/protocol/numerics.ts +11 -0
- package/packages/irc-core/src/protocol/parser.ts +27 -2
- package/packages/irc-core/src/state/connection.ts +41 -0
- package/packages/irc-core/src/types.ts +88 -2
- package/packages/irc-core/stryker.commands.conf.json +41 -0
- package/packages/irc-core/stryker.protocol.conf.json +26 -0
- package/packages/irc-core/tests/account-store.test.ts +88 -0
- package/packages/irc-core/tests/admission.test.ts +229 -0
- package/packages/irc-core/tests/caps/capabilities.test.ts +22 -0
- package/packages/irc-core/tests/case-fold.test.ts +80 -0
- package/packages/irc-core/tests/cloak.test.ts +78 -0
- package/packages/irc-core/tests/commands/chathistory.test.ts +996 -0
- package/packages/irc-core/tests/commands/join.test.ts +246 -2
- 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/privmsg.test.ts +146 -2
- package/packages/irc-core/tests/commands/registration.test.ts +380 -20
- package/packages/irc-core/tests/commands/sasl.test.ts +536 -0
- package/packages/irc-core/tests/commands/tagmsg.test.ts +688 -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/config.test.ts +721 -0
- package/packages/irc-core/tests/flood-control.test.ts +394 -0
- package/packages/irc-core/tests/message-store.test.ts +530 -0
- package/packages/irc-core/tests/parser.test.ts +44 -1
- package/packages/irc-core/tests/ports.test.ts +263 -0
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +186 -44
- package/packages/irc-server/src/dispatch.ts +89 -5
- package/packages/irc-server/src/index.ts +2 -0
- package/packages/irc-server/src/routing.ts +160 -0
- package/packages/irc-server/tests/actor.test.ts +690 -15
- package/packages/irc-server/tests/dispatch.test.ts +84 -0
- package/packages/irc-server/tests/routing.test.ts +204 -0
- package/packages/irc-test-support/README.md +32 -0
- package/packages/irc-test-support/package.json +37 -0
- package/packages/{cf-adapter/tests/integration → irc-test-support/src}/in-memory-harness.ts +6 -5
- package/packages/irc-test-support/src/index.ts +24 -0
- package/packages/{cf-adapter/tests/integration/harness.test.ts → irc-test-support/tests/in-memory-harness.test.ts} +1 -1
- package/packages/{cf-adapter/tests/integration/scenarios.test.ts → irc-test-support/tests/in-memory-scenarios.test.ts} +1 -2
- package/packages/irc-test-support/tests/smoke.test.ts +11 -0
- package/packages/irc-test-support/tsconfig.build.json +16 -0
- package/packages/irc-test-support/tsconfig.test.json +14 -0
- package/packages/irc-test-support/vitest.config.ts +20 -0
- package/pnpm-workspace.yaml +1 -0
- package/tools/ci-hardening/package.json +30 -0
- package/tools/ci-hardening/src/index.ts +6 -0
- package/tools/ci-hardening/src/validate.ts +103 -0
- package/tools/ci-hardening/tests/__no_thresholds__.txt +11 -0
- package/tools/ci-hardening/tests/__partial_thresholds__.txt +16 -0
- package/tools/ci-hardening/tests/__stryker_bad__.json +4 -0
- package/tools/ci-hardening/tests/validate.test.ts +177 -0
- package/tools/ci-hardening/tsconfig.build.json +12 -0
- package/tools/ci-hardening/tsconfig.test.json +10 -0
- package/tools/ci-hardening/vitest.config.ts +21 -0
- package/tools/seed-aws-accounts.ts +80 -0
- package/tools/tcp-ws-forwarder/package.json +1 -1
- package/tools/tcp-ws-forwarder/src/forwarder.ts +34 -23
- package/tools/tcp-ws-forwarder/src/logger.ts +155 -0
- package/tools/tcp-ws-forwarder/src/main.ts +33 -14
- package/tools/tcp-ws-forwarder/tests/forwarder.test.ts +86 -0
- package/tools/tcp-ws-forwarder/tests/logger.test.ts +136 -0
- package/packages/cf-adapter/tests/integration/index.ts +0 -17
- /package/packages/{cf-adapter/tests/integration → irc-test-support/src}/harness.ts +0 -0
- /package/packages/{cf-adapter/tests/integration → irc-test-support/src}/scenarios.ts +0 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to ServerlessIRCd are documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/) with
|
|
7
|
+
the 0.x convention (any release may change anything).
|
|
8
|
+
|
|
9
|
+
For the release process itself — versioning policy, pre-release checklist,
|
|
10
|
+
cutting a tag, rolling back — see [`docs/release.md`](docs/release.md).
|
|
11
|
+
Cross-reference `progress.md` / `tickets.md` for per-ticket detail.
|
|
12
|
+
|
|
13
|
+
## [0.3.0] - 2026-07-25
|
|
14
|
+
|
|
15
|
+
### Added — Operator authentication (`@serverless-ircd/irc-core`)
|
|
16
|
+
|
|
17
|
+
- **`OPER` command reducer**: `OPER <name> <password>` authenticates the
|
|
18
|
+
caller against the deployment's configured operator credentials
|
|
19
|
+
(`OperCred[]`) and, on success, grants the `o` user mode, replying
|
|
20
|
+
`381 RPL_YOUREOPER`. Missing parameters → `461 ERR_NEEDMOREPARAMS`;
|
|
21
|
+
no credentials configured → `491 ERR_NOOPERHOST`; wrong name/password
|
|
22
|
+
→ `464 ERR_PASSWDMISMATCH`. Already an operator is an idempotent
|
|
23
|
+
no-op that re-confirms with `381`. `OPER` is the *only* path to oper
|
|
24
|
+
— user `MODE +o` is hard-rejected with `481 ERR_NOPRIVILEGES` — and
|
|
25
|
+
unlocks the `313 RPL_WHOISOPERATOR` branch of WHOIS and the `*`
|
|
26
|
+
oper flag of WHO.
|
|
27
|
+
|
|
28
|
+
### Added — IRCv3 `TAGMSG` (`@serverless-ircd/irc-core`)
|
|
29
|
+
|
|
30
|
+
- **`TAGMSG` reducer** (message-tags extension): carries tags only, no
|
|
31
|
+
text body. Gated behind the `message-tags` capability (clients that
|
|
32
|
+
did not negotiate it see `421 ERR_UNKNOWNCOMMAND`). Follows NOTICE
|
|
33
|
+
semantics — no numeric error replies on any rejection path — for both
|
|
34
|
+
channel and user targets, and is recorded so `draft/chathistory`
|
|
35
|
+
replay picks up `TAGMSG` lines.
|
|
36
|
+
|
|
37
|
+
### Added — RFC 1459 case-mapping (`@serverless-ircd/irc-core`)
|
|
38
|
+
|
|
39
|
+
- **`case-fold.ts`**: RFC 1459 case-insensitive comparison for nicks
|
|
40
|
+
and channels, advertised via `005 CASEMAPPING=rfc1459`. Replaces the
|
|
41
|
+
prior ASCII-only comparison across nick collision/registry, kick and
|
|
42
|
+
invite target lookup, and membership matching.
|
|
43
|
+
|
|
44
|
+
### Added — Registration MOTD (`@serverless-ircd/irc-core`)
|
|
45
|
+
|
|
46
|
+
- The registration welcome now renders the **real configured MOTD**
|
|
47
|
+
(`motd-lines.ts`), replacing the placeholder banner. The `MOTD`
|
|
48
|
+
command and the welcome flow share one rendering path.
|
|
49
|
+
|
|
50
|
+
### Added — Cloudflare adapter (`@serverless-ircd/cf-adapter`)
|
|
51
|
+
|
|
52
|
+
- **`ChannelRegistryDO`**: a fourth Durable Object holding the channel
|
|
53
|
+
directory (channel name → `ChannelDO` routing), alongside
|
|
54
|
+
`ConnectionDO`, `ChannelDO`, and `RegistryDO`. Wired into the Worker
|
|
55
|
+
exports, bindings, and SQLite migrations.
|
|
56
|
+
- **Cloudflare config loader**: reduces Workers `vars` to the shared
|
|
57
|
+
`ServerConfigSchema`, matching the AWS (`loadServerConfigFromLambdaEnv`)
|
|
58
|
+
and local-CLI loaders.
|
|
59
|
+
- `CfRuntime` completed — the previously stubbed `IrcRuntime` methods
|
|
60
|
+
(`getConnection`, `getChannelConnections`, `listChannels`, and
|
|
61
|
+
cross-connection `disconnect`) are now implemented.
|
|
62
|
+
|
|
63
|
+
### Added — SASL account persistence (`@serverless-ircd/aws-adapter`)
|
|
64
|
+
|
|
65
|
+
- SASL `PLAIN` accounts are now persisted in the DynamoDB `Accounts`
|
|
66
|
+
table with **scrypt** password hashes (never plaintext). The Lambda
|
|
67
|
+
cold-start path scans the table once and loads credentials into the
|
|
68
|
+
synchronous `AccountStore` port.
|
|
69
|
+
- **`tools/seed-aws-accounts.ts`**: provisioning script that scrypt-hashes
|
|
70
|
+
`username:password` pairs (from CLI args or a file) and writes them
|
|
71
|
+
to the `Accounts` table. Re-running with the same username overwrites
|
|
72
|
+
the prior row.
|
|
73
|
+
|
|
74
|
+
### Changed
|
|
75
|
+
|
|
76
|
+
- `@serverless-ircd/in-memory-runtime`: `admitConnection` now returns a
|
|
77
|
+
stable empty `recordId` when admission is disabled, dropping the
|
|
78
|
+
per-call id minting (the id was never consulted).
|
|
79
|
+
- Deploy scripts: root `pnpm deploy:aws:staging` / `deploy:aws:prod`
|
|
80
|
+
now run `pnpm build` before `cdk deploy`, so the esbuild-bundled
|
|
81
|
+
Lambda picks up fresh workspace `dist/` — matching what CI already
|
|
82
|
+
did and fixing local deploys shipping stale code.
|
|
83
|
+
|
|
84
|
+
### Known limitations
|
|
85
|
+
|
|
86
|
+
- SASL `EXTERNAL` (mTLS) and production TLS transport remain pending
|
|
87
|
+
(0.x work).
|
|
88
|
+
- Deployed stacks remain WebSocket-only; stock TCP IRC clients still
|
|
89
|
+
reach them through the local `tcp-ws-forwarder` bridge.
|
|
90
|
+
|
|
91
|
+
### Security
|
|
92
|
+
|
|
93
|
+
- None. No vulnerabilities have been reported or fixed in this release.
|
|
94
|
+
|
|
95
|
+
---
|
|
96
|
+
|
|
97
|
+
## [0.2.0] - 2026-07-23
|
|
98
|
+
|
|
99
|
+
### Added — Authentication & flood control (`@serverless-ircd/irc-core`)
|
|
100
|
+
|
|
101
|
+
- **SASL `AUTHENTICATE`**: pure reducer for the IRCv3 SASL exchange.
|
|
102
|
+
`PLAIN` is fully implemented (decodes the payload, validates against
|
|
103
|
+
the injected synchronous `AccountStore` port, emits `900 RPL_LOGGEDIN`
|
|
104
|
+
+ `903 RPL_SASLSUCCESS` on success, `904 ERR_SASLFAIL` on failure).
|
|
105
|
+
`EXTERNAL` is recognized but stubbed to `904` until mTLS lands; any
|
|
106
|
+
other mechanism gets `908 ERR_SASLMECHS`. Multi-frame `<=400`-byte
|
|
107
|
+
chunked payloads and `AUTHENTICATE *` abort (`906 ERR_SASLABORT`) are
|
|
108
|
+
handled. `CAP sasl` is now negotiated end-to-end (no longer
|
|
109
|
+
advertised-only).
|
|
110
|
+
- **Token-bucket flood control**: `wrapFloodControl` is a pure
|
|
111
|
+
higher-order reducer wrapper enforcing a per-connection token bucket.
|
|
112
|
+
The bucket is refilled from the injected `Clock` (deterministic —
|
|
113
|
+
never `Date.now()`) and, once it crosses the configured negative
|
|
114
|
+
threshold, short-circuits with `Disconnect("Excess Flood")` without
|
|
115
|
+
invoking the inner reducer — matching hybrid/charybdis/Unreal.
|
|
116
|
+
- **Per-command flood cost**: each command carries an integer cost;
|
|
117
|
+
control-plane commands (`PING`/`PONG`/`QUIT`/`CAP`/`AUTHENTICATE`)
|
|
118
|
+
are exempt so registration and keepalive can never be starved.
|
|
119
|
+
|
|
120
|
+
### Added — IRCv3 chat history (`@serverless-ircd/irc-core`)
|
|
121
|
+
|
|
122
|
+
- **`draft/chathistory`**: pure reducer for `CHATHISTORY <sub> <target>
|
|
123
|
+
[<args>...]`. Reads backlog from the injected `MessageStore` port and
|
|
124
|
+
replays matching `PRIVMSG`/`NOTICE`/`TAGMSG` lines wrapped in a single
|
|
125
|
+
`BATCH chathistory <target>` frame, each carrying its original
|
|
126
|
+
`@time=…` (server-time) and `msgid=…` tags. The cap must be
|
|
127
|
+
negotiated before queries are accepted. Backed by a flood-wrapped
|
|
128
|
+
routed-reducer path so chat-history reads cannot bypass the bucket.
|
|
129
|
+
|
|
130
|
+
### Added — Observability & configuration (`@serverless-ircd/irc-core`)
|
|
131
|
+
|
|
132
|
+
- **Structured logger port**: a `Logger` port with `traceId` /
|
|
133
|
+
`connectionId` propagation, injected through `ctx` so reducers and
|
|
134
|
+
the actor layer emit structured JSON logs without ambient state.
|
|
135
|
+
- **Shared `ServerConfigSchema`**: a single Zod-style schema validating
|
|
136
|
+
`serverName`, `networkName`, `motdLines`, `serverPassword`,
|
|
137
|
+
`cloaking`, `maxConnectionsPerIp`, `maxConnectionsPerUser`, and
|
|
138
|
+
`perIpConnectionRate` with the same defaults across every adapter.
|
|
139
|
+
Each adapter ships a thin loader (`loadServerConfigFromLambdaEnv`,
|
|
140
|
+
CF `vars`, local CLI flags) that reduces to the shared schema.
|
|
141
|
+
|
|
142
|
+
### Added — Security hardening (`@serverless-ircd/irc-core`, `@serverless-ircd/in-memory-runtime`, `@serverless-ircd/local-cli`)
|
|
143
|
+
|
|
144
|
+
- **Server password enforcement**: when `serverPassword` is set in
|
|
145
|
+
`ServerConfig`, connections must supply it via `PASS <password>`
|
|
146
|
+
(or authenticate via SASL PLAIN) before registration completes;
|
|
147
|
+
otherwise the server replies `464 ERR_PASSWDMISMATCH` and closes
|
|
148
|
+
the link.
|
|
149
|
+
- **Hostmask cloaking**: `cloakHost(realHost, config, suffix)` replaces
|
|
150
|
+
the client's visible IP with a deterministic 64-bit FNV-1a hash
|
|
151
|
+
encoded as lowercase base32, using a deployment-wide secret. Enabled
|
|
152
|
+
via `cloaking: { enabled, secret, cloakedSuffix }` in `ServerConfig`.
|
|
153
|
+
- **Connection admission gate**: `AdmissionStats` + `decideAdmission`
|
|
154
|
+
enforce three independent limits at connection-accept time, each with
|
|
155
|
+
a distinct rejection reason:
|
|
156
|
+
- Per-IP simultaneous-connection cap (`maxConnectionsPerIp`, default 10).
|
|
157
|
+
- Per-user simultaneous-connection cap (`maxConnectionsPerUser`,
|
|
158
|
+
default 5; falls back to source IP when no SASL identity is present).
|
|
159
|
+
- Per-IP connection-rate limit (sliding window
|
|
160
|
+
`perIpConnectionRate`, default 5 per 60 s).
|
|
161
|
+
- **512-byte input cap**: the IRC line parser rejects any line whose
|
|
162
|
+
post-CRLF-strip length exceeds `MAX_INPUT_BYTES` (512), matching
|
|
163
|
+
RFC 2812 §2.3 and the existing output-side `enforceLineLimit`.
|
|
164
|
+
|
|
165
|
+
### Added — AWS adapter (`@serverless-ircd/aws-adapter`, `@serverless-ircd/aws-stack`)
|
|
166
|
+
|
|
167
|
+
The second serverless substrate lands, mirroring the Cloudflare adapter
|
|
168
|
+
on the same `irc-core` reducers and `IrcRuntime` port. Cloudflare and
|
|
169
|
+
AWS are now both deployable.
|
|
170
|
+
|
|
171
|
+
- **`AwsRuntime`**: `IrcRuntime` implementation backed by DynamoDB +
|
|
172
|
+
`ApiGatewayManagementApi`. Marshal/unmarshal helpers map irc-core
|
|
173
|
+
state shapes to/from DynamoDB rows.
|
|
174
|
+
- **Lambda handlers**: `$connect` / `$disconnect` / `$default` entry
|
|
175
|
+
point dispatched on `event.requestContext.routeKey`, wired through
|
|
176
|
+
`ConnectionActor` + `dispatch` against `AwsRuntime`.
|
|
177
|
+
- **Gone-connection sweeper** (`sweeperHandler`): scheduled Lambda that
|
|
178
|
+
reaps connections whose WebSocket link is dead, using atomic DynamoDB
|
|
179
|
+
membership transactions so roster/part fanout stays consistent.
|
|
180
|
+
- **Idle / PING checker** (`pingCheckerHandler`): scheduled Lambda that
|
|
181
|
+
drives the PING / no-PONG disconnect sweep, the AWS analogue of the
|
|
182
|
+
Cloudflare DO `alarm()`.
|
|
183
|
+
- **CDK v2 stack** (`apps/aws-stack`): API Gateway v2 WebSocket API +
|
|
184
|
+
auto-deploying `prod` stage, one Node 20 `NodejsFunction` for the
|
|
185
|
+
runtime, one for the sweeper, and one for the PING checker, five
|
|
186
|
+
DynamoDB tables (`Connections`, `ChannelMeta`, `ChannelMembers`,
|
|
187
|
+
`Nicks`, `Accounts`), and least-privilege IAM (per-table data-plane
|
|
188
|
+
grants + `execute-api:ManageConnections` scoped to the stage).
|
|
189
|
+
- **Per-environment isolation**: `environmentName` prop isolates
|
|
190
|
+
stacks (`IrcAwsStack-<env>`); configurable server identity
|
|
191
|
+
(name / network / MOTD).
|
|
192
|
+
- **AWS deploy workflow** (`.github/workflows/deploy-aws.yml`):
|
|
193
|
+
serialized `cdk deploy --all` to staging on every push to `main`,
|
|
194
|
+
followed by the smoke e2e (`scripts/smoke.mjs`) against the deployed
|
|
195
|
+
`ConnectUrl`. Supports static keys or OIDC web-identity role
|
|
196
|
+
assumption.
|
|
197
|
+
|
|
198
|
+
### Added — Shared test harness (`@serverless-ircd/irc-test-support`)
|
|
199
|
+
|
|
200
|
+
- New workspace package owning the parametrized IRC scenario suite
|
|
201
|
+
(`runIrcScenarios`, 31 Phase 2 scenarios) and the
|
|
202
|
+
`IrcHarnessFactory` / `IrcHarness` / `ClientHarness` seam each
|
|
203
|
+
adapter implements. Ships the reference `inMemoryHarnessFactory`.
|
|
204
|
+
cf-adapter and aws-adapter integration tests both consume it, so the
|
|
205
|
+
same flows run against in-memory, Cloudflare, and AWS runtimes.
|
|
206
|
+
|
|
207
|
+
### Added — CI / build
|
|
208
|
+
|
|
209
|
+
- **Coverage gate**: vitest per-package `thresholds` now enforce 100%
|
|
210
|
+
line/function/branch/statement on `irc-core` / `irc-server` /
|
|
211
|
+
`in-memory-runtime` and ≥90% on every other package. The gate is a
|
|
212
|
+
dedicated named CI step; a regression blocks the merge.
|
|
213
|
+
- **Mutation testing**: Stryker spot-checks on `irc-core/protocol` and
|
|
214
|
+
`irc-core/commands` (≥80% mutation score gate), running as a parallel
|
|
215
|
+
CI job. Repo-level `pnpm mutation` / `mutation:protocol` /
|
|
216
|
+
`mutation:commands` scripts drive it locally.
|
|
217
|
+
- **`@serverless-ircd/ci-hardening`** tool: validates the coverage-gate
|
|
218
|
+
and mutation-testing configs exist and meet the documented
|
|
219
|
+
thresholds, catching drift (a lowered threshold or dropped Stryker
|
|
220
|
+
config) before it reaches `main`.
|
|
221
|
+
- **CF observability**: Worker `observability.invocation_logs` enabled
|
|
222
|
+
for runtime visibility in the Cloudflare dashboard.
|
|
223
|
+
- **tcp-ws-forwarder logging**: structured logger with configurable
|
|
224
|
+
log levels.
|
|
225
|
+
|
|
226
|
+
### Changed
|
|
227
|
+
|
|
228
|
+
- `CAP LS` now advertises `draft/chathistory`; `sasl` is negotiated
|
|
229
|
+
end-to-end rather than advertised-only.
|
|
230
|
+
- The local CLI now binds a **second RFC-style TCP listener** by default
|
|
231
|
+
(`--tcp-port`, default `<port> + 1`) alongside the WebSocket listener,
|
|
232
|
+
so stock IRC clients can dial in directly without the forwarder.
|
|
233
|
+
Pass `--no-tcp` for WebSocket-only. `--motd-file` loads the MOTD from
|
|
234
|
+
disk.
|
|
235
|
+
|
|
236
|
+
### Known limitations
|
|
237
|
+
|
|
238
|
+
- **No config loader for oper credentials / max clients.** Server name,
|
|
239
|
+
network, MOTD, cloaking secret, and admission limits are configurable,
|
|
240
|
+
but oper credentials and dynamic max-client limits remain hardcoded
|
|
241
|
+
defaults.
|
|
242
|
+
- **No TLS / raw TCP transport in production.** The local CLI exposes a
|
|
243
|
+
TCP listener for development; deployed stacks (CF + AWS) are
|
|
244
|
+
WebSocket text frames only. `irc+tls` is 0.x work.
|
|
245
|
+
- **SASL `EXTERNAL` is reserved.** It is recognized but returns `904`
|
|
246
|
+
until mTLS lands.
|
|
247
|
+
- **`chathistory` storage is adapter-supplied.** The `MessageStore` port
|
|
248
|
+
is implemented per adapter; clients that query backlog against a
|
|
249
|
+
runtime with no store configured will see an empty result.
|
|
250
|
+
|
|
251
|
+
## [0.1.0] - 2026-07-21
|
|
252
|
+
|
|
253
|
+
First tagged release. Establishes the pure-reducer protocol core, the
|
|
254
|
+
runtime port + reference implementation, a runnable local CLI server,
|
|
255
|
+
and a working Cloudflare Workers adapter deployed to staging.
|
|
256
|
+
|
|
257
|
+
This is a **preview release**. The AWS adapter, SASL authentication,
|
|
258
|
+
flood control, server-password enforcement, and a CI coverage gate
|
|
259
|
+
are not yet implemented — see **Known limitations** below. The
|
|
260
|
+
Cloudflare staging deployment is functional; production deploys are
|
|
261
|
+
manual per `docs/release.md` §8.3.
|
|
262
|
+
|
|
263
|
+
### Added — Protocol core (`@serverless-ircd/irc-core`)
|
|
264
|
+
|
|
265
|
+
- Pure reducer architecture: every command handler is
|
|
266
|
+
`(state, msg, ctx) => { state, effects }`. Side effects are values
|
|
267
|
+
(`Effect[]`), interpreted by a separate `dispatch` step.
|
|
268
|
+
- Discriminated `Effect` taxonomy (`Send`, `Broadcast`, `Disconnect`,
|
|
269
|
+
`ReserveNick`, `ChangeNick`, `ReleaseNick`, `ApplyChannelDelta`,
|
|
270
|
+
`Lookup`, …) covering every side effect referenced in PLAN §2.1.
|
|
271
|
+
- Determinism ports `Clock` and `IdFactory` injected into `ctx` so
|
|
272
|
+
reducers never touch real timers or randomness.
|
|
273
|
+
- RFC 1459 / 2812 command set: registration (`NICK` / `USER` / `PASS`),
|
|
274
|
+
`PING` / `PONG`, `QUIT`, `JOIN`, `PART`, `PRIVMSG`, `NOTICE`,
|
|
275
|
+
`TOPIC`, `NAMES`, `MOTD`, `MODE` (channel + user), `KICK`, `INVITE`,
|
|
276
|
+
`WHO`, `WHOIS`, `LIST`.
|
|
277
|
+
- Channel modes: `o v b i k l t n m s p`. User modes: `i`, `o` (local
|
|
278
|
+
only), `w`, `s`.
|
|
279
|
+
- Welcome numeric sequence `001`–`005` with `isupport` (`005`)
|
|
280
|
+
generator emitting `NETWORK=`, `CHANLIMIT=`, `CASEMAPPING=`,
|
|
281
|
+
`SAFELIST`, and the negotiated IRCv3 caps.
|
|
282
|
+
- IRCv3 `CAP` negotiation: full `LS` / `REQ` / `ACK` / `NAK` / `END` /
|
|
283
|
+
`LIST` / `NEW` / `DEL` lifecycle.
|
|
284
|
+
- IRCv3 extensions: `message-tags`, `server-time`, `echo-message`,
|
|
285
|
+
`multi-prefix`, `extended-join`, `chghost`, `away-notify`,
|
|
286
|
+
`invite-notify`, `batch`, and the `safelist` capability +
|
|
287
|
+
`SAFELIST` isupport token.
|
|
288
|
+
|
|
289
|
+
### Added — Orchestration (`@serverless-ircd/irc-server`)
|
|
290
|
+
|
|
291
|
+
- The `IrcRuntime` port: side-effect interface (transport + state IO)
|
|
292
|
+
that every adapter implements.
|
|
293
|
+
- `dispatch(effects, runtime)` interpreter that walks an `Effect[]`
|
|
294
|
+
against a bound runtime, with ordering and dedup verified.
|
|
295
|
+
- `ConnectionActor`: bytes → framed messages → reducer → dispatch.
|
|
296
|
+
Drives the full registration handshake, PING/PONG cadence, and
|
|
297
|
+
command dispatch.
|
|
298
|
+
|
|
299
|
+
### Added — In-memory runtime (`@serverless-ircd/in-memory-runtime`)
|
|
300
|
+
|
|
301
|
+
- Single-process reference implementation of `IrcRuntime` backed by
|
|
302
|
+
in-memory Maps. Used by integration tests and the local CLI server.
|
|
303
|
+
|
|
304
|
+
### Added — Local CLI server (`@serverless-ircd/local-cli`)
|
|
305
|
+
|
|
306
|
+
- Runnable WebSocket IRC server on `ws://127.0.0.1:6667/` (default),
|
|
307
|
+
built on the in-memory runtime. Speaks WebSocket text frames, one
|
|
308
|
+
IRC message per frame. `SIGINT` / `SIGTERM` perform graceful
|
|
309
|
+
shutdown. Manual-test harness and e2e fixture target.
|
|
310
|
+
- CLI flags: `--port`, `--host`, `-h/--help`.
|
|
311
|
+
|
|
312
|
+
### Added — Cloudflare adapter (`@serverless-ircd/cf-adapter`)
|
|
313
|
+
|
|
314
|
+
- `ConnectionDO`: hibernatable WebSocket handler (one DO per
|
|
315
|
+
connection). Owns `ConnectionState`, drives `ConnectionActor`,
|
|
316
|
+
persists state across hibernation, drives the PING / no-PONG
|
|
317
|
+
disconnect sweep via `alarm()`.
|
|
318
|
+
- `RegistryDO`: sharded nick-uniqueness authority. Shard assignment is
|
|
319
|
+
`fnv1a32(lowercased(nick)) mod N`, default fleet size 32. DO
|
|
320
|
+
single-threadedness makes the uniqueness invariant structural.
|
|
321
|
+
- `ChannelDO`: per-lowercased-channel-name DO holding roster, modes,
|
|
322
|
+
topic, and fanout via `ConnectionDO` stubs.
|
|
323
|
+
- `CfRuntime`: implements the `IrcRuntime` port against the three DO
|
|
324
|
+
classes.
|
|
325
|
+
|
|
326
|
+
### Added — Cloudflare Worker (`@serverless-ircd/cf-worker`)
|
|
327
|
+
|
|
328
|
+
- Worker edge entry point: WebSocket upgrade → allocates
|
|
329
|
+
`connectionId = crypto.randomUUID()` → routes to `ConnectionDO`.
|
|
330
|
+
Non-upgrade requests get a human-readable 200 health string.
|
|
331
|
+
- `wrangler.toml` with default (production) + `[env.staging]`
|
|
332
|
+
environments, DO bindings, `[[migrations]]` block.
|
|
333
|
+
- `scripts/smoke.mjs`: replay CONNECT → NICK/USER → JOIN → PRIVMSG →
|
|
334
|
+
QUIT and assert the server emits `001`, `376`, `353`, `366`, then
|
|
335
|
+
closes. Run by CI against staging, runnable locally against
|
|
336
|
+
`wrangler dev`.
|
|
337
|
+
|
|
338
|
+
### Added — TCP ↔ WebSocket forwarder (`@serverless-ircd/tcp-ws-forwarder`)
|
|
339
|
+
|
|
340
|
+
- Local bridge so stock TCP IRC clients (WeeChat, HexChat, irssi) can
|
|
341
|
+
reach the WebSocket-only server. Listens on a TCP port, opens one
|
|
342
|
+
WebSocket per connection, reassembles the TCP byte stream into one
|
|
343
|
+
message per WS frame outbound, normalizes inbound frames back to
|
|
344
|
+
canonical CRLF. Transport-agnostic — ships no
|
|
345
|
+
`@serverless-ircd/*` dependency.
|
|
346
|
+
|
|
347
|
+
### Added — CI / build
|
|
348
|
+
|
|
349
|
+
- GitHub Actions CI (`.github/workflows/ci.yml`): lint, typecheck,
|
|
350
|
+
test with coverage on every push / pull request to `main`.
|
|
351
|
+
- GitHub Actions CF deploy (`.github/workflows/deploy-cf.yml`):
|
|
352
|
+
serialized staging deploy on every push to `main`, followed by the
|
|
353
|
+
smoke e2e against the deployed URL. Failure blocks the merge.
|
|
354
|
+
- Toolchain: pnpm 9 workspaces, turbo, TypeScript (strict +
|
|
355
|
+
`exactOptionalPropertyTypes`), vitest + `@vitest/coverage-v8`,
|
|
356
|
+
fast-check, Biome.
|
|
357
|
+
|
|
358
|
+
### Added — Documentation
|
|
359
|
+
|
|
360
|
+
- `README.md` — architecture, repository layout, toolchain, local
|
|
361
|
+
server + forwarder usage, testing strategy, roadmap, protocol scope.
|
|
362
|
+
- `docs/architecture-aws.md` — AWS adapter design notes (adapter
|
|
363
|
+
itself lands in a later release).
|
|
364
|
+
- `docs/deployment-cf.md` — end-to-end Cloudflare deployment guide
|
|
365
|
+
(prerequisites, account setup, local dev, staging + production
|
|
366
|
+
deploys, config reference, sharding, cost, CI/CD, troubleshooting).
|
|
367
|
+
- `docs/release.md` — this release process runbook.
|
|
368
|
+
|
|
369
|
+
### Changed
|
|
370
|
+
|
|
371
|
+
- None (this is the first tagged release).
|
|
372
|
+
|
|
373
|
+
### ⚠️ Migration required
|
|
374
|
+
|
|
375
|
+
- None. No prior releases to migrate from. The DO `[[migrations]]`
|
|
376
|
+
block in `wrangler.toml` is `tag = "v1"` and will provision cleanly
|
|
377
|
+
on a fresh account.
|
|
378
|
+
|
|
379
|
+
### Known limitations
|
|
380
|
+
|
|
381
|
+
- **No AWS adapter.** Tickets TICKET-039 through TICKET-044 (CDK
|
|
382
|
+
stack, APIGW WebSocket handlers, DynamoDB transactions, EventBridge
|
|
383
|
+
timers, deploy pipeline, deployment doc) are pending. Cloudflare is
|
|
384
|
+
the only deployable platform in 0.1.0.
|
|
385
|
+
- **No SASL.** `CAP sasl` is advertised in the capability list but
|
|
386
|
+
`AUTHENTICATE` is not wired into the dispatcher. Plain-text password
|
|
387
|
+
authentication via `PASS` is also not enforced (TICKET-046).
|
|
388
|
+
- **No flood control.** A token-bucket wrapper (TICKET-028) is
|
|
389
|
+
pending. Misbehaving clients can send unbounded commands.
|
|
390
|
+
- **No server-password enforcement, rate limits, cloaking, or oper
|
|
391
|
+
credentials** (TICKET-046).
|
|
392
|
+
- **No config loader.** Server name, network name, MOTD, channel
|
|
393
|
+
prefixes, max clients, oper creds are hardcoded defaults
|
|
394
|
+
(TICKET-047). The `MOTD.txt` file at the repo root is informational
|
|
395
|
+
only; the live MOTD is compiled into the Worker from
|
|
396
|
+
`[vars].MOTD_LINES`.
|
|
397
|
+
- **No CI coverage gate.** Coverage is computed and uploaded as an
|
|
398
|
+
artifact, but no threshold is enforced (TICKET-048). Inspect
|
|
399
|
+
`packages/*/coverage/` before deploying.
|
|
400
|
+
- **No TLS / raw TCP transport.** v1 is WebSocket text frames only.
|
|
401
|
+
TLS-hardened and `irc+tls` transports are 0.x work (TICKET-052
|
|
402
|
+
through TICKET-058). Stock TCP IRC clients reach the server through
|
|
403
|
+
the local `tcp-ws-forwarder` bridge.
|
|
404
|
+
- **No `chathistory` playback.** TICKET-063 is pending; clients that
|
|
405
|
+
request backlog on JOIN will see an empty batch or no response.
|
|
406
|
+
|
|
407
|
+
### Security
|
|
408
|
+
|
|
409
|
+
- None. No vulnerabilities have been reported or fixed in this
|
|
410
|
+
release. Report security issues per the contributing guide.
|
|
411
|
+
|
|
412
|
+
---
|
|
413
|
+
|
|
414
|
+
<!-- Release template — copy for the next release, fill in, move above this line.
|
|
415
|
+
|
|
416
|
+
## [Unreleased]
|
|
417
|
+
|
|
418
|
+
### Added
|
|
419
|
+
-
|
|
420
|
+
|
|
421
|
+
### Changed
|
|
422
|
+
-
|
|
423
|
+
|
|
424
|
+
### ⚠️ Migration required
|
|
425
|
+
-
|
|
426
|
+
|
|
427
|
+
### Fixed
|
|
428
|
+
-
|
|
429
|
+
|
|
430
|
+
### Known limitations
|
|
431
|
+
-
|
|
432
|
+
|
|
433
|
+
### Security
|
|
434
|
+
-
|
|
435
|
+
|
|
436
|
+
-->
|