# YTunnel
```
___ ___ _______ __
A TUI-first CLI for managing Cloudflare Tunnels with custom domains. Think ngrok, but using your own Cloudflare domain with persistent URLs and a dashboard to manage them.
**Supported Platforms:** macOS and Linux
## Features
- **TUI Dashboard** - Interactive interface to manage all your tunnels
- **Live Metrics** - Real-time request counts, error rates, and connection status
- **Persistent tunnels** - Tunnels run as background daemons (launchd on macOS, systemd on Linux)
- **Automatic DNS management** - Creates and updates CNAME records automatically
- **Multi-account support** - Manage tunnels across multiple Cloudflare accounts
- **Multi-zone support** - Use different domains for different tunnels
- **SSL/HTTPS** - Automatic via Cloudflare
- **Ephemeral mode** - Quick one-off tunnels with automatic cleanup on exit
## Prerequisites
1. **cloudflared** - Cloudflare's tunnel daemon
**macOS:**
```bash
brew install cloudflare/cloudflare/cloudflared
```
**Linux (Debian/Ubuntu):**
```bash
curl -L https://pkg.cloudflare.com/cloudflare-main.gpg | sudo tee /usr/share/keyrings/cloudflare-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/cloudflare-archive-keyring.gpg] https://pkg.cloudflare.com/cloudflared $(lsb_release -cs) main" | sudo tee /etc/apt/sources.list.d/cloudflared.list
sudo apt update && sudo apt install cloudflared
```
**Linux (other):**
```bash
sudo cp cloudflared /usr/local/bin/
sudo chmod +x /usr/local/bin/cloudflared
```
> **Note:** You only need to *install* cloudflared. Do NOT run it as a system service. YTunnel manages cloudflared processes directly.
2. **Cloudflare API Token** with these permissions:
- Zone → Zone → Edit
- Zone → DNS → Edit
- Account → Cloudflare Tunnel → Edit
Create one at: https://dash.cloudflare.com/profile/api-tokens
3. **A domain** managed by Cloudflare (free tier works)
## Installation
### Homebrew (recommended)
```bash
brew install yetidevworks/ytunnel/ytunnel
```
### From crates.io
```bash
cargo install ytunnel
```
### From source
```bash
git clone https://github.com/yetidevworks/ytunnel
cd ytunnel
cargo install --path .
```
### Pre-built binaries
Download from [GitHub Releases](https://github.com/yetidevworks/ytunnel/releases).
## Quick Start
```bash
# First-time setup
ytunnel init
# Open the TUI dashboard
ytunnel
# Or add a tunnel directly from CLI
ytunnel add myapp localhost:3000 --start
```
## Architecture
### How YTunnel Works
YTunnel is a **management tool**, not a daemon itself. Here's how the pieces fit together:
```
┌─────────────────────────────────────────────────────────────────┐
│ ytunnel (CLI/TUI) │
│ Management tool - runs only when you invoke it │
└─────────────────────────────────────────────────────────────────┘
│
Creates & manages configs for
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ launchd (macOS) / systemd (Linux) │
│ System service manager - always running │
│ │
│ macOS: ~/Library/LaunchAgents/com.ytunnel.<account>.<name>.plist │
│ Linux: ~/.config/systemd/user/ytunnel-<account>-<name>.service │
└─────────────────────────────────────────────────────────────────┘
│
Starts/stops/monitors
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ cloudflared processes │
│ One process per tunnel - runs in background │
│ │
│ • cloudflared tunnel --config myapp.yml run │
│ • cloudflared tunnel --config api.yml run │
└─────────────────────────────────────────────────────────────────┘
│
Connects to
│
▼
┌─────────────────────────────────────────────────────────────────┐
│ Cloudflare Edge │
│ Routes traffic to your tunnels │
└─────────────────────────────────────────────────────────────────┘
```
### Persistence Model
| **Ephemeral** (`ytunnel run`) | Foreground process | No | Quick testing, one-off tunnels |
| **Persistent** (`ytunnel add --start`) | launchd/systemd | Yes* | Production, always-on services |
*Tunnels don't auto-start by default. They start when you run `ytunnel start` and keep running until you `ytunnel stop` or reboot. To auto-start on login, press `A` in the TUI to toggle auto-start (⟳ indicator shows when enabled).
### What YTunnel Creates
When you run `ytunnel add myapp localhost:3000 --start`:
1. **Cloudflare Tunnel** - Created via API, persists in your Cloudflare account
2. **DNS Record** - CNAME pointing `myapp.yourdomain.com` → tunnel
3. **Credentials** - Tunnel credentials JSON file
4. **Config** - cloudflared YAML config file
5. **Service** - launchd plist (macOS) or systemd unit (Linux)
6. **State** - Entry in `tunnels.toml`
The service file tells launchd/systemd to run cloudflared with your config. Logs go to the logs directory.
## TUI Dashboard
Run `ytunnel` with no arguments to open the interactive dashboard:
```
┌─ Tunnels (3) ─────────────────────┬─ Logs: myapp ─────────────────────────────────┐
│ ● myapp myapp.example.com │ 2024-01-20 10:30:15 INF Starting tunnel │
│ ● api api.example.com │ 2024-01-20 10:30:16 INF Connection registered │
│ ○ staging staging.example.com │ 2024-01-20 10:30:17 INF Tunnel connected │
│ │ 2024-01-20 10:30:18 INF Route propagated │
│ │ 2024-01-20 10:30:21 INF Request served GET / │
│ ├─ Metrics ─────────────────────────────────────┤
│ │ Requests: 1,247 Errors: 3 Active: 2 │
│ │ Health: ✓ healthy │
│ │ HA Connections: 4 Edge: dfw08, den01 │
│ │ Status Codes: 200:1198 304:42 404:3 500:4 │
│ │ Traffic: ▁▂▃▅▆▄▃▂▁▂▃▄▅▆▇█▆▅▄▃▂▁▂▃▄▅▆▇ │
├───────────────────────────────────┴───────────────────────────────────────────────┤
│ Started myapp │
│ [a]dd [s]tart [S]top [R]estart [c]opy [o]pen [h]ealth [d]elete [r]efresh [q]uit │
└───────────────────────────────────────────────────────────────────────────────────┘
```
**Status indicators:**
- `●` Running (green)
- `○` Stopped (yellow)
- `✗` Error (red)
- `⟳` Auto-start enabled (cyan, shown after hostname)
**Keyboard shortcuts:**
| `a` | Add a new tunnel |
| `s` | Start selected tunnel |
| `S` | Stop selected tunnel |
| `R` | Restart tunnel (updates daemon config) |
| `c` | Copy tunnel URL to clipboard |
| `o` | Open tunnel URL in browser |
| `h` | Check tunnel health |
| `A` | Toggle auto-start on login (⟳ = enabled) |
| `d` | Delete selected tunnel |
| `m` | Import ephemeral tunnel as managed |
| `;` | Cycle through accounts (when multiple configured) |
| `r` | Refresh status |
| `↑/↓` or `j/k` | Navigate list |
| `q` | Quit |
Tunnels continue running in the background after you close the TUI.
### Metrics Panel
For running tunnels, the TUI displays live metrics from cloudflared's Prometheus endpoint:
- **Requests** - Total requests handled by the tunnel
- **Errors** - Number of failed requests (red if > 0)
- **Active** - Currently in-flight concurrent requests
- **Health** - Whether the tunnel URL is reachable (✓ healthy / ✗ unreachable)
- **HA Connections** - Number of connections to Cloudflare edge (4 = healthy)
- **Edge** - Cloudflare edge locations (e.g., `dfw08` = Dallas)
- **Status Codes** - Breakdown of HTTP response codes
- **Traffic** - Sparkline showing request rate over time
Metrics auto-refresh every 5 seconds. Health checks run every 30 seconds. Use `h` for immediate health check.
### Notifications
When a tunnel goes down or comes back up, ytunnel sends a system notification. This helps you catch issues even when the TUI isn't visible.
- **macOS:** Uses `terminal-notifier` (if installed) or `osascript`
- **Linux:** Uses `notify-send` (requires `libnotify`)
### Ephemeral Tunnels
**Ephemeral tunnels** (created with `ytunnel run`) also appear in the TUI marked as `[ephemeral]`. You can:
- View them alongside managed tunnels
- Delete them from Cloudflare
- Import them as managed tunnels (press `m`) to add daemon control
## CLI Commands
### Persistent Tunnels
```bash
# Add a tunnel (doesn't start it)
ytunnel add myapp localhost:3000
# Add and start immediately
ytunnel add myapp localhost:3000 --start
# Use a specific zone
ytunnel add api localhost:8080 -z dev.example.com
# Start/stop/restart tunnels
ytunnel start myapp
ytunnel stop myapp
ytunnel restart myapp # Stop, update config, start
# View logs
ytunnel logs myapp # Last 50 lines
ytunnel logs myapp -n 100 # Last 100 lines
ytunnel logs myapp -f # Follow (like tail -f)
# List all tunnels with status
ytunnel list
# Delete a tunnel
ytunnel delete myapp
# Reset all configuration (start fresh)
ytunnel reset
ytunnel reset -y # Skip confirmation
```
### Ephemeral Tunnels
For quick one-off tunnels that stop when you press Ctrl+C:
```bash
# Auto-generated subdomain (ytunnel-abc123.example.com)
ytunnel run localhost:3000
# Named subdomain (myapp.example.com)
ytunnel run myapp localhost:3000
# Different zone
ytunnel run api -z dev.example.com localhost:8080
```
### Account Management
```bash
# Add a new account (interactive)
ytunnel init
# List all configured accounts
ytunnel account list
# Set the default account
ytunnel account select production
# or
ytunnel account default production
# Remove an account
ytunnel account remove old-account
# Use a specific account for any command
ytunnel add myapp localhost:3000 --account production
ytunnel list --account dev
```
### Zone Management
```bash
# List available zones
ytunnel zones
# Change default zone
ytunnel zones default dev.example.com
```
## Configuration
### File Locations
**macOS:**
| `~/Library/Application Support/ytunnel/config.toml` | API credentials and zones |
| `~/Library/Application Support/ytunnel/tunnels.toml` | Persistent tunnel state |
| `~/Library/Application Support/ytunnel/<tunnel-id>.json` | Cloudflare tunnel credentials |
| `~/Library/Application Support/ytunnel/tunnel-configs/<name>.yml` | cloudflared config files |
| `~/Library/Application Support/ytunnel/logs/<name>.log` | Tunnel daemon logs |
| `~/Library/LaunchAgents/com.ytunnel.<account>.<name>.plist` | launchd service files |
**Linux:**
| `~/.config/ytunnel/config.toml` | API credentials and zones |
| `~/.config/ytunnel/tunnels.toml` | Persistent tunnel state |
| `~/.config/ytunnel/<tunnel-id>.json` | Cloudflare tunnel credentials |
| `~/.config/ytunnel/tunnel-configs/<name>.yml` | cloudflared config files |
| `~/.config/ytunnel/logs/<name>.log` | Tunnel daemon logs |
| `~/.config/systemd/user/ytunnel-<account>-<name>.service` | systemd service files |
### Main Config
Config file location: `~/Library/Application Support/ytunnel/config.toml` (macOS) or `~/.config/ytunnel/config.toml` (Linux):
```toml
selected_account = "dev"
[[accounts]]
name = "dev"
api_token = "your-token"
account_id = "your-account-id"
default_zone_id = "zone-id"
default_zone_name = "example.com"
[[accounts.zones]]
id = "zone-id"
name = "example.com"
[[accounts]]
name = "production"
api_token = "another-token"
account_id = "another-account-id"
default_zone_id = "prod-zone-id"
default_zone_name = "mysite.io"
[[accounts.zones]]
id = "prod-zone-id"
name = "mysite.io"
```
### Tunnel State
`tunnels.toml` (same directory as config.toml):
```toml
[[tunnels]]
name = "myapp"
account_name = "dev"
target = "localhost:3000"
zone_id = "abc123"
zone_name = "example.com"
hostname = "myapp.example.com"
tunnel_id = "cf-tunnel-id"
enabled = true
auto_start = false # Set to true to start on login
```
## Troubleshooting
### Check tunnel status
```bash
# Via ytunnel
ytunnel list
# Via system service manager
```
### View logs
```bash
# In TUI: select tunnel and view right pane
# Or directly
tail -f ~/Library/Application\ Support/ytunnel/logs/myapp.log # macOS
tail -f ~/.config/ytunnel/logs/myapp.log # Linux
```
### Tunnel won't start
1. Check if cloudflared is installed: `cloudflared --version`
2. Check the log file for errors
3. Verify credentials exist in the config directory
4. Try running manually: `cloudflared tunnel --config <config-path> run`
### Manually manage a tunnel
**macOS:**
```bash
# Stop (replace <account> with your account name, e.g., "dev")
launchctl unload ~/Library/LaunchAgents/com.ytunnel.<account>.myapp.plist
# Start
launchctl load ~/Library/LaunchAgents/com.ytunnel.<account>.myapp.plist
# Remove completely
launchctl unload ~/Library/LaunchAgents/com.ytunnel.<account>.myapp.plist
rm ~/Library/LaunchAgents/com.ytunnel.<account>.myapp.plist
```
**Linux:**
```bash
# Stop (replace <account> with your account name, e.g., "dev")
systemctl --user stop ytunnel-<account>-myapp.service
# Start
systemctl --user start ytunnel-<account>-myapp.service
# Remove completely
systemctl --user stop ytunnel-<account>-myapp.service
systemctl --user disable ytunnel-<account>-myapp.service
rm ~/.config/systemd/user/ytunnel-<account>-myapp.service
systemctl --user daemon-reload
```
## License
MIT