# Multiprotocol UDP Transport Layer ## Functional & Technical Specification v1.0 ## 1. Purpose This project is a Linux userspace client/server system for transporting arbitrary UDP datagrams through networks where direct UDP connectivity may be restricted. Its primary initial use case is carrying WireGuard traffic, but the software MUST remain completely protocol-agnostic. The system MUST NOT: - configure WireGuard; - inspect WireGuard packets; - depend on WireGuard libraries; - manipulate WireGuard interfaces; - assume anything about the encapsulated UDP protocol. The fundamental abstraction is: > Transport opaque UDP datagrams between two endpoints using the least complex viable carrier. Typical WireGuard deployment: ```text WireGuard client │ │ UDP ▼ 127.0.0.1:51821 │ ▼ Transport client │ │ selected carrier ▼ Internet │ ▼ Transport server │ │ UDP ▼ 127.0.0.1:51820 │ ▼ WireGuard server ``` The exact same setup MUST be usable with any other UDP application. --- # 2. Design Principles The project SHALL follow these principles: 1. Protocol independence. 2. Minimal packet transformation. 3. Datagram semantics preserved wherever possible. 4. No custom cryptography. 5. Pluggable transports. 6. Automatic fallback from simpler transports to more compatible ones. 7. Low-overhead direct mode. 8. Multi-client server from the first release. 9. Unprivileged execution wherever possible. 10. Human-readable TOML configuration. 11. Sensible defaults with optional advanced tuning. 12. Explicit observability and diagnostics. 13. Architecture prepared for multiple client tunnels in a future release. 14. Security-sensitive parsing kept small and deterministic. 15. Transport-specific behavior MUST NOT leak unnecessarily into the core. --- # 3. Initial Platform Scope Initial supported operating system: ```text Linux ``` The project SHALL run entirely in userspace. Root privileges SHALL NOT be required during normal operation. Binding privileged ports such as 443 MAY be accomplished using: ```text CAP_NET_BIND_SERVICE ``` or an external supervisor/socket mechanism. The daemon SHALL NOT require: ```text CAP_NET_ADMIN CAP_SYS_ADMIN ``` for normal operation. --- # 4. Operating Roles The project has two runtime roles: ```text client server ``` They SHOULD initially be exposed through one executable: ```bash project client --config client.toml project server --config server.toml ``` The final project name may replace `project`. --- # 5. Client Data Path The client listens on a configured local UDP address. Default example: ```text 127.0.0.1:51821 ``` An arbitrary local application sends UDP datagrams to this socket. The client: 1. receives the datagram; 2. associates it with the configured tunnel; 3. passes it unchanged to the active transport; 4. transports it to the server; 5. receives return datagrams; 6. sends them to the originating local UDP peer. The application payload MUST remain opaque. --- # 6. Local Client Peer Handling Although v1 represents one configured tunnel, the local UDP listener MUST track the local source peer. For example: ```text 127.0.0.1:49218 -> 127.0.0.1:51821 ``` The most recently active local peer becomes the return destination. For v1, one active local peer per tunnel is sufficient. If packets arrive concurrently from a different local peer, the implementation SHOULD: 1. update the active peer; 2. emit a warning/debug event; 3. continue operation. This avoids coupling the software to a fixed local source port. Future multi-flow support MAY replace this behavior. --- # 7. Server Data Path The server MUST support multiple clients concurrently. Every authenticated client transport session maps to its own backend UDP socket. Example: ```text Client A │ ▼ Session A │ UDP socket :41001 │ ├─────────────► backend :51820 Client B │ ▼ Session B │ UDP socket :41002 │ ├─────────────► backend :51820 Client C │ ▼ Session C │ UDP socket :41003 │ └─────────────► backend :51820 ``` Each backend socket provides natural response demultiplexing. The server MUST NOT inspect the backend protocol in order to determine response ownership. --- # 8. Backend Target The server configuration defines one UDP backend destination for v1. Example: ```toml [backend] target = "127.0.0.1:51820" ``` Every authenticated client receives an independent UDP socket connected to this backend. Future versions MAY support client-specific backend targets. That feature is explicitly out of scope for v1. --- # 9. Transport Architecture The core operates on logical datagrams. A transport session SHALL conceptually expose: ```rust pub trait TransportSession { async fn send_datagram(&self, data: Bytes) -> Result<()>; async fn recv_datagram(&self) -> Result; async fn close(&self) -> Result<()>; } ``` Connection establishment SHALL be separated from established-session behavior. Conceptually: ```rust pub trait TransportConnector { async fn connect( &self, endpoint: &ServerEndpoint, ) -> Result>; } ``` The final implementation MAY use associated types, enums, channels, or trait objects depending on ergonomics and performance. The architectural boundary is more important than the exact Rust syntax. --- # 10. Supported Transports The planned v1 transport order is: ```text 1. direct UDP 2. QUIC DATAGRAM 3. HTTP/3 CONNECT-UDP 4. TLS/TCP framed fallback ``` All four SHALL share the same logical session API. --- # 11. Direct UDP Transport Direct UDP is the lowest-overhead transport. It provides: - native datagram behavior; - minimal encapsulation; - easiest debugging; - lowest latency; - baseline performance measurements. It also provides the least firewall traversal capability. Direct UDP MUST therefore have its own lightweight session and authentication protocol. --- # 12. QUIC Transport QUIC SHALL be the primary encapsulated datagram transport. Application packets SHALL use QUIC DATAGRAM support rather than QUIC streams. Desired semantics: ```text application datagram │ ▼ QUIC DATAGRAM │ ▼ QUIC │ ▼ UDP ``` QUIC provides: - TLS 1.3; - server authentication; - encrypted transport metadata; - connection identity; - connection state; - congestion control; - transport-level integrity; - migration primitives; - keepalive support. The QUIC connection itself constitutes the transport session. No redundant project session identifier SHALL be placed in every QUIC data packet. --- # 13. HTTP/3 Transport HTTP/3 SHALL use legitimate HTTP/3 semantics. Preferred architecture: ```text UDP payload │ ▼ CONNECT-UDP │ ▼ HTTP Datagram │ ▼ HTTP/3 │ ▼ QUIC ``` The implementation SHOULD follow the standardized MASQUE CONNECT-UDP architecture rather than inventing an HTTP-shaped custom transport. The purpose of this transport is firewall compatibility, not sophisticated fingerprint impersonation. --- # 14. TLS/TCP Fallback Some restrictive networks may block UDP completely. The final fallback SHALL therefore operate over TLS/TCP. Datagrams MUST be explicitly framed because TCP has no message boundaries. Frame format: ```text +----------------+----------------------+ | payload length | UDP payload | | u16 BE | variable | +----------------+----------------------+ ``` Maximum payload: ```text 65535 bytes ``` In practice the project SHALL enforce a much lower configurable transport maximum. The fallback MUST preserve original datagram boundaries after reassembly. TCP mode SHALL be considered a compatibility mechanism rather than a performance-optimal transport because head-of-line blocking is unavoidable. --- # 15. Automatic Transport Selection Default client configuration: ```toml [transport] mode = "auto" ``` Automatic mode SHALL attempt transports in the following order: ```text direct_udp ↓ failure quic ↓ failure http3 ↓ failure tls_tcp ``` The first working transport is selected. The selected transport remains active until: - the connection fails; - the daemon is restarted; - the user explicitly requests reconnection; - configuration is reloaded requiring reconnection. After failure, the client SHALL begin transport probing again. --- # 16. Probe Behavior Default values: ```toml [transport.auto] probe_timeout_ms = 1500 retries = 2 ``` A transport is considered unavailable if connection establishment or authentication fails within the configured limit. Authentication rejection MUST be distinguished from connectivity failure. A rejected credential MUST NOT cause automatic fallback through every transport indefinitely. Instead: ```text authentication rejected ↓ stop probing ↓ report configuration/authentication error ``` This prevents invalid credentials from producing unnecessary connection traffic. --- # 17. Transport Preference Override Users MAY explicitly select a transport: ```toml [transport] mode = "quic" ``` Valid initial values: ```text auto direct_udp quic http3 tls_tcp ``` When a specific transport is configured, automatic fallback SHALL be disabled unless separately enabled in a future version. --- # 18. Direct UDP Protocol Direct UDP requires a small project-specific wire protocol. Protocol version: ```text 1 ``` All integer fields SHALL use network byte order. All packet parsers MUST reject malformed or truncated messages before allocating large buffers. --- # 19. Direct UDP Packet Types Initial packet types: ```text 0x01 HELLO 0x02 ACCEPT 0x03 REJECT 0x10 DATA 0x11 KEEPALIVE 0x12 CLOSE ``` Unknown packet types MUST be ignored or rejected according to protocol context. They MUST NOT cause daemon termination. --- # 20. Direct UDP Common Header All direct transport messages use: ```text +---------+---------+---------+---------+ | version | type | flags | hdr_len | | u8 | u8 | u8 | u8 | +---------+---------+---------+---------+ | | | session_id | | u64 | | | +-----------------------------------+ ``` Total common header: ```text 12 bytes ``` Fields: ```text version protocol version type packet type flags type-specific flags hdr_len complete header length in bytes session_id zero during initial HELLO assigned by server after authentication ``` A 64-bit session identifier is selected for v1. It is sufficiently large when generated randomly and checked for collision. --- # 21. Session Identifier Generation Session IDs MUST: - be generated by a cryptographically secure random number generator; - never use sequential counters; - never be predictable; - never use zero; - be collision-checked against active sessions. If a collision occurs, the server generates another identifier. Session IDs are identifiers, not secrets. Authentication MUST NOT rely solely on knowledge of the session ID. --- # 22. Client Identity Each configured client has: ```text client_id shared_secret ``` Example server configuration: ```toml [[auth.clients]] id = "laptop" secret = "base64-or-hex-secret" [[auth.clients]] id = "server-a" secret = "base64-or-hex-secret" ``` Client IDs are not confidential. Secrets MUST have at least 256 bits of entropy. A project CLI SHOULD eventually provide: ```bash project generate-secret ``` --- # 23. Direct UDP HELLO The HELLO packet contains: ```text common header client_id_length u8 client_id bytes timestamp u64 client_nonce 16 bytes authenticator 32 bytes ``` `session_id` SHALL be zero. The authenticator is: ```text HMAC-SHA256( client_secret, protocol_version || client_id || timestamp || client_nonce ) ``` The exact serialization used for the HMAC input MUST be canonical and documented in the protocol implementation. --- # 24. Replay Protection The server MUST protect HELLO authentication against replay. A HELLO is accepted only if: ```text abs(server_time - client_timestamp) <= allowed_clock_skew ``` Default: ```text 60 seconds ``` The server SHALL also retain recently accepted client nonces for the replay window. A repeated: ```text client_id + client_nonce ``` combination MUST be rejected. Nonce replay state MAY expire after: ```text 2 × allowed_clock_skew ``` --- # 25. Direct UDP ACCEPT After successful authentication, the server creates a session and responds: ```text type ACCEPT session_id generated u64 server_nonce 16 bytes idle_timeout u32 seconds max_datagram u16 authenticator 32 bytes ``` The server authenticator SHALL be: ```text HMAC-SHA256( client_secret, session_id || client_nonce || server_nonce || idle_timeout || max_datagram ) ``` This proves that the ACCEPT originated from a party possessing the configured secret. --- # 26. Direct UDP REJECT REJECT SHALL contain a small reason code. Example codes: ```text 0x01 unsupported_version 0x02 unknown_client 0x03 authentication_failed 0x04 replay_detected 0x05 server_busy 0x06 rate_limited ``` Detailed authentication information MUST NOT be returned. For example, an unknown client and an invalid HMAC MAY both be externally represented as: ```text authentication_failed ``` if reducing information leakage is preferred. --- # 27. Direct DATA Packets DATA packets SHALL use: ```text common header sequence u32 auth_tag 16 bytes payload remaining bytes ``` Sequence numbers are included. They provide: - replay protection; - diagnostics; - loss measurement; - duplicate detection; - future protocol extensibility. They MUST NOT be used to reorder packets. The transport preserves the natural UDP arrival order. --- # 28. Direct Session Authentication Direct UDP DATA packets require integrity/authentication. A per-session key SHALL be derived after the handshake. Conceptually: ```text session_key = HKDF-SHA256( client_secret, client_nonce || server_nonce || session_id ) ``` The implementation SHALL use a well-established Rust cryptographic library. The project SHALL NOT implement HMAC, HKDF, or MAC primitives manually. --- # 29. DATA Authentication Tag Each DATA packet SHALL carry a truncated authentication tag. Recommended: ```text HMAC-SHA256 truncated to 128 bits ``` The MAC covers: ```text version type flags session_id sequence payload ``` A 128-bit tag provides ample integrity protection while keeping packet overhead manageable. Unauthenticated DATA packets MUST be discarded before forwarding. --- # 30. Direct DATA Overhead Approximate direct data overhead: ```text common header 12 bytes sequence 4 bytes auth tag 16 bytes -------------------------- total 32 bytes ``` The design favors simple, robust parsing over saving a handful of bytes. Future protocol versions MAY reduce overhead if profiling shows a meaningful benefit. --- # 31. Sequence Numbers Each direction SHALL maintain an independent 32-bit sequence counter. Example: ```text client -> server counter server -> client counter ``` Initial value SHALL be random. Sequence numbers increment modulo `2^32`. Replay protection SHALL use a sliding receive window. Recommended window: ```text 1024 packets ``` Packets may arrive out of order within this window. Already-seen sequence numbers MUST be dropped. Packets older than the replay window MUST be dropped. --- # 32. NAT Rebinding Direct UDP sessions MUST tolerate client NAT rebinding. The server SHALL NOT permanently bind session identity to: ```text source IP + source port ``` Instead: 1. receive packet; 2. parse session ID; 3. verify packet authentication; 4. verify sequence/replay protection; 5. if valid, update the session's current peer address. Therefore: ```text 192.0.2.20:43871 ``` may become: ```text 192.0.2.20:51203 ``` without renegotiating the session. An attacker cannot redirect the session merely by spoofing its session ID because a valid packet MAC is also required. --- # 33. KEEPALIVE Direct transport KEEPALIVE messages SHALL use the same session authentication mechanism as DATA messages. They carry: ```text common header sequence auth_tag ``` with no application payload. Default interval: ```text 15 seconds ``` Keepalive MAY be disabled. Keepalives count as session activity. --- # 34. CLOSE CLOSE SHALL be authenticated. The receiver SHOULD immediately release the associated session. Loss of a CLOSE packet is harmless because idle timeout remains authoritative. --- # 35. Session Timeout Default idle timeout: ```text 120 seconds ``` A session is active when an authenticated: ```text DATA KEEPALIVE ``` packet is received. Expired sessions SHALL: 1. close their backend socket; 2. release memory; 3. remove replay state; 4. increment expiration metrics. --- # 36. QUIC and HTTP/3 Authentication TLS authenticates the server but does not necessarily authenticate the client. QUIC and HTTP/3 transports SHALL therefore use the same logical: ```text client_id shared_secret ``` credentials as direct mode. Authentication occurs once after transport establishment. It SHALL use a challenge-response protocol based on HMAC rather than directly sending the shared secret. Conceptually: ```text server -> random challenge client -> client_id client_nonce HMAC(secret, challenge || client_nonce || client_id) server -> authenticated ``` The exact representation MAY use: - a QUIC control stream; - an HTTP request; - a dedicated initial protocol message. Application DATA MUST NOT be accepted before authentication completes. --- # 37. TLS/TCP Authentication TLS/TCP SHALL use the same challenge-response client authentication mechanism. This gives all transports one conceptual authentication model: ```text server authenticated by TLS where applicable client authenticated by project shared secret ``` --- # 38. Future Authentication The architecture SHOULD permit later introduction of: ```text mTLS public-key client identity short-lived credentials external authentication ``` without altering the core datagram API. These are not required for v1. --- # 39. DoS and Resource Protection The server MUST assume that its public listeners receive hostile traffic. It SHALL implement: ```text maximum active sessions maximum sessions per source IP handshake timeout authentication rate limiting maximum authentication attempts per source maximum datagram size idle session timeout maximum pending handshakes ``` Defaults SHOULD be conservative. Example: ```toml [limits] max_sessions = 1024 max_sessions_per_ip = 32 max_pending_handshakes = 256 handshake_timeout_ms = 3000 ``` Limits MUST be configurable. --- # 40. Allocation Policy Expensive resources MUST NOT be allocated before authentication where avoidable. In particular, the server SHOULD NOT open a backend UDP socket until client authentication succeeds. Desired order: ```text receive handshake ↓ cheap validation ↓ authentication ↓ rate-limit check ↓ session allocation ↓ backend socket creation ``` --- # 41. MTU and Datagram Size The project SHALL treat transport datagram size as a first-class capability. Every active transport session exposes: ```text max_datagram_size ``` The core SHALL reject application datagrams larger than this value. Initial behavior: ```text oversized datagram ↓ drop ↓ increment counter ↓ debug/warn log ``` The project SHALL NOT implement application-layer fragmentation in v1. --- # 42. Recommended Initial Datagram Limit A conservative default application payload limit SHOULD begin around: ```text 1200 bytes ``` and later be refined using transport/path information. Users MAY override this value. The default MUST favor reliability over maximum packet size. --- # 43. Configuration Format Configuration SHALL use TOML. Parsing stack: ```text serde toml ``` Configuration MUST be validated before network listeners start. Invalid configuration SHALL result in a clear fatal error. --- # 44. Minimal Client Configuration ```toml [client] server = "vpn.example.com" id = "laptop" secret_file = "/etc/project/client.secret" [local] listen = "127.0.0.1:51821" [transport] mode = "auto" ``` The server hostname is static in v1. DNS resolution SHALL use the system resolver. --- # 45. Minimal Server Configuration ```toml [server] listen = "0.0.0.0" [backend] target = "127.0.0.1:51820" [tls] certificate = "/etc/letsencrypt/live/vpn.example.com/fullchain.pem" private_key = "/etc/letsencrypt/live/vpn.example.com/privkey.pem" [[auth.clients]] id = "laptop" secret_file = "/etc/project/clients/laptop.secret" ``` --- # 46. Advanced Client Configuration ```toml [client] server = "vpn.example.com" id = "laptop" secret_file = "/etc/project/client.secret" [local] listen = "127.0.0.1:51821" [transport] mode = "auto" [transport.auto] order = [ "direct_udp", "quic", "http3", "tls_tcp", ] probe_timeout_ms = 1500 retries = 2 [transport.direct_udp] port = 51830 keepalive_secs = 15 [transport.quic] port = 443 keepalive_secs = 15 [transport.http3] port = 443 path = "/tunnel" [transport.tls_tcp] port = 443 [network] max_datagram_size = 1200 [logging] level = "info" ``` --- # 47. Advanced Server Configuration ```toml [server] listen = "0.0.0.0" [backend] target = "127.0.0.1:51820" [transports.direct_udp] enabled = true port = 51830 [transports.quic] enabled = true port = 443 [transports.http3] enabled = true port = 443 path = "/tunnel" [transports.tls_tcp] enabled = true port = 443 [tls] certificate = "/etc/letsencrypt/live/vpn.example.com/fullchain.pem" private_key = "/etc/letsencrypt/live/vpn.example.com/privkey.pem" [session] idle_timeout_secs = 120 [limits] max_sessions = 1024 max_sessions_per_ip = 32 max_pending_handshakes = 256 handshake_timeout_ms = 3000 [[auth.clients]] id = "laptop" secret_file = "/etc/project/clients/laptop.secret" [[auth.clients]] id = "desktop" secret_file = "/etc/project/clients/desktop.secret" [logging] level = "info" ``` --- # 48. Secret Storage Inline secrets MAY be accepted for testing but SHOULD generate a warning. Preferred configuration: ```toml secret_file = "/path/to/secret" ``` Secret files SHOULD require restrictive permissions. Recommended: ```text 0600 ``` Secrets MUST NOT appear in logs. --- # 49. TLS Certificates TLS-capable transports SHALL use normal X.509 server certificates. Expected provisioning includes: ```text Certbot acme.sh Caddy other ACME tooling ``` The project does not implement ACME itself in v1. Clients SHALL perform normal hostname verification against the configured static hostname. Certificate verification MUST NOT be disabled by default. --- # 50. Certificate Reload Automatic certificate reload is desirable but optional for the first prototype. The architecture SHOULD allow certificates to be reloaded without rebuilding transport logic. --- # 51. Session Model Core session representation: ```rust struct Session { id: SessionId, client_id: ClientId, transport: TransportKind, backend: UdpSocket, created_at: Instant, last_activity: Instant, stats: SessionStats, } ``` Transport-specific information SHOULD remain outside the generic session where possible. --- # 52. Datagram Representation Initial representation: ```rust struct Datagram { payload: Bytes, } ``` Do not add unnecessary metadata until required. Potential future form: ```rust struct Datagram { payload: Bytes, received_at: Instant, flow_id: Option, } ``` --- # 53. Client State Machine ```text STARTING │ ▼ RESOLVING │ ▼ PROBING │ ├─ direct_udp ├─ quic ├─ http3 └─ tls_tcp │ ▼ AUTHENTICATING │ ▼ CONNECTED │ ├──────── traffic │ └──────── failure │ ▼ RECONNECTING │ ▼ PROBING ``` A transport failure MUST NOT terminate the daemon. --- # 54. Backoff Repeated connection failures SHALL use exponential backoff with jitter. Example bounds: ```text minimum 250 ms maximum 30 s ``` Successful connection resets the backoff. This avoids aggressive reconnect loops during outages. --- # 55. Server Session Lifecycle ```text HANDSHAKE │ ▼ AUTHENTICATING │ ├── reject │ ▼ ACTIVE │ ├── client close ├── transport loss ├── timeout └── server shutdown │ ▼ CLOSED ``` Resources SHALL be released deterministically. --- # 56. Keepalive Strategy Keepalive behavior is transport-specific. ```text direct_udp authenticated protocol KEEPALIVE quic QUIC keepalive http3 underlying QUIC keepalive tls_tcp transport heartbeat or TCP state ``` Keepalive defaults SHOULD preserve common NAT mappings without producing unnecessary traffic. --- # 57. Observability At minimum the client SHALL expose: ```text state server hostname resolved remote address selected transport connection duration RTT where available bytes sent bytes received datagrams sent datagrams received datagrams dropped oversized datagrams authentication failures reconnections max datagram size ``` The server SHALL additionally expose: ```text active sessions sessions by transport sessions by client ID handshake attempts authentication rejections rate-limited attempts expired sessions backend errors ``` --- # 58. Logging Use: ```text tracing tracing-subscriber ``` Logs SHOULD contain structured fields such as: ```text client_id session_id transport remote_addr event ``` Secrets and application payloads MUST NEVER be logged by default. Packet payload logging SHOULD not exist in normal release builds. --- # 59. Logging Levels Suggested semantics: ```text ERROR unrecoverable subsystem failure WARN transport failure malformed traffic rejected config behavior INFO startup successful connection selected transport authenticated session reconnect shutdown DEBUG probing details MTU decisions session lifecycle TRACE low-level protocol state ``` --- # 60. Local Control Interface The daemon SHOULD expose a Unix domain control socket. Example: ```text $XDG_RUNTIME_DIR/project.sock ``` The control protocol MAY initially use newline-delimited JSON. Expected commands: ```text status stats reconnect reload shutdown ``` The control socket MUST use filesystem permissions to prevent unauthorized local control. --- # 61. CLI Initial commands: ```bash project client --config FILE project server --config FILE project check-config --config FILE project generate-secret ``` Later: ```bash project status project stats project reconnect project reload ``` --- # 62. Graceful Shutdown On SIGTERM or SIGINT: 1. stop accepting new sessions; 2. notify transports where appropriate; 3. stop local ingestion; 4. close backend sockets; 5. flush useful final metrics/logs; 6. terminate cleanly. Shutdown MUST have a bounded timeout. --- # 63. Configuration Reload Full hot reload is not mandatory for the first milestone. Architecture SHOULD nonetheless separate: ```text parsed configuration runtime state ``` A future reload MUST be capable of updating: - clients; - limits; - log level; - certificate paths; without requiring architectural redesign. --- # 64. Future Multiple Tunnel Support V1 exposes one client tunnel. Internally, configuration SHOULD already use: ```rust struct TunnelConfig ``` rather than embedding tunnel fields directly everywhere. Initial: ```rust struct ClientConfig { tunnel: TunnelConfig, } ``` Future: ```rust struct ClientConfig { tunnels: Vec, } ``` Future TOML: ```toml [[tunnels]] name = "home" [[tunnels]] name = "work" ``` No v1 UI or runtime behavior is required for multiple tunnels. --- # 65. Rust Technical Stack Recommended language: ```text Rust ``` Recommended runtime: ```text Tokio ``` Recommended major libraries: ```text tokio quinn rustls h3 h3-quinn bytes serde toml clap tracing tracing-subscriber thiserror anyhow socket2 hmac sha2 hkdf rand subtle ``` Potential optional libraries: ```text secrecy zeroize ``` Exact versions SHALL be selected at implementation time. --- # 66. Error Handling Library/core modules SHOULD use typed errors with: ```text thiserror ``` Binary/CLI boundaries MAY use: ```text anyhow ``` Recoverable network failure MUST NOT be represented as an unrecoverable process error. --- # 67. Constant-Time Authentication Authentication tag comparison MUST use constant-time equality. Do not compare authentication data with ordinary: ```rust a == b ``` unless the cryptographic library explicitly guarantees appropriate behavior. Use primitives supplied by the selected crypto implementation or a constant-time helper library. --- # 68. Crypto Policy The project SHALL NOT: - invent encryption algorithms; - implement SHA manually; - implement HMAC manually; - implement HKDF manually; - implement TLS manually. All cryptographic operations SHALL use maintained, reviewed Rust libraries. The direct transport payload itself does not require encryption because the encapsulated application may already be encrypted. Direct transport DOES require integrity and authentication. Users requiring outer confidentiality SHALL use QUIC, HTTP/3, or TLS/TCP. --- # 69. Core Module Structure Initial source tree SHOULD remain simple: ```text src/ ├── main.rs ├── cli.rs ├── config.rs ├── datagram.rs ├── session.rs ├── stats.rs ├── client/ │ ├── mod.rs │ ├── listener.rs │ └── supervisor.rs ├── server/ │ ├── mod.rs │ ├── session.rs │ ├── backend.rs │ └── limits.rs ├── protocol/ │ ├── mod.rs │ ├── direct.rs │ ├── auth.rs │ └── replay.rs └── transport/ ├── mod.rs ├── direct_udp.rs ├── quic.rs ├── http3.rs └── tls_tcp.rs ``` A Cargo workspace SHOULD NOT be introduced until module boundaries have stabilized. --- # 70. Core Dependency Direction Desired dependency structure: ```text configuration │ ▼ core ┌────────┴────────┐ │ │ client server │ │ └──── transport ──┘ │ ┌─────────┼──────────┐ ▼ ▼ ▼ UDP QUIC HTTP/3 │ etc. ``` The core MUST NOT contain scattered transport type checks. Avoid: ```rust if transport == Transport::Quic { ... } ``` unless behavior genuinely belongs at the orchestration layer. --- # 71. Performance Model The fast path SHOULD minimize allocations. Preferred packet representation: ```rust Bytes BytesMut ``` Buffers SHOULD be reused where practical. The implementation SHOULD avoid: - copying payloads unnecessarily; - global mutexes on every packet; - per-packet heap allocation where easily avoidable; - synchronous filesystem operations in packet paths. Correctness takes priority over premature micro-optimization. --- # 72. Concurrency Model Tokio tasks SHALL be used around logical session operations. A server session will approximately contain: ```text transport receive ─────► backend UDP send transport send ◄─────── backend UDP receive ``` These may be implemented using: - paired tasks; - `tokio::select!`; - channels; - a session supervisor. The simplest implementation that preserves cancellation and ownership clarity SHOULD be preferred. --- # 73. Cancellation All long-lived tasks MUST support deterministic cancellation. Recommended primitives include: ```text CancellationToken broadcast/watch channels task ownership through supervisors ``` Detached tasks that cannot be shut down SHOULD be avoided. --- # 74. Server Multi-Client Requirements The server MUST: - accept concurrent clients; - isolate session errors; - prevent one client from crashing another session; - enforce per-client limits; - maintain independent backend sockets; - clean up dead sessions; - maintain aggregate and per-session stats. One malformed client packet MUST NOT terminate a listener. --- # 75. Direct UDP Listener Model The direct listener may use one public UDP socket. Incoming packets are dispatched by: ```text packet type session ID ``` Initial HELLO packets use: ```text session_id = 0 ``` Authenticated DATA packets identify their session through the assigned random ID. --- # 76. QUIC Listener Model One QUIC endpoint accepts many connections. Each authenticated QUIC connection becomes one server session. QUIC connection state acts as transport identity. Application packets arrive through QUIC DATAGRAM frames. --- # 77. HTTP/3 Listener Model HTTP/3 operates over the QUIC listener architecture. Each successful authenticated CONNECT-UDP exchange maps to one backend session. Where feasible, QUIC and HTTP/3 MAY share certificate/configuration infrastructure. --- # 78. TCP Listener Model Each accepted TLS/TCP connection becomes one potential session. No backend socket is created until project authentication succeeds. After authentication, framed datagrams are transported bidirectionally. --- # 79. Protocol Versioning Direct wire protocol packets contain: ```text version = 1 ``` Breaking protocol changes SHALL increment the version. Unknown versions MUST result in clean rejection. Configuration schema and project binary version are independent from transport wire protocol version. --- # 80. Metrics and Benchmarking The project SHOULD eventually include benchmarks for: ```text datagrams/sec throughput latency overhead memory/session CPU usage direct UDP overhead QUIC overhead HTTP/3 overhead TCP fallback overhead ``` Performance tests SHOULD compare the tunnel against direct UDP baseline. --- # 81. Testing Strategy Unit tests SHALL cover: ```text packet serialization packet parsing malformed packets HMAC verification session key derivation sequence windows replay detection configuration validation timeout logic ``` Integration tests SHALL cover: ```text client ↔ server direct UDP multiple simultaneous clients NAT-like source port change authentication rejection server restart client reconnect backend unavailable oversized datagram session expiration ``` Later tests SHALL cover each transport. --- # 82. Fuzzing The direct UDP protocol parser is a good fuzzing target. The parser SHOULD be written so arbitrary byte strings can be fed into it independently of network state. Fuzz targets SHOULD include: ```text direct packet parser TCP frame parser authentication message parser configuration parser where appropriate ``` --- # 83. Development Milestone 1 Goal: ```text generic authenticated UDP relay ``` Implement only: ```text client local UDP listener direct UDP transport server listener authentication handshake session creation backend UDP sockets DATA packet authentication replay protection keepalive session expiry multiple simultaneous clients TOML configuration structured logs basic statistics ``` No QUIC yet. This validates the entire core architecture. --- # 84. Development Milestone 2 Add QUIC. Requirements: ```text TLS certificate loading hostname validation QUIC connection establishment project client authentication QUIC DATAGRAM send/receive keepalive MTU reporting reconnect multi-client server handling ``` No core datagram API redesign should be necessary. If substantial core redesign is required, the transport abstraction should be reconsidered before continuing. --- # 85. Development Milestone 3 Implement: ```text transport = "auto" ``` Automatic order: ```text direct_udp quic ``` Include: - probing; - timeouts; - retries; - exponential reconnect backoff; - status reporting. --- # 86. Development Milestone 4 Implement: ```text HTTP/3 CONNECT-UDP ``` using the existing QUIC infrastructure where practical. Add: ```text HTTP/3 listener CONNECT request handling authentication HTTP datagrams backend session mapping ``` --- # 87. Development Milestone 5 Implement TLS/TCP fallback. Add: ```text TLS listener framed packet parser project authentication bidirectional datagram reconstruction automatic fallback integration ``` --- # 88. Development Milestone 6 Operational polish: ```text Unix control socket status command stats command reconnect command config validation command systemd units CAP_NET_BIND_SERVICE documentation certificate reload configuration reload benchmarks fuzzing installation documentation ``` --- # 89. Definition of v1 Completion Version 1 is considered complete when: 1. multiple clients can concurrently reach one UDP backend; 2. no WireGuard-specific logic exists; 3. direct UDP works reliably; 4. QUIC DATAGRAM works reliably; 5. HTTP/3 CONNECT-UDP works; 6. TLS/TCP fallback works; 7. automatic transport selection works; 8. authentication is enforced on every public transport; 9. direct UDP includes replay and integrity protection; 10. NAT rebinding works for direct UDP; 11. sessions expire and clean up correctly; 12. the server survives malformed and unauthenticated traffic; 13. the daemon runs unprivileged except for optional privileged-port capability; 14. TOML configuration is validated clearly; 15. meaningful status and statistics are available; 16. reconnects occur automatically; 17. automated integration tests cover multi-client operation. --- # 90. Explicit Non-Goals for v1 The following SHALL remain out of scope: ```text WireGuard configuration TUN/TAP interfaces routing management firewall management multi-hop routing browser fingerprint imitation traffic shaping for censorship evasion packet-size mimicry TLS fingerprint spoofing dynamic server discovery server clustering automatic roaming between servers GUI Windows macOS multiple simultaneous client tunnels application-layer fragmentation custom encryption ``` These MAY be explored after the core transport system is mature. --- # 91. Architectural Summary The system can be summarized as: ```text CLIENT Application │ │ UDP ▼ Local listener │ ▼ Tunnel/session core │ ▼ Transport abstraction │ ├── Direct UDP ├── QUIC DATAGRAM ├── HTTP/3 CONNECT-UDP └── TLS/TCP │ ════════════════ Internet ════════════════ │ ▼ Transport listener │ ▼ Authenticated server session │ ▼ Dedicated backend UDP socket │ ▼ Configured UDP backend ``` The core sees only: ```text send datagram receive datagram session opened session closed transport capabilities ``` It does not care whether the external representation is: ```text custom direct UDP packet QUIC connection HTTP/3 CONNECT-UDP flow TLS/TCP stream ``` --- # 92. Final Design Rule The project SHALL always preserve this separation: ```text application protocol ≠ tunnel session ≠ transport ≠ authentication ``` The encapsulated application remains opaque. Sessions provide logical client isolation. Transports determine how datagrams cross the network. Authentication determines who may create sessions. Keeping those four responsibilities independent is the central architectural rule of the project.