Your browser sucks, JavaScript disabled! Gunnar's console needs JavaScript and WebAssembly. Git over SSH on port 2222 does not.

nordisk/nornir

src/mimir_lens/room_feed/boundary.rs

//! **The system boundary, as the lenses read it**: which of OUR functions
//! can reach which class of system call (file, network, process, memory,
//! time …), and for each one a shortest path you can show.
//!
//! The facts are the code room's with the dependency universe folded in:
//! `syscall` nodes (the kernel's table, one per call, `ty` = its class),
//! `SYSCALLS` edges (a C function's `ffi` node → the calls it makes, through
//! the curated glibc / musl map; a fn handing the raw door a number; the raw
//! door → `<indirect>`), written once per toolchain / target by
//! `mimir_ingest::boundary`; and CALLS from our code down through std and
//! the dependencies to those functions. A call whose `SYSCALLS` hop is
//! UNCERTAIN (`partial`: the libc version, a vDSO, contention decide) says so
//! on its row. An `extern "C"` item declared in our OWN code is answered from
//! the same C map here (the producer maps the dependencies only).
//!
//! Three readers share it: the `system_boundary` lens (per repo: the classes
//! it reaches and from where), `data_reach` (its `syscalls` section: which of
//! our fns reach a class, `class` = e.g. `network`), `reachable_vuln` (what
//! the vulnerable crate our code calls can reach at the boundary).

use std::collections::{BTreeMap, BTreeSet};

use mimir_ingest::boundary::{self, Class, Libc, Mapped, Target};
use mimir_room::{EdgeSel, Mask, MaskSpec, Room, Scope};
use mimir_schema::{EdgeKind, Origin, kind as nk, moniker};
use serde::Serialize;
use serde_json::{Value, json};

use super::graph::internal;
use super::{Basis, CALLS_LOWER_BOUND, Query};
use crate::code_room::{Reach, Refusal, State};

/// The kinds a boundary walk follows.
pub const WALK: [EdgeKind; 2] = [EdgeKind::Calls, EdgeKind::Syscalls];

/// Rows (functions with their path) shown per class.
pub const ROWS_PER_CLASS: usize = 12;

/// The ring room of a request: the request's repo (or every repo) with the
/// dependency universe folded in.
pub fn ring_room(reach: &Reach<'_>, repo: &str) -> Result<Room, Refusal> {
    let handle = reach.code_room()?;
    let scope = if repo.is_empty() { Scope::current() } else { Scope::repo(repo, None) };
    handle.room(&scope.with_universe(true))
}

/// What [`reach_classes`] says when the room holds no system call node.
pub(crate) const NO_BOUNDARY: &str =
    "no system call node in the room: the boundary (`mimir_ingest::boundary::ensure`) has not run for this lake yet — it runs once per toolchain / target after a consume";

/// Does the lake hold ANY `syscall` node — answered by a targeted scan of
/// the node streams, never a fold. `data_reach` opens the ring room (the
/// dependency universe folded in) only for its `syscalls` section; a lake
/// the boundary never ran on folded 8.2 M ring nodes in 951 s (lake copy,
/// 2026-10-19) to answer "no boundary". `false` is exact — no row of kind
/// `syscall` in any node stream means the room's syscall mask is empty
/// (symbols carry no kind) — while any row, or any error, reads `true` and
/// the fold proceeds exactly as before.
pub fn boundary_in_lake(reach: &Reach<'_>) -> bool {
    let Ok(handle) = reach.code_room() else { return true };
    let Ok(log) = handle.log() else { return true };
    let filter = skade::ScanFilter::is_in("kind", [nk::SYSCALL]);
    // on a thread of its own, on the lake's runtime: callable from any thread
    std::thread::scope(|s| {
        s.spawn(|| {
            log.block_on(async {
                for kind in [mimir_ingest::Kind::Nodes, mimir_ingest::Kind::BinNodes] {
                    match log.scan_rows_where(kind, &filter, &[]).await {
                        Ok(rows) => {
                            if rows.batches.iter().any(|b| b.num_rows() > 0) {
                                return true;
                            }
                        }
                        Err(_) => return true,
                    }
                }
                false
            })
        })
        .join()
        .unwrap_or(true)
    })
}

/// One function of ours that reaches a class, with one shortest path.
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct Row {
    pub class: String,
    /// Our function, by its readable path.
    pub function: String,
    pub repo: String,
    #[serde(rename = "crate")]
    pub krate: String,
    /// `file:line` of the function.
    pub at: Option<String>,
    /// The system call the shortest path ends in.
    pub syscall: String,
    /// The path, our function first, the system call last (readable names).
    pub path: Vec<String>,
    pub hops: usize,
    /// A hop to the system call is uncertain (the C function MAY make it).
    pub uncertain: bool,
    /// The kernel function the call enters (from the kernel's BTF), when read.
    pub kernel_entry: Option<String>,
    /// The function itself leaves our code toward the call (its next hop is a
    /// dependency): where our code crosses into the boundary.
    pub border: bool,
}

/// One class of system call, over the request's functions.
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
pub struct ClassReach {
    pub class: String,
    /// How many of our functions can reach a call of this class.
    pub functions: usize,
    /// The calls of the class our code can reach.
    pub syscalls: Vec<String>,
    /// Functions shown, border crossings first.
    pub from: Vec<Row>,
}

/// The boundary of one request.
#[derive(Debug, Clone, Default, PartialEq, Eq, Serialize)]
pub struct Reached {
    pub classes: Vec<ClassReach>,
    /// Our functions that reach no system call at all (with ones that do: the
    /// share of pure code).
    pub functions_seen: usize,
    pub functions_reaching: usize,
    /// `extern "C"` items declared in our own code the C map has no answer for.
    pub unmapped_own_ffi: Vec<String>,
}

fn target() -> Target {
    Target::host().unwrap_or_else(|| Target { triple: "x86_64-unknown-linux-gnu".into(), arch: "x86_64", libc: Libc::Glibc })
}

fn readable(room: &Room, o: u64) -> String {
    let n = room.node(o);
    if n.kind == Some(nk::SYSCALL) {
        let name = n.name.unwrap_or("?");
        let nr = boundary::syscall(&target_arch(room, o), name).map(|s| format!(" #{}", s.nr)).unwrap_or_default();
        return format!("syscall {name}{nr} ({})", n.ty.unwrap_or("?"));
    }
    if n.krate == Some(moniker::KERNEL_PACKAGE) {
        return format!("kernel {}", n.name.unwrap_or("?"));
    }
    room.display_path(o)
}

fn target_arch(room: &Room, o: u64) -> String {
    moniker::parse(room.moniker(o)).and_then(|(_, _, d)| d.split('/').next().map(str::to_string)).unwrap_or_default()
}

fn mask_of(room: &Room, v: &BTreeSet<u64>) -> Mask {
    room.mask_of(&v.iter().copied().collect::<Vec<_>>())
}

/// What our code reaches at the system boundary: per class (`only` = the
/// classes asked for, empty = all), the functions and one path each.
pub fn reach_classes(room: &Room, repos: &[String], only: &[String], per_class: usize, basis: &mut Basis) -> Result<Reached, Refusal> {
    let root = format!("code room {} (dependencies folded in)", room.scope());
    let sys = room.mask(&MaskSpec { node_kinds: vec![nk::SYSCALL.into()], ..Default::default() }).map_err(internal)?.value;
    basis.fact("code_nodes:syscall", sys.len(), root.clone());
    if sys.is_empty() {
        basis.gap(NO_BOUNDARY);
        return Ok(Reached::default());
    }
    let mut own: Vec<u64> = Vec::new();
    for k in [nk::FN, nk::METHOD] {
        let spec = MaskSpec { node_kinds: vec![k.into()], origins: vec![Origin::Local], defined_only: true, repos: repos.to_vec(), ..Default::default() };
        own.extend_from_slice(room.mask(&spec).map_err(internal)?.value.ordinals());
    }
    own.sort_unstable();
    own.dedup();
    let own_set: BTreeSet<u64> = own.iter().copied().collect();
    basis.fact("code_nodes:fn+method (own)", own.len(), root.clone());
    basis.lower_bound(CALLS_LOWER_BOUND);
    basis.lower_bound("a std / dependency crate dumped by a driver older than the fn-as-value CALLS edge (`each_addr(addr, TcpStream::connect)`) misses those hops until it is dumped again");

    // the classes of the calls, and our own `extern "C"` items through the C map
    let tg = target();
    let mut targets: BTreeMap<Class, BTreeSet<u64>> = BTreeMap::new();
    let mut call_of_ffi: BTreeMap<u64, (String, bool)> = BTreeMap::new();
    for &o in sys.ordinals() {
        let c = room.node(o).ty.and_then(Class::parse).unwrap_or(Class::System);
        targets.entry(c).or_default().insert(o);
    }
    let own_ffi = room.mask(&MaskSpec { node_kinds: vec![nk::FFI.into()], origins: vec![Origin::Local], defined_only: true, ..Default::default() }).map_err(internal)?.value;
    let mut unmapped = BTreeSet::new();
    let is_call = |n: &str| boundary::syscall(tg.arch, n).is_some();
    for &o in own_ffi.ordinals() {
        let n = room.node(o);
        let symbol = room.attr(o, "link_name").and_then(|v| v.as_str().map(str::to_string)).or_else(|| n.name.map(str::to_string)).unwrap_or_default();
        match boundary::libc_map::lookup(&symbol, tg.libc, &is_call) {
            Some(Mapped::Calls(calls)) => {
                for (c, sure) in calls {
                    targets.entry(boundary::class_of(c)).or_default().insert(o);
                    call_of_ffi.entry(o).or_insert((c.to_string(), sure));
                }
            }
            Some(Mapped::Pure) => {}
            None => {
                unmapped.insert(symbol);
            }
        }
    }

    let mut out = Reached { functions_seen: own.len(), unmapped_own_ffi: unmapped.into_iter().collect(), ..Default::default() };
    // the class reach of our code: the kept walk advanced by the room's
    // delta (`reach_inc`), or — the reference, the zero-loss oracle — the
    // original walks over the whole room
    let reaching = if crate::mimir_lens::reach_inc::reference() {
        classes_reference(room, &own, &own_set, &targets, &call_of_ffi, only, per_class, &mut out)?
    } else {
        classes_kept(room, repos, &own, &targets, &call_of_ffi, only, per_class, &mut out)?
    };
    out.functions_reaching = reaching;
    out.classes.sort_by(|a, b| b.functions.cmp(&a.functions).then(a.class.cmp(&b.class)));
    if !out.unmapped_own_ffi.is_empty() {
        basis.note(format!("{} `extern \"C\"` item(s) of our own code link a C symbol the boundary map has no answer for: {}", out.unmapped_own_ffi.len(), out.unmapped_own_ffi.iter().take(12).cloned().collect::<Vec<_>>().join(", ")));
    }
    Ok(out)
}

/// The REFERENCE class reach (the zero-loss oracle of the kept walk): ONE
/// forward walk from our functions, then per class one backward walk from
/// the targets reached, the border edges and the rows. Returns how many of
/// our functions reach an asked class.
#[allow(clippy::too_many_arguments)]
fn classes_reference(
    room: &Room,
    own: &[u64],
    own_set: &BTreeSet<u64>,
    targets: &BTreeMap<Class, BTreeSet<u64>>,
    call_of_ffi: &BTreeMap<u64, (String, bool)>,
    only: &[String],
    per_class: usize,
    out: &mut Reached,
) -> Result<usize, Refusal> {
    // what our code reaches at all: ONE forward walk
    let fwd: BTreeSet<u64> = room.reach(own, &WALK, None, None).map_err(internal)?.value.reached.into_iter().collect();
    let mut reaching: BTreeSet<u64> = BTreeSet::new();
    for (class, ts) in targets {
        if !only.is_empty() && !only.iter().any(|c| c == class.as_str()) {
            continue;
        }
        let hit: BTreeSet<u64> = ts.iter().copied().filter(|t| fwd.contains(t) || own_set.contains(t)).collect();
        if hit.is_empty() {
            continue;
        }
        let tv: Vec<u64> = hit.iter().copied().collect();
        let back: BTreeSet<u64> = room.dependents_by(&tv[..], &WALK, None, None).map_err(internal)?.value.reached.into_iter().collect();
        let fns: BTreeSet<u64> = back.iter().copied().filter(|o| own_set.contains(o)).collect();
        if fns.is_empty() {
            continue;
        }
        reaching.extend(fns.iter().copied());
        // the border: our fns whose CALLS leave our code for a node on the way
        let onward: BTreeSet<u64> = back.iter().copied().chain(hit.iter().copied()).filter(|o| !own_set.contains(o)).collect();
        let sel = EdgeSel { kinds: vec![EdgeKind::Calls], from: Some(&mask_of(room, &fns)), to: Some(&mask_of(room, &onward)), cap: None, heaviest_first: false };
        let border: BTreeSet<u64> = room.edges(&sel).map_err(internal)?.value.edges.into_iter().map(|e| e.from).collect();
        let mut order: Vec<u64> = fns.iter().copied().collect();
        order.sort_by_key(|o| (!border.contains(o), room.node(*o).repo.unwrap_or("").to_string(), room.display_path(*o)));
        let mut rows = Vec::new();
        for &f in order.iter().take(per_class) {
            if let Some(r) = row(room, f, &tv, *class, border.contains(&f), call_of_ffi)? {
                rows.push(r);
            }
        }
        let names: BTreeSet<String> = hit
            .iter()
            .map(|t| match call_of_ffi.get(t) {
                Some((c, _)) => c.clone(),
                None => room.node(*t).name.unwrap_or("?").to_string(),
            })
            .collect();
        out.classes.push(ClassReach { class: class.as_str().into(), functions: fns.len(), syscalls: names.into_iter().collect(), from: rows });
    }
    Ok(reaching.len())
}

/// The class reach from the KEPT walk ([`crate::mimir_lens::reach_inc`]):
/// the same classes, counts, order and rows as [`classes_reference`] — the
/// tests hold the two equal — from per-function class masks the kept walk
/// advances over the room's delta.
#[allow(clippy::too_many_arguments)]
fn classes_kept(
    room: &Room,
    repos: &[String],
    own: &[u64],
    targets: &BTreeMap<Class, BTreeSet<u64>>,
    call_of_ffi: &BTreeMap<u64, (String, bool)>,
    only: &[String],
    per_class: usize,
    out: &mut Reached,
) -> Result<usize, Refusal> {
    use crate::mimir_lens::reach_inc;
    let mut tclass: BTreeMap<u64, u32> = BTreeMap::new();
    for (c, ts) in targets {
        for t in ts {
            *tclass.entry(*t).or_default() |= reach_inc::class_bit(*c);
        }
    }
    let scope = reach_inc::scope_key(room, repos);
    let sets = reach_inc::boundary_sets(room, &scope, own, &tclass, call_of_ffi).map_err(internal)?;
    let mut reaching: BTreeSet<u64> = BTreeSet::new();
    // display paths, once per function across the classes
    let mut shown: BTreeMap<usize, String> = BTreeMap::new();
    for (class, ts) in targets {
        if !only.is_empty() && !only.iter().any(|c| c == class.as_str()) {
            continue;
        }
        let bit = reach_inc::class_bit(*class);
        let hit: BTreeSet<u64> = ts.iter().copied().filter(|t| sets.hit.contains(t)).collect();
        if hit.is_empty() {
            continue;
        }
        let tv: Vec<u64> = hit.iter().copied().collect();
        let fns: Vec<usize> = (0..own.len()).filter(|&i| sets.cm_own[i] & bit != 0).collect();
        if fns.is_empty() {
            continue;
        }
        reaching.extend(fns.iter().map(|&i| own[i]));
        // the reference's order — border first, then repo, then the readable
        // path, ties by ordinal (its stable sort over ascending ordinals) —
        // with the paths read only for the groups the rows take from
        let mut groups: BTreeMap<(bool, String), Vec<usize>> = BTreeMap::new();
        for &i in &fns {
            groups.entry((sets.border[i] & bit == 0, room.node(own[i]).repo.unwrap_or("").to_string())).or_default().push(i);
        }
        let mut order: Vec<usize> = Vec::new();
        for (_, mut g) in groups {
            if order.len() >= per_class {
                break;
            }
            for &i in &g {
                shown.entry(i).or_insert_with(|| room.display_path(own[i]));
            }
            g.sort_by(|a, b| shown[a].cmp(&shown[b]).then(own[*a].cmp(&own[*b])));
            order.extend(g);
        }
        let mut rows = Vec::new();
        for &i in order.iter().take(per_class) {
            if let Some(r) = row(room, own[i], &tv, *class, sets.border[i] & bit != 0, call_of_ffi)? {
                rows.push(r);
            }
        }
        let names: BTreeSet<String> = hit
            .iter()
            .map(|t| match call_of_ffi.get(t) {
                Some((c, _)) => c.clone(),
                None => room.node(*t).name.unwrap_or("?").to_string(),
            })
            .collect();
        out.classes.push(ClassReach { class: class.as_str().into(), functions: fns.len(), syscalls: names.into_iter().collect(), from: rows });
    }
    Ok(reaching.len())
}

/// One row: the shortest path from `f` to a target of the class.
fn row(room: &Room, f: u64, targets: &[u64], class: Class, border: bool, call_of_ffi: &BTreeMap<u64, (String, bool)>) -> Result<Option<Row>, Refusal> {
    let Some(p) = room.path(&[f][..], targets, &WALK, None, None).map_err(internal)?.value else { return Ok(None) };
    let last = *p.nodes.last().expect("a path has an end");
    let mut path: Vec<String> = p.nodes.iter().map(|o| readable(room, *o)).collect();
    let mut uncertain = false;
    // the SYSCALLS hop's certainty
    if p.nodes.len() >= 2 && p.kinds.last() == Some(&EdgeKind::Syscalls) {
        let prev = p.nodes[p.nodes.len() - 2];
        let sel = EdgeSel { kinds: vec![EdgeKind::Syscalls], from: Some(&room.mask_of(&[prev])), to: Some(&room.mask_of(&[last])), cap: None, heaviest_first: false };
        uncertain = room.edges(&sel).map_err(internal)?.value.edges.iter().any(|e| e.partial);
    }
    // our own `extern "C"` item: the C map names the call
    let syscall = match call_of_ffi.get(&last) {
        Some((c, sure)) => {
            uncertain |= !sure;
            path.push(format!("syscall {c} (C map, {})", boundary::class_of(c)));
            c.clone()
        }
        None => room.node(last).name.unwrap_or("?").to_string(),
    };
    let kernel_entry = if room.node(last).kind == Some(nk::SYSCALL) {
        let sel = EdgeSel { kinds: vec![EdgeKind::Calls], from: Some(&room.mask_of(&[last])), to: None, cap: Some(1), heaviest_first: false };
        room.edges(&sel).map_err(internal)?.value.edges.first().map(|e| room.node(e.to).name.unwrap_or("?").to_string())
    } else {
        None
    };
    let n = room.node(f);
    Ok(Some(Row {
        class: class.as_str().into(),
        function: room.display_path(f),
        repo: n.repo.unwrap_or("").into(),
        krate: n.krate.unwrap_or("").into(),
        at: n.file.map(|file| format!("{file}:{}", n.start_line.unwrap_or(0))),
        syscall,
        hops: p.hops(),
        path,
        uncertain,
        kernel_entry,
        border,
    }))
}

/// The class list a request asks for (`class` = `network` or `network,file`).
pub fn classes_asked(q: &Query<'_>) -> Result<Vec<String>, Refusal> {
    let raw = q.str("class");
    let asked: Vec<String> = raw.split(',').map(str::trim).filter(|s| !s.is_empty()).map(str::to_string).collect();
    if let Some(bad) = asked.iter().find(|c| Class::parse(c).is_none()) {
        let known: Vec<&str> = Class::ALL.iter().map(|c| c.as_str()).collect();
        return Err(Refusal::new(State::InvalidArgument, format!("`{bad}` is not a system call class (known: {})", known.join(", "))));
    }
    Ok(asked)
}

/// `system_boundary` — per repo: the classes of system call it can reach and
/// from where (border functions first, each with a shortest path).
pub fn system_boundary(reach: &Reach<'_>, _wh: &super::Warehouse<'_>, q: &Query<'_>) -> Result<(Basis, Value), Refusal> {
    let mut basis = Basis::default();
    let only = classes_asked(q)?;
    let g = super::graph::Graph::open(reach, q, &mut basis)?;
    let per_class = q.limit.clamp(1, ROWS_PER_CLASS);
    let r = reach_classes(&g.room, &g.repos, &only, per_class, &mut basis)?;
    let worst: Vec<&Row> = r.classes.iter().flat_map(|c| c.from.iter()).take(q.limit).collect();
    let summary: Vec<String> = r.classes.iter().map(|c| format!("{} {} fn(s)", c.class, c.functions)).collect();
    let note = if r.classes.is_empty() {
        "system_boundary: no function of the request reaches a system call in this room.".to_string()
    } else {
        format!("system_boundary: {} of {} function(s) reach the system boundary — {}.", r.functions_reaching, r.functions_seen, summary.join(", "))
    };
    Ok((
        basis,
        json!({
            "kind": "system_boundary",
            "classes": r.classes,
            "functions_seen": r.functions_seen,
            "functions_reaching": r.functions_reaching,
            "unmapped_own_ffi": r.unmapped_own_ffi,
            "worst": worst,
            "evidence_note": note,
            "model_attribution": "Composition of code-room facts with the dependency universe folded in: CALLS from our code through std and the dependencies × SYSCALLS (C function → system call, through the curated glibc / musl map; the raw door) × the kernel's syscall table (x86_64) and, when read, its BTF (the entry function). Uncertain hops are marked.",
        }),
    ))
}

/// Severity order of the classes when ONE path is shown for a crate.
const SHOWN_FIRST: [Class; 10] =
    [Class::Network, Class::Process, Class::File, Class::System, Class::Ipc, Class::Signal, Class::Memory, Class::Event, Class::Time, Class::Random];

/// What the code of `krate@version` that `repo`'s own functions call can
/// reach at the system boundary: the classes, and one shortest path (our
/// function → into the crate → … → the call) for the most severe class.
/// `None` = our code calls nothing of that crate in this room.
pub fn reach_of_crate(room: &Room, repo: &str, krate: &str, version: &str) -> Result<Option<Value>, Refusal> {
    let mut own: Vec<u64> = Vec::new();
    for k in [nk::FN, nk::METHOD] {
        let spec = MaskSpec { node_kinds: vec![k.into()], origins: vec![Origin::Local], defined_only: true, repos: vec![repo.to_string()], ..Default::default() };
        own.extend_from_slice(room.mask(&spec).map_err(internal)?.value.ordinals());
    }
    let names: BTreeSet<String> = [krate.to_string(), krate.replace('-', "_")].into();
    let spec = MaskSpec { crates: names.iter().map(|n| format!("{n}@{version}")).collect(), ..Default::default() };
    let theirs = room.mask(&spec).map_err(internal)?.value;
    if own.is_empty() || theirs.is_empty() {
        return Ok(None);
    }
    let own_mask = room.mask_of(&own);
    let sel = EdgeSel { kinds: vec![EdgeKind::Calls], from: Some(&own_mask), to: Some(&theirs), cap: None, heaviest_first: false };
    let calls = room.edges(&sel).map_err(internal)?.value.edges;
    let entries: BTreeSet<u64> = calls.iter().map(|e| e.to).collect();
    if entries.is_empty() {
        return Ok(None);
    }
    let ev: Vec<u64> = entries.iter().copied().collect();
    let reached = room.reach(&ev[..], &WALK, None, None).map_err(internal)?.value.reached;
    let mut by_class: BTreeMap<Class, Vec<u64>> = BTreeMap::new();
    for o in reached {
        let n = room.node(o);
        if n.kind == Some(nk::SYSCALL) {
            by_class.entry(n.ty.and_then(Class::parse).unwrap_or(Class::System)).or_default().push(o);
        }
    }
    let classes: Vec<&str> = by_class.keys().map(|c| c.as_str()).collect();
    let shown = SHOWN_FIRST.iter().chain([Class::Indirect].iter()).find(|c| by_class.contains_key(c));
    let mut path = Vec::new();
    if let Some(c) = shown
        && let Some(p) = room.path(&ev[..], &by_class[c][..], &WALK, None, None).map_err(internal)?.value
    {
        let first = p.nodes[0];
        if let Some(caller) = calls.iter().find(|e| e.to == first) {
            path.push(readable(room, caller.from));
        }
        path.extend(p.nodes.iter().map(|o| readable(room, *o)));
    }
    Ok(Some(json!({
        "classes": classes,
        "syscalls": by_class.values().map(Vec::len).sum::<usize>(),
        "entries_called": entries.len(),
        "path": path,
    })))
}

/// The kept per-finding walks of one `reachable_vuln` ask
/// ([`crate::mimir_lens::reach_inc`]): opened before the findings, closed
/// (kept) after them.
pub struct KeptFindings {
    local: std::collections::HashSet<u64>,
    path: String,
    rows: std::collections::HashMap<(String, String, String), crate::mimir_lens::reach_inc::KeptRow>,
    total: usize,
    rewalked: usize,
}

impl KeptFindings {
    pub fn open(room: &Room, scope: &str) -> anyhow::Result<Self> {
        let (kept, local, path) = crate::mimir_lens::reach_inc::vuln_kept(room, scope)?;
        let rows = kept.map(|k| k.rows.clone()).unwrap_or_default();
        Ok(Self { local, path, rows, total: 0, rewalked: 0 })
    }

    pub fn close(self, room: &Room, scope: &str) -> anyhow::Result<()> {
        use crate::mimir_lens::reach_inc;
        let path = if self.path.starts_with("full") { self.path } else if self.rewalked == 0 { "carried".to_string() } else { "advanced".to_string() };
        reach_inc::vuln_keep(scope, reach_inc::KeptVuln { universe: room.universe_key().unwrap_or(0), local: self.local, rows: self.rows });
        reach_inc::vuln_record(path, self.total, self.rewalked);
        Ok(())
    }
}

/// [`reach_of_crate`] over the kept walks: a finding whose entries are the
/// same items and whose walk met no local node (the universe half, carried
/// while the universe holds) answers from the kept walk, with the caller
/// prefix of its path read from the current calls; any other is walked.
pub fn reach_of_crate_kept(room: &Room, repo: &str, krate: &str, version: &str, k: &mut KeptFindings) -> Result<Option<Value>, Refusal> {
    use crate::mimir_lens::reach_inc::{KeptRow, key_of};
    let mut own: Vec<u64> = Vec::new();
    for kd in [nk::FN, nk::METHOD] {
        let spec = MaskSpec { node_kinds: vec![kd.into()], origins: vec![Origin::Local], defined_only: true, repos: vec![repo.to_string()], ..Default::default() };
        own.extend_from_slice(room.mask(&spec).map_err(internal)?.value.ordinals());
    }
    let names: BTreeSet<String> = [krate.to_string(), krate.replace('-', "_")].into();
    let spec = MaskSpec { crates: names.iter().map(|n| format!("{n}@{version}")).collect(), ..Default::default() };
    let theirs = room.mask(&spec).map_err(internal)?.value;
    if own.is_empty() || theirs.is_empty() {
        return Ok(None);
    }
    let own_mask = room.mask_of(&own);
    let sel = EdgeSel { kinds: vec![EdgeKind::Calls], from: Some(&own_mask), to: Some(&theirs), cap: None, heaviest_first: false };
    let calls = room.edges(&sel).map_err(internal)?.value.edges;
    let entries: BTreeSet<u64> = calls.iter().map(|e| e.to).collect();
    if entries.is_empty() {
        return Ok(None);
    }
    k.total += 1;
    let mut ekeys: Vec<u64> = entries.iter().map(|o| key_of(room.moniker(*o))).collect();
    ekeys.sort_unstable();
    let id = (repo.to_string(), krate.to_string(), version.to_string());
    if let Some(r) = k.rows.get(&id)
        && r.pure
        && r.entries == ekeys
        && !ekeys.iter().any(|e| k.local.contains(e))
    {
        let mut v = r.value.clone();
        if let Some(first) = r.first.as_deref().and_then(|m| room.ordinal(m))
            && let Some(caller) = calls.iter().find(|e| e.to == first)
            && let Some(p) = v["path"].as_array_mut()
        {
            p.insert(0, Value::String(readable(room, caller.from)));
        }
        return Ok(Some(v));
    }
    k.rewalked += 1;
    let ev: Vec<u64> = entries.iter().copied().collect();
    let reached = room.reach(&ev[..], &WALK, None, None).map_err(internal)?.value.reached;
    let pure = !ev.iter().chain(reached.iter()).any(|o| k.local.contains(&key_of(room.moniker(*o))));
    let mut by_class: BTreeMap<Class, Vec<u64>> = BTreeMap::new();
    for o in reached {
        let n = room.node(o);
        if n.kind == Some(nk::SYSCALL) {
            by_class.entry(n.ty.and_then(Class::parse).unwrap_or(Class::System)).or_default().push(o);
        }
    }
    let classes: Vec<&str> = by_class.keys().map(|c| c.as_str()).collect();
    let shown = SHOWN_FIRST.iter().chain([Class::Indirect].iter()).find(|c| by_class.contains_key(c));
    let mut tail = Vec::new();
    let mut prefix = None;
    let mut first = None;
    if let Some(c) = shown
        && let Some(p) = room.path(&ev[..], &by_class[c][..], &WALK, None, None).map_err(internal)?.value
    {
        let f = p.nodes[0];
        first = Some(room.moniker(f).to_string());
        if let Some(caller) = calls.iter().find(|e| e.to == f) {
            prefix = Some(readable(room, caller.from));
        }
        tail.extend(p.nodes.iter().map(|o| readable(room, *o)));
    }
    let value = json!({
        "classes": classes,
        "syscalls": by_class.values().map(Vec::len).sum::<usize>(),
        "entries_called": entries.len(),
        "path": tail,
    });
    k.rows.insert(id, KeptRow { entries: ekeys, value: value.clone(), first, pure });
    let mut v = value;
    if let (Some(pre), Some(p)) = (prefix, v["path"].as_array_mut()) {
        p.insert(0, Value::String(pre));
    }
    Ok(Some(v))
}