# Development workflow ## Branching Use feature branches off `master`. The project is small enough that long-lived branches are unnecessary. ## Coding - Follow existing formatting (enforced by `cargo fmt`). - Use `anyhow` for application-level errors and `thiserror` for library-style error enums. - Prefer `tracing` over `println!` for diagnostic output. Secrets must be wrapped with `Redacted` before logging. - Use `Arc` for shared strings across async boundaries (e.g., auth tokens in `ListenerConfig` and `ConnectorConfig`). - Keep synchronous protocol parsing in dedicated modules (`socks5.rs`, `framing.rs`) and async I/O in `tunnel.rs`. ## Testing - Inline tests live in `#[cfg(test)]` modules at the bottom of each source file. - Use `tempfile::tempdir()` for generating test credentials. Clean up temp directories with `std::fs::remove_dir_all`. - Use `std::net::TcpListener::bind("127.0.0.1:0")` to get free ports for E2E tests. - E2E tests spawn tokio tasks for listener, connector, and target servers, then abort them in cleanup. ## Committing - Follow conventional commit style: `feat:`, `fix:`, `docs:`, `chore:`. - Keep commits focused. The git history shows the evolution from HTTP-per-stream to multiplexed tunnel; preserve that narrative clarity.