serverless-ircd 0.7.0 → 0.9.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 +7 -3
- package/.gitmodules +1 -1
- package/CHANGELOG.md +518 -29
- package/README.md +221 -161
- package/apps/aws-stack/package.json +1 -1
- package/apps/aws-stack/src/aws-stack.ts +186 -18
- package/apps/aws-stack/tests/stack.test.ts +400 -56
- package/apps/cf-tcp-container/package.json +1 -1
- package/apps/cf-tcp-container/src/container-server.ts +21 -1
- package/apps/cf-tcp-container/tests/config-loader.test.ts +43 -0
- package/apps/cf-tcp-container/tests/container-server.test.ts +249 -1
- package/apps/cf-tcp-container/tests/persistence.test.ts +9 -0
- package/apps/cf-tcp-container/tests/tls-e2e.test.ts +24 -5
- package/apps/cf-worker/package.json +1 -1
- package/apps/cf-worker/src/worker.ts +4 -4
- package/apps/cf-worker/tests/fixtures/web-dist/webclient/index.html +18 -0
- package/apps/cf-worker/tests/smoke.test.ts +5 -5
- package/apps/cf-worker/wrangler.test.toml +6 -6
- package/apps/cf-worker/wrangler.toml +7 -5
- package/apps/local-cli/package.json +1 -1
- package/apps/local-cli/src/config-loader.ts +8 -0
- package/apps/local-cli/src/main.ts +16 -0
- package/apps/local-cli/src/server.ts +95 -31
- package/apps/local-cli/tests/config-loader.test.ts +14 -0
- package/apps/local-cli/tests/config-resolution.test.ts +65 -0
- package/apps/local-cli/tests/motd-file-non-error.test.ts +29 -0
- package/apps/local-cli/tests/rehash.test.ts +147 -0
- package/apps/local-cli/tests/server-helpers.test.ts +63 -0
- package/apps/local-cli/tests/tcp.test.ts +89 -0
- package/apps/local-cli/tests/ws-subprotocol.test.ts +92 -0
- package/apps/web/landing/index.html +237 -16
- package/apps/web/package.json +3 -2
- package/apps/web/scripts/build.mjs +46 -16
- package/apps/web/src/build-env.ts +1 -1
- package/apps/web/src/config-schema.ts +6 -6
- package/apps/web/src/render-docs.ts +292 -0
- package/apps/web/tests/build-smoke.test.ts +43 -14
- package/apps/web/tests/config-schema.test.ts +1 -1
- package/apps/web/tests/landing-content.test.ts +103 -0
- package/apps/web/tests/render-docs.test.ts +198 -0
- package/docs/AWS-Adapter-Architecture.md +3 -2
- package/docs/AWS-Deployment.md +21 -8
- package/docs/Cloudflare-Deployment-Guide.md +22 -6
- package/docs/PlanExtensions.md +113 -3
- package/docs/Release-Process.md +23 -13
- package/docs/Services.md +578 -0
- package/docs/WebClientGuide.md +32 -31
- package/package.json +2 -2
- package/packages/aws-adapter/package.json +1 -1
- package/packages/aws-adapter/src/aws-runtime.ts +20 -1
- package/packages/aws-adapter/src/cdk-table-defs.ts +6 -0
- package/packages/aws-adapter/src/config-loader.ts +11 -0
- package/packages/aws-adapter/src/connection-counter.ts +89 -0
- package/packages/aws-adapter/src/dynamo-services-store.ts +649 -0
- package/packages/aws-adapter/src/handlers/connect.ts +55 -51
- package/packages/aws-adapter/src/handlers/default.ts +36 -4
- package/packages/aws-adapter/src/handlers/index.ts +15 -0
- package/packages/aws-adapter/src/handlers/nlb-stream.ts +25 -2
- package/packages/aws-adapter/src/handlers/sweeper.ts +5 -1
- package/packages/aws-adapter/src/index.ts +4 -0
- package/packages/aws-adapter/src/stats.ts +6 -1
- package/packages/aws-adapter/src/tables.ts +34 -4
- package/packages/aws-adapter/tests/aws-harness.ts +3 -0
- package/packages/aws-adapter/tests/aws-runtime.test.ts +23 -1
- package/packages/aws-adapter/tests/config-loader.test.ts +8 -0
- package/packages/aws-adapter/tests/connect.test.ts +158 -32
- package/packages/aws-adapter/tests/connection-counter.test.ts +144 -0
- package/packages/aws-adapter/tests/dynamo-services-store-dynamo.test.ts +183 -0
- package/packages/aws-adapter/tests/dynamo-services-store-unit.test.ts +568 -0
- package/packages/aws-adapter/tests/global-setup.ts +28 -1
- package/packages/aws-adapter/tests/gone-exception.test.ts +21 -2
- package/packages/aws-adapter/tests/handlers.test.ts +105 -3
- package/packages/aws-adapter/tests/nlb-stream.test.ts +29 -1
- package/packages/aws-adapter/tests/sweeper.test.ts +20 -0
- package/packages/aws-adapter/tests/tables.test.ts +6 -1
- package/packages/cf-adapter/package.json +1 -1
- package/packages/cf-adapter/src/config-loader.ts +11 -0
- package/packages/cf-adapter/src/connection-do.ts +130 -8
- package/packages/cf-adapter/src/d1-services-store.ts +703 -0
- package/packages/cf-adapter/src/env.ts +8 -0
- package/packages/cf-adapter/src/index.ts +5 -0
- package/packages/cf-adapter/tests/config-loader.test.ts +19 -0
- package/packages/cf-adapter/tests/connection-do-nickserv-d1.test.ts +128 -0
- package/packages/cf-adapter/tests/connection-do-pure.test.ts +130 -0
- package/packages/cf-adapter/tests/connection-do.test.ts +150 -2
- package/packages/cf-adapter/tests/d1-services-store.test.ts +582 -0
- package/packages/cf-adapter/tests/serialize.test.ts +1 -0
- package/packages/in-memory-runtime/package.json +1 -1
- package/packages/irc-core/package.json +1 -1
- package/packages/irc-core/scripts/generate-build-info.mjs +26 -5
- package/packages/irc-core/src/commands/account-auth.ts +200 -0
- package/packages/irc-core/src/commands/chanserv.ts +1166 -0
- package/packages/irc-core/src/commands/hostserv.ts +522 -0
- package/packages/irc-core/src/commands/index.ts +13 -0
- package/packages/irc-core/src/commands/join.ts +171 -9
- package/packages/irc-core/src/commands/markread.ts +202 -0
- package/packages/irc-core/src/commands/memoserv.ts +319 -0
- package/packages/irc-core/src/commands/mode.ts +96 -4
- package/packages/irc-core/src/commands/nickserv.ts +402 -0
- package/packages/irc-core/src/commands/oper.ts +18 -1
- package/packages/irc-core/src/commands/operserv.ts +346 -0
- package/packages/irc-core/src/commands/pre-away.ts +3 -1
- package/packages/irc-core/src/commands/privmsg.ts +42 -0
- package/packages/irc-core/src/commands/read-marker.ts +8 -8
- package/packages/irc-core/src/commands/registration.ts +78 -12
- package/packages/irc-core/src/commands/sasl.ts +18 -49
- package/packages/irc-core/src/commands/service-aliases.ts +52 -0
- package/packages/irc-core/src/commands/tagmsg.ts +41 -6
- package/packages/irc-core/src/commands/topic.ts +60 -10
- package/packages/irc-core/src/config.ts +36 -5
- package/packages/irc-core/src/effects.ts +56 -1
- package/packages/irc-core/src/ports.ts +1653 -84
- package/packages/irc-core/src/protocol/numerics.ts +8 -0
- package/packages/irc-core/src/state/channel.ts +38 -1
- package/packages/irc-core/src/state/connection.ts +25 -1
- package/packages/irc-core/src/types.ts +48 -12
- package/packages/irc-core/tests/commands/chanserv.test.ts +2335 -0
- package/packages/irc-core/tests/commands/chathistory.test.ts +6 -0
- package/packages/irc-core/tests/commands/hostserv.test.ts +1006 -0
- package/packages/irc-core/tests/commands/join.test.ts +572 -1
- package/packages/irc-core/tests/commands/markread.test.ts +361 -0
- package/packages/irc-core/tests/commands/memoserv.test.ts +654 -0
- package/packages/irc-core/tests/commands/mode.test.ts +381 -2
- package/packages/irc-core/tests/commands/nickserv.test.ts +990 -0
- package/packages/irc-core/tests/commands/oper.test.ts +13 -0
- package/packages/irc-core/tests/commands/operserv.test.ts +656 -0
- package/packages/irc-core/tests/commands/privmsg.test.ts +147 -0
- package/packages/irc-core/tests/commands/read-marker.test.ts +28 -28
- package/packages/irc-core/tests/commands/registration.test.ts +1015 -20
- package/packages/irc-core/tests/commands/sasl.test.ts +229 -12
- package/packages/irc-core/tests/commands/server-info.test.ts +9 -5
- package/packages/irc-core/tests/commands/service-aliases.test.ts +52 -0
- package/packages/irc-core/tests/commands/tagmsg.test.ts +73 -33
- package/packages/irc-core/tests/commands/topic.test.ts +94 -2
- package/packages/irc-core/tests/commands/unified-account.test.ts +416 -0
- package/packages/irc-core/tests/config.test.ts +49 -5
- package/packages/irc-core/tests/effects.test.ts +19 -0
- package/packages/irc-core/tests/message-store.test.ts +63 -0
- package/packages/irc-core/tests/persistent-services-store.test.ts +582 -0
- package/packages/irc-core/tests/services-store.test.ts +1289 -0
- package/packages/irc-core/tests/state/channel.test.ts +3 -0
- package/packages/irc-server/package.json +1 -1
- package/packages/irc-server/src/actor.ts +121 -16
- package/packages/irc-server/src/dispatch.ts +94 -7
- package/packages/irc-server/src/routing.ts +19 -0
- package/packages/irc-server/tests/actor.test.ts +988 -15
- package/packages/irc-server/tests/dispatch.test.ts +270 -2
- package/packages/irc-server/tests/routing.test.ts +6 -0
- package/packages/irc-test-support/package.json +1 -1
- package/packages/irc-test-support/src/in-memory-harness.ts +37 -8
- package/packages/irc-test-support/src/index.ts +1 -0
- package/packages/irc-test-support/src/scenarios.ts +21 -6
- package/packages/irc-test-support/tests/in-memory-harness.test.ts +51 -0
- package/packages/irc-test-support/vitest.config.ts +6 -1
- package/tools/ci-hardening/package.json +1 -1
- package/tools/load-test/package.json +1 -1
- package/tools/load-test/src/client.ts +13 -13
- package/tools/load-test/tests/client.test.ts +258 -2
- package/tools/load-test/tests/config.test.ts +39 -0
- package/tools/load-test/tests/harness.test.ts +21 -0
- package/tools/load-test/tests/metrics.test.ts +7 -0
- package/tools/tcp-ws-forwarder/package.json +1 -1
- package/tools/tcp-ws-forwarder/tests/close-error.test.ts +40 -0
- package/tools/tcp-ws-forwarder/tests/defensive-branches.test.ts +78 -0
- package/tools/tcp-ws-forwarder/tests/forwarder.test.ts +53 -2
- package/tools/tcp-ws-forwarder/tests/logger.test.ts +31 -1
- package/apps/cf-worker/tests/fixtures/web-dist/app/index.html +0 -18
- package/packages/irc-core/tests/read-marker-store.test.ts +0 -108
package/docs/Services.md
ADDED
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
# IRC Services Reference
|
|
2
|
+
|
|
3
|
+
Operator and contributor reference for ServerlessIRCd's integrated IRC
|
|
4
|
+
services — **NickServ**, **ChanServ**, **HostServ**, **OperServ**, and
|
|
5
|
+
**MemoServ**. These ship as part of the daemon itself: there is no separate
|
|
6
|
+
services process, no pseudo-server, and no server-to-server (S2S) link.
|
|
7
|
+
|
|
8
|
+
> **Migration caveat (read first).** The integrated-services design is
|
|
9
|
+
> **not binary-compatible with Atheme or Anope.** There is no S2S protocol,
|
|
10
|
+
> so an existing Atheme/Anope services process cannot connect to
|
|
11
|
+
> ServerlessIRCd, and existing services databases cannot be imported.
|
|
12
|
+
> Operators migrating from a services-aware network must **re-register
|
|
13
|
+
> nicks and channels** against the ServerlessIRCd `ServicesStore`. See
|
|
14
|
+
> [§12 Migration notes](#12-migration-notes-from-athemeanope).
|
|
15
|
+
|
|
16
|
+
Cross-reference: `docs/PlanExtensions.md` §6 (architecture decision and
|
|
17
|
+
services scope), `PLAN.md` §1 (S2S is an explicit non-goal), the
|
|
18
|
+
`ServicesStore` port in `packages/irc-core/src/ports.ts`, and the
|
|
19
|
+
per-service reducers in `packages/irc-core/src/commands/`.
|
|
20
|
+
|
|
21
|
+
**Acceptance criterion (TICKET-131):** a new contributor can register a
|
|
22
|
+
NickServ account and a ChanServ channel end-to-end following only this doc.
|
|
23
|
+
See the [walkthrough in §13](#13-end-to-end-walkthrough).
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 1. Architecture: integrated services (not pseudo-servers)
|
|
28
|
+
|
|
29
|
+
Two designs exist in the wild:
|
|
30
|
+
|
|
31
|
+
- **Pseudo-client services (the Atheme / Anope model).** A separate process
|
|
32
|
+
connects to the ircd as one or more virtual IRC clients named `NickServ`
|
|
33
|
+
and so on; users interact via `PRIVMSG NickServ :REGISTER …`. The link
|
|
34
|
+
between ircd and services speaks a server-to-server protocol (TS6 / P10 /
|
|
35
|
+
IRCv3 S2S).
|
|
36
|
+
- **Integrated services (this project).** Service names are reserved nicks
|
|
37
|
+
recognized by the daemon at `PRIVMSG` time and routed to dedicated pure
|
|
38
|
+
reducers backed by a `ServicesStore` port. **No S2S, no separate process,
|
|
39
|
+
no pseudo-client connection.**
|
|
40
|
+
|
|
41
|
+
ServerlessIRCd chose **integrated services**. The rationale (full detail in
|
|
42
|
+
`docs/PlanExtensions.md` §6.1):
|
|
43
|
+
|
|
44
|
+
- S2S is an explicit `PLAN.md` §1 non-goal, and the pseudo-client model
|
|
45
|
+
requires it.
|
|
46
|
+
- The pure-reducer + location-of-authority architecture already has the
|
|
47
|
+
right seam: a `ServicesStore` port absorbs the side effects, and reducers
|
|
48
|
+
stay pure and unit-testable.
|
|
49
|
+
- One storage layer is shared conceptually with the SASL `AccountStore` —
|
|
50
|
+
authentication and nick ownership are meant to compose, not duplicate.
|
|
51
|
+
- The serverless substrates (Cloudflare Durable Objects, AWS DynamoDB)
|
|
52
|
+
handle persistence natively; a separate long-running services process
|
|
53
|
+
would defeat the deployment model.
|
|
54
|
+
|
|
55
|
+
**Trade-off:** integrated services are not binary-compatible with existing
|
|
56
|
+
Atheme/Anope modules. Operators migrating from a services-aware network
|
|
57
|
+
must re-register nicks and channels.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## 2. How services are enabled
|
|
62
|
+
|
|
63
|
+
Services are present on a deployment **iff** a `ServicesStore` is injected
|
|
64
|
+
into the actor's `Ctx.services`. Each adapter constructs and binds one when
|
|
65
|
+
its services backing store is configured:
|
|
66
|
+
|
|
67
|
+
| Adapter | Services backend | Wired when |
|
|
68
|
+
|---------|------------------|------------|
|
|
69
|
+
| **Cloudflare** | D1 (`nickserv_accounts`, `chanserv_channels`, …) | the `ACCOUNTS_DB` D1 binding is set — `loadD1ServicesStore()` in `packages/cf-adapter/src/d1-services-store.ts`, invoked from the connection Durable Object |
|
|
70
|
+
| **AWS** | DynamoDB (single services table, partitioned) | a services table name is supplied — `loadDynamoServicesStore()` in `packages/aws-adapter/src/dynamo-services-store.ts` |
|
|
71
|
+
| **in-memory / local CLI / tests** | `InMemoryServicesStore` (`Map`s) | always, for the local CLI and unit tests |
|
|
72
|
+
|
|
73
|
+
Both D1 and DynamoDB backends are **write-behind**: the store loads a
|
|
74
|
+
snapshot once per cold start into a synchronously-readable cache (reducers
|
|
75
|
+
must stay pure and free of IO), and flushes writes back to the durable
|
|
76
|
+
table. A redeploy/reconnect rehydrates from the persisted snapshot, so
|
|
77
|
+
registrations and channel access lists survive.
|
|
78
|
+
|
|
79
|
+
If no store is bound, every services command replies:
|
|
80
|
+
```
|
|
81
|
+
:NickServ!NickServ@services NOTICE <nick> :Services are not available on this server.
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### Config knobs (`packages/irc-core/src/config.ts`)
|
|
85
|
+
|
|
86
|
+
- **`servicesEnabled`** (`boolean`, optional) — currently gates only the
|
|
87
|
+
`ACCOUNTEXTBAN=a` ISUPPORT token advertised by `005`. It does **not**
|
|
88
|
+
gate the reducers; those are live whenever a store is injected.
|
|
89
|
+
- **`nickEnforceGraceMs`** (`integer ≥ 0`, optional) — grace period, in
|
|
90
|
+
milliseconds, NickServ waits before force-disconnecting a `ghost`-enforced
|
|
91
|
+
client. `0` makes `ghost` behave like `kill` (immediate). Omit for the
|
|
92
|
+
**30 000 ms** default (`DEFAULT_NICK_ENFORCE_GRACE_MS`).
|
|
93
|
+
|
|
94
|
+
---
|
|
95
|
+
|
|
96
|
+
## 3. Conventions
|
|
97
|
+
|
|
98
|
+
Every service command is sent as a `PRIVMSG` whose target is the service
|
|
99
|
+
nick and whose trailing parameter is the subcommand and arguments:
|
|
100
|
+
|
|
101
|
+
```
|
|
102
|
+
PRIVMSG <Service> :<subcommand> [args…]
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
All service replies are `NOTICE`s from a fixed hostmask independent of the
|
|
106
|
+
deployment's `serverName`:
|
|
107
|
+
|
|
108
|
+
```
|
|
109
|
+
:<Service>!<Service>@services NOTICE <nick> :<text>
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
Notable conventions:
|
|
113
|
+
|
|
114
|
+
- **`PRIVMSG` only.** Services are routed only for `PRIVMSG`. A `NOTICE`
|
|
115
|
+
or `TAGMSG` to a service nick is not intercepted and falls through to the
|
|
116
|
+
normal nick-target path.
|
|
117
|
+
- **Identification is the `+r` user mode.** A connection is "identified"
|
|
118
|
+
when NickServ `IDENTIFY` (or a SASL login) has set `state.account`. Many
|
|
119
|
+
commands require this.
|
|
120
|
+
- **Case-folding.** All nicks, accounts, and channel keys in the store are
|
|
121
|
+
**rfc1459 case-folded** (advertised via `005 CASEMAPPING=rfc1459`);
|
|
122
|
+
display spellings are preserved on returned records.
|
|
123
|
+
- **Numerics vs NOTICEs.** Services emit **no** success/error numerics of
|
|
124
|
+
their own — feedback is always a `NOTICE` from the pseudo-client. The
|
|
125
|
+
only numeric emissions tied to services are enforcement/gate codes:
|
|
126
|
+
`519` (ChanServ `+R` JOIN gate), `432` (OperServ JUPE), `404` (ChanServ
|
|
127
|
+
`+R`/`+M` send gates), `472` (services-only channel-mode letters).
|
|
128
|
+
|
|
129
|
+
### Shortcut aliases (`/NICKSERV`, `/NS`, `/CS`, …)
|
|
130
|
+
|
|
131
|
+
For compatibility with the Atheme/Anope services convention (and IRCd
|
|
132
|
+
alias modules such as InspIRCd `m_alias` / UnrealIRCd `alias{}`), every
|
|
133
|
+
service also accepts a **shortcut verb** that the daemon rewrites to the
|
|
134
|
+
canonical `PRIVMSG <ServiceNick> :<joined args>` before routing. These are
|
|
135
|
+
not RFC-standardized but are widely expected by clients and users.
|
|
136
|
+
|
|
137
|
+
| Shortcut verb(s) | Equivalent to |
|
|
138
|
+
|---|---|
|
|
139
|
+
| `NICKSERV`, `NS` | `PRIVMSG NickServ :<args>` |
|
|
140
|
+
| `CHANSERV`, `CS` | `PRIVMSG ChanServ :<args>` |
|
|
141
|
+
| `HOSTSERV`, `HS` | `PRIVMSG HostServ :<args>` |
|
|
142
|
+
| `MEMOSERV`, `MS` | `PRIVMSG MemoServ :<args>` |
|
|
143
|
+
| `OPERSERV`, `OS` | `PRIVMSG OperServ :<args>` |
|
|
144
|
+
|
|
145
|
+
The alias verb is the first token; everything after it is joined with
|
|
146
|
+
single spaces and becomes the `PRIVMSG` trailing body, so e.g.
|
|
147
|
+
`NICKSERV IDENTIFY hunter2` behaves identically to
|
|
148
|
+
`PRIVMSG NickServ :IDENTIFY hunter2`, and `NS REGISTER pw email` is the
|
|
149
|
+
same as `PRIVMSG NickServ :REGISTER pw email`. A colon-trailing form
|
|
150
|
+
(`NS :IDENTIFY hunter2`) is accepted too. The verbs are matched
|
|
151
|
+
case-insensitively. The alias → service nick table is the single source
|
|
152
|
+
of truth in `packages/irc-core/src/commands/service-aliases.ts`
|
|
153
|
+
(`SHORTCUT_TO_SERVICE`); both the dispatcher rewrite and the service-nick
|
|
154
|
+
routing consult it, so no service-reducer changes were needed.
|
|
155
|
+
|
|
156
|
+
When no `ServicesStore` is bound, a shortcut behaves exactly like a direct
|
|
157
|
+
`PRIVMSG` to the service nick — it falls through to the normal nick-target
|
|
158
|
+
path (a `401 ERR_NOSUCHNICK` for the unrecognised pseudo-client), never a
|
|
159
|
+
bare `421 ERR_UNKNOWNCOMMAND`.
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 4. NickServ — nick registration & identification
|
|
164
|
+
|
|
165
|
+
Source: `packages/irc-core/src/commands/nickserv.ts`. Wire nick `NickServ`.
|
|
166
|
+
|
|
167
|
+
NickServ owns nick registration, identification (`+r`), nick-enforcement
|
|
168
|
+
policy, and nick-info queries. A registered nick is an account.
|
|
169
|
+
|
|
170
|
+
### Commands
|
|
171
|
+
|
|
172
|
+
| Command | Syntax | Notes |
|
|
173
|
+
|---|---|---|
|
|
174
|
+
| `REGISTER` | `PRIVMSG NickServ :REGISTER <password> <email>` | Registers the *current* nick. Does **not** auto-identify (no `+r` set). Reply: `Nickname <nick> is now registered.` Already-registered → `Nickname <nick> is already registered.` |
|
|
175
|
+
| `IDENTIFY` | `PRIVMSG NickServ :IDENTIFY [nick] <password>` | One-arg form identifies the current nick; two-arg form identifies a named nick. On success: sets `state.account`, sets user mode **`+r`**, fans `ACCOUNT` to `account-notify` peers, then delivers unread MemoServ memos. Reply: `You are now identified for nick <account>.` |
|
|
176
|
+
| `DROP` | `PRIVMSG NickServ :DROP [nick]` | Requires prior identify. Drops the registration, clears `state.account` and `+r`, fans `ACCOUNT *`. Reply: `Nickname <nick> has been dropped.` |
|
|
177
|
+
| `INFO` | `PRIVMSG NickServ :INFO [nick]` | Defaults to the current nick. Replies (NOTICE): `Nick:`, `Account:`, and (only if the caller is identified as the owning account **or** is an oper) `Email:`. A non-owner sees `Nick:` / `Account:` only. Unregistered → `Nick <target> is not registered.` |
|
|
178
|
+
| `SET ENFORCE` | `PRIVMSG NickServ :SET ENFORCE none|ghost|kill` | Requires identify as the owning account. See enforcement below. |
|
|
179
|
+
|
|
180
|
+
Help / unknown command: `Available commands: REGISTER, IDENTIFY, DROP, INFO, SET`.
|
|
181
|
+
|
|
182
|
+
### Nick enforcement
|
|
183
|
+
|
|
184
|
+
`SET ENFORCE <policy>` decides what happens when an **unidentified** client
|
|
185
|
+
uses a registered nick (checked at welcome completion and on every `NICK`
|
|
186
|
+
change):
|
|
187
|
+
|
|
188
|
+
- `none` (default) — warning NOTICE only:
|
|
189
|
+
`This nickname is registered and protected. If it is yours, IDENTIFY; otherwise choose another.`
|
|
190
|
+
- `ghost` — force-disconnect the unidentified client after the grace period
|
|
191
|
+
(`nickEnforceGraceMs`, default 30 s); `0` makes it immediate.
|
|
192
|
+
- `kill` — force-disconnect **immediately** with reason
|
|
193
|
+
`Nick enforced by NickServ`.
|
|
194
|
+
|
|
195
|
+
> **Note on `IDENTIFY` vs SASL.** Both set `state.account` and the `+r`
|
|
196
|
+
> user mode and broadcast `ACCOUNT`. They differ in what else runs: SASL
|
|
197
|
+
> success additionally seeds persisted read-markers and replays persisted
|
|
198
|
+
> away state; NickServ `IDENTIFY` additionally delivers unread memos. The
|
|
199
|
+
> two paths are otherwise equivalent account-setting entry points.
|
|
200
|
+
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## 5. ChanServ — channel registration & access
|
|
204
|
+
|
|
205
|
+
Source: `packages/irc-core/src/commands/chanserv.ts`. Wire nick `ChanServ`.
|
|
206
|
+
ChanServ-emitted `MODE` lines use hostmask `ChanServ!ChanServ@services`.
|
|
207
|
+
|
|
208
|
+
ChanServ owns channel registration, founder/set management, the access list
|
|
209
|
+
(SOP/AOP/HOP/VOP via `ACCESS` and shorthand), configurable level
|
|
210
|
+
thresholds, and the `+r`/`+R`/`+M` channel modes.
|
|
211
|
+
|
|
212
|
+
### Registration & info
|
|
213
|
+
|
|
214
|
+
| Command | Syntax | Notes |
|
|
215
|
+
|---|---|---|
|
|
216
|
+
| `REGISTER` | `PRIVMSG ChanServ :REGISTER <#channel>` | Requires identify (founder = `state.account`). Sets channel mode **`+r`**. Reply: `Channel <channel> is now registered.` |
|
|
217
|
+
| `DROP` | `PRIVMSG ChanServ :DROP <#channel>` | Founder-only. Clears `+r`/`+R`/`+M`. Reply: `Channel <channel> has been dropped.` |
|
|
218
|
+
| `INFO` | `PRIVMSG ChanServ :INFO <#channel>` | Replies: `Channel:`, `Founder:`. |
|
|
219
|
+
|
|
220
|
+
### `SET` (founder-only)
|
|
221
|
+
|
|
222
|
+
All `SET` subcommands require the caller be identified as the channel
|
|
223
|
+
founder.
|
|
224
|
+
|
|
225
|
+
| Subcommand | Syntax | Effect |
|
|
226
|
+
|---|---|---|
|
|
227
|
+
| `FOUNDER` | `SET FOUNDER <#channel> <account>` | Transfers founder to a registered nick. |
|
|
228
|
+
| `MLOCK` | `SET MLOCK <#channel> <modestring>` | Mode lock. Allowed letters: `i t n m s p r R M k l o v b` (optional leading `+`/`-`). An op later unsetting a positively-locked boolean letter triggers ChanServ to reassert it (`:ChanServ!ChanServ@services MODE <chan> +<letters>`). `*` or absent clears the lock. |
|
|
229
|
+
| `RESTRICTED` | `SET RESTRICTED <#channel> ON|OFF` | Toggles channel mode **`+R`** (block unidentified). |
|
|
230
|
+
| `KEEPTOPIC` | `SET KEEPTOPIC <#channel> ON|OFF` | Persists the topic; restores it when the channel is recreated after going empty. |
|
|
231
|
+
|
|
232
|
+
### Access list (SOP / AOP / HOP / VOP)
|
|
233
|
+
|
|
234
|
+
`ACCESS <#channel> ADD|DEL <account>` and the shorthand verbs
|
|
235
|
+
`SOP`/`AOP`/`HOP`/`VOP` (each `ADD|DEL|LIST`) map an account to a privilege
|
|
236
|
+
level. `ADD`/`DEL` are founder-only; `LIST` is read-only.
|
|
237
|
+
|
|
238
|
+
Default shorthand levels and the auto-prefix they grant on `JOIN`:
|
|
239
|
+
|
|
240
|
+
| Verb | Level | Auto-prefix on JOIN |
|
|
241
|
+
|---|---|---|
|
|
242
|
+
| `SOP` | 10 | `@` (op) |
|
|
243
|
+
| `AOP` | 5 (`AUTOOP`) | `@` (op) |
|
|
244
|
+
| `HOP` | 4 (`AUTOHALFOP`) | (halfop, where represented) |
|
|
245
|
+
| `VOP` | 3 (`AUTOVOICE`) | `+` (voice) |
|
|
246
|
+
|
|
247
|
+
A level **≥ `AUTOOP`** grants op; **≥ `AUTOVOICE`** grants voice; below
|
|
248
|
+
that, no prefix. The channel **founder** always receives `@` on join. The
|
|
249
|
+
auto-prefix is broadcast as `:ChanServ!ChanServ@services MODE <chan> +o|+v <nick>`
|
|
250
|
+
so all clients update their display. (The first joiner to an empty channel
|
|
251
|
+
gets `@` by RFC 2812, bypassing ChanServ.)
|
|
252
|
+
|
|
253
|
+
### `LEVELS` (founder-only for `SET`/`RESET`)
|
|
254
|
+
|
|
255
|
+
`LEVELS <#channel> SET|LIST|RESET` redefines the numeric threshold each
|
|
256
|
+
auto-prefix op requires (`AUTOOP`, `AUTOHALFOP`, `AUTOVOICE`). `LIST` is
|
|
257
|
+
read-only and falls back to the defaults when no override is set.
|
|
258
|
+
|
|
259
|
+
### Channel modes introduced by ChanServ
|
|
260
|
+
|
|
261
|
+
| Mode | Meaning | Set by |
|
|
262
|
+
|---|---|---|
|
|
263
|
+
| `+r` | Channel is registered | ChanServ `REGISTER` |
|
|
264
|
+
| `+R` | Block join/message from unidentified nicks | `SET RESTRICTED ON` |
|
|
265
|
+
| `+M` | Moderated-identified: only identified (or op/voice) may speak | (cleared on `DROP`; not directly set by a ChanServ command) |
|
|
266
|
+
|
|
267
|
+
These three letters are **services-only**: a user `MODE` attempt to set
|
|
268
|
+
them returns `472 ERR_UNKNOWNMODE`. Their enforcement:
|
|
269
|
+
|
|
270
|
+
- `+R` **JOIN gate:** unidentified joiner →
|
|
271
|
+
`519 ERR_ALLMUSTREGISTER <chan> :You must register with NickServ to join this channel`.
|
|
272
|
+
- `+R` / `+M` **PRIVMSG gate:** unidentified sender (and, for `+M`, any
|
|
273
|
+
non-op/non-voice) → `404 ERR_CANNOTSENDTOCHAN`.
|
|
274
|
+
|
|
275
|
+
---
|
|
276
|
+
|
|
277
|
+
## 6. HostServ — virtual hosts
|
|
278
|
+
|
|
279
|
+
Source: `packages/irc-core/src/commands/hostserv.ts`. Wire nick `HostServ`.
|
|
280
|
+
|
|
281
|
+
HostServ manages per-account virtual hosts (vhosts), layered on the
|
|
282
|
+
existing cloak machinery. Toggling a vhost mutates the connection's host
|
|
283
|
+
and broadcasts an IRCv3 `CHGHOST` (`chghost` cap) to every shared-channel
|
|
284
|
+
peer.
|
|
285
|
+
|
|
286
|
+
| Command | Syntax | Gating | Notes |
|
|
287
|
+
|---|---|---|---|
|
|
288
|
+
| `ON` | `PRIVMSG HostServ :ON` | identified | Activates the account's assigned vhost; `CHGHOST` broadcast. Reply: `Your vhost is now active.` |
|
|
289
|
+
| `OFF` | `PRIVMSG HostServ :OFF` | identified | Restores the real (cloaked) host; `CHGHOST` broadcast. Reply: `Your vhost has been disabled.` |
|
|
290
|
+
| `REQUEST` | `PRIVMSG HostServ :REQUEST <vhost>` | identified | Behaviour depends on `hostservAutoApproveVhosts` (see below). Auto-approve mode: records the vhost immediately; reply `Vhost <vhost> has been requested and approved.` Queue mode: records a pending request; reply `Vhost <vhost> is pending oper approval.` The vhost must match a DNS-shape grammar (labels ≤63 chars, ≤253 total). |
|
|
291
|
+
| `SET` | `PRIVMSG HostServ :SET <account> <vhost>` | **oper-only** | Records a vhost directly against a registered nick. `*` clears. Not an oper → `You must be an oper to use SET.` |
|
|
292
|
+
| `APPROVE` (alias `ACTIVATE`) | `PRIVMSG HostServ :APPROVE <account>` | **oper-only** | Activates the pending vhost request recorded via queue-mode `REQUEST`. Calls `setVhost` and replies `Vhost <vhost> for <account> has been approved and activated.` A best-effort NOTICE is sent to the requester via `SendToNick` when they are online, telling them to run `/msg HostServ ON` to apply the vhost to their active connection. |
|
|
293
|
+
| `REJECT` | `PRIVMSG HostServ :REJECT <account>` | **oper-only** | Drops the pending vhost request without activating it. Replies `Vhost request for <account> has been rejected.` and best-effort NOTICEs the requester via `SendToNick` when they are online. |
|
|
294
|
+
| `LIST` | `PRIVMSG HostServ :LIST` | **oper-only** | Enumerates every pending request as `<account>: <vhost>` NOTICE lines. Empty queue → `No pending vhost requests.` |
|
|
295
|
+
|
|
296
|
+
### Approval mode (`hostservAutoApproveVhosts`)
|
|
297
|
+
|
|
298
|
+
The `ServerConfig.hostservAutoApproveVhosts` knob (boolean, default
|
|
299
|
+
`false`) selects how `REQUEST <vhost>` is handled:
|
|
300
|
+
|
|
301
|
+
- **`true` (auto-approve):** the legacy behaviour. `REQUEST`
|
|
302
|
+
records the vhost against the account immediately via `setVhost`. The
|
|
303
|
+
user can then `/msg HostServ ON` to apply it. `APPROVE`/`REJECT`/`LIST`
|
|
304
|
+
are still wired but only relevant for any pre-existing pending requests.
|
|
305
|
+
- **`false` (default — oper queue):** `REQUEST` records a **pending**
|
|
306
|
+
request via `ServicesStore.requestVhost` and replies that the request
|
|
307
|
+
is pending oper approval. The vhost is NOT activated and no `CHGHOST`
|
|
308
|
+
is emitted until an oper runs `APPROVE <account>`. `REJECT <account>`
|
|
309
|
+
drops the request; `LIST` enumerates the queue.
|
|
310
|
+
|
|
311
|
+
The queue + the oper commands are available in either mode; only
|
|
312
|
+
`REQUEST`'s routing through them is gated on the knob. The default
|
|
313
|
+
(`false`) matches the Atheme/Anope convention of requiring oper review
|
|
314
|
+
for vhosts; set `true` to restore the legacy self-service auto-approve.
|
|
315
|
+
|
|
316
|
+
> **Pending-request delivery note.** When an oper `APPROVE`s or `REJECT`s
|
|
317
|
+
> a pending request and the requester is online, HostServ delivers a
|
|
318
|
+
> NOTICE via the actor layer's `SendToNick` (silently dropped when the
|
|
319
|
+
> requester is offline). The actual `CHGHOST` application on the
|
|
320
|
+
> requester's live connection is **not** done from the oper's reducer —
|
|
321
|
+
> the pure reducer model forbids cross-connection state mutation — so
|
|
322
|
+
> the requester must run `/msg HostServ ON` themselves to apply an
|
|
323
|
+
> approved vhost to their active connection. The NOTICE tells them so.
|
|
324
|
+
|
|
325
|
+
---
|
|
326
|
+
|
|
327
|
+
## 7. OperServ — network bans, jupes, raw (oper-only)
|
|
328
|
+
|
|
329
|
+
Source: `packages/irc-core/src/commands/operserv.ts`. Wire nick `OperServ`.
|
|
330
|
+
|
|
331
|
+
> **Routing.** `PRIVMSG OperServ` is routed to the OperServ reducer in the
|
|
332
|
+
> actor's target routing (alongside NickServ, ChanServ, HostServ, and
|
|
333
|
+
> MemoServ). A client `PRIVMSG OperServ :<command>` reaches the reducer
|
|
334
|
+
> instead of falling through to the nick-target path. Interception is
|
|
335
|
+
> **PRIVMSG-only**: `NOTICE`/`TAGMSG OperServ` are not intercepted and fall
|
|
336
|
+
> through to the normal user-target path, matching the other four services.
|
|
337
|
+
>
|
|
338
|
+
> **Enforcement hooks** (unchanged) are wired through the registration
|
|
339
|
+
> reducer:
|
|
340
|
+
> - **AKILL** — checked at welcome completion against the connecting
|
|
341
|
+
> hostmask; a match disconnects with reason `AKILL: network-wide ban`
|
|
342
|
+
> after a `NOTICE` from `OperServ!OperServ@services`.
|
|
343
|
+
> - **JUPE** — checked on every `NICK` attempt; a juped nick is rejected
|
|
344
|
+
> with `432 ERR_ERRONEUSNICKNAME`.
|
|
345
|
+
>
|
|
346
|
+
> The command surface below (AKILL/JUPE/RAW management) is oper-gated and
|
|
347
|
+
> now reachable by clients via `PRIVMSG OperServ`. A non-oper receives
|
|
348
|
+
> `Permission denied — oper privileges required.` with no state change.
|
|
349
|
+
|
|
350
|
+
Command surface (oper-only, reachable via `PRIVMSG OperServ`):
|
|
351
|
+
|
|
352
|
+
| Command | Syntax | Notes |
|
|
353
|
+
|---|---|---|
|
|
354
|
+
| `AKILL ADD` | `PRIVMSG OperServ :AKILL ADD <mask> <reason>` | Records a network-wide ban; matched at connect time. Re-adding a mask replaces the reason. |
|
|
355
|
+
| `AKILL DEL` | `PRIVMSG OperServ :AKILL DEL <mask>` | Removes a mask. |
|
|
356
|
+
| `AKILL LIST` | `PRIVMSG OperServ :AKILL LIST` | Lists `<mask> — <reason>`. |
|
|
357
|
+
| `JUPE` | `PRIVMSG OperServ :JUPE <nick> [reason]` | Blocks a nick/server name locally. |
|
|
358
|
+
| `UNJUPE` | `PRIVMSG OperServ :UNJUPE <nick>` | Removes a jupe. |
|
|
359
|
+
| `RAW` | `PRIVMSG OperServ :RAW <line>` | Emits a raw IRC line (recovery use only). Oper-only and logged. |
|
|
360
|
+
|
|
361
|
+
Every subcommand requires oper privileges; non-opers get
|
|
362
|
+
`Permission denied — oper privileges required.` with no state change.
|
|
363
|
+
|
|
364
|
+
---
|
|
365
|
+
|
|
366
|
+
## 8. MemoServ — offline messages
|
|
367
|
+
|
|
368
|
+
Source: `packages/irc-core/src/commands/memoserv.ts`. Wire nick `MemoServ`.
|
|
369
|
+
|
|
370
|
+
MemoServ queues messages for a registered nick and delivers them when the
|
|
371
|
+
recipient next identifies (NickServ `IDENTIFY`) or logs in via SASL. All
|
|
372
|
+
commands require identification. Memos are delivered as
|
|
373
|
+
`:MemoServ!MemoServ@services NOTICE <nick> :[Memo #<id> from <from>] <body>`.
|
|
374
|
+
|
|
375
|
+
| Command | Syntax | Notes |
|
|
376
|
+
|---|---|---|
|
|
377
|
+
| `SEND` | `PRIVMSG MemoServ :SEND <nick> <message>` | Records a memo against the recipient's account (recipient must be a registered nick). The body may contain spaces. Queued; delivered on the recipient's next identify/SASL login. |
|
|
378
|
+
| `LIST` | `PRIVMSG MemoServ :LIST` | Lists **unread** memos: `#<id> <from>: <body>`. |
|
|
379
|
+
| `READ` | `PRIVMSG MemoServ :READ <n>` | Displays memo `#n` and marks it read. |
|
|
380
|
+
| `DEL` | `PRIVMSG MemoServ :DEL <n>` | Deletes memo `#n`. |
|
|
381
|
+
|
|
382
|
+
---
|
|
383
|
+
|
|
384
|
+
## 9. The `ServicesStore` port
|
|
385
|
+
|
|
386
|
+
The services side effects are absorbed by a single synchronous port in
|
|
387
|
+
`packages/irc-core/src/ports.ts` (synchronous because reducers are pure and
|
|
388
|
+
free of IO; adapters pre-load a snapshot and flush writes asynchronously).
|
|
389
|
+
The reference implementation `InMemoryServicesStore` ships in the same
|
|
390
|
+
file; D1 and DynamoDB backends live in their adapter packages.
|
|
391
|
+
|
|
392
|
+
Method groups (signatures abbreviated; see `ports.ts` for canonical forms):
|
|
393
|
+
|
|
394
|
+
- **NickServ** — `registerNick`, `verifyNick`, `dropNick`, `isRegisteredNick`,
|
|
395
|
+
`setNickEnforce`/`getNickEnforce`, `getNick`.
|
|
396
|
+
- **ChanServ (registration)** — `registerChannel`, `dropChannel`,
|
|
397
|
+
`getChannelFounder`, `setChannelAccess`/`getChannelAccess`/`listChannelAccess`.
|
|
398
|
+
- **ChanServ (levels)** — `getChannelLevel`/`setChannelLevel`/
|
|
399
|
+
`listChannelLevels`/`resetChannelLevels`.
|
|
400
|
+
- **ChanServ (record)** — `getChannel`, `setChannelFounder`,
|
|
401
|
+
`setChannelMlock`/`getChannelMlock`, `setChannelRestricted`,
|
|
402
|
+
`setChannelKeepTopic`, `setChannelTopic`/`getChannelTopic`.
|
|
403
|
+
- **HostServ** — `setVhost`/`getVhost`, plus the pending-vhost queue
|
|
404
|
+
(`requestVhost`/`getPendingVhost`/`listPendingVhosts`/`approveVhost`/
|
|
405
|
+
`rejectVhost`) consumed by queue-mode `REQUEST` and the oper
|
|
406
|
+
`APPROVE`/`REJECT`/`LIST` commands.
|
|
407
|
+
- **MemoServ** — `recordMemo`, `listMemos`, `markMemoRead`, `deleteMemo`.
|
|
408
|
+
- **OperServ** — `addAkill`/`removeAkill`/`listAkills`/`matchAkill`,
|
|
409
|
+
`addJupe`/`removeJupe`/`isJuped`/`getJupe`/`listJupes`.
|
|
410
|
+
- **Read-marker (IRCv3 `draft/read-marker`)** — `getLastReadMarker`/
|
|
411
|
+
`setLastReadMarker`/`listReadMarkers`.
|
|
412
|
+
|
|
413
|
+
Password comparison is constant-time. All nick/account/channel keys are
|
|
414
|
+
rfc1459 case-folded.
|
|
415
|
+
|
|
416
|
+
---
|
|
417
|
+
|
|
418
|
+
## 10. Admin tooling & seeding accounts
|
|
419
|
+
|
|
420
|
+
Two seed scripts ship under `tools/` for **SASL accounts** (the
|
|
421
|
+
scrypt-hashed `accounts`/`Accounts` store used by `AUTHENTICATE PLAIN`):
|
|
422
|
+
|
|
423
|
+
- `tools/seed-cf-accounts.ts` — writes to a Cloudflare D1 `accounts` table
|
|
424
|
+
via `wrangler d1 execute`, scrypt-hashing each password.
|
|
425
|
+
- `tools/seed-aws-accounts.ts` — writes to a DynamoDB `Accounts` table via
|
|
426
|
+
the AWS SDK, scrypt-hashing each password.
|
|
427
|
+
|
|
428
|
+
Both accept `--accounts alice:s3cret bob:pw …` or `--file accounts.txt`
|
|
429
|
+
(newline-delimited `username:password`).
|
|
430
|
+
|
|
431
|
+
```
|
|
432
|
+
# Cloudflare
|
|
433
|
+
node --import tsx tools/seed-cf-accounts.ts \
|
|
434
|
+
--database serverless-ircd-accounts-staging --env staging --remote \
|
|
435
|
+
--accounts alice:s3cret
|
|
436
|
+
|
|
437
|
+
# AWS
|
|
438
|
+
node --import tsx tools/seed-aws-accounts.ts \
|
|
439
|
+
--table StagingAccounts --endpoint http://localhost:8000 \
|
|
440
|
+
--accounts alice:s3cret
|
|
441
|
+
```
|
|
442
|
+
|
|
443
|
+
> **NickServ accounts vs SASL accounts.** NickServ registrations live in
|
|
444
|
+
> the **`ServicesStore`** (`nickserv_accounts`), which is unified with the
|
|
445
|
+
> SASL `AccountStore` the seed scripts above populate: a NickServ
|
|
446
|
+
> registration is a SASL account and vice versa, both scrypt-hashed (see
|
|
447
|
+
> the [walkthrough](#13-end-to-end-walkthrough)). The recommended way to
|
|
448
|
+
> create a NickServ account is the live
|
|
449
|
+
> `PRIVMSG NickServ :REGISTER …` flow over an established connection. A
|
|
450
|
+
> dedicated `tools/seed-nickserv-accounts.ts` mirroring the SASL seeders
|
|
451
|
+
> is tracked as a follow-up; because credentials are scrypt-hashed at rest
|
|
452
|
+
> (see [§11](#11-security-characteristics--known-gaps)), bulk-seeded rows
|
|
453
|
+
> must supply a scrypt `algorithm`/`salt`/`hash` triple rather than a
|
|
454
|
+
> plaintext password.
|
|
455
|
+
|
|
456
|
+
Operators can manage AKILL/JUPE entries via `PRIVMSG OperServ` (oper-only,
|
|
457
|
+
see [§7](#7-operserv--network-bans-jupes-raw-oper-only)), or pre-populate
|
|
458
|
+
them by writing directly into the `ServicesStore` backing tables.
|
|
459
|
+
|
|
460
|
+
---
|
|
461
|
+
|
|
462
|
+
## 11. Security characteristics & known gaps
|
|
463
|
+
|
|
464
|
+
Be aware of the following when operating or contributing to services:
|
|
465
|
+
|
|
466
|
+
- **NickServ passwords are stored scrypt-hashed** in the `ServicesStore`
|
|
467
|
+
(the same scrypt path the SASL `AccountStore` uses:
|
|
468
|
+
`InMemoryServicesStore.registerNick` hashes via `hashAccountCredential`
|
|
469
|
+
before persisting; `verifyNick` re-derives via `verifyHashedPassword`;
|
|
470
|
+
the D1 `nickserv_accounts` table stores `algorithm`/`salt`/`hash`
|
|
471
|
+
columns, never the plaintext password). NickServ and SASL accounts are
|
|
472
|
+
unified, so a NickServ registration is a SASL account and vice versa.
|
|
473
|
+
Treat the services backing store (D1 database / DynamoDB table) as
|
|
474
|
+
sensitive regardless: restrict access and enable at-rest encryption
|
|
475
|
+
(DynamoDB encryption-at-rest is on by default).
|
|
476
|
+
- **HostServ `REQUEST` approval mode is configurable** — the default
|
|
477
|
+
(`hostservAutoApproveVhosts: false`) queues every request for oper
|
|
478
|
+
review via the `APPROVE`/`REJECT`/`LIST` commands (see [§6](#6-hostserv--virtual-hosts)).
|
|
479
|
+
Set `hostservAutoApproveVhosts: true` to restore the legacy
|
|
480
|
+
self-service auto-approve, where any identified user can self-assign a
|
|
481
|
+
DNS-shape vhost.
|
|
482
|
+
- **OperServ management is oper-gated** — every `PRIVMSG OperServ`
|
|
483
|
+
subcommand (AKILL/JUPE/RAW) requires oper privileges; non-opers receive
|
|
484
|
+
`Permission denied — oper privileges required.` with no state change
|
|
485
|
+
(see [§7](#7-operserv--network-bans-jupes-raw-oper-only)).
|
|
486
|
+
- **Memo bodies** are stored and replayed verbatim; no size cap beyond the
|
|
487
|
+
general IRC line-length budget applied at the wire.
|
|
488
|
+
- **`+r` user mode** is set by NickServ `IDENTIFY` and SASL; it is not set
|
|
489
|
+
by the legacy `PASS <nick>:<password>` form (that path is tracked
|
|
490
|
+
separately).
|
|
491
|
+
|
|
492
|
+
---
|
|
493
|
+
|
|
494
|
+
## 12. Migration notes (from Atheme/Anope)
|
|
495
|
+
|
|
496
|
+
- **No S2S, no services process.** An existing Atheme/Anope instance cannot
|
|
497
|
+
attach to ServerlessIRCd. Plan to **re-register** nicks and channels.
|
|
498
|
+
- **Re-register nicks** with `PRIVMSG NickServ :REGISTER <password> <email>`
|
|
499
|
+
on the new server. There is no import path from Atheme/Anope databases.
|
|
500
|
+
- **Re-register channels** with `ChanServ REGISTER` from the identified
|
|
501
|
+
founder, then re-create the access list via `ACCESS`/`SOP`/`AOP`/`HOP`/
|
|
502
|
+
`VOP`. MLOCK, RESTRICTED, and KEEPTOPIC must be re-applied with `SET`.
|
|
503
|
+
- **OperServ parity is complete.** AKILL (connect-time enforcement) and
|
|
504
|
+
JUPE (nick-time enforcement) are live, and the AKILL/JUPE/RAW management
|
|
505
|
+
commands are client-reachable via `PRIVMSG OperServ` (oper-only, see
|
|
506
|
+
[§7](#7-operserv--network-bans-jupes-raw-oper-only)).
|
|
507
|
+
- **HostServ vhost approval.** The default
|
|
508
|
+
(`hostservAutoApproveVhosts: false`) matches the Atheme/Anope
|
|
509
|
+
convention: `REQUEST` queues for oper `APPROVE`/`REJECT`/`LIST`
|
|
510
|
+
(see [§6](#6-hostserv--virtual-hosts)). Set `hostservAutoApproveVhosts: true`
|
|
511
|
+
to restore self-service auto-approve.
|
|
512
|
+
- **Out of scope** (will not migrate): `BotServ` (channel bots are a
|
|
513
|
+
runtime cost serverless deployments should not absorb — run your own bot
|
|
514
|
+
as an ordinary client) and `StatServ` (covered by `STATS` and the
|
|
515
|
+
observability stack). Grouped/linked nicks and fantasy commands are also
|
|
516
|
+
out of scope for this pass.
|
|
517
|
+
|
|
518
|
+
---
|
|
519
|
+
|
|
520
|
+
## 13. End-to-end walkthrough
|
|
521
|
+
|
|
522
|
+
This satisfies the TICKET-131 acceptance criterion: register a NickServ
|
|
523
|
+
account and a ChanServ channel using only this doc. Connect to a
|
|
524
|
+
services-enabled deployment with any IRC client and register first:
|
|
525
|
+
|
|
526
|
+
```
|
|
527
|
+
NICK alice
|
|
528
|
+
USER alice 0 * :Alice
|
|
529
|
+
PRIVMSG NickServ :REGISTER hunter2 alice@example.com
|
|
530
|
+
```
|
|
531
|
+
```
|
|
532
|
+
:NickServ!NickServ@services NOTICE alice :Nickname alice is now registered.
|
|
533
|
+
```
|
|
534
|
+
|
|
535
|
+
The nick is registered but not yet identified. Identify to receive the `+r`
|
|
536
|
+
user mode (required before registering a channel or sending memos):
|
|
537
|
+
|
|
538
|
+
```
|
|
539
|
+
PRIVMSG NickServ :IDENTIFY alice hunter2
|
|
540
|
+
```
|
|
541
|
+
```
|
|
542
|
+
:NickServ!NickServ@services NOTICE alice :You are now identified for nick alice.
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
Now register a channel (you are the founder):
|
|
546
|
+
|
|
547
|
+
```
|
|
548
|
+
JOIN #mychannel
|
|
549
|
+
PRIVMSG ChanServ :REGISTER #mychannel
|
|
550
|
+
```
|
|
551
|
+
```
|
|
552
|
+
:ChanServ!ChanServ@services NOTICE alice :Channel #mychannel is now registered.
|
|
553
|
+
:ChanServ!ChanServ@services MODE #mychannel +r
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
Add a friend (who has already registered their nick `bob`) to the access
|
|
557
|
+
list as an auto-op, lock the topic, and restrict the channel to identified
|
|
558
|
+
users:
|
|
559
|
+
|
|
560
|
+
```
|
|
561
|
+
PRIVMSG ChanServ :AOP #mychannel ADD bob
|
|
562
|
+
PRIVMSG ChanServ :SET KEEPTOPIC #mychannel ON
|
|
563
|
+
PRIVMSG ChanServ :SET RESTRICTED #mychannel ON
|
|
564
|
+
```
|
|
565
|
+
```
|
|
566
|
+
:ChanServ!ChanServ@services NOTICE alice :Bob added to the access list for #mychannel at level 5.
|
|
567
|
+
:ChanServ!ChanServ@services NOTICE alice :Keep-topic for #mychannel is now set to ON.
|
|
568
|
+
:ChanServ!ChanServ@services NOTICE alice :Restricted (block unidentified) for #mychannel is now set to ON.
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
When `bob` later identifies and joins, ChanServ auto-ops them:
|
|
572
|
+
|
|
573
|
+
```
|
|
574
|
+
:ChanServ!ChanServ@services MODE #mychannel +o bob
|
|
575
|
+
```
|
|
576
|
+
|
|
577
|
+
That completes the core nick + channel registration flow. From here, the
|
|
578
|
+
per-service references in §4–§8 cover every shipped command.
|