Proof of concept mechanical port of ircnet/ircd to Rust as part of a bit about C being insecure for network services
0

Configure Feed

Select the types of activity you want to include in your feed.

P10 — Rename to leveva + typed identifier string types#

P10 is the idiomatic-Rust end state: the workspace is renamed to leveva and gains validating newtype strings for every IRC identifier class. Greenfield Rust — no cref_ oracle, no extern "C" seam, no ircd-sys dependency. Tests are ordinary Rust unit / doc tests.

Note: P10 is being started early, before P8/P9, at the user's request — the string foundation is useful to have in place. The crate/binary rename of the ircd-* crates is still deferred to the real P10 slot (after P8 deletes the C and P9 does the std-lib cleanup).

Entries#

  • 2026-06-07 — leveva crate bootstrapped: RFC 1459 string layer. New workspace member leveva/ (std-only, no deps). Foundation modules:

    • casemap — textbook RFC 1459 case folding: A-Z↔a-z plus the Scandinavian set [↔{ \↔| ]↔} ~↔^ (to_lower/to_upper const fns + eq_ignore_case/cmp/ hash byte-slice helpers). Deliberately differs from the faithful-port tolowertab in common/match.c, which is ASCII-only — confirmed by reading the C table; leveva implements the rule the RFC actually specifies (user decision).
    • stringIrcStr (#[repr(transparent)] over str, one localized unsafe cast like std Path) and IrcString (String), whose Eq/Ord/Hash are RFC 1459 case-insensitive while Display preserves original casing. IrcString: Borrow<IrcStr> so it keys a HashMap looked up by &IrcStr. Construction rejects the framing bytes NUL/CR/LF (IrcError::Forbidden). Plus IrcStringBuilder.
    • ident — validating newtypes around IrcString, generated by an ident_newtype! macro: Nick (do_nick_name rule: no leading -/digit, not anonymous, NVALID char class, NICKLEN 15), ChanName (#&+! prefix + chanstring, CHANNELLEN 50), Sid/Uid/ Cid (exact-length base-36 A-Z0-9: 4/9/5), ServerName, UserName (USERLEN 10), HostName (HOSTLEN 63). IdentError enum. Grammars grounded in common/struct_def.h, common/match.c char_atribs, ircd/s_user.c do_nick_name, ircd/s_id.c.
    • messageMessage/MessageBuilder: prefix/param(s)/trailing (each impl Display, so typed idents pass through) → to_line() and 512-byte-safe CRLF to_wire() (truncation respects UTF-8 char boundaries).
    • Gate: cargo test -p leveva — 27 unit + 4 doc tests green; cargo clippy -p leveva clean; 0 warnings. Each identifier type round-trips its legal inputs and rejects every illegal class (the inverse-invariant discipline). Plan: docs/superpowers/plans/2026-06-07-leveva-rfc1459-string.md.
  • 2026-06-07 — leveva::patricia: generic longest-prefix-match trie. Idiomatic, 100% safe Rust descendant of ircd/patricia.c (which was a CIDR-only, void*-valued, raw-pointer/MyMalloc trie). User decisions: generic core + IP helpers, idiomatic Rust API.

    • Generic over the value V (no void*) and over the key — a Prefix is just bytes + a bit length, so the trie serves IP CIDRs, MAC ranges, routing labels, any hierarchical bit key. Prefix masks insignificant trailing bits on construction, so 10.5.0.0/8 == 10.0.0.0/8 (correct network identity for Eq/Hash).
    • No unsafe, no raw pointers. Nodes live in a Vec<Option<Node>> arena with a free list; children/parent are Option<usize> indices. The C node->bit < maxbits OOB guard is subsumed by bit_test reading past-end bits as 0, which also makes mixed-width keys safe. Removed slots are recycled.
    • API: new/insert(→old Option<V>)/get/get_mut/contains/remove/ longest_match/iter (+ Default/FromIterator/Extend/IntoIterator/Debug). IP layer: Prefix::{from_ipv4,from_ipv6,from_ip,host}, IpCidr (FromStr: "10.0.0.0/8" or bare host; CidrParseError), insert_cidr/get_cidr/remove_cidr/ longest_match_ip(impl Into<IpAddr>).
    • Faithfulness: insert/search_exact/search_best(inclusive)/remove ported from the C algorithms incl. glue-node creation and the three-case removal (two-children → demote to glue; leaf → unlink + fold glue parent; one-child → splice up). Iteration is deterministic left-first pre-order (not claimed byte-identical to the C PATRICIA_WALK).
    • Gate: cargo test -p leveva — 17 patricia unit tests + 1 doc test green (full crate 41 unit + 5 doc); cargo clippy -p leveva --all-targets clean; 0 warnings. Tests pair every positive with its inverse (insert→remove→gone, glue fold + slot reuse, remove-all → empty, longest-match fallback after removal, 256-prefix stress with odd-removal).
  • 2026-06-07 — leveva::numeric: the Numeric reply/error enum. Pure-data port of the complete numeric table in common/numeric_def.h (184 codes, 001–759) into a single #[repr(u16)] #[non_exhaustive] enum whose discriminant is the three-digit code, so n as u16 / Numeric::code() is the wire value directly.

    • Naming: variants keep the Rpl/Err prefix with the remainder title-cased (RplWelcome, ErrNosuchnick). The prefix is load-bearing — dropping it would collide RPL_STATSKLINE (216) with ERR_STATSKLINE (499), the only such clash in the table. The canonical C symbol is always recoverable via Numeric::name() ("RPL_WELCOME").
    • API: code(), name(), is_error() (the 400599 convention), From<Numeric> for u16, TryFrom<u16> (→ UnknownNumeric(u16)), and a Display that renders the zero-padded three-digit wire token (001). RPL_WHOISTLS is exposed as an associated const aliasing RplWhoisextra (320), mirroring the C #define (a repr enum can't carry a duplicate discriminant).
    • Gate: cargo test -p leveva — 8 numeric unit + 1 doc test green; clippy clean; 0 warnings. Tests cover code==discriminant, three-digit padding, name round-trip, known/unknown TryFrom, the STATSKLINE 216-vs-499 distinction, and the WHOISTLS alias.
  • 2026-06-07 — leveva::mode: the UserMode and ChanMode enums. Pure-data port of the mode tables, grounded in ircd/s_user.c, ircd/channel.c, and common/struct_def.h.

    • UserMode — the six modes in user_modes[] (o O i w r a), bits from the FLAGS_* constants. as_char/TryFrom<char>, bit/from_bit, flag_name() ("FLAGS_OPER"), ALL in table order, and render(bits) (set letters, table order).
    • ChanMode — all 17 channel mode letters, categorised by ChanModeKind into Flag (p s m n t i a r), Membership (o v O), List (b e I R), and Param (k l). bit() returns the MODE_* value (membership bits double as the low CHFL_* bits); mode_name() recovers the C symbol; kind()/takes_parameter(); render(bits) emits only flag-kind letters in flags[] order (membership/list/param can't be reconstructed from a channel bitmask alone). MODE_QUIET (0x02000) is omitted — it has no mode letter and never appears on the wire.
    • Grounding caveats: +I (MODE_INVITE, invite-exception list) is kept distinct from +i (MODE_INVITEONLY); takes_parameter() documents that the oracle still omits the parameter specifically when removing +l (parse behaviour, left to the handler port).
    • Gate: cargo test -p leveva — 8 mode unit + 1 doc test green (full crate 56 unit + 7 doc); clippy clean; 0 warnings. Tests cover char/bit round-trips against the oracle values, the four ChanModeKind categories, and table-ordered render.
  • 2026-06-07 — leveva::config: KDL-based, typed ircd configuration. The first leveva module with dependencies (kdl v6 +v1-fallback and thiserror v2 — the exact versions iauth-rs pins, so the lockfile is shared). Replaces the C daemon's pipe-delimited single-letter config (the M/A/Y/P/I/O/C/N/H/L/K lines parsed in ircd/config_read.c + ircd/s_conf.c) with a KDL document parsed into a typed Config. Plan: docs/superpowers/plans/2026-06-07-leveva-config-kdl.md. User decisions: lives in leveva (deps are fine), idiomatic redesign (not a faithful line-mirror), pragmatic core scope.

    • Idiomatic schema, not a line-mirror: C+N merge into one connect block (send-password/accept-password) with H/L folded into a role "hub" "*" / role "leaf" property; I/O-line flag letters become named booleans; the O-line ACL_* flag string becomes an OperPrivilege enum (20 granular variants, grounded in struct_def.h:1043-1070 + the letter table in s_conf.c:306-383). Tokens "kill"/ "squit"/"connect" expand to local+remote; "all" matches the C A letter (everything bar clients/no-penalty/can-flood); ACL_LOCOP is the local field, not a privilege.
    • Typed by construction: identifier fields (server.name, server.sid, connect name) parse through leveva's ServerName/Sid newtypes, so a malformed value is a ConfigError::BadField at parse time. Glob/CIDR patterns (user@host, *.x) stay String.
    • Passwords classified, not carried verbatim: operator/connect/allow passwords are a Password enum (Hashed|Plain). You can't reliably distinguish a hash from plaintext (a 13-char alnum plaintext == a DES crypt(3) hash), so classification is conservative: a (hash)/(plain) KDL type annotation (read via entry.ty()) forces the choice — and a declared (hash) is structurally validated, so a typo'd hash is a BadField at load, not a failed login; with no annotation, a well-formed Modular Crypt Format value ($id$…) is auto-detected as a hash and everything else (incl. bare DES-13) is Plain. warnings() flags plaintext operator/link passwords. (The C makes this a compile-time choice — CRYPT_OPER_PASSWORD, off in this build — and uses the config password as the crypt() salt; per-password classification is a deliberate leveva improvement.)
    • Parsing mirrors iauth-rs/src/config.rs: src.parse::<KdlDocument>(), walk doc.nodes() by name, read each leaf as a child node's single positional arg; typed helpers (req_string/opt_string/flag/req_u32/…). Note kdl v6 as_integer() returns i128 (range-checked into u16/u32/u64). Scope: server, admin, options, class, listen, allow, operator, connect, ban. Cross-reference validation (every class named by allow/operator/connect is defined; singleton server/admin; unique class names) + non-fatal warnings().
    • KDL v2 gotcha: bare true/false is illegal in KDL v2 — it only parses via the fragile v1-fallback (which requires the whole document be valid v1, so it works intermittently). Canonical form is #true/#false; a bare node name also means true. The flag() helper + the example doc this.
    • Deferred (out of this cut): the rare line types (bounce B / version V / quarantine Q / deny D / gecos-X / service S / zip-connect c), a KDL writer for rehash round-trip, a Mask newtype, and wiring into the daemon boot path (belongs with the connection/rehash port — this module just parses → typed Config).
    • Gate: cargo test -p leveva — 26 config unit + 1 doc test green (full crate 82 unit
      • 8 doc); cargo clippy -p leveva --all-targets clean; 0 warnings. Worked example at leveva/examples/ircd.kdl (also the happy-path test fixture via include_str!); each error variant has a negative test, plus password classification (MCF auto-detect, (plain) override, malformed declared hash, unknown annotation).
  • 2026-06-07 — leveva::message gains a parser + leveva::matching glob/hostmask. The two halves of the protocol-text layer that were missing.

    • Message::parse / FromStr — the inverse of the existing builder: a raw line → optional :-prefix, command, space-separated middle params, optional :-trailing. Strips a trailing CRLF, tolerates runs of spaces, keeps a bare : as Some("") (distinct from no trailing), preserves the command verbatim (case left to a future Command enum), and round-trips with to_line(). ParseError::{Empty, MissingCommand}.
    • leveva::matching — descendant of match()/collapse() (common/match.c). matches(mask, name) supports *, ?, # (the IRCnet digit wildcard — so a literal # in a mask matches a digit; use \#), and \-escaping, case-insensitive under leveva's RFC 1459 casemapping (deliberate divergence from match.c's ASCII-only tolowertab, the same call the rest of leveva makes). Greedy * backtracking is linear-time, so the C MAX_ITERATIONS cap is unnecessary. collapse() minimizes redundant wildcards (*?*?*??).
    • HostMask — the nick!user@host pattern whose three components are matched independently (faithful to BanMatch, ircd/channel.c:49). parse() mirrors parseNUH (channel.c:1815): split on the first !, then the last @; with no !, the text before @ is the nick (a real gotcha — foo@bar ⇒ nick=foo, host=bar), and empty components default to *. matches_parts(nick,user,host) is the primary API (the server already has the three components); matches_target("n!u@h") is the convenience.
    • Gate: cargo test -p leveva — 8 message + 13 matching unit tests + their doc tests green (full crate 101 unit + 10 doc); clippy --all-targets clean; 0 warnings. Covers the parse round-trip, empty/extra-space/error cases, each wildcard + escape, the no-catastrophic-backtracking shapes, collapse minimization, and the parseNUH split edge cases.
  • 2026-06-07 — leveva::matching glob parsing moved to nom + a compiled Pattern. Replaced the hand-rolled byte-walker tokenizer with a nom parser (nom 7, the version ircd-common already pins) that compiles a mask into a Vec<Tok>. The token alt's final any-byte arm makes the parser total, so many0 consumes the whole mask without looping. New public Pattern type (compile once, is_match many) so a ban/host mask checked against many users isn't re-parsed per call; matches() is now the compile-once-match-once convenience over it. Matching semantics are unchanged (*/?/#/\, RFC 1459 case-insensitive, linear greedy-* backtracking); collapse() and HostMask untouched. Gate: full crate 103 unit + 11 doc green; clippy clean.