Skip to content
Agentmart
Submit a tool

CLAUDE.md · joaoh82/rustunnel

CLAUDE.md from joaoh82/rustunnel: 251 lines covering What is rustunnel, Parent project, Common Commands, Workspace Structure and more.

Template by joaoh82

IndexedAGPL-3.0★ 657
Write a review

Install

Works with Claude Code. Read the repository before you give any tool access to your data.

curl -fsSL https://raw.githubusercontent.com/joaoh82/rustunnel/main/CLAUDE.md -o CLAUDE.md

Read it and adapt it to your project before use.

Get the weekly diff

The weekly diff: one email a week with new agent tools, notable updates and grade changes, plus security alerts for tools you follow. Double opt-in, one-click unsubscribe.

We use your email only for this newsletter. Privacy

About

The CLAUDE.md file that joaoh82/rustunnel (657 stars) keeps for coding agents. It has 251 lines in 11 sections: What is rustunnel; Parent project; Common Commands; Workspace Structure; Architecture; Databases; Configuration; Observability; Repository Conventions; CI; Knowledge Base. Use it as a model for your own project's instructions.

Use cases: code-generation

No reviews yet. Installed it? Reviews come from verified installers only. How reviews work

What's in it

A CLAUDE.md file: project memory Claude Code loads at the start of every session.

For
Claude Code
Length
251 lines
File
CLAUDE.md

Sections

  1. What is rustunnel
  2. Parent project
  3. Common Commands
  4. Workspace Structure
  5. Architecture
  6. Databases
  7. Configuration
  8. Observability
  9. Repository Conventions
  10. CI
  11. Knowledge Base

First lines (shown under the repo's AGPL-3.0 license)

# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What is rustunnel

rustunnel is a self-hosted secure tunnel server written in Rust (similar to ngrok). It exposes local services through a public server via encrypted WebSocket connections with TLS termination, HTTP/TCP/UDP proxying, P2P direct mode (NAT hole punching via STUN/QUIC), a live dashboard, Prometheus metrics, and audit logging.

## Parent project

**Parent project:** [rustunnel](/Users/joaoh82/projects/rustunnel/CLAUDE.md) — read for shared dev commands, env vars, cross-service architecture, **and the global working guidelines** (never-touch-main, task management, pull-request registration, reserved ports, engineering defaults). Those guideline sections live only in the parent `CLAUDE.md` — this file does not duplicate them.

### How I fit in

This crate (`rustunnel/`) is the **core tunnel server + CLI client + MCP server**. Siblings in the parent meta-repo:

| Sibling | Stack | Dev port | Role |
|---------|-------|----------|------|
| `rustunnel-web/` | Rust (Axum) + Next.js 15 | 3001 / 3000 | Platform API + public website + marketing pages (registration, JWT, billing) |
| `rustunnel-admin-dashboard/` | Next.js 14 | 3002 | Internal admin UI |
| `rustunnel-private/` | Markdown / Bash / Ansible | — | Ops docs, provisioning playbooks |
| `docs/` | Mintlify (MDX) | 3000 | Public documentation |
| `homebrew-rustunnelcli/` | Ruby | — | Homebrew tap formula |
| `rustunnel-status/` | Upptime (YAML + Actions) | — | `status.rustunnel.com`, probes every edge |

This server shares its **PostgreSQL database** with `rustunnel-web/platform-api` (the `tokens` table is the auth boundary). Edge servers (`eu`, `us`, `ap`) all run this binary; `platform-api` runs on a separate VPS at `api.rustunnel.com`.

## Common Commands

```bash
# Build
cargo build --workspace
make build
make build-full        # rebuild dashboard UI (Next.js export) + server crate (use after UI changes)

# Release build (optimized binaries: rustunnel-server, rustunnel-client, rustunnel-mcp)
make release

# Test (requires PostgreSQL — start first)
make db-start
make test
# Or manually:
TEST_DATABASE_URL=postgres://rustunnel:test@localhost:5432/rustunnel_test cargo test --workspace

# Stop test DB
make db-stop

# Lint & format
make fmt           # cargo fmt --all
make lint          # cargo clippy --workspace --all-targets -- -D warnings
make check         # fmt check + clippy (mirrors CI)

# Run a single test
TEST_DATABASE_URL=postgres://rustunnel:test@localhost:5432/rustunnel_test cargo test <test_name> -p <crate_name>

# Install pre-push hooks (run once)
make install-hooks
```

### Local Development

```bash
# One-time TLS cert in /tmp/rustunnel-dev (re-run after reboot — tmpfs is wiped)
make dev-setup

# Run server (uses deploy/local/server.toml: self-signed cert, require_auth = false)
cargo run -p rustunnel-server -- --config deploy/local/server.toml

# Run client — HTTP, TCP, UDP
cargo run -p rustunnel-client -- http 3000 --server localhost:4040 --token dev-secret-change-me --insecure
cargo run -p rustunnel-client -- tcp  5432 --server localhost:4040 --token dev-secret-change-me --insecure
cargo run -p rustunnel-client -- udp  27015 --server localhost:4040 --token dev-secret-change-me --insecure

# Dashboard UI (Next.js, dev mode — proxies API to localhost:8443)
make ui-install    # first time
make ui-dev
```

## Workspace Structure

Four crates in `crates/`, plus integration tests and a Next.js dashboard (`dashboard-ui/`) whose static export is embedded into the server binary:

| Crate | Purpose |
|-------|---------|
| `rustunnel-protocol` | Shared `ControlFrame` enum + `TunnelProtocol` (Http/Https/Tcp/Udp/P2p) and serialization |
| `rustunnel-server` | Server: control plane, HTTP/TCP/UDP edges, dashboard, TLS, ACME |
| `rustunnel-client` | CLI client with auto-reconnect, local port forwarding, P2P/STUN, region auto-select, Ratatui terminal UI (`src/tui/`) and a loopback request inspector (`src/inspect/`) |
| `rustunnel-mcp` | MCP server exposing rustunnel as tools to AI agents |

## Architecture

### High-Level Data Flow

```
Internet → HTTP/TCP/UDP Edge → TunnelCore (router) → Control Plane → yamux stream → Local service
                                                  ↘ P2P direct (QUIC, NAT-punched) ↗
```

The server spawns concurrent tasks for each subsystem:
- **Control Plane** (`:4040`) — WebSocket server for client connections; handles auth, tunnel registration, `NewConnection` delivery, P2P signaling
- **HTTP/HTTPS Edge** (`:80` / `:443`) — TLS-terminated reverse proxy; routes by subdomain
- **TCP Edge** (`:20000+`) — Raw TCP proxy; routes by assigned port
- **UDP Edge** (configurable port range) — UDP proxy; routes by assigned port
- **Dashboard** (`:8443`) — REST API + embedded Next.js SPA + per-instance SQLite request capture
- **Prometheus Metrics** (`:9090`)
- **ACME Renewal** (background, optional — Let's Encrypt + Cloudflare DNS-01)

### Connection Handoff (Core Mechanism)

1. HTTP/TCP/UDP edge receives inbound connection → registers a `pending_conn` (oneshot channel) in `TunnelCore` with a `conn_id`
2. Control plane sends `NewConnection { conn_id, protocol }` to the relevant client over WebSocket
3. Client opens a new yamux stream for that `conn_id`
4. Server resolves the oneshot, unblocking the edge task
5. Edge task and client stream data bidirectionally through yamux

### P2P Direct Mode

For peer-to-peer tunnels (`TunnelProtocol::P2p`), the server acts as a rendezvous/signaling broker only — bytes do not flow through it once the punch succeeds:

1. Publisher registers a named tunnel with a `secret_hash`; reports its NAT type and STUN-mapped addresses (`P2pNatInfo`)
2. Subscriber sends `P2pConnect { target_tunnel_name, secret_hash }`
3. Server uses `classify_nat_pair` to pick a hole-punching strategy and exchanges peer addresses via P2P frames
4. Both peers establish a direct **QUIC** connection (via `quinn`) — server is out of the data path
5. Falls back to relayed mode when NAT classification rules out direct connectivity

Client implementation: `crates/rustunnel-client/src/p2p_direct.rs`, `stun.rs`. Server-side state: `P2pPublisher` in `core/router.rs`.

### TunnelCore (`crates/rustunnel-server/src/core/router.rs`)

Central shared state, `Arc`-wrapped and passed to all subsystems:

```rust
pub struct TunnelCore {
    http_routes: DashMap<String, Arc<TunnelGroup>>,  // subdomain → group of members
    tcp_routes:  DashMap<u16, Arc<TunnelGroup>>,     // port → group of members
    udp_routes:  DashMap<u16, Arc<TunnelGroup>>,     // port → group of members
    p2p_tunnels: DashMap<String, P2pPublisher>,      // name → P2P publisher
    sessions:    DashMap<Uuid, SessionInfo>,         // connected clients
    available_tcp_ports: Mutex<Vec<u16>>,            // free TCP port pool
    available_udp_ports: Mutex<Vec<u16>>,            // free UDP port pool
    tunnel_index: DashMap<Uuid, TunnelKey>,          // tunnel_id → key (O(1) removal)
    pending_conns: DashMap<Uuid, oneshot::Sender>,   // conn_id → yamux resolver
    tcp_events: broadcast::Sender<TcpTunnelEvent>,
    udp_events: broadcast::Sender<UdpTunnelEvent>,
    rate_limiter: Arc<RateLimiter>,                  // per-tunnel token bucket
    ip_limiter:   Arc<IpRateLimiter>,                // per-source-IP sliding window
}
```

Each route key resolves to a `TunnelGroup` of one or more `GroupMember`s
(see `core/tunnel.rs`). Today every group has exactly one member — the route
shape is in place ahead of TUNNEL-7 group-based load balancing (`see
../rustunnel-private/docs/development/load-balancing-and-health-checks.md`).
`resolve_http` / `resolve_tcp` / `resolve_udp` pick a healthy member uniformly
at random; ungrouped registrations stay healthy for life. The
`[load_balancing] enabled` flag in `server.toml` is the kill switch for
honouring multi-member groups (defaults to `false`; flipped on per region
during the EU → US → AP rollout).

### Protocol (`crates/rustunnel-protocol/src/frame.rs`)

Signals

Stars
657
Forks
48
Open issues
0
Contributors
4
Last commit (this folder)
Jul 31, 2026 (2 months ago)
Last release
Sep 3, 2026 (1 month ago)

From GitHub (fetched Oct 24, 2026).

Details

License
AGPL-3.0
First seen
Oct 24, 2026
Last checked
Oct 24, 2026

More from joaoh82

Cite this page

CLAUDE.md · joaoh82/rustunnel. Agent Mart. https://agentmart.dev/templates/joaoh82-rustunnel-claude-md (last updated 2026-10-24)