Goal
Add a shared, queryable state framework that tracks live data and metrics
as traffic flows, for both traffic sources (--mode pcap and --mode mitm):
- Live connections — currently-open connections (4-tuple / id, since-when, per-direction counters, cleartext/encrypted).
- Packet count in/out — packets per direction.
- Bytes in/out — bytes per direction.
The immediate consumer is the TUI (--tui) — a metrics bar + a connections
view — but the store should be source-agnostic so stdout mode / future exporters
can read it too.
Background (current architecture)
- pcap:
capture::run → net::parse_frame → flow::ConnTable::handle(seg, pg_port). ConnTable already keys connections by 4-tuple (ConnKey { client, server }), reassembles, and removes them on RST — but exposes nothing.
- mitm:
proxy::serve → per-connection handle_connection spawns two pump tasks (client→server, server→client) that read/write + decode. There is no central connection registry today; each connection is independent.
- Output: decoded/status lines flow through a single
crossbeam-channel (decode::OUTPUT_TX) to a consumer (stdout thread or TUI). The metrics store is orthogonal to that channel — it's shared state read on demand (e.g. per TUI frame).
Proposed design
A new state module holding an Arc<Metrics> shared between source threads and
the consumer/TUI.
pub struct Metrics {
// aggregate (lock-free)
conns_opened: AtomicU64,
conns_live: AtomicUsize,
pkts_in: AtomicU64, // toward server (client -> server)
pkts_out: AtomicU64, // toward client (server -> client)
bytes_in: AtomicU64,
bytes_out:AtomicU64,
// live connection registry
conns: Mutex<HashMap<ConnId, ConnStats>>,
}
pub struct ConnStats {
client: Endpoint, server: Endpoint,
since: Instant,
encrypted: bool,
// per-connection atomics (so hot updates don't take the registry lock)
pkts_in: AtomicU64, pkts_out: AtomicU64,
bytes_in: AtomicU64, bytes_out: AtomicU64,
}
- Counters as atomics (lock-free hot path); the registry behind a
Mutex touched only on open/close.
Metrics::snapshot() reads atomics + clones registry entries for the TUI to render each frame (tearing across fields is fine for display).
- Threaded into the sources at startup:
capture::run(opts, metrics) / proxy::serve(opts, metrics).
Integration points
| Source |
Where metrics update |
Notes |
| pcap |
flow::ConnTable::handle (already classifies direction + has payload.len()) |
register on new conn, deregister on RST/FIN; one segment = one packet |
| mitm |
proxy::handle_connection (open/close) + pump (per read) |
register a ConnStats on accept; client→server pump = in, server→client pump = out |
decode::out already fires per decoded message — bytes/packets are best counted
at the transport edge (ConnTable / pump), not in the decoder.
TUI surfacing
- Metrics bar in the title:
conns 3 · in 1.2k/412p · out 8.4k/530p.
- New connections panel (list of
ConnStats, selectable/filterable) — natural follow-up to the current single log view.
Decisions needed
- in/out convention — propose in = toward server (client→server), out = toward client (server→client), from the server/tap viewpoint. Confirm.
- "packet" for mitm — the proxy layer sees TLS records / app reads, not IP packets. Options: (a) count logical reads per direction, (b) count pgwire messages (via
decode), (c) bytes-only for mitm + true packet counts for pcap. Recommend (a) reads as the mitm analog, clearly labeled.
- Connection identity — unify pcap (4-tuple
ConnKey) and mitm (proxy-local id) into one ConnId? Or keep separate views?
- stdout surfacing — metrics only in
--tui, or an optional periodic status line / --metrics flag for stdout mode?
- Scope of "live" — drop connections on close only, or also expose
conns_opened totals + rates (pkts/s) later?
Non-goals (for this issue)
- Persistent storage / replay of metrics.
- Export endpoints (Prometheus, OpenMetrics) — future, once the snapshot exists.
- pcap byte/packet counts for the non-PG-direction traffic (we only see the filtered PG stream).
References
Goal
Add a shared, queryable state framework that tracks live data and metrics
as traffic flows, for both traffic sources (
--mode pcapand--mode mitm):The immediate consumer is the TUI (
--tui) — a metrics bar + a connectionsview — but the store should be source-agnostic so stdout mode / future exporters
can read it too.
Background (current architecture)
capture::run→net::parse_frame→flow::ConnTable::handle(seg, pg_port).ConnTablealready keys connections by 4-tuple (ConnKey { client, server }), reassembles, and removes them on RST — but exposes nothing.proxy::serve→ per-connectionhandle_connectionspawns twopumptasks (client→server,server→client) that read/write + decode. There is no central connection registry today; each connection is independent.crossbeam-channel(decode::OUTPUT_TX) to a consumer (stdout thread or TUI). The metrics store is orthogonal to that channel — it's shared state read on demand (e.g. per TUI frame).Proposed design
A new
statemodule holding anArc<Metrics>shared between source threads andthe consumer/TUI.
Mutextouched only on open/close.Metrics::snapshot()reads atomics + clones registry entries for the TUI to render each frame (tearing across fields is fine for display).capture::run(opts, metrics)/proxy::serve(opts, metrics).Integration points
flow::ConnTable::handle(already classifies direction + haspayload.len())proxy::handle_connection(open/close) +pump(per read)ConnStatson accept;client→serverpump = in,server→clientpump = outdecode::outalready fires per decoded message — bytes/packets are best countedat the transport edge (ConnTable / pump), not in the decoder.
TUI surfacing
conns 3 · in 1.2k/412p · out 8.4k/530p.ConnStats, selectable/filterable) — natural follow-up to the current single log view.Decisions needed
decode), (c) bytes-only for mitm + true packet counts for pcap. Recommend (a) reads as the mitm analog, clearly labeled.ConnKey) and mitm (proxy-local id) into oneConnId? Or keep separate views?--tui, or an optional periodic status line /--metricsflag for stdout mode?conns_openedtotals + rates (pkts/s) later?Non-goals (for this issue)
References
ConnTable,ConnKey)serve,handle_connection,pump)OUTPUT_TX,Output)