@rubytech/create-maxy-code 0.1.109 → 0.1.111
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +50 -150
- package/dist/snap-chromium.js +1 -2
- package/dist/uninstall.js +10 -9
- package/package.json +1 -1
- package/payload/platform/neo4j/schema.cypher +6 -3
- package/payload/platform/plugins/admin/PLUGIN.md +1 -0
- package/payload/platform/plugins/admin/mcp/dist/index.js +1 -1
- package/payload/platform/plugins/admin/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/admin/skills/upgrade/SKILL.md +34 -0
- package/payload/platform/plugins/cloudflare/.claude-plugin/plugin.json +1 -1
- package/payload/platform/plugins/cloudflare/PLUGIN.md +9 -16
- package/payload/platform/plugins/cloudflare/mcp/dist/index.js +7 -12
- package/payload/platform/plugins/cloudflare/mcp/dist/index.js.map +1 -1
- package/payload/platform/plugins/cloudflare/references/dashboard-guide.md +3 -3
- package/payload/platform/plugins/cloudflare/references/manual-setup.md +19 -54
- package/payload/platform/plugins/cloudflare/references/reset-guide.md +29 -28
- package/payload/platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md +29 -149
- package/payload/platform/plugins/docs/references/admin-session.md +1 -1
- package/payload/platform/plugins/docs/references/admin-ui.md +1 -1
- package/payload/platform/plugins/docs/references/cloudflare.md +20 -29
- package/payload/platform/plugins/docs/references/platform.md +1 -1
- package/payload/platform/plugins/docs/references/plugins-guide.md +1 -1
- package/payload/platform/plugins/docs/references/troubleshooting.md +1 -1
- package/payload/platform/scripts/check-no-task-id-leaks.mjs +1 -1
- package/payload/platform/templates/agents/admin/IDENTITY.md +1 -1
- package/payload/platform/templates/specialists/agents/personal-assistant.md +1 -1
- package/payload/platform/plugins/cloudflare/scripts/__tests__/tunnel-ingress.test.ts +0 -241
- package/payload/platform/plugins/cloudflare/scripts/list-cf-domains.sh +0 -98
- package/payload/platform/plugins/cloudflare/scripts/list-cf-domains.ts +0 -715
- package/payload/platform/plugins/cloudflare/scripts/reset-tunnel.sh +0 -107
- package/payload/platform/plugins/cloudflare/scripts/setup-tunnel.sh +0 -852
- package/payload/platform/plugins/cloudflare/scripts/tunnel-ingress.ts +0 -291
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: cloudflare
|
|
3
|
-
description: Cloudflare Tunnel operations — setup, reset, dashboard guidance. Zero agent-facing MCP tools; every operation is
|
|
3
|
+
description: Cloudflare Tunnel operations — setup, reset, dashboard guidance. Zero agent-facing MCP tools; every operation is the agent invoking `cloudflared` directly via Bash or quoting a dashboard click-path the operator performs themselves.
|
|
4
4
|
tools: []
|
|
5
5
|
mcp-manifest: skip
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Cloudflare Tunnel
|
|
9
9
|
|
|
10
|
-
Each installation has its own Cloudflare account. The operator signs in with OAuth via `cloudflared tunnel login` (
|
|
10
|
+
Each installation has its own Cloudflare account. The operator signs in with OAuth via `cloudflared tunnel login` (issued by the agent through Bash); `cloudflared` writes `cert.pem` to the brand-scoped config directory. The Cloudflare dashboard is the single source of truth for which domains, addresses, and tunnels exist; the plugin never reads or mutates account state via any API path — only `cloudflared` CLI shell-outs the agent runs, and DNS + HTTPS probes against public surfaces.
|
|
11
11
|
|
|
12
12
|
## When to activate
|
|
13
13
|
|
|
@@ -25,41 +25,34 @@ Each installation has its own Cloudflare account. The operator signs in with OAu
|
|
|
25
25
|
|
|
26
26
|
## Operator-facing surface
|
|
27
27
|
|
|
28
|
-
The plugin registers no agent-facing MCP tools. Every Cloudflare operation is
|
|
29
|
-
|
|
30
|
-
### Scripts
|
|
31
|
-
|
|
32
|
-
| Script | Purpose |
|
|
33
|
-
|---|---|
|
|
34
|
-
| [`scripts/setup-tunnel.sh`](scripts/setup-tunnel.sh) | Autonomous end-to-end setup: OAuth login, tunnel resolve (operator-supplied identity), DNS route, config + state, service restart, post-restart verification. Invocation: `~/setup-tunnel.sh <brand> <port> <admin-hostname> [<public-hostname>] [<apex-hostname>]`. Required env: `ACCOUNT_DIR`, AND exactly one of `TUNNEL_ID` (operator selected an existing tunnel — the agent enumerates them via `cloudflared tunnel list --output json` and presents the list in chat) or `TUNNEL_NAME` (operator typed a name to create). Apex hostnames print an `ACTION REQUIRED` block for the dashboard record the CLI cannot create. Step 1 (wrappers faithfully relay third-party CLI) spawns `cloudflared tunnel login`, extracts the argotunnel URL from its stdout, and prints it as `OAUTH_URL: <url>` on its own stdout — the native Claude Code PTY linkifies bare URLs so the operator clicks it from chat and authorizes Cloudflare in their own browser. The script does not spawn a browser of its own; cloudflared's OAuth callback writes `~/.cloudflared/cert.pem` regardless of which browser completed the Authorize click. 180 s budget with a 2-second `step=oauth-login result=awaiting-cert` heartbeat. No CDP auto-click, no DOM matcher. |
|
|
35
|
-
| [`scripts/reset-tunnel.sh`](scripts/reset-tunnel.sh) | Deletes every tunnel on the brand's CF account and wipes `${CFG_DIR}`. Does not touch the platform service, stray CNAMEs, or token-mode connectors — those require dashboard cleanup or `pkill`. Invocation: `~/reset-tunnel.sh <brand>`. No polling blocks — every long-wait is bounded by `cloudflared`'s network round-trip, so no heartbeat contract applies. |
|
|
28
|
+
The plugin registers no agent-facing MCP tools. Every Cloudflare operation is the agent invoking `cloudflared` directly via Bash, following the numbered steps in `references/manual-setup.md`, with the cloudflared stdout/stderr streamed verbatim into the PTY chat. The OAuth URL printed by `cloudflared tunnel login` is linkified by the native PTY; the operator clicks it in their own browser. There is no shell-script wrapper, no orchestrator state machine, no MCP-tool surface.
|
|
36
29
|
|
|
37
30
|
### Skills
|
|
38
31
|
|
|
39
32
|
| Skill | Purpose |
|
|
40
33
|
|---|---|
|
|
41
|
-
| [setup-tunnel/SKILL.md](skills/setup-tunnel/SKILL.md) | Names the
|
|
34
|
+
| [setup-tunnel/SKILL.md](skills/setup-tunnel/SKILL.md) | Names the outcome contract (external HTTP 200 to the configured hostname), the inputs to collect, the runbook to execute, and the tool-discipline rule that binds the agent. |
|
|
42
35
|
|
|
43
36
|
### References
|
|
44
37
|
|
|
45
38
|
| Reference | Topics |
|
|
46
39
|
|---|---|
|
|
47
|
-
| [manual-setup.md](references/manual-setup.md) | Step-by-step
|
|
40
|
+
| [manual-setup.md](references/manual-setup.md) | Step-by-step runbook — Steps 0–7 with isolated `cloudflared` command blocks. The agent reads the relevant step before issuing each command. |
|
|
48
41
|
| [dashboard-guide.md](references/dashboard-guide.md) | Click-paths for the operations only the Cloudflare dashboard can perform — sign in, switch accounts, add a site, edit an apex CNAME, verify nameservers, delete a tunnel, manage CNAME records. |
|
|
49
42
|
| [reset-guide.md](references/reset-guide.md) | Decision tree for reset vs. patch, the exact `pkill` incantation for token-mode connectors, and the dashboard cleanup paths for stray records and rogue entries. |
|
|
50
43
|
|
|
51
44
|
The agent loads these references on demand via `plugin-read` as the conversation requires. They are not auto-injected into the system prompt.
|
|
52
45
|
|
|
53
|
-
###
|
|
46
|
+
### Success contract
|
|
54
47
|
|
|
55
|
-
|
|
48
|
+
The setup-done claim only fires when `curl -I https://<hostname>` issued from outside the local network returns `HTTP/2 200` (or `HTTP/1.1 200 OK`) and the response is surfaced verbatim in chat. No state file, no service-active claim, no `cloudflared` exit code substitutes for the live HTTP response. When the curl returns anything else, the agent diagnoses with `cloudflared tunnel info <tunnelId>` and `systemctl --user status ${BRAND}-cloudflared.service`, cites the relevant step in `references/reset-guide.md`, and stops.
|
|
56
49
|
|
|
57
50
|
## Identity model
|
|
58
51
|
|
|
59
52
|
- **Product identity** (Maxy vs Real Agent) — known from `brand.json` (`productName`, `configDir`).
|
|
60
53
|
- **Cloudflare account identity** — `cert.pem` from OAuth. One account per brand per device.
|
|
61
|
-
- **Account binding drift** — `~/{configDir}/cloudflared/account-binding.json` is a historical drift marker. Reset
|
|
54
|
+
- **Account binding drift** — `~/{configDir}/cloudflared/account-binding.json` is a historical drift marker. Reset by `rm -rf ~/.${BRAND}/cloudflared/` per `references/reset-guide.md` when switching accounts.
|
|
62
55
|
|
|
63
56
|
## Discipline
|
|
64
57
|
|
|
65
|
-
Loaded into IDENTITY.md § Cloudflare operations at install time. The short form: the agent's permitted surfaces are the
|
|
58
|
+
Loaded into IDENTITY.md § Cloudflare operations at install time. The short form: the agent's permitted surfaces are direct `cloudflared` invocations via Bash following the runbook, the three reference files, and `curl -I` reachability checks — everything else (Playwright, WebSearch-for-CF-recipes, Cloudflare API / SDK, ad-hoc `cloudflared` flag invention not in the runbook) is out of bounds. When a step fails, the agent reports the exact `cloudflared` output, cites the recovery step from `references/reset-guide.md`, and stops.
|
|
@@ -2,18 +2,13 @@ import { initStderrTee } from "../../../../lib/mcp-stderr-tee/dist/index.js";
|
|
|
2
2
|
initStderrTee("cloudflare");
|
|
3
3
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
4
4
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
5
|
-
//
|
|
6
|
-
// Cloudflare operation is
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// See platform/plugins/cloudflare/skills/setup-tunnel/SKILL.md and
|
|
13
|
-
// platform/plugins/cloudflare/references/ for the operator-facing
|
|
14
|
-
// surface. `lib/cloudflared.ts` and `lib/setup-orchestrator.ts` are
|
|
15
|
-
// retained as private implementation layers that nothing currently
|
|
16
|
-
// imports; a follow-up task will delete them once that is confirmed.
|
|
5
|
+
// The Cloudflare plugin exposes zero agent-facing tools. Every
|
|
6
|
+
// Cloudflare operation is the agent invoking `cloudflared` directly via
|
|
7
|
+
// the Bash tool, following the runbook in
|
|
8
|
+
// platform/plugins/cloudflare/references/manual-setup.md, with
|
|
9
|
+
// cloudflared stdout/stderr streamed verbatim into the PTY. The MCP
|
|
10
|
+
// server process still spawns for lifecycle and stderr-tee consistency
|
|
11
|
+
// with other plugins, but its tool list is empty by design.
|
|
17
12
|
const server = new McpServer({
|
|
18
13
|
name: "cloudflare",
|
|
19
14
|
version: "0.4.0",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,8CAA8C,CAAC;AAC7E,aAAa,CAAC,YAAY,CAAC,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,+DAA+D;AAC/D,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,8CAA8C,CAAC;AAC7E,aAAa,CAAC,YAAY,CAAC,CAAC;AAE5B,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AAEjF,+DAA+D;AAC/D,wEAAwE;AACxE,0CAA0C;AAC1C,+DAA+D;AAC/D,oEAAoE;AACpE,uEAAuE;AACvE,4DAA4D;AAE5D,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,YAAY;IAClB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,2BAA2B,GAAG,EAAE,CAAC,CAAC;IAChD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -58,7 +58,7 @@ If the account shown in the top-left is not the one that owns the zone you need,
|
|
|
58
58
|
|
|
59
59
|
## Edit an apex CNAME (bare-domain hostname)
|
|
60
60
|
|
|
61
|
-
Apex hostnames — a record at the zone root, e.g. `maxy.chat` rather than `www.maxy.chat` — cannot be routed by `cloudflared tunnel route dns` because standard DNS forbids CNAMEs at zone apex. Cloudflare's workaround (CNAME flattening) is only exposed via the dashboard. When
|
|
61
|
+
Apex hostnames — a record at the zone root, e.g. `maxy.chat` rather than `www.maxy.chat` — cannot be routed by `cloudflared tunnel route dns` because standard DNS forbids CNAMEs at zone apex. Cloudflare's workaround (CNAME flattening) is only exposed via the dashboard. When the agent prints an `ACTION REQUIRED` block for an apex (per `references/manual-setup.md` § Step 4), these are the steps:
|
|
62
62
|
|
|
63
63
|
1. Click **Websites** in the sidebar, then click the apex zone (e.g. `maxy.chat`).
|
|
64
64
|
2. Click **DNS** in the sidebar, then **Records**.
|
|
@@ -105,8 +105,8 @@ Use this when you need to audit which hostnames are pointing at which `<UUID>.cf
|
|
|
105
105
|
|
|
106
106
|
## Author an Access policy for SSH or SMB
|
|
107
107
|
|
|
108
|
-
When
|
|
109
|
-
`ACTION REQUIRED` block naming this click-path. The
|
|
108
|
+
When the agent adds an SSH or SMB ingress hostname (per `references/manual-setup.md`), it surfaces an
|
|
109
|
+
`ACTION REQUIRED` block naming this click-path. The agent does NOT
|
|
110
110
|
create the Access policy — Cloudflare API/SDK is banned
|
|
111
111
|
(`feedback_cf_api_total_eradication`) and `cloudflared` CLI has no
|
|
112
112
|
Access-application create subcommand — so the operator must author it
|
|
@@ -39,43 +39,13 @@ Mode B skips Steps 1–5 of this runbook entirely — the user receives a token
|
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
42
|
-
##
|
|
42
|
+
## How this runbook is used
|
|
43
43
|
|
|
44
|
-
|
|
44
|
+
This runbook is the contract the agent follows when setting up a Cloudflare tunnel. The agent reads the step it is about to execute, runs the `cloudflared` command (or writes the config / starts the service) via the Bash tool, and streams the literal output back to the operator. There is no shell-script wrapper, no orchestrator state machine, no MCP-tool surface — the PTY itself is the operator-visible surface, and cloudflared's stdout/stderr is the evidence.
|
|
45
45
|
|
|
46
|
-
|
|
46
|
+
The setup is done when, and only when, `curl -I https://<admin-hostname>` issued from outside the local network returns `HTTP/2 200` (or `HTTP/1.1 200 OK`). The agent must paste the curl response into chat before claiming success. No service-active or `cloudflared exit 0` claim substitutes for that response.
|
|
47
47
|
|
|
48
|
-
|
|
49
|
-
setup-tunnel.sh <brand> <port> <hostname> [<hostname> ...]
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
Example:
|
|
53
|
-
|
|
54
|
-
```
|
|
55
|
-
~/setup-tunnel.sh maxy 19200 admin.maxy.bot public.maxy.bot maxy.chat
|
|
56
|
-
```
|
|
57
|
-
|
|
58
|
-
Mirrors this runbook verbatim (Steps 0–5b), calls `systemctl --user restart "${BRAND}.service"` at the end to spawn the connector via `resume-tunnel.sh`, then polls each subdomain hostname (up to 60s per host) for a non-530 response. Apex hostnames cannot be routed via CLI (see §Step 4); the script prints an explicit ACTION REQUIRED block naming the dashboard record to edit or add.
|
|
59
|
-
|
|
60
|
-
```
|
|
61
|
-
reset-tunnel.sh <brand>
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
Example:
|
|
65
|
-
|
|
66
|
-
```
|
|
67
|
-
~/reset-tunnel.sh maxy
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
Deletes every tunnel on the brand's Cloudflare account (via the brand's cert) and wipes `${CFG_DIR}`. Does **not** touch the platform service or any stray misrouted CNAMEs in DNS — those require a manual dashboard delete.
|
|
71
|
-
|
|
72
|
-
After manual transfer to a device, run `chmod +x ~/setup-tunnel.sh ~/reset-tunnel.sh` before the first invocation.
|
|
73
|
-
|
|
74
|
-
**Walk through manually (instead of scripting) when:**
|
|
75
|
-
- Diagnosing a step that's failing under the script.
|
|
76
|
-
- Recovering a partial state where the scripts assume a clean start.
|
|
77
|
-
- Validating runbook changes before re-baking them into the scripts.
|
|
78
|
-
- Working on a device where the scripts aren't deployed yet.
|
|
48
|
+
Apex hostnames (e.g. `maxy.chat`) cannot be routed by `cloudflared tunnel route dns` — see §Step 4. The agent quotes the ACTION REQUIRED block from §Step 4 verbatim and the operator edits the apex CNAME in the dashboard.
|
|
79
49
|
|
|
80
50
|
---
|
|
81
51
|
|
|
@@ -222,7 +192,7 @@ cloudflared --origincert "${CFG_DIR}/cert.pem" tunnel route dns --overwrite-dns
|
|
|
222
192
|
2. Overwritten: `Added CNAME <hostname> which will route to this tunnel` (same shape as new; `--overwrite-dns` makes overwrite transparent)
|
|
223
193
|
3. Idempotent no-op: `<timestamp> INF <hostname> is already configured to route to your tunnel tunnelID=<UUID>`
|
|
224
194
|
|
|
225
|
-
Shape 3 is what a
|
|
195
|
+
Shape 3 is what a repeat run against an already-configured hostname emits — `cloudflared` exits 0 and the agent treats it as a no-op success.
|
|
226
196
|
|
|
227
197
|
**If it fails with `zone not found`:** the hostname's parent domain isn't on this brand's Cloudflare account. Either add it in the dashboard (Websites → Add a site) and re-run, or sign into the account that already owns the domain.
|
|
228
198
|
|
|
@@ -306,12 +276,10 @@ cloudflared --origincert "${CFG_DIR}/cert.pem" tunnel route dns --overwrite-dns
|
|
|
306
276
|
cloudflared --origincert "${CFG_DIR}/cert.pem" tunnel route dns --overwrite-dns "${TUNNEL_ID}" smb.maxy.bot
|
|
307
277
|
```
|
|
308
278
|
|
|
309
|
-
**Order matters
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
`[tunnel-install] {ssh,smb}-ingress-deferred` and config.yml is rendered
|
|
314
|
-
without it.
|
|
279
|
+
**Order matters:** route HTTPS hostnames first and rewrite `config.yml`
|
|
280
|
+
only after the HTTPS pass is durable, so a failure on `ssh` or `smb` route
|
|
281
|
+
DNS does not nuke the HTTPS ingress; the failing hostname is logged and
|
|
282
|
+
config.yml is rendered without it.
|
|
315
283
|
|
|
316
284
|
**SMB depends.** Before adding the SMB ingress, confirm the
|
|
317
285
|
brand stanza exists in `/etc/samba/smb.conf`:
|
|
@@ -355,7 +323,7 @@ cloudflared access tcp --hostname smb.<brand>.<rootdomain> --url tcp://localhost
|
|
|
355
323
|
|
|
356
324
|
## Step 5b — Write tunnel.state
|
|
357
325
|
|
|
358
|
-
`resume-tunnel.sh` (the `ExecStartPre=` in the brand's service) reads `${CFG_DIR}/tunnel.state` to discover which tunnel to run. **Without this file the script exits 0 silently and no connector is spawned** — see lines 34-37 of `platform/scripts/resume-tunnel.sh`. The
|
|
326
|
+
`resume-tunnel.sh` (the `ExecStartPre=` in the brand's service) reads `${CFG_DIR}/tunnel.state` to discover which tunnel to run. **Without this file the script exits 0 silently and no connector is spawned** — see lines 34-37 of `platform/scripts/resume-tunnel.sh`. The raw CLI flow in this runbook does not write `tunnel.state` automatically, so write it explicitly here:
|
|
359
327
|
|
|
360
328
|
Re-run the Step 5a fail-fast guard if your shell is new or if Step 5a was a while ago:
|
|
361
329
|
|
|
@@ -381,8 +349,8 @@ EOF
|
|
|
381
349
|
|
|
382
350
|
Omit `sshHostname` and `smbHostname` when those ingresses are not in
|
|
383
351
|
use. The fields are additive; the script's re-run path rehydrates them
|
|
384
|
-
on every invocation so
|
|
385
|
-
`SSH_HOSTNAME` / `SMB_HOSTNAME`
|
|
352
|
+
on every invocation so a repeat setup run without
|
|
353
|
+
`SSH_HOSTNAME` / `SMB_HOSTNAME` set does not silently drop the
|
|
386
354
|
SSH/SMB ingress.
|
|
387
355
|
|
|
388
356
|
**Why each field:** `resume-tunnel.sh` reads `tunnelId`, `domain`, `configPath` (all used; domain is for logging, tunnelId for the log line, configPath is passed as `--config` to cloudflared). `credentialsPath` and `tunnelName` are not read by `resume-tunnel.sh` itself but are consumed by other tools (e.g. `tunnel-status` in the cloudflared plugin), so write them anyway.
|
|
@@ -403,23 +371,20 @@ Prints the UUID from Step 3. If it prints empty or null, the heredoc's env expan
|
|
|
403
371
|
|
|
404
372
|
You do **not** run `cloudflared` manually. The brand's existing user-space systemd unit (`~/.config/systemd/user/${BRAND}.service`) declares `ExecStartPre=/home/<user>/${BRAND}/platform/scripts/resume-tunnel.sh`, and that pre-start script reads `${CFG_DIR}/tunnel.state` and `${CFG_DIR}/config.yml` (the files Steps 5 and 5b just wrote) and spawns the connector in the user's cgroup. Restarting the brand service is what picks up the new config.
|
|
405
373
|
|
|
406
|
-
> **Note:** When walking through by hand you run this step yourself. The automation script `platform/plugins/cloudflare/scripts/setup-tunnel.sh` runs it for you — with a critical twist documented below. If you used the script, this step is already done and the service will restart a few seconds after the script exits.
|
|
407
|
-
|
|
408
|
-
|
|
409
374
|
```
|
|
410
375
|
systemctl --user restart "${BRAND}.service"
|
|
411
376
|
```
|
|
412
377
|
|
|
413
|
-
**
|
|
378
|
+
**Cgroup trap — when the admin agent issues this command via the Bash tool, the Bash subprocess runs *inside* `${BRAND}.service`'s cgroup.** A direct `systemctl --user restart ${BRAND}.service` from that cgroup tells systemd to SIGTERM the entire cgroup — the node server, the claude subprocess, the Bash child, and the agent itself all die simultaneously. cgroup membership is inherited: `setsid`, `nohup`, `disown`, and `&` all stay in the caller's cgroup, and `systemd-run --scope` runs in the caller's scope. Only `systemd-run --user --unit=<name> --on-active=<N>s` creates a genuinely new transient unit with its own cgroup. The agent uses that primitive to arm the restart a few seconds after the Bash call returns:
|
|
414
379
|
|
|
415
380
|
```
|
|
416
|
-
systemd-run --user --unit=maxy-tunnel-restart
|
|
381
|
+
systemd-run --user --unit=maxy-tunnel-restart-$(date +%s).service --on-active=3s --collect \
|
|
417
382
|
/bin/systemctl --user restart "${BRAND}.service"
|
|
418
383
|
```
|
|
419
384
|
|
|
420
|
-
The
|
|
385
|
+
The transient timer fires from outside the service's cgroup — semantically identical to a direct `systemctl --user restart`, but the agent survives.
|
|
421
386
|
|
|
422
|
-
When walking through manually you do **not** need `systemd-run` — your SSH shell already lives in a separate user-scope cgroup (`user@<uid>.service`), so the direct `systemctl restart` does not kill the caller. The
|
|
387
|
+
When walking through manually from an SSH shell you do **not** need `systemd-run` — your SSH shell already lives in a separate user-scope cgroup (`user@<uid>.service`), so the direct `systemctl restart` does not kill the caller. The extra indirection only matters when the caller *is* inside the service being restarted (the admin agent's case).
|
|
423
388
|
|
|
424
389
|
**Why:** `resume-tunnel.sh` is the deterministic, brand-scoped spawner. Running `cloudflared` manually duplicates the connector (two processes for one tunnel) and races the brand service on every service restart. The service path is the only correct production path.
|
|
425
390
|
|
|
@@ -540,20 +505,20 @@ sudo cloudflared service uninstall
|
|
|
540
505
|
If `admin.<yourdomain>` is rendering the public-agent UI, the tunnel is fine but the platform UI is treating the admin hostname as public. The platform UI classifies a host as public when either:
|
|
541
506
|
|
|
542
507
|
- the hostname starts with `public.`, or
|
|
543
|
-
- the hostname appears in `${
|
|
508
|
+
- the hostname appears in `${HOME}/.${BRAND}/alias-domains.json` (note: brand root, **not** `${CFG_DIR}` — `alias-domains.json` lives one level above `cloudflared/`, because that's where the platform UI watches).
|
|
544
509
|
|
|
545
510
|
Pre-Task-548 sessions populated this file via the now-deleted `tunnel-add-hostname` MCP tool, which wrote every routed hostname — including `admin.*` — into the alias set. The pollution persists across installs.
|
|
546
511
|
|
|
547
512
|
**Diagnose:**
|
|
548
513
|
|
|
549
514
|
```
|
|
550
|
-
cat "${
|
|
515
|
+
cat "${HOME}/.${BRAND}/alias-domains.json"
|
|
551
516
|
```
|
|
552
517
|
|
|
553
518
|
If `admin.<yourdomain>` appears in the array, remove it:
|
|
554
519
|
|
|
555
520
|
```
|
|
556
|
-
jq --arg H "admin.<yourdomain>" 'map(select(. != $H))' "${
|
|
521
|
+
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"
|
|
557
522
|
```
|
|
558
523
|
|
|
559
524
|
Replace `<yourdomain>` with your actual domain. The platform UI watches the file — no restart required; the next request to the admin hostname routes to the admin surface.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Cloudflare reset — when and how
|
|
2
2
|
|
|
3
|
-
Reset is a heavier action than recovery.
|
|
3
|
+
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.
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
@@ -17,51 +17,50 @@ Ask three questions in order. The first `yes` determines the action.
|
|
|
17
17
|
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` entry?**
|
|
18
18
|
→ Patch (see § Patching below). Reset would destroy correct local state alongside the bad record.
|
|
19
19
|
|
|
20
|
-
When no question reaches `yes`, the state is probably recoverable per-step via `references/manual-setup.md`.
|
|
20
|
+
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.
|
|
21
21
|
|
|
22
22
|
---
|
|
23
23
|
|
|
24
|
-
## Full reset
|
|
24
|
+
## Full reset
|
|
25
25
|
|
|
26
26
|
### What it does
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
- Lists every tunnel on the account the cert authorises.
|
|
30
|
-
- Deletes every one of those tunnels via `cloudflared tunnel delete`.
|
|
31
|
-
- Removes `${HOME}/.${BRAND}/cloudflared/` entirely.
|
|
28
|
+
The agent issues these commands in order via Bash, against the brand cert:
|
|
32
29
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- 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).
|
|
37
|
-
- 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.
|
|
38
|
-
- It does not switch accounts. If the bound account was wrong, a fresh `cloudflared tunnel login` from inside `setup-tunnel.sh` on the next run will pick a new one — but the operator must sign in with the correct account in the browser.
|
|
30
|
+
```
|
|
31
|
+
BRAND=$(jq -r .hostname ~/.<configDir>/brand.json)
|
|
32
|
+
CFG_DIR="${HOME}/.${BRAND}/cloudflared"
|
|
39
33
|
|
|
40
|
-
|
|
34
|
+
for t in $(cloudflared --origincert "${CFG_DIR}/cert.pem" tunnel list --output json | jq -r '.[].id'); do
|
|
35
|
+
cloudflared --origincert "${CFG_DIR}/cert.pem" tunnel delete -f "$t"
|
|
36
|
+
done
|
|
41
37
|
|
|
42
|
-
|
|
43
|
-
~/reset-tunnel.sh <brand>
|
|
38
|
+
rm -rf "${CFG_DIR}"
|
|
44
39
|
```
|
|
45
40
|
|
|
46
|
-
|
|
41
|
+
Then stop the brand's cloudflared user service so the now-deleted tunnel's connector exits:
|
|
47
42
|
|
|
48
43
|
```
|
|
49
|
-
|
|
44
|
+
systemctl --user stop "${BRAND}-cloudflared.service" 2>/dev/null || true
|
|
50
45
|
```
|
|
51
46
|
|
|
52
|
-
|
|
47
|
+
### What it does not do
|
|
48
|
+
|
|
49
|
+
- 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).
|
|
50
|
+
- 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.
|
|
51
|
+
- 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.
|
|
53
52
|
|
|
54
53
|
### After reset
|
|
55
54
|
|
|
56
|
-
|
|
55
|
+
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.
|
|
57
56
|
|
|
58
57
|
---
|
|
59
58
|
|
|
60
59
|
## Patching — targeted cleanup without reset
|
|
61
60
|
|
|
62
|
-
### Stop a token-mode connector
|
|
61
|
+
### Stop a token-mode connector
|
|
63
62
|
|
|
64
|
-
Token-mode tunnels are created by a central operator (e.g. Maxy-provided subdomains under `maxy.bot`). The connector runs with `--token <X>` and does not consult `cert.pem`, so
|
|
63
|
+
Token-mode tunnels are created by a central operator (e.g. Maxy-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:
|
|
65
64
|
|
|
66
65
|
```
|
|
67
66
|
pkill -f 'cloudflared.*tunnel run --token'
|
|
@@ -77,7 +76,7 @@ No lines with `--token` should remain. If multiple token-mode connectors are run
|
|
|
77
76
|
|
|
78
77
|
### Delete a stray CNAME that survived a tunnel delete
|
|
79
78
|
|
|
80
|
-
When
|
|
79
|
+
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:
|
|
81
80
|
|
|
82
81
|
1. See `references/dashboard-guide.md` § "Delete a stray CNAME" for the click-path.
|
|
83
82
|
2. Verify the CNAME's target is the tunnel you just deleted (not a live one).
|
|
@@ -87,16 +86,18 @@ When the operator is unsure which records are stray, have them list the zone's C
|
|
|
87
86
|
|
|
88
87
|
### Remove a rogue entry from `alias-domains.json`
|
|
89
88
|
|
|
90
|
-
`alias-domains.json` controls which hostnames the platform UI classifies as public (serving the public agent) rather than admin.
|
|
89
|
+
`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.
|
|
90
|
+
|
|
91
|
+
A prior setup flow may have written an `admin.*` hostname into this file by mistake. Remove it manually:
|
|
91
92
|
|
|
92
93
|
```
|
|
93
|
-
cat "${
|
|
94
|
+
cat "${HOME}/.${BRAND}/alias-domains.json"
|
|
94
95
|
```
|
|
95
96
|
|
|
96
97
|
If `admin.<yourdomain>` is listed, strip it:
|
|
97
98
|
|
|
98
99
|
```
|
|
99
|
-
jq --arg H "admin.<yourdomain>" 'map(select(. != $H))' "${
|
|
100
|
+
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"
|
|
100
101
|
```
|
|
101
102
|
|
|
102
103
|
Replace `<yourdomain>` with the actual domain. The platform UI watches the file and reloads without a restart.
|
|
@@ -109,10 +110,10 @@ If a hostname's CNAME is pointing at the wrong tunnel (e.g. after an account swi
|
|
|
109
110
|
cloudflared --origincert "${CFG_DIR}/cert.pem" tunnel route dns --overwrite-dns "${TUNNEL_ID}" <hostname>
|
|
110
111
|
```
|
|
111
112
|
|
|
112
|
-
This
|
|
113
|
+
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").
|
|
113
114
|
|
|
114
115
|
---
|
|
115
116
|
|
|
116
117
|
## Failure discipline
|
|
117
118
|
|
|
118
|
-
When
|
|
119
|
+
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.
|