# IpMixStream Architecture
## Overview
`IpMixStream` tunnels IP packets through the Nym mixnet to an IP Packet Router
(IPR) exit gateway. It provides a high-level API over a single `MixnetStream`,
which handles LP Stream framing and Sphinx packet transport automatically.
## Data Flow
```text
Client IPR
|<--- IpPacketResponse (ips) ---- LP Stream (s=0) -|
| |
|-- IpPacketRequest (data) ------- LP Stream ----->| -> TUN -> internet
|<--- IpPacketResponse (data) --- LP Stream (s=1+) | <- TUN <- internet
```
## Layer Stack
```text
IpMixStream IPR protocol (connect, data, disconnect)
MixnetStream AsyncRead + AsyncWrite, LP Stream framing, seq numbers
Stream Router Dispatches inbound messages by stream_id
MixnetClient Sphinx packet encryption, SURB management
Mixnet Entry GW -> Mix1 -> Mix2 -> Mix3 -> Exit GW
```
## LP Stream Framing
All messages between client and IPR are wrapped in LP Stream frames:
- **Client -> IPR**: `MixnetStream.write()` wraps each write in an LP Stream
Data frame (stream_id, sequence number, payload). The IPR detects
`LpFrameKind::Stream` and strips the header before processing.
- **IPR -> Client**: Both inline responses (connect handshake, pong) and async
TUN responses are wrapped in LP Stream frames with the same stream_id. The
client's stream router dispatches by stream_id to the correct `MixnetStream`.
## IP Allocation
The IPR owns a subnet (`10.0.0.0/16` for IPv4, `fc00::/112` for IPv6) and
allocates addresses to clients on connect. Two modes are supported:
- **Dynamic** (used by `IpMixStream`): The IPR picks a random unused `IpPair`
from the pool (up to 100 retries). `IpMixStream` uses this path.
- **Static**: The client requests specific IPs; the IPR checks availability.
On dynamic connect:
1. IPR calls `find_new_ip()` — random selection from the subnet
2. Registers the client in dual `HashMap<Ipv4Addr, Client>` / `HashMap<Ipv6Addr, Client>`
maps (keyed by allocated IP). The client identity (anonymous sender tag
for v8, Nym address for v6/v7) is stored inside the `ConnectedClient` value.
3. Returns `DynamicConnectSuccess { ips }` to the client
The client uses the allocated IPv4/IPv6 as its source address when constructing
packets. When the TUN device receives a response destined for that IP, the IPR
looks up which client owns it and forwards the packet back through the mixnet.
Reserved addresses: `10.0.0.1` / `fc00::1` (TUN gateway, never allocated).
> **Note — pool isolation from LP dVPN:** This pool (`10.0.0.0/16`, `fc00::/112`
> on `nymtun`) is separate from the authenticator/LP dVPN WireGuard pool
> (`10.1.0.0/16`, `fc01::/112` on `nymwg`). The second octet differs (`10.0` vs
> `10.1`) and each uses its own TUN interface. Both sets of constants live in
> `common/network-defaults/src/constants.rs` (`mixnet_vpn` vs `wireguard` modules).
> The IPR addresses are hardcoded; the WG addresses are configurable but default to
> the non-overlapping range.
## Connection Lifecycle
1. `IpMixStream::new()` discovers the best IPR via Nym API
2. Opens a `MixnetStream` to the IPR (`client.open_stream()`)
3. Sends a dynamic connect request, IPR allocates an `IpPair`
4. Ready for `send_ip_packet()` / `handle_incoming()` loop
5. `disconnect()` shuts down the mixnet client