7.7 KiB
CLAUDE.md — Mesh Agent
This file provides agent-specific guidance for the Mesh desktop agent (Rust).
Agent Role
The Mesh Agent is a desktop application (Linux/Windows/macOS) that provides:
- P2P data plane: QUIC connections for file/folder/terminal transfer
- Local capabilities: Terminal/PTY management, file watching
- Server integration: WebSocket control plane, REST API
- Gotify notifications: Direct notification sending
Critical: The agent handles ONLY data plane via QUIC. Media (audio/video/screen) is handled by the web client via WebRTC.
Technology Stack
- Rust stable (edition 2021)
- tokio: Async runtime
- quinn: QUIC implementation
- tokio-tungstenite: WebSocket client
- reqwest: HTTP client
- portable-pty: Cross-platform PTY
- tracing: Logging framework
- thiserror: Error types
Project Structure
agent/
├── src/
│ ├── main.rs # Entry point
│ ├── config/
│ │ └── mod.rs # Configuration management
│ ├── mesh/
│ │ ├── mod.rs
│ │ ├── types.rs # Event type definitions
│ │ ├── ws.rs # WebSocket client
│ │ └── rest.rs # REST API client
│ ├── p2p/
│ │ ├── mod.rs
│ │ ├── endpoint.rs # QUIC endpoint
│ │ └── protocol.rs # P2P protocol messages
│ ├── share/
│ │ ├── mod.rs
│ │ ├── file_send.rs # File transfer
│ │ └── folder_zip.rs # Folder zipping
│ ├── terminal/
│ │ └── mod.rs # PTY management
│ └── notifications/
│ └── mod.rs # Gotify client
├── tests/
├── Cargo.toml
├── Cargo.lock
└── CLAUDE.md
Development Commands
Setup
cd agent
# Install Rust if needed: https://rustup.rs/
rustup update stable
Build
cargo build
Run
cargo run
Build Release
cargo build --release
# Binary in target/release/mesh-agent
Run Tests
cargo test
Format & Lint
cargo fmt
cargo clippy -- -D warnings
Configuration
The agent creates a config file at:
- Linux:
~/.config/mesh/agent.toml - macOS:
~/Library/Application Support/Mesh/agent.toml - Windows:
%APPDATA%\Mesh\agent.toml
Config structure:
device_id = "uuid-v4"
server_url = "http://localhost:8000"
ws_url = "ws://localhost:8000/ws"
auth_token = "optional-jwt-token"
gotify_url = "optional-gotify-url"
gotify_token = "optional-gotify-token"
quic_port = 0 # 0 for random
log_level = "info"
QUIC P2P Protocol
Session Flow
- Request session via WebSocket (
p2p.session.request) - Receive session info (
p2p.session.created) with endpoints and auth - Establish QUIC connection to peer
- Send P2P_HELLO with session_token
- Receive P2P_OK or P2P_DENY
- Transfer data via QUIC streams
- Close session
First Message: P2P_HELLO
Every QUIC stream MUST start with:
{
"t": "P2P_HELLO",
"session_id": "uuid",
"session_token": "jwt-from-server",
"from_device_id": "uuid"
}
The peer validates the token before accepting the stream.
Message Types
File transfer:
FILE_META: name, size, hashFILE_CHUNK: offset, dataFILE_ACK: last_offsetFILE_DONE: final hash
Folder transfer:
FOLDER_MODE: zip or syncZIP_META,ZIP_CHUNK,ZIP_DONE(for zip mode)
Terminal:
TERM_OUT: output data (UTF-8)TERM_RESIZE: cols, rowsTERM_IN: input data (requiresterminal:controlcapability)
See protocol_events_v_2.md for complete protocol.
Error Handling Rules
CRITICAL: NO unwrap() or expect() in production code.
Use Result<T, E> everywhere:
use anyhow::Result;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum AgentError {
#[error("WebSocket error: {0}")]
WebSocket(String),
#[error("QUIC error: {0}")]
Quic(String),
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
}
Handle errors gracefully:
match risky_operation().await {
Ok(result) => handle_success(result),
Err(e) => {
tracing::error!("Operation failed: {}", e);
// Attempt recovery or return error
}
}
Logging
Use tracing crate:
use tracing::{info, warn, error, debug};
info!("Connection established");
warn!("Retrying connection: attempt {}", attempt);
error!("Failed to send file: {}", err);
debug!("Received chunk: offset={}, len={}", offset, len);
Never log:
- Passwords, tokens, secrets
- Full file contents
- Sensitive user data
Security Checklist
- All QUIC sessions validated with server-issued tokens
- Tokens checked for expiration
- Terminal input ONLY accepted with
terminal:controlcapability - SSH secrets never transmitted (stay on local machine)
- File transfers use chunking with hash verification
- No secrets in logs
- TLS 1.3 for all QUIC connections
Terminal/PTY Management
Default mode: Preview (read-only)
- Agent creates PTY locally
- Spawns shell (bash/zsh on Unix, pwsh on Windows)
- Streams output via
TERM_OUTmessages - Ignores
TERM_INmessages unless control granted
Control mode: (requires server-arbitrated capability)
- ONE controller at a time
- Server issues
terminal:controlcapability token - Agent validates token before accepting input
- Input sent via
TERM_INmessages
Important: Can run ssh user@host in the PTY for SSH preview.
File Transfer Strategy
Chunking:
- Default chunk size: 256 KB
- Adjustable based on network conditions
Hashing:
- Use
blake3for speed - Hash each chunk + final hash
- Receiver validates
Resume:
- Track
last_offsetwithFILE_ACK - Resume from last acknowledged offset on reconnect
Backpressure:
- Wait for
FILE_ACKbefore sending next batch - Limit in-flight chunks
Cross-Platform Considerations
PTY:
- Unix:
portable-ptywith bash/zsh - Windows:
portable-ptywith PowerShell or ConPTY
File paths:
- Use
std::path::PathBuf(cross-platform) - Handle path separators correctly
Config directory:
- Linux:
~/.config/mesh/ - macOS:
~/Library/Application Support/Mesh/ - Windows:
%APPDATA%\Mesh\
Build & Packaging
Single binary per platform:
- Linux:
mesh-agent(ELF) - macOS:
mesh-agent(Mach-O) - Windows:
mesh-agent.exe(PE)
Installers (V1/V2):
- Linux:
.deb,.rpm - macOS:
.dmg,.pkg - Windows:
.msi
Release profile (Cargo.toml):
[profile.release]
opt-level = 3
lto = true
codegen-units = 1
strip = true
Testing Strategy
- Unit tests: Individual functions, protocol parsing
- Integration tests: QUIC handshake, file transfer end-to-end
- Manual tests: Cross-platform PTY, real network conditions
Performance Targets
- QUIC connection establishment: < 500ms
- File transfer: > 100 MB/s on LAN
- Terminal latency: < 50ms
- Memory usage: < 50 MB idle, < 200 MB active transfer
Development Workflow
Iterative approach (CRITICAL):
- Build compilable skeleton first
- Add one module at a time
- Test after each module
- NO "big bang" implementations
Module order (recommended):
- Config + logging
- WebSocket client (basic connection)
- REST client (health check, auth)
- QUIC endpoint (skeleton)
- File transfer (simple)
- Terminal/PTY (preview only)
- Gotify notifications
- Advanced features (folder sync, terminal control)
Remember: The agent is data plane only (QUIC). WebRTC media is handled by the web client. Work in short iterations, and never use unwrap() in production code.