{"id":31794448,"url":"https://github.com/0xrelogic/tunly","last_synced_at":"2026-04-13T18:00:47.983Z","repository":{"id":304293600,"uuid":"1018370373","full_name":"0xReLogic/Tunly","owner":"0xReLogic","description":"Tunly is a lightweight, open-source HTTP tunnel solution for developers who want full control and instant setup. No quotas. No dashboards. No tracking. Just you, your app, and a tunnel pure and simple.","archived":false,"fork":false,"pushed_at":"2026-04-09T17:41:01.000Z","size":1391,"stargazers_count":9,"open_issues_count":12,"forks_count":1,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-04-09T19:23:16.996Z","etag":null,"topics":["developer-tools","localhost-tunnel","port-forwarding","reverse-proxy","rust","self-hosted","server","tunnel-client","tunnel-server","websocket"],"latest_commit_sha":null,"homepage":"","language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/0xReLogic.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-07-12T05:49:02.000Z","updated_at":"2026-02-10T13:19:45.000Z","dependencies_parsed_at":"2025-07-12T07:21:07.126Z","dependency_job_id":"dfcb1ffd-4e59-4f09-9473-35d4f6d41892","html_url":"https://github.com/0xReLogic/Tunly","commit_stats":null,"previous_names":["0xrelogic/relogic_tunnel"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/0xReLogic/Tunly","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xReLogic%2FTunly","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xReLogic%2FTunly/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xReLogic%2FTunly/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xReLogic%2FTunly/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/0xReLogic","download_url":"https://codeload.github.com/0xReLogic/Tunly/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/0xReLogic%2FTunly/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31764317,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-13T15:25:13.801Z","status":"ssl_error","status_checked_at":"2026-04-13T15:25:09.162Z","response_time":93,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["developer-tools","localhost-tunnel","port-forwarding","reverse-proxy","rust","self-hosted","server","tunnel-client","tunnel-server","websocket"],"created_at":"2025-10-10T19:45:47.059Z","updated_at":"2026-04-13T18:00:47.977Z","avatar_url":"https://github.com/0xReLogic.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Tunly\n\n[![Rust](https://img.shields.io/badge/Rust-000000?style=for-the-badge\u0026logo=rust\u0026logoColor=white)](https://www.rust-lang.org/)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg?style=for-the-badge)](https://opensource.org/licenses/MIT)\n[![Platform](https://img.shields.io/badge/Platform-Windows%20%7C%20Linux%20%7C%20macOS-blue?style=for-the-badge)](https://github.com/0xReLogic/Tunly/releases)\n[![Download Client](https://img.shields.io/badge/Download-Client-2ea44f?style=for-the-badge\u0026logo=github)](https://github.com/0xReLogic/Tunly/releases)\n\n[![CodeQL](https://github.com/0xReLogic/Tunly/actions/workflows/codeql.yml/badge.svg)](https://github.com/0xReLogic/Tunly/actions/workflows/codeql.yml)\n[![Rust CI](https://github.com/0xReLogic/Tunly/actions/workflows/rust-ci.yml/badge.svg)](https://github.com/0xReLogic/Tunly/actions/workflows/rust-ci.yml)\n[![Frontend CI](https://github.com/0xReLogic/Tunly/actions/workflows/frontend-ci.yml/badge.svg)](https://github.com/0xReLogic/Tunly/actions/workflows/frontend-ci.yml)\n[![Release](https://github.com/0xReLogic/Tunly/actions/workflows/release.yml/badge.svg)](https://github.com/0xReLogic/Tunly/actions/workflows/release.yml)\n\n**Tunly** is a simple, lightweight, and open-source HTTP tunnel solution for exposing local applications to the internet.\n\n---\n\n## Motivation\n\n**Tunly** is built for developers, makers, and anyone who wants:\n\n- **Access local applications from anywhere** without hassle\n- **No login, no dashboard, no limits**\n- **100% open source** and self-hostable\n- **Easy distribution**: simple setup with token-based authentication\n\n---\n\n## Key Features\n\n- **Zero Configuration**: No login, no dashboard, and no complex registration.\n- **Advanced Security**: Secure **JWT-based authentication** with IP binding and single-use protection.\n- **High Performance**: Native **HTTP/2 support** with multiplexing and transparent **Zlib compression**.\n- **Full Observability**: Prometheus metrics (`/metrics`), structured JSON logging, and a built-in session activity viewer (`/_log`).\n- **Production Built**: Lightweight binary with persistent connection pooling and enforced security limits.\n- **Self-Hostable**: Easily deploy on any VPS or Cloud (DigitalOcean, Vultr, Koyeb, etc.).\n- **Privacy First**: 100% open-source with zero tracking or telemetry.\n\n---\n\n## When to Use Tunly?\n\n### **Demo \u0026 Presentations**\nClient wants direct access to your app? But your project is still on localhost?  \n**Solution**: Tunly makes your localhost accessible from anywhere in 30 seconds.\n\n### **Client Testing**\nClient needs to test new features but you haven't deployed to production yet?  \n**Solution**: Share tunnel URL, client can test immediately without complex setup.\n\n### **Development \u0026 Debugging**\nRemote work but need access to apps on your home computer?  \n**Solution**: Tunnel from home to office, access applications from anywhere.\n\n### **Mobile Testing**\nNeed to test web apps on phone but they only run on laptop?  \n**Solution**: Tunnel laptop, access from phone via WiFi or data.\n\n### **Quick Prototyping**\nHave a new idea, want to share with friends but haven't deployed yet?  \n**Solution**: Tunnel localhost, share URL, friends can try immediately.\n\n### **Private Testing**\nWant to test apps on the internet without complex setup?  \n**Solution**: Tunly provides simple, self-hosted tunneling without hassle.\n\n---\n\n## How to Use\n\n### Modes\n- **Self-host**: Run your own server on a VPS or cloud platform and point the client to it.\n- **Local testing**: Run both server and client locally for development.\n\n### Self-Hosted Setup\n\n1. **Download** `tunly-client` and `tunly-server` for your OS from [Releases](https://github.com/0xReLogic/Tunly/releases)\n2. **Start the server** on your VPS or cloud:\n   ```bash\n   tunly-server --port 8080\n   ```\n3. **Run the client** locally:\n   ```bash\n   tunly-client --remote-host your-server.com:8080\n   ```\n4. When prompted, get a token from `http://your-server.com:8080/token` and paste it\n5. Enter your local address when prompted (default: `127.0.0.1:80`)\n6. The client will print your **Public URL**, e.g., `https://your-server.com/s/\u003csession\u003e/` — share this URL\n7. View the session log: `https://your-server.com/s/\u003csession\u003e/_log`\n\n\u003e Notes:\n\u003e - Long flags use kebab-case (e.g., `--remote-host`, `--token-url`, `--allow-token-query`).\n\u003e - Default auth uses header `Authorization: Bearer \u003ctoken\u003e`. Query `?token=...` works only if server enables `--allow-token-query`.\n\u003e - For self-host without TLS, pass `--use-wss=false` so the client uses `ws://` (the flag accepts an explicit boolean, e.g., `--use-wss=false`).\n\n### Quick start (from source, via Cargo)\n\nIf building from source:\n\n- **Self-host** — run your own server, then point client to it.\n\n  1) Start server (ephemeral token mode):\n  ```\n  cargo run --bin tunly-server -- --bind 0.0.0.0:9000\n  ```\n\n  2a) Start client (interactive, custom server):\n  ```\n  cargo run --bin tunly-client -- --remote-host \u003cserver-ip-or-host\u003e:9000 --use-wss=false --local 127.0.0.1:8080\n  ```\n\n  2b) Start client (auto-fetch token):\n  ```\n  cargo run --bin tunly-client -- --remote-host \u003cserver-ip-or-host\u003e:9000 \\\n    --use-wss=false \\\n    --local 127.0.0.1:8080 \\\n    --token-url http://\u003cserver-ip-or-host\u003e:9000/token\n  ```\n\n  3) Open the Public URL printed by the client, e.g.:\n  ```\n  http://\u003cserver-ip-or-host\u003e:9000/s/\u003csession\u003e/\n  ```\n\n  4) Check recent paths accessed by visitors for that session:\n  ```\n  http://\u003cserver-ip-or-host\u003e:9000/s/\u003csession\u003e/_log\n  ```\n\n#### Local offline test (no TLS)\n\nFor a quick local test without internet:\n\n1) Start server with a fixed token:\n```\ncargo run --bin tunly-server -- --bind 127.0.0.1:9000 --token devtoken\n```\n\n2) Start client (interactive) and connect over ws:\n```\ncargo run --bin tunly-client -- --remote-host 127.0.0.1:9000 --use-wss=false\n```\nWhen prompted, enter `devtoken`, then your local app address (e.g., `127.0.0.1:8080`).\n\n### Loginless / Ephemeral Token Mode (no dashboard, no signup)\n\nIf you don't want to manage a static token, run the server without `--token` and without env `TUNLY_TOKEN`. The server will issue one-time tokens bound to the requester's IP via `/token`.\n\n- **Start server (ephemeral mode)**\n  ```\n  tunly-server.exe --port 9000\n  ```\n- **Client auto-fetch token (advanced)**\n  ```\n  tunly-client.exe --remote-host \u003cvps-address\u003e:9000 --token-url http://\u003cvps-address\u003e:9000/token\n  ```\n  The client fetches a token from `/token` (JSON or plain text) and connects via WebSocket using that token.\n\nNotes:\n- Tokens are one-time use, may be bound to the requester IP, and expire in ~5 minutes.\n- Default auth is via header `Authorization: Bearer \u003ctoken\u003e`; `?token=` query is disabled unless `--allow-token-query` is set on the server.\n- If you prefer a fixed token, set `--token \u003cvalue\u003e` or env `TUNLY_TOKEN` on the server and keep using `config.txt` or env on the client.\n\n### Fixed vs Ephemeral Tokens\n\n- **Fixed Token**\n  - Server: run with `--token \u003cvalue\u003e` or env `TUNLY_TOKEN`.\n  - Client: paste token when prompted, or set it via `config.txt`/`TUNLY_TOKEN`.\n  - Best for interactive UX testing and simple setups.\n\n- **Ephemeral Token**\n  - Server: run without `--token` (issues one-time tokens via `/token`, tied to `session`+IP, TTL ~5 minutes).\n  - Client: use `--token-url http://\u003cserver\u003e:\u003cport\u003e/token` so the token matches the current `sid` automatically.\n  - Manual prompt is not compatible with Ephemeral mode (will be rejected as invalid).\n\n### Server Hosting Options\n- **Cheap VPS**: DigitalOcean, Vultr, Linode ($5/month)\n- **Free cloud**: Oracle Cloud Free Tier, Google Cloud Free Tier\n- **Platform-as-a-Service**: Render, Railway, Koyeb (easy deployment)\n\n---\n\n## Environment \u0026 Deploy\n\nYou can configure Tunly using environment variables. See the `.env.example` files in the root, `backend/`, and `frontend/` directories for templates.\n\n- **Server env**:\n  - `PORT` (from platform, e.g., Render, Koyeb) — server listens on this port automatically.\n  - `TUNLY_TOKEN` — optional; if set, server uses fixed-token mode. If not set and `--token` is not provided, server uses ephemeral mode with `/token` issuance.\n  - `TUNLY_INTERNAL_KEY` — optional; if set, restricts `/token` access to requests providing this key in the `X-Internal-Key` header (prevents direct `curl` requests to your backend).\n- **Client config**:\n  - `config.txt` with `token: \u003cvalue\u003e` (tolerant to `token=`/`token:`/`tokenn`).\n  - Or env `TUNLY_TOKEN`.\n  - Or runtime fetch via `--token-url http://\u003cserver\u003e:\u003cport\u003e/token` (ephemeral mode).\n- **Frontend env**:\n  - `BACKEND_BASE_URL` — base URL of your Tunly backend (e.g., `https://\u003cyour-app\u003e.koyeb.app` or your custom domain). Used by the Next.js proxy route `app/api/token/route.ts` to call `/token`.\n  - `TUNLY_INTERNAL_KEY` — must match the server's key to allow the frontend to fetch tokens securely via the Next.js API route.\n- **Deploy on Koyeb**:\n  - Source: Docker → Dockerfile path: `backend/Dockerfile`\n  - Health check: `GET /healthz`\n  - Environment:\n    - `TUNLY_TOKEN` (optional): set for Fixed mode; leave empty for Ephemeral mode (`/token` enabled)\n    - `PORT`: injected automatically by Koyeb (no need to set)\n  - Optional: add a custom domain; Koyeb will provision TLS automatically\n\n---\n\n## Security \u0026 Limits\n\n- `/token` rate limit: 10 requests per 60 seconds per IP\n- Ephemeral token TTL: ~5 minutes; single use; bound to requester's IP and session id\n- Proxy request body limit: 2 MB\n- Session idle TTL: ~10 minutes (inactive sessions are garbage-collected)\n\n---\n\n## Logs \u0026 Observability\n\n- **Server logs** each proxied request:\n  ```\n  PROXY GET / -\u003e 200 in 16ms (sid=abc123)\n  ```\n- **Client logs** each local request it performs:\n  ```\n  LOCAL GET /api -\u003e 200 in 8ms\n  ```\n- **Session log page** lists the last ~50 requests for a session:\n  - URL: `http://\u003cserver\u003e/s/\u003csession\u003e/_log`\n  - Shows: Method, URI, Status, Duration (ms)\n  - Includes quick links to `/, /api, /blog` for quick checks\n\n## API Endpoints\n\n- `GET /healthz` — health check\n- `GET /token` — issue ephemeral token (available only in Ephemeral mode)\n- `GET /ws?sid=\u003csession\u003e` — WebSocket entrypoint (use `Authorization: Bearer \u003ctoken\u003e` header)\n- `GET /s/:sid/_log` — recent paths accessed for the session\n- `ANY /s/:sid/\u003c...\u003e` — proxied traffic routed to the connected client\n\n## Troubleshooting\n\n- **“Token is invalid or has expired.”**\n  - Cause: Server is in Ephemeral mode but client used manual token prompt (token doesn’t match `sid`).\n  - Fix: Use `--token-url http://\u003cserver\u003e:\u003cport\u003e/token`, or run server with a fixed token (`--token \u003cvalue\u003e`) and then use manual prompt.\n\n- **Cannot connect over wss on localhost/self-host**\n  - Cause: No TLS certificate for your self-hosted server.\n  - Fix: Add `--use-wss=false` to use plain `ws://` during local testing.\n\n## Security\n\n- Token is the \"password\" for your tunnel.\n- Don't share tokens with untrusted people.\n- Change tokens regularly for extra security.\n\n---\n\n## License\n\nMIT License — free to use for commercial and non-commercial purposes.\n \n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0xrelogic%2Ftunly","html_url":"https://awesome.ecosyste.ms/projects/github.com%2F0xrelogic%2Ftunly","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2F0xrelogic%2Ftunly/lists"}