7.3 KiB
M001: TCP Transport
Gathered: 2026-01-13 Status: Ready for planning
Project Description
Add TCP socket transport to wx-cli's daemon communication layer, enabling remote clients to query WeChat data over the network. Refactor the existing platform-specific IPC code into a trait-based abstraction to eliminate duplication and make future transport additions easy.
Why This Milestone
Currently wx-cli only supports local IPC (Unix sockets on macOS/Linux, named pipes on Windows). This limits usage to the same machine as the WeChat daemon. Adding TCP transport enables remote access, containerized deployments, and multi-machine setups.
User-Visible Outcome
When this milestone is complete, the user can:
- Start the daemon with TCP listening:
wx daemon start --tcp 127.0.0.1:9876 - Query WeChat data over TCP:
wx sessions --tcp 127.0.0.1:9876 - Use all existing commands without
--tcpand get unchanged local behavior - Check daemon status and logs over TCP:
wx daemon status --tcp 127.0.0.1:9876
Entry point / environment
- Entry point:
wxCLI command with global--tcp host:portflag - Environment: local dev or remote machine (TCP network)
- Live dependencies involved: wx-daemon process
Completion Class
- Contract complete means: Transport traits defined, all three implementations compile, protocol handling is shared
- Integration complete means: Daemon listens on local + TCP simultaneously, client connects via TCP and gets correct response
- Operational complete means: Daemon starts with
--tcp, handles bind errors cleanly, client fails with clear error when TCP unreachable
Final Integrated Acceptance
To call this milestone complete, we must prove:
cargo checkpasses on macOS, Linux, and Windows targets- Daemon started with
--tcp 127.0.0.1:9876accepts TCP connections and responds correctly - Client with
--tcp 127.0.0.1:9876returns same results as local transport - Client with
--tcp 127.0.0.1:9999(unreachable) fails with clear error within 15s - Commands without
--tcpstill work via local transport (no regression)
Architectural Decisions
Transport abstraction via traits
Decision: Use Listener and Connector traits to abstract transport primitives, implement for Unix socket, Windows named pipe, and TCP.
Rationale: Current code has ~50 lines of duplicated JSON-line protocol handling across Unix/Windows. Traits eliminate duplication and provide clear extension point for future transports (TLS, WebSocket).
Alternatives Considered:
- Continue #[cfg] branching — current approach, hard to extend, duplicative
interprocesscrate for all transports — doesn't support TCP natively- Abstract at protocol level only — would still need per-platform listener/connection code
One request per connection (unchanged)
Decision: Keep existing protocol model — one JSON-line request per connection, no keepalive or pooling.
Rationale: Matches existing behavior, minimal complexity, sufficient for CLI usage patterns.
Alternatives Considered:
- Persistent connections with multiplexing — adds protocol complexity, not needed for CLI
- Connection pooling — overkill for single-client CLI tool
Global CLI flag for TCP
Decision: --tcp host:port as global clap flag on root Cli struct, inherited by all subcommands.
Rationale: Discoverable, consistent UX. User specifies once, affects all commands.
Alternatives Considered:
- Environment variables — hidden, harder to discover
- Per-subcommand flag — repetitive, inconsistent
- Config file only — requires edit before use
No built-in TCP security
Decision: No TLS, no auth tokens, no IP whitelist in this milestone. Bind exactly as user specifies.
Rationale: User handles firewall/ACL at OS level. Adding TLS requires cert management, tokio-rustls dependency, and significantly more complexity. Can be added later non-breaking.
Alternatives Considered:
- Default to localhost-only — too restrictive, user should control bind address
- Built-in IP whitelist — adds config complexity, OS firewall is better tool
Error Handling Strategy
- TCP bind failure:
"TCP bind failed on {addr}: {reason}"— daemon aborts startup - TCP connection failure:
"Failed to connect to {addr}: {reason}"— hard error, no fallback - Connection timeout: 15s connect, 120s read/write (matches existing)
- Connection dropped mid-request:
"Connection lost: daemon closed or network error" - Mixed transport mismatch:
"No daemon listening on {addr}"— same as current "daemon not alive" path - No
--tcp: Existing local transport behavior, no change
Risks and Unknowns
- Windows named pipe refactoring may require
interprocesscrate changes — the crate's API differs from std Unix sockets daemon startsubcommand needs to handle existing auto-start behavior (currently daemon starts on first query viaensure_daemon())
Existing Codebase / Prior Art
src/daemon/server.rs— current IPC server, needs refactoring to use Listener traitsrc/cli/transport.rs— current IPC client, needs refactoring to use Connector traitsrc/ipc.rs— protocol types (Request/Response), well-abstracted, no changes neededsrc/config.rs— needs tcp_addr field extension
Relevant Requirements
- R001 — TCP transport on server (M001/S01)
- R002 — TCP transport on client (M001/S02)
- R003 — Transport abstraction layer (M001/S01)
- R004 — Global
--tcpCLI flag (M001/S02) - R005 — Daemon start command (M001/S01)
- R006 — Cross-platform compilation (M001/S01)
- R007 — Error handling for TCP failures (M001/S02)
- R008 — Integration: CLI ↔ daemon over TCP (M001/S04)
Scope
In Scope
- Trait-based transport abstraction (Listener, Connector)
- TCP implementation (TcpListener, TcpStream)
- Global
--tcp host:portCLI flag wx daemon startsubcommand- Error handling for TCP failures
- Cross-platform compilation
Out of Scope / Non-Goals
- TLS encryption
- Authentication tokens
- IP whitelisting
- Connection pooling / keepalive
- Changing the JSON-line protocol
Technical Constraints
- Must maintain backwards compatibility: no
--tcp= existing behavior - tokio is already a dependency (TcpListener/TcpStream available)
interprocesscrate for Windows named pipes — API differs from std
Integration Points
src/daemon/server.rs→src/transport/— server uses Listener traitsrc/cli/transport.rs→src/transport/— client uses Connector traitsrc/config.rs→ optional tcp_addr fieldsrc/cli/mod.rs→ global --tcp flag on Cli struct
Testing Requirements
cargo checkon x86_64-unknown-linux-gnu, x86_64-pc-windows-msvc, and current platform- Unit tests for transport::protocol.rs (JSON round-trip)
- Existing scanner tests continue passing
- Manual smoke test: daemon on TCP, client queries over TCP
Acceptance Criteria
- S01: Transport traits defined, all implementations compile on all platforms, existing behavior unchanged
- S02:
wx daemon start --tcp 127.0.0.1:9876starts daemon listening on TCP - S03:
wx sessions --tcp 127.0.0.1:9876connects via TCP and returns correct results - S04: End-to-end TCP communication verified manually on localhost
Open Questions
- None — scope confirmed, architecture agreed, error strategy defined