one-click-tunnel
Enables management of Cloudflare Quick Tunnels and named tunnels, including creating temporary public URLs with password protection, assigning custom domains via Cloudflare DNS, and checking Cloudflare login/status.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@one-click-tunnelCreate a temporary tunnel to my local port 3000 with password secret123 for one hour"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
one-click-tunnel (OCT)
OCT = One Click Tunnel. Publish a port from this machine or from a LAN device to a temporary public URL, behind a password gate, with a native window + tray, a CLI and an MCP server.
One-click temporary public tunnels for Windows, built on Cloudflare Quick Tunnels — with a password gate in front of your service, a local config page, a system tray icon, a CLI and an MCP server (so an AI agent can manage tunnels too). No admin rights, no dependencies, single-file executable.
Install
From the latest release:
Download | What it is |
one-click-tunnel-setup-.exe | One-click installer (per-user, no admin). Adds Start Menu / Desktop shortcuts and an uninstall entry. |
one-click-tunnel--win-x64.zip | Portable bundle: unpack and double-click install.cmd |
Verify your download against SHA256SUMS.txt in the release assets.
Related MCP server: powershell-mcp
What it does
Publishes a local port to a temporary https://xxxx.trycloudflare.com URL that requires a password
The password gate binds to 127.0.0.1 only, so public traffic must come through the tunnel
Everything auto-closes when the TTL expires (per tunnel)
Run many tunnels at once, each with its own local port, gateway port, password and expiry time
Manage them from the config page, the tray icon, the CLI, or let an AI agent do it over MCP
Four ways to start / manage tunnels
# 1. graphical config page + tray (recommended)
oct.exe gui --open
# 2. tray only (background daemon is started automatically)
oct.exe tray
# 3. command line, many tunnels
oct.exe add --name web --port 3000 --ttl 1h --auto-start
oct.exe start web
oct.exe list
oct.exe stop --all
# 4. MCP: let an AI host drive it
oct.exe mcpThe classic single-tunnel flags still work exactly as before:
oct.exe --port 3000 --ttl 1h --password mypasswordPermanent address with your own domain (named tunnels)
Quick tunnels give you a random *.trycloudflare.com address that changes on every run. If you own a domain
managed by Cloudflare, you can give a tunnel a permanent address instead:
oct.exe login # one browser authorization (pick the domain/zone)
oct.exe domain web app.example.com # creates the named tunnel + the DNS record
oct.exe start web # now serving https://app.example.com, permanentlytunnel loginstores the origin certificate instate/cloudflare/cert.pem(once per account).tunnel domain <id> <hostname>creates the tunnel, copies its credentials intostate/cloudflare/and points the hostname at it. It also writesmode: namedandttl: foreverinto the config, so the address never expires.The tunnel still terminates at the password gate on 127.0.0.1, so a permanent hostname is not an open door.
tunnel info [id]shows mode, hostname, tunnel id and anything still missing.--dry-runprints the generated cloudflared config and command without starting anything.Config page: a Cloudflare login button, plus a per-tunnel quick/named switch and domain binding.
AI hosts get the same through the MCP tools
cloudflare_login,set_custom_domainandcloudflare_status.Requirements: the domain's DNS must be managed by Cloudflare (the free plan is enough).
Generated state/cloudflare/<id>.cloudflared.yml:
tunnel: <tunnel-id>
credentials-file: '<...>/<id>.credentials.json'
no-autoupdate: true
ingress:
- hostname: app.example.com
service: http://127.0.0.1:18080 # <- the local password gate, not your app port
- service: http_status:404Where is the tray icon?
The tray is not part of the web page - a browser page cannot own a notification-area icon. The background daemon starts a tiny Windows process (PowerShell WinForms, no extra dependency) that shows the icon:
Windows 11 hides new tray icons by default: click the ^ (show hidden icons) next to the clock, or open Settings -> Personalization -> Taskbar -> Other system tray icons and switch the "one-click-tunnel / 一键隧道" entry on to keep it visible.
The icon only lives while the daemon runs. In 1.2.x the daemon lived in the console window, so closing that window killed the tray as well. From 1.3.0 the launcher is windowless and the daemon is detached, so closing the app window keeps the tunnels and the tray alive.
Start it manually any time: oct.exe tray (reuses the running daemon; exits if one is already there)
Turn it off: config page -> Global settings -> Tray, or tray.enabled=false in config.json.
If the daemon dies, the tray closes itself within ~10 seconds (watchdog), so no ghost icons are left behind.
Desktop app (no console window)
The shortcut starts the background daemon hidden and opens the config page inside a chromeless desktop window (Edge/Chrome app mode: no address bar, no tabs, its own taskbar entry and icon). Closing that window does not stop the tunnels - the tray icon stays, and the tray menu (or the "close everything" shortcut, which runs stop-all.vbs) quits everything. No cmd window appears: the shortcut runs a .vbs launcher and the daemon is spawned detached and hidden.
oct.exe app # open the desktop window (starts the daemon when needed)
oct.exe gui --browser # use a normal browser tab instead of the app windowConfig file (config.json, next to the exe)
{
"version": 1,
"gui": { "port": 18400, "token": "<auto generated>", "openBrowser": true },
"mcp": { "enabled": true, "allowStart": true, "allowStop": true },
"tray": { "enabled": true },
"defaults": { "ttl": "1h", "passwordMode": "random", "gatewayStart": 18080, "rateLimit": 3000 },
"update": { "enabled": true, "repo": "Thunderunruly/one-click-tunnel",
"apiBase": "https://api.github.com", "checkIntervalHours": 6,
"autoDownload": false, "includePrerelease": false,
"downloadMirror": "", "ignoredVersion": "" },
"profiles": [
{ "id": "web", "name": "web 3000", "enabled": true, "autoStart": false,
"port": 3000, "gateway": 18080, "ttl": "30m",
"passwordMode": "random", "password": "", "host": "127.0.0.1" }
]
}One profile = one local port + one gateway port + one password + one TTL. Each profile runs as its own child process, so session keys, rate limits, lockouts and timers are isolated per tunnel.
MCP (AI integration)
{
"mcpServers": {
"one-click-tunnel": {
"command": "C:\\Users\\<you>\\AppData\\Local\\one-click-tunnel\\oct.exe",
"args": ["mcp"]
}
}
}Tools exposed: list_tunnels, create_tunnel, start_tunnel, stop_tunnel, update_tunnel, delete_tunnel, set_password, tunnel_status, stop_all. Tunnels created by the AI show up in the config page and can be stopped by hand at any time. Use mcp.allowStart / mcp.allowStop to limit what the AI may do.
Updates
The program checks the GitHub release channel for a newer version (default: every 6 hours, configurable) and shows a banner on the config page when one exists. You can also check on demand:
oct.exe update # check now
oct.exe update --notes # print the release notes
oct.exe update --download # download the installer into updates\ (SHA-256 verified)
oct.exe update --install # silently run the downloaded setup.exe
oct.exe version # print the current versionDownloads are verified against the release SHA256SUMS.txt; a mismatch deletes the file and fails loudly.
Auto-download (never auto-install) can be enabled with update.autoDownload.
Behind a blocked network, point update.apiBase at a GitHub API mirror and/or set update.downloadMirror to something like https://your-mirror.example/{url} -- the {url} placeholder is replaced with the real asset URL.
Ignore a version from the banner (it is stored as update.ignoredVersion).
AI hosts get the same capability through the MCP tool check_update.
Security
Password gate on 127.0.0.1 only; constant-time password comparison; per-IP lockout (8 failures / 5 min) and rate limit
Session cookie: HMAC signature, random per-session id, HttpOnly, SameSite=Lax, Secure over https, server-side revocation on logout
Requests are sanitised before proxying: hop-by-hop headers stripped, Host rewritten unless allow-listed, X-Forwarded-For / X-Real-IP overwritten with the real client IP, gateway credentials never forwarded upstream
TRACE/TRACK, CONNECT and absolute-form request lines rejected; unauthenticated requests never reach your service
Config page: loopback only, token required, same-origin enforced on writes, strict CSP, no external resources
TTL auto-shutdown, process-tree cleanup, orphan cloudflared reaping, logs never contain the password
Verified by re-runnable suites (offline unless noted):
npm test # 72 + 25 + 29 + 27 checks
npm run test:exe # packaged exe: CLI / GUI / self-spawn
npm run test:installer # installer end-to-end
npm run test:live # real Cloudflare tunnel from the public side (needs internet)Build from source
git clone https://github.com/Thunderunruly/one-click-tunnel.git
cd one-click-tunnel
node build/make-exe.mjs # dist/oct.exe (single file, Node SEA)
node build/make-zip.mjs # release/one-click-tunnel-<version>-win-x64.zip
node build/make-setup.mjs # release/one-click-tunnel-setup-<version>.exe (needs Inno Setup 6)Releases are produced automatically by GitHub Actions: push a tag like v1.1.0 and the Release workflow runs the test suites, builds the exe, the portable zip and the Inno Setup installer, then publishes them to the GitHub release.
MIT licensed. cloudflared is a separate binary by Cloudflare (Apache-2.0).
Why not Go? (measured, not guessed)
The password gate is I/O bound, not CPU bound. On a local loopback benchmark (build/bench.mjs, 4000 requests,
40 concurrent, keep-alive) it adds +0.62 ms p50 and keeps ~86% of the raw throughput (14.0k vs 16.3k req/s),
while the real request path - Internet -> Cloudflare edge -> cloudflared -> gate -> your app - is dominated by the
network and by cloudflared, which is itself written in Go.
metric | measured |
gate overhead | +0.62 ms p50, +1.25 ms p95 |
throughput | 14,035 req/s gated vs 16,260 req/s direct |
per-tunnel memory | 72 MB gate process + 46 MB cloudflared |
cold start | 55 ms |
artifacts | exe 83.4 MB, cloudflared 52.4 MB, portable zip 49.2 MB |
A Go rewrite would mostly buy a smaller binary (~10 MB) and less memory per tunnel - packaging wins, not throughput -
so the Node implementation stays. Re-measure any time with node build/bench.mjs (or npm run bench); if a real
workload ever reaches these numbers, revisit the decision with data.
Launcher: no console window, no VBScript
The desktop/Start-Menu shortcut points at one-click-tunnel.exe - a ~7 KB native launcher compiled from
build/launcher/Launcher.cs with the csc.exe that ships with Windows. It is a GUI-subsystem binary
(PE Subsystem=2), so it starts oct.exe app hidden and exits immediately: no console window flashes.
It also removed the old VBScript launcher, which matters because Microsoft is deprecating VBScript
(optional feature since Windows 11 24H2). Rebuild it with node build/make-launcher.mjs.
Expose a LAN device, not just localhost
By default the tunnel points at 127.0.0.1:<port>. To publish another machine on your network - a NAS, a camera, a
printer web UI, a server on the LAN - set the target explicitly:
oct.exe --target 192.168.1.50:8080 --ttl 1h
oct.exe add --name nas --port 5000 --target 192.168.1.50:5000Allowed by default: loopback, 10/8, 172.16/12, 192.168/16, fc00::/7 and hostnames that resolve to them.
Refused by default: public IPs, 0.0.0.0/::, multicast and link-local (169.254.x.x, incl. 169.254.169.254), so the tunnel can never be turned into an open proxy. Override with
--allow-public-targetif you really mean it.The password gate applies exactly the same way, and the
Hostheader seen by the device is its own address (override with--upstream-host).HTTPS upstreams:
--target-secure(+--target-insecure-tlsfor self-signed certificates).
This server cannot be deployed
Maintenance
Related MCP Connectors
Secure tunneling, reverse proxy and remote access for local applications.
Get share links, publish and manage websites, artifacts and agents. No account needed.
Securely control computers you explicitly pair through files, terminals, processes, screenshots, desktop UI/input, clipboard, browser automation, diagnostics, and document tools.
Operate Linux, macOS and Windows from your LLM. Every action runs through an auditable allowlist.
Related MCP Servers
- FlicenseCqualityDmaintenanceEnables ChatGPT to control a Windows PC remotely via OpenAI Secure MCP Tunnel, executing file operations, PowerShell commands, and system actions through a local MCP server.15-
- AlicenseAqualityAmaintenanceEnables AI agents to run PowerShell commands and manage Windows services without intrusive console windows, while also supporting remote execution via SSH and WinRM.10123 npmMIT
- FlicenseNot gradedqualityCmaintenanceEnables AI assistants to securely access and control a Windows machine remotely, running commands and managing files via an MCP server protected by Bearer-token authentication and exposed through a Cloudflare Tunnel.-
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to perform local development tasks on Windows by reading and editing files, running commands, and controlling browser and desktop tools, all within isolated workspaces. Supports secure remote access via an optional tunnel for ChatGPT clients.MIT