mirror of
https://github.com/ruvnet/RuView
synced 2026-08-11 20:41:44 +00:00
feat(privshield): add veil custom terminal harness + TUI (ADR-288 §harness)
A dependency-free native binary (`src/bin/veil.rs`) — the in-repo counterpart to the npm metaharness — that drives the same crate API the tests use: - Interactive ANSI dashboard (TUI): live status, re-ID off/on vs chance, throughput, compliance (energy 1.000× / not jamming), a block-sparkline collapse curve, config, and PASS/OUT-OF-SPEC verdict. Command-driven redraw loop (std-only, no crossterm/ratatui): on/off, passes/bits/n/snr, metric euclid|cosine, preset scif|board|ward|hotel, optimize, proof. - Scriptable subcommands: report, sweep, optimize, adaptive <N>, proof, doctor (exit 0 = healthy). Auto-picks TUI on a terminal, one-shot report when piped; honors NO_COLOR. Std-only, so it builds with no extra deps and runs in any pipe/CI. The wasm leaf story is unchanged (validated with `--lib`; the bin is native-only). All readouts are SYNTHETIC/L0 and never relabeled. Validated: 38 tests + doctest pass, clippy --all-targets -D warnings clean, rustfmt clean, wasm --lib builds; all subcommands + a scripted TUI session exercised. Documented in the crate README and ADR-288. Co-Authored-By: claude-flow <ruv@ruv.net> Claude-Session: https://claude.ai/code/session_01WEXNqzs7UsfNFBcP5yW21p
This commit is contained in:
@@ -23,3 +23,10 @@ readme = "README.md"
|
||||
[lib]
|
||||
name = "wifi_densepose_privshield"
|
||||
path = "src/lib.rs"
|
||||
|
||||
# `veil` — the custom, dependency-free terminal harness + TUI (ADR-288 §harness).
|
||||
# Native counterpart to the npm metaharness. Std-only; builds without any extra
|
||||
# deps. Excluded from the wasm leaf story (that stays `--lib`).
|
||||
[[bin]]
|
||||
name = "veil"
|
||||
path = "src/bin/veil.rs"
|
||||
|
||||
@@ -66,6 +66,40 @@ anything.
|
||||
cargo test -p wifi-densepose-privshield --no-default-features
|
||||
```
|
||||
|
||||
## `veil` — terminal harness & TUI
|
||||
|
||||
A custom, **dependency-free** native harness ships with the crate (the in-repo
|
||||
counterpart to the npm metaharness). It drives the same model the tests use — an
|
||||
interactive ANSI dashboard plus scriptable subcommands, std-only (no
|
||||
`crossterm`/`ratatui`), so it runs in any terminal, pipe, or CI.
|
||||
|
||||
```bash
|
||||
cargo run -p wifi-densepose-privshield --bin veil # interactive TUI (or a one-shot report when piped)
|
||||
cargo run -p wifi-densepose-privshield --bin veil -- sweep # re-ID vs passes + throughput vs bits
|
||||
cargo run -p wifi-densepose-privshield --bin veil -- optimize
|
||||
cargo run -p wifi-densepose-privshield --bin veil -- doctor # self-check, exit 0 = healthy
|
||||
```
|
||||
|
||||
```text
|
||||
┌──────────────────────────────────────────────────────────
|
||||
│ VEIL · wifi-sensing privacy shield ● PROTECTED
|
||||
│
|
||||
│ re-ID off 100.0% re-ID on 4.7% (chance 6.25%)
|
||||
│ throughput 97.6% emission 1.000× · not jamming
|
||||
│
|
||||
│ collapse ████▇▆▅▂▂▂▁▂ passes 2→112 · op 96
|
||||
│
|
||||
│ config passes 96 · bits 5 · N 16 · snr 20dB · euclid
|
||||
│ verdict ✓ PASS — re-ID at chance · throughput ≥95% · compliant
|
||||
└──────────────────────────────────────────────────────────
|
||||
```
|
||||
|
||||
In the TUI, type commands to steer the shield live: `on`/`off`, `passes <n>`,
|
||||
`bits <n>`, `n <k>`, `snr <db>`, `metric euclid|cosine`,
|
||||
`preset scif|board|ward|hotel`, `optimize`, `proof`, `quit`. All readouts are
|
||||
**SYNTHETIC / L0**. (A graphical VEIL Console web dashboard mirrors this same
|
||||
instrument; `veil` is the terminal-native version.)
|
||||
|
||||
## Modules
|
||||
|
||||
| Module | Purpose |
|
||||
|
||||
@@ -0,0 +1,542 @@
|
||||
//! `veil` — a custom, dependency-free terminal harness for the VEIL privacy
|
||||
//! shield (ADR-288). It is the in-repo, native counterpart to the npm
|
||||
//! metaharness (`harness/wifi-densepose-privshield/`, ADR-289): where that one
|
||||
//! assists *development*, this one *drives the model* — an interactive TUI plus
|
||||
//! scriptable subcommands over the same crate API the tests use.
|
||||
//!
|
||||
//! Std-only on purpose: no `crossterm`/`ratatui`, no external deps. The TUI is
|
||||
//! a command-driven ANSI dashboard (line input, redraw on change), which keeps
|
||||
//! the crate a pure leaf and lets the harness run in any pipe or CI.
|
||||
//!
|
||||
//! ```text
|
||||
//! veil # TUI if attached to a terminal, else a one-shot report
|
||||
//! veil tui # force the interactive dashboard
|
||||
//! veil report # print the dashboard once (plain, pipe-friendly)
|
||||
//! veil sweep # re-ID vs passes and throughput vs bits tables
|
||||
//! veil optimize # run the hyper-optimizer, print the recommendation
|
||||
//! veil adaptive <N> # derive the shield for a room of N candidate identities
|
||||
//! veil proof # verify the deterministic witness
|
||||
//! veil doctor # self-check (exit 0 = healthy)
|
||||
//! ```
|
||||
//!
|
||||
//! All numbers are **SYNTHETIC / L0** — reproduced by `cargo test`, describing
|
||||
//! the model, not real hardware.
|
||||
|
||||
use std::io::{self, BufRead, IsTerminal, Write};
|
||||
|
||||
use veil::optimize;
|
||||
use veil::{run, ExperimentConfig, ExperimentReport, Metric, Proof};
|
||||
use wifi_densepose_privshield as veil;
|
||||
|
||||
// ---- ANSI palette (matches the VEIL Console: teal shield, amber threat) ----
|
||||
const TEAL: &str = "\x1b[38;2;32;211;192m";
|
||||
const AMBER: &str = "\x1b[38;2;245;158;75m";
|
||||
const GOOD: &str = "\x1b[38;2;62;207;142m";
|
||||
const CRIT: &str = "\x1b[38;2;242;107;111m";
|
||||
const MUTE: &str = "\x1b[38;2;139;160;159m";
|
||||
const BOLD: &str = "\x1b[1m";
|
||||
const RST: &str = "\x1b[0m";
|
||||
const BLOCKS: [char; 8] = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
|
||||
|
||||
/// Emit color only for a real terminal, and never when `NO_COLOR` is set.
|
||||
fn color_enabled() -> bool {
|
||||
io::stdout().is_terminal() && std::env::var_os("NO_COLOR").is_none()
|
||||
}
|
||||
|
||||
/// Wrap `s` in `code` when color is on.
|
||||
fn c(s: &str, code: &str, on: bool) -> String {
|
||||
if on {
|
||||
format!("{code}{s}{RST}")
|
||||
} else {
|
||||
s.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
/// A raw color code, or "" when color is off — for inline `format!` colouring.
|
||||
fn k(code: &'static str, on: bool) -> &'static str {
|
||||
if on {
|
||||
code
|
||||
} else {
|
||||
""
|
||||
}
|
||||
}
|
||||
|
||||
/// Shield-on re-ID at a given mixing budget, holding the rest of `cfg`.
|
||||
fn reid_at(cfg: &ExperimentConfig, passes: usize) -> f32 {
|
||||
let mut c = cfg.clone();
|
||||
c.shield.givens_passes = passes;
|
||||
run(&c).accuracy_shield_on
|
||||
}
|
||||
|
||||
/// A block-sparkline character for a value in `[0, 1]`.
|
||||
fn spark(v: f32) -> char {
|
||||
let i = (v.clamp(0.0, 1.0) * 7.0).round() as usize;
|
||||
BLOCKS[i.min(7)]
|
||||
}
|
||||
|
||||
/// Render the full dashboard as colored lines (left-bar panel; no right border,
|
||||
/// so ANSI escape width never has to be counted).
|
||||
fn dashboard(cfg: &ExperimentConfig, on: bool) -> Vec<String> {
|
||||
let rep: ExperimentReport = run(cfg);
|
||||
let chance = rep.chance_level * 100.0;
|
||||
let off = rep.accuracy_shield_off * 100.0;
|
||||
let onp = rep.accuracy_shield_on * 100.0;
|
||||
let tp = rep.throughput_ratio * 100.0;
|
||||
|
||||
let (state, scode) = if !cfg.shield.enabled {
|
||||
("EXPOSED", CRIT)
|
||||
} else if rep.passed() {
|
||||
("PROTECTED", GOOD)
|
||||
} else {
|
||||
("AT RISK", AMBER)
|
||||
};
|
||||
let on_code = if onp <= rep.chance_band * 100.0 {
|
||||
GOOD
|
||||
} else {
|
||||
AMBER
|
||||
};
|
||||
let tp_code = if tp >= 95.0 { GOOD } else { CRIT };
|
||||
let bar = c("│", MUTE, on);
|
||||
|
||||
let mut out = Vec::new();
|
||||
out.push(c(
|
||||
"┌──────────────────────────────────────────────────────────",
|
||||
MUTE,
|
||||
on,
|
||||
));
|
||||
out.push(format!(
|
||||
"{} {}{}VEIL{} {}· wifi-sensing privacy shield{} {}● {}{}",
|
||||
bar,
|
||||
k(BOLD, on),
|
||||
k(TEAL, on),
|
||||
k(RST, on),
|
||||
k(MUTE, on),
|
||||
k(RST, on),
|
||||
k(scode, on),
|
||||
state,
|
||||
k(RST, on),
|
||||
));
|
||||
out.push(bar.clone());
|
||||
out.push(format!(
|
||||
"{} re-ID off {}{:>6.1}%{} re-ID on {}{:>5.1}%{} {}(chance {:.2}%){}",
|
||||
bar,
|
||||
k(AMBER, on),
|
||||
off,
|
||||
k(RST, on),
|
||||
k(on_code, on),
|
||||
onp,
|
||||
k(RST, on),
|
||||
k(MUTE, on),
|
||||
chance,
|
||||
k(RST, on),
|
||||
));
|
||||
out.push(format!(
|
||||
"{} throughput {}{:>6.1}%{} emission {}{:.3}×{} {}· not jamming{}",
|
||||
bar,
|
||||
k(tp_code, on),
|
||||
tp,
|
||||
k(RST, on),
|
||||
k(GOOD, on),
|
||||
rep.compliance.energy_ratio,
|
||||
k(RST, on),
|
||||
k(MUTE, on),
|
||||
k(RST, on),
|
||||
));
|
||||
out.push(bar.clone());
|
||||
|
||||
let cand = optimize::PASS_CANDIDATES;
|
||||
let line: String = cand.iter().map(|&p| spark(reid_at(cfg, p))).collect();
|
||||
out.push(format!(
|
||||
"{} {}collapse{} {}{}{} {}passes {}→{} · op {}{}",
|
||||
bar,
|
||||
k(MUTE, on),
|
||||
k(RST, on),
|
||||
k(TEAL, on),
|
||||
line,
|
||||
k(RST, on),
|
||||
k(MUTE, on),
|
||||
cand[0],
|
||||
cand[cand.len() - 1],
|
||||
cfg.shield.givens_passes,
|
||||
k(RST, on),
|
||||
));
|
||||
out.push(bar.clone());
|
||||
|
||||
let metric = match cfg.attacker_metric {
|
||||
Metric::Euclidean => "euclid",
|
||||
Metric::Cosine => "cosine",
|
||||
};
|
||||
out.push(format!(
|
||||
"{} {}config{} passes {} · bits {} · N {} · snr {:.0}dB · {}",
|
||||
bar,
|
||||
k(MUTE, on),
|
||||
k(RST, on),
|
||||
cfg.shield.givens_passes,
|
||||
cfg.shield.feedback_bits,
|
||||
cfg.scene.identities,
|
||||
cfg.link.snr_db,
|
||||
metric,
|
||||
));
|
||||
let (vlabel, vcode) = if !cfg.shield.enabled {
|
||||
("SHIELD OFF — room exposed", CRIT)
|
||||
} else if rep.passed() {
|
||||
(
|
||||
"✓ PASS — re-ID at chance · throughput ≥95% · compliant",
|
||||
GOOD,
|
||||
)
|
||||
} else {
|
||||
(
|
||||
"△ OUT OF SPEC — raise passes/bits to re-enter the chance band",
|
||||
AMBER,
|
||||
)
|
||||
};
|
||||
out.push(format!(
|
||||
"{} {}verdict{} {}{}{}",
|
||||
bar,
|
||||
k(MUTE, on),
|
||||
k(RST, on),
|
||||
k(vcode, on),
|
||||
vlabel,
|
||||
k(RST, on)
|
||||
));
|
||||
out.push(c(
|
||||
"└──────────────────────────────────────────────────────────",
|
||||
MUTE,
|
||||
on,
|
||||
));
|
||||
out
|
||||
}
|
||||
|
||||
/// Deployment presets (mirror `optimize::adaptive_shield` results per room).
|
||||
fn preset(name: &str, cfg: &mut ExperimentConfig) -> bool {
|
||||
let (n, passes, bits, snr) = match name {
|
||||
"scif" => (64, 96, 5, 20.0),
|
||||
"board" => (16, 96, 5, 25.0),
|
||||
"ward" => (32, 96, 5, 15.0),
|
||||
"hotel" => (48, 64, 5, 20.0),
|
||||
_ => return false,
|
||||
};
|
||||
cfg.scene.identities = n;
|
||||
cfg.shield.givens_passes = passes;
|
||||
cfg.shield.feedback_bits = bits;
|
||||
cfg.link.snr_db = snr;
|
||||
true
|
||||
}
|
||||
|
||||
fn print_dashboard(cfg: &ExperimentConfig, on: bool) {
|
||||
for l in dashboard(cfg, on) {
|
||||
println!("{l}");
|
||||
}
|
||||
}
|
||||
|
||||
fn cmd_sweep(cfg: &ExperimentConfig, on: bool) {
|
||||
println!(
|
||||
"{}re-ID (shield on) vs Givens passes — N={}{}",
|
||||
k(MUTE, on),
|
||||
cfg.scene.identities,
|
||||
k(RST, on)
|
||||
);
|
||||
for &p in &optimize::PASS_CANDIDATES {
|
||||
let robust =
|
||||
optimize::passes_collapse_at_n(cfg, p, cfg.shield.feedback_bits, cfg.scene.identities);
|
||||
println!(
|
||||
" passes {:>3} re-ID {:>5.1}% {}",
|
||||
p,
|
||||
reid_at(cfg, p) * 100.0,
|
||||
if robust {
|
||||
c("collapses", GOOD, on)
|
||||
} else {
|
||||
c("above chance", AMBER, on)
|
||||
}
|
||||
);
|
||||
}
|
||||
println!(
|
||||
"\n{}throughput vs feedback bits — snr={:.0}dB{}",
|
||||
k(MUTE, on),
|
||||
cfg.link.snr_db,
|
||||
k(RST, on)
|
||||
);
|
||||
for bits in 1..=12u32 {
|
||||
let mut s = cfg.shield.clone();
|
||||
s.feedback_bits = bits;
|
||||
let tp = cfg.link.throughput_ratio(&s) * 100.0;
|
||||
let barlen = ((tp - 90.0).clamp(0.0, 10.0) / 10.0 * 24.0) as usize;
|
||||
println!(
|
||||
" {:>2} bit {:>6.3}% {}{}{}",
|
||||
bits,
|
||||
tp,
|
||||
k(TEAL, on),
|
||||
"█".repeat(barlen),
|
||||
k(RST, on)
|
||||
);
|
||||
}
|
||||
let (sb, _) = optimize::spec_optimal_feedback_bits(cfg);
|
||||
println!(" {}spec-optimal: {} bit{}", k(MUTE, on), sb, k(RST, on));
|
||||
}
|
||||
|
||||
fn cmd_optimize(cfg: &ExperimentConfig, on: bool) {
|
||||
let opt = veil::hyper_optimize(cfg);
|
||||
let r = &opt.report;
|
||||
println!("{}hyper-optimizer{}", k(BOLD, on), k(RST, on));
|
||||
println!(" min robust passes : {}", opt.min_passes);
|
||||
println!(
|
||||
" shipped passes : {} {}(min × 2 margin, throughput-free){}",
|
||||
opt.shipped_passes,
|
||||
k(MUTE, on),
|
||||
k(RST, on)
|
||||
);
|
||||
println!(
|
||||
" spec-optimal bits : {} {}(model optimum {}){}",
|
||||
opt.spec_optimal_bits,
|
||||
k(MUTE, on),
|
||||
opt.model_optimal_bits,
|
||||
k(RST, on)
|
||||
);
|
||||
println!(
|
||||
" result : re-ID {}{:.1}%{} · throughput {}{:.1}%{} · {}",
|
||||
k(GOOD, on),
|
||||
r.accuracy_shield_on * 100.0,
|
||||
k(RST, on),
|
||||
k(GOOD, on),
|
||||
r.throughput_ratio * 100.0,
|
||||
k(RST, on),
|
||||
if r.passed() {
|
||||
c("PASS", GOOD, on)
|
||||
} else {
|
||||
c("FAIL", CRIT, on)
|
||||
}
|
||||
);
|
||||
println!(
|
||||
" {}SNR → model-optimal bits: {:?}{}",
|
||||
k(MUTE, on),
|
||||
optimize::optimal_bits_across_snr(cfg),
|
||||
k(RST, on)
|
||||
);
|
||||
}
|
||||
|
||||
fn cmd_adaptive(cfg: &ExperimentConfig, n: usize, on: bool) {
|
||||
let sh = veil::adaptive_shield(cfg, n);
|
||||
println!(
|
||||
"adaptive shield for N={}: passes {} · bits {} {}(mixing budget is N-independent in this model){}",
|
||||
n, sh.givens_passes, sh.feedback_bits, k(MUTE, on), k(RST, on)
|
||||
);
|
||||
}
|
||||
|
||||
fn cmd_proof(on: bool) -> i32 {
|
||||
let w = Proof::witness(&Proof::run_reference());
|
||||
let ok = w == Proof::EXPECTED_WITNESS;
|
||||
println!(
|
||||
"witness {:#018x} expected {:#018x} {}",
|
||||
w,
|
||||
Proof::EXPECTED_WITNESS,
|
||||
if ok {
|
||||
c("MATCH", GOOD, on)
|
||||
} else {
|
||||
c("DRIFT", CRIT, on)
|
||||
}
|
||||
);
|
||||
i32::from(!ok)
|
||||
}
|
||||
|
||||
fn cmd_doctor(on: bool) -> i32 {
|
||||
let rep = run(&ExperimentConfig::default());
|
||||
let checks = [
|
||||
("reference experiment passes", rep.passed()),
|
||||
(
|
||||
"attack is real without shield",
|
||||
rep.attack_is_effective_without_shield(),
|
||||
),
|
||||
("collapse drives to chance", rep.drives_to_chance()),
|
||||
("throughput ≥ 95%", rep.preserves_throughput()),
|
||||
("emission is compliant", rep.compliance.is_compliant()),
|
||||
(
|
||||
"deterministic witness matches",
|
||||
Proof::witness(&Proof::run_reference()) == Proof::EXPECTED_WITNESS,
|
||||
),
|
||||
];
|
||||
let mut ok = true;
|
||||
for (label, pass) in checks {
|
||||
ok &= pass;
|
||||
println!(
|
||||
"{} {label}",
|
||||
if pass {
|
||||
c("PASS", GOOD, on)
|
||||
} else {
|
||||
c("FAIL", CRIT, on)
|
||||
}
|
||||
);
|
||||
}
|
||||
println!(
|
||||
"\nveil doctor: {}",
|
||||
if ok {
|
||||
c("all checks passed", GOOD, on)
|
||||
} else {
|
||||
c("problems found", CRIT, on)
|
||||
}
|
||||
);
|
||||
i32::from(!ok)
|
||||
}
|
||||
|
||||
fn help() {
|
||||
println!(
|
||||
"veil — VEIL privacy-shield harness (SYNTHETIC / L0)\n\n\
|
||||
USAGE\n veil [command]\n\n\
|
||||
COMMANDS\n\
|
||||
\x20 tui interactive dashboard (default on a terminal)\n\
|
||||
\x20 report print the dashboard once\n\
|
||||
\x20 sweep re-ID vs passes + throughput vs bits\n\
|
||||
\x20 optimize run the hyper-optimizer\n\
|
||||
\x20 adaptive <N> derive the shield for N candidate identities\n\
|
||||
\x20 proof verify the deterministic witness\n\
|
||||
\x20 doctor self-check (exit 0 = healthy)\n\
|
||||
\x20 help this text\n\n\
|
||||
TUI COMMANDS (type + Enter)\n\
|
||||
\x20 on | off toggle the shield\n\
|
||||
\x20 passes <n> · bits <n> · n <k> · snr <db>\n\
|
||||
\x20 metric euclid|cosine\n\
|
||||
\x20 preset scif|board|ward|hotel\n\
|
||||
\x20 run | optimize | proof | help | quit"
|
||||
);
|
||||
}
|
||||
|
||||
fn tui(mut cfg: ExperimentConfig, on: bool) {
|
||||
let stdin = io::stdin();
|
||||
let interactive = stdin.is_terminal();
|
||||
let redraw = |cfg: &ExperimentConfig, msg: &str| {
|
||||
if interactive {
|
||||
print!("\x1b[2J\x1b[H");
|
||||
}
|
||||
print_dashboard(cfg, on);
|
||||
if !msg.is_empty() {
|
||||
println!(" {}{}{}", k(MUTE, on), msg, k(RST, on));
|
||||
}
|
||||
print!("{}veil›{} ", k(TEAL, on), k(RST, on));
|
||||
let _ = io::stdout().flush();
|
||||
};
|
||||
redraw(&cfg, "type `help` for commands");
|
||||
for line in stdin.lock().lines() {
|
||||
let line = match line {
|
||||
Ok(l) => l,
|
||||
Err(_) => break,
|
||||
};
|
||||
let mut it = line.split_whitespace();
|
||||
let cmd = it.next().unwrap_or("");
|
||||
let arg = it.next().unwrap_or("");
|
||||
let mut msg = String::new();
|
||||
match cmd {
|
||||
"" => {}
|
||||
"quit" | "q" | "exit" => break,
|
||||
"help" | "h" => {
|
||||
if interactive {
|
||||
print!("\x1b[2J\x1b[H");
|
||||
}
|
||||
help();
|
||||
continue;
|
||||
}
|
||||
"on" => cfg.shield.enabled = true,
|
||||
"off" => cfg.shield.enabled = false,
|
||||
"passes" => match arg.parse::<usize>() {
|
||||
Ok(v) => cfg.shield.givens_passes = v.clamp(1, 512),
|
||||
Err(_) => msg = "passes: need a number".into(),
|
||||
},
|
||||
"bits" => match arg.parse::<u32>() {
|
||||
Ok(v) => cfg.shield.feedback_bits = v.clamp(1, 12),
|
||||
Err(_) => msg = "bits: need 1..12".into(),
|
||||
},
|
||||
"n" => match arg.parse::<usize>() {
|
||||
Ok(v) => cfg.scene.identities = v.clamp(2, 128),
|
||||
Err(_) => msg = "n: need 2..128".into(),
|
||||
},
|
||||
"snr" => match arg.parse::<f64>() {
|
||||
Ok(v) => cfg.link.snr_db = v.clamp(0.0, 60.0),
|
||||
Err(_) => msg = "snr: need a number (dB)".into(),
|
||||
},
|
||||
"metric" => match arg {
|
||||
"euclid" | "euclidean" => cfg.attacker_metric = Metric::Euclidean,
|
||||
"cosine" | "cos" => cfg.attacker_metric = Metric::Cosine,
|
||||
_ => msg = "metric: euclid | cosine".into(),
|
||||
},
|
||||
"preset" => {
|
||||
if !preset(arg, &mut cfg) {
|
||||
msg = "preset: scif | board | ward | hotel".into();
|
||||
}
|
||||
}
|
||||
"run" => msg = "ran — numbers above reflect current settings".into(),
|
||||
"optimize" | "opt" => {
|
||||
cfg.shield = veil::hyper_optimize(&cfg).shield;
|
||||
msg = format!(
|
||||
"optimized → passes {} · bits {}",
|
||||
cfg.shield.givens_passes, cfg.shield.feedback_bits
|
||||
);
|
||||
}
|
||||
"proof" => {
|
||||
let w = Proof::witness(&Proof::run_reference());
|
||||
msg = format!(
|
||||
"witness {:#018x} ({})",
|
||||
w,
|
||||
if w == Proof::EXPECTED_WITNESS {
|
||||
"match"
|
||||
} else {
|
||||
"drift"
|
||||
}
|
||||
);
|
||||
}
|
||||
other => msg = format!("unknown: {other} (try `help`)"),
|
||||
}
|
||||
redraw(&cfg, &msg);
|
||||
}
|
||||
if interactive {
|
||||
println!();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let on = color_enabled();
|
||||
let args: Vec<String> = std::env::args().skip(1).collect();
|
||||
let cfg = ExperimentConfig::default();
|
||||
let code = match args.first().map(String::as_str).unwrap_or("") {
|
||||
"" => {
|
||||
if io::stdout().is_terminal() {
|
||||
tui(cfg, on);
|
||||
} else {
|
||||
print_dashboard(&cfg, on);
|
||||
}
|
||||
0
|
||||
}
|
||||
"tui" => {
|
||||
tui(cfg, on);
|
||||
0
|
||||
}
|
||||
"report" => {
|
||||
print_dashboard(&cfg, on);
|
||||
0
|
||||
}
|
||||
"sweep" => {
|
||||
cmd_sweep(&cfg, on);
|
||||
0
|
||||
}
|
||||
"optimize" | "opt" => {
|
||||
cmd_optimize(&cfg, on);
|
||||
0
|
||||
}
|
||||
"adaptive" => {
|
||||
let n = args
|
||||
.get(1)
|
||||
.and_then(|s| s.parse().ok())
|
||||
.unwrap_or(cfg.scene.identities);
|
||||
cmd_adaptive(&cfg, n, on);
|
||||
0
|
||||
}
|
||||
"proof" => cmd_proof(on),
|
||||
"doctor" => cmd_doctor(on),
|
||||
"help" | "-h" | "--help" => {
|
||||
help();
|
||||
0
|
||||
}
|
||||
other => {
|
||||
eprintln!("unknown command: {other}. Try `veil help`.");
|
||||
2
|
||||
}
|
||||
};
|
||||
std::process::exit(code);
|
||||
}
|
||||
Reference in New Issue
Block a user