CHAN.RUN
Restunnel is a Rust monorepo with platform-specific apps for Android (Kotlin) and macOS (Tauri). This page covers how to build from source and understand the project structure.
mise.toml for the exact version# Install mise, then from the repo root:
mise installmise run android:buildmise run android:lintmise run desktop:buildmise run desktop:devtunnel.chan.run/
├── crates/
│ ├── core/ # restunnel-core — shared library
│ │ └── src/
│ │ ├── noise.rs # Noise protocol handshake
│ │ ├── transport.rs # Encrypted channel read/write
│ │ ├── protocol.rs # Binary message codec
│ │ ├── relay.rs # TCP relay + private IP blocking
│ │ ├── auth.rs # Enrollment + key management
│ │ ├── node.rs # Node-side protocol
│ │ ├── keepalive.rs # PING/PONG state machine
│ │ ├── reconnect.rs # Exponential backoff
│ │ └── session/ # Connection management + stream relay
│ ├── hub/ # restunnel-hub binary
│ ├── node-cli/ # restunnel-node binary
│ └── android/ # JNI bridge (cdylib)
├── apps/
│ ├── android/ # Kotlin Android project
│ └── desktop/ # Tauri macOS app
├── docs/ # Internal project documentation
├── docs-site/ # Public documentation (this site)
└── infra/ # Hetzner test infrastructure (OpenTofu)| Crate | Type | Purpose |
|---|---|---|
restunnel-core | Library | Noise handshake, transport, codec, relay, auth, session loop. Shared by all consumers. |
restunnel-hub | Binary | Tunnel server, SOCKS5/HTTP proxy, dashboard, node management. |
restunnel-node | Binary | CLI exit node — thin wrapper around restunnel-core. |
restunnel-android | cdylib | JNI bridge — exposes core to Kotlin via high-level functions. |
restunnel-desktop | Binary | Tauri app — uses core directly via Rust. |
# Build all workspace crates
mise run build
# Run tests
mise run test
# Format check + linting
mise run format
mise run lint
# Security audit
mise run auditmise run android:build # Build .so + APK (debug)
mise run android:install # Install on connected device
mise run android:lint # Kotlin lintmise run desktop:dev # Dev mode with hot reload
mise run desktop:build # Release build (.dmg)
mise run desktop:lint # Clippy on desktop crateThe hub runs on ARM Linux (Hetzner). Cross-compile from macOS:
mise run cross:build # Builds hub + node for aarch64-linux-gnu
mise run deploy:hub # Cross-compile + deploy + restartRequires Docker running (uses cross).
mise run fuzz:all # Run all fuzz targets (60s each)
mise run fuzz:message 300 # Fuzz message codec for 5 minutesRequires cargo-fuzz and nightly Rust.
Conventional Commits with phase scope during phased work:
feat(phase-1a): implement Noise_IK handshake in core
fix(hub): prevent panic on malformed frame
docs: update readmeCommit types drive automated version bumps via release-please.
thiserror for typed errors in restunnel-core (callers need to match variants)anyhow for binaries (hub, node-cli).unwrap() outside of teststracing crate with structured fields:
tracing::info!(node_id = %id, latency_ms = lat, "node connected");Security-critical code (noise, relay, auth, protocol) gets tests immediately. Everything else grows coverage incrementally. See the fuzz targets for codec robustness testing.
A Hetzner Cloud server is available for real-world testing:
mise run infra:apply # Create the server
mise run infra:info # Get IPs
mise run deploy:hub # Deploy the hub binary
mise run infra:destroy # Tear downSee infra/README.md for details.