Metadata-Version: 2.4
Name: tunnex
Version: 26.10.9.19
Summary: Expose local HTTP, WebSocket, TCP and UDP services at a public *.tunnex.org address
Home-page: https://tunnex.org
Author: Tunnex
Author-email: dev@tunnex.org
License: MIT
Project-URL: Source, https://gitlab.com/homycoder/tunnex
Classifier: Programming Language :: Python :: 3
Classifier: Environment :: Console
Classifier: Intended Audience :: Developers
Classifier: Topic :: Internet :: Proxy Servers
Classifier: Topic :: Software Development
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: click>=8.0.0
Requires-Dist: rich>=13.0.0
Requires-Dist: websockets>=12.0
Requires-Dist: httpx>=0.25.0
Requires-Dist: fastapi>=0.104.0
Requires-Dist: uvicorn>=0.24.0
Requires-Dist: sqlalchemy[asyncio]>=2.0.0
Requires-Dist: aiosqlite>=0.19.0
Requires-Dist: pydantic>=2.0.0
Requires-Dist: pyyaml>=6.0
Dynamic: author
Dynamic: author-email
Dynamic: classifier
Dynamic: description
Dynamic: description-content-type
Dynamic: home-page
Dynamic: license
Dynamic: license-file
Dynamic: project-url
Dynamic: requires-dist
Dynamic: requires-python
Dynamic: summary

# Tunnex

Expose local services to the internet through public domains. Supports HTTP/HTTPS, WebSocket, TCP and UDP.

```
localhost:3000  →  https://my-app.tunnex.org
```

## Features

- **HTTP/HTTPS** — proxy web applications with automatic TLS
- **WebSocket** — full WS support (HMR, Socket.IO, chats)
- **TCP** — tunnels for databases, SSH and any TCP service
- **UDP** — tunnels for VoIP, game servers, IoT
- **Web dashboard** — live view of requests and TCP/UDP streams, settings management
- **Custom subdomains** — `your-name.tunnex.org`
- **Rate limiting** — on both the client and the server
- **Custom response** — stub mode with a configurable response
- **Replay** — resend HTTP requests from the dashboard

## Quick start

### Install

```bash
pip install tunnex
```

### Run

```bash
tunnex --port 3000
```

Once started:
- Public URL: `https://<subdomain>.tunnex.org`
- Dashboard: `http://127.0.0.1:8080`

Default server: `wss://connect.tunnex.org` (override with `--server`).

### CLI options

| Option | Description | Default |
|---|---|---|
| `--port, -p` | Local application port | — (required) |
| `--dashboard-port, -d` | Dashboard port | `8080` |
| `--server, -s` | Tunnel server URL | `wss://connect.tunnex.org` |
| `--config` | Path to the client config | — |

### Subcommands

```bash
tunnex start --port 3000    # start (default)
tunnex list                 # list registered subdomains
```

## Architecture

```
┌──────────────┐                    ┌───────────────┐    WebSocket (TLS)    ┌───────────────┐
│  Your app    │◄──────────────────►│ Tunnex client │◄─────────────────────►│ Tunnex server │
│  :3000       │  localhost proxy   │   (Python)    │   encrypted tunnel    │               │
└──────────────┘                    └───────────────┘                       └───────┬───────┘
                                                                                    │ HTTPS/TCP/UDP
                                                                                    ▼
                                                                            ┌───────────────┐
                                                                            │    Users      │
                                                                            │  (internet)   │
                                                                            └───────────────┘
```

### Tech stack

| Component | Technologies |
|---|---|
| Server | Python 3.10+, FastAPI, Uvicorn, SQLAlchemy, aiosqlite |
| Client | Python 3.10+, websockets, httpx, Click, Rich |
| Protocol | WebSocket + JSON, Base64 for binary data |
| Deployment | Docker, Nginx, GitLab CI/CD |
| Database | SQLite (client), SQLite/PostgreSQL (server) |

## Dashboard

Local dashboard at `http://127.0.0.1:8080`:

- **Requests** — live list of HTTP requests, filtering by method/status, URL search, headers and bodies
- **Replay** — resend a request, optionally with modified parameters
- **TCP** — active TCP streams, hex view, send data, close connections
- **UDP** — UDP events, packet view (hex/text), send packets
- **Settings** — subdomain, local port, rate limit, custom response
- **Status** — connection state, public URL, uptime, errors

Updates arrive in real time over WebSocket.

## Server deployment

### Domain layout

| Host | Purpose |
|---|---|
| `tunnex.org` | Landing page (`www` redirects here) |
| `connect.tunnex.org` | Control endpoint the client connects to (`/tunnel/connect`, `/api/*`) |
| `*.tunnex.org` | Public tunnels |

DNS is hosted on Cloudflare. All records are **DNS-only** (not proxied): tunnels use raw TCP/UDP
ports and second-level wildcards that the Cloudflare proxy cannot serve. Reserved subdomains
(`www`, `connect`, `api`, `mail`, …) cannot be claimed by tunnels.

### Bootstrap a fresh host

[deploy/bootstrap-server.sh](deploy/bootstrap-server.sh) prepares an Ubuntu 24.04 host: installs Docker,
Nginx, certbot and ufw, issues a wildcard Let's Encrypt certificate for `tunnex.org` + `*.tunnex.org`
via the `dns-cloudflare` plugin, installs [deploy/nginx/tunnex.conf](deploy/nginx/tunnex.conf) and
[deploy/server-config.yaml](deploy/server-config.yaml), and opens ports 22, 80, 443 and 45000–46000 (TCP/UDP).

```bash
CF_API_TOKEN=<token with Zone:DNS:Edit> ./deploy/bootstrap-server.sh root@<host>
```

Certificates renew automatically via `certbot.timer`; Nginx is reloaded after renewal.

### Docker

The server container is started by CI, equivalent to:

```bash
docker run -d \
  --name tunnex-server \
  --restart unless-stopped \
  --network host \
  -v /opt/tunnex/config.yaml:/app/config.yaml:ro \
  -v /opt/tunnex/certs:/app/certs:ro \
  -v /opt/tunnex/data:/app/data \
  -e SERVER_CONFIG=/app/config.yaml \
  registry.gitlab.com/homycoder/tunnex/server:latest
```

### Server requirements

- Public IP address
- Domain with a wildcard DNS record (`*.example.com`)
- Reverse proxy (Nginx) for TLS termination
- Open ports: 80, 443, TCP/UDP range 45000–46000

## CI/CD (GitLab)

### Pipelines

**On changes to `server_/` or `shared/`:**
1. Build the Docker image and push it to the GitLab Container Registry
2. Deploy to the server (pull + restart the container)

**On changes to `client/`, `shared/`, `setup.py`:**
1. Bump the version (`YY.MM.DD.patch`)
2. Build wheel + sdist
3. Publish to PyPI

**On changes to `landing/`:** rsync to `/opt/tunnex/landing` on the server.

### CI/CD variables

**Server deployment** (GitLab → Settings → CI/CD → Variables):
- `SSH_PRIVATE_KEY` — SSH private key for deployment
- `SSH_USER` — server user
- `SERVER_HOST` — server address

**PyPI publishing:**
- `PYPI_USERNAME` — `__token__`
- `PYPI_PASSWORD` — PyPI API token

## Development

```bash
make install-dev       # dev tools (ruff, black, mypy)
make lint              # lint (ruff)
make fmt               # format (black)
make mypy              # type check
make test              # tests
make build-client      # build the client package
```

### Project layout

```
├── client/            # tunnel client
│   ├── client.py      # CLI (Click)
│   ├── tunnel_client.py  # tunnel logic
│   ├── dashboard.py   # FastAPI dashboard
│   ├── models.py      # SQLAlchemy models
│   └── static/        # dashboard frontend
├── server_/           # tunnel server
│   ├── main.py        # FastAPI + WS + HTTP proxy
│   ├── tunnel_manager.py  # tunnel registry
│   ├── tcp_udp_manager.py # TCP/UDP manager
│   ├── models.py      # SQLAlchemy models
│   └── Dockerfile
├── shared/            # shared code
│   └── protocol.py    # protocol message types
├── landing/           # tunnex.org landing page
├── deploy/            # server bootstrap, Nginx and server config
├── learn/             # step-by-step walkthroughs of the protocol
└── setup.py           # tunnex package
```

## Contact

dev@tunnex.org

## License

MIT
