Skip to content

Sharing a preview

Put a branch's environment on the internet so a phone, a designer or a webhook can reach it.

Read this first

A tunnel makes your development environment reachable by anyone with the URL. Minato cannot apply a Cloudflare Access policy — that needs Cloudflare's API, and everything here goes through the cloudflared CLI — so it cannot promise anything is guarding it.

Put an Access policy in front of the hostname yourself. Minato will not expose anything without --public, and it repeats the warning every time.

You need a Cloudflare account with a domain on it.

Install and log in

console
$ brew install cloudflared
$ cloudflared tunnel login

That opens a browser. Minato does not run it for you: an interactive prompt in a daemon hangs an agent at a step it cannot answer, the same reason minato setup prints its sudo commands rather than running them.

Turn it on

console
$ minato tunnel enable --domain example.com --public
  ✓ starting the tunnel
╭ tunnel ─────────────────────────────────────────────────────────────────╮
│ running  *.example.com                                                  │
│                                                                         │
│ DNS  *.myapp.example.com                                                │
│                                                                         │
│ this environment is reachable from the internet.                        │
│ Minato cannot see whether a Cloudflare Access policy is in front of it. │
╰─────────────────────────────────────────────────────────────────────────╯

Behind that: a named tunnel created, a wildcard DNS record routed for the project, cloudflared started. All idempotent, so running it again is fine.

console
$ minato status -w feature-checkout
╭ myapp / feature-checkout ──────────────────────────────────╮
│ feature/checkout  /path/to/myapp.wt/feature-checkout       │
│                                                            │
│ ● web  ready  https://web.feature-checkout.myapp.localhost │
│                                                            │
│ shared over the tunnel:                                    │
│ web  https://web-feature-checkout.myapp.example.com        │
╰────────────────────────────────────────────────────────────╯

Send the second one. Service and workspace are joined with - because tunnel hostnames only reliably support one level of subdomain.

console
$ minato status -w feature-checkout --json \
  | python3 -c 'import json,sys; print(json.load(sys.stdin)["workspace"]["services"][0]["tunnel_url"])'

What your reviewer gets

  • A stopped environment still works. Their first request wakes it, taking a second or two, exactly as a local request does. The tunnel hostname is an ordinary route in the same table.
  • Only exposed services are reachable. Anything with expose = false — the database — has no tunnel hostname and cannot be reached even by guessing.
  • A real certificate. TLS terminates at Cloudflare's edge, so there is no warning and nothing to trust. Your local CA is not involved.

Add Access

Minato cannot do this part. In the Cloudflare dashboard, under Zero Trust → Access → Applications, add a self-hosted application for *.myapp.example.com and a policy — an email domain, or a one-time PIN for someone outside your organisation.

Do it before sharing anything you would not put on a public web server.

Turn it off

console
$ minato tunnel disable
╭ tunnel ─────────────────╮
│ disabled  *.example.com │
╰─────────────────────────╯

The tunnel hostnames stop routing immediately; local URLs are untouched. The named tunnel and DNS records stay in Cloudflare, so re-enabling needs no login:

console
$ minato tunnel enable --public

Across a restart

The daemon brings a tunnel that was on back up when it restarts, and rebuilds the routing table with it. A link you sent someone keeps working after you reboot.

When it does not work

console
$ minato tunnel status
$ minato doctor | grep -i tunnel
$ tail -f ~/.minato/logs/minatod.log   # cloudflared logs here too
SymptomLikely cause
needs logincloudflared tunnel login has not been run
not installedbrew install cloudflared
stopped while enabledcloudflared exited — check the daemon log
Cloudflare 1016The DNS record is missing. Re-run tunnel enable
A wildcard record is refusedYour Cloudflare plan may not allow one

Verified against a stub

The routing, the generated configuration and the CLI arguments are all tested, including scale-to-zero through a tunnel hostname. What has not been exercised is a real named tunnel against a real zone.

Next

Released under the MIT License.