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.

ircd.rs / docs / superpowers / plans / 2026-06-15-p11-slice173-msgid-unixnano.md
3.8 kB

P11 slice 173 — msgid from unix-nanosecond time, not a monotonic counter#

Goal#

Change leveva::msgid::generate so the counter half of a msgid (<sid>-<n>) is derived from the unix-nanosecond wall clock instead of a process-global monotonic AtomicU64 that starts at 0 on every boot. This better satisfies the IRCv3 message-ids spec's restart-collision guidance — the spec explicitly warns against "simple numeric counters … being reset if the server restarts" and suggests prefixing "the server startup timestamp at a suitable precision." A whole-value nanosecond timestamp goes one better: every issued id encodes wall-clock time, so a restart keeps advancing rather than rewinding to 0.

Locked decisions#

  • The id stays <sid>-<nanos:x> (sid prefix → cross-server uniqueness; hex of the nanosecond value, same lowercase-hex shape the old counter used). No format break for clients (the spec says treat the id as an opaque case-sensitive string).
  • The clock source is the existing crate::clock::unixnano() (leveva's native time unit) — no new clock plumbing.
  • Uniqueness is enforced, not assumed. A coarse clock (or two calls in the same nanosecond, or a backward NTP step) could otherwise mint two equal values. A process-global AtomicU64 LAST_NANOS makes the issued value max(now, last+1) via a CAS loop, so the per-process id stream is strictly increasing ⇒ no two ids this server mints ever collide, regardless of clock behaviour.

Design#

leveva/src/msgid.rs:

  • Pure core bump_to_unique(prev: u64, now: u64) -> u64 = now.max(prev.saturating_add(1)).
  • next_nanos(last: &AtomicU64, now: u64) -> u64 — CAS loop over bump_to_unique, takes the atomic by ref so tests drive it with a fresh atomic (the global static would otherwise leak state across #[test]s and across proptest cases).
  • generate(sid) = format!("{sid}-{nanos:x}") where nanos = next_nanos(&LAST_NANOS, unixnano()).
  • is_valid unchanged.

No call-site changes: generate/is_valid keep their signatures (callers in command/message.rs, command/tagmsg.rs, s2s/relay.rs).

Tests (write first — RED)#

  • Unit: generate is is_valid + sid-prefixed; two generates differ; distinct sids → distinct prefixes; is_valid rejects the forbidden bytes (carried over).
  • Unit: the numeric half of a generated id is a plausible recent nanosecond value (> 1.5e18), proving it's time-derived, not a counter starting at 0 — this is the slice's point.
  • Unit (pure): bump_to_unique(prev, now) is always > prev, and == now when now > prev.
  • Proptest fuzz (the required "fuzzing"): over an arbitrary sequence of now values fed to next_nanos with a fresh atomic, the issued sequence is strictly increasing (⇒ all ids distinct — the uniqueness invariant) and each issued value == max(now_i, prev_issued+1) (tracks the clock when it advances, only synthesises forward when it doesn't). Inverse covered: a non-advancing/backward now does not produce a duplicate or a rewind.

Divergences#

Update the module doc: drop the "restart resets the counter → reissue risk" divergence; replace with the nanosecond stance (restart keeps advancing; intra-process strict-monotonic enforcement removes same-nanosecond/back-step collisions; residual risk is only a wall-clock step backward across a restart, vanishingly small at nanosecond precision and bounded by the sid prefix).

Gate#

cargo test -p leveva green + cargo clippy -p leveva --tests clean + cargo build --workspace 0 warnings. Recorded per [[leveva-slice-recording]]: this plan file + p11.md heredoc entry + PLAN.md P11 row bump (Done: 173).