flowscope 0.11.1

Passive flow & session tracking for packet capture (runtime-free, cross-platform)
Documentation
# Getting started

flowscope is passive flow & session tracking for packet-capture
pipelines. Single crate, runtime-free, cross-platform. Pair with
[netring](https://crates.io/crates/netring) for live Linux capture
or with pcap files for offline replay.

This guide walks through four minimal pipelines so you can see
the shape before you commit to a layer:

0. **The shortest path**`Pipeline`, one import, one builder.
1. Lifecycle only — flow events without bytes.
2. Typed L7 messages — HTTP requests from a pcap.
3. Async live capture — same, but via netring.

After each, the right doc to read next is called out.

## Install

```toml
[dependencies]
flowscope = "0.9"
```

MSRV is Rust 1.88 (June 2025).

The default features cover the core stack (`extractors`,
`tracker`, `reassembler`, `session`). Opt into protocol parsers
and observability piecemeal:

```toml
flowscope = { version = "0.9", features = ["l7", "pcap", "metrics", "tracing"] }
```

| Feature | What it adds |
|---------|--------------|
| `http`, `tls`, `dns`, `icmp` | L7 parsers, one feature each |
| `l7` | Umbrella — enables `http` + `tls` + `dns` + `icmp` |
| `pcap` | Offline pcap source |
| `metrics`, `tracing` | Observability (zero-cost when off) |
| `serde` | `Serialize` + `Deserialize` on every public event / message type |
| `ja3` | TLS ClientHello fingerprinting |
| `test-helpers` | Reusable parser stubs for downstream test crates |

## 0. The shortest path: `Pipeline`

The 90 % case: one import, one builder chain, one iterator. The
high-level entry point introduced in 0.9.0.

```rust,ignore
use flowscope::prelude::*;
use flowscope::http::HttpParser;

fn main() -> flowscope::Result<()> {
    let mut pipeline = Pipeline::builder(FiveTuple::bidirectional())
        .session(HttpParser::default())
        .build();

    for event in pipeline.run_pcap("trace.pcap")? {
        match event? {
            Event::Tcp(SessionEvent::Application { message, .. }) => {
                println!("HTTP: {message:?}");
            }
            Event::Flow(FlowEvent::Started { key, .. }) => {
                println!("+ flow {key:?}");
            }
            _ => {}
        }
    }
    Ok(())
}
```

Build with `cargo run --features http,pcap`. The `prelude`
re-exports `Pipeline`, `Event`, `SessionEvent`, `FlowEvent`,
`FiveTuple`, `Timestamp`, `Result`, and the per-feature parser
types you need.

`Pipeline` opinionates the defaults: anomalies emitted, monotonic
timestamps for offline replay. Configure via
`.config(FlowTrackerConfig { … })` and the chainable setters.
When you need per-flow user state, custom drainers, or multiple
parsers per L4, drop down to `FlowSessionDriver` /
`FlowDatagramDriver` — see `recipes.md`.

**Read next:** [`concepts.md`](concepts.md) — the three-tier
shape (`Pipeline` → driver builders → `layers`).

## 1. Lifecycle only

The cheapest layer: a tracker that emits a `FlowEvent` per
packet. No L7. No bytes. Five tuples + state machine.

```rust,ignore
use flowscope::extract::FiveTuple;
use flowscope::pcap::PcapFlowSource;
use flowscope::FlowEvent;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let source = PcapFlowSource::open("trace.pcap")?
        .views(FiveTuple::bidirectional());

    for evt in source {
        match evt? {
            FlowEvent::Started { l4, key, .. } => {
                println!("+ {:?} {:?}", l4, key);
            }
            FlowEvent::Ended { reason, stats, l4, .. } => {
                println!("- {:?} {:?} packets={}",
                    l4, reason,
                    stats.packets_initiator + stats.packets_responder);
            }
            _ => {}
        }
    }
    Ok(())
}
```

Build with `cargo run --features pcap`.

**Read next:** [`concepts.md`](concepts.md) — Layer 1 (extractor)
and Layer 2 (tracker).

## 2. Typed HTTP messages from a pcap

Add the `http` feature and ship parsed `HttpRequest` /
`HttpResponse` events.

```rust,ignore
use flowscope::extract::FiveTuple;
use flowscope::http::{HttpMessage, HttpParser};
use flowscope::pcap::PcapFlowSource;
use flowscope::SessionEvent;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let source = PcapFlowSource::open("trace.pcap")?
        .sessions(FiveTuple::bidirectional(), HttpParser::default());

    for evt in source {
        match evt? {
            SessionEvent::Application {
                message: HttpMessage::Request(req), ..
            } => {
                println!("{} {} (host={})",
                    req.method, req.path,
                    req.host().unwrap_or("?"));
            }
            SessionEvent::Application {
                message: HttpMessage::Response(resp), ..
            } => {
                println!("  → {} {}", resp.status, resp.reason);
            }
            _ => {}
        }
    }
    Ok(())
}
```

Build with `cargo run --features http,pcap`.

The `host()` / `user_agent()` / `cookie()` / `content_type()` /
`content_length()` / `set_cookie()` / generic `header(name)`
accessors save the `find().and_then(str::from_utf8)` dance every
header lookup would otherwise need.

**Read next:** [`recipes.md`](recipes.md) — picking the right
parser API, writing your own, and multi-protocol patterns.

## 3. Async live capture

Same parser, but driven from a live AF_PACKET / AF_XDP socket via
netring:

```rust,ignore
use futures::StreamExt;
use netring::AsyncCapture;
use flowscope::extract::FiveTuple;
use flowscope::http::{HttpMessage, HttpParser};
use flowscope::SessionEvent;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let mut stream = AsyncCapture::open("eth0")?
        .flow_stream(FiveTuple::bidirectional())
        .session_stream(HttpParser::default());

    while let Some(evt) = stream.next().await {
        if let SessionEvent::Application {
            message: HttpMessage::Request(req), ..
        } = evt?
        {
            println!("{} {}", req.method, req.path);
        }
    }
    Ok(())
}
```

flowscope itself is sync — `tokio` lives in netring. The trait
`HttpParser` is the same one in both the offline and async
pipelines.

## Where to go from here

Pick one based on what you're building:

| Want | Read |
|------|------|
| Understand the layers | [`concepts.md`]concepts.md |
| Worked patterns (multi-protocol, correlation, custom parsers) | [`recipes.md`]recipes.md |
| Metrics + tracing + severity routing | [`observability.md`]observability.md |
| Benchmarks and methodology | [`performance.md`]performance.md |
| Why flowscope is shaped this way | [`design.md`]design.md |

## Running the bundled examples

```sh
cargo run --features pcap --example pcap_flow_summary -- trace.pcap
cargo run --features http,pcap --example http_log       -- trace.pcap
cargo run --features dns,pcap  --example dns_log        -- trace.pcap
cargo run --features tls,pcap  --example tls_observer   -- trace.pcap
cargo run --features l7,pcap   --example multi_protocol_monitor -- trace.pcap
```

`generate_fixtures` builds the synthesised pcap test data the
parser tests use; you don't normally need it.