Conversation
An orthogonal presentation layer over the existing traffic sources: the pcap capture or the mitm proxy runs in a background thread and feeds decoded lines into a shared sink; the TUI (ratatui + crossterm) drains them on the main thread and renders a scrollable, filterable, follow-tail view. - decode: SharedSink (thread-safe, capped at 50k lines) + decode::status; out() routes to the sink when installed, else stdout (unchanged) - capture: new lib module — pcap loop + device helpers moved out of main so the TUI can spawn it; status routed via decode::status (stderr when not in TUI) - proxy: serve() now pub async; status/connection messages via decode::status - tui: new module; --tui works with --mode pcap and --mode mitm (mitm spawns its own tokio runtime in the background thread) - main: --tui flag; thin dispatch (source x view) - controls: q/Ctrl-C, j/k/arrows/PgUp/PgDn, g/G, f follow, / filter, c clear Verified under a PTY against bundled PostgreSQL 18: mitm+TUI renders live decoded TLS traffic (title/log/footer, coloured by direction) and quits cleanly.
Replaces the SharedSink mutex with a single crossbeam-channel: decode::out and decode::status push onto OUTPUT_TX; a consumer chosen at startup owns the receiver. The decoder is now fully sink-agnostic (one output path, no mode branch), and the stdout/stderr split is preserved by the consumer (Line→stdout, Status→stderr) rather than baked into the decoder. - decode: Output enum (Line/Status), set_output/close_output, deliver(); drop SharedSink/SHARED_SINK - main: run_stdout() spawns a single consumer thread for the stdout modes and joins it on source exit so nothing is left unflushed - tui: app_loop consumes the channel via try_recv instead of draining a sink crossbeam-channel (runtime-independent) chosen over tokio mpsc so the channel works from both plain threads (pcap/stdout) and tokio tasks (mitm) without requiring tokio's `sync` feature. Verified: stdout mitm routes Line→stdout / Status→stderr with no leakage; TUI still renders live traffic and quits cleanly (rc=0) under a PTY.
- B→F traffic is now green (was dark grey, which read as too dim). - The packet/message name (Query, DataRow, ReadyForQuery, …) is rendered bold by splitting each decoded line into [prefix | kind | rest] spans, styling the kind with the bold modifier. Warnings stay red and connection notices yellow. Verified via a PTY capture: F→B emits \e[38;5;6m (cyan), B→F emits \e[38;5;2m (green), and the message name is wrapped in \e[1m…\e[22m.
Revert the whole-line / message-name colouring: now only the `[F→B]`/`[B→F]` symbol is highlighted (F→B cyan, B→F magenta — high contrast against each other, bold), and the timestamp, message name, and body stay the default colour for easy reading. Warnings remain red, connection notices yellow. Verified via PTY: symbol spans emit \e[38;5;6 (cyan) / \e[38;5;5 (magenta); prefix/suffix spans are unstyled (default fg); no whole-line colour remains.
Re-add bold to the packet/message name (e.g. Query, DataRow, ReadyForQuery), but in the default colour — only the direction symbol stays coloured. So a decoded line reads: default timestamp, coloured+bold symbol, bold packet name (default colour), default body. Verified via PTY: symbol emits \e[38;5;5 (magenta) / \e[38;5;6 (cyan); the packet name is wrapped with bold-on/(\e[22m) bold-off at default colour.
The substring filter needs a more careful design pass (structured filtering by connection / message type, saved filters, search-within, etc.), so drop it for now rather than ship a half-baked version. Removed: the App filter fields, the `filtered()` helper, the `/`-mode key handling, the filter input/footer/cursor rendering, and the README row. `draw` now reads `app.events` directly. Scrolling, follow-tail, and clear remain. Verified via PTY: renders traffic, `c` clears, `q` quits cleanly.
Embed tui.png at the top of the Interactive TUI section.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an interactive, scrollable, filterable TUI as an orthogonal flag
(
--tui) over the existing traffic sources — it works with both--mode pcapand
--mode mitm. The chosen source runs in a background thread and feeds ashared sink; the TUI (ratatui 0.30 + crossterm) drains it on the main thread.
Why orthogonal
Source (
pcap/mitm) and view (stdout/tui) are separate concerns, so--tuiis a flag rather than a third
--modevalue. For mitm+TUI the proxy's tokioruntime is moved into a background thread so the TUI owns the main thread.
Changes
decode:SharedSink(thread-safe, capped at 50k lines) +decode::status;out()routes to the sink when installed, otherwise stdout (unchangedbehaviour). This is the seam that lets the (multi-threaded) sources feed the TUI.
capture(new lib module): pcap loop + device helpers moved out ofmainso the TUI can spawn it in a thread; status routed via
decode::status(stderr when not in TUI → identical to before).
proxy:serve()is nowpub async; status/per-connection messages gothrough
decode::statusso they appear in the TUI.tui(new): installs the sink, spawns the source, runs the ratatui loop,always restores the terminal on exit.
main:--tuiflag + thinsource × viewdispatch.README: documents--tuiwith controls.Controls
q/Ctrl-Cj/k, arrows,PgUp/PgDng/Gf/Enterapplies,Esccancels)cLines are coloured by direction (
F→Bcyan,B→Fgrey; connection noticesyellow; warnings red).
Verification
cargo fmt/cargo clippy --all-targets -- -D warnings/cargo test(4/4) cleannix build✅ andnix flake check --no-build✅ (CI gates)mitm+TUI): renders livedecoded TLS traffic — title bar (
tapgres — mitm mode · N events · following),bordered log with coloured lines (
Query: SELECT 'tui-hello'…,DataRow: { msg='tui-hello' },Terminate), footer keybindings — and exits cleanly(
rc=0, terminal restored) onq.Notes / scope
filtering (by connection / message type), saved filters, search-within, etc.
are natural follow-ups.
--tuiwithpcapstill needs capture privileges (CAP_NET_RAW/root), sameas the stdout path.
this cut).
Checklist
cargo fmtcargo clippy --all-targets -- -D warningscargo testnix build/nix flake check --no-buildREADMEupdated