=== p8-started.md ===
---
name: p8-started
description: "P8 (delete C + retire oracle) COMPLETE 2026-06-08 (P8u, then P8v deleted the C source tree + ircd-sys); the variadic-trampoline rip-out mechanism + the full done-list"
metadata: 
  node_type: memory
  type: project
  originSessionId: b8e14360-5ea1-4ea5-acab-6d8a1784a927
---

**STATUS: P8 COMPLETE (2026-06-08, P8u — the final sub-phase).** Every ircd C TU is ported;
**`ircd-sys/build.rs` compiles zero C** (the `make`/`cref_*`/`ctruth.o`/`libircd_c.a`/whole-archive
link + `-lz -lm -lcrypt` all dropped — none needed; `pow` resolves from glibc 2.29+). `build.rs`
now only runs `configure` + bindgen over the C *headers* (the struct view `ircd-common` still uses
until P9–P12) + expands install-path Makefile vars via a `make_var()` helper. The L1/L2 oracle is
mothballed: `ircd-testkit`/`ircd-golden` are `exclude`d from the workspace (git-recoverable, not
deleted); `ircd-sys/ctruth.c` + `tests/layout.rs` (the bindgen-vs-real-compile drift-net) deleted.
Final differential run was green (L1 all pass; L2 86 pass + only the documented `s_serv_stats`
reference-C-garbage flake where Rust is correct — [[p5-s2s-stats-flake]]). Gate: `cargo build
--workspace` 0 warnings links pure Rust; `cargo test --workspace` 698 pass / 0 fail / 125 binaries.
The L1 characterization lives on as the `ircd-common/tests/*_snap.rs` insta snapshots ([[ircd-common-snapshot-tests]]).

**P8v (2026-06-08, the literal end of C) DELETED the C source tree AND `ircd-sys` itself.** P8u kept
`ircd-sys` alive to bindgen the C *headers*; P8v froze that generated `bindings.rs` (4953 lines,
bindgen 0.72.1, locked config) verbatim into a committed **`ircd-common/src/bindings.rs`** (`pub mod
bindings;`). **The whole tree's `ircd_sys::bindings::*` / `ircd_sys::MOTD_PATH` paths still resolve
unchanged** because `ircd-common/src/lib.rs` has `extern crate self as ircd_sys;` + `pub const`
install-path literals (the former `env!`-injected `MOTD_PATH`/`PID_PATH`/`USERLOG_PATH`/`CONNLOG_PATH`/
`IAUTH_PATH`/`IAUTH`/`SERVER_PATH`/`CONF_PATH`/`TUNE_PATH`/`CONF_PATH_Z`/`TUNE_PATH_Z`, frozen at the
`/usr/local/...` configure defaults). So **`ircd_sys` is now an alias for `ircd-common` itself** — there
is no `ircd-sys` crate. `ircd-rs` calls `ircd_common::ircd::c_ircd_main` directly. DELETED: the
`ircd-sys` crate, the mothballed `ircd-testkit`/`ircd-golden` oracle crates, `iauth-rs/tests/
differential_c.rs`, and ALL C source (`common/ ircd/ iauth/ support/ contrib/ cbuild/ configure
.clang-format* .github/workflows/clangformat.yml`). Workspace = 5 pure-Rust crates (`ircd-rs`,
`ircd-common`, `iauth-rs`, `leveva`, `leveva-integration`). **Not one line of C remains in the repo.**
Gate: `cargo build --workspace` 0 warnings (pure-Rust link); `cargo test --workspace` 695 pass / 0 fail.
Caveat for future work: `bindings.rs` is FROZEN, no longer regenerated — edit it by hand only with
extreme care (it mirrors the locked configure); the P9–P12 idiomatic passes replace these bindgen types
with native Rust field by field.

**Next = P9 (std-lib cleanup), verified against the snapshot suite.** History below.

P8 = the final phase that deletes the last C. At P7 exit only **data globals + ~25
variadic sender trampolines** remained (stable Rust can't define `extern "C"` variadics).
P8 replaces each trampoline with a **non-variadic Rust sender API** + converts its call
sites, then drops all `cc::Build` from `ircd-sys` and mothballs the L1/L2 oracle (tests
migrate into `ircd-common`). See [[rust-migration-plan]], [[p7-started]].

**Locked decisions (user, 2026-06-07):** rip out the cleanest trampoline first; build the
wire line faithfully NOW, adopt **leveva** typed `Message`/identifier newtypes LATER,
alongside the IRC senders. **Builder choice (P8b):** a `wire!`/`WirePart` **raw-byte** line
builder (in `ircd-common/src/send.rs`) is preferred over `format!` — `format!` goes through
a UTF-8 `String` (lossy on arbitrary comment/kill bytes → byte-diff risk); `wire!` reads C
strings as raw bytes (`cstr_bytes`) so user content passes verbatim. (`format!`+lossy `cs`
is acceptable too, as P8a's `ia_str` did; the user briefly pushed for `format!` then
retracted — `wire!` is fine.) `wire!` impls cover `&[u8;N]`/`&[u8]`/`*const`/`*mut c_char`/
`c_int` (`%d`). See [[project-name-leveva-p10]].

**Sender call-graph caveat:** the C variadic senders call EACH OTHER, so a target may not
be a graph leaf. Pick a sender with NO live C caller, or pull the coupled C caller(s) into
the same rip-out. (P8b: `sendto_ops_butone` was `sendto_serv_butone`'s only in-`send.c`
caller → porting serv_butone forced porting ops_butone to avoid an `undefined reference`.)
Check with `nm send_link.o | grep <sym>` (defined vs undefined) before scoping.

**The rip-out mechanism (per trampoline):** write a non-variadic Rust core (`fn
foo(line: &[u8])` or a typed sender) that does the C body's delivery logic; convert every
Rust caller `foo(c"fmt %s", args)` → `foo(format!("fmt {}", …).as_bytes())`; delete the
variadic `extern "C" { fn foo(…, ...) }` decls; `#ifndef PORT_*_P8x`-guard the C function
+ add the `-D` to the relevant `*_link.o` compile in `ircd-sys/build.rs`. The cref oracle
keeps the unguarded `.o` so `cref_foo` survives for the L1 byte-diff. Faithful-port
discipline still applies (replicate even C bugs; byte-identical wire output).

**Done:**
- **P8a — `sendto_iauth`** (s_auth.c:120-170). Non-variadic `pub unsafe fn
  sendto_iauth(&[u8])` + `ia_str(*const c_char)->String` in `ircd-common/src/s_auth.rs`;
  24 call sites (s_auth/s_user/s_bsd/ircd/res). `-DPORT_S_AUTH_SENDTO_IAUTH_P8a`. Cleanest
  first because `nm` showed ZERO live C callers (all in `#ifdef`'d-out bodies). L1
  `sendto_iauth_diff.rs` (socketpair byte-diff).
- **P8b — `sendto_serv_butone` + `sendto_ops_butone`** (send.c:517/989, the coupled
  server-broadcast pair). Non-variadic `pub unsafe fn sendto_serv_butone(one, body: &[u8])`
  (loop `local[fdas.fd[i]]`, prep ≤510+CRLF, `send_message` each) + `sendto_ops_butone(one,
  from, body: &[u8])` (`:from WALLOPS :body` via serv_butone + `sendto_flag(SCH_WALLOP)`
  still-C). 18 call sites built with `wire!`. `-DPORT_SEND_SERV_BUTONE_P8b`/
  `-DPORT_SEND_OPS_BUTONE_P8b`. L1 `sendto_serv_butone_diff.rs` (parallel `local`/`fdas` vs
  `cref_*` worlds, sendQ byte-diff; QUIT/trunc/uplink-exclusion/WALLOPS); L2-S2S = existing
  `golden_s2s_{away,quit,nick,kill,wallops,membership,join,save,s_serv_connect,squit}`.

- **P8c — `sendto_serv_v`** (send.c:541-572, the `serv_butone` sibling). Same
  `local[]`/`fdas` loop; `ver` param is dead (`#if 0`'d version gate) → always returns 0.
  Non-variadic `pub unsafe fn sendto_serv_v(one, _ver, body: &[u8]) -> c_int` (clones
  serv_butone, reuses `prep_line`); 10 call sites (ircd s_die / channel NJOIN / s_user
  KILL+SAVE / s_serv 4×EOB+SDIE relay+m_sdie) built with `wire!`. NO live C caller → ripped
  out alone. `-DPORT_SEND_SERV_V_P8c`. L1 `sendto_serv_v_diff.rs`; L2-S2S already covered by
  `golden_s2s_{save,join,membership,kill,s_serv_connect,s_serv_squit}`.

- **P8d — `sendto_match_servs` + `sendto_match_servs_v`** (send.c:790-876, the channel-mask
  server-broadcast pair). Same `local[]`/`fdas` loop but: skip `cptr == from` *directly*
  (NOT `one->from`), `&`-leading channel returns at once, and a `chan:server-mask`
  (`get_channelmask` = `libc::strrchr(name,':')`, JAPANESE off) filters recipients via
  `match_`. `_v`'s `ver` gate is dead `#if 0` → always rc = 0. Clean leaves, ripped out as a
  pair; all 12 call sites are in channel.rs (TOPIC/PART/JOIN×2/MODE+o + KICK×3/NJOIN×3/MODE)
  built with `wire!`; cores reuse `prep_line`. `-DPORT_SEND_MATCH_SERVS_P8d`. L1
  `sendto_match_servs_diff.rs` (5 cases incl. the mask-filter inverse); L2-S2S covered by
  existing `golden_s2s_{topic,kick,membership,mode,njoin,join}`.

- **P8e — `sendto_prefix_one`** (send.c:1017-1027, the first `vsendpreprep`-based prefix
  sender). Clean leaf: NO in-send.c caller (internal paths use the `static
  vsendto_prefix_one`; only external now-Rust callers reach the public variadic).
  Introduces the **`prep_preprep` Rust core** = faithful `vsendpreprep` (send.c:388-419,
  IRCII_KLUDGE off): special path (`to && from && MyClient(to) && IsPerson(from)`)
  collapses the `:%s` prefix arg `par` to `:from->name!user@host` when `mycmp(par,
  from->name)==0`, else `:par`; non-special emits `:par`+rest verbatim; reuses `prep_line`.
  Two faithfulness reductions: every caller leads with `:%s` so the C `!strncmp(...,":%s",3)`
  guard reduces to the to/from test (caller supplies `par`+pre-formatted `rest`); the
  `from == &anon` disjunct is unreachable from Rust (only still-C channel senders pass
  `&anon`, they keep using C `vsendpreprep`) → encoded only the `mycmp` arm, anon lands
  with those senders. `pub unsafe fn sendto_prefix_one(to, from, par: *const c_char, rest:
  &[u8])`; 10 call sites (s_numeric numeric relay; channel INVITE×2; s_user
  PRIVMSG/NOTICE×4 + KILL) build `rest` with `wire!`. The static `vsendto_prefix_one` +
  `vsendpreprep` STAY C. `-DPORT_SEND_PREFIX_ONE_P8e`. L1 `sendto_prefix_one_diff.rs`
  (collapse / no-collapse / non-special verbatim / >510 trunc; **gotcha:** NUL-terminate
  fixtures before `strcpy` or you overrun `namebuf` → heap corruption — use a bounded
  `set_cstr`); L2 = existing `golden_channel_invite` / `golden_s_user_message` /
  `golden_s2s_message` / `golden_s_user_kill` / `golden_s2s_kill`.

- **P8f — `sendto_match_butone`** (send.c:935-987, the `$$`/`$#`-mask broadcast prefix
  sender). Clean leaf: NO in-send.c caller, ONE call site (`m_message` in s_user.rs).
  REUSES the P8e `prep_preprep`/`sendto_prefix_one` core verbatim — delivery is
  `vsendto_prefix_one(cptr, from, …)` with the **real** `from` (NEVER `&anon`), so no anon
  machinery needed (that's the channel senders' problem). `pub unsafe fn
  sendto_match_butone(one, from, mask, what, par, rest: &[u8])` walks `i=0..=highest_fd`
  over `local[i]`, skip NULL + origin `one`; server recipient → walk `cptr->prev` for first
  `is_registered_user(srch) && srch->from==cptr && match_it(srch,mask,what)`; non-server →
  require client itself match; each survivor → `sendto_prefix_one`. Added `match_it`
  (MATCH_HOST=2→`user->host` array, MATCH_SERVER=1→`user->server` ptr; `match_`==0 on match)
  + `is_registered_user` (= `is_person`). `-DPORT_SEND_MATCH_BUTONE_P8f`. L1
  `sendto_match_butone_diff.rs` (4 cases incl. inverses; **gotcha:** NUL-terminate `mask`/
  `par` fixtures — bare `b"*.ok"` overran → `match_` missed); L2 = existing
  `golden_s_user_message`/`golden_s2s_message`.

- **P8g — `sendto_channel_butone` + the `anon` machinery** (send.c:459-510). The channel
  broadcaster + the FIRST anon-needing sender. Ported `anon`/`ausr`/`initanonymous` from C
  to Rust as `#[no_mangle] pub static mut` globals (`MaybeUninit::zeroed`) + an extern-C
  `initanonymous`; the still-C `vsendpreprep`/`sendto_channel_butserv` reference them via
  `extern` decls (guarded by `-DPORT_SEND_ANON_P8g`, replacing the `static` defs with
  `extern`). `prep_preprep` grew the `from == anon_ptr()` collapse arm. The non-variadic
  core routes BOTH the C raw-server (`vsendprep`) and prefixed-MyClient (`vsendpreprep`)
  paths through the P8e `sendto_prefix_one` core — byte-identical because `prep_preprep`
  already branches on `my_client(to)` (server → else-path `:par`, MyClient → collapse; a
  local STAT_CLIENT always has `user` so the MyConnect-non-eligible case is never MyClient).
  7 call sites (s_numeric/channel×5/s_user) build `rest` with `wire!`.
  `-DPORT_SEND_CHANNEL_BUTONE_P8g`. L1 `sendto_channel_butone_diff.rs` (parallel
  Rust/cref channels — channel passed by arg, no global; 4 cases: broadcast
  collapse/verbatim/IsMe-skip, `one` exclusion+inverse, self-send-not-doubled, anon-channel
  collapse; **gotchas:** cref's global `sendbuf`/`psendbuf` + `anon` globals → `GLOBALS_LOCK`
  serialize; `STAT_ME == -1` not -2); L2 = channel join/part/kick/topic/mode_set/invite +
  s_user_message + s2s message/join/kick/topic/mode.

- **P8h — `sendto_common_channels`** (send.c:620-729, the local-channel-peers broadcaster
  for NICK/QUIT). Clean leaf, NO C caller, **no anon** (skips anonymous channels → `from`
  always the real `user`). Key faithfulness call: C preps the line ONCE (`if (!len)`,
  DEBUGMODE off) into `psendbuf` + reuses it for all recipients → ported as a local
  `[u8;520]` + `len`, calling `prep_preprep` only when `len==0`, NOT `sendto_prefix_one` per
  recipient (which re-preps against each `to`) — matters in the big-client branch where a
  remote (`fd==-1`, not MyClient) `clist` member still receives the cached FIRST-recipient
  line. Both Comstud branches ported (`highest_fd<50` HUB `IsMember`-over-`user->channel` +
  the `>=50` `clist`/`sentalong` walk). `sentalong` is a per-call `vec![0;MAXCONNECTIONS]`
  indexed via raw `.offset((*cptr).fd as isize)` (matches C's `fd==-1` OOB pointer arith, no
  Vec panic). New private `is_member`/`is_quiet` (via P2 `find_channel_link`). `pub unsafe fn
  sendto_common_channels(user, par: *const c_char, rest: &[u8])`; 3 call sites (s_misc
  exit_client QUIT; s_user nick-save + m_nick NICK) → par + `wire!` rest.
  `-DPORT_SEND_COMMON_CHANNELS_P8h` (the now-unused `sentalong` static guarded too). L1
  `sendto_common_channels_diff.rs` (parallel rust/cref `local[]`+`highest_fd` worlds w/
  per-client `user->channel` Link lists, 4 cases: shared-channel broadcast+self-send+inverse,
  quiet-skip, anon-skip, remote-user no-self-send; `GLOBALS_LOCK` serializes); L2 =
  `golden_s_user_nick`/`golden_s_user_quit` + S2S `golden_s2s_nick`/`golden_s2s_quit`.

- **P8i — `sendto_flog` + `logfiles_open`/`logfiles_close` + the `userlog`/`connlog` fd
  statics** (send.c:1202-1440). The FIRST **non-variadic** leaf — `void sendto_flog(cptr,
  char msg, username, hostname)`. Ported the whole connected component over the shared
  file-private fds (the logger + its open/close pair + the two statics) to
  `ircd-common/src/send.rs`; `setup_svchans`/`svchans` (a *separate* component shared with
  the still-C variadic `sendto_flag`) stay C. **Formatting = byte-identical by
  construction:** `libc::sprintf` with the exact C format string `"%c %d %d %s %s %s %s %d
  %s %lu %llu %lu %llu "` + args cast to C's varargs promotions (`%c`/`%d`→`c_int`,
  `%lu`→`c_ulong`, `%llu`→`c_ulonglong`, `(u_int)firsttime`/`(u_int)timeofday` via `as u_int
  as c_int`) — same libc engine + same fmt ⇒ no logic to drift. Config: `LOG_OLDFORMAT`/
  `USE_SYSLOG`/`USE_SERVICES`/`LOG_SERVER_CHANNELS`/`LOGFILES_ALWAYS_CREATE` all OFF (only the
  new-format `#else` + the `logfile==-1` early-return + a no-`O_CREAT` open compile).
  `FNAME_USERLOG`/`FNAME_CONNLOG` reach Rust via new `IRCD_USERLOG_PATH`/`IRCD_CONNLOG_PATH`
  rustc-envs (the `PID_PATH` make-expand trick) → `ircd_sys::{USERLOG_PATH,CONNLOG_PATH}`
  consts. `-DPORT_SEND_FLOG_P8i`. L1 `sendto_flog_diff.rs` = the **write_pidfile pattern**:
  both worlds open the same hardcoded log path so drive open→flog→close in rust+cref, read
  back, assert identical bytes; sandbox `/usr/local/var/log` is unwritable → both no-op
  (`None==None`), bytes pinned only where writable. **No L2/S2S** — file-only, `USE_SERVICES`
  off → never on the wire; boot smoke via any golden (`logfiles_open` runs at every boot).
  1 live `sendto_flog` caller (s_bsd `add_connection` clone-reject).

- **P8j — `sendto_flag` (the keystone) + `dead_link`** (send.c:1141/50). The server-notice
  broadcaster, ~220 call sites across 16 files → non-variadic `sendto_flag(chan, body: &[u8])`
  that clamps `chan`, looks up the now-Rust `svchans[]` (connected component `svchans` +
  `setup_svchans` ported too) and wraps `:me NOTICE <chname> :<body>` via the still-C variadic
  `sendto_channel_butserv`. `dead_link` (its last C caller) ported alongside. WirePart grew
  `%u`/`%08x`/`%x`/`%X`/`%#x`/`%02d`/`%2d`/`%c`/`%p` newtypes (`Hex08`/`Hex`/`HexUp`/`HexAlt`/
  `Dec02`/`Dec2`/`Chr`/`Ptr`); integers via `format!` (byte-identical), `%#x`/`%p` via libc.
  `-DPORT_SEND_FLAG_P8j -DPORT_SEND_DEAD_LINK_P8j`. L1 `sendto_flag_diff.rs` (4) + 16 golden.

- **P8k — `sendto_channel_butserv`** (send.c:746-780, the channel local-members broadcaster).
  Unblocked once P8j made `sendto_flag` (its only in-send.c caller) Rust. A clean leaf like P8g
  minus the server broadcast: only ever reaches MyClient members → constant `lfrm` → the
  per-recipient P8e `sendto_prefix_one` core is byte-identical to the C `psendbuf`/`if(!len)`
  cache (no cache to replicate). `pub unsafe fn sendto_channel_butserv(chptr, from, par, rest)`;
  14 call sites (12 channel.rs, 1 s_misc anon-PART, the sendto_flag wrapper). Its now-orphaned
  statics `vsendpreprep` + `vsendto_prefix_one` + the `psendbuf` buffer go with it under the
  same guard (channel_butserv was their last unguarded caller); `vsendprep`/`sendbuf` stay live
  (`vsendto_one`); the `anon`/`ausr` externs stay (harmless). `-DPORT_SEND_CHANNEL_BUTSERV_P8k`.
  L1 `sendto_channel_butserv_diff.rs` (4 cases — local+inverse/quiet/anon/remote; the anon case
  needs `initanonymous`+`cref_initanonymous` to populate both worlds' separate `anon` so
  `IsPerson(&anon)` holds) + 14 golden (channel + s2s) byte-identical.

- **P8l — `sendto_one` (THE KEYSTONE)** (send.c:450-468). The single-client sender behind
  ~every numeric reply; 435 call sites across 15 ircd-common files. Non-variadic core
  `pub unsafe fn send::sendto_one(to, body: &[u8]) -> c_int` = `vsendprep` (libc-`vsprintf`
  ≤510 trunc + CRLF into a local `[u8;520]`) + `send_message`. Two call-site builders: `wire!`
  for the 58 `c"..."` literal sites; a NEW `reply_one!(to, fmt, args...)` macro (send.rs) for
  the 377 `reply(...)`/runtime-pattern sites — renders via libc `snprintf` into a 2048-byte buf
  then hands the C string to the core. The 2 `rlen += sendto_one(...)` (channel.rs) call the
  core directly. Guarded `-DPORT_SEND_ONE_P8l` (sendbuf+vsendprep+vsendto_one+sendto_one).
  Converted by a 15-agent per-file ultracode workflow (edit-only, central verify). L1
  `sendto_one_diff.rs` (5 cases, GLOBALS_LOCK for the cref_sendbuf/dbuf-freelist race) + full
  111-test golden byte-identical bar the 2 known [[p5-s2s-stats-flake]] STATS garbage flakes.

  **KEY CORRECTION to the earlier "blocked on leveva typed-numerics (P10)" claim:** that
  overstated it. `reply(i)` with a CONSTANT `i` (the ~430 `reply(ERR_X)` sites) has a
  compile-time-known format — NOT runtime. Only ~6 sites are genuinely-runtime `reply(var)`
  (`reply(num)`/`reply(cj)`/`reply(rpl)`/`reply(tmp_rpl)`/`reply(REPORT_ARRAY[idx][1])`). And
  the delivery path `vsendprep` formats via **libc `vsprintf`** (NOT the custom `irc_vsprintf`),
  so the `reply_one!`/`snprintf` bridge is byte-identical for ALL sites incl. the runtime ones.
  ⇒ sendto_one was rippable NOW via wire!+snprintf, no P10 numeric layer needed. The idiomatic
  leveva-`Numeric` conversion of the reply sites stays P11. See [[project-name-leveva-p10]].

- **P8m — the `esendto_*` cluster** (ircd/s_send.c — the LAST variadic sender trampolines, the
  UID-aware service-routing senders `esendto_one`/`esendto_serv_butone`/`esendto_channel_butone`/
  `esendto_match_servs` + the `esend_message`/`build_old_prefix`/`build_new_prefix`/`build_suffix`
  machinery + 4 buffers/6 length ints). USE_SERVICES OFF → **ZERO callers** in both C and Rust →
  no call-site conversion, **no L2 path** (L1 is the gate). Faithful non-variadic
  `ircd-common/src/s_send.rs`: variadic `fmt,...`→`suffix: &[u8]`; `:%s %s %s` prefixes via
  `libc::sprintf` (byte-identical incl. glibc `(null)` for the NULL `oname` build_new_prefix can
  pass — same libc-for-format-semantics call as P8j/P8l); all quirks replicated (maxplen+slen>512
  suffix truncation, UID-selection + newplen=-1 bail, hardcoded `:anonymous!…` local prefix,
  &-channel return + match() mask filter; loop-index decrement placed to keep C `for(;i--)` under
  `continue`). The 4 senders are plain `pub unsafe fn` (NOT extern "C": `&[u8]` not FFI-safe, zero
  callers). **`s_send.o` dropped OUTRIGHT** (added to `PORTED`, no `s_send_link.o`, no `-DPORT_*` —
  cref keeps the unguarded .o in CREF_OBJS for L1). L1 `s_send_diff.rs` 9/9 (branches+inverses) +
  golden_registration byte-identical. Done via a 5-agent ultracode workflow (1 impl + 3 adversarial
  faithfulness audits, all "faithful" / 0 findings + 1 golden smoke).

**ALL variadic sender trampolines are now Rust (P8a–P8m).** (The P8l-era "all senders done" note
was wrong — it missed s_send.c's `esendto_*`; P8m closed that.) P8 then turned to the residual C
**data globals** (NON-sender):

- **P8n — the `s_auth.c` iauth data globals (the FIRST data-global drop) → `s_auth.o` dropped
  outright.** `nm --defined-only s_auth_link.o` showed it defined ONLY the five iauth globals
  (`iauth_options` u_char / `iauth_spawn` u_int / `iauth_version` char* / `iauth_conf`/`iauth_stats`
  bindgen-`LineItem*`) + module-private `rcsid` (every fn already `-DPORT_S_AUTH_*`-guarded out
  P7u..P8a). Moved them to Rust-owned `#[no_mangle] pub static mut` statics in
  `ircd-common/src/s_auth.rs` (zero/NULL-init = the C BSS defs). **DATA-symbol seam = the
  function-symbol seam:** the OTHER modules (ircd.rs/s_user.rs/s_bsd.rs) keep their
  `ircd_sys::bindings::iauth_options`/`iauth_spawn` imports — those bindgen `extern "C" { static mut … }`
  *decls* resolve to the Rust *defs* at link (proven for hundreds of fns + for data via `anon`/`ausr`
  P8g, `svchans` P8j). **Surgery confined to s_auth.rs itself:** you MUST drop the colliding
  `use ircd_sys::bindings::iauth_options` + the two local `extern "C"` decls (iauth_conf/stats + the
  separate iauth_version block) and reference the new module defs (else same-module name clash).
  build.rs: `s_auth.o`→`PORTED` (no `s_auth_link.o`; deleted the whole `-DPORT_S_AUTH_*` second-compile
  step + the dead `… => "s_auth_link.o"` map-arm); cref keeps unguarded `s_auth.o` for L1. **Gate for a
  pure-data drop (no behavioral L1 diff of the globals themselves):** lean on the existing differentials
  that exercise them end-to-end — `read_iauth_diff` is load-bearing (drives Rust `read_iauth`, which
  mutates all five, vs `cref_read_iauth`) + iauth_reporters/sendto_iauth/start_iauth diffs + `nm` (all 5
  = Rust BSS, none `U`) + `golden_registration`. 0 warnings.

- **P8o — `send.o` dropped outright (the second data-global drop, the trivial one P8n flagged).**
  `send.c` is now FULLY ported: over P3d + P8b..P8l every sender + `dead_link` + the `sendto_flog`
  logfile cluster + the `svchans`/`setup_svchans` component + the `anon`/`ausr`/`initanonymous`
  machinery were `#ifdef`'d out of `send_link.o` and reimplemented in `ircd-common/src/send.rs`,
  leaving `nm --defined-only cbuild/send_link.o` = just `rcsid` (lowercase `r`, internal-linkage
  static version string, unreferenced). So `send.o`→`PORTED`; deleted the whole `send_link.o`
  second-compile `run(make … --eval=…)` step + the `-DPORT_SEND_P3 .. -DPORT_SEND_ONE_P8l` guard
  chain + the `"send.o" => "send_link.o"` link-map arm in build.rs. cref keeps the unguarded `send.o`
  for the L1 send oracle. The Rust `logfiles_open` still reads FNAME_* via the IRCD_*_PATH rustc-envs
  (unaffected). **Gate for a pure inert-drop:** the existing 14 send L1 differentials (57 tests,
  zero-diff) + `golden_registration` (2) byte-identical + `nm` (logfiles_open/svchans = Rust defs,
  no undefined send symbols). 0 warnings. (`sendto_one`/`sendto_flag` are now plain Rust `fn`s, not
  C-ABI symbols, so they don't appear in `nm` — their callers are all Rust.)

- **P8p — `s_bsd.o` dropped outright (the third data-global drop).** All of s_bsd.c's *logic* went
  Rust over P7d..P7ff; `nm --defined-only s_bsd_link.o` then showed it defined ONLY the event-loop
  data globals (`local` aClient*[MAXCONNECTIONS=50] / `fdas`/`fdall` FdAry / `highest_fd`/`readcalls`
  int / `udpfd`/`resfd`/`adfd` int=-1 / `timeofday` time_t / `mysk` sockaddr_in6, P7s-de-static'd) +
  module-private `rcsid`. Moved all ten to `ircd-common/src/s_bsd.rs` as `#[no_mangle] pub static mut`
  with byte-exact C inits (`udpfd`/`resfd`/`adfd = -1` → `.data` D; rest → BSS B; `fdas`/`fdall =
  FdAry{fd:[0;50],highest:0}`; `mysk = MaybeUninit::zeroed`). DATA-symbol seam (= P8n): the other
  modules keep their `ircd_sys::bindings::{local,highest_fd,fdas,…}` extern refs → resolve to these
  defs at link; s_bsd.rs's own full-path `bindings::X`/`b::X` reads resolve the same (distinct paths,
  no clash). **Two in-module clears REQUIRED:** drop the old `extern "C" { static mut mysk }` decl
  (get_my_name block), and rename `read_message`'s `let local = local_base()` → `local_p` (E0530: a
  `let` can't shadow the new static; also pruned `adfd`/`highest_fd` from start_iauth's fn-local
  `use`). build.rs: `s_bsd.o`→`PORTED`; deleted the whole `s_bsd_link.o` second-compile (the long
  `-DPORT_S_BSD_LEAF_P7d..READ_MESSAGE_P7ff` chain + IRCDPID/IAUTH path defines) + the `"s_bsd.o" =>
  "s_bsd_link.o"` link-map arm. Rust write_pidfile/start_iauth still read paths via IRCD_PID_PATH/
  IRCD_IAUTH_PATH/IRCD_IAUTH rustc-envs (unchanged); cref keeps unguarded `s_bsd.o` for L1.
  **Gate (pure-data drop):** the existing s_bsd L1 differentials that exercise the globals end-to-end
  (read_message/add_connection/close_*/list[add_fd/del_fd]/s_bsd_leaves/connect_server/check_client/
  setup_ping/send_ping/check_pings/get_my_name[mysk]/init_sys/daemonize/start_iauth/delayed_kills/
  calc_pref) zero-diff + the cross-module wire seam (sendto_one/flag/common_channels read `local`) +
  `golden_registration` byte-identical; `nm` (all 10 = Rust B/D, none `U`); 0 warnings.

- **P8q — `ircd.o` dropped outright (the FOURTH data-global drop, completing ircd.c).** All of
  ircd.c's *logic* went Rust over P7c..P7oo; `nm --defined-only ircd_link.o` then showed NO `T`
  symbols — only the data globals + module-private `rcsid`. Moved them to `ircd-common/src/ircd.rs`
  as `#[no_mangle] pub static mut` with byte-exact C inits: `me` (aClient) + `istat`/`iconf` via
  `MaybeUninit::zeroed`; `client = core::ptr::addr_of_mut!(me)` (const); `portnum`/`debuglevel = -1`,
  `serverbooting = 1`, `bootopt = (BOOT_PROT|BOOT_STRICTPROT) as c_int`, the six `=1` timers
  (`nextconnect`/`nextgarbage`/`nextping`/`nextexpire`/`nextiarestart`/`nextpreference`) → `.data`;
  `rehashed`/`firstrejoindone`/`myargv`/`sbrk0`/`ListenerLL`/the `=0` timers/the three signal flags
  → BSS. **The `me` self-pointer hazard ([[ircd-migration-hazards]]) was a NON-issue at definition:**
  the interior `me.name → me.serv->namebuf` pointers are set at *runtime* by the already-ported
  `make_server`/`setup_me`, so the static is just a zeroed `aClient` (the C BSS def) — no special
  handling needed; the "never move/copy `me` by value" invariant is already honored tree-wide via
  `addr_of_mut!(me)`. **DATA-symbol seam (= P8n/o/p):** other modules keep their
  `ircd_sys::bindings::{me,client,istat,…}` extern refs → resolve to these defs at link. **The 5
  non-bindgen flags/timers** (`dorehash`/`dorestart`/`restart_iauth`/`nextpreference`/`nextiarestart`,
  absent from any `*_ext.h`) were reached via two local `extern "C"` blocks in ircd.rs (signal-handler
  cluster + inside `io_loop`) — BOTH removed; now module statics, bare refs resolve directly.
  **Import-clash surgery (REQUIRED, same as P8n):** ircd.rs both imported AND now defines these, so
  trim the three `use ircd_sys::bindings::{…}` lists of the now-defined names (`bootopt`/`iconf`/`istat`/
  `me`/`myargv`/`rehashed`/`sbrk0`/`tunefile` @block20; the six `next*` @block34;
  `configfile`/`serverbooting` in the inner `c_ircd_main` `use`). **Path defaults:** `configfile =
  IRCDCONF_PATH` / `tunefile = IRCDTUNE_PATH` via new `IRCD_CONF_PATH`/`IRCD_TUNE_PATH` rustc-envs
  (the make-expand trick) → NUL-terminated `ircd_sys::CONF_PATH_Z`/`TUNE_PATH_Z` consts (the `concat!`
  + `env!` MUST run in ircd-sys, not the consumer) → `.as_ptr() as *mut c_char`. build.rs: `ircd.o`→
  `PORTED`; deleted the `ircd_link.o` second-compile (the long `-DPORT_IRCD_TUNE_P7c..MAIN_P7oo` chain
  + the `-DIRCD*_PATH` machine paths) + the `"ircd.o" => "ircd_link.o"` link-map arm; cref keeps the
  unguarded `ircd.o` for the L1 ircd differentials. **Gate (pure-data drop):** the ircd L1 diffs that
  exercise the globals (try_connections/check_pings/calculate_preference/delayed_kills/ircd_signals
  [writes dorehash/dorestart/restart_iauth]/ircd_tune/ircd_cli_helpers/setup_signals/
  activate_delayed_listeners) zero-diff + `golden_registration` byte-identical; `nm target/debug/ircd`
  (all ~26 = Rust B/D, none `U`, correct .data/.bss split); 0 warnings.

- **P8r — `support.o` dropped outright (the LAST C-logic TU).** `nm support_link.o` had shown only
  `dgets`/`make_isupport`/`snprintf_append` (`T`) + `ipv6string`/`minus_one` (data) + private `rcsid`.
  Ported `dgets` (the `\r`/`\n` line reader w/ `\`-continuation splice — `static dgbuf[8192]`+head/tail
  as byte OFFSETS into a module-private static, the de-escape loop's `t = s` resume-from-post-splice
  replicated verbatim) + `make_isupport` (005 ISUPPORT token strings; config macros baked as consts
  `MAXMODEPARAMS=3`/…/`CHIDLEN=5`/`BUFSIZE=512`; `tis[1]` built byte-faithfully w/ raw `networkname`
  copy) to `ircd-common/src/support.rs`, + defined `ipv6string`/`minus_one` as `#[no_mangle] pub static
  mut` (DATA-symbol seam, = P8n/o/p/q). **`snprintf_append` NOT ported** — its only caller (s_user.c
  WHOX) is already a field-by-field `Vec<u8>` builder in s_user.rs → ZERO live Rust callers → it died
  with the `.o` (stable Rust can't define variadic `extern "C"` anyway; `irc_sprintf` likewise
  deleted-not-ported in P11). build.rs: `support.o`→`PORTED`; deleted the `support_link.o`
  second-compile (`-DPORT_SUPPORT_P1 -DPORT_SUPPORT_P2`) + the `"support.o" => "support_link.o"`
  link-map arm → `link_objs()` is now a plain `CREF_OBJS \ PORTED` set difference (NO `*_link.o`
  variants left anywhere); cref keeps unguarded `support.o` for L1. Gate: new L1 `dgets_matches_reference`
  (pipe-driven) + `make_isupport_matches_reference` zero-diff; full `support_diff` (7) zero-diff;
  `golden_registration` (2) byte-identical; seam reads via `s_conf_match_ipmask_diff`/`res_gethost_diff`
  zero-diff; `nm` (dgets/make_isupport=`T`, ipv6string=`B`, minus_one=`D`, snprintf_append absent);
  0 warnings.

**MILESTONE — every ircd C TU is now Rust.** After P8r, `CREF_OBJS \ PORTED` is EMPTY. After P8t
(below) the product link set (`libircd_c.a`) holds only the L1 harness's `ctruth.o`. All ircd logic
AND data is Rust.

- **P8t — `version.c` ported to Rust (the LAST *generated* C TU) → `version.o` no longer built.**
  `version.c` (generated by `ircd/version.c.SH`) is pure data: `generation`/`creation`/`pass_version`
  (char*), `infotext[]` (char*[], NULL-term), `isupport` (char**, runtime-filled) + private `rcsid`.
  NOT in `CREF_OBJS` (it was archived straight into `libircd_c.a`) → no `cref_*`, no L1 differential.
  Moved all five to `#[no_mangle] pub static mut` in new `ircd-common/src/version.rs` (+
  `VERSION_LINK_ANCHOR` in `lib.rs::link_anchor`); DATA-symbol seam (= P8n–q): consumers
  (`s_serv` m_info walks `infotext`; `s_user` RPL_CREATED; `s_debug`/`s_bsd`) keep their
  `ircd_sys::bindings::*` extern refs → resolve to the Rust defs at link. `infotext`'s bindgen decl
  is incomplete `[*mut c_char; 0]`; the real len (46) lives only in the Rust def, walked via
  `addr_of!`. **Version sourcing (user-directed): crate metadata at build time** — `generation` =
  `env!("CARGO_PKG_VERSION")`, `creation` = `env!("IRCD_BUILD_CREATION")` from a NEW
  `ircd-common/build.rs` (formats `SystemTime::now()` to the C-like `Wkd Mon D YYYY at HH:MM:SS UTC`
  shape, dependency-free Hinnant civil-from-days, UTC, pinned via lone `rerun-if-changed=build.rs`).
  `pass_version` = bindgen `PATCHLEVEL` const; `infotext[]` copied verbatim. build.rs: deleted the
  `version.c.SH` gen step + the `version.o` compile + `ar.arg("version.o")` → `libircd_c.a` is now
  `ctruth.o`-only. **Canonicalizer:** `creation` is now genuinely build-time volatile (ref-C
  version.o stamp vs Rust crate-metadata stamp differ), so added a 003 RPL_CREATED mask to
  `ircd-golden::canonicalize` (`:This server was created <MASKED>`) — the analogue of the existing
  371 Birth-Date mask. No `.snap` captures the value (the `s_err` replies table holds the static
  format *template* `":%s 003 %s :This server was created %s"`, unaffected). Gate: 0 warnings; `nm`
  all 5 = Rust D/B none `U`, `version.o` absent (`ar t` = ctruth.o only); `ircd-common` snapshots
  green; golden 58 pass + the 1 known [[p5-s2s-stats-flake]].

- **P8s — all 115 L1 `cref_` differentials migrated to self-contained `ircd-common` `insta`
  snapshot tests** (the "migrate load-bearing tests" item). Each `ircd-testkit/tests/<n>_diff.rs`
  now has an `ircd-common/tests/<n>_snap.rs` driving ONLY the Rust port (`#[no_mangle]` syms via
  `ircd_common::link_anchor()`; NO `cref_`, NO `ircd_testkit`). `insta = "1"` added as a dev-dep.
  **Soundness = the capture chain:** step 0 confirms the matching differential is green (Rust==cref)
  BEFORE capturing, so `snapshot==cref` golden — the reference-C correctness is frozen forward with
  no C oracle. Dual-World diffs collapse to a single Rust world over the same corpus/branches/inverses.
  **Determinism rules:** NEVER snapshot raw pointers/addresses/timestamps/fd-numbers/ephemeral-ports/
  HashMap-order; self/interior-pointer asserts → derived invariants (bool `name==namebuf`, offsets,
  list-walk sequences, buffer bytes); keep clock interposition + global `Mutex`es. Verified by 2 clean
  re-runs/file + 3 full-suite runs (stable). Built: hand-proved `s_err`/`s_id` → 6-tier pilot →
  multi-agent ultracode Workflow (107 files; the first 107-wide burst tripped the org token-rate limit,
  64 agents 429'd → redone in throttled batches of 8, zero fails). Guide:
  `docs/superpowers/plans/2026-06-08-p8-test-migration-guide.md`. `dead_code` on snapshot-shape structs
  (read only via `#[derive(Debug)]`) silenced with file-level `#![allow(dead_code)]` in 34 files.
  Gate: 115 `_snap.rs` / 707 `.snap`, 1:1 coverage, suite green + stable, `cargo build --workspace`
  0 warnings. **`ircd-testkit`/`cref_`/`ircd-golden` left INTACT** (still the differential proof) —
  mothballing them is part of the remaining work below.

P8's remaining work (after P8t): drop the LAST `cc::Build` C from `ircd-sys` — the `ctruth.o` layout
exporter + the bindgen-over-`wrapper.h` C invocation — then mothball the L1/L2/cref oracle
(`ircd-testkit`/`cref_*`/`ircd-golden`) after a final full green run (the `ircd-common` snapshot suite
is the replacement). NOTE: dropping bindgen means the `ircd_sys::bindings` types/extern decls the whole
tree imports must come from somewhere else (hand-written or a committed snapshot of the generated
bindings) — that is the crux of the final `cc::Build` removal, not just deleting compile steps. The
P1-deferred `irc_sprintf`/FORMAT engine is **deleted, not ported, in P11**.
=== project-name-leveva-p10.md ===
---
name: project-name-leveva-p10
description: "the idiomatic-Rust end state of this ircd port ships under the project name \"leveva\"; rename happens at Phase 10"
metadata: 
  node_type: memory
  type: project
  originSessionId: bae0dadf-c2f9-4e1f-8521-413d1df9ceb7
---

The all-Rust product of this ircd port is to be named **leveva**. Per PLAN.md, **Phase 10 (P10) is where the project starts being called `leveva`** — the P10 row was renamed to "Rename to `leveva` + typed identifier string types" and its scope now includes renaming the workspace crates/binaries/docs from the `ircd*` mechanical-port names to `leveva`.

**Why:** user's chosen name for the final all-Rust product; not derivable from the code (the workspace crates are `ircd-rs`/`ircd-common`/`iauth-rs`/etc. through P0–P9). User explicitly set the rename to begin at Phase 10 (2026-06-06), superseding an earlier note that put it at P8.

**How to apply:** the faithful-port + cleanup phases (P0–P9) keep the `ircd-*` names; at P10, rename to `leveva` (Cargo.toml package names, binary artifact, README/docs). See [[rust-migration-plan]].

**Update 2026-06-07 — leveva crate STARTED EARLY (string layer):** the user pulled P10's typed-string foundation forward (right after P7 completed, before P8/P9). A new greenfield `leveva/` workspace member now exists — std-only, NO `ircd-sys`/`ircd-common` dep, no `cref_` oracle, ordinary Rust unit/doc tests. Modules: `casemap` (textbook RFC 1459 fold — `A-Z↔a-z` PLUS Scandinavian `[↔{ \↔| ]↔} ~↔^`; deliberately differs from this daemon's ASCII-only `tolowertab` in `common/match.c`), `string` (`IrcStr`/`IrcString` casemapping `Eq/Ord/Hash`, `Borrow<IrcStr>`, NUL/CR/LF reject, `IrcStringBuilder`), `ident` (validating newtypes via `ident_newtype!` macro: `Nick`/`ChanName`/`Sid`/`Uid`/`Cid`/`ServerName`/`UserName`/`HostName`, grounded in struct_def.h lengths + char_atribs NVALID + do_nick_name + s_id.c), `message` (`Message`/`MessageBuilder` → 512-safe CRLF wire). The crate/binary **rename of the `ircd-*` crates is still deferred** to the real P10 slot. Progress: `docs/progress-log/p10.md`; plan: `docs/superpowers/plans/2026-06-07-leveva-rfc1459-string.md`.
=== rust-migration-plan.md ===
---
name: rust-migration-plan
description: "The C→Rust strangler-fig migration of this IRCnet ircd 2.11 codebase — locked decisions, seam, and plan location"
metadata: 
  node_type: memory
  type: project
  originSessionId: 83d912ac-5e1e-4525-89a0-e5166a07c355
---

This repo (IRCnet ircd 2.11, ~51k LOC C) is being mechanically rewritten to Rust via a strangler-fig migration. Approved plan: `/home/xe/.claude/plans/i-would-like-to-humming-curry.md` (written 2026-06-01).

**Four locked user decisions** (do not re-litigate):
1. Faithful **mechanical** port now → one dedicated **idiomatic** Rust pass at the very end (P8). No async/ownership redesign mid-migration.
2. **Rust-driven build from day one**: Rust `fn main()`; remaining C compiled as an `ircd-sys` crate (cc::Build + bindgen over the `*_ext.h`/`struct_def.h` headers); strangle inward by dropping each `.c` and re-exporting its symbols from Rust.
3. **iauth rewritten first**, standalone greenfield Rust binary (`iauth-rs`) speaking the identical `<id> <category> <info>` pipe protocol.
4. **Full 4-layer testing** (L1 differential unit, L2 golden, L3 fuzz, L4 conformance) + a permanently-maintained **C reference build** as the oracle.

**Phase order:** P0 scaffold+baseline+harness · P-IAUTH (parallel) · P1 leaf utils · P2 data/lookup · P3 transport/format · P4 parser/dispatch · P5 handlers (cluster-based) · P6 config/DNS · P7 I/O core + event loop (last) · P8 idiomatic.

**Why this is feasible:** every `foo.c` ships `foo_ext.h` (its public extern API = a ready-made per-module C ABI; 41 headers / ~417 symbols), and dispatch is a mutable fn-pointer table `msgtab[]` in `common/parse.c`. See [[ircd-migration-hazards]] for the verified gotchas that constrain the port.
