@rubytech/create-maxy-code 0.1.299 → 0.1.300

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.
Files changed (151) hide show
  1. package/package.json +1 -1
  2. package/payload/platform/plugins/cloudflare/references/manual-setup.md +31 -16
  3. package/payload/platform/plugins/cloudflare/references/reset-guide.md +19 -1
  4. package/payload/platform/plugins/cloudflare/skills/cloudflare/SKILL.md +16 -3
  5. package/payload/platform/scripts/check-no-esm-require.mjs +4 -0
  6. package/payload/server/{chunk-7H4KMTMC.js → chunk-JHSF5VPE.js} +171 -95
  7. package/payload/server/maxy-edge.js +19 -1
  8. package/payload/server/public/assets/AdminShell-BFdBxg4P.js +1 -0
  9. package/payload/server/public/assets/{Checkbox-DHnspkuk.js → Checkbox-BZ1XbA1t.js} +1 -1
  10. package/payload/server/public/assets/{Transcript-9FlneAzM.js → Transcript-CaVix6W0.js} +1 -1
  11. package/payload/server/public/assets/admin-BK7xIr3o.js +1 -0
  12. package/payload/server/public/assets/{arc-CPOhuw1V.js → arc-DxSm8tli.js} +1 -1
  13. package/payload/server/public/assets/architecture-YZFGNWBL-6g9jRVgc.js +1 -0
  14. package/payload/server/public/assets/{architectureDiagram-Q4EWVU46-BaR37f-j.js → architectureDiagram-Q4EWVU46-7JSAh0vL.js} +1 -1
  15. package/payload/server/public/assets/audio-attachment-mime-CFyc3gpD.js +30 -0
  16. package/payload/server/public/assets/{blockDiagram-DXYQGD6D-Du7NMQfQ.js → blockDiagram-DXYQGD6D-DLoVmHKD.js} +1 -1
  17. package/payload/server/public/assets/{browser--bCCzvRh.js → browser-ClnGUKp-.js} +1 -1
  18. package/payload/server/public/assets/{c4Diagram-AHTNJAMY-F3GnFLbs.js → c4Diagram-AHTNJAMY-Nv1G0o9P.js} +1 -1
  19. package/payload/server/public/assets/channel-BgQLJanG.js +1 -0
  20. package/payload/server/public/assets/chat-ZTAhSoJV.js +1 -0
  21. package/payload/server/public/assets/{chunk-2KRD3SAO-SUv9UlAA.js → chunk-2KRD3SAO-BpqPMhoA.js} +1 -1
  22. package/payload/server/public/assets/chunk-336JU56O-DgXHlVU_.js +2 -0
  23. package/payload/server/public/assets/chunk-426QAEUC-Dr_XVuUq.js +1 -0
  24. package/payload/server/public/assets/{chunk-4BX2VUAB-D5iKMyHO.js → chunk-4BX2VUAB-nwyZSZH8.js} +1 -1
  25. package/payload/server/public/assets/{chunk-4TB4RGXK-BahbQ7-Z.js → chunk-4TB4RGXK-Tfxz9LHX.js} +1 -1
  26. package/payload/server/public/assets/{chunk-55IACEB6-DfF9myIx.js → chunk-55IACEB6-6FMJQGXG.js} +1 -1
  27. package/payload/server/public/assets/{chunk-5FUZZQ4R-jF6a9UVe.js → chunk-5FUZZQ4R-g2FgZF3D.js} +1 -1
  28. package/payload/server/public/assets/{chunk-5PVQY5BW-BOCI-roy.js → chunk-5PVQY5BW-DtLwXsPH.js} +1 -1
  29. package/payload/server/public/assets/{chunk-67CJDMHE-E5wGK0dP.js → chunk-67CJDMHE-K91CP5ZU.js} +1 -1
  30. package/payload/server/public/assets/{chunk-7N4EOEYR-CtT0GsUT.js → chunk-7N4EOEYR-BGpCQhqK.js} +1 -1
  31. package/payload/server/public/assets/{chunk-AA7GKIK3-CGUj8eo-.js → chunk-AA7GKIK3-jkDbInck.js} +1 -1
  32. package/payload/server/public/assets/{chunk-BSJP7CBP-OPAIjvSk.js → chunk-BSJP7CBP-mDosdzGs.js} +1 -1
  33. package/payload/server/public/assets/{chunk-CIAEETIT-S_D8FhIT.js → chunk-CIAEETIT-DhBxewpQ.js} +1 -1
  34. package/payload/server/public/assets/{chunk-EDXVE4YY-CkD4AyZi.js → chunk-EDXVE4YY-B7c-9Emg.js} +1 -1
  35. package/payload/server/public/assets/{chunk-ENJZ2VHE-CmoK-c5f.js → chunk-ENJZ2VHE-Cqhhncox.js} +1 -1
  36. package/payload/server/public/assets/{chunk-FMBD7UC4-DcyogdNf.js → chunk-FMBD7UC4-BpDq6wj1.js} +1 -1
  37. package/payload/server/public/assets/{chunk-FOC6F5B3-8iNobZdv.js → chunk-FOC6F5B3-Ds02-ktu.js} +1 -1
  38. package/payload/server/public/assets/{chunk-ICPOFSXX-DeGZE9pw.js → chunk-ICPOFSXX-BTX5POFr.js} +2 -2
  39. package/payload/server/public/assets/{chunk-K5T4RW27-CkoKFdgU.js → chunk-K5T4RW27-Du1PXXBU.js} +1 -1
  40. package/payload/server/public/assets/{chunk-KGLVRYIC-DyNP1efP.js → chunk-KGLVRYIC-siasOAf8.js} +1 -1
  41. package/payload/server/public/assets/{chunk-LIHQZDEY-WZPpo5Dd.js → chunk-LIHQZDEY-xf1rbZtf.js} +1 -1
  42. package/payload/server/public/assets/{chunk-ORNJ4GCN-Cr7E0-Df.js → chunk-ORNJ4GCN-DeTLQ_LD.js} +1 -1
  43. package/payload/server/public/assets/{chunk-OYMX7WX6-BPjxo1Oc.js → chunk-OYMX7WX6-DQ7_oVJn.js} +1 -1
  44. package/payload/server/public/assets/chunk-QZHKN3VN-DF4o9HRJ.js +1 -0
  45. package/payload/server/public/assets/{chunk-U2HBQHQK-CuDS7yVX.js → chunk-U2HBQHQK-CXmFUKgn.js} +1 -1
  46. package/payload/server/public/assets/{chunk-X2U36JSP-DEkde3xe.js → chunk-X2U36JSP-Bj8-8Wkz.js} +1 -1
  47. package/payload/server/public/assets/{chunk-XPW4576I-BwRCuKyu.js → chunk-XPW4576I-Con3TXqt.js} +1 -1
  48. package/payload/server/public/assets/{chunk-YZCP3GAM-DD9nUp-w.js → chunk-YZCP3GAM-Dx8BHGWf.js} +1 -1
  49. package/payload/server/public/assets/{chunk-ZZ45TVLE-BDH9b11G.js → chunk-ZZ45TVLE-Dp4Q5Y-f.js} +1 -1
  50. package/payload/server/public/assets/classDiagram-6PBFFD2Q-DAbPKU6u.js +1 -0
  51. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-BKuFQLcv.js +1 -0
  52. package/payload/server/public/assets/clone-DnNHGhNe.js +1 -0
  53. package/payload/server/public/assets/{cose-bilkent-S5V4N54A-CfeophtW.js → cose-bilkent-S5V4N54A-pZiCv69E.js} +1 -1
  54. package/payload/server/public/assets/{dagre-CVxEmku2.js → dagre-CLqhEgbL.js} +1 -1
  55. package/payload/server/public/assets/{dagre-KV5264BT-TOuVLdjq.js → dagre-KV5264BT-BCxE8iyk.js} +1 -1
  56. package/payload/server/public/assets/{data-B8Waqy4Q.js → data-DDjeQQ7e.js} +1 -1
  57. package/payload/server/public/assets/{diagram-5BDNPKRD-BJQnfJjx.js → diagram-5BDNPKRD-slUEdZzz.js} +1 -1
  58. package/payload/server/public/assets/{diagram-G4DWMVQ6-CqK94J29.js → diagram-G4DWMVQ6-DPcraMfu.js} +1 -1
  59. package/payload/server/public/assets/{diagram-MMDJMWI5-BCnATv2X.js → diagram-MMDJMWI5-DR6luhGK.js} +1 -1
  60. package/payload/server/public/assets/{diagram-TYMM5635-Cl3BUrVs.js → diagram-TYMM5635-Jap1B4wA.js} +1 -1
  61. package/payload/server/public/assets/{erDiagram-SMLLAGMA--Uw0kvib.js → erDiagram-SMLLAGMA-DJKUs2hO.js} +1 -1
  62. package/payload/server/public/assets/{file-download-Pk6cNtuB.js → file-download-DBMW0BNz.js} +1 -1
  63. package/payload/server/public/assets/{flatten-DtM0XAMt.js → flatten-Cl1Bc3dR.js} +1 -1
  64. package/payload/server/public/assets/{flowDiagram-DWJPFMVM-B_VGZIbJ.js → flowDiagram-DWJPFMVM-C8W-ZZ9W.js} +1 -1
  65. package/payload/server/public/assets/{ganttDiagram-T4ZO3ILL-D-vdf1dC.js → ganttDiagram-T4ZO3ILL-DYCX4Xu2.js} +1 -1
  66. package/payload/server/public/assets/gitGraph-7Q5UKJZL-g7mvu6IY.js +1 -0
  67. package/payload/server/public/assets/{gitGraphDiagram-UUTBAWPF-CpyAlvu3.js → gitGraphDiagram-UUTBAWPF-CZu_fpgS.js} +1 -1
  68. package/payload/server/public/assets/{graph-D-VQpkaS.js → graph-CR74qec3.js} +1 -1
  69. package/payload/server/public/assets/{graph-labels-cJvlyVxD.js → graph-labels-1Uz0ZBRd.js} +1 -1
  70. package/payload/server/public/assets/{graphlib-DaV4XyBp.js → graphlib-Bdp7TA4y.js} +1 -1
  71. package/payload/server/public/assets/info-OMHHGYJF-DZqJuIkB.js +1 -0
  72. package/payload/server/public/assets/infoDiagram-42DDH7IO-cGfGccHz.js +2 -0
  73. package/payload/server/public/assets/{isEmpty-D__iH1WQ.js → isEmpty-D1pGOD1J.js} +1 -1
  74. package/payload/server/public/assets/{ishikawaDiagram-UXIWVN3A-_4NvfwwU.js → ishikawaDiagram-UXIWVN3A-B7KfIX6z.js} +1 -1
  75. package/payload/server/public/assets/{journeyDiagram-VCZTEJTY-PJQ-tPWv.js → journeyDiagram-VCZTEJTY-CSDwBfhF.js} +1 -1
  76. package/payload/server/public/assets/{jsx-runtime-BjFkEGXb.css → jsx-runtime-BqGVUT1h.css} +1 -1
  77. package/payload/server/public/assets/{kanban-definition-6JOO6SKY-asCNlyH4.js → kanban-definition-6JOO6SKY-CWzcEKTr.js} +1 -1
  78. package/payload/server/public/assets/{line-BSXr2IPU.js → line-BNQSlbYn.js} +1 -1
  79. package/payload/server/public/assets/{linear-CSAKvvko.js → linear-BN6kGN93.js} +1 -1
  80. package/payload/server/public/assets/mermaid-parser.core-CcFhkElq.js +4 -0
  81. package/payload/server/public/assets/mermaid.core-BsIeJ7Cc.js +11 -0
  82. package/payload/server/public/assets/{mindmap-definition-QFDTVHPH-IxwnMT2Q.js → mindmap-definition-QFDTVHPH-CQVzj6D_.js} +1 -1
  83. package/payload/server/public/assets/operator-QP_z97s8.js +1 -0
  84. package/payload/server/public/assets/{ordinal-BRQ5CzZY.js → ordinal-CBZeH4Yp.js} +1 -1
  85. package/payload/server/public/assets/packet-4T2RLAQJ-Hn8rlHpy.js +1 -0
  86. package/payload/server/public/assets/{page-CFgywgGE.js → page-CLijftr1.js} +1 -1
  87. package/payload/server/public/assets/pie-ZZUOXDRM-BPGWVqBm.js +1 -0
  88. package/payload/server/public/assets/{pieDiagram-DEJITSTG-DFqSnPPB.js → pieDiagram-DEJITSTG-DnrQ9fyn.js} +1 -1
  89. package/payload/server/public/assets/preload-helper-CQ36nxok.js +1 -0
  90. package/payload/server/public/assets/public-W-HholkU.js +6 -0
  91. package/payload/server/public/assets/{quadrantDiagram-34T5L4WZ-1AB3hlEb.js → quadrantDiagram-34T5L4WZ-BQWlCyWi.js} +1 -1
  92. package/payload/server/public/assets/radar-PYXPWWZC-DMdLrj3E.js +1 -0
  93. package/payload/server/public/assets/{reduce-CkcXOmmJ.js → reduce-PuPlFPRX.js} +1 -1
  94. package/payload/server/public/assets/{requirementDiagram-MS252O5E-C2Zpzq3l.js → requirementDiagram-MS252O5E-Du_vZhU1.js} +1 -1
  95. package/payload/server/public/assets/{sankeyDiagram-XADWPNL6-DkmhPhwS.js → sankeyDiagram-XADWPNL6-Cf6Z8GPQ.js} +1 -1
  96. package/payload/server/public/assets/{sequenceDiagram-FGHM5R23-1hQLpeZ1.js → sequenceDiagram-FGHM5R23-CS8GVol8.js} +1 -1
  97. package/payload/server/public/assets/{stateDiagram-FHFEXIEX-CBHIwm3f.js → stateDiagram-FHFEXIEX-Bv1NW2F1.js} +1 -1
  98. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-BGUCxPmp.js +1 -0
  99. package/payload/server/public/assets/{timeline-definition-GMOUNBTQ-CLRWWipW.js → timeline-definition-GMOUNBTQ-ClGYAsr0.js} +1 -1
  100. package/payload/server/public/assets/treeView-SZITEDCU-CWxofUbI.js +1 -0
  101. package/payload/server/public/assets/treemap-W4RFUUIX-Cfgb_ZG0.js +1 -0
  102. package/payload/server/public/assets/{useSelectionMode-6fff2UIV.js → useSelectionMode-geYq13zs.js} +1 -1
  103. package/payload/server/public/assets/{vennDiagram-DHZGUBPP-ByFoQMek.js → vennDiagram-DHZGUBPP-C2wTcxQT.js} +1 -1
  104. package/payload/server/public/assets/wardley-RL74JXVD-B45olYjj.js +1 -0
  105. package/payload/server/public/assets/{wardleyDiagram-NUSXRM2D-DIkXRSha.js → wardleyDiagram-NUSXRM2D-D86-ivEx.js} +1 -1
  106. package/payload/server/public/assets/{xychartDiagram-5P7HB3ND-BNdEcN73.js → xychartDiagram-5P7HB3ND-Ba5WAN8P.js} +1 -1
  107. package/payload/server/public/browser.html +6 -6
  108. package/payload/server/public/chat.html +9 -8
  109. package/payload/server/public/data.html +5 -5
  110. package/payload/server/public/graph.html +8 -8
  111. package/payload/server/public/index.html +9 -9
  112. package/payload/server/public/operator.html +9 -9
  113. package/payload/server/public/public.html +7 -6
  114. package/payload/server/server.js +117 -122
  115. package/payload/server/public/assets/AdminShell-Eo1RXZxq.js +0 -1
  116. package/payload/server/public/assets/admin-ORJGvOqo.js +0 -1
  117. package/payload/server/public/assets/architecture-YZFGNWBL-CQHotPO2.js +0 -1
  118. package/payload/server/public/assets/audio-attachment-mime-CFRERhV0.js +0 -1
  119. package/payload/server/public/assets/channel-DfO_IgSU.js +0 -1
  120. package/payload/server/public/assets/chat-DIrzZjdO.js +0 -1
  121. package/payload/server/public/assets/chunk-336JU56O-DHUrM3QG.js +0 -2
  122. package/payload/server/public/assets/chunk-426QAEUC-CBo-Lx7X.js +0 -1
  123. package/payload/server/public/assets/chunk-QZHKN3VN-CNlL2tiW.js +0 -1
  124. package/payload/server/public/assets/classDiagram-6PBFFD2Q-Bo-iLGpk.js +0 -1
  125. package/payload/server/public/assets/classDiagram-v2-HSJHXN6E-Bz7DNpvQ.js +0 -1
  126. package/payload/server/public/assets/clone-Bh_bM_44.js +0 -1
  127. package/payload/server/public/assets/gitGraph-7Q5UKJZL-Ds2hoRX4.js +0 -1
  128. package/payload/server/public/assets/info-OMHHGYJF-BuX4gDTt.js +0 -1
  129. package/payload/server/public/assets/infoDiagram-42DDH7IO-pcFw8-8F.js +0 -2
  130. package/payload/server/public/assets/mermaid-parser.core-DFxv-h16.js +0 -4
  131. package/payload/server/public/assets/mermaid.core-Be34BFtA.js +0 -11
  132. package/payload/server/public/assets/operator-DyLRFmNE.js +0 -1
  133. package/payload/server/public/assets/packet-4T2RLAQJ-9qQ2zAJ3.js +0 -1
  134. package/payload/server/public/assets/pie-ZZUOXDRM-aDbmozld.js +0 -1
  135. package/payload/server/public/assets/public-_pD6yEQ6.js +0 -35
  136. package/payload/server/public/assets/radar-PYXPWWZC-CEm-iLey.js +0 -1
  137. package/payload/server/public/assets/stateDiagram-v2-QKLJ7IA2-DHGLsXgi.js +0 -1
  138. package/payload/server/public/assets/treeView-SZITEDCU-DYa2gPJa.js +0 -1
  139. package/payload/server/public/assets/treemap-W4RFUUIX-D816ED1y.js +0 -1
  140. package/payload/server/public/assets/wardley-RL74JXVD-BniDWDlr.js +0 -1
  141. /package/payload/server/public/assets/{_baseFor-SRtUHe7G.js → _baseFor-brRjpUOX.js} +0 -0
  142. /package/payload/server/public/assets/{array-BpsnM-Py.js → array-BGFCBI0e.js} +0 -0
  143. /package/payload/server/public/assets/{cytoscape.esm-D4Rl1H5h.js → cytoscape.esm-TvRJ2tGX.js} +0 -0
  144. /package/payload/server/public/assets/{defaultLocale-C7sQPl-O.js → defaultLocale-CRZydyG6.js} +0 -0
  145. /package/payload/server/public/assets/{dist-BXJR8AkG.js → dist-Dbh7HrZX.js} +0 -0
  146. /package/payload/server/public/assets/{init-BydxaDEV.js → init-B8gtcn7T.js} +0 -0
  147. /package/payload/server/public/assets/{jsx-runtime-B54kfC3e.js → jsx-runtime-D3JkFIbi.js} +0 -0
  148. /package/payload/server/public/assets/{katex-B0fVeDx4.js → katex-RxgSu_dy.js} +0 -0
  149. /package/payload/server/public/assets/{path-jlWYQ2i9.js → path-DZF-JdEe.js} +0 -0
  150. /package/payload/server/public/assets/{rough.esm-c4PR5shF.js → rough.esm-6CnTHTkH.js} +0 -0
  151. /package/payload/server/public/assets/{src-CPkr6SrY.js → src-BoaldmnP.js} +0 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rubytech/create-maxy-code",
3
- "version": "0.1.299",
3
+ "version": "0.1.300",
4
4
  "description": "Install Maxy — AI for Productive People",
5
5
  "bin": {
6
6
  "create-maxy-code": "./dist/index.js"
@@ -6,14 +6,15 @@ Prerequisites: `cloudflared` installed; the brand's VNC running on its own X dis
6
6
 
7
7
  ---
8
8
 
9
- ## Why two hostnames (admin and public)
9
+ ## Why several hostnames (admin, public, operator)
10
10
 
11
- Each Maxy installation exposes two distinct surfaces from the same device and the same local port. The platform UI distinguishes them by the inbound `Host:` header.
11
+ Each Maxy installation exposes distinct surfaces from the same device and the same local port. The platform UI distinguishes them by the inbound `Host:` header.
12
12
 
13
13
  - **admin hostname** — the operator's private chat + admin panel. PIN-protected. Only the owner uses it.
14
14
  - **public hostname** — the public-facing agent (customer chat, marketing agent, WhatsApp webhook target). Reachable by anyone on the internet.
15
+ - **operator hostname** (optional) — a trimmed admin dashboard (sessions, chat, data — no graph, no browser). Admin-gated like the admin host, recognised by membership in `operator-domains.json`. See § "Operator dashboard ingress".
15
16
 
16
- Two tunnel hostnames, one connector, one local service. The tunnel's `ingress:` block routes each hostname to `http://localhost:${PORT}`; the platform UI handles the rest.
17
+ Several tunnel hostnames, one connector, one local service. The tunnel's `ingress:` block routes each hostname to `http://localhost:${PORT}`; the platform UI classifies the host (operator → public → admin) and serves the matching surface.
17
18
 
18
19
  ---
19
20
 
@@ -596,36 +597,50 @@ systemctl --user restart "cloudflared-${BRAND}.service"
596
597
 
597
598
  ---
598
599
 
599
- ## Operator dashboard ingress (`operator.<host>`)
600
+ ## Operator dashboard ingress (operator host)
600
601
 
601
- Task 822 adds a trimmed operator dashboard served on `operator.<BRAND_HOSTNAME>`. Unlike a prototype surface, it routes to the **same brand UI port** as the admin host (`${MAXY_UI_PORT}`) and is gated by the **same admin gate** (Cloudflare Access / remote-auth + PIN + Claude OAuth) — it is not a public host. A request to `operator.<host>` that is unauthenticated hits the identical login flow as the bare admin host; the UI server classifies the host and serves the operator shell (sessions, chat, WhatsApp reader, data — no graph, no browser).
602
+ The operator dashboard is a trimmed admin surface (sessions, chat, WhatsApp reader, data no graph, no browser). It routes to the **same brand UI port** as the admin host (`${MAXY_UI_PORT}`) and is gated by the **same admin gate** (Cloudflare Access / remote-auth + PIN + Claude OAuth) — it is not a public host. A request to the operator host that is unauthenticated hits the identical login flow as the bare admin host; the UI server then classifies the host and serves the operator shell.
602
603
 
603
- Add it with the same Multi-ingress flow above, using `SURFACE=operator` and `PORT=${MAXY_UI_PORT}` (the brand UI port, not a separate dev server):
604
+ **The operator hostname is arbitrary** — any FQDN the operator chooses (e.g. `glsops.siteoffice.online`). Task 829 removed the `operator.` prefix rule: the UI classifies a host as operator **only** when the host is listed in `~/.${BRAND}/operator-domains.json`. Provisioning therefore has two scripted parts — the tunnel ingress (so the host reaches the brand UI) and the operator-domains.json write (so the UI serves the operator shell instead of the full admin shell).
605
+
606
+ Take the operator FQDN from chat:
604
607
 
605
608
  ```
606
- SURFACE="operator"
607
- PORT="${MAXY_UI_PORT}" # the brand UI port — same service as admin/public
608
- HOSTNAME="operator.${BRAND_HOSTNAME}"
609
+ OPERATOR_HOSTNAME="glsops.siteoffice.online" # the operator's chosen FQDN
610
+ PORT="${MAXY_UI_PORT}" # the brand UI port — same service as admin/public
609
611
  ```
610
612
 
611
- Then run the awk append (step 1), validate (step 2), create the DNS row (step 3), and reload the connector (step 4) exactly as documented above. After the edit the ingress block carries the operator row before the catch-all:
613
+ ### 1. Ingress + DNS
614
+
615
+ Run the Multi-ingress flow above with `HOSTNAME="${OPERATOR_HOSTNAME}"` and `PORT="${MAXY_UI_PORT}"`: the awk append (step 1), validate (step 2), create the DNS row (step 3), reload the connector (step 4). After the edit the ingress block carries the operator row before the catch-all (hostnames are illustrative — yours are whatever the operator registered):
612
616
 
613
617
  ```yaml
614
618
  ingress:
615
- - hostname: admin.<BRAND_HOSTNAME>
619
+ - hostname: glsmith.siteoffice.online # admin (primary)
616
620
  service: http://localhost:<MAXY_UI_PORT>
617
- - hostname: public.<BRAND_HOSTNAME>
621
+ - hostname: gls.siteoffice.online # public (also in alias-domains.json)
618
622
  service: http://localhost:<MAXY_UI_PORT>
619
- - hostname: operator.<BRAND_HOSTNAME>
623
+ - hostname: glsops.siteoffice.online # operator (also in operator-domains.json)
620
624
  service: http://localhost:<MAXY_UI_PORT>
621
625
  - service: http_status:404
622
626
  ```
623
627
 
624
- When a provisioning script adds this row it logs `[tunnel-install] operator-ingress added host=operator.<BRAND_HOSTNAME> :${MAXY_UI_PORT}`; the absence of an `operator.` ingress on a brand that should have one is the failure signature.
628
+ ### 2. Classify the host as operator
629
+
630
+ The ingress only gets the request to the brand UI; without this write the UI serves the **full admin shell** on the operator host. Append the hostname to `operator-domains.json` at the **brand root** (not `${CFG_DIR}` — the platform UI watches the brand root and hot-reloads within ~2 s, no restart):
631
+
632
+ ```
633
+ F="${HOME}/.${BRAND}/operator-domains.json"
634
+ [ -f "$F" ] || echo '[]' > "$F"
635
+ jq --arg H "${OPERATOR_HOSTNAME}" 'if index($H) then . else . + [$H] end' "$F" > /tmp/op.json && mv /tmp/op.json "$F"
636
+ cat "$F"
637
+ ```
638
+
639
+ The UI logs `[operator-domains] reloaded <n> operator domain(s): …` within ~2 s of the write. A configured brand whose boot/reload log shows `0 operator domain(s)` while an operator host is expected means the file was written to the wrong path (e.g. under `cloudflared/`).
625
640
 
626
- **Verification.** `curl -I https://operator.<BRAND_HOSTNAME>` returns the admin login (not a 404, not the public agent page). After login the dashboard shows the sessions list and WhatsApp reader, the header menu carries only Dashboard / Chat / Data, and `https://operator.<BRAND_HOSTNAME>/graph` returns 404.
641
+ **Verification.** `curl -I https://${OPERATOR_HOSTNAME}` returns the admin login (not a 404, not the public agent page). After login the dashboard shows the sessions list and WhatsApp reader, the header menu carries only Dashboard / Chat / Data, and `https://${OPERATOR_HOSTNAME}/graph` returns 404. The standing serve signal is `[host-class] host=${OPERATOR_HOSTNAME} class=operator served=operator.html`; `class=admin served=index.html` for a configured operator host is the full-shell leak signature (the operator-domains.json write was missed or written to the wrong path).
627
642
 
628
- > Installer auto-provisioning of this row is out of scope for Task 822 (documented manual/scripted add only); a follow-up task owns wiring it into the installer.
643
+ > Installer auto-provisioning of this host is out of scope here (documented manual/scripted add only); Task 825 owns wiring it into the installer.
629
644
 
630
645
  ---
631
646
 
@@ -14,7 +14,7 @@ Ask three questions in order. The first `yes` determines the action.
14
14
  2. **Is `config.yml` referencing a tunnel UUID that no longer exists on the account, or a hostname that the account no longer owns?**
15
15
  → Full reset. The tunnel the config names is unreachable; recovering per-step is slower than starting clean.
16
16
 
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?**
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` / `operator-domains.json` entry?**
18
18
  → Patch (see § Patching below). Reset would destroy correct local state alongside the bad record.
19
19
 
20
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.
@@ -102,6 +102,24 @@ jq --arg H "admin.<yourdomain>" 'map(select(. != $H))' "${HOME}/.${BRAND}/alias-
102
102
 
103
103
  Replace `<yourdomain>` with the actual domain. The platform UI watches the file and reloads without a restart.
104
104
 
105
+ ### Remove a rogue entry from `operator-domains.json`
106
+
107
+ `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.
108
+
109
+ 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:
110
+
111
+ ```
112
+ cat "${HOME}/.${BRAND}/operator-domains.json"
113
+ ```
114
+
115
+ If the wrong host is listed, strip it:
116
+
117
+ ```
118
+ 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"
119
+ ```
120
+
121
+ The platform UI watches the file and reloads without a restart; the next request to the host routes to its corrected surface.
122
+
105
123
  ### Fix a single wrong DNS record without tearing everything down
106
124
 
107
125
  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:
@@ -36,7 +36,19 @@ Every Cloudflare operation proves itself with a live behavioural signal surfaced
36
36
 
37
37
  ## Inputs (tunnel)
38
38
 
39
- Four inputs come from the operator in chat: the admin FQDN, optional public FQDN, optional apex FQDN, and the admin password. `BRAND` is derived from disk — never hardcoded — with `BRAND=$(jq -r .hostname ~/.<configDir>/brand.json)`. The same install can host different brands on different ports; `brand.json.hostname` is the only authoritative source.
39
+ Five inputs come from the operator in chat: the admin FQDN, optional public FQDN, optional operator-dashboard FQDN, optional apex FQDN, and the admin password. `BRAND` is derived from disk — never hardcoded — with `BRAND=$(jq -r .hostname ~/.<configDir>/brand.json)`. The same install can host different brands on different ports; `brand.json.hostname` is the only authoritative source.
40
+
41
+ ## Host classes — three peers on one brand UI port
42
+
43
+ A brand serves three peer host classes, all routed to the **same brand UI port** (`${MAXY_UI_PORT}`); the platform UI classifies each request by its `Host` header. The hostnames are arbitrary — there is no required `admin.`/`public.`/`operator.` prefix.
44
+
45
+ | Class | How the UI recognises it | What it serves |
46
+ |---|---|---|
47
+ | **admin** (default) | the bare primary host — in neither domains file | full admin shell (chat, data, graph, browser) — admin-gated |
48
+ | **public** | host starts with `public.` **or** is listed in `~/.${BRAND}/alias-domains.json` | the public agent page only — no admin surfaces |
49
+ | **operator** | host is listed in `~/.${BRAND}/operator-domains.json` | the trimmed operator dashboard (sessions, chat, data — no graph, no browser) — admin-gated |
50
+
51
+ Precedence is operator → public → admin: a host listed in `operator-domains.json` is operator even if it also matches a public rule. Both domains files live at the **brand root** `~/.${BRAND}/` (not under `cloudflared/`) because the platform UI watches the brand root and hot-reloads within ~2 s. Provisioning admin needs no domains-file write; public writes `alias-domains.json`; operator writes `operator-domains.json`. See `references/manual-setup.md` § "Operator dashboard ingress".
40
52
 
41
53
  ## Execution
42
54
 
@@ -48,7 +60,8 @@ Tunnel mutations the agent performs from `references/manual-setup.md` (each one
48
60
  - `cloudflared tunnel --origincert <cert> create <name>` — produces `<tunnelId>.json` credentials.
49
61
  - Write `~/.${BRAND}/cloudflared/config.yml` (ingress block per the runbook).
50
62
  - `cloudflared tunnel --origincert <cert> route dns <tunnelId> <hostname>` — one call per non-apex hostname.
51
- - For non-admin hostnames not starting with `public.`, append to `~/.${BRAND}/alias-domains.json` so `isPublicHost()` treats them as public.
63
+ - For a public hostname not starting with `public.`, append it to `~/.${BRAND}/alias-domains.json` so `isPublicHost()` treats it as public.
64
+ - For an operator-dashboard hostname, append it to `~/.${BRAND}/operator-domains.json` so the UI serves the trimmed operator shell (admin-gated). See `references/manual-setup.md` § "Operator dashboard ingress".
52
65
  - Install + start the `${BRAND}-cloudflared.service` user unit (`systemctl --user daemon-reload && systemctl --user enable --now ${BRAND}-cloudflared.service`).
53
66
 
54
67
  After the service is up, the agent runs `curl -I https://<admin-hostname>` and pastes the response. The setup-done claim only fires when a `200` line appears in that response.
@@ -67,7 +80,7 @@ When the operator's request touches Cloudflare, the agent's permitted actions ar
67
80
 
68
81
  - Invoke `cloudflared` via Bash, following `references/manual-setup.md`.
69
82
  - Invoke `wrangler` and the Cloudflare API via Bash, following `references/api.md`, `references/hosting-sites.md`, and `references/d1-data-capture.md`, using the reused per-scope narrow token (minted once if absent, then reused).
70
- - Write `config.yml` and `alias-domains.json` per the runbook.
83
+ - Write `config.yml`, `alias-domains.json`, and `operator-domains.json` per the runbook.
71
84
  - Install and start the brand's cloudflared user service.
72
85
  - Quote `references/manual-setup.md`, `references/reset-guide.md`, or `references/dashboard-guide.md` verbatim when a dashboard click-path is the right tool.
73
86
  - Verify reachability via `curl -I https://<hostname>` and surface the response.
@@ -51,6 +51,10 @@ const ALLOWLIST = new Set([
51
51
  // (server-init-line-stamper.cjs); the test loads it via the sanctioned
52
52
  // createRequire(import.meta.url) ESM→CJS bridge, not a raw require.
53
53
  'platform/ui/app/lib/__tests__/server-init-line-stamper.test.ts',
54
+ // Same vi.hoisted pattern: Task 829's operator-domains test builds a tmpdir
55
+ // before the module's `../paths` mock fires, to redirect MAXY_DIR off the
56
+ // operator's real home config.
57
+ 'platform/ui/app/lib/__tests__/operator-domains.test.ts',
54
58
  ])
55
59
 
56
60
  const REQUIRE_CALL_RE = /\brequire\s*\(/