emulebb-rust ⇄ emulebb-mfc — Full Parity Review (2026-07-05)¶
Current policy note (2026-07-08): this is retained evidence from the pre-reset emulebb-mfc parity lane. The active product target is stock eMule parity with SX1 as the only pre-approved permanent drop; RUST-REF-004 owns the non-SX1 re-audit.
RUST-REF-002 evidence. A three-lane read-only audit of the Rust client against
the MFC oracle (workspaces/workspace/app/emulebb-main/srchybrid), scoped to
find unregistered divergences. At the time of this review, the registered
omissions in policy/rust-client-omissions.toml were suppressed; current audits
must follow RUST-REF-004 instead.
Verdict¶
The Rust client is an oracle-faithful port at wire parity across Kad, the
eD2K client-to-client transfer protocol, the eD2K server protocol, the /api/v1
REST contract, and SQLite persistence. No blocker-class divergence.
2026-07-10 Reconciliation¶
The later code audit found one Major reachability gap not represented in this
snapshot: the missing Kad FINDSOURCE fallback when a LowID source carried a
buddy ID without a buddy endpoint. RUST-FEAT-035 closed that gap in emulebb-rust
commit 41b139a.
The audit also reconciled implementation state for RUST-FEAT-025/030/031/032. RUST-FEAT-030/031/032 are complete after focused accounting tests and the full serialized Kad swarm gate passed; RUST-FEAT-025 remains in progress for its captured-event assertion and live converged-soak comparison.
The same pass closed three registered state/protocol divergences:
- RUST-PAR-026 implements MFC's adaptive connection-spike suppressor.
- RUST-PAR-027 wires
safeServerConnectto one-versus-two automatic server attempts, first-login-wins selection, and immediate failed-server cycling. - RUST-PAR-028 implements paced, challenge-validated UDP server description polling and persistence for non-connected server metadata. FEAT-025 (duplicate-done/queued block rejection) was verified line-by-line conformant with the oracle ledger. Thirteen unregistered divergences were found; two warrant a code fix, the rest are register-as-omission or defer.
Parity matrix (by lane)¶
Only the not-AT-PARITY rows are listed; everything else in each lane was confirmed at parity (full per-subsystem tables are in the review transcripts).
| Lane | Behavior | Status | Disposition |
|---|---|---|---|
| Kad | KADEMLIA2_REQ→RES returns unverified contacts (missing IsIpVerified() filter) |
PARTIAL | FIX (P-1) |
| Kad | Inbound flood tracker LAN exemption is unconditional (MFC: LAN-mode only) | PARTIAL | REGISTER (P-4) |
| Kad | Self-imposed global source/notes index ceilings (MFC has none) | DIVERGENT | DEFER (P-6) |
| Kad | Network-size estimate uses base firewalled constants, not live ratio | PARTIAL | DEFER (P-6) |
| eD2K | Slow-upload cooldown suppression not enforced (promote/recycle thrash) | PARTIAL | REGISTER (P-4) |
| eD2K | Duplicate-queued rejection is intra-packet only (synchronous-serve model) | PARTIAL | REGISTER (P-4) |
| eD2K | DoneBlocks history bounded to 128 vs MFC unbounded set | DIVERGENT | DEFER (P-6) |
| eD2K | OP_OutOfPartReqs quarantine escalation missing | MISSING | DEFER → defensive Phase D (P-5) |
| eD2K | Upload-admission cooldowns missing | MISSING | DEFER → defensive Phase E (P-5) |
| eD2K | download_queue_rank_flood ban missing | MISSING | DEFER → defensive Phase C-rem (P-5) |
| Server | OP_SERVERLIST auto-add not gated by an "add-servers-from-server" pref | PARTIAL | FIX comment + REGISTER (P-2) |
| Server | Server obfuscation ports/flags not persisted across restart | PARTIAL | REGISTER (P-4, re-dispositioned) |
| REST | /transfers/{hash}/operations/preview has no partial-file semantics |
PARTIAL | REGISTER (P-4) |
Registered intentional omissions (15) and the reserved-but-unwritten forward SQLite tables were excluded per scope and are not gaps.
Execution status (2026-07-05)¶
All items landed on main, gate green. P-1 Kad fix (e473a52), P-2 OP_SERVERLIST
preference (5778c10), P-4 registrations (5 new omissions incl. the
re-dispositioned obfuscation item), P-5/P-6 defers recorded in
docs/RELEASE-SCOPE.md. RUST-REF-002 closed; parity baseline validated → cleared
for the Phase-4 converged soak.
The fixes (P-1 / P-2)¶
-
Kad — filter unverified contacts out of
KADEMLIA2_RES. MFCCRoutingBin::GetClosestTogates onGetType() <= uMaxType && IsIpVerified()(RoutingBin.cpp:242); the REQ responder uses it (KademliaUDPListener.cpp:738). Rustget_closest_max_typefilters onlyoracle_type() <= max_typewith noverifiedcheck (crates/emulebb-kad-routing/src/zone.rs:190-195,table.rs:161-172, dispatchcrates/emulebb-core/src/lib.rs:6485). TheContact.verifiedflag already exists (set on handshake / legacy challenge), so the fix is to add theverifiedpredicate to the REQ/RES path only (get_closest/ bootstrap must stay unfiltered to matchGetBootstrapContacts). Anti-poisoning hygiene; wire-visible. Functional. -
~~Server — persist server obfuscation ports/flags across restart.~~ RE-DISPOSITIONED to REGISTER (2026-07-05). On implementation the fix proved larger and worse than the review scoped: obfuscation is dropped one layer earlier than the audit saw — at the
ServerInfoREST/state DTO (crates/emulebb-core/src/rest_model.rs:153), which Lane C confirmed is at/api/v1contract parity without obfuscation fields. The server.met parser does not extract obfuscation tags and OP_SERVERLIST carries only(ip, port), so no non-config path supplies obfuscation data; persisting the vestigial columns would store zeros, and adding obfuscation toServerInfowould regress the documented REST server schema. Configured servers (the soak-critical path) keep obfuscation from config on every restart. Registered asserver-obfuscation-metadata-non-config.
Register-as-omission (P-4)¶
Add registry entries + [review_reporting] ids for these deliberate, wire-
neutral-or-gentler divergences so they stop surfacing in future audits:
- kad-flood-lan-exemption — LAN IPs are always flood-exempt (be-gentle; a
public VPN'd node rarely sees LAN Kad peers).
- upload-slow-cooldown-suppression — recycled slow uploaders are demoted to
the queue tail without a cooldown-until/score-suppression term (local slot
policy; no wire impact; simpler than eMule's broadband cooldown-probe).
- upload-duplicate-queued-intra-packet — the queued-duplicate ledger is
per-request-batch (synchronous serve model); cross-packet queued duplicates
collapse into the done-block reject path — anti-abuse coverage preserved, event
label can differ.
- ed2k-partial-file-preview — operations/preview returns the transfer view
only; no incomplete-file preview action (GUI concept, headless-inapplicable).
Fix + register (P-2)¶
- OP_SERVERLIST auto-add. The handler comment claims a
GetAddServersFromServer()gate that does not exist (crates/emulebb-ed2k/src/ed2k_server/packet_handler.rs:156-161); the merge is unconditional (crates/emulebb-core/src/lib.rs:1291-1337). eMule defaults the pref ON, so default behavior matches. Fix the misleading comment; register the always-on behavior as an omission (or, optional, add the pref + gate).
Defer (P-5 / P-6)¶
- P-5 (defensive-measures plan): OP_OutOfPartReqs quarantine escalation (Phase D), upload-admission cooldowns (Phase E), download_queue_rank_flood ban (Phase C-remainder). Already governed by [[emulebb-rust-defensive-measures-plan]]; record the parking there, not as release surprises. Functional anti-abuse depth, none blocker.
- P-6 (memory-safety / stat cosmetics): self-imposed Kad index ceilings, network-size estimate constants, the 128-entry DoneBlocks bound. Documented, effectively non-binding, no wire impact. Note in the deferred list.
Execution plan — full parity validation before live tests¶
Sequenced; each step lands on main with the gate green.
- P-1 fixes (2) — Kad
IsIpVerifiedfilter + server obfuscation persistence, each with a unit test (Kad: REQ/RES omits an unverified contact but bootstrap still returns it; server: obfuscation ports survive an upsert→reload round trip). One coherent commit each. - P-2 — correct the OP_SERVERLIST comment; register the always-on behavior.
- P-4 — historical step: add the four register-as-omission entries to
rust-client-omissions.tomland extendRELEASE-SCOPE.mdunder the old omission model. Current audits must re-run those dispositions through RUST-REF-004. - P-5 / P-6 — record the deferred anti-abuse items against the defensive- measures plan and add the memory-safety/stat cosmetics to the RELEASE-SCOPE deferred list with the parking rationale.
- Re-green the gate —
rust_quality_gate.py ci-test+quick; confirm zero undispositioned findings. Update RUST-REF-002 to DONE with this review as the evidence record. - Gate to live tests — only once every row above is fixed/registered/deferred
is the parity baseline validated; then proceed to the Phase-4 converged soak
(live
diag_event_difffor the FEAT-025repeatCountalignment, witness of UDP-reask/buddy/firewall-check, and the operator VPN-pull pktmon evidence).
Provenance¶
Three parallel general-purpose review agents (Kad; eD2K transfer+protocol; server+REST+persistence), each citing rust file:line and MFC file:line evidence. No files were modified during the audit.