# Transport Quick Reference

The anonymity layer. Full detail: `DESIGN.md` §3.3, §13.1, §14.2.

## Architecture
- **v1 = Tor via `arti`** (pure-Rust Tor). One uniform transport; bound to an identity/profile, never per-contact.
- Rotating-token rendezvous maps to ephemeral onion services (restricted discovery).
- Fixed-size padded envelopes (16 KB) + randomized latency/jitter to blunt traffic analysis.
- **Rust owns the bytes + Tor logic; the native layer owns lifecycle/scheduling/power** — never an always-on daemon.
- Nym mixnet = v3, behind an `AnonTransport` trait. Direct/clearnet = a separate v2 "Direct-capable" profile.

## Key crates
```rust
// arti-client, tokio, rustls (QUIC later)
```

## Getting onto Tor on a DPI-censored network (obfs4)
If a vanilla bridge bootstraps but then **every** circuit fails (onion AND exit) — while working from
another box through the same bridge — the network is running **DPI** that fingerprints+throttles the
bridge's Tor TLS. Fix: **obfs4** pluggable transport (disguises traffic as random bytes). `tor.rs`
`bridge_pt_name()` detects an `obfs4 IP:port FP cert=… iat-mode=0` bridge line and registers a managed
transport (`arti_client::config::pt::TransportConfigBuilder`, `pt-client` feature) pointing at the
**lyrebird** binary. `pt_binary_path()` resolves it: `PVTCOMS_PT_PATH` → next to the exe → an **embedded**
copy extracted at runtime (build.rs `include_bytes` of `PVTCOMS_LYREBIRD_BIN`; keeps a single-file
download). Server obfs4 setup: memory `relay-deployment`. Verified end-to-end (bootstrap+deposit) over obfs4.

## Reaching the relay on a censored network (clearnet-over-Tor, onion fallback)
Through ONE bridge, the relay's **onion** fails ~100% (an HS needs ~6 hops across several sub-circuits —
HSDir fetch + intro + rendezvous — that can't all complete on a weak guard link; verified: works from a
low-latency box, fails on the user's home network through the SAME bridge). The fix (0.1.28): reach the
relay as a **clearnet destination over Tor** — `connect_over_tor(host, port, is_onion=false)` builds a
normal **3-hop exit circuit** to the relay IP:port (one circuit, no rendezvous). Config key `relayc=IP:port`
(env `PVTCOMS_RELAY_TCP`), carried in the sidecar + invites; `open_relay` prefers it, falls back to the
onion `relay=`. Client stays anonymous (exit sees relay IP, relay sees exit, neither sees client); relay
IP is already public (= bridge IP). Server: `socat` front door on exit-friendly **:80** → localhost relay
(see memory `relay-deployment`). Both paths run over Tor through the bridge — still fail-closed, never a
clearnet-direct path. Verified 3/3 cold-start deposits.

## Onion reliability through a single bridge (still the fallback path)
On a censored network we route through ONE vanilla bridge (the only entry guard); all HS circuits
funnel through it, so arti's defaults bail too early. In `client/src/tor.rs` `bootstrapped_client`:
- `stream_timeouts().connect_timeout` → **120s** (default 10s is far too short for an HS through one
  guard — it cuts `connect()` off before arti's internal HS retries finish; this was THE bug).
- `circuit_timing()`: `request_timeout` 120s, `hs_desc_fetch_attempts` 12, `hs_intro_rend_attempts` 12.
- **Serialize** onion connects (`ONION_GATE` semaphore in `mailbox.rs`) — concurrent HS builds starve
  each other through the one guard link.
- **Prime** a relay circuit at launch (`prime_relay_circuit`) when a bridge is set; warms arti's caches.
See memory `onion-single-bridge-tuning` for the field-validated rationale.

## Fail-closed direct-TCP policy (0.1.35)
In a Tor build, a **non-onion** `relay=` address is dialed **over a Tor exit circuit** (`open_relay`
step 3, `mailbox.rs`) — never by direct TCP, which would expose the user's IP. Two narrow carve-outs:
loopback hosts (`127/8`, `::1`, `localhost` — can't leak; keeps the local testbed working) and the
explicit dev opt-in `PVTCOMS_ALLOW_DIRECT_TCP=1`. No-Tor builds (default `cargo build`) keep the plain
LAN/tests TCP path. Flags parse via `env_flag_on` — anything but an explicit yes is OFF.

## Per-conversation stream isolation (0.1.42, opt-in, ADR-012)
`PVTCOMS_STREAM_ISOLATION=1` isolates relay streams by an opaque conversation key (contact pubkey for
real traffic; a fixed self/cover key; none for random-token media + first-contact) so a relay can't
group a user's rotating tokens by shared exit/circuit. `isolation_token_for` interns one arti
`IsolationToken` per key (same key reuses a circuit; different keys → different circuits); threaded as
`iso` through `relay_deposit`/`relay_pull`/`relay_peek` → `open_relay` → `connect_with_prefs`.
**Default OFF** — per-conversation circuits multiply builds through a single bridge (the starvation the
ONION_GATE tuning avoids), so it needs field verification before becoming the default. NOT a fix for
the cover self-deposit/self-pull token-reuse pattern (hides circuit, not the token graph).

## Diagnostic logging is opt-in (0.1.35)
`pvtcoms-tor.log` (arti circuit/guard/HS internals + `dlog` lines) is written **only** when
`PVTCOMS_TOR_DEBUG_LOG=1` is set. By default nothing is written to disk (the log is transport
metadata — exactly the at-rest surface THREAT_MODEL.md protects); `RUST_LOG` to stderr still works.

## Common Mistakes
- Letting the Rust core try to keep sockets alive in background (OS kills it silently).
- Any "fall back to direct if Tor is slow" path — FORBIDDEN, fail closed.
- Logging IPs, onion addresses, or circuit IDs.
- Default-on disk diagnostics (`pvtcoms-tor.log` is gated behind `PVTCOMS_TOR_DEBUG_LOG=1`).
- Building onion (HS) circuits concurrently through a single bridge — serialize them.

## Full Patterns
→ `DESIGN.md` §3.3 / §13.
