Imported from marctjones/corsair (
AGENTS.md). Install upstream withnpx skills add marctjones/corsair. Copyright stays with the author.
AI Agent Development Guide for Corsair
This document provides instructions for AI coding assistants (Claude Code, Gemini, Cursor, etc.) working on the Corsair Tor daemon.
Before Starting Any Work
ALWAYS read IMPLEMENTATION_PLAN.md first to understand:
- Current project status (what's complete, what's in progress)
- What phases are blocked and why
- The specific next tasks to work on
- Detailed step-by-step implementation plans
The implementation plan has checkboxes showing exactly where we left off.
Project Overview
Corsair is an embedded Tor proxy daemon for Servo-based browsers. It provides:
- Tor connectivity via Arti (Rust Tor implementation)
- Binary IPC protocol over Unix Domain Sockets
- Process isolation from the main browser (avoids Arti/Stylo conflicts)
- Lightweight daemon design
Why Corsair Exists
Arti (Tor in Rust) and Servo's Stylo (CSS engine) have conflicting trait implementations that cause Rust compiler recursion overflow when compiled together. Corsair solves this by:
- Running as a separate process
- Communicating via binary IPC over Unix sockets
- Providing a simple request/response protocol
Important: Do NOT try to embed Arti directly in Servo - it will fail with recursion limit errors.
Repository Structure
corsair/
├── Cargo.toml # Package manifest (isolated workspace)
├── src/
│ ├── main.rs # Daemon entry point
│ ├── ipc.rs # IPC protocol handling
│ └── tor.rs # Arti/Tor integration
├── README.md
├── DESIGN.md
└── IMPLEMENTATION_PLAN.md
Coding Standards
Rust Guidelines
- Edition: Rust 2021
- Async Runtime: Tokio (required by Arti)
- Error Handling:
anyhowfor application errors - Serialization:
bincodefor IPC messages - Logging:
tracingcrate (Arti's choice)
Code Style
// Good: Clear error context
async fn connect_tor(host: &str, port: u16) -> anyhow::Result<TorStream> {
let client = TOR_CLIENT.get()
.ok_or_else(|| anyhow!("Tor client not initialized"))?;
client.connect((host, port))
.await
.context("Failed to establish Tor connection")
}
// Good: Proper IPC message handling
async fn handle_message(msg: IpcMessage) -> IpcResponse {
match msg {
IpcMessage::Connect { host, port } => {
match connect_tor(&host, port).await {
Ok(stream) => IpcResponse::Connected,
Err(e) => IpcResponse::Error(e.to_string()),
}
}
IpcMessage::NewIdentity => {
// Request new Tor circuit
}
}
}
Binary IPC Protocol
Message Format:
┌──────────────────┬─────────────────────────────┐
│ Length (4 bytes) │ Bincode-encoded payload │
│ (u32 LE) │ │
└──────────────────┴─────────────────────────────┘
Request Types:
#[derive(Serialize, Deserialize)]
pub enum Request {
Connect { host: String, port: u16 },
NewIdentity,
Status,
Shutdown,
}
Response Types:
#[derive(Serialize, Deserialize)]
pub enum Response {
Connected,
Error(String),
Status { circuits: u32, bootstrap: u8 },
Ok,
}
Key Concepts
Why NOT SOCKS5?
- SOCKS5 adds overhead and complexity
- Binary IPC is faster and simpler
- Direct control over Tor features (new identity, etc.)
- Better error reporting
Process Lifecycle
- Browser starts Corsair daemon
- Corsair bootstraps Tor network
- Browser connects to Corsair socket
- Browser sends connection requests
- Corsair establishes Tor connections
- Data relayed bidirectionally
- Browser sends shutdown on exit
Socket Path
Default: /tmp/corsair.sock
Configurable via --socket argument
Development Tasks
Adding a New IPC Command
- Add variant to
Requestenum inipc.rs - Add variant to
Responseenum - Implement handler in
handle_request() - Update Rigging's
TorConnectorto use it - Write tests
- Update documentation
Modifying Tor Behavior
- Check Arti documentation: https://docs.rs/arti-client
- Modify
tor.rsinitialization - Test with actual Tor network
- Consider bootstrap time impact
Common Commands
# Build
cargo build --release
# Run daemon
./target/release/corsair --socket /tmp/corsair.sock
# Run with verbose logging
RUST_LOG=debug ./target/release/corsair
# Test IPC (manual)
# Use a test client that speaks the binary protocol
# Check Arti version
cargo tree | grep arti
Arti Integration
Client Initialization
use arti_client::{TorClient, TorClientConfig};
use tor_rtcompat::tokio::TokioRuntimeHandle;
async fn init_tor() -> anyhow::Result<TorClient<TokioRuntimeHandle>> {
let config = TorClientConfig::default();
let runtime = TokioRuntimeHandle::current();
TorClient::create_bootstrapped(runtime, config).await
}
Connection Handling
async fn connect(client: &TorClient<impl Runtime>, host: &str, port: u16)
-> anyhow::Result<DataStream>
{
let stream = client.connect((host, port)).await?;
Ok(stream)
}
Important Notes
- Isolated Workspace: Corsair must remain in its own workspace to avoid Stylo conflicts
- Bootstrap Time: Tor bootstrap takes 10-60 seconds on first run
- Circuit Reuse: Multiple connections may share circuits for performance
- Memory Usage: Arti uses ~50-100MB RAM
- No onion services yet: Client-only for now
Error Scenarios
| Error | Cause | Solution |
|---|---|---|
| Bootstrap timeout | Network issues | Check connectivity, increase timeout |
| Socket permission denied | Wrong permissions | Check socket file permissions |
| Address already in use | Daemon already running | Kill existing process |
| Recursion limit | Compiled with Servo | Must be separate workspace |
Testing
# Unit tests
cargo test
# Integration test (requires network)
cargo test --features integration
# Manual testing
# 1. Start daemon
./target/release/corsair &
# 2. Use test client or Rigging's TorConnector
Security Considerations
- Socket has user-only permissions (0600)
- Daemon runs with minimal privileges
- No sensitive data logged
- Tor directory stored in user's home
Knowledge Management Strategy
This project uses a four-tier content organization system (same as llmfp, klareco, pdfe, harbor, rigging, compass):
| Content Type | Where It Goes | Why |
|---|---|---|
| Concepts, algorithms, theory | Wiki | Educational, timeless reference |
| Research, ideas, lab notes | Discussions | Unstructured exploration, feedback |
| Bugs, features, tasks | Issues | Actionable items with completion criteria |
| Code documentation, setup | Markdown files | Version-controlled, code-specific |
Key Rules
- DO NOT create markdown files for educational content - Use the Wiki
- DO NOT add TODO comments in code - Create GitHub issues instead
- DO reference issue numbers in commits:
Fixes #17orSee issue #25 - DO create issues proactively when discovering bugs or enhancements
GitHub CLI Commands
# Issues
gh issue list # List open issues
gh issue create --title "X" --label "bug" # Create issue
gh issue close 123 --comment "Fixed" # Close with comment
# Wiki (separate git repo)
git clone https://github.com/marctjones/corsair.wiki.git
# Edit .md files, then: git add . && git commit -m "msg" && git push