Commit graph

24 commits

Author SHA1 Message Date
289470ca0c
db: compaction was silently dropping channel and jupe state
All checks were successful
CI / check (push) Successful in 3m52s
compact() rebuilds channels field-by-field, and it omitted several fields, so
automatic log compaction (should_compact -> compact) silently deleted them:
channel url, email, successor, noexpire, the restricted/noautoop/bot_greet/
nobot settings, and every jupe. Emits them all now (settings gated on !=
default via a new PartialEq). Adds a compaction round-trip property test
(compact()+reopen == before) alongside the fold-parity one; both share the
broad build/snapshot helpers.
2026-07-16 11:08:55 +00:00
90167dd46f
db: persist jupes across restart (event-source them)
All checks were successful
CI / check (push) Successful in 3m50s
A fold-parity property test caught it: jupes were plain in-memory Db fields
that logged no event, so after any services restart every juped server was
silently un-juped and could relink. Moved jupes into NetData (like akills/
forbids/groups) and event-sourced them with JupeAdded/JupeRemoved (Local
scope), so a jupe now replays from the log. Adds the round-trip property test
(live state == replayed state) as a permanent guard against this bug class.
2026-07-16 10:56:22 +00:00
61005f52f5
services: throttle emailed codes to stop RESEND/RESETPASS email-bombing
All checks were successful
CI / check (push) Successful in 3m46s
REGISTER was rate-limited but RESEND and RESETPASS weren't — anyone could
repeatedly request confirmation/reset codes for any account with an address
on file, flooding the victim's inbox and burning the service's sender
reputation. A per-account 60s cooldown (code_issue_wait, mirroring the vhost
request throttle) now gates both paths; guessing was already infeasible
(2^40 code + 5-try limit), so this closes the abuse, not an auth hole.
2026-07-16 10:41:52 +00:00
99df7baf10
engine: apply gossiped network bans to the ircd immediately
All checks were successful
CI / check (push) Successful in 3m47s
A local AKILL/SQLINE/etc. pushes an ADDLINE to the ircd at once, but a ban
arriving by gossip was only held for the next netburst — so a user banned on
one network could keep hopping to peers until their next relink. ingest now
signals BanAdded/BanLifted (all xline kinds), and gossip_ingest pushes the
ADDLINE/DELLINE. Renamed AccountChange to IngestEffect to match its broader
role.
2026-07-16 09:28:56 +00:00
886fe65e4d
engine: log out local sessions on a gossiped account suspension
All checks were successful
CI / check (push) Successful in 3m43s
A local SUSPEND logs out the account's active sessions, but a suspension
arriving by gossip only set the flag — a session already identified on a
remote node stayed logged in despite the network-wide suspension. ingest
now signals AccountChange::Suspended, and gossip_ingest ends those sessions
(the account and its channels are kept). Factors the per-session logout into
a shared logout_uid helper.
2026-07-16 09:13:53 +00:00
01ea0fa95e
ChanServ: add SET EMAIL channel contact address
Founders can attach a contact email to a channel, shown in INFO and
cleared when set empty. Mirrors SET URL exactly; Local-scope channel
metadata. Completes the channel DESC/URL/EMAIL trio.
2026-07-16 03:06:08 +00:00
7eb1adce5e
ChanServ: add SET URL channel homepage
Founders can attach a homepage URL to a channel; it shows in INFO and
clears when set empty, mirroring SET DESC. Local-scope (channel metadata),
not gossiped, consistent with the entrymsg/description events.
2026-07-16 02:58:44 +00:00
053f8cf832
NickServ: add SET HIDE STATUS for last-seen privacy
Lets an account keep its last-seen/online line in INFO visible only to
itself and to opers. Default stays public (Anope's default). Accepts
STATUS (with USERMASK as an alias) per Anope's SET HIDE <field> grammar.
2026-07-16 02:33:55 +00:00
2c4bc9079c
NickServ: add SET KILL to toggle nick protection
Nick protection was unconditional. SET KILL OFF lets an account opt out —
an unidentified user keeping one of its nicks is no longer prompted or
renamed to a guest. Stored inverted (no_protect) so protection stays the
default. Accepts Anope's ON/QUICK/IMMED/OFF; grace is a fixed interval
here, so the finer variants simply enable protection like ON.
2026-07-16 01:43:10 +00:00
87e21ff85f
NickServ: add SET AUTOOP per-account auto-op opt-out
A user with SET AUTOOP OFF is never auto-opped on join, even where they
hold channel access — they op themselves via ChanServ UP. Stored inverted
(no_autoop) so the default stays auto-op. Join gates on both the channel
setting and the account preference; either can opt out.
2026-07-16 01:37:31 +00:00
0027decdb7
OperServ: add SET READONLY lockdown
All checks were successful
CI / check (push) Successful in 3m31s
2026-07-16 01:05:48 +00:00
dd43b9a331
BotServ: add BOTLIST and AUTOASSIGN default bot
All checks were successful
CI / check (push) Successful in 3m33s
2026-07-16 00:57:06 +00:00
d819b2c75f
MemoServ: add RSEND read receipts
Some checks failed
CI / check (push) Has been cancelled
2026-07-16 00:43:01 +00:00
2850620be1
ChanServ: add SET AUTOOP
All checks were successful
CI / check (push) Successful in 3m35s
SET AUTOOP {ON|OFF} controls whether access members are auto-opped/voiced
on join (on by default). Stored inverted as a settings flag so the
default stays on, and gated in the join handler.
2026-07-16 00:36:19 +00:00
2c282d036f
MemoServ: add SET NOTIFY and SET LIMIT
All checks were successful
CI / check (push) Successful in 3m35s
SET NOTIFY {ON|OFF} controls whether you're told about new memos on login;
SET LIMIT <n>|NONE sets your mailbox cap (honoured on SEND, NONE = the
network default). New memo_notify/memo_limit account fields + one
MemoPrefsSet event.
2026-07-16 00:29:33 +00:00
b5e08c33cf
MemoServ: add IGNORE
All checks were successful
CI / check (push) Successful in 3m34s
IGNORE ADD/DEL/LIST manages a per-account memo-ignore list; a memo from
an ignored account is silently dropped, and the sender is still told it
was sent so the ignore isn't revealed. New memo_ignore account field +
events, and a check in SEND.
2026-07-16 00:10:34 +00:00
53d3d63464
NickServ GETEMAIL + ChanServ SET RESTRICTED
All checks were successful
CI / check (push) Successful in 3m51s
GETEMAIL (auspex) lists accounts whose email matches a glob. SET
RESTRICTED makes a channel kick anyone without access (or oper) as they
join, mirroring the SECUREOPS on-join enforcement.
2026-07-15 19:25:41 +00:00
be4860c9a8
OperServ: add FORBID (nick/channel/email registration bans)
All checks were successful
CI / check (push) Successful in 3m56s
FORBID ADD/DEL/LIST bans a NICK, CHAN, or EMAIL glob from being
registered — a network-wide policy that gossips like an AKILL and every
node enforces. Nick registration is blocked in pre_register_check (both
NickServ REGISTER and the account-registration relay), and channel
registration in ChanServ REGISTER. New Forbid store + events, mirroring
the akill subsystem.
2026-07-15 19:14:07 +00:00
ac50af92fc
ChanServ: add SET SUCCESSOR with founder inheritance
All checks were successful
CI / check (push) Successful in 3m59s
SET <#channel> SUCCESSOR <account>|OFF names an account that inherits the
channel if the founder's account is dropped or expires, instead of the
channel being released. Adds a ChannelInfo.successor field + event, and
centralises the founder-gone channel release into a single
release_founded_channels store method used by DROP, expiry, and the
gossip account-gone path so all three transfer consistently.
2026-07-15 18:58:08 +00:00
994e8c7347
Make the SCRAM verifier the sole password credential; finish the account-registration relay
All checks were successful
CI / check (push) Successful in 4m1s
Credentials: the SCRAM-SHA-256 verifier is now the only password
credential. PLAIN and IDENTIFY verify the plaintext against it via
scram::verify_plain, exactly as a SCRAM login proves knowledge of it, so
an account provisioned from a verifier alone (external authority) works
over every mechanism, not only SCRAM. Removed the redundant argon2 hash
entirely: the field, the Credentials member, the AccountPasswordSet
field, hash_password/verify_password, and the argon2 crate. SASL SCRAM
already forces a PBKDF2 verifier into the store, so the hash never raised
the at-rest floor. OS RNG now comes from rand_core.

Registration: finished draft/account-registration over the ircd relay.
VERIFY confirms the emailed code, RESEND reissues it, STATUS reports
state, and REGISTER now emails the code and replies verification_required
on the relay path too, not only through NickServ.
2026-07-15 15:47:41 +00:00
e551a01cbf
Harden for production: fsync writes, deployment + backup tooling
All checks were successful
CI / check (push) Successful in 3m55s
The event log is the account/channel authority, so make it durable:
persist() now fsyncs every committed event before reporting success, and
a serialisation failure is an error instead of a silently-dropped blank
line. Add a hardened systemd unit (scripts/echo.service), an atomic
gzip backup script with retention (scripts/backup.sh), and a Deployment
wiki page (install, durability, backup/restore, upgrade, monitoring).
2026-07-15 11:45:50 +00:00
d9c0878b00
Log store write failures at the source; share the oper guard
The store's map_err(|_| Internal) discarded the real IO error, so a
disk-full or permissions failure was invisible; log it once in the log
append path where it happens. Add echo_api::require_oper (parameterised
by the required privilege) and route the three copied per-service guards
through it, keeping each service's own wording and policy.
2026-07-14 23:19:00 +00:00
993f5b2eea
Rename the project to Echo (was fedserv)
Rebrand the daemon and workspace: the binary is now `echo`, every crate
is `echo-*` (`echo-api` and the module crates), Rust imports use
`echo_*`, the gRPC proto is `proto/echo.proto` with package `echo.v1`,
and the log filter, gossip peer-name default, and on-disk store default
(`echo.db.jsonl`) follow. Docs updated throughout. No behaviour change;
crate directories and the config schema are untouched.
2026-07-14 15:24:41 +00:00
0c7976e80d
Reorganise the store into a domain-split module tree
engine/db.rs had grown past 4k lines. Split the single Db type's
operations into sibling files by domain — account, channel, network,
the event log, and the module-facing store facade — while all type
definitions, the log plumbing, and shared helpers stay in db/mod.rs.
Pure reorganisation: no behaviour change, tests unchanged and green.
2026-07-14 13:57:05 +00:00