Designing a Transparent Proxy in Rust: The Foundation Behind Lens
In my previous article, I introduced Lens, an open-source Rust project that aims to visualize application traffic during local development.
One of the first architectural decisions I had to make was how Lens should observe network traffic.
Should it inspect packets?
Use eBPF?
Require application instrumentation?
Or sit directly in the network path?
After evaluating the trade-offs, I decided to build Lens around a transparent proxy architecture.
Why a Proxy?
Consider a typical local development setup.
Client
│
▼
Application
│
▼
PostgreSQL
With a transparent proxy, the flow becomes:
Client
│
▼
Lens Proxy
│
▼
Application
│
▼
Lens Proxy
│
▼
PostgreSQL
This gives Lens complete visibility into requests and responses while remaining independent of application logic.
Benefits
A proxy-based design allows Lens to:
- Decode application protocols
- Measure request latency
- Build request dependency graphs
- Redact sensitive information
- Support multiple protocols through a common abstraction
Challenges
The approach isn't without trade-offs.
A transparent proxy needs to:
- Preserve protocol correctness
- Introduce minimal latency
- Handle streaming traffic
- Support TLS
- Remain cross-platform
Each of these influences the overall architecture.
Why Rust?
Networking software benefits from:
- predictable performance
- memory safety
- zero-cost abstractions
- excellent async support through Tokio
Rust lets me focus on protocol logic without sacrificing performance.
Looking Ahead
The next milestone is expanding protocol support while keeping the architecture modular enough to accommodate additional parsers.
I'm documenting the journey as I build Lens, so future articles will dive into topics like protocol parsing, async networking, and terminal UI design.
Repository:
https://github.com/dpsyfk/lens
I'd love contributors and their contributions.
Top comments (0)