Commit graph

231 commits

Author SHA1 Message Date
f15c9998be
Source channel mode changes from ChanServ
Channel modes (+r, mode lock, auto-op, enforcement) are sent from the
ChanServ pseudoclient instead of the services server, so users see
ChanServ as the setter. The engine locates the channel-managing service;
the FMODE filter now ignores anything under our SID prefix (server and
pseudoclients) so enforcement can't loop on our own changes.
2026-07-12 10:18:07 +00:00
e785c9d8ac
Auto-op the founder on join
Surface channel joins (FJOIN members, stripping the membid, and IJOIN) and
set +o for a joining user who is identified as the channel's founder.
2026-07-12 10:07:02 +00:00
353aee1b54
Add ChanServ mode lock (MLOCK)
Founders lock channel modes with MLOCK <#channel> [modes]. The lock is a
ChannelMlock event, so it replicates and compacts like everything else;
it is applied on channel creation alongside +r and enforced by reverting
any FMODE that breaks it. Our own changes are filtered so enforcement
can't loop. Simple (paramless) modes for now.
2026-07-12 09:33:23 +00:00
727713ee3f
Reword NickServ replies to match ChanServ's friendlier tone
Clearer, more actionable wording across IDENTIFY, LOGOUT, HELP, the
unknown-command hint, and the registration replies, so the two services
read consistently.
2026-07-12 08:59:36 +00:00
c0b41c6af5
Show ChanServ INFO with a readable date and friendlier text
INFO prints the founder and a UTC registration date instead of a raw
Unix timestamp (formatted with a small civil-date helper, no date crate),
and the notices are reworded to read more naturally.
2026-07-12 08:52:34 +00:00
eb22612da8
Re-assert +r when a registered channel is created
fedserv now handles FJOIN (channel create/burst) and sets +r on any
channel that is registered, so a registered channel regains the mode
after it empties and is recreated, and after a services relink. Single
joins arrive as IJOIN and are ignored.
2026-07-12 08:41:45 +00:00
64500906d8
Set channel mode +r when a channel is registered
ChanServ sends FMODE to mark a registered channel +r (the m_services
c_registered mode), and -r on drop. Sourced from our server, which the
ircd requires for that mode, with a low TS so it applies to the existing
channel.
2026-07-12 08:33:39 +00:00
89593ebeb4
Add ChanServ with channel registration
REGISTER/INFO/DROP with founder auth, stored as ChannelRegistered and
ChannelDropped events so channel registrations replicate across nodes
and compact like accounts. Registration requires an identified account;
only the founder can drop.
2026-07-12 08:19:58 +00:00
64c6facd56
Self-heal a dropped gossip push without waiting for the timer
When a forwarder falls behind and drops pushes (broadcast lag), it sends
a digest with reply set; the peer answers with its own digest and we
re-send exactly what it is missing. Recovery no longer waits out a full
poll interval, so the 10s backstop rarely matters.
2026-07-12 08:01:47 +00:00
d30f5c05a6
Refresh README and config example
Document the event-sourced store, gossip replication, push-on-commit,
compaction and the TLS peer link; drop the stale persistence status.
2026-07-12 07:31:16 +00:00
0af7f10c3d
Add mutual-TLS to the gossip link
Optional [gossip.tls] wraps the peer link in TLS: each node presents a
certificate and requires the peer to present one signed by the configured
CA. scripts/gen-certs.sh builds a CA and node certs.
2026-07-12 07:31:16 +00:00
cc0265548f
Compact the event log into per-account snapshots
Once the log grows past ~3x the account count it is rewritten to one
AccountRegistered per account (churned cert events dropped), via a temp
file and rename. Cert replay is now idempotent, so a snapshot plus
re-delivered events converge and a peer syncing from a compacted node
still gets every account. Runs on a 30m timer.
2026-07-12 07:24:44 +00:00
df7b413862
Push new log entries to peers on commit
A broadcast channel forwards each freshly committed entry straight to
connected peers, so a registration propagates in milliseconds. The
periodic digest drops to a 10s anti-entropy backstop.
2026-07-12 07:20:25 +00:00
d0556ebe8c
Add gossip replication between nodes
Nodes run anti-entropy over a TCP link: each advertises its version
vector, the peer replies with the log entries it lacks, and ingest is
idempotent so re-delivery and reconnect after a split both converge.
The engine is shared behind a mutex; config gains [gossip] and [[peer]].
2026-07-12 07:05:07 +00:00
82e41e95b2
Add parser tests for NICK and UID 2026-07-12 06:30:37 +00:00
3161a97a5e
Add Lamport clock and gossip ingest to the event log
Entries carry a Lamport timestamp alongside origin/seq. EventLog keeps a
per-origin version vector and ingests peer entries idempotently.
Db::ingest folds them in. No transport yet.
2026-07-12 06:30:37 +00:00
c066f1072f
Add EventLog with origin/seq metadata for replication 2026-07-12 06:08:27 +00:00
65729a62b8
Track login state and nick changes in NickServ 2026-07-12 06:05:33 +00:00
60fc687162
Add NickServ LOGOUT command
Clears the account (RPL_LOGGEDOUT) and renames the user to a
configurable guest nick (default Guest) via SVSNICK.
2026-07-12 05:06:34 +00:00
a382ac8ad3
Add SASL EXTERNAL 2026-07-12 03:59:24 +00:00
831f538a98
Expire and cap in-progress SASL sessions 2026-07-12 03:59:24 +00:00
9a918839fe
Derive registration credentials off-thread; rate-limit REGISTER; constant-time SCRAM compare 2026-07-12 03:59:24 +00:00
496c0f99a6
Add SASL SCRAM-SHA-256 and SCRAM-SHA-512 2026-07-12 03:59:24 +00:00
172e50c748
Skip registering passwords too long for one IRC line
The irctest controller's registerUser sends NickServ REGISTER as a single
PRIVMSG, so a password that overflows the 512-byte line limit gets
truncated on the wire and can never match a later SASL login. Raise
NotImplementedByController for those, like the reference services
controllers, so such cases skip instead of failing spuriously.
2026-07-12 00:52:56 +00:00
e5c15421c8
Add SASL PLAIN engine tests
Cover the login exchange end to end without an ircd: single-chunk,
non-400-multiple chunking, an exact-800 response ending on a lone "+",
and a bad password. Guards the base64 reassembly and the accountname
login the uplink turns into RPL_LOGGEDIN.
2026-07-12 00:41:49 +00:00
65deeef2f7
Complete IRCv3 SASL 3.2 login for PLAIN
Turn the PLAIN verification exchange into a full account login:

- Advertise the mechanism list to the uplink as `saslmechlist` network
  metadata, so clients see `sasl=PLAIN` in CAP LS (SASL 3.2).
- On a valid response set the client's `accountname` metadata (drives
  RPL_LOGGEDIN 900) before reporting success (D S, drives 903); a bad
  credential or unknown mechanism reports failure (D F, drives 904).
- Reassemble the base64 response from the uplink's 400-byte AUTHENTICATE
  chunks, ending on a short chunk or a lone "+", with an upper bound to
  cap a pre-auth client's buffer.
- Log the user in on NickServ REGISTER and IDENTIFY too, through the same
  accountname metadata, and return the canonical account name from the
  store so the login keeps its registered casing.

Verified against irctest server_tests/sasl.py (PLAIN success/failure,
non-ASCII, no-authzid, chunked payloads, unknown mechanism, too-long,
retry) on InspIRCd + fedserv.
2026-07-12 00:36:51 +00:00
63028d99e5
Add the SASL PLAIN verification exchange
fedserv now handles the SASL exchange an ircd relays over ENCAP (modes H/S/C/D):
on PLAIN it answers the empty challenge, decodes the client payload, and verifies
the credentials against the account store, replying D S or D F. Wiring the
account login on success and advertising the mechanism list to the ircd are the
remaining integration steps.
2026-07-11 21:18:42 +00:00
18a28ed49b
Handle the account-registration relay from an ircd
As the registration authority, fedserv accepts an account-registration request
relayed over the server link (ACCTREGISTER), creates the account through the
same store as classic NickServ REGISTER, and replies (ACCTREGRESULT) with
success or an error carrying the IRCv3 FAIL code — one account-creation path for
both the classic command and the draft/account-registration capability.
2026-07-11 20:50:18 +00:00
9285105afd
Add persisted account store with NickServ REGISTER/IDENTIFY
Accounts are an append-only JSONL event log (state is the fold of the log) with
argon2id-hashed passwords. NickServ gains REGISTER <password> [email] and
IDENTIFY <password>; the engine tracks UID->nick from the burst so a command
resolves to the sender's current nick. Classic and ircd-agnostic — the cap-based
account-registration forward is the next step.
2026-07-11 20:28:42 +00:00
7929f5f9f4
Fix InspIRCd UID and message-tag parsing; add irctest controller
The UID introduction omitted the second user field (insp sends both a real and a
displayed user), and the parser didn't strip IRCv3 message-tag prefixes, so
tagged PRIVMSGs addressed to a service were dropped. With both fixed a service
links to a real insp4 uplink and answers commands. Adds an irctest services
controller so this is verified against an isolated, throwaway ircd.
2026-07-11 19:59:47 +00:00
f82ab0c70a
Scaffold the federated services daemon
Protocol-agnostic core: a Protocol trait maps raw server-to-server lines to a
normalized event/action model, so the engine never touches a raw line and a new
ircd is one new module. Adds event-log state (state is a fold over the log) and
a service framework. First protocol impl links to an InspIRCd (insp4) uplink —
completes the CAPAB/SERVER handshake, bursts, and introduces NickServ.
2026-07-11 19:28:30 +00:00