otun

module
v0.3.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 19, 2026 License: MIT

README

otun (Open Tunnel)

A lightweight, open-source ngrok alternative. Expose local services to the internet in seconds.

otun http 3000
INFO Tunnel ready! url=https://af2c9b1e.tunnel.otun.dev
INFO Forwarding requests to=localhost:3000
INFO Request method=GET path=/

Installation

Quick install (macOS/Linux):

curl -fsSL https://raw.githubusercontent.com/bc183/otun/main/install.sh | sh

Manual download: GitHub Releases

From source:

git clone https://github.com/bc183/otun && cd otun && make build
# Binary at ./bin/otun

Usage

otun http 3000                    # Expose localhost:3000
otun http 3000 -s myapp           # Custom subdomain → https://myapp.tunnel.otun.dev
otun http 8080 -S myserver:4443   # Use your own server
otun http 3000 -t my-api-key      # Authenticate with API key
otun version                      # Show version info
Flag Short Default Description
--subdomain -s (random) Custom subdomain
--server -S tunnel.otun.dev:4443 Tunnel server address
--token -t API key for authentication
--config -c ~/.otun.yaml Path to config file
--debug -d false Show debug logs
--no-reconnect false Disable automatic reconnection
--max-retries 0 Max reconnection attempts (0 = unlimited)
--no-inspect false Disable the local inspect UI
--inspect-addr 127.0.0.1:4040 Address for the inspect UI
--inspect-store memory Storage backend: memory or sqlite
--inspect-db ~/.otun/inspect.db SQLite path (when --inspect-store=sqlite)
--inspect-max-records 500 Rolling-buffer size (memory store only; sqlite persists everything)
--inspect-max-body 1048576 Max bytes of each body to capture (1 MiB)

Config File

Store settings in ~/.otun.yaml to avoid repeating flags:

server: tunnel.example.com:4443
token: my-api-key
subdomain: myapp
debug: false
reconnect: true
max_retries: 0

inspect:
  enabled: true
  addr: 127.0.0.1:4040
  store: memory          # or sqlite
  db: ~/.otun/inspect.db
  max_records: 500       # memory store only
  max_body: 1048576

CLI flags override config file values.

Request Inspector

When you run otun http, a local inspection UI is served at http://127.0.0.1:4040. Every request flowing through the tunnel is captured with its headers, body, status, and duration — filter by method/status/path, open a request to see the full request and response, and click ↻ replay to re-fire it directly against your local service.

In-memory (default): rolling buffer of the last --inspect-max-records requests (500 by default). Fast, zero-config, discarded on exit.

SQLite (persistent): survives restarts and keeps every request.

otun http 3000 --inspect-store=sqlite
# records written to ~/.otun/inspect.db by default; override with --inspect-db

Request and response bodies are captured up to --inspect-max-body bytes (1 MiB default); larger bodies still pass through to your service unchanged, the UI just shows the prefix. WebSocket upgrades and text/event-stream responses bypass capture and stream through raw.

Disable the inspector with --no-inspect or move it off the default port with --inspect-addr=127.0.0.1:9999.

Features

  • Fast - Single TCP connection with yamux multiplexing
  • Secure - Automatic HTTPS with Let's Encrypt
  • Reliable - Auto-reconnects on connection loss with exponential backoff
  • Authenticated - Optional API key authentication
  • Simple - One command, optional config file
  • Self-hostable - Run your own server
  • WebSocket support - Full bidirectional streaming
  • Request inspector - Local UI to view, filter, and replay captured traffic

Self-Hosting

Run your own tunnel server with automatic TLS.

1. Setup DNS

Point your domain to your server:

tunnel.example.com    →  A  →  your-server-ip
*.tunnel.example.com  →  A  →  your-server-ip
2. Run Server

Binary:

curl -LO https://github.com/bc183/otun/releases/latest/download/otun-server_linux_amd64.tar.gz
tar xzf otun-server_linux_amd64.tar.gz
sudo ./otun-server -domain tunnel.example.com

Docker:

docker run -d --name otun --restart unless-stopped \
  -p 4443:4443 -p 443:443 -p 80:80 \
  -v otun-certs:/var/lib/otun/certs \
  ghcr.io/bc183/otun:latest \
  otun-server -domain tunnel.example.com

Systemd:

sudo tee /etc/systemd/system/otun.service << 'EOF'
[Unit]
Description=otun tunnel server
After=network.target

[Service]
Type=simple
ExecStart=/usr/local/bin/otun-server -domain tunnel.example.com -api-keys "your-secret-key"
Restart=always

[Install]
WantedBy=multi-user.target
EOF

sudo systemctl enable --now otun
3. Connect
otun http 3000 -S tunnel.example.com:4443
Server Options
Flag Default Description
-domain (required) Base domain for tunnels
-control :4443 Client connection port
-https :443 Public HTTPS port
-http :80 ACME challenge port
-certs /var/lib/otun/certs Certificate storage
-api-keys Comma-separated API keys (enables auth)
-version Print version and exit
Authentication

To require API keys for connections:

# Server
otun-server -domain tunnel.example.com -api-keys "key1,key2,key3"

# Client
otun http 3000 -t key1

When -api-keys is set, clients must provide a valid token to connect.

How It Works

┌──────────┐        ┌─────────────┐        ┌──────┐        ┌─────────┐
│ Browser  │──HTTPS─▶│ otun-server │──yamux─▶│ otun │──HTTP─▶│ Local   │
└──────────┘        └─────────────┘        └──────┘        │ Service │
                                                           └─────────┘
  1. Client (otun) connects to server over TCP with yamux multiplexing
  2. Server terminates TLS and routes requests by subdomain
  3. Requests are forwarded through the tunnel to your local service

Development

make build    # Build binaries
make test     # Run tests

# Local testing (no TLS)
./bin/otun-server -http :8080 -control :4443
./bin/otun http 3000 -s test -S localhost:4443
curl -H "Host: test.localhost:8080" http://localhost:8080/

Roadmap

  • Stream multiplexing (yamux)
  • Subdomain routing
  • Automatic TLS (Let's Encrypt)
  • Request logging
  • Automatic reconnection
  • API key authentication
  • Config file support
  • Web dashboard (request inspection UI)

License

MIT

Directories

Path Synopsis
cmd
client command
Package main implements the otun client.
Package main implements the otun client.
server command
Package main implements the otun server.
Package main implements the otun server.
internal
client
Package client implements the otun tunnel client.
Package client implements the otun tunnel client.
inspect
Package inspect captures, stores, and replays HTTP requests that pass through the tunnel client.
Package inspect captures, stores, and replays HTTP requests that pass through the tunnel client.
protocol
Package protocol defines the control protocol messages for otun.
Package protocol defines the control protocol messages for otun.
proxy
Package proxy provides utilities for bidirectional data transfer between connections.
Package proxy provides utilities for bidirectional data transfer between connections.
server
Package server implements the otun tunnel server.
Package server implements the otun tunnel server.
version
Package version provides version information for otun.
Package version provides version information for otun.
webui
Package webui serves the client-side inspection UI (htmx + Alpine) over HTTP.
Package webui serves the client-side inspection UI (htmx + Alpine) over HTTP.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL