feat(leveva): AWAY command (P11 slice 15) — 301/305/306, +a, WHO G/H, WHOIS 301
Add the AWAY command and away-state, the 15th P11 leveva slice. An away message
is stored in the shared registry record (read by other connections), the
server-managed +a (FLAGS_AWAY) bit is toggled in lockstep, and away-ness is wired
into its three observers:
- AWAY :msg -> 306 RPL_NOWAWAY (truncate to TOPICLEN=255, char-boundary safe);
bare/empty AWAY -> 305 RPL_UNAWAY (unconditional, faithful to m_away).
- PRIVMSG (not NOTICE) to an away nick -> 301 RPL_AWAY to the sender.
- WHO shows G (away) / H (here); WHOIS inserts 301 after 312.
+a is server-managed: MODE keeps ignoring 'a', only AWAY toggles it. Grounded in
the original C (b88264f3^: m_away/send_away/who_one/send_whois) + the P5g oracle
port. Unit + boot-golden + proptest (fuzz) coverage; leveva-only, no shared C state.
Gate: cargo test -p leveva (344 lib + golden + proptest) + -p leveva-integration
(10) green; clippy clean; workspace build 0 warnings.
feat(leveva): channel modes, chanop bootstrap, NAMES prefixes + gates
Give channels a privilege model and mode-state. The first joiner of a fresh
#/& channel becomes a chanop (@), mirroring the oracle's m_join bootstrap;
membership now tracks per-member op/voice (MemberStatus), surfaced as the @/+
NAMES prefixes.
channel.rs gains ChannelModes (flag bitmask + key + limit + ban list) and the
operations over it: apply_modes (op-gated, no-op-eliding, returning the
effective changes + 441/467 signals), check_join (+k/+l/+i/+b enforcement),
can_send (+n/+m), channel_mode_string (324 render), is_op, bans. set_topic now
honours +t (SetTopic::NeedOps).
command.rs adds the MODE handler: channel query (324), ban-list (367/368), the
mode-string parser over the supported set (o v n m t i p s k l b; others 472),
op-gate (482), 401/441/467, and the coalesced :src MODE broadcast. The
retroactive gates are wired: +t -> TOPIC 482, +n/+m -> PRIVMSG/NOTICE 404, and
JOIN enforcement -> 475/471/473/474. MODE <nick> is a minimal 221/502.
Two boot-golden snapshots shift to show the creator's @ prefix in NAMES.
fix(leveva): burst NJOIN carries the channel TS — P11 slice 215
A channel a server learned **for the first time in a burst** was re-clocked to
the receiver's local nanosecond clock instead of adopting the origin's
`created_at`: the leading `CHANTS` is dropped on an unknown channel
(`apply_encap_chants` → `ChanTsOutcome::Unknown`), and the burst `NJOIN` was
TS-less, so `handle_njoin` stamped `clock::unixnano()`. Two servers then
disagreed on a channel's TS by *when each learned it*, and the next merge
deopped the later learner — the divergence slice 167 fixed for the live NJOIN,
left unfixed on the burst path. Observed in prod: updating a server to v0.1.0
and relinking stripped `#xe`'s ops + `+R` that should have survived the merge.
Fix: the burst `NJOIN` now carries the channel TS as a middle param
(`:<sid> NJOIN <chan> <ts> :<members>`, the live form since slice 167) so a
peer adopts the origin's TS. Safe w.r.t. the merge case despite the slice-167
caveat: the burst is CHANTS-first (slice 171), so `we_lost` reads the
pre-merge `created_at` before NJOIN runs, and `njoin_member` only stamps on
creation — so for an existing channel the NJOIN-TS is a no-op (CHANTS owns
arbitration), and it only supplies the TS for a newly-learned channel.
`njoin_member_chunks` subtracts `1 + ts_digits` from the line budget; the boot
canonicalizer masks the new `NJOIN <chan> <ts> :` TS like CHANTS/USERTS.
Tests: burst_channel_ts_is_adopted_not_reclocked (the regression),
burst_njoin_line_carries_the_ts_middle_param, …_does_not_clobber_an_existing
_channels_arbitrated_ts (inverse merge), and a fuzz round-trip over arbitrary
origin TS. Updated the two burst snapshots + golden_s2s_njoin_live.
Gate: cargo test -p leveva green (pre-existing golden_s2s_keepalive BrokenPipe
flake aside); clippy clean; workspace build 0 warnings.
feat(leveva): DIE + RESTART + REHASH oper control-plane (P11 slice 25)
The first leveva commands that act on the whole process. Introduces a server
control plane (leveva/src/control.rs: a process-global ServerControl =
Notify + latching AtomicU8) — the process-level analogue of slice 19's
per-connection eject plane.
Faithful to the IRCnet 2.11 m_die/m_restart/m_rehash msgtab gate
({ m_nop, m_nopriv, m_<cmd>, m_nop, m_unreg }): a non-oper -> 481; an oper ->
the handler, which re-gates on is_allowed(ACL_DIE|RESTART|REHASH) -> also 481
without the flag. This wakes the dormant OperPrivilege machinery (parsed since
slice 16): Registered gains a `privileges` set, populated from the matched
block on a successful OPER, and each control command gates on its OperPrivilege
(a non-oper's empty set => 481, reproducing both oracle arms in one check).
- DIE/RESTART (shutdown_all): eject every registered client (issuer included)
with a `Server (Terminating|Restarting). <nick>[<user>@<host>]` NOTICE as the
eject wire, then control::request(Die|Restart). main's select! drains the
ejects over a 200ms grace then exits (Die) or reexec()s the binary (Restart,
CommandExt::exec; CLOEXEC sockets re-bind cleanly).
- REHASH: 382 RPL_REHASHING + re-read/validate the on-disk config.
Documented divergences: killer as <nick>[<user>@<host>]; single-server (no
server-link ERROR branch); RESTART re-exec is review-covered not golden-tested;
REHASH only validates the re-read (applying it to the immutable ServerContext
is deferred); no SCH_NOTICE; KILL/WALLOPS still gate on the oper bit only. No
leveva-integration differential (all three are impure into global buffers).
Verified: unit (control 8; die/restart/rehash 3 each w/ inverses; OPER stores
the set) + control_proptest (gate / eject-conservation / panic-freedom) +
golden_control (mortal 481, oper 382, oper DIE -> NOTICE then exit) + live
boots (DIE->EOF->exit; RESTART->re-exec->port re-bind). cargo test -p leveva
green (lib 468 + all golden/proptest bins), clippy/fmt clean, workspace 0
warnings, leveva-integration unchanged.
feat(leveva): CYCLE — self-only channel refresh (P11 slice 295)
charybdis-style `CYCLE <channel>` (extensions/m_cycle.c): part and rejoin a
channel *without races*. Faithful to the real m_cycle, the effect is entirely
client-side for the issuer — `:source PART <chan> :Cycling`, `:source JOIN
<chan>`, then a fresh 353/366 — and nothing changes server-side, so the user
keeps op/voice and place and cannot be locked out by +i/+k/+l or lose a glare.
No new Channels seam (CYCLE never mutates): it composes the read-only
display_name/is_member/names_query accessors. Per-channel gate ladder:
461 no param → 403 nonexistent → 442 not-a-member → success, existence before
membership; the re-sent NAMES honours the issuer's multi-prefix/userhost-in-names.
Divergences (leveva-native, no oracle): self-only/observationally-pure (NOT
member-visible — that would drop ops, destroy a sole-member channel, and desync
peers), fixed :Cycling reason, local-only.
7 command units + cycle_proptest (observational-purity + arbitrary-args) +
golden_cycle; CYCLE.md help + COMMANDS allowlist + index.md.
feat(leveva): D-lines (DLINE/UNDLINE) — IP-level operator bans (P11 slice 284)
charybdis-style D-lines, the IP/CIDR sibling of the temporary K-line family
(slices 281–283): `DLINE <duration> <ip/cidr> [:reason]` bans a raw IP or CIDR
block, `UNDLINE <ip/cidr>` lifts it. Built as a near-clone of the K-line plane
keyed on a single IP/CIDR `mask` (matched with the CIDR-aware
`matching::host_component_matches`) instead of `user@host`.
- `dline.rs`: `DlineStore` mirrors `KlineStore` (Vec+Mutex, NOCASE dedup,
`find_active`, same `database {}` SQLite write-through + boot reload as slice
282); `Dline::covers` = `host_component_matches`. Reuses the kline duration
grammar/clock/clamp.
- `command/dline.rs`: `dline`/`undline` mirror `tkline`/`untkline` (461→481 gate,
empty/`*` mask → Incorrect format, no success reply); `reap_matching_ip` ejects
local matching clients by `orighost` (real connect IP), skipping remote +
`kline-exempt`. New `OperPrivilege::Dline` (bit 0x400000).
- `session.rs`: registration gate checked before the K-line gate, matching the
pre-cloak connect IP → 465+ERROR+REJ snomask, never counted/claimed.
- `s2s/dline.rs`+`forward.rs`+`burst.rs`: `ENCAP * DLINE`/`UNDLINE` propagation
(server-prefixed, remaining-seconds, slice-281 KLINE shape) + burst
re-assertion.
- `stats.rs`: `STATS d` → `250 RPL_STATSDLINE` via `dline_report`.
- `server.rs`: `ctx.dlines` opens against the same `database {}` file (persists
across restart); boot test + inverse extended.
- help `DLINE.md`/`UNDLINE.md` + COMMANDS allowlist.
Tests (TDD, inverse invariants, mandated fuzzing): unit+proptest per module;
`tests/golden_dline.rs` (real binary, `dline.kdl` fixture) and
`tests/dline_proptest.rs` (6 properties incl. model-lockstep registration gate
over `Session::feed`). leveva-native, no oracle differential.
`cargo test -p leveva` green (2530 lib); clippy clean; workspace 0 warnings.
feat(leveva): IRCv3 draft/read-marker + the MARKREAD command (P11 slice 300)
The read-state companion to draft/chathistory (slice 274). MARKREAD
<target> [timestamp=<iso8601>] records or queries how far a client has
read a target; a set stores max(existing, new) (monotonic — never
regresses), echoes the effective value, and on a genuine advance fans
the MARKREAD to the user's other capable connections (cross-device
read-state sync). A bare MARKREAD <target> queries the stored marker.
Markers are owned by the services account (synced across connections,
survives a reconnect) when logged in, else by the connection UID
(per-connection, dropped on Session::release). The cap is statically
advertised and valueless; the command is processed regardless of the
issuer's own cap — the cap gates only the cross-connection fan, per spec.
leveva-native: the C 2.11 oracle predates IRCv3, so the gate is unit +
golden + proptest (no differential).
- readmarker.rs: pure parse/format seam (parse_set_timestamp /
format_value) + the monotonic ReadMarkers store (account|UID owner key)
- command/markread.rs: the handler (NEED_MORE_PARAMS / query / set /
INVALID_PARAMS / advance + fan)
- cap.rs: DRAFT_READ_MARKER const + SUPPORTED entry + read_marker
ClientCaps bit; CAP LS snapshots refreshed (a long SASL mechlist now
tips the 302 LS into a spec-legal multiline split)
- server.rs: read_markers field; session.rs: forget_connection release hook
- help/MARKREAD.md + COMMANDS allowlist + index.md Queries line
- tests: readmarker/markread units (inverse invariants), golden_markread
(single-conn round-trip + cross-device push vs uncapped sibling),
readmarker_proptest (total parse + round-trip; advance keeps-max,
order-independent + idempotent)
feat(leveva): IP cloaking / the +x hidden-host usermode + user modes on connect (P11 slice 72)
Ports Elemental-IRCd's extensions/ip_cloaking.c to leveva as a client-settable
`+x` usermode that hides a client's host behind a deterministic FNV-keyed
scramble of its original host. The host change is distributed by reusing the
slice-71 chghost plane, so a cap co-member sees an in-place CHGHOST and the
network an ENCAP CHGHOST.
- cloak.rs: faithful port of the C — fnv_hash(s,32), cloak_ip (keep IPv4 first
two octets / IPv6 first half, substitute the rest from the g–z alphabet),
cloak_host (scramble first-label letters then digits), dispatch on IpAddr.
Wire-safety: never introduces a leading ':'.
- mode.rs/numeric.rs: UserMode::Cloak (+x, 0x0100, appended last); 396
RPL_HOSTHIDDEN. +x is NOT in SEND_UMODES — it propagates as a host change,
not a umode diff (no double-propagation).
- registry.rs: immutable ClientRecord.orighost (set at try_claim, never by
set_host) so the cloak is computed from / reverts to the real connect host.
- command/mode.rs: 'x' joins the i|w|B settable arm; apply_cloak_change cloaks/
restores the host, mirrors set_host, distributes over the chghost plane, and
emits 396 (is now your hidden host / hostname reset).
- config/parse.rs: default-user-modes now accepts 'x' so a network can
auto-cloak everyone; a default-+x client is cloaked at registration (the C
check_new_user path).
- session.rs: clients are now told their user modes on connect (:nick MODE nick
:+<modes> after the welcome burst).
leveva-native (no C oracle) → unit + golden + proptest, no differential. Tests:
cloak/mode/numeric/registry/command/session/config units + golden_ip_cloaking
(127.0.0.1 -> 127.0.y.p) + cloak_proptest (the fuzz target). 4 registration
snapshots regenerated (004 -> oOiwraWBx + the trailing MODE notice). clippy
clean, 0 warnings.
feat(leveva): +l locops + LOCOPS command, and make all user modes global — P11 slice 254
Part A — +l locops user mode + LOCOPS command (charybdis UMODE_LOCOPS), the
local-server sibling of +z/OPERWALL: an oper broadcasts to +l users on the
originating server only; the command never crosses a link. Oper-gated to set,
letter 'l' (bit 0x40000). New command/locops.rs + golden_locops.rs +
locops_proptest.rs. The last named standard charybdis umode — surface complete.
Part B — leveva has NO local-only user modes (product decision). SEND_UMODES is
now every UserMode::ALL bit (self-maintaining const loop), so +O/+x/+s/+g/+G/+D/
+R/+Q/+z/+l all propagate and a remote WHOIS shows them. Verified safe: the burst
already rendered the full unmasked set, and a user's host is always carried
explicitly (UNICK field / ENCAP CHGHOST), never recomputed from the +x bit, so
+x cannot double-apply the cloak (+x now emits both UMODE +x and ENCAP CHGHOST).
Enforcement unchanged: caller-id (+g/+G) and +R stay gated on the target's home
server — the ACCEPT list lives only there and is not propagated, so those S2S
gates are the actual enforcement, not redundant re-checks, and are kept.
- mode.rs: UserMode::LocOps + is_locops(); doc sweep (bit propagates, enforcement
is server-local)
- command/mode.rs: 'l' in the oper-gated arm + umode_diff; +x test asserts both frames
- s2s/umode.rs: SEND_UMODES = every umode; exclusion test -> carries-every-mode
- snotice.rs / s2s/relay.rs: stale 'not in SEND_UMODES' comments corrected
- help/LOCOPS.md + USER_MODES +l row; 004 umode list -> ...zSl (6 snapshots)
- golden_s2s_umode rewritten (demonstrates +D now propagating)
feat(leveva): +r registered-only-join channel mode (charybdis MODE_REGONLY) — P11 slice 241
Adds the leveva-native channel flag mode +r: on a +r channel only a client
signed in to a services account may JOIN; a signed-out client is refused 477
(charybdis ERR_NEEDREGGEDNICK, which overloads numeric 477 — reuses
Numeric::ErrNochanmodes since a duplicate-discriminant variant is impossible).
The join-plane sibling of +g; a hard JOIN gate like +O/+S (an INVITE/key/
exception does NOT bypass it — account identity is a user property), so KNOCK to
a +r channel is refused too.
- mode.rs: ChanMode::RegisteredOnly, letter r, bit 0x80000000 (last free u32
bit), Flag kind; added to ALL/as_char/bit/mode_name/from_char.
- command/mode.rs: supported_flag 'r' (the second allowlist).
- channel.rs: pure regonly_blocks(set, has_account) seam + the hard-gate clause
in check_join (reads attrs.account, same source as $a extban); new
JoinReject::RegisteredOnly.
- command/join.rs + command/knock.rs: reject mapping + KNOCK refusal.
- ISUPPORT/MYINFO auto-derive (CHANMODES …SzTgr, MYINFO …SzTgr…); 10 snapshots
+ isupport/registration assertions updated (token-only).
Reclaiming the slice-77 ripped-out reop letter r triggered the
unknown-chanmode-letter hazard: removed the obsolete ripped_out_reop_is_unknown
test, updated chan_mode_bits_match_oracle, switched golden_modes's unknown-mode
probe to +>, and repurposed the modes_proptest AnonReop op to BareAdmin.
Tests: golden_regonly.rs (RED-first), channel/mode/command::mode units (gate +
inverse + hard-gate proof + truth table), regonly_proptest.rs, modes_proptest
FLAGS += RegisteredOnly.
cargo test -p leveva green; cargo clippy -p leveva --tests clean; cargo build
--workspace 0 warnings.
feat(leveva): IRCv3 MONITOR / the online-offline watch list (P11 slice 70)
Adds the MONITOR extension: a per-connection nick watch list with server-pushed
730/731 online/offline notifications, replacing ISON polling. Advertised via the
MONITOR=100 ISUPPORT token (not a CAP). New leveva::monitor shared index (keyed by
watcher UID, off the Registered struct), command/monitor.rs handler (+/-/C/L/S,
734 overflow, comma-chunked replies), live-notify hooks at the registration /
disconnect / nick-change seams, numerics 730-734, and help/MONITOR.md.
Single-server-local: live pushes fire on local lifecycle events only (remote S2S
transitions deferred). Gate: monitor module + command + seam units, golden_monitor
(two clients), monitor_proptest; full leveva suite 1222 pass, clippy clean, 0 warnings.
feat(leveva): IRCv3 MONITOR / the online-offline watch list (P11 slice 70)
Adds the MONITOR extension: a per-connection nick watch list with server-pushed
730/731 online/offline notifications, replacing ISON polling. Advertised via the
MONITOR=100 ISUPPORT token (not a CAP). New leveva::monitor shared index (keyed by
watcher UID, off the Registered struct), command/monitor.rs handler (+/-/C/L/S,
734 overflow, comma-chunked replies), live-notify hooks at the registration /
disconnect / nick-change seams, numerics 730-734, and help/MONITOR.md.
Single-server-local: live pushes fire on local lifecycle events only (remote S2S
transitions deferred). Gate: monitor module + command + seam units, golden_monitor
(two clients), monitor_proptest; full leveva suite 1222 pass, clippy clean, 0 warnings.
feat(leveva): IRCv3 MONITOR / the online-offline watch list (P11 slice 70)
Adds the MONITOR extension: a per-connection nick watch list with server-pushed
730/731 online/offline notifications, replacing ISON polling. Advertised via the
MONITOR=100 ISUPPORT token (not a CAP). New leveva::monitor shared index (keyed by
watcher UID, off the Registered struct), command/monitor.rs handler (+/-/C/L/S,
734 overflow, comma-chunked replies), live-notify hooks at the registration /
disconnect / nick-change seams, numerics 730-734, and help/MONITOR.md.
Single-server-local: live pushes fire on local lifecycle events only (remote S2S
transitions deferred). Gate: monitor module + command + seam units, golden_monitor
(two clients), monitor_proptest; full leveva suite 1222 pass, clippy clean, 0 warnings.
feat(leveva): IRCv3 MONITOR / the online-offline watch list (P11 slice 70)
Adds the MONITOR extension: a per-connection nick watch list with server-pushed
730/731 online/offline notifications, replacing ISON polling. Advertised via the
MONITOR=100 ISUPPORT token (not a CAP). New leveva::monitor shared index (keyed by
watcher UID, off the Registered struct), command/monitor.rs handler (+/-/C/L/S,
734 overflow, comma-chunked replies), live-notify hooks at the registration /
disconnect / nick-change seams, numerics 730-734, and help/MONITOR.md.
Single-server-local: live pushes fire on local lifecycle events only (remote S2S
transitions deferred). Gate: monitor module + command + seam units, golden_monitor
(two clients), monitor_proptest; full leveva suite 1222 pass, clippy clean, 0 warnings.
feat(leveva): OLIST — operator LIST revealing +s/+p channels (P11 slice 293)
charybdis extensions/m_olist.c: the operator variant of LIST that bypasses the
secret (+s) / private (+p) channel-hiding so an oper enumerates every channel,
including the hidden ones a normal LIST skips. The read-only sibling of LIST and
the latest member of the charybdis operator diagnostic family
(TESTLINE/TESTMASK/MASKTRACE/CHANTRACE/FINDFORWARDS/OPME).
- Channels::list_all() (full folded sweep) + list_one_any(name) (named incl.
hidden) — the visibility-bypass seams, no filter; also the fuzz seam.
- command/olist.rs: plain oper-bit gate (non-oper 481 before any sweep), same
321/322/323 numerics as LIST; no-arg → all incl. +s/+p, named → that channel
revealed-or-skipped, no ELIST conditions (faithful to mo_olist).
- Wired "OLIST" + mod olist; help/OLIST.md + COMMANDS allowlist + index.md.
Divergences (leveva-native, no oracle): plain oper-bit gate (not a per-command
privilege), no +s audit snomask (read-only diagnostic family precedent),
local-only.
Tests: list_all/list_one_any units; 6 command units; olist_proptest (full-set
+ superset-of-list model equality, arbitrary_args_never_panic); golden_olist
(LIST hides #secret, OLIST reveals it, non-oper 481). Regenerating
golden_help_users also picks up the pre-existing stale operator-index line
(OPME/FINDFORWARDS from slices 291/292).
cargo test -p leveva green; clippy clean; build --workspace 0 warnings.
feat(leveva): +z operwall user mode + OPERWALL command (charybdis UMODE_OPERWALL) — P11 slice 252
The oper-only sibling of +w/WALLOPS: an operator broadcasts OPERWALL to every
+z operator network-wide. Closes one of the named remaining charybdis umodes.
A near-exact mirror of the slice-178 WALLOPS machinery (command/operwall.rs +
s2s/operwall.rs).
- +z (letter "z", bit 0x10000, FLAGS_OPERWALL): oper-gated to set like +s
(non-oper +z silently ignored, -z always allowed); purely local, NOT in
SEND_UMODES (the OPERWALL command propagates and each server fans to its own
local +z — diverges from +w, which is in SEND_UMODES only for oracle
faithfulness).
- OPERWALL <text>: oper-only (481), 461 on empty, fans :mask OPERWALL :text to
local +z, propagates :<uid> OPERWALL :text; inbound fans is_local_uid-gated +
split-horizon relay.
- mode.rs UserMode::Operwall (+ is_operwall predicate); umode_diff/RENDERED;
004 umode literal +z; help/OPERWALL.md + USER_MODES.md row.
- The mode.rs round-trip test also moved off "z" as its unknown example to ">".
Tests: command/operwall.rs + s2s/operwall.rs units (mirror wallops), +z oper-gate
units, golden_operwall.rs (+z oper receives, -z oper and -z sender do not),
operwall_proptest.rs (delivery decided by +z alone). 5 welcome-burst snapshots
gain the z letter.
cargo test -p leveva green; clippy clean; build --workspace 0 warnings.
fix(leveva): burst NJOIN carries the channel TS — P11 slice 215
A channel a server learned **for the first time in a burst** was re-clocked to
the receiver's local nanosecond clock instead of adopting the origin's
`created_at`: the leading `CHANTS` is dropped on an unknown channel
(`apply_encap_chants` → `ChanTsOutcome::Unknown`), and the burst `NJOIN` was
TS-less, so `handle_njoin` stamped `clock::unixnano()`. Two servers then
disagreed on a channel's TS by *when each learned it*, and the next merge
deopped the later learner — the divergence slice 167 fixed for the live NJOIN,
left unfixed on the burst path. Observed in prod: updating a server to v0.1.0
and relinking stripped `#xe`'s ops + `+R` that should have survived the merge.
Fix: the burst `NJOIN` now carries the channel TS as a middle param
(`:<sid> NJOIN <chan> <ts> :<members>`, the live form since slice 167) so a
peer adopts the origin's TS. Safe w.r.t. the merge case despite the slice-167
caveat: the burst is CHANTS-first (slice 171), so `we_lost` reads the
pre-merge `created_at` before NJOIN runs, and `njoin_member` only stamps on
creation — so for an existing channel the NJOIN-TS is a no-op (CHANTS owns
arbitration), and it only supplies the TS for a newly-learned channel.
`njoin_member_chunks` subtracts `1 + ts_digits` from the line budget; the boot
canonicalizer masks the new `NJOIN <chan> <ts> :` TS like CHANTS/USERTS.
Tests: burst_channel_ts_is_adopted_not_reclocked (the regression),
burst_njoin_line_carries_the_ts_middle_param, …_does_not_clobber_an_existing
_channels_arbitrated_ts (inverse merge), and a fuzz round-trip over arbitrary
origin TS. Updated the two burst snapshots + golden_s2s_njoin_live.
Gate: cargo test -p leveva green (pre-existing golden_s2s_keepalive BrokenPipe
flake aside); clippy clean; workspace build 0 warnings.
feat(leveva): +l locops + LOCOPS command, and make all user modes global — P11 slice 254
Part A — +l locops user mode + LOCOPS command (charybdis UMODE_LOCOPS), the
local-server sibling of +z/OPERWALL: an oper broadcasts to +l users on the
originating server only; the command never crosses a link. Oper-gated to set,
letter 'l' (bit 0x40000). New command/locops.rs + golden_locops.rs +
locops_proptest.rs. The last named standard charybdis umode — surface complete.
Part B — leveva has NO local-only user modes (product decision). SEND_UMODES is
now every UserMode::ALL bit (self-maintaining const loop), so +O/+x/+s/+g/+G/+D/
+R/+Q/+z/+l all propagate and a remote WHOIS shows them. Verified safe: the burst
already rendered the full unmasked set, and a user's host is always carried
explicitly (UNICK field / ENCAP CHGHOST), never recomputed from the +x bit, so
+x cannot double-apply the cloak (+x now emits both UMODE +x and ENCAP CHGHOST).
Enforcement unchanged: caller-id (+g/+G) and +R stay gated on the target's home
server — the ACCEPT list lives only there and is not propagated, so those S2S
gates are the actual enforcement, not redundant re-checks, and are kept.
- mode.rs: UserMode::LocOps + is_locops(); doc sweep (bit propagates, enforcement
is server-local)
- command/mode.rs: 'l' in the oper-gated arm + umode_diff; +x test asserts both frames
- s2s/umode.rs: SEND_UMODES = every umode; exclusion test -> carries-every-mode
- snotice.rs / s2s/relay.rs: stale 'not in SEND_UMODES' comments corrected
- help/LOCOPS.md + USER_MODES +l row; 004 umode list -> ...zSl (6 snapshots)
- golden_s2s_umode rewritten (demonstrates +D now propagating)
feat(leveva)!: per-connection SendQ accounting + Max-SendQ backpressure kill
The per-connection mailbox was an unbounded `mpsc`, so a client that stops
reading its socket (TCP backpressure blocks its serve loop's `write_all`)
let every other connection's fan-out pile onto its queue without limit — a
write-side memory DoS, the twin of the slice-118 recvq cap. `STATS l`'s
`<sendq>` was hardcoded to 0 for the same reason.
Account the live queue depth on the slice-145 shared `ConnStats`: the
registry's four client-delivery sites add a frame's `wire_len` on enqueue,
`serve_client` drains it on dequeue. `sendq_admit(len, max)` kills a
connection whose depth overruns its ceiling — it latches dead (one
terminal `ERROR :Closing Link: <host> (Max SendQ exceeded)` eject, the
rest dropped, bounding memory even under a wedged socket). KILL ejects and
collision SAVEs bypass the cap (control plane, always delivered).
The ceiling is the connection class's `sendq`, or `DEFAULT_SENDQ` (1 MiB)
when the class sets none — a local client always has a ceiling (a SendQ
limit is a safety mechanism, never truly unbounded, like the oracle's
MAXSENDQ). Remote-user / test claims (`try_claim`, no serve loop draining
them) pass `sendq_max = 0`: accounted but never killed. `STATS l` now
renders the live depth (canonicalizer already masks the 211 tail).
Test fixtures carried a cosmetic `sendq 1000` (meaningless when it was
unenforced; 1000 bytes can't hold a handful of lines). Now that it is
load-bearing, bump every fixture to the shipped example's realistic
`512000`, and refresh the one `STATS Y` golden that snapshots it.
Unit-tested (connstats admit/overflow/drain/latch + registry eject/bypass/
depth, with inverse round-trips); the kill path is timing-dependent over a
real socket so it is fuzzed/unit-tested rather than boot-golden'd.
feat(leveva): TESTLINE ban-match diagnostic (P11 slice 285)
TESTLINE <[nick!]user@host | ip/cidr | nick | #channel> — a charybdis-style
read-only operator command, the capstone of the ban family (RESV 272, TKLINE
281, DLINE 284). It reports which active ban (K/D-line or RESV) would match a
mask, laying/lifting nothing.
- Param gate (461) then a plain oper-bit gate (any oper, no per-ban privilege
→ 481), matching charybdis (TESTLINE needs IsOper with no specific flag).
- A pure probe() classifier strips an optional nick! then routes by mask shape:
user@host → D-line(host)-then-K-line; #channel → channel RESV; bare token →
D-line(as IP)-then-nick RESV. Reports the first hit, priority D > K > R.
- New numerics RplTestline=725 (<type> <minutes-left> <ban-mask> :<reason>; 0
minutes for a permanent RESV) and RplNotestline=726 (<mask> :No matches).
Expiry honoured for free via find_active.
- TESTLINE.md help + COMMANDS allowlist (bijection test).
leveva-native, no oracle (2.11 has no TESTLINE): single best match, no
I-line/auth-block arm, local-only (no S2S propagation, mirroring charybdis).
Tests (TDD, failing first + fuzzing): command/testline.rs units + inline
probe_is_total proptest; testline_proptest.rs (reply-well-formed, non-oper-481,
laid-ban-always-reported inverse); golden_testline.rs (real binary, minutes
field masked). cargo test -p leveva green (2543 lib), clippy clean, workspace
0 warnings.
feat(leveva): TESTMASK mask-population diagnostic (P11 slice 288)
TESTMASK <[nick!]user@host> [<gecos>] counts how many connected clients match
a hostmask (and optional realname glob), split into local vs remote — the
read-only population sibling of TESTLINE (slice 285, which reports bans),
together completing charybdis's test* diagnostic family.
461 param gate before the plain oper-bit gate (481); a malformed mask (no @, or
empty user/host) yields a NOTICE :Invalid parameters (faithful to charybdis's
sendto_one_notice, not a numeric); a registry sweep glob-matches
nick/user/host(+orighost)/gecos (nick & gecos default *) and tallies
is_local_uid into one 727 RPL_TESTMASKGECOS reply. New numeric 727 (charybdis's
724 is dead — no format string, no caller). Pure parse_mask fuzz seam.
Confirmed against cloned charybdis m_testmask.c / messages.h / numeric.h.
leveva-native, local-only (no S2S, mirroring charybdis).
Tests: 13 units (inverse filters + quit-drops-count), golden_testmask (real
binary), testmask_proptest (4 properties incl. l+g <= population, never panics).
feat(leveva): channel modes, chanop bootstrap, NAMES prefixes + gates
Give channels a privilege model and mode-state. The first joiner of a fresh
#/& channel becomes a chanop (@), mirroring the oracle's m_join bootstrap;
membership now tracks per-member op/voice (MemberStatus), surfaced as the @/+
NAMES prefixes.
channel.rs gains ChannelModes (flag bitmask + key + limit + ban list) and the
operations over it: apply_modes (op-gated, no-op-eliding, returning the
effective changes + 441/467 signals), check_join (+k/+l/+i/+b enforcement),
can_send (+n/+m), channel_mode_string (324 render), is_op, bans. set_topic now
honours +t (SetTopic::NeedOps).
command.rs adds the MODE handler: channel query (324), ban-list (367/368), the
mode-string parser over the supported set (o v n m t i p s k l b; others 472),
op-gate (482), 401/441/467, and the coalesced :src MODE broadcast. The
retroactive gates are wired: +t -> TOPIC 482, +n/+m -> PRIVMSG/NOTICE 404, and
JOIN enforcement -> 475/471/473/474. MODE <nick> is a minimal 221/502.
Two boot-golden snapshots shift to show the creator's @ prefix in NAMES.
feat(leveva): user modes (MODE <nick> → +i/+w) + config default +iw
P11 slice 14: the first *user*-mode slice. `MODE <nick>` was a stub
(221 `+` / 502); now a client has real, tracked user modes, a
configurable registration default, and +i (invisible) affects WHO.
- State: a u32 FLAGS_* bitmask (reusing the P10 UserMode enum) on
command::Registered (authoritative) + registry::ClientRecord (the
WHO-visible snapshot), synced by Registry::set_modes.
- Handler (command/mode.rs): faithful to the oracle m_umode resolved
to the client-toggleable set — other nick → 502; own nick query →
221 +<render>; change string applies i/w, silently ignores o/O/r/a
(no 501), 501s any unknown letter; on a net change echoes
:nick MODE nick :<diff> (send_umode sign-grouped diff) + mirrors to
the registry. No change → no echo.
- Config: `default-user-modes` (options block), restricted to i/w,
absent → +iw; Options::Default matches so a config with no options
block is also +iw (the out-of-box default). Seeded silently at
registration; documented in examples/*.kdl.
- WHO: +i users are omitted from the user sweeps (no-arg/0/*, exact,
glob) unless the requester shares a channel or is them; channel WHO
is unaffected. Retires the "no +i filtering" divergence.
Tests: +10 umode unit (with inverses), +4 WHO invisible-filter unit,
+1 config matrix, golden_usermode boot snapshot, usermode_proptest
(3 properties / 400 cases: model lockstep, WHO visibility matrix,
arbitrary-bytes panic-freedom). golden_who regenerated — carol now
hidden from the global sweep (a clean +iw-default demonstration); the
12 direct-ServerContext proptest harnesses get default_user_modes: 0.
cargo test -p leveva (328 lib + golden + proptest) + -p
leveva-integration green; clippy clean; workspace 0 warnings.