nordisk-pairing 0.5.26

The boxes of an estate find each other: a seed each box is installed with, a one-time join against the front, a tunnel (WireGuard by default) they meet inside, roles decided once, a pairing loop each box runs itself, and the signal it reports. Shared by gunnar and holger; the laptop's xtask only lays machines, writes seeds and reads the signal. Design: .nornir/pairing-design.md (2026-09-24).
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
//! **What a collected log line may never carry** (0.3.0). The installer
//! stores every VM's boot log on the operator's machine; a line that carries
//! a secret is withheld there — replaced by its class and its size, never a
//! byte of the content. The rule moved here from private-gunnar-ops'
//! `xtask/src/bootnarr.rs` (the UDP boot narration's filter, retired with the
//! narration on 2026-09-24), so gunnar and holger share one filter.
//!
//! The classes, and why each one is here:
//!
//! * `cmdline` — the kernel command line carries `korp.args` with the
//!   monetize public key and `clock_key=` on it, so ONE echoed cmdline
//!   discloses the medium's whole configuration.
//! * `token` — any admin or API token, any `Bearer`, a password, a secret. Since 0.5.26
//!   a line that only NAMES one is kept with its VALUES withheld in place (test3-holger-rs
//!   2026-10-22: "waiting for holger_console_token" was dropped whole for 40 minutes);
//!   withheld whole still when it also carries a Stripe secret, key material, an
//!   environment file or an environment record.
//! * `key` — any signing or private key material, and any SSH key line.
//! * `pubkey` — a PUBLIC verifying key. It discloses nothing; it is withheld
//!   anyway, on the owner's rule, because it is not progress.
//! * `env` — the shape of a secrets file being read out.
//! * `envrecord` — an environment record (`NAME=value`, an upper-case variable):
//!   a kernel `with environment:` line, a process printing its environment.
//! * `highentropy` — a long unbroken base64/hex run: what a key looks like
//!   when nobody labelled it.

/// The class a line must be withheld under, or `None` when it may be kept.
pub fn secret_class(line: &str) -> Option<&'static str> {
    let lower = line.to_ascii_lowercase();
    if lower.contains("kernel command line") || lower.contains("/proc/cmdline") || lower.contains("korp.args") || lower.contains("korp.log_udp=") || lower.contains("korp.env.") || lower.contains("clock_key=") {
        return Some("cmdline");
    }
    if lower.contains("token") || lower.contains("bearer ") || lower.contains("password") || lower.contains("secret") || stripe_secret(&lower) || has_url_userinfo(line) {
        return Some("token");
    }
    if lower.contains("private key") || lower.contains("begin openssh") || lower.contains("ssh-ed25519") || lower.contains("ssh-rsa") || lower.contains("signing.key") || lower.contains("time_ed25519") {
        return Some("key");
    }
    if lower.contains("pubkey") || lower.contains("public key") || lower.contains("sha256:") {
        return Some("pubkey");
    }
    if lower.contains(".env") {
        return Some("env");
    }
    if has_env_record(line) {
        return Some("envrecord");
    }
    if longest_opaque_run(line) >= 40 {
        return Some("highentropy");
    }
    None
}

/// A Stripe secret by its prefix (2026-09-28): a secret or restricted API key
/// (`sk_live_`, `sk_test_`, `rk_live_`, `rk_test_`) or a webhook signing
/// secret (`whsec_`). A real `whsec_` value is 32 base64 characters —
/// shorter than the `highentropy` run — and a test key can be shorter still,
/// so the prefix is the rule: the front logs monetize's lines now
/// (gunnar-front stderr capture), and monetize talks to Stripe.
fn stripe_secret(lower: &str) -> bool {
    ["sk_live_", "sk_test_", "rk_live_", "rk_test_", "whsec_"].iter().any(|p| lower.contains(p))
}

/// **An environment record**: a `NAME=value` token whose name is an upper-case
/// variable (3+ of `A-Z0-9_`, a letter first) — a kernel `with environment:` record,
/// or a process printing its environment. The value is what must not travel; a short
/// secret-shaped value escapes the `highentropy` run, so the shape is the rule
/// (the same rule gunnar-server's narration door and nordisk-narrate's kmsg follower keep).
fn has_env_record(line: &str) -> bool {
    line.split_whitespace().any(|tok| {
        let Some((name, value)) = tok.split_once('=') else { return false };
        !value.is_empty() && name.len() >= 3 && name.starts_with(|c: char| c.is_ascii_uppercase()) && name.chars().all(|c| c.is_ascii_uppercase() || c.is_ascii_digit() || c == '_')
    })
}

/// The longest unbroken run of base64/hex alphabet.
fn longest_opaque_run(line: &str) -> usize {
    let mut best = 0usize;
    let mut run = 0usize;
    for c in line.chars() {
        if c.is_ascii_alphanumeric() || c == '+' || c == '/' || c == '=' {
            run += 1;
            best = best.max(run);
        } else {
            run = 0;
        }
    }
    best
}

/// `line` with every opaque base64/hex run of the `highentropy` length (40)
/// replaced by [`VALUE_WITHHELD`].
fn withhold_runs(line: &str) -> String {
    let mut out = String::with_capacity(line.len());
    let mut run = String::new();
    let flush = |run: &mut String, out: &mut String| {
        if run.chars().count() >= 40 {
            out.push_str(VALUE_WITHHELD);
        } else {
            out.push_str(run);
        }
        run.clear();
    };
    for c in line.chars() {
        if c.is_ascii_alphanumeric() || c == '+' || c == '/' || c == '=' {
            run.push(c);
        } else {
            flush(&mut run, &mut out);
            out.push(c);
        }
    }
    flush(&mut run, &mut out);
    out
}

/// The text that stands in for a withheld line.
pub fn withheld(class: &str, bytes: usize) -> String {
    format!("<<WITHHELD: a {class}-class line of {bytes} bytes was not recorded>>")
}

/// **The text that stands in for a RUN of withheld lines** of one class, in
/// a row (0.5.5): one line with the count, never one line each. A run of one
/// is [`withheld`] exactly.
pub fn withheld_run(class: &str, count: u64, bytes: u64) -> String {
    if count == 1 {
        return withheld(class, bytes as usize);
    }
    format!("<<WITHHELD: {count} {class}-class lines in a row, {bytes} bytes, were not recorded>>")
}

/// What stands in for a public key's VALUE inside a kept line (0.5.5).
pub const VALUE_WITHHELD: &str = "<withheld>";

/// Is `line` this module's own marker? A marker is kept as it is: cleaning
/// is idempotent, so the VM and the installer may both clean.
pub fn is_marker(line: &str) -> bool {
    line.starts_with("<<WITHHELD: ")
}

/// What cleaning makes of one line.
#[derive(Debug, Clone, PartialEq, Eq)]
pub enum Cleaned<'a> {
    /// The line may be stored: itself, or itself with its public-key values
    /// withheld in place.
    Kept(std::borrow::Cow<'a, str>),
    /// The whole line is withheld: only its class and its size may be.
    Withheld { class: &'static str, bytes: usize },
}

/// **What may be stored of `line`** (0.5.5). A line of the `pubkey` class
/// is KEPT with every public-key value withheld inside it; a line of any
/// other class is withheld whole; a marker is kept as it is.
///
/// Why `pubkey` is redacted inside the line (MEASURED 2026-09-28, test2's
/// main VM: 8 502 of 20 008 held lines read back as a pubkey-class marker —
/// every ssh login and every audited RPC names the key's fingerprint): the
/// line that says "a key logged in" is progress; only the key is not. A
/// whole-line marker per login made the log unreadable and said nothing a
/// withheld value does not.
pub fn cleaned(line: &str) -> Cleaned<'_> {
    if is_marker(line) {
        return Cleaned::Kept(std::borrow::Cow::Borrowed(line));
    }
    match secret_class(line) {
        None => Cleaned::Kept(std::borrow::Cow::Borrowed(line)),
        Some("pubkey") => match redact_values(line) {
            Some(kept) => Cleaned::Kept(std::borrow::Cow::Owned(kept)),
            None => Cleaned::Kept(std::borrow::Cow::Borrowed(line)),
        },
        // An unlabelled opaque run (a hash, a digest — or a key nobody
        // labelled) is withheld IN PLACE: the run never stored, the line kept.
        Some("highentropy") => Cleaned::Kept(std::borrow::Cow::Owned(withhold_runs(line))),
        // A line that NAMES a token, a bearer, a password or a secret is progress (0.5.26):
        // only the VALUES in it are withheld, in place. Withheld whole still: a line that
        // also carries a Stripe secret, key material, an environment file or record.
        Some("token") => match token_line_kept(line) {
            Some(kept) => Cleaned::Kept(std::borrow::Cow::Owned(kept)),
            None => Cleaned::Withheld { class: "token", bytes: line.len() },
        },
        Some(class) => Cleaned::Withheld { class, bytes: line.len() },
    }
}

/// **The line as it may be stored**: itself, itself with its public-key
/// values withheld in place, or the withheld marker. Idempotent.
pub fn clean(line: &str) -> std::borrow::Cow<'_, str> {
    match cleaned(line) {
        Cleaned::Kept(kept) => kept,
        Cleaned::Withheld { class, bytes } => std::borrow::Cow::Owned(withheld(class, bytes)),
    }
}

/// **Many lines as they may be stored** (0.5.5): each [`clean`]ed, and a run
/// of withheld lines of one class in a row folded into ONE counted marker
/// ([`withheld_run`]) — so redaction never multiplies lines, also for a VM
/// that holds its lines raw (before 0.5.5).
pub fn clean_lines<'a, I: IntoIterator<Item = &'a str>>(lines: I) -> Vec<String> {
    let mut out: Vec<String> = Vec::new();
    let mut run: Option<(&'static str, u64, u64)> = None;
    for line in lines {
        match cleaned(line) {
            Cleaned::Kept(kept) => {
                out.push(kept.into_owned());
                run = None;
            }
            Cleaned::Withheld { class, bytes } => {
                if let Some((c, n, b)) = run.as_mut() {
                    if *c == class {
                        *n += 1;
                        *b += bytes as u64;
                        let text = withheld_run(class, *n, *b);
                        if let Some(last) = out.last_mut() {
                            *last = text;
                        }
                        continue;
                    }
                }
                out.push(withheld(class, bytes));
                run = Some((class, 1, bytes as u64));
            }
        }
    }
    out
}

/// A word that names a secret (`token`, `Bearer`, `password`, `secret`, `Authorization`,
/// `credential`, an API key): the word itself is a name, never a value.
fn names_a_secret(word: &str) -> bool {
    let w = word.to_ascii_lowercase();
    ["token", "bearer", "password", "passwd", "secret", "authorization", "credential", "apikey", "api_key", "api-key"].iter().any(|n| w.contains(n))
}

/// **The plain words that may follow a secret's NAME** and are no value (`token is missing`,
/// `password incorrect`, `bearer expired`): after a bare naming word any other word is
/// withheld (the Gunnar session's review of 0.5.26: `password correcthorsebattery`).
const PLAIN_AFTER_A_NAME: &[&str] = &[
    "a", "an", "the", "is", "was", "are", "were", "be", "been", "has", "have", "had", "it", "its", "this", "that", "and", "or", "not", "no", "for", "from", "to", "in", "of", "on", "at",
    "by", "with", "as", "into", "over", "via", "missing", "incorrect", "accepted", "refused", "rejected", "expired", "unfed", "fed", "declared", "written", "set", "unset", "required", "invalid",
    "empty", "ok", "file", "rotated", "revoked", "minted", "read", "loaded", "saved", "changed", "unchanged", "present", "absent", "given", "wrong", "failed", "denied", "mismatch", "too",
    "short", "weak", "unknown", "yet", "never", "again", "now", "still", "already", "issued", "verified", "checked", "matched", "kept", "dropped", "cleared", "renewed", "sealed", "derived",
    "offered", "waits", "waiting", "until", "after", "before", "since", "when", "while",
];

fn plain_after_a_name(word: &str) -> bool {
    let w = word.trim_matches(|c: char| !c.is_ascii_alphanumeric()).to_ascii_lowercase();
    PLAIN_AFTER_A_NAME.contains(&w.as_str())
}

/// The first `=` or `:` of `seg` that is no URL's `://`.
fn kv_sep(seg: &str) -> Option<usize> {
    seg.char_indices().find(|&(i, c)| (c == '=' || c == ':') && !seg[i..].starts_with("://")).map(|(i, _)| i)
}

/// `seg` with the password of a URL's userinfo (`scheme://user:pass@host`) withheld.
fn withhold_userinfo(seg: &str) -> Option<String> {
    let at = seg.find("://")? + 3;
    let rest = &seg[at..];
    let end = rest.find(['/', '?', '#']).unwrap_or(rest.len());
    let auth = &rest[..end];
    let amp = auth.rfind('@')?;
    let colon = auth[..amp].find(':')?;
    let pass = &auth[colon + 1..amp];
    (!pass.is_empty() && pass != VALUE_WITHHELD).then(|| format!("{}{}{VALUE_WITHHELD}{}", &seg[..at], &auth[..=colon], &seg[at + amp..]))
}

/// Does `line` carry a URL with a password in its userinfo?
fn has_url_userinfo(line: &str) -> bool {
    line.split_whitespace().any(|w| withhold_userinfo(w).is_some())
}

/// `w` with every secret-naming `k=v` / `k:v` VALUE withheld, a URL's query (`?`, `&`, `;`
/// parts) included, and a URL's userinfo password; `None` when nothing needed withholding.
fn withhold_kv_parts(w: &str) -> Option<String> {
    let mut out = String::with_capacity(w.len());
    let mut changed = false;
    for (i, seg) in w.split(['?', '&', ';']).enumerate() {
        if i > 0 {
            // The delimiter this segment followed, as it was.
            let at: usize = w.split(['?', '&', ';']).take(i).map(|s| s.len() + 1).sum::<usize>() - 1;
            out.push_str(&w[at..at + 1]);
        }
        let seg = match withhold_userinfo(seg) {
            Some(s) => {
                changed = true;
                std::borrow::Cow::Owned(s)
            }
            None => std::borrow::Cow::Borrowed(seg),
        };
        match kv_sep(&seg).filter(|&at| names_a_secret(&seg[..at])) {
            Some(at) if !matches!(seg[at + 1..].trim_matches(|c: char| matches!(c, '"' | '\'' | '`' | ',' | '}' | ')')), "" | VALUE_WITHHELD) => {
                out.push_str(&seg[..=at]);
                out.push_str(VALUE_WITHHELD);
                changed = true;
            }
            _ => out.push_str(&seg),
        }
    }
    changed.then_some(out)
}

/// **A `token`-class line as it may be kept**, or `None` when it must be withheld whole
/// (it also carries a Stripe secret, key material, an environment file or record). Every
/// value is replaced by [`VALUE_WITHHELD`]: the value of a secret-naming `k=v` / `k:v` (also
/// in a URL's query), a URL's userinfo password, the word after `Basic`, the word after a
/// secret-naming `k:`, any word after a bare secret-naming word that is no plain word
/// ([`PLAIN_AFTER_A_NAME`]), and any opaque run of 16 or more; a public key's value as
/// [`cleaned`] withholds it. Words are split on any whitespace, kept as it was. The names
/// stay: "waiting for holger_console_token" is kept.
fn token_line_kept(line: &str) -> Option<String> {
    let lower = line.to_ascii_lowercase();
    let key_material = lower.contains("private key") || lower.contains("begin openssh") || lower.contains("ssh-ed25519") || lower.contains("ssh-rsa") || lower.contains("signing.key") || lower.contains("time_ed25519");
    if stripe_secret(&lower) || key_material || lower.contains(".env") || has_env_record(line) {
        return None;
    }
    // The line as words and the whitespace between them, kept as it was.
    let mut pieces: Vec<(bool, &str)> = Vec::new();
    let mut start = 0;
    let mut in_word = false;
    for (i, c) in line.char_indices() {
        let word_char = !c.is_whitespace();
        if i > start && word_char != in_word {
            pieces.push((in_word, &line[start..i]));
            start = i;
        }
        in_word = word_char;
    }
    if start < line.len() {
        pieces.push((in_word, &line[start..]));
    }
    let mut out = String::with_capacity(line.len());
    let mut prev = "";
    for (is_word, w) in pieces {
        if !is_word {
            out.push_str(w);
            continue;
        }
        let bare = |x: &str| x.trim_matches(|c: char| !c.is_ascii_alphanumeric()).to_ascii_lowercase();
        let has_alnum = w.chars().any(|c| c.is_ascii_alphanumeric());
        let auth_scheme = matches!(bare(w).as_str(), "bearer" | "basic");
        let prev_bare_name = names_a_secret(prev) && !prev.contains(['=', ':']);
        let word = if w == VALUE_WITHHELD {
            w.to_string()
        } else if let Some(k) = withhold_kv_parts(w) {
            k
        } else if prev == "Basic" && has_alnum {
            VALUE_WITHHELD.to_string()
        } else if names_a_secret(prev) && prev.ends_with([':', '=']) && !auth_scheme && !names_a_secret(w) && has_alnum {
            VALUE_WITHHELD.to_string()
        } else if prev_bare_name && has_alnum && !names_a_secret(w) && !auth_scheme && !plain_after_a_name(w) {
            VALUE_WITHHELD.to_string()
        } else if longest_opaque_run(w) >= 16 {
            VALUE_WITHHELD.to_string()
        } else {
            w.to_string()
        };
        out.push_str(&word);
        prev = w;
    }
    Some(redact_values(&out).unwrap_or(out))
}

/// A word that names a public key (`pubkey`, `public_key`, `monetize_pubkey`,
/// `fingerprint`).
fn names_a_key(word: &str) -> bool {
    let w = word.to_ascii_lowercase();
    w.contains("pubkey") || w.contains("public_key") || w.contains("public-key") || w.contains("fingerprint")
}

/// A value that looks like key material: a `SHA256:` fingerprint or an
/// opaque base64/hex run of 16 or more.
fn looks_like_key(value: &str) -> bool {
    value.to_ascii_lowercase().contains("sha256:") || longest_opaque_run(value) >= 16
}

/// A short word that may be a key's value when a key-naming word precedes
/// it: the base64/hex alphabet only, with at least one digit (`eb16`, not
/// `revoked`).
fn short_value(word: &str) -> bool {
    !word.is_empty()
        && word.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '+' | '/' | '=' | ':'))
        && word.chars().any(|c| c.is_ascii_digit())
}

/// The line with every public-key value replaced by [`VALUE_WITHHELD`]: a
/// `SHA256:` fingerprint, any long opaque run, the value of a key-naming
/// `k=v`, and a value-shaped word after a key-naming word or `public key`.
/// `None` when nothing needed withholding.
fn redact_values(line: &str) -> Option<String> {
    let words: Vec<&str> = line.split(' ').collect();
    let mut out: Vec<String> = Vec::with_capacity(words.len());
    let mut after_name = false;
    for (i, w) in words.iter().enumerate() {
        let word = if *w == VALUE_WITHHELD {
            (*w).to_string()
        } else if let Some((k, v)) = w.split_once('=') {
            let bare = v.trim_matches('"');
            if !bare.is_empty() && bare != VALUE_WITHHELD && (names_a_key(k) || looks_like_key(bare)) {
                format!("{k}={VALUE_WITHHELD}")
            } else {
                (*w).to_string()
            }
        } else if looks_like_key(w) || (after_name && short_value(w)) {
            VALUE_WITHHELD.to_string()
        } else {
            (*w).to_string()
        };
        let public_key = i > 0 && words[i - 1].eq_ignore_ascii_case("public") && w.eq_ignore_ascii_case("key");
        after_name = !w.contains('=') && (names_a_key(w.trim_end_matches(':')) || public_key);
        out.push(word);
    }
    let kept = out.join(" ");
    (kept != line).then_some(kept)
}

#[cfg(test)]
mod tests {

    /// **A hash is not a secret, and a line is not its hash** (MEASURED
    /// estate-box3 2026-09-30): every line with a 40-character run was
    /// withheld WHOLE — every git push naming a commit, gunnar's
    /// self-registration naming its plugin's blake3 ("67 highentropy-class
    /// lines in a row" on main), so the installer never saw the step, and an
    /// ERROR naming a hash would never reach the log gate. The run is withheld
    /// IN PLACE; the line — its time, level, target and words — is kept.
    #[test]
    fn an_opaque_run_is_withheld_in_place_and_its_line_kept() {
        let sha = "0123456789abcdef0123456789abcdef01234567";
        let l = format!("2026-09-30T07:00:00.1Z ERROR gunnar::store: push rejected for {sha}: object missing");
        match cleaned(&l) {
            Cleaned::Kept(k) => {
                assert!(!k.contains(sha), "{k}");
                assert!(k.contains("ERROR gunnar::store: push rejected for <withheld>: object missing"), "{k}");
            }
            w => panic!("the line was withheld whole: {w:?}"),
        }
        let digest = "dba8e30fe8af4a881572f52144afa386df347e80f5029066f695810784e751dd";
        let reg = format!("2026-09-30T07:00:00.1Z  INFO gunnar_server::self_register: plugin: ADDED — gunnar blake3 {digest} event=\"plugin.self_register\"");
        let kept = clean(&reg);
        assert!(kept.contains("plugin: ADDED — gunnar blake3 <withheld>") && !kept.contains(digest), "{kept}");
        // An unlabelled key run is still never stored, whatever surrounds it.
        let key = "AAAAC3NzaC1lZDI1NTE5AAAAIGx8r8b4v8v5r7q2Zk1pQ3aQ";
        assert!(!clean(&format!("x {key} y")).contains(key));
        assert_eq!(clean(&clean(&reg)), clean(&reg), "idempotent");
    }
    use super::*;

    /// **A line that NAMES a token is progress; only a token's VALUE is withheld**
    /// (test3-holger-rs 2026-10-22: the main stood 40+ minutes at `TICK stage=pairing`
    /// and its whole installer log read `<<WITHHELD: 2 token-class lines in a row, 618
    /// bytes, were not recorded>>` every 30 s — the box said what it waited for, by
    /// name, and the filter dropped the line because the name has `token` in it).
    #[test]
    fn a_line_that_names_a_token_is_kept_with_only_the_values_withheld() {
        let w = VALUE_WITHHELD;
        for (line, kept) in [
            // Names only: kept word for word.
            ("pairing: waiting for holger_console_token from the shop".to_string(), "pairing: waiting for holger_console_token from the shop".to_string()),
            (
                "pair status: main: step serve (try 81: waiting for the server: the boot waits for the installer to feed `holger_console_token` (/data/holger-console-token is declared and not written yet)) · not paired".to_string(),
                "pair status: main: step serve (try 81: waiting for the server: the boot waits for the installer to feed `holger_console_token` (/data/holger-console-token is declared and not written yet)) · not paired".to_string(),
            ),
            ("REFUSED [holger-console-token-unfed] the installer never fed the token in 3600 s".to_string(), "REFUSED [holger-console-token-unfed] the installer never fed the token in 3600 s".to_string()),
            ("password incorrect for user admin".to_string(), "password incorrect for user admin".to_string()),
            // Values: masked in place, the line kept.
            ("console: token=Q7zP2xY9abc accepted".to_string(), format!("console: token={w} accepted")),
            ("auth: Authorization: Bearer eyJhbGciOiJFZERTQSJ9 from 10.44.0.1".to_string(), format!("auth: Authorization: Bearer {w} from 10.44.0.1")),
            ("login: password: hunter2 refused".to_string(), format!("login: password: {w} refused")),
            (r#"feed {"admin_token":"0123456789abcdef0123"} kept"#.to_string(), format!(r#"feed {{"admin_token":{w} kept"#)),
            ("fed the console token 9f8e7d6c5b4a39281706f5e4d3c2b1a0 to main".to_string(), format!("fed the console token {w} to main")),
        ] {
            assert_eq!(secret_class(&line), Some("token"), "{line}");
            assert_eq!(clean(&line), kept, "{line}");
            assert_eq!(clean(&clean(&line)), clean(&line), "idempotent: {line}");
        }
        // Still withheld WHOLE: a Stripe secret, a key, an environment record, a cmdline.
        for line in [
            "monetize-server: stripe: POST /v1/checkout/sessions with sk_live_51HabcDEF token",
            "GUNNAR_UI_ADMIN_TOKEN=xyz",
            "reading GUNNAR_UI_ADMIN_TOKEN=abc from the env",
            "token in -----BEGIN OPENSSH PRIVATE KEY-----",
            "the secret is in /etc/monetize/monetize.env",
        ] {
            assert!(clean(line).starts_with("<<WITHHELD: a token-class"), "{line} -> {}", clean(line));
        }
        // And a run of them is still one counted marker; a kept line breaks the run.
        let out = clean_lines(["GUNNAR_UI_ADMIN_TOKEN=a", "GUNNAR_UI_ADMIN_TOKEN=b", "waiting for the token", "GUNNAR_UI_ADMIN_TOKEN=c"]);
        assert_eq!(out, vec![withheld_run("token", 2, 46), "waiting for the token".to_string(), withheld("token", 23)]);
    }

    /// **The Gunnar session's review of 0.5.26** (2026-10-22): a secret in a URL's query or
    /// userinfo, and a lower-case value with no digit after a bare naming word, were kept; a
    /// tab did not separate words. Each is withheld now; a plain word after a name is kept.
    #[test]
    fn a_url_secret_a_plain_value_after_a_name_and_a_tab_separated_value_are_withheld() {
        let w = VALUE_WITHHELD;
        for (line, kept) in [
            ("GET https://x.example/cb?token=ab12cd&state=1 200".to_string(), format!("GET https://x.example/cb?token={w}&state=1 200")),
            ("redirect https://x.example/cb?state=1&access_token=Zz&client_secret=q;password=p".to_string(), format!("redirect https://x.example/cb?state=1&access_token={w}&client_secret={w};password={w}")),
            ("clone https://user:hunter@git.example/r.git".to_string(), format!("clone https://user:{w}@git.example/r.git")),
            ("login: password correcthorsebattery refused".to_string(), format!("login: password {w} refused")),
            ("secret letmein".to_string(), format!("secret {w}")),
            ("console token\tq7zp2xy9abc\tok".to_string(), format!("console token\t{w}\tok")),
            ("password\tincorrect for admin".to_string(), "password\tincorrect for admin".to_string()),
            // Plain words after a name stay: the progress a person reads.
            ("the console token is missing: holger_console_token was never fed".to_string(), "the console token is missing: holger_console_token was never fed".to_string()),
            ("REFUSED the bearer expired at 18:12Z".to_string(), "REFUSED the bearer expired at 18:12Z".to_string()),
            // Gunnar's UI admin token and console bearer (hex / base64 runs) are withheld; the
            // request id and its short value are kept.
            (
                "INFO gunnar_ui::auth: admin token 3f9a8b7c6d5e4f30a1b2c3d4e5f60718 issued x-gunnar-request-id=r-4711".to_string(),
                format!("INFO gunnar_ui::auth: admin token {w} issued x-gunnar-request-id=r-4711"),
            ),
            (
                "WARN gunnar_front: console bearer dGhpc2lzYXZlcnlsb25nYmFzZTY0c2VjcmV0 refused x-gunnar-request-id: r-4712".to_string(),
                format!("WARN gunnar_front: console bearer {w} refused x-gunnar-request-id: r-4712"),
            ),
        ] {
            assert_eq!(clean(&line), kept, "{line}");
            assert_eq!(clean(&clean(&line)), clean(&line), "idempotent: {line}");
        }
    }

    #[test]
    fn progress_is_kept_and_every_secret_class_is_withheld_without_its_content() {
        assert_eq!(clean("korp-start: stage net up at 6.34 s"), "korp-start: stage net up at 6.34 s");
        for (line, class) in [
            ("[    0.000000] Kernel command line: korp.args=monetize_pubkey=abc", "cmdline"),
            ("GUNNAR_UI_ADMIN_TOKEN=xyz", "token"),
            ("-----BEGIN OPENSSH PRIVATE KEY-----", "key"),
            ("reading /etc/monetize/monetize.env", "env"),
            // Stripe's secrets by prefix: a whsec_ value is shorter than the
            // highentropy run, a test key shorter still.
            ("monetize-server: stripe: POST /v1/checkout/sessions with sk_live_51Habc", "token"),
            ("monetize-server: hmac key whsec_5WbX5kEWLlfzsGNjH64I8lOOqUB6e8FH", "token"),
            ("monetize-server: vendor key rk_test_abc", "token"),
        ] {
            assert_eq!(secret_class(line), Some(class), "{line}");
            let c = clean(line);
            assert!(c.starts_with("<<WITHHELD: a ") && c.contains(class) && !c.contains(line), "{c}");
            assert_eq!(clean(&c), c, "a marker cleans to itself");
        }
        // An unlabelled key run: classed highentropy, and withheld IN PLACE —
        // nothing of it stored, the line (here: nothing else) kept.
        let run = "AAAAC3NzaC1lZDI1NTE5AAAAIGx8r8b4v8v5r7q2Zk1pQ3a";
        assert_eq!(secret_class(run), Some("highentropy"));
        assert_eq!(clean(run), VALUE_WITHHELD);
    }

    /// **An environment record is withheld by the SHARED filter** (release-3 DEV-NARRATE
    /// review, redact-no-envrecord): gunnar-server's own narration door withheld
    /// `envrecord` and `korp.env.` lines, this filter — the one the log door and every
    /// other path use — kept them: a kernel `with environment:` record, or any line that
    /// prints `NAME=value` for an upper-case variable, went out with its value. A short,
    /// secret-shaped value passes the `highentropy` run, so the shape is the rule.
    #[test]
    fn an_environment_record_is_withheld_and_names_no_value() {
        for (line, class) in [
            ("kmsg 6,368,90210,-;with environment: HOME=/ TERM=linux KORP_INSTALLER_ROOT=/run/installer", "envrecord"),
            ("kmsg 6,369,90211,-;with environment: MONETIZE_VERIFY_KEY=MCowBQYDK2VwAyEA", "envrecord"),
            ("box: STRIPE_ACCOUNT=acct_1Qx9 MONETIZE_SEAL=q7Zp2", "envrecord"),
            ("app @4s korp.env.GUNNAR_DATA_ROOT applied", "cmdline"),
        ] {
            assert_eq!(secret_class(line), Some(class), "{line}");
            let c = clean(line);
            assert!(c.starts_with("<<WITHHELD: a ") && c.contains(class) && !c.contains("q7Zp2") && !c.contains("MCowBQ"), "{c}");
        }
        // Lower-case structured fields are a tracing line's own, not an environment.
        assert_eq!(secret_class(r#"INFO gunnar::audit: served event="grpc.call" decision="allow" elapsed_ms=3"#), None);
        assert_eq!(secret_class("HTTP/1.1 200 OK"), None);
    }

    #[test]
    fn a_pubkey_line_is_kept_with_its_values_withheld_in_place() {
        let w = VALUE_WITHHELD;
        for (line, kept) in [
            ("fed monetize pubkey eb16".to_string(), format!("fed monetize pubkey {w}")),
            (
                "WARN gunnar::audit: event=\"ssh.auth.accepted\" fingerprint=SHA256:47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU principal=admin".to_string(),
                format!("WARN gunnar::audit: event=\"ssh.auth.accepted\" fingerprint={w} principal=admin"),
            ),
            ("feed: monetize_pubkey=abc public key 1f2e3d4c".to_string(), format!("feed: monetize_pubkey={w} public key {w}")),
            ("public key revoked".to_string(), "public key revoked".to_string()),
        ] {
            assert_eq!(secret_class(&line), Some("pubkey"), "{line}");
            let c = clean(&line);
            assert_eq!(c, kept);
            assert_eq!(clean(&c), c, "idempotent: the VM and the installer both clean");
        }
    }

    #[test]
    fn a_run_of_withheld_lines_is_one_counted_marker() {
        // Lines withheld WHOLE (an environment record naming a token): a line that only
        // names a token is kept since 0.5.26.
        let lines = ["boot", "A_TOKEN=1", "B_TOKEN=22", "reading x.env", "up", "C_TOKEN=3", "D_TOKEN=4"];
        let out = clean_lines(lines);
        assert_eq!(
            out,
            vec![
                "boot".to_string(),
                withheld_run("token", 2, 19),
                withheld("env", 13),
                "up".to_string(),
                withheld_run("token", 2, 18),
            ]
        );
        assert!(out.iter().all(|l| clean(l) == l.as_str()), "markers clean to themselves");
        assert_eq!(withheld_run("key", 1, 7), withheld("key", 7));
    }
}