---
name: setup-tunnel
description: Set up a Cloudflare Tunnel using deterministic MCP tools for all operations. Browser-specialist navigates to the auth URL and verifies URLs — never interacts with forms or dashboard pages.
---

# Set Up Cloudflare Tunnel

Create a Cloudflare Tunnel so the platform is accessible from the public internet via `admin.{domain}` and `public.{domain}`.

Authentication: `tunnel-login` — one-click OAuth via `cloudflared`. The user signs in to Cloudflare and clicks Authorize. The MCP tools derive credentials automatically.

All operations are deterministic tool calls — exact same calls in exact same order every time. No screenshots, no snapshots, no page state evaluation, no specialist dispatches for Cloudflare pages.

## User-facing language

Never say "API token", "scoped token", "cert.pem", "Zone", "DNS", "CNAME", "ingress", or any Cloudflare-internal terminology to the user. Use plain language:

| Internal term | Say instead |
|---------------|-------------|
| API token / cert.pem | connection |
| Zone | domain |
| DNS records | domain routing |
| Nameservers | domain settings |
| CNAME | routing entry |
| Tunnel ID | (don't mention) |
| Ingress rules | (don't mention) |

Technical details are for tool calls and specialist briefs only — never in messages to the user.

## Prerequisites

Before starting, call `tunnel-status` to assess the current state.

- **Tunnel already running with no DNS warnings:** Tunnel is set up. Confirm to the user and stop.
- **Tunnel running but DNS NOT RESOLVING warning:** The tunnel process works but URLs are dead because nameservers don't point to Cloudflare. Proceed to "Check domain status."
- **cloudflared not installed:** Call `tunnel-install`. If it fails, tell the user and stop.
- **`tokenValid: true`, not running:** Token is valid — skip auth. Proceed to "Check domain status."
- **`hasCert: true` but `hasToken: false`:** cert.pem exists but credentials haven't been derived. Call `tunnel-login` — it will derive credentials from the cert automatically.
- **`hasCert: false` and `hasToken: false`:** Full flow required. Proceed to "Connect to Cloudflare."

## Connect to Cloudflare

The platform needs to connect to the user's Cloudflare account. Three deterministic tool calls — no screenshots, no snapshots, no specialist.

### Flow

1. **Show the browser.** Call `render-component` with `name: "browser-viewer"` and `data: { title: "Cloudflare" }`.

2. **Start the login.** Call `tunnel-login`. It returns an auth URL. Store the auth URL.

3. **Open the auth URL.** Two deterministic Playwright calls — no specialist, no evaluation:
   - `browser_navigate` with the auth URL
   - `browser_tabs` with `action: "select"` on the current tab (brings it to front in VNC)

4. **Tell the user what to do.** Say exactly: "Sign in to your Cloudflare account in the browser and click Authorize when you see the prompt. Let me know when you're done." Then STOP and WAIT. Do not take screenshots. Do not check the page. Do not dispatch a specialist. Just wait.

5. **When the user confirms:** Call `tunnel-login` again. It detects the cert and derives credentials automatically. If it returns "Credentials derived" — done, proceed to "Check domain status." If it returns an error, tell the user and ask them to try again in the browser. Maximum 3 attempts.

## Check domain status

7. **Call `cf-zone-status`** to list all domains on the account with their activation status and nameservers.

8. **Handle the domain status.**
   - **Domain is Active:** Proceed to "Create the tunnel."
   - **Domain is Pending:** Nameservers haven't been updated yet. Proceed to "Update nameservers."
   - **No domain on the account:** Call `cf-add-zone` with the domain. If it succeeds, proceed based on the returned status (Active → create tunnel; Pending → update nameservers). If it fails due to permissions, tell the user: "I need you to add your domain to Cloudflare. Sign in at cloudflare.com, click 'Add a site', and enter your domain. Let me know when it's done." Then re-run `cf-zone-status` to confirm.

### Update nameservers

9. **Get the Cloudflare nameservers** from the `cf-zone-status` output (two hostnames ending in `.ns.cloudflare.com`).

10. **Ask the user which registrar they use.** "Your domain needs to be pointed to Cloudflare. Which company did you register your domain with? (e.g., Namecheap, GoDaddy, Porkbun)"

11. **Show the browser** (if title changed). Call `render-component` with:
    - `name: "browser-viewer"`
    - `data: { title: "Registrar" }`

12. **Navigate to the registrar in the VNC browser.** Dispatch personal-assistant:

    > Navigate to {registrar_url}. Take a screenshot. Report whether a sign-in page is shown or the user is already signed in.

13. **User signs in to their registrar.** Tell the user: "Sign in to your {registrar} account in the browser. Let me know when you're signed in."

14. **Navigate to nameserver settings.** Dispatch personal-assistant:

    > Find the domain "{domain}" in the registrar dashboard. Navigate to its DNS or nameserver settings. Take a screenshot. Report the current nameservers and whether there is an option to set custom nameservers.

15. Tell the user to update the nameservers:

    > Update the nameservers in the browser to:
    > - `{cf_nameserver_1}`
    > - `{cf_nameserver_2}`
    >
    > Replace the existing nameservers and save. Let me know when done.

16. Wait for confirmation. Re-run `cf-zone-status` to check the domain status. If still Pending: "The settings are updated but it can take a few minutes to take effect. We can continue setting up the tunnel — everything will start working once the change propagates."

    Proceed to "Create the tunnel."

## Create the tunnel

17. **Call `tunnel-create`** with the domain. `tunnel-create` is idempotent — safe to call again. It creates the tunnel on Cloudflare's edge, configures ingress rules, and creates CNAME records for `admin.{domain}` and `public.{domain}`. If the SDK DNS calls fail due to token scope, it falls back to CLI-based DNS routing automatically — no intervention needed.

18. **Check the response for DNS warnings.** If the domain isn't Active on Cloudflare, return to "Update nameservers." If DNS propagation is in progress, wait and re-check.

19. **Tell the user.** "Your tunnel is created and your domain is configured. One more step — setting a password for remote access."

## Enable the tunnel

20. **Set remote password.** The remote password gates external access to the admin interface — `tunnel-enable` refuses to start without it. The password is set by the user in their own browser, never in chat.

    Call `remote-auth-status` (admin MCP tool). The response is deterministic: `configured` (boolean) and `setupUrl` (the browser URL for setting a password).

    - If `configured: true`: proceed to enable.
    - If `configured: false`: tell the user to open `setupUrl` in their own browser (not the VNC browser) and set a remote access password. This password protects admin access over the internet — separate from the local PIN.

      **IMPORTANT — markdown escaping hazard:** The `setupUrl` contains double underscores (`/__remote-auth/setup`). Always wrap it in backticks (inline code) so markdown does not escape `__` to `\_\_`. A URL with backslash-escaped underscores will 404.

      > No remote password set yet. You need to create one — it protects your admin interface when accessed over the internet.
      >
      > Open this URL in your own browser (not the one in the chat) and set a password:
      > `{setupUrl from remote-auth-status}`
      >
      > Choose something strong — at least 8 characters with a number and a special character. Let me know when you've submitted it.

    - **Verification gate:** After the user confirms, wait 3 seconds before the first check — scrypt hashing on ARM takes 2-5 seconds, so an immediate call always returns `false`. Then call `remote-auth-status`. If `configured: true`, confirm and proceed. If still `false`, retry every 3 seconds up to 4 more attempts (15 seconds total). If still `false` after all attempts, tell the user: "Still waiting — this can take a moment on slower devices. Please check the browser to make sure the form submitted, then let me know to try again."

    - **The agent never collects, sees, or handles the password.** It only checks the tool response. No filesystem paths, no Bash commands.

21. **Enable the tunnel.** Call `tunnel-enable` with the tunnel ID and domain.

22. **Verify the URLs work.** `tunnel-enable` already verifies reachability — it HTTP-checks `https://public.{domain}` through Cloudflare's edge and only reports success if the URL responds. If `tunnel-enable` reports success, the URLs are verified. If it reports failure, relay the error to the user.

## Add alias domain

An alias domain (e.g. `maxy.chat`) serves the public chat directly — the URL stays as the alias domain in the browser bar. No redirect.

### Prerequisites

- A working tunnel (the main setup flow above must be complete)
- The alias domain must be an Active zone on the Cloudflare account (same as the primary domain — added via the dashboard or purchased through Cloudflare)
- The tunnel ID (from `tunnel-status`)

### Flow

1. **Confirm the alias domain.** The user says something like "attach maxy.chat to my public chat" or "add maxy.chat as an alias." Extract the alias domain.

2. **Check the alias domain is on Cloudflare.** Call `cf-zone-status`. The alias domain must appear as Active. If it's not listed, guide the user through adding it (same as "Check domain status" in the main flow). If it's Pending, guide nameserver changes.

3. **Get the tunnel ID.** Call `tunnel-status`. The tunnel must be running or at least have a known tunnel ID.

4. **Add the alias hostname.** Call `tunnel-add-hostname` with the alias domain and tunnel ID. The tool handles everything: adds the hostname to the tunnel ingress, creates a CNAME in the alias domain's zone pointing to the tunnel, and registers the alias so the web server recognises it. The web server hot-reloads alias domains automatically — no restart needed.

5. **Verify the alias URL works.** Wait a few seconds for the web server to pick up the change, then use `dns-lookup` to confirm the CNAME resolves. Tell the user: "Your public chat is now live at https://{alias_domain}. The URL stays as {alias_domain} — no redirect." If DNS doesn't resolve yet, tell the user propagation takes 1-5 minutes.

### User-facing language

Same rules as the main flow — no technical terminology. Say "alias domain" or "custom domain" when talking to the user. Never say CNAME, ingress, or zone.

### Idempotency

`tunnel-add-hostname` is idempotent — safe to call multiple times with the same hostname. If the alias is already configured, it reports that and does nothing.

## Reinstalling / upgrading the platform

When re-running the installer (upgrade, reconfiguration, or recovery), preserve the device's current identity by passing explicit flags. The installer treats `--hostname` and `--port` as the single authority when provided — no detection, no fallback.

1. Call `system-status` to get the current `hostname` and `port` values.
2. Derive the brand package name from the `brand` field in the response (e.g., `Maxy` → `@rubytech/create-maxy`, `Real Agent` → `@rubytech/create-realagent`).
3. Run the installer with both flags:

```bash
npx -y @rubytech/create-realagent --hostname muvin --port 19400
```

Without these flags, the installer falls back to detection (reads the current OS hostname and port from `.env` or the service file). Detection is reliable for simple cases but fails when the OS hostname was previously reset to the brand default — which is the problem `--hostname` exists to solve.

## Important

- **Deterministic.** Every tool call in this flow is the same every time. No screenshots, no snapshots, no page state evaluation, no specialist dispatches for any Cloudflare page. The only Playwright interaction is `browser_navigate` + `browser_tabs select` to open the auth URL.
- **Never navigate the Cloudflare dashboard.** The ONLY Cloudflare URL opened in the browser is the auth URL from `tunnel-login`. No API token pages, no dashboard forms, nothing else.
- **Never suggest creating or pasting API tokens.** `tunnel-login` handles all authentication. If it fails after 3 attempts, tell the user and stop.
- **The user signs in themselves.** Open the URL, tell the user what to do, wait. Do not evaluate the page.
- **`tunnel-enable` verifies URLs.** It HTTP-checks the public URL through Cloudflare's edge. Do not separately verify with screenshots or specialists.
- **`render-component` before `browser_navigate`.** Show the browser viewer first so the user can see the page.
- **All tunnel tools are idempotent** — `tunnel-login`, `tunnel-create`, `tunnel-add-hostname` — safe to call multiple times.
- **DNS propagation takes 1-5 minutes** after tunnel creation. Nameserver propagation takes minutes to 24 hours.
