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.
This commit is contained in:
Jean Chevronnet 2026-07-11 20:28:42 +00:00
parent 7929f5f9f4
commit 9285105afd
No known key found for this signature in database
11 changed files with 348 additions and 41 deletions

View file

@ -1,39 +1,34 @@
use std::collections::HashMap;
// Live network view the services reason about.
// Live network view, rebuilt from the uplink's burst each connect (ephemeral —
// unlike the account store, which persists).
#[derive(Default)]
pub struct Network {
pub users: HashMap<String, User>, // keyed by UID
pub users: HashMap<String, User>, // keyed by UID
pub channels: HashMap<String, Channel>,
}
pub struct User {
pub uid: String,
pub nick: String,
pub account: Option<String>,
}
#[allow(dead_code)]
pub struct Channel {
pub name: String,
pub ts: u64,
}
// The Sable-inspired core: every persistent change is an Event, and state is a
// fold over the log. Single-node today; replicating this log across service
// nodes is what turns it federated later, without rewriting the services.
#[derive(Debug, Clone)]
pub enum Event {
AccountRegistered { account: String, uid: String },
ChannelRegistered { channel: String, founder: String },
}
impl Network {
pub fn user_connect(&mut self, uid: String, nick: String) {
self.users.insert(uid.clone(), User { uid, nick });
}
#[derive(Default)]
pub struct EventLog {
pub events: Vec<Event>,
}
pub fn user_quit(&mut self, uid: &str) {
self.users.remove(uid);
}
impl EventLog {
pub fn append(&mut self, event: Event) {
self.events.push(event);
pub fn nick_of(&self, uid: &str) -> Option<&str> {
self.users.get(uid).map(|u| u.nick.as_str())
}
}