Repository navigation
Security
- cli: WebSocket connections skipped every tunnel access check. A tunnel
shared with--password,--auth-tokenor--allow-ipaccepted WebSockets
from anyone, because the relay bridges them outside the HTTP middleware.
They now pass the same checks before the dev server is dialled. - relay: the client IP was read from
Fly-Client-IP/CF-Connecting-IP
first, headers any visitor can send, so--allow-ipand the per-IP rate
limits (including magic-link e-mails) could be bypassed. Forwarding headers
are now trusted only from the relay's own reverse proxy, and the WebSocket
path stampsX-Forwarded-Forthe way the HTTP path does. - relay: dependencies bumped past vulnerabilities the relay calls: pgx
v5.9.2 (GO-2026-5004), golang-jwt v5.2.2 (GO-2025-3553), gorilla/websocket
v1.5.3 (GO-2026-6278), x/text v0.39.0 (GO-2026-5970). - build: binaries are built with Go 1.27 (were 1.22, which carried 30 known
standard-library vulnerabilities).go installneeds Go 1.26+.
Added
- cli:
--demo-allow/--demo-blockrules ("[METHOD ]/path[*]",WSfor
sockets) to let a read-only POST through or block a GET that writes; also
demoAllow/demoBlockin.tunr.json.
Changed
- cli:
--demonow also drops messages the visitor sends over WebSockets
(server pushes still arrive; dev-server HMR sockets are exempt), lets GraphQL
queries sent as POST through (mutations stay blocked), and echoes a JSON
request body back instead of a generic payload. Blocked GraphQL mutations get
200 {"data": null}. - cli:
"demo": trueon a tcp/udp/tls tunnel is now an error instead of
being silently ignored.
Fixed
- relay: a search crawler (Googlebot, Bingbot, YandexBot) visiting a real
page of a sleeping app got the edge's synthetic200 okmeant for health
checks, so the page could be indexed as "ok". Crawlers on real pages now wake
the app and get the real page, without resetting the idle clock. SEO tools and
scanners on a sleeping app's real pages get503+Retry-Afterinstead of
the synthetic 200; health checks and uptime monitors are unchanged. - relay: after a relay restart (idle clocks live in memory), an app reached
only by monitors or crawlers never slept again: the sweeper skipped any app
with no recorded request. It now starts the clock the first time it sees such
an app, so a fresh deploy or a restarted relay gets one full idle window and
then the normal HOT → WARM → STOPPED ladder. - cli:
--jsonoutput was interleaved withINFOlines on stdout, so
tunr share/tcp/udp/tls --json | jqfailed. Progress lines now go to stderr
whenever--jsonis set. - docs: removed claims for features that aren't wired up — OAuth2/SAML SSO,
SOC2 audit logging and SSRF/private-IP validation exist only as unused code —
and corrected the feedback widget description (it sends a text note; there
are no visual pins, and the script is inline, not loaded from a CDN).
go installnow needs Go 1.26+.
Changelog
- be06aa4: build: Go 1.27 toolchain, patched deps, blocking govulncheck (@ahmetvural79)
- 81f9c7f: build: move to golangci-lint v2.14.0, lint on Go 1.27 (@ahmetvural79)
- e2d65b6: chore(release): v0.7.0 (@ahmetvural79)
- cce93fc: fix(cli): keep --json stdout machine-readable; drop claims for unwired features (@ahmetvural79)
- f63eea9: fix(relay): a crawler waking an unseen app must start its idle clock (@ahmetvural79)
- c89d60b: fix(relay): serve crawlers the real page instead of the health-check "ok" (@ahmetvural79)
- 98d97b0: fix(relay): start the idle clock for apps the sweeper has never seen served (@ahmetvural79)
- 9ebe426: fix(relay): stop trusting client-sent IP headers (@ahmetvural79)
- 5b215f4: fix: enforce tunnel auth on WebSockets; close --demo gaps (@ahmetvural79)
- 880f54d: refactor(proxy): move LocalProxy from Director to Rewrite (@ahmetvural79)
How to Install?
# macOS / Linux (Homebrew)
brew install tunr-dev/tap/tunr
# macOS / Linux (install script)
curl -fsSL https://raw.githubusercontent.com/tunr-dev/tunr/main/install.sh | sh
# Go
go install github.com/tunr-dev/tunr/cmd/tunr@latestSDKs: pip install tunr · npm install @tunr/cli
For more: https://tunr.sh/docs