# Cloudflare reset — when and how

Reset is a heavier action than recovery. A reset deletes every tunnel on the brand's Cloudflare account and wipes the on-disk cloudflared config directory. Use it when the state is genuinely corrupt or when the operator wants a known-good baseline; use patching (targeted cleanup) when the state is mostly correct but one record or process is wrong.

---

## Decision tree — reset or patch?

Ask three questions in order. The first `yes` determines the action.

1. **Is cert.pem bound to an account the operator no longer wants to be signed into?** (e.g. the operator signed in to the wrong account, or the previous account was scrubbed / rotated / delisted.)
   → Full reset. The cert authorises everything `cloudflared` does; a wrong-account cert poisons every subsequent step.

2. **Is `config.yml` referencing a tunnel UUID that no longer exists on the account, or a hostname that the account no longer owns?**
   → Full reset. The tunnel the config names is unreachable; recovering per-step is slower than starting clean.

3. **Is the only problem a stray CNAME in the dashboard, a rogue token-mode connector process, or an out-of-date `alias-domains.json` / `operator-domains.json` entry?**
   → Patch (see § Patching below). Reset would destroy correct local state alongside the bad record.

When no question reaches `yes`, the state is probably recoverable per-step via `references/manual-setup.md`. Re-run the relevant manual setup steps before reaching for reset.

---

## Full reset

### What it does

The agent issues these commands in order via Bash, against the brand cert:

```
BRAND=$(jq -r .hostname ~/.<configDir>/brand.json)
CFG_DIR="${HOME}/.${BRAND}/cloudflared"

for t in $(cloudflared --origincert "${CFG_DIR}/cert.pem" tunnel list --output json | jq -r '.[].id'); do
  cloudflared --origincert "${CFG_DIR}/cert.pem" tunnel delete -f "$t"
done

rm -rf "${CFG_DIR}"
```

Then stop the brand's cloudflared user service so the now-deleted tunnel's connector exits:

```
systemctl --user stop "cloudflared-${BRAND}.service" 2>/dev/null || true
```

### What it does not do

- It does not touch DNS records. Records pointing at `<UUID>.cfargotunnel.com` from a deleted tunnel become stray; delete them in the dashboard (see § Patching below).
- It does not stop token-mode connector processes. If the device is running `cloudflared ... tunnel run --token <X>` for a tunnel the brand's cert does not own, that connector keeps running. Use `pkill` per § Patching.
- It does not switch accounts. A fresh `cloudflared tunnel login` after reset picks a new one — but the operator must sign in with the correct account in the browser.

### After reset

Re-run the setup flow from `references/manual-setup.md` § Step 1 with the operator's chosen hostnames (see `SKILL.md` for the input-collection questions). `cloudflared tunnel login` runs again; the operator authorises a fresh cert from the correct account in their own browser via the linkified OAuth URL.

---

## Patching — targeted cleanup without reset

### Stop a token-mode connector

Token-mode tunnels are created by a central operator (e.g. Real Agent-provided subdomains under `maxy.bot`). The connector runs with `--token <X>` and does not consult `cert.pem`, so the full-reset flow above (which authorises via cert.pem) cannot stop or delete it. Kill the process directly:

```
pkill -f 'cloudflared.*tunnel run --token'
```

The pattern matches exactly connectors started with `tunnel run --token`, leaving cert-mode connectors (started without `--token`) alone. Confirm the kill:

```
ps -ef | grep '[c]loudflared'
```

No lines with `--token` should remain. If multiple token-mode connectors are running, all are killed — scope with a more specific pattern if that is not desired (`pkill -f 'cloudflared.*tunnel run --token eyJ...'` matches the token prefix).

### Delete a stray CNAME that survived a tunnel delete

When a tunnel is deleted, the DNS CNAMEs that pointed at `<UUID>.cfargotunnel.com` remain in the zone — Cloudflare deliberately does not cascade-delete DNS when you delete a tunnel. Clean up in the dashboard:

1. See `references/dashboard-guide.md` § "Delete a stray CNAME" for the click-path.
2. Verify the CNAME's target is the tunnel you just deleted (not a live one).
3. Delete.

When the operator is unsure which records are stray, have them list the zone's CNAMEs (see `references/dashboard-guide.md` § "List CNAME records in a zone") and compare against the currently-running `cloudflared tunnel list` on the device.

### Remove a rogue entry from `alias-domains.json`

`alias-domains.json` controls which hostnames the platform UI classifies as public (serving the public agent) rather than admin. It lives at `${HOME}/.${BRAND}/alias-domains.json` — the brand root, **not** `${CFG_DIR}` (which is `${HOME}/.${BRAND}/cloudflared`). The platform UI watches the brand root; writing to `${CFG_DIR}/alias-domains.json` is invisible to the watcher.

A prior setup flow may have written an `admin.*` hostname into this file by mistake. Remove it manually:

```
cat "${HOME}/.${BRAND}/alias-domains.json"
```

If `admin.<yourdomain>` is listed, strip it:

```
jq --arg H "admin.<yourdomain>" 'map(select(. != $H))' "${HOME}/.${BRAND}/alias-domains.json" > /tmp/alias.json && mv /tmp/alias.json "${HOME}/.${BRAND}/alias-domains.json"
```

Replace `<yourdomain>` with the actual domain. The platform UI watches the file and reloads without a restart.

### Remove a rogue entry from `operator-domains.json`

`operator-domains.json` controls which hostnames the platform UI classifies as **operator** (serving the trimmed operator dashboard — sessions, chat, data, no graph/browser) rather than admin or public. It lives at `${HOME}/.${BRAND}/operator-domains.json` — the brand root, **not** `${CFG_DIR}` — for the same reason as `alias-domains.json`: the platform UI watches the brand root.

A host wrongly listed here serves the operator shell instead of the full admin shell (or instead of the public agent, since operator wins over public). Remove it manually:

```
cat "${HOME}/.${BRAND}/operator-domains.json"
```

If the wrong host is listed, strip it:

```
jq --arg H "<host-to-remove>" 'map(select(. != $H))' "${HOME}/.${BRAND}/operator-domains.json" > /tmp/op.json && mv /tmp/op.json "${HOME}/.${BRAND}/operator-domains.json"
```

The platform UI watches the file and reloads without a restart; the next request to the host routes to its corrected surface.

### Fix a single wrong DNS record without tearing everything down

If a hostname's CNAME is pointing at the wrong tunnel (e.g. after an account switch left old records), `cloudflared` can overwrite it once the correct cert is in place:

```
cloudflared --origincert "${CFG_DIR}/cert.pem" tunnel route dns --overwrite-dns "${TUNNEL_ID}" <hostname>
```

This is the same call the manual-setup runbook makes for each subdomain. Apex hostnames do not work here — the dashboard is the only path (see `references/dashboard-guide.md` § "Edit an apex CNAME").

---

## Failure discipline

When a reset or patch command exits non-zero, report the failure with the exact output. Cite the relevant section above for the next action. Do not chain alternative recovery attempts — the agent's job ends at the boundary of a sanctioned surface, and improvisation outside that boundary is the discipline violation `SKILL.md` § "Tool discipline" exists to prevent.
