serverless-ircd 0.4.0 → 0.6.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 +34 -2
- package/.github/workflows/deploy-aws.yml +1 -2
- package/.github/workflows/deploy-cf-tcp.yml +1 -5
- package/.github/workflows/deploy-cf.yml +1 -2
- package/CHANGELOG.md +435 -0
- package/README.md +206 -27
- package/apps/aws-stack/README.md +37 -3
- package/apps/aws-stack/package.json +1 -1
- package/apps/aws-stack/src/aws-stack.ts +106 -13
- package/apps/aws-stack/tests/coverage-command.test.ts +155 -0
- package/apps/aws-stack/tests/stack.test.ts +49 -3
- package/apps/aws-stack/tests/synth-no-bundle.test.ts +129 -0
- package/apps/aws-stack/tests/vitest.setup.ts +18 -0
- package/apps/aws-stack/vitest.config.ts +3 -0
- package/apps/cf-tcp-container/package.json +1 -1
- package/apps/cf-tcp-container/src/config-loader.ts +6 -7
- package/apps/cf-tcp-container/src/container-server.ts +3 -0
- package/apps/cf-tcp-container/tests/config-loader.test.ts +10 -3
- package/apps/cf-tcp-container/wrangler.toml +1 -1
- package/apps/cf-worker/package.json +2 -1
- package/apps/cf-worker/vitest.config.ts +1 -1
- package/apps/cf-worker/wrangler.toml +5 -3
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +6 -6
- package/apps/local-cli/src/main.ts +20 -0
- package/apps/local-cli/src/server.ts +138 -28
- package/apps/local-cli/tests/e2e.test.ts +113 -29
- package/apps/local-cli/tests/tcp.test.ts +71 -19
- package/apps/local-cli/tests/ws-subprotocol.test.ts +262 -0
- package/biome.json +1 -0
- 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 +110 -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 +96 -0
- package/docs/ADR-007-deterministic-ports.md +82 -0
- package/docs/ADR-008-monorepo-tooling.md +60 -0
- package/docs/ADR-009-dual-transport-wss-and-irc-tls.md +138 -0
- package/docs/ADR-Index.md +15 -0
- package/docs/AWS-Adapter-Architecture.md +496 -0
- package/docs/AWS-Deployment.md +1275 -0
- package/docs/AWS-TCP-Deployment.md +410 -0
- package/docs/Cloudflare-Deployment-Guide.md +719 -0
- package/docs/Cloudflare-TCP-Deployment.md +510 -0
- package/docs/Home.md +15 -0
- package/docs/Observability.md +87 -0
- package/docs/PlanExtensions.md +812 -0
- package/docs/PlanIRCv3Websocket.md +503 -0
- package/docs/PlanWebClient.md +451 -0
- package/docs/Release-Process.md +443 -0
- package/package.json +2 -2
- package/packages/aws-adapter/package.json +1 -1
- package/packages/aws-adapter/src/aws-runtime.ts +69 -0
- package/packages/aws-adapter/src/handlers/connect.ts +36 -5
- package/packages/aws-adapter/src/handlers/default.ts +66 -5
- package/packages/aws-adapter/src/handlers/index.ts +41 -2
- package/packages/aws-adapter/src/handlers/nlb-stream.ts +18 -0
- package/packages/aws-adapter/src/index.ts +2 -0
- package/packages/aws-adapter/src/serialize.ts +40 -2
- package/packages/aws-adapter/src/stats.ts +80 -0
- package/packages/aws-adapter/tests/account-store-dynamo.test.ts +49 -8
- package/packages/aws-adapter/tests/account-store.test.ts +4 -7
- package/packages/aws-adapter/tests/aws-harness.ts +5 -2
- package/packages/aws-adapter/tests/aws-integration.test.ts +8 -4
- package/packages/aws-adapter/tests/aws-runtime.test.ts +140 -0
- package/packages/aws-adapter/tests/config-loader.test.ts +11 -2
- package/packages/aws-adapter/tests/connect.test.ts +100 -4
- package/packages/aws-adapter/tests/disconnect-fanout.test.ts +3 -4
- package/packages/aws-adapter/tests/global-setup.ts +55 -30
- package/packages/aws-adapter/tests/gone-exception.test.ts +4 -9
- package/packages/aws-adapter/tests/handlers.test.ts +238 -4
- package/packages/aws-adapter/tests/message-store.test.ts +6 -5
- package/packages/aws-adapter/tests/nlb-stream.test.ts +62 -8
- package/packages/aws-adapter/tests/stats.test.ts +317 -0
- package/packages/cf-adapter/package.json +6 -1
- package/packages/cf-adapter/src/cf-runtime.ts +66 -1
- package/packages/cf-adapter/src/channel-do.ts +2 -2
- package/packages/cf-adapter/src/connection-do.ts +185 -54
- package/packages/cf-adapter/src/env.ts +25 -6
- package/packages/cf-adapter/src/index.ts +2 -0
- package/packages/cf-adapter/src/registry-do.ts +22 -3
- package/packages/cf-adapter/src/serialize.ts +25 -4
- package/packages/cf-adapter/src/sharding.ts +1 -2
- package/packages/cf-adapter/src/stats.ts +65 -0
- package/packages/cf-adapter/tests/cf-harness.ts +1 -1
- package/packages/cf-adapter/tests/cf-integration.test.ts +4 -4
- package/packages/cf-adapter/tests/cf-runtime.test.ts +307 -2
- package/packages/cf-adapter/tests/channel-do.test.ts +119 -2
- package/packages/cf-adapter/tests/config-loader.test.ts +49 -0
- package/packages/cf-adapter/tests/connection-do-channel-registration.test.ts +2 -2
- package/packages/cf-adapter/tests/connection-do-no-batching-reservation.test.ts +2 -2
- package/packages/cf-adapter/tests/connection-do-pure.test.ts +91 -0
- package/packages/cf-adapter/tests/connection-do-ws-spec-contract.test.ts +289 -0
- package/packages/cf-adapter/tests/connection-do-ws-subprotocol.test.ts +184 -0
- package/packages/cf-adapter/tests/connection-do.test.ts +27 -2
- package/packages/cf-adapter/tests/d1-account-store.test.ts +48 -0
- package/packages/cf-adapter/tests/registry-do.test.ts +108 -4
- package/packages/cf-adapter/tests/serialize.test.ts +308 -0
- package/packages/cf-adapter/tests/sharding.test.ts +1 -1
- package/packages/cf-adapter/tests/stats.test.ts +120 -0
- package/packages/cf-adapter/tests/worker/main.ts +7 -7
- package/packages/cf-adapter/tests/worker/stubs/channel-stub.ts +2 -2
- package/packages/cf-adapter/tests/worker/stubs/registry-stub.ts +8 -2
- package/packages/cf-adapter/vitest.config.ts +1 -1
- package/packages/cf-adapter/wrangler.test.toml +7 -0
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/in-memory-runtime/src/in-memory-runtime.ts +39 -0
- package/packages/in-memory-runtime/tests/in-memory-runtime.test.ts +259 -0
- package/packages/irc-core/package.json +1 -1
- package/packages/irc-core/src/admission.ts +16 -15
- package/packages/irc-core/src/caps/capabilities.ts +38 -4
- package/packages/irc-core/src/caps/index.ts +1 -0
- package/packages/irc-core/src/caps/sts.ts +84 -0
- package/packages/irc-core/src/commands/account-notify.ts +53 -0
- package/packages/irc-core/src/commands/away.ts +9 -3
- package/packages/irc-core/src/commands/cap.ts +23 -5
- package/packages/irc-core/src/commands/chathistory.ts +40 -11
- package/packages/irc-core/src/commands/index.ts +42 -0
- package/packages/irc-core/src/commands/invite.ts +2 -4
- package/packages/irc-core/src/commands/isupport.ts +59 -2
- package/packages/irc-core/src/commands/kick.ts +2 -4
- package/packages/irc-core/src/commands/kill.ts +127 -0
- package/packages/irc-core/src/commands/list.ts +1 -1
- package/packages/irc-core/src/commands/lusers.ts +204 -0
- package/packages/irc-core/src/commands/mode.ts +12 -9
- package/packages/irc-core/src/commands/monitor.ts +327 -0
- package/packages/irc-core/src/commands/multiline.ts +256 -0
- package/packages/irc-core/src/commands/names.ts +3 -5
- package/packages/irc-core/src/commands/part.ts +2 -4
- package/packages/irc-core/src/commands/pre-away.ts +112 -0
- package/packages/irc-core/src/commands/privmsg.ts +19 -12
- package/packages/irc-core/src/commands/read-marker.ts +108 -0
- package/packages/irc-core/src/commands/registration.ts +8 -0
- package/packages/irc-core/src/commands/rehash.ts +119 -0
- package/packages/irc-core/src/commands/sasl.ts +24 -1
- package/packages/irc-core/src/commands/setname.ts +109 -0
- package/packages/irc-core/src/commands/stats.ts +152 -0
- package/packages/irc-core/src/commands/tagmsg.ts +71 -1
- package/packages/irc-core/src/commands/topic.ts +2 -4
- package/packages/irc-core/src/commands/trace.ts +137 -0
- package/packages/irc-core/src/commands/wallops.ts +118 -0
- package/packages/irc-core/src/commands/whois.ts +5 -0
- package/packages/irc-core/src/config.ts +72 -10
- package/packages/irc-core/src/effects.ts +41 -1
- package/packages/irc-core/src/index.ts +2 -0
- package/packages/irc-core/src/ports.ts +568 -0
- package/packages/irc-core/src/protocol/index.ts +14 -0
- package/packages/irc-core/src/protocol/labeled-response.ts +133 -0
- package/packages/irc-core/src/protocol/numerics.ts +57 -11
- package/packages/irc-core/src/protocol/outbound.ts +36 -4
- package/packages/irc-core/src/protocol/standard-replies.ts +277 -0
- package/packages/irc-core/src/state/connection.ts +32 -1
- package/packages/irc-core/src/types.ts +120 -1
- package/packages/irc-core/src/ws-framing.ts +132 -0
- package/packages/irc-core/src/ws-subprotocol.ts +66 -0
- package/packages/irc-core/stryker.commands.conf.json +1 -2
- package/packages/irc-core/tests/admission.test.ts +18 -0
- package/packages/irc-core/tests/away-store.test.ts +73 -0
- package/packages/irc-core/tests/caps/capabilities.test.ts +119 -2
- package/packages/irc-core/tests/caps/sts.test.ts +123 -0
- package/packages/irc-core/tests/commands/account-notify.test.ts +171 -0
- package/packages/irc-core/tests/commands/away.test.ts +85 -2
- package/packages/irc-core/tests/commands/cap.test.ts +165 -0
- package/packages/irc-core/tests/commands/chathistory.test.ts +148 -1
- package/packages/irc-core/tests/commands/echo-message.test.ts +9 -3
- package/packages/irc-core/tests/commands/isupport.test.ts +80 -0
- package/packages/irc-core/tests/commands/kill.test.ts +243 -0
- package/packages/irc-core/tests/commands/lusers.test.ts +368 -0
- package/packages/irc-core/tests/commands/mode.test.ts +129 -0
- package/packages/irc-core/tests/commands/monitor.test.ts +411 -0
- package/packages/irc-core/tests/commands/multiline.test.ts +428 -0
- package/packages/irc-core/tests/commands/pre-away.test.ts +241 -0
- package/packages/irc-core/tests/commands/privmsg.test.ts +154 -3
- package/packages/irc-core/tests/commands/read-marker.test.ts +211 -0
- package/packages/irc-core/tests/commands/registration.test.ts +64 -0
- package/packages/irc-core/tests/commands/rehash.test.ts +171 -0
- package/packages/irc-core/tests/commands/sasl.test.ts +116 -2
- package/packages/irc-core/tests/commands/setname.test.ts +225 -0
- package/packages/irc-core/tests/commands/stats.test.ts +294 -0
- package/packages/irc-core/tests/commands/tagmsg.test.ts +357 -0
- package/packages/irc-core/tests/commands/trace.test.ts +282 -0
- package/packages/irc-core/tests/commands/wallops.test.ts +231 -0
- package/packages/irc-core/tests/commands/whois.test.ts +34 -0
- package/packages/irc-core/tests/config.test.ts +103 -13
- package/packages/irc-core/tests/dropped-s2s-and-obsolete-verbs.test.ts +90 -0
- package/packages/irc-core/tests/effects.test.ts +14 -0
- package/packages/irc-core/tests/labeled-response.test.ts +181 -0
- package/packages/irc-core/tests/message-tags.test.ts +114 -0
- package/packages/irc-core/tests/monitor-store.test.ts +200 -0
- package/packages/irc-core/tests/numerics.test.ts +90 -0
- package/packages/irc-core/tests/outbound.test.ts +51 -0
- package/packages/irc-core/tests/ports.test.ts +22 -0
- package/packages/irc-core/tests/raw-modules.d.ts +11 -0
- package/packages/irc-core/tests/read-marker-store.test.ts +108 -0
- package/packages/irc-core/tests/standard-replies.test.ts +412 -0
- package/packages/irc-core/tests/stats-store.test.ts +222 -0
- package/packages/irc-core/tests/types.test.ts +27 -0
- package/packages/irc-core/tests/ws-framing.test.ts +213 -0
- package/packages/irc-core/tests/ws-subprotocol.test.ts +111 -0
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +699 -19
- package/packages/irc-server/src/dispatch.ts +109 -16
- package/packages/irc-server/src/routing.ts +3 -0
- package/packages/irc-server/src/runtime.ts +31 -0
- package/packages/irc-server/src/transport.ts +10 -7
- package/packages/irc-server/tests/actor.test.ts +2523 -42
- package/packages/irc-server/tests/dispatch.test.ts +300 -2
- package/packages/irc-server/tests/raw-modules.d.ts +11 -0
- package/packages/irc-server/tests/routing.test.ts +1 -0
- package/packages/irc-server/tests/runtime.test.ts +7 -0
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/index.ts +6 -0
- package/packages/irc-test-support/src/scenarios.ts +9 -1
- package/packages/irc-test-support/src/test-config.ts +54 -0
- package/packages/irc-test-support/tests/in-memory-scenarios.test.ts +1 -1
- package/packages/irc-test-support/tests/test-config.test.ts +51 -0
- package/pnpm-workspace.yaml +1 -0
- package/tools/ci-hardening/package.json +1 -1
- package/tools/ci-hardening/tests/ddb-local.test.ts +82 -0
- package/tools/load-test/package.json +33 -0
- package/tools/load-test/src/client.ts +351 -0
- package/tools/load-test/src/config.ts +313 -0
- package/tools/load-test/src/harness.ts +116 -0
- package/tools/load-test/src/main.ts +120 -0
- package/tools/load-test/src/metrics.ts +168 -0
- package/tools/load-test/src/report.ts +106 -0
- package/tools/load-test/tests/client.test.ts +212 -0
- package/tools/load-test/tests/config.test.ts +152 -0
- package/tools/load-test/tests/framing.test.ts +37 -0
- package/tools/load-test/tests/harness.test.ts +165 -0
- package/tools/load-test/tests/metrics.test.ts +174 -0
- package/tools/load-test/tests/report.test.ts +161 -0
- package/tools/load-test/tests/smoke.test.ts +67 -0
- package/tools/load-test/tsconfig.build.json +12 -0
- package/tools/load-test/tsconfig.test.json +10 -0
- package/tools/load-test/vitest.config.ts +29 -0
- package/tools/package.json +6 -1
- package/tools/seed-cf-accounts.ts +4 -1
- package/tools/tcp-ws-forwarder/package.json +1 -1
- package/tools/tcp-ws-forwarder/src/forwarder.ts +57 -9
- package/tools/tcp-ws-forwarder/tests/forwarder.test.ts +34 -1
- package/tools/tcp-ws-forwarder/tests/framing.test.ts +65 -1
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure reducer for the IRC `WALLOPS` command (oper-only global broadcast).
|
|
3
|
+
*
|
|
4
|
+
* PLAN §2.1 location-of-authority: WALLOPS runs in the Connection entity.
|
|
5
|
+
* The reducer is pure — it enforces the oper gate, validates the message,
|
|
6
|
+
* and emits a single {@link BroadcastWallopsEffect}. The bound runtime's
|
|
7
|
+
* `broadcastWallops` method owns the cross-connection enumeration that
|
|
8
|
+
* the gate cannot observe purely (it does not see which other
|
|
9
|
+
* connections carry the `+w` user mode).
|
|
10
|
+
*
|
|
11
|
+
* Wire format (RFC 2812 §4.7):
|
|
12
|
+
* - `WALLOPS :<message>`
|
|
13
|
+
* - `481 ERR_NOPRIVILEGES`:
|
|
14
|
+
* `:<server> 481 <nick> :Permission Denied - You're not an IRC operator`
|
|
15
|
+
* - `412 ERR_NOTEXTTOSEND`:
|
|
16
|
+
* `:<server> 412 <nick> :No text to send`
|
|
17
|
+
*
|
|
18
|
+
* Behaviour:
|
|
19
|
+
* - Non-oper sender → `481` (checked before the message is inspected, so
|
|
20
|
+
* a non-oper cannot probe the command shape).
|
|
21
|
+
* - Empty / missing message → `412 ERR_NOTEXTTOSEND`. We pick `412` over
|
|
22
|
+
* `461 ERR_NEEDMOREPARAMS` to match the message-bearing commands
|
|
23
|
+
* PRIVMSG/NOTICE, which use `412` for "no text to send". WALLOPS is a
|
|
24
|
+
* single-param message command, so the semantically closer numeric wins.
|
|
25
|
+
* - Otherwise emit `BroadcastWallops([:<oper> WALLOPS :<message>], sender)`
|
|
26
|
+
* so the runtime fans the line out to every `+w` connection.
|
|
27
|
+
*
|
|
28
|
+
* Delivery / skip-self: the originator is ALWAYS passed as `except`. This
|
|
29
|
+
* matches NOTICE/PRIVMSG channel-broadcast semantics (the sender never
|
|
30
|
+
* receives their own message back) and avoids a redundant echo to the
|
|
31
|
+
* oper who just typed it. The `+w` user mode is the *recipient* gate,
|
|
32
|
+
* owned by the runtime — the sender's own `+w` is therefore irrelevant
|
|
33
|
+
* under this default. An oper who wants a delivery receipt can query
|
|
34
|
+
* logs. (RFC leaves delivery to `+w` regardless of oper; we deliberately
|
|
35
|
+
* diverge to match the rest of the codebase's broadcast primitives.)
|
|
36
|
+
*
|
|
37
|
+
* The `+w` user mode is accepted by the MODE reducer (`w → 'wallops'`)
|
|
38
|
+
* and advertised in `004 RPL_MYINFO`, but no command consumed it before
|
|
39
|
+
* this one — setting `+w` used to be a silent no-op. This reducer lights
|
|
40
|
+
* up the consumer.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
import { Effect } from '../effects.js';
|
|
44
|
+
import type { RawLine } from '../effects.js';
|
|
45
|
+
import { Numerics } from '../protocol/numerics.js';
|
|
46
|
+
import { hostmaskOf } from '../state/connection.js';
|
|
47
|
+
import type { ConnectionState } from '../state/connection.js';
|
|
48
|
+
import type { Ctx, Reducer } from '../types.js';
|
|
49
|
+
|
|
50
|
+
/** Fixed text emitted for `481 ERR_NOPRIVILEGES` (matches `kill.ts` / `mode.ts`). */
|
|
51
|
+
const NOPRIVILEGES_TRAILING_TEXT = "Permission Denied - You're not an IRC operator";
|
|
52
|
+
|
|
53
|
+
/** Fixed text emitted for `412 ERR_NOTEXTTOSEND` (matches `privmsg.ts`). */
|
|
54
|
+
const NOTEXTTOSEND_TRAILING_TEXT = 'No text to send';
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Renders the `:<source> WALLOPS :<message>` wire line that every `+w`
|
|
58
|
+
* connection receives. `<source>` is the oper's full hostmask
|
|
59
|
+
* (`nick!user@host`), consistent with how PRIVMSG/NOTICE/QUIT attribute
|
|
60
|
+
* user-originated messages. When the hostmask is incomplete (e.g. no
|
|
61
|
+
* `user`/`host` yet, which should not happen for a registered oper but is
|
|
62
|
+
* defended against) the bare nick is used, falling back to `*` only when
|
|
63
|
+
* no nick is set at all.
|
|
64
|
+
*/
|
|
65
|
+
export function wallopsLine(state: ConnectionState, message: string): RawLine {
|
|
66
|
+
const source = hostmaskOf(state) ?? state.nick ?? '*';
|
|
67
|
+
return { text: `:${source} WALLOPS :${message}` };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Handles `WALLOPS :<message>`.
|
|
72
|
+
*
|
|
73
|
+
* Enforces the oper gate, stamps `lastSeen`, and on success emits a single
|
|
74
|
+
* {@link BroadcastWallopsEffect} addressed to every `+w` connection. The
|
|
75
|
+
* sender is always excluded (skip-self). The reducer never performs IO.
|
|
76
|
+
*/
|
|
77
|
+
export const wallopsReducer: Reducer<ConnectionState> = (state, msg, ctx) => {
|
|
78
|
+
state.lastSeen = ctx.clock.now();
|
|
79
|
+
|
|
80
|
+
if (!state.userModes.oper) {
|
|
81
|
+
return {
|
|
82
|
+
state,
|
|
83
|
+
effects: [
|
|
84
|
+
Effect.send(ctx.connId, [
|
|
85
|
+
numericLine(ctx, Numerics.ERR_NOPRIVILEGES, NOPRIVILEGES_TRAILING_TEXT),
|
|
86
|
+
]),
|
|
87
|
+
],
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const message = msg.params[0];
|
|
92
|
+
if (message === undefined || message.length === 0) {
|
|
93
|
+
return {
|
|
94
|
+
state,
|
|
95
|
+
effects: [
|
|
96
|
+
Effect.send(ctx.connId, [
|
|
97
|
+
numericLine(ctx, Numerics.ERR_NOTEXTTOSEND, NOTEXTTOSEND_TRAILING_TEXT),
|
|
98
|
+
]),
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
return {
|
|
104
|
+
state,
|
|
105
|
+
effects: [Effect.broadcastWallops([wallopsLine(state, message)], ctx.connId)],
|
|
106
|
+
};
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Builds a `:<server> <code> <nick> :<trailing>` line. The nick falls back
|
|
111
|
+
* to `*` when the connection has not yet registered one, matching the rest
|
|
112
|
+
* of the codebase's numeric formatting.
|
|
113
|
+
*/
|
|
114
|
+
function numericLine(ctx: Ctx, code: number, trailing: string): RawLine {
|
|
115
|
+
const nick = ctx.connection.nick ?? '*';
|
|
116
|
+
const codeStr = code.toString().padStart(3, '0');
|
|
117
|
+
return { text: [`:${ctx.serverName}`, codeStr, nick, `:${trailing}`].join(' ') };
|
|
118
|
+
}
|
|
@@ -134,6 +134,11 @@ export function whoisReducer(
|
|
|
134
134
|
text: `:${ctx.serverName} ${Numerics.RPL_WHOISOPERATOR.toString().padStart(3, '0')} ${requesterNick} ${tNick} :is an IRC operator`,
|
|
135
135
|
});
|
|
136
136
|
}
|
|
137
|
+
if (target.userModes.tls) {
|
|
138
|
+
lines.push({
|
|
139
|
+
text: `:${ctx.serverName} ${Numerics.RPL_WHOISSECURE.toString().padStart(3, '0')} ${requesterNick} ${tNick} :is using a secure connection`,
|
|
140
|
+
});
|
|
141
|
+
}
|
|
137
142
|
if (target.account !== undefined) {
|
|
138
143
|
lines.push({
|
|
139
144
|
text: `:${ctx.serverName} ${Numerics.RPL_WHOISACCOUNT.toString().padStart(3, '0')} ${requesterNick} ${tNick} ${target.account} :is logged in as`,
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
|
|
19
19
|
import { z } from 'zod';
|
|
20
20
|
import { BUILD_DATE } from './build-info.js';
|
|
21
|
+
import { DEFAULT_MULTILINE_MAX_BYTES } from './caps/capabilities.js';
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Schema for a single IRC operator credential.
|
|
@@ -61,6 +62,33 @@ export const CloakingConfigSchema = z.object({
|
|
|
61
62
|
cloakedSuffix: z.string().min(1).optional(),
|
|
62
63
|
});
|
|
63
64
|
|
|
65
|
+
/**
|
|
66
|
+
* IRCv3 `sts` (Strict Transport Security) policy. When supplied, the
|
|
67
|
+
* server advertises an `sts` capability whose value tells compliant
|
|
68
|
+
* clients how long to pin TLS and which port to upgrade to on a
|
|
69
|
+
* plaintext connection. Omit entirely (the default) to disable STS.
|
|
70
|
+
*
|
|
71
|
+
* Spec: https://ircv3.net/specs/extensions/sts
|
|
72
|
+
*/
|
|
73
|
+
export const StsConfigSchema = z.object({
|
|
74
|
+
/**
|
|
75
|
+
* Pin duration in seconds. Defaults to 30 days at the cap-builder layer
|
|
76
|
+
* when omitted; supply an explicit value in production.
|
|
77
|
+
*/
|
|
78
|
+
duration: z.number().int().positive(),
|
|
79
|
+
/**
|
|
80
|
+
* TLS listener port advertised on plaintext connections. The canonical
|
|
81
|
+
* IRC-over-TLS port is 6697 (RFC 7193). Defaults to 6697 at the
|
|
82
|
+
* cap-builder layer when omitted.
|
|
83
|
+
*/
|
|
84
|
+
port: z.number().int().positive(),
|
|
85
|
+
/**
|
|
86
|
+
* Optional preload directive. When true the policy may be hard-coded
|
|
87
|
+
* into a client preload list (only advertised on plaintext connections).
|
|
88
|
+
*/
|
|
89
|
+
preload: z.boolean().optional(),
|
|
90
|
+
});
|
|
91
|
+
|
|
64
92
|
/**
|
|
65
93
|
* Per-IP connection-rate limit. Caps how many new connections a single
|
|
66
94
|
* source IP may open within a sliding `windowMs` window before the
|
|
@@ -256,6 +284,13 @@ export const ServerConfigSchema = z.object({
|
|
|
256
284
|
channelLen: z.number().int().positive().default(50),
|
|
257
285
|
/** Maximum topic length (advertised as `TOPICLEN=`). */
|
|
258
286
|
topicLen: z.number().int().nonnegative().default(390),
|
|
287
|
+
/**
|
|
288
|
+
* Maximum realname (GECOS) length enforced by `SETNAME`. There is no
|
|
289
|
+
* standard ISUPPORT token for this; registration-time `USER` does not
|
|
290
|
+
* enforce a cap, so the knob is optional on {@link ServerConfig} and the
|
|
291
|
+
* reducer falls back to a built-in default when unset.
|
|
292
|
+
*/
|
|
293
|
+
realnameLen: z.number().int().nonnegative().default(50),
|
|
259
294
|
/** Default QUIT reason when the client disconnects without one. */
|
|
260
295
|
quitMessage: z.string().default('Client Quit'),
|
|
261
296
|
|
|
@@ -265,6 +300,43 @@ export const ServerConfigSchema = z.object({
|
|
|
265
300
|
* lower per deployment. Only consulted when a MessageStore is bound.
|
|
266
301
|
*/
|
|
267
302
|
chatHistoryPlaybackLimit: z.number().int().positive().default(50),
|
|
303
|
+
|
|
304
|
+
/**
|
|
305
|
+
* IRCv3 `sts` (Strict Transport Security) policy. Omit entirely (the
|
|
306
|
+
* default) to disable STS advertisement; supply `{ duration, port }`
|
|
307
|
+
* (plus optional `preload`) to advertise the cap and let compliant
|
|
308
|
+
* clients upgrade from plaintext to TLS and pin the secure listener.
|
|
309
|
+
*/
|
|
310
|
+
sts: StsConfigSchema.optional(),
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* IRCv3 `draft/multiline` byte budget. Advertised as
|
|
314
|
+
* `draft/multiline=<n>` in `CAP LS` and enforced on every finalized
|
|
315
|
+
* multi-line batch. Defaults to the IRCv3-recommended 4096; raise or
|
|
316
|
+
* lower per deployment.
|
|
317
|
+
*/
|
|
318
|
+
multilineMaxBytes: z.number().int().positive().default(DEFAULT_MULTILINE_MAX_BYTES),
|
|
319
|
+
|
|
320
|
+
/**
|
|
321
|
+
* Per-connection MONITOR watchlist ceiling, advertised as `MONITOR=<n>`
|
|
322
|
+
* in `005 RPL_ISUPPORT`. Omit to inherit the monitor reducer's built-in
|
|
323
|
+
* ceiling (DEFAULT_MONITOR_LIMIT, 30).
|
|
324
|
+
*/
|
|
325
|
+
monitorLimit: z.number().int().positive().optional(),
|
|
326
|
+
|
|
327
|
+
/**
|
|
328
|
+
* Extended-ban prefix sigil for `EXTBAN=<prefix>,…` in
|
|
329
|
+
* `005 RPL_ISUPPORT`. Omit for the IRC-standard `~`.
|
|
330
|
+
*/
|
|
331
|
+
extbanPrefix: z.string().min(1).optional(),
|
|
332
|
+
|
|
333
|
+
/**
|
|
334
|
+
* Enables IRC-services-derived ISUPPORT tokens (currently
|
|
335
|
+
* `ACCOUNTEXTBAN=a`). Set to true once a `ServicesStore` is bound; until
|
|
336
|
+
* then account-name extended bans are suppressed because unregistered
|
|
337
|
+
* nicks make them meaningless.
|
|
338
|
+
*/
|
|
339
|
+
servicesEnabled: z.boolean().optional(),
|
|
268
340
|
});
|
|
269
341
|
|
|
270
342
|
/** Input type: what adapters supply (fields with defaults may be omitted). */
|
|
@@ -296,16 +368,6 @@ export function parseServerConfig(input: unknown): ParsedServerConfig {
|
|
|
296
368
|
return result.data;
|
|
297
369
|
}
|
|
298
370
|
|
|
299
|
-
/**
|
|
300
|
-
* Default server config: the result of parsing an empty input. Useful as a
|
|
301
|
-
* known-good baseline for tests and for adapters that want to overlay
|
|
302
|
-
* per-deployment overrides via object spread.
|
|
303
|
-
*/
|
|
304
|
-
export const DEFAULT_SERVER_CONFIG: ParsedServerConfig = ServerConfigSchema.parse({
|
|
305
|
-
serverName: 'irc.example.com',
|
|
306
|
-
networkName: 'ServerlessIRCd',
|
|
307
|
-
});
|
|
308
|
-
|
|
309
371
|
/**
|
|
310
372
|
* Resolves the effective server version for wire output: the configured
|
|
311
373
|
* value when present, otherwise {@link DEFAULT_SERVER_VERSION}. Used by the
|
|
@@ -43,6 +43,18 @@ export interface BroadcastEffect {
|
|
|
43
43
|
except?: ConnId;
|
|
44
44
|
cap?: string;
|
|
45
45
|
capLines?: RawLine[];
|
|
46
|
+
/**
|
|
47
|
+
* OR-semantics cap list: the recipient receives `lines` iff they
|
|
48
|
+
* negotiated ANY of the listed caps. Used by capabilities whose delivery
|
|
49
|
+
* rule is the union of two caps — e.g. `draft/typing` TAGMSGs reach
|
|
50
|
+
* peers that announced `draft/typing` OR `message-tags` (the latter
|
|
51
|
+
* because every TAGMSG goes to message-tags peers regardless of tag).
|
|
52
|
+
*
|
|
53
|
+
* Mutually exclusive with {@link cap}: set one or the other, not both.
|
|
54
|
+
* When neither is set, dispatch takes the runtime's bulk broadcast fast
|
|
55
|
+
* path.
|
|
56
|
+
*/
|
|
57
|
+
caps?: readonly string[];
|
|
46
58
|
}
|
|
47
59
|
|
|
48
60
|
export interface DisconnectEffect {
|
|
@@ -89,6 +101,26 @@ export interface SendToNickEffect {
|
|
|
89
101
|
notFoundLines?: RawLine[];
|
|
90
102
|
}
|
|
91
103
|
|
|
104
|
+
/**
|
|
105
|
+
* Global cross-connection fanout for `WALLOPS`. Unlike
|
|
106
|
+
* {@link BroadcastEffect} (channel-scoped via the roster), this reaches
|
|
107
|
+
* every live connection whose `+w` user mode is set, regardless of shared
|
|
108
|
+
* channel membership. `except` skips a single connection — by convention
|
|
109
|
+
* the originator, so an oper never receives their own wallops back
|
|
110
|
+
* (matching NOTICE/PRIVMSG channel-broadcast semantics).
|
|
111
|
+
*
|
|
112
|
+
* The reducer does not know which connections carry `+w`; the bound
|
|
113
|
+
* runtime's `broadcastWallops` method owns that enumeration. On the
|
|
114
|
+
* in-memory runtime that is a scan of its connection map; on CF it walks
|
|
115
|
+
* the registry shards; on AWS it scans the `Connections` table. Each
|
|
116
|
+
* adapter applies its own cost cap (documented at the method).
|
|
117
|
+
*/
|
|
118
|
+
export interface BroadcastWallopsEffect {
|
|
119
|
+
tag: 'BroadcastWallops';
|
|
120
|
+
lines: RawLine[];
|
|
121
|
+
except?: ConnId;
|
|
122
|
+
}
|
|
123
|
+
|
|
92
124
|
/** Discriminated union of every effect the dispatch interpreter knows. */
|
|
93
125
|
export type Effect =
|
|
94
126
|
| SendEffect
|
|
@@ -98,7 +130,8 @@ export type Effect =
|
|
|
98
130
|
| ChangeNickEffect
|
|
99
131
|
| ReleaseNickEffect
|
|
100
132
|
| ApplyChannelDeltaEffect
|
|
101
|
-
| SendToNickEffect
|
|
133
|
+
| SendToNickEffect
|
|
134
|
+
| BroadcastWallopsEffect;
|
|
102
135
|
|
|
103
136
|
/** Literal string tag of every {@link Effect} variant. */
|
|
104
137
|
export type EffectTag = Effect['tag'];
|
|
@@ -129,11 +162,13 @@ export const Effect = {
|
|
|
129
162
|
except?: ConnId,
|
|
130
163
|
cap?: string,
|
|
131
164
|
capLines?: RawLine[],
|
|
165
|
+
caps?: readonly string[],
|
|
132
166
|
): Effect {
|
|
133
167
|
const e: BroadcastEffect = { tag: 'Broadcast', chan, lines };
|
|
134
168
|
if (except !== undefined) e.except = except;
|
|
135
169
|
if (cap !== undefined) e.cap = cap;
|
|
136
170
|
if (capLines !== undefined) e.capLines = capLines;
|
|
171
|
+
if (caps !== undefined) e.caps = caps;
|
|
137
172
|
return e;
|
|
138
173
|
},
|
|
139
174
|
disconnect(conn: ConnId, reason?: string): Effect {
|
|
@@ -158,4 +193,9 @@ export const Effect = {
|
|
|
158
193
|
if (notFoundLines !== undefined) e.notFoundLines = notFoundLines;
|
|
159
194
|
return e;
|
|
160
195
|
},
|
|
196
|
+
broadcastWallops(lines: RawLine[], except?: ConnId): Effect {
|
|
197
|
+
const e: BroadcastWallopsEffect = { tag: 'BroadcastWallops', lines };
|
|
198
|
+
if (except !== undefined) e.except = except;
|
|
199
|
+
return e;
|
|
200
|
+
},
|
|
161
201
|
} as const;
|
|
@@ -14,6 +14,8 @@ export * from './credential-hashing.js';
|
|
|
14
14
|
export * from './flood-control.js';
|
|
15
15
|
export * from './cloak.js';
|
|
16
16
|
export * from './case-fold.js';
|
|
17
|
+
export * from './ws-subprotocol.js';
|
|
18
|
+
export * from './ws-framing.js';
|
|
17
19
|
export * from './admission.js';
|
|
18
20
|
export * from './config.js';
|
|
19
21
|
export type {
|