feat(hh): ratatui TUI client — chat, live roster, themes

- Connect subcommand: SRP auth then a ratatui UI over tokio + crossterm.
- Async ws (tokio-tungstenite); reader task decrypts/parses frames into events.
- Panes: top bar (e2e + house N/cap), chat scrollback, roster (self marked ⛧),
  input box. Undecryptable frames surface as a system line, not a silent drop.
- Themes (vestments) via TOML --theme; default occult-monochrome + neon.
- Verified live in tmux: render, chat round-trip, roster, join/leave.
- Adds fernet python->rust interop regression test.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
leetcrypt
2026-05-30 13:57:07 -07:00
parent bb1d662ee1
commit 14aa369fb2
10 changed files with 1121 additions and 0 deletions
+14
View File
@@ -235,3 +235,17 @@ mod tests {
assert_eq!(hex::encode(&ch.h_amk), HAMK_HEX, "H_AMK mismatch");
}
}
#[cfg(test)]
mod fernet_interop {
// Token produced by Python `cryptography` Fernet with key = urlsafe_b64(0x42*32).
const KEY: &str = "PulnLblVVdOu6iB0rjW8rQ2U2pwgsky3eod8I2OhLdE=";
const TOK: &str = "gAAAAABqG0ufNzHGkbfMWh4-46KVthUTnXUN9jVvGJ2UxklQFdBMIqBCMXmTmciEnB14kl_H613IOYm5w22bebVUhpu9ULuLf1fjq4jjaIK_ZHZNwCyqjy0=";
#[test]
fn rust_decrypts_python_fernet() {
let f = fernet::Fernet::new(KEY).unwrap();
let pt = f.decrypt(TOK).expect("rust must decrypt python fernet token");
assert_eq!(pt, b"room key interop test");
}
}