name: leveva-config-ban-enforcement description: "leveva P11 slice 191 — static config ban (K-line) blocks are now enforced at registration (were silently dead); the config_ban_match pure core, the kline_exempt-gated finalize gate, and why no-resolve/xline-exempt stay deferred by design" metadata: node_type: memory type: project originSessionId: 41caad9f-3671-4d56-9ee6-9d5e75bb53b7#
P11 slice 191 fixed a silent gap: config.bans (the parsed ban { mask; reason; gecos } blocks
— the C static K-line) was never enforced. ServerContext::from_config started
klines: KlineStore::new() empty and nothing read config.bans — only runtime TKLINEs
(ctx.klines) bit, so an admin's static ban blocks did nothing.
Now enforced at session::finalize_registration, after the runtime K-line gate, gated by
!admission.kline_exempt (static bans are K-lines → the I-line CFLAG_KEXEMPT exemption bypasses
them too). Pure core: kline::config_ban_match(bans, user, host, realname) -> Option<&Ban> — the
first ban whose user@host mask matches (CIDR-aware, via the shared
matching::allow_host_matches) and whose optional gecos glob-matches the realname (None
ignores it). Refusal = 465 :You (user@host) are banned … : <reason> + ERROR :Closing Link … (Kill line active: <reason>) + closing. A *@* mask + a gecos pattern is a realname/X-line
ban.
Live/rehash: held via the slice-91 live-or-ctx ConfStore idiom — bans added to
StatsConf (boot fallback), ConfBlocks/config_conf, ConfStore + control::live_bans();
seeded in main.rs. REHASH re-reads ban blocks (see [[leveva-rehash-matrix]]).
Why the other two slice-190 "carried-not-acted" allow attrs stay DEFERRED BY DESIGN (not a
backlog): no-resolve is inert — leveva is IP-only (client host = peer.ip().to_string(),
no reverse DNS anywhere, leveva_iauth::AuthVerdict has no hostname field) so there is no resolved
hostname to suppress; xline-exempt has no separate X-line list because a gecos'd static
ban already subsumes realname bans. Don't "implement" either without first building the absent
underlying feature (reverse DNS / a distinct X-line block).
Follow-on CLOSED (slice 192): STATS k now appends one 216 RPL_STATSKLINE per static
config ban (k <host> <user> :<reason>, no ttl — absent integer marks a permanent ban,
4 params vs the runtime t-kline's 5) after the runtime KlineStore, read live-or-ctx. Pure core
stats::config_ban_report + split_ban_mask (last-@ split); gecos folds into the reason
trailing. Config bans stay under lowercase k (unified view); uppercase K stays recognized-but-
empty. Tests: stats units + stats_k_appends_one_216_per_config_ban proptest + golden_stats_config_ban.rs.
Follow-on CLOSED (slice 197): the kline-exempt I-line flag (the C CFLAG_KEXEMPT →
persistent FLAGS_EXEMPT) is now honoured at runtime TKLINE reaping, not just the two
registration-time gates. It was consumed at registration and never stored, so an already-registered
exempt client got reaped anyway by an operator's later matching TKLINE. Fix: new
ClientRecord.kline_exempt field (default false; remote/test claims are never local K-line
targets) + Registry::set_kline_exempt setter, mirrored from admission.kline_exempt in
finalize_registration (next to set_class); command::tkline's reap loop now continues past
rec.kline_exempt before the mask test (the ban is still laid — exemption protects the client,
not the ban). Tests: tkline_does_not_reap_a_kline_exempt_connection unit +
set_kline_exempt_toggles… registry unit + a_kline_exempt_iline_marks_the_record_exempt session
unit + kline_proptest::exempt_clients_are_never_reaped (ejected set == covered ∧ ¬exempt).
Tests: kline::config_ban_match_* units + config_ban_match_equals_a_reference_scan proptest;
session gate tests (a_config_banned_host_is_refused_465 + inverses + the exempt bypass);
golden_rehash_ban.rs boot-golden (enforce + live reload). Recording per [[leveva-slice-recording]].