# SeeHandshake
> Visualize TLS handshakes in your terminal, in real time.
`seehandshake` is a passive TLS handshake observer. It captures packets on a
chosen network interface, reassembles TLS records off the TCP stream, parses
the plaintext portions of the handshake (TLS 1.3 `ClientHello` and
`ServerHello`), and renders each connection in a three-panel Ratatui interface
with an optional educational overlay that explains what each message does and
why it exists.
Think of it as *htop meets Wireshark meets an interactive TLS textbook.*
## Status
Early development. The MVP targets TLS 1.3 visualization; see the
[roadmap](#roadmap) for what is coming next.
## Screenshots
*(Screenshots will land here once the UI stabilizes.)*
## What you can see, and what you cannot
TLS 1.3 encrypts every handshake message *after* `ServerHello` under keys
derived from the server's ephemeral private key. A passive observer without
those keys cannot decrypt `EncryptedExtensions`, `Certificate`,
`CertificateVerify`, or `Finished`.
`seehandshake` is honest about this:
- `ClientHello` and `ServerHello` are parsed in full (SNI, ALPN offered/chosen,
cipher suites offered/chosen, supported groups, key share group, TLS
version).
- Later stages are detected from encrypted record boundaries and labeled
accordingly. The certificate Subject/Issuer fields display
`encrypted (TLS 1.3)` for pure TLS 1.3 connections.
- The **three-pane view** (connections on the left, the record timeline
in the middle, connection metadata or per-record sections on the
right) lets you step through every TLS record on the wire in order.
Arrowing into a record flips the right pane into a sectioned, educational
breakdown: plaintext handshake messages are broken out field by field
(random, session_id, every cipher suite with its hex code, every
extension with its decoded body and raw bytes); encrypted records get an
honest flight-position label (e.g. `likely EncryptedExtensions +
Certificate`) and a ciphertext preview. Each section carries a direction
hint ("You sent this" / "The server sent you this") and a short "what
this is / why it matters" blurb.
- On Linux, every row also shows the **local process** that opened the
socket: `curl (pid 1234, uid 1000)` with the full cmdline, or `firefox`
for browser flows. CLI cmdlines *are* the user action; browser processes
identify the app, not the specific click. See
[`docs/attribution.md`](docs/attribution.md).
See [`docs/tls13-visibility.md`](docs/tls13-visibility.md) for the full
explanation. Support for `SSLKEYLOGFILE`-based decryption and for TLS 1.2
(where the certificate is sent in plaintext) is planned.
## Install
Live capture needs elevated privileges on every platform. After installing,
see [Permissions](#permissions) for the one extra step.
### crates.io (any platform with Rust)
```
cargo install seehandshake
```
### Debian / Ubuntu 24.04+ or Debian 13+ (prebuilt `.deb`)
Download the `.deb` for your architecture from the
[latest release](https://github.com/AustinJAkerley/SeeHandshake/releases/latest),
then let apt pull in the dependencies:
```
sudo apt install ./seehandshake_*_amd64.deb # or _arm64.deb on ARM
```
Older distros (Ubuntu 22.04 and earlier) ship `libpcap0.8` rather than the
time64 build the `.deb` expects. Use `cargo install seehandshake` there
instead.
### macOS and Windows (prebuilt binaries)
Grab the archive for your platform from the
[latest release](https://github.com/AustinJAkerley/SeeHandshake/releases/latest)
and extract the `seehandshake` binary:
- macOS (Intel or Apple Silicon): no runtime to install; `libpcap` ships with
the system. If Gatekeeper blocks the binary, run
`xattr -d com.apple.quarantine ./seehandshake`.
- Windows: install [Npcap](https://npcap.com/#download) first, then run from an
Administrator terminal.
### From source
```
git clone https://github.com/AustinJAkerley/SeeHandshake.git
cd SeeHandshake
cargo install --path .
```
Requires:
- Rust 1.74 or newer
- `libpcap` development headers
- Debian/Ubuntu: `sudo apt install libpcap-dev`
- Fedora: `sudo dnf install libpcap-devel`
- macOS: preinstalled with the system; no action needed
- Windows: install the [Npcap SDK](https://npcap.com/#download)
### Package managers (in progress)
A Homebrew tap, an AUR package, and eventual submission to the official Debian
archive (`sudo apt install seehandshake`) are planned.
## Permissions
Live packet capture requires elevated privileges.
- **Linux**: either run as root, or grant the binary the required
capabilities:
```
sudo setcap cap_net_raw,cap_net_admin=eip $(which seehandshake)
```
- **macOS**: `/dev/bpf*` devices must be readable by the invoking user, or
run with `sudo`.
- **Windows**: install Npcap and run from an Administrator terminal.
## Quickstart
```
seehandshake --list-interfaces # list available interfaces
seehandshake --interface en0 # capture on en0
seehandshake # capture on the default interface
```
Then, in another terminal:
```
curl https://example.com
```
The connection will appear in the left panel; the middle panel lists every
TLS record on the wire as it arrives; the right panel shows the negotiated
metadata for the selected connection, including an **Origin** row on
Linux naming the local process that owns the socket. Use `←` / `→` (or
`Tab`) to move focus between panes and `↑` / `↓` to select within a pane.
Arrowing through records flips the right pane into a per-record
educational breakdown, one section per field/extension. Press `enter` (or
`→`) to step further right; on a highlighted section it expands the
long-form explanation. `esc` moves focus one pane left. Press `e` to
globally toggle the educational long-form text. Press `f` to switch the
middle pane between the record list and a Client↔Server flow diagram, `d`
to toggle the full-screen handshake reference overlay, and `w` to wipe all
tracked connections. Press `q` to quit.
## Architecture
```
┌──────────┐ frames ┌───────────────┐ updates ┌────────┐
│ capture │────────────▶│ parser+track │─────────────▶│ UI │
│ (pcap) │ │ (etherparse + │ │(Ratatui)│
│ │ │ tls-parser) │ │ │
└──────────┘ └───────────────┘ └────────┘
▲ │ │
│ │ │
PacketSource HandshakeInfo keyboard
trait (swappable (serde::Serialize: (crossterm)
for pcap files, export-ready)
test mocks)
```
Three threads, connected by `std::sync::mpsc` channels: no global mutable
state, no async runtime. See [`docs/architecture.md`](docs/architecture.md).
## Documentation
- [`docs/architecture.md`](docs/architecture.md): module and threading model
- [`docs/tls13-visibility.md`](docs/tls13-visibility.md): what a passive
observer can and cannot see
- [`docs/attribution.md`](docs/attribution.md): how the Linux Origin row is
derived from `/proc/net/tcp` + `/proc/*/fd`, and its limits
- [`docs/development.md`](docs/development.md): building, testing, and
running the TUI locally
- [`docs/packaging.md`](docs/packaging.md): cutting releases and shipping
through crates.io, apt, Homebrew, AUR, Nix, MacPorts, and winget
- `cargo doc --open`: full API reference
## Contributing
See [`CONTRIBUTING.md`](CONTRIBUTING.md) and
[`CODE_OF_CONDUCT.md`](CODE_OF_CONDUCT.md).
## Security
To report a vulnerability, see [`SECURITY.md`](SECURITY.md).
## Roadmap
Design decisions in the MVP already leave room for:
- TLS 1.2 support (plaintext certificate parsing)
- `SSLKEYLOGFILE` decryption
- PCAP file import (offline analysis)
- JSON, Markdown, and Mermaid export
- Certificate chain visualization
- Live traffic statistics
- Session resumption visualization
- Terminal themes
- Plugin architecture
## License
MIT. See [`LICENSE`](LICENSE).