Cloudflare Tunnel — the dashboard is the source of truth
Each installation has its own Cloudflare account. Sign-in is OAuth: the agent invokes cloudflared tunnel login via Bash; the Cloudflare Authorize URL streams into the admin chat PTY and the native terminal renders it as a clickable link. Click it, authorise in your own browser, and cloudflared writes cert.pem to the brand's config directory. The agent never reads or mutates Cloudflare account state directly — whatever you see in your logged-in dashboard is the single source of truth. When something needs doing on the account side (adding a domain, deleting a stray entry, switching accounts), the agent relays the click-paths; you run them in your browser.
Identity model
| Concept | Source |
|---|---|
| Product identity (Maxy vs Real Agent) | brand.json (productName, configDir) — known at install. |
| Cloudflare account identity | cert.pem from OAuth. One account per brand per device. |
| Domain scope (which zones the operator can route) | Live Cloudflare dashboard — the operator picks the zone in the dashboard during OAuth or names it in chat. The agent does not enumerate zones programmatically. |
| Local tunnel state | ~/{configDir}/cloudflared/ — cert.pem, <UUID>.json, config.yml, alias-domains.json. |
There is no token-based auth for the operator-owned path (Mode A). To switch Cloudflare accounts, the agent runs the reset flow from plugins/cloudflare/references/reset-guide.md (deletes the cert and every tunnel on the current account), then the manual-setup flow again — cloudflared tunnel login picks a fresh account when you sign in.
Setup flow
Ask the agent to set up Cloudflare. The agent confirms the domain is already on your Cloudflare account (if not, it quotes the dashboard click-path — see below) and collects the inputs in plain chat:
- Admin address — the hostname that will serve the admin chat (e.g.
admin.yourdomain.com). - Public address — optional hostname for the public agent (e.g.
public.yourdomain.comorchat.yourdomain.com). - Proxy apex — optional bare-domain hostname (e.g.
yourdomain.com) that should also serve the public agent. - Admin password — the password used to gate remote access to the admin surface.
The agent then sets the admin password via curl -X POST http://127.0.0.1:${PORT}/api/remote-auth/set-password (same endpoint the local onboarding form uses), and works through plugins/cloudflare/references/manual-setup.md Steps 1–7 directly via the Bash tool. cloudflared's stdout streams into the PTY verbatim. The OAuth URL is linkified by the terminal; click it in your own browser to authorise. After the tunnel is up, the agent appends each non-public.* public or apex hostname to ~/{configDir}/alias-domains.json so isPublicHost() classifies it as public, and starts the brand's cloudflared user service.
If any step's cloudflared invocation exits non-zero, the agent names the literal exit code, surfaces the stderr verbatim, and cites reset-guide.md for the next action — no retry under a different flag, no Playwright-driven dashboard inspection.
The setup-done claim only fires after the agent runs curl -I https://<admin-hostname> from outside the local network and the response shows a 200 line. That HTTP response is the only success terminal.
Getting a domain on Cloudflare
The tunnel needs a domain on the Cloudflare account the device will sign into. Two paths, both in your browser:
Option A: Buy a new domain through Cloudflare. Navigate to cloudflare.com → Domains and buy one. Cloudflare sets everything up.
Option B: Add an existing domain. In the dashboard: Websites → Add a site. Cloudflare imports the existing DNS records; review them to confirm your website and email entries are preserved. Cloudflare gives you two nameservers; replace the registrar's nameservers with those. Propagation is usually minutes (up to 24 hours); the zone shows Active when ready.
Existing website traffic continues to work during and after the switch. Only DNS resolution changes owners.
Reset / account switch
Ask the agent to reset Cloudflare. The agent executes the reset flow from plugins/cloudflare/references/reset-guide.md:
- Deletes every tunnel on the brand's current Cloudflare account (via the bound cert).
- Wipes the brand's
${CFG_DIR}. - Stops the brand's cloudflared user service.
The agent does not stop token-mode connector processes or delete stray misrouted CNAMEs in the dashboard. If any of those apply, the agent guides you through the manual cleanup — pkill -f 'cloudflared.*tunnel run --token' on the device, or deleting the stray CNAME in the dashboard.
After reset, run setup again. The fresh cloudflared tunnel login will pick whichever Cloudflare account you sign into.
Manual runbook
The step-by-step runbook at plugins/cloudflare/references/manual-setup.md is the contract the agent follows. It is also what an operator runs by hand when needed — every numbered step is an isolated cloudflared command block with success conditions and troubleshooting.
Dashboard operations the CLI cannot do
The CLI cannot add a domain, switch accounts, edit an apex CNAME, or delete stray records. plugins/cloudflare/references/dashboard-guide.md has one numbered click-path per operation. The agent quotes the relevant steps verbatim when you need to do one of these things.
Troubleshooting
Tunnel won't start
Ask the agent to check. The agent reads systemctl --user status ${BRAND}-cloudflared.service and the cloudflared log under ~/{configDir}/cloudflared/. Common states:
- No cloudflared process running — the cloudflared service exited or never started. The agent runs the manual-setup flow to re-issue tunnel creation.
tunnel not found— the UUID inconfig.ymldoes not match any tunnel on the currently-bound account. Usually follows an account switch that didn't reset local state. The agent runs the reset flow and then a fresh setup.
URL returns 530
DNS propagation or account mismatch. Wait 30–60 seconds and retry first. If the 530 persists:
- The domain may be on a Cloudflare account different from the one
cert.pemis bound to — the agent re-runs the manual setup steps to re-validate. - The UDP buffer for QUIC may be undersized on this device — check the cloudflared log for
failed to sufficiently increase receive buffer size.
URL returns connection refused
The tunnel is live but nothing is listening on the platform port. Start the platform service: systemctl --user start ${BRAND}.service.
Admin hostname serves the public agent
admin.yourdomain is being misclassified as public. The platform UI treats a host as public when either (a) the hostname starts with public., or (b) the hostname appears in ${CFG_DIR}/alias-domains.json. Older install flows wrote every routed hostname into alias-domains.json; the pollution survives across reinstalls.
The agent reads alias-domains.json, removes the offending admin.* entry, and the platform UI hot-reloads — no restart needed. See plugins/cloudflare/references/reset-guide.md § "Remove a rogue entry from alias-domains.json" for the exact jq command.
DNS not resolving
The most common cause is wrong nameservers on the domain. The domain must use Cloudflare's nameservers, not the registrar's defaults. In the dashboard: Websites → your domain → status must say Active, not Pending. If Pending, follow the dashboard's nameserver instructions and wait for propagation.
Remote login issues
- 5 failed login attempts → 15-minute lockout — wait for expiry.
- The remote password is set during Cloudflare Tunnel onboarding — the agent asks for one in chat and stores it deterministically. The browser form at
/__remote-auth/setupremains available for resets on the local network.
What the agent does and does not do
Does: invokes cloudflared directly via Bash, following plugins/cloudflare/references/manual-setup.md step by step; quotes click-paths from the reference files verbatim; verifies external reachability with curl -I and surfaces the response.
Does not: drive the Cloudflare dashboard via Playwright, synthesise alternative cloudflared flag sequences not in the runbook, call any Cloudflare API or SDK, write or edit cert.pem / config.yml directly outside the runbook's instructions.
When a command fails, the agent reports the failure and cites the relevant recovery step. It does not improvise.