Smoke test required

...

← All documentation

Documentation

Cloudflare Setup

**Status:** active · last reviewed 2026-05-03

source: docs/getting-started/CLOUDFLARE_SETUP.md

What this does: exposes your local control-panel-server (running at http://localhost:8080 on your Mac) to the public internet via a Cloudflare Tunnel, so the PWA at https://kiwimaddog2020.github.io/ensemble-dashboard/ can reach it from any device, anywhere.

Cost: $0 if you use a Quick Tunnel (ephemeral URL). $0 + ~$10/yr for a domain if you upgrade to a Named Tunnel (permanent URL + Cloudflare Access auth gate).


TL;DR

# Install (once)
brew install cloudflared

# Path A — Quick Tunnel (no domain, ephemeral URL)
cloudflared tunnel --url http://localhost:8080
# → grab the https://xxx-yyy-zzz.trycloudflare.com URL it prints
# → paste into /pair.html along with your DASHBOARD_TOKEN
# → URL changes every time cloudflared restarts (Mac reboot, etc.)

# Path B — Named Tunnel (permanent URL, custom domain, optional auth gate)
# Requires: a Cloudflare account + a registered domain on Cloudflare DNS
# See "Path B" section below.

Prerequisites

  • macOS with Homebrew installed
  • Free Cloudflare account: https://dash.cloudflare.com/sign-up
  • The Ensemble control-panel-server running at localhost:8080 (bash bin/control-panel-server.sh or via launchd)
  • Your DASHBOARD_TOKEN from ~/.claude/orchestrator/.env.local

Install cloudflared

brew install cloudflared
cloudflared --version    # should print 2026.x or newer

Path A — Quick Tunnel (free, no domain, ephemeral)

Best for: testing the flow end-to-end, or if you don’t have a domain yet.

Run

cloudflared tunnel --url http://localhost:8080

cloudflared prints a URL like https://random-words-1234.trycloudflare.com and registers a tunnel connection (you’ll see “Registered tunnel connection” in the log when it’s live).

Pair the PWA

  1. Open https://kiwimaddog2020.github.io/ensemble-dashboard/pair.html from any device (your phone, another laptop, anywhere).
  2. Server URL field: paste the trycloudflare.com URL from cloudflared.
  3. Token field: paste your DASHBOARD_TOKEN.
  4. Submit. The PWA stores both in localStorage on this device.

Now /terminal.html, /character-creator.html, etc. all reach your Mac via the tunnel.

Caveats

  • URL rotates on cloudflared restart. Mac reboot, killing cloudflared, network change → new URL. You’ll need to re-pair.
  • No Cloudflare Access auth gate. Your DASHBOARD_TOKEN is the only thing protecting the server from anyone who guesses the URL — fine for short testing windows, not production.
  • Quick Tunnels can drop. Cloudflare reserves the right to terminate trycloudflare.com tunnels. Production needs Path B.

Run as a background service (still ephemeral URL)

nohup cloudflared tunnel --url http://localhost:8080 --no-autoupdate \
  > /tmp/cloudflared-quick.log 2>&1 &

Then tail -f /tmp/cloudflared-quick.log to watch for the URL when you need to re-pair.


Path B — Named Tunnel (permanent URL + custom domain)

Best for: ongoing personal use. Requires a domain on Cloudflare.

1. Register / move a domain to Cloudflare

If you already have a domain elsewhere, transfer DNS to Cloudflare (free, ~10 min, no migration of email or anything else needed).

If you don’t have one, register through Cloudflare directly (~$8-10/yr for .com).

You don’t need to actually USE the apex — you’ll just point a subdomain like ensemble.your-domain.com at the tunnel.

2. Authorize cloudflared once

cloudflared tunnel login

Opens a browser tab. Pick the zone for your domain, click Authorize. Saves a cert to ~/.cloudflared/cert.pem.

3. Create the tunnel

cloudflared tunnel create ensemble-mac

Prints a tunnel UUID. Saves credentials to ~/.cloudflared/<UUID>.json. The tunnel is now registered with Cloudflare but not yet wired to a hostname.

4. Point a hostname at the tunnel

cloudflared tunnel route dns ensemble-mac ensemble.your-domain.com

Replace ensemble.your-domain.com with the subdomain you actually want.

5. Write the config file

Create ~/.cloudflared/config.yml:

tunnel: ensemble-mac
credentials-file: /Users/kevin/.cloudflared/<UUID>.json

ingress:
  - hostname: ensemble.your-domain.com
    service: http://localhost:8080
  - service: http_status:404

Replace <UUID> with the actual UUID from step 3 (or use ~/.cloudflared/ relative path if cloudflared resolves it — verify per its current docs).

6. Run the tunnel

cloudflared tunnel run ensemble-mac

The hostname is now permanent. Next time you reboot, just run this command again — the URL stays the same.

7. Re-pair the PWA (one final time)

/pair.html → paste https://ensemble.your-domain.com + your DASHBOARD_TOKEN. After this, the PWA never needs re-pairing on this device until you change the URL or the token.

8. (Optional) Add Cloudflare Access auth gate

For an extra layer beyond the DASHBOARD_TOKEN:

  1. Cloudflare Zero Trust dashboard → Access → Applications
  2. Add an application → Self-hosted
  3. Application domain: ensemble.your-domain.com
  4. Policy: “Allow” + Include “Emails ending in @your-email-domain.com” (or just your literal email)
  5. Save.

Now any HTTP request to ensemble.your-domain.com first goes through a Cloudflare-hosted login (one-time-PIN to email or Google SSO). Browsers get redirected through it transparently; your DASHBOARD_TOKEN still guards the server itself.

9. (Optional) Run cloudflared as a launchd service

So the tunnel survives reboots automatically:

sudo cloudflared service install

This sets up cloudflared to run as a system service. You can stop with:

sudo launchctl stop com.cloudflare.cloudflared

Troubleshooting

“Could not reach the server” in the PWA

Most common cause: stale localStorage.ENSEMBLE_SERVER_URL from a previous pair attempt (especially if you used a Quick Tunnel that’s since rotated).

Safari fix:

  1. Develop menu → Show Web Inspector
  2. Storage tab → Local Storage → kiwimaddog2020.github.io
  3. Delete ENSEMBLE_SERVER_URL and DASHBOARD_TOKEN
  4. Hard-refresh: Cmd+Option+E (empty cache) then Cmd+R
  5. Re-pair via /pair.html with the current URL + token

Chrome fix: same idea, Application tab → Local Storage.

cloudflared tunnel --url ... exits immediately

Usually means port 8080 isn’t actually serving anything. Verify with:

curl -s http://localhost:8080/state

If that fails, your control-panel-server isn’t running. Start it with bash bin/control-panel-server.sh.

Quick Tunnel URL responds 502

The tunnel is registered with Cloudflare but cloudflared can’t reach localhost:8080. Check that the Mac firewall isn’t blocking loopback, and that the server is actually listening on 0.0.0.0:8080 (not just 127.0.0.1 — control-panel-server does the right thing by default).

CORS error in DevTools console

The server already sends Access-Control-Allow-Origin: * on every endpoint, so this is rare. If you see it, hard-refresh after re-pairing. If it persists, check whether a browser extension is stripping headers.

Cloudflared SIGTERM / connection drops

Quick Tunnels die periodically (it’s free, no SLA). Check /tmp/cloudflared-quick.log for “Retrying connection” loops; just kill + restart cloudflared. For permanent reliability, switch to Path B.


Verify it’s working

From a different network than your Mac (e.g. your phone on cellular):

1. Open https://kiwimaddog2020.github.io/ensemble-dashboard/terminal.html
2. The status pill in the top-right should be "live" (green) within a few seconds
3. The chats list on the left should populate with your projects
4. Click a chat — the scrollback should load + new messages should stream in

If any step fails, open DevTools Console — the runtime logs diagnostic [ensemble:terminal-controller] and [ensemble:character-creator] warnings that tell you exactly which thing broke.


Where the secrets live

  • DASHBOARD_TOKEN: ~/.claude/orchestrator/.env.local
  • Cloudflared cert: ~/.cloudflared/cert.pem (only after tunnel login)
  • Tunnel credentials: ~/.cloudflared/<tunnel-UUID>.json
  • Per-device PWA state: browser localStorage at the GH Pages origin (ENSEMBLE_SERVER_URL + DASHBOARD_TOKEN)

Don’t commit any of these to git. The .env.local is already in .gitignore; the ~/.cloudflared/ directory lives outside the repo.