dodo-mcp 1.0.1 → 1.0.3

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 (49) hide show
  1. package/README.md +49 -19
  2. package/dist/cli/main.js +108 -43
  3. package/dist/cli/main.js.map +1 -1
  4. package/dist/cli/menu.js +5 -6
  5. package/dist/cli/menu.js.map +1 -1
  6. package/dist/config/globalConfig.js +23 -1
  7. package/dist/config/globalConfig.js.map +1 -1
  8. package/dist/config/stateImport.js +2 -2
  9. package/dist/config/stateImport.js.map +1 -1
  10. package/dist/config/tunnelConfig.js +2 -3
  11. package/dist/config/tunnelConfig.js.map +1 -1
  12. package/dist/server/appServer.js +89 -9
  13. package/dist/server/appServer.js.map +1 -1
  14. package/dist/server/configUi/app.js +54 -65
  15. package/dist/server/configUi/index.html +21 -22
  16. package/dist/server/configUi/workbench.js +7 -4
  17. package/dist/server/ipcDispatch.js +25 -0
  18. package/dist/server/ipcDispatch.js.map +1 -1
  19. package/dist/server/localConfig.js +50 -63
  20. package/dist/server/localConfig.js.map +1 -1
  21. package/dist/server/remoteConfig.js +317 -0
  22. package/dist/server/remoteConfig.js.map +1 -0
  23. package/dist/tunnel/control.js +4 -6
  24. package/dist/tunnel/control.js.map +1 -1
  25. package/dist/tunnel/credentials.js +5 -19
  26. package/dist/tunnel/credentials.js.map +1 -1
  27. package/dist/tunnel/runtime.js +3 -4
  28. package/dist/tunnel/runtime.js.map +1 -1
  29. package/dist/tunnel/supervisor.js +8 -10
  30. package/dist/tunnel/supervisor.js.map +1 -1
  31. package/docs/AI_PROVIDERS.md +1 -1
  32. package/docs/ARCHITECTURE.md +29 -14
  33. package/docs/COMPATIBILITY.md +7 -3
  34. package/docs/MANUAL_ACCEPTANCE.md +39 -10
  35. package/docs/RELEASE_1.0.0.md +3 -0
  36. package/docs/RELEASE_1.0.2.md +46 -0
  37. package/docs/RELEASE_1.0.3.md +57 -0
  38. package/docs/RELEASE_NOTES.md +37 -0
  39. package/docs/SECURITY.md +39 -13
  40. package/docs/TEST_REPORT.md +61 -2
  41. package/docs/TUNNEL.md +114 -53
  42. package/docs/WINDOWS.md +1 -1
  43. package/docs/adr/031-owner-selected-tunnel-supervision.md +8 -2
  44. package/docs/adr/044-global-launcher-and-cli-menu.md +3 -3
  45. package/docs/adr/047-temporary-remote-config.md +47 -0
  46. package/docs/adr/048-persistent-connection-mode.md +58 -0
  47. package/docs/adr/README.md +2 -0
  48. package/package.json +3 -1
  49. package/schemas/global-config.schema.json +6 -12
package/docs/TUNNEL.md CHANGED
@@ -1,86 +1,147 @@
1
- # DODO MCP — Tunnel Guide
1
+ # DODO MCP — Local และ Cloudflare Tunnel
2
2
 
3
- DODO เปิด MCP และ OAuth บน local loopback ผู้ใช้เป็นผู้สร้าง Cloudflare remotely-managed Tunnel, public hostname และ DNS เอง DODO supervise เฉพาะ process `cloudflared` ที่เริ่มในรอบปัจจุบันและไม่ติดตั้งเป็น system service
3
+ DODO มีโหมดเชื่อมต่อระดับ installation เพียงหนึ่งโหมดในแต่ละเวลา:
4
4
 
5
- ## Local endpoints
5
+ - `local` — ใช้ MCP ที่ `http://127.0.0.1:21730/mcp` และไม่เริ่ม `cloudflared`
6
+ - `tunnel` — ใช้ public HTTPS origin เป็น MCP URL หลัก และ DODO เริ่ม/หยุด
7
+ `cloudflared` ของตัวเองพร้อมทุก `dodo start`
6
8
 
7
- - MCP + OAuth: `http://127.0.0.1:21730`
8
- - MCP endpoint: `http://127.0.0.1:21730/mcp`
9
- - Local Config: `http://127.0.0.1:21731`
10
- - managed readiness/metrics: `http://127.0.0.1:21732` โดยค่าเริ่มต้น
9
+ การเลือกจะถูกบันทึกใน private global config และไม่มี fallback อัตโนมัติ หากเลือก
10
+ `tunnel` แล้ว credential, executable หรือ readiness ไม่พร้อม การเริ่ม DODO จะล้มเหลว
11
+ พร้อมคำอธิบาย โดยไม่เปลี่ยนไปเปิด local แทน
11
12
 
12
- Tunnel ต้อง route health, discovery, OAuth และ MCP ทุก path ไปยัง listener `21730` เดียวกัน ห้าม route Local Config `21731`, metrics `21732`, private IPC หรือ debug endpoint ออก public
13
+ ## พอร์ตและขอบเขต
13
14
 
14
- ## Public origin
15
+ | บริการ | ค่าเริ่มต้น | การเปิดเผย |
16
+ |---|---:|---|
17
+ | MCP + OAuth upstream | `127.0.0.1:21730` | Local เท่านั้น; Tunnel route มาที่พอร์ตนี้ |
18
+ | Local Config | `127.0.0.1:21731` | Loopback เท่านั้นเสมอ |
19
+ | Tunnel readiness/metrics | `127.0.0.1:21732` | Loopback เท่านั้นเสมอ |
20
+
21
+ Cloudflare public hostname ต้อง route **ทุก path** มาที่
22
+ `http://127.0.0.1:21730` เพื่อให้ health, OAuth discovery, authorization และ `/mcp`
23
+ ทำงานครบ ห้าม route พอร์ต 21731, 21732, private IPC หรือ debug endpoint ออก public
24
+
25
+ แม้โหมด Tunnel ยังต้องมี listener 21730 เป็น private upstream ให้ `cloudflared`
26
+ แต่ DODO จะ advertise public HTTPS URL เป็น endpoint ที่มีผล ส่วน Local Config ยังคง
27
+ เข้าผ่าน loopback ยกเว้น bounded `/config` lease ที่เจ้าของเปิดชั่วคราวเอง
28
+
29
+ ## เลือก Local
15
30
 
16
31
  ```bash
17
- dodo init --public-url https://mcp.example.com
18
- dodo start --root /path/to/project
32
+ dodo tunnel configure --local
33
+ dodo start
19
34
  ```
20
35
 
21
- ## เปิดพร้อม DODO ด้วย token ชั่วคราว
36
+ ตรวจค่าที่ใช้อยู่:
22
37
 
23
38
  ```bash
24
- dodo setup --check --components cloudflared
25
- dodo init --public-url https://mcp.example.com
26
- dodo start --root /path/to/project
27
- # Cloudflare Tunnel token (temporary; Enter = local only):
39
+ dodo tunnel status
28
40
  ```
29
41
 
30
- `dodo setup --yes` ใช้ component `all` เป็นค่าเริ่มต้นและรวม `cloudflared` บน macOS
31
- หรือเลือกจากเมนู `dodo --cli` ข้อ “ติดตั้ง/ตรวจ dependencies ทั้งหมด” ได้
42
+ Local mode ไม่ใช้ public origin และไม่เริ่ม Tunnel หากต้องการกลับไป Tunnel ต้องเลือก
43
+ ใหม่อย่างชัดเจนด้วยคำสั่งหรือหน้า Local Config แล้ว restart DODO
32
44
 
33
- กรอก Tunnel token ที่ Cloudflare ออกให้สำหรับ remotely-managed Tunnel Token จะอยู่
34
- เฉพาะใน DODO process และ environment ของ child `cloudflared` ระหว่างรอบ เมื่อ DODO
35
- หยุด child จะหยุดตาม กด Enter โดยไม่กรอกหรือใช้คำสั่งต่อไปนี้เพื่อเปิด local MCP เท่านั้น:
45
+ ## เลือก DODO Tunnel
46
+
47
+ เจ้าของต้องสร้าง remotely-managed Tunnel, public hostname และ DNS ใน Cloudflare ก่อน
48
+ DODO ไม่สร้าง/ลบ Tunnel, เปลี่ยน DNS, เปิด firewall หรือติดตั้ง system service
49
+
50
+ บน macOS ตรวจหรือติดตั้ง executable ผ่าน setup:
36
51
 
37
52
  ```bash
38
- dodo start --no-tunnel
53
+ dodo setup --check --components cloudflared
54
+ dodo setup --yes --components cloudflared
39
55
  ```
40
56
 
41
- หน้า Local Config มีช่อง **Temporary Tunnel token** สำหรับเริ่ม tunnel ใน process ที่
42
- เปิดอยู่ได้ทันที และมีปุ่มหยุดเฉพาะ child ที่ process นี้เป็นเจ้าของ ช่องถูกล้างหลังส่ง
43
- Backend ตอบ `tokenStored:false` และไม่เขียนค่าลง config หรือ credential store
57
+ จากนั้นเลือก Tunnel และบันทึก token ใน macOS Keychain ผ่าน hidden prompt:
58
+
59
+ ```bash
60
+ dodo tunnel configure \
61
+ --tunnel \
62
+ --public-url https://mcp.example.com \
63
+ --os-credential
64
+
65
+ dodo start
66
+ ```
44
67
 
45
- คำสั่งตรวจสถานะที่ไม่มี secret:
68
+ Windows ใช้ Credential Manager และ Linux ใช้ Secret Service เมื่อเลือก
69
+ `--os-credential` สำหรับ headless environment สามารถใช้ owner-controlled reference:
46
70
 
47
71
  ```bash
48
- dodo tunnel status
49
- dodo tunnel doctor
50
- dodo tunnel logs --lines 100
51
- dodo tunnel stop
72
+ dodo tunnel configure --tunnel --public-url https://mcp.example.com --token-env DODO_OWNER_TUNNEL_TOKEN
73
+ # หรือไฟล์ private regular file ที่เป็น absolute path
74
+ dodo tunnel configure --tunnel --public-url https://mcp.example.com --token-file /private/path/tunnel-token
52
75
  ```
53
76
 
54
- `status` ใช้ authenticated owner IPC และไม่อ่าน credential ส่วน `doctor` เป็นคำสั่งตรวจแบบ explicit: ตรวจ executable, credential availability, local `/healthz`, managed `/ready` และ public `/healthz` แยกกัน Public health ที่ผ่านไม่ได้แปลว่า AI client เชื่อมต่ออยู่
77
+ ค่า environment/file เป็น reference ที่เจ้าของดูแลเอง คำสั่งตรวจค่าและไฟล์ก่อนบันทึก
78
+ แต่ไม่คัดลอก token เข้า config เมื่อใช้หน้า Local Config token จะเป็น write-only และ
79
+ ถูกส่งตรงไปยัง OS credential store; หลังบันทึก UI แสดงเพียงว่ามี credential
55
80
 
56
- ## ขอบเขตของ token
81
+ ทุก `dodo start` ใน Tunnel mode จะอ่าน credential จาก reference เริ่ม `cloudflared`
82
+ ผ่าน child environment และรอ readiness ก่อนถือว่า startup สำเร็จ Token ไม่อยู่ใน
83
+ CLI argument, config JSON, log, audit, MCP response, browser storage หรือ environment
84
+ ของ MCP jobs เมื่อ DODO หยุด child ที่ process นี้เป็นเจ้าของจะหยุดตาม
57
85
 
58
- DODO ไม่รับ token เป็น CLI argument และไม่ใส่ token ใน `cloudflared` argv เส้นทาง
59
- มาตรฐานไม่ใช้ macOS Keychain, Windows Credential Manager, Linux Secret Service,
60
- token file หรือ shell environment Token จาก terminal และ Local Config ถูกส่งผ่าน
61
- environment ของ child ที่สร้างเองเท่านั้น Tunnel diagnostics ถูกจำกัดขนาดและ redact
62
- ก่อนเขียนลง private state MCP jobs จะไม่ได้รับ `TUNNEL_TOKEN` หรือ
63
- `TUNNEL_TOKEN_FILE` แม้ owner จะใส่ชื่อไว้ใน environment allowlist
86
+ ## เปลี่ยนโหมดจากหน้าเว็บ
64
87
 
65
- คำสั่ง `dodo tunnel configure/start` และ credential reference รุ่นเดิมยังคงอยู่เพื่อ
66
- advanced/headless compatibility แต่ไม่ถูกเรียกโดย `dodo start`, `dodo --cli` หรือ
67
- Local Config และต้องเกิดจากคำสั่ง owner โดยตรง
88
+ เปิด Local Config จาก URL ที่ DODO แสดงบนเครื่องเจ้าของ แล้วใช้ส่วน
89
+ **การเชื่อมต่อ MCP**:
68
90
 
69
- runtime ใช้ bounded restart หลัง `cloudflared` จบ retry ของตัวเอง และ `stop` ส่งสัญญาณเฉพาะ live child handle ที่ supervisor เป็นผู้สร้าง ไม่มีการ kill saved PID หรือ process ชื่อเหมือนกัน
91
+ 1. ตั้ง Public origin เป็น HTTPS origin ของ Tunnel
92
+ 2. เลือก `DODO Tunnel`
93
+ 3. ใส่ Cloudflare Tunnel token หากยังไม่มี credential ที่บันทึกไว้
94
+ 4. กดบันทึกและ restart DODO
70
95
 
71
- ## Client setup
96
+ หรือเลือก `Local` แล้วบันทึกและ restart ค่าในหน้าเว็บไม่เปลี่ยน endpoint ของ process
97
+ ที่กำลังรันอยู่ทันที หน้าเว็บจะแสดงสถานะ restart ที่ตรงกับ runtime จริง
72
98
 
73
- 1. ใช้ `https://mcp.example.com/mcp` ใน MCP client
74
- 2. เลือก OAuth
75
- 3. คัดลอก exact callback จาก client
76
- 4. ลงทะเบียนด้วย `dodo auth add-client --redirect-uri ...`
77
- 5. ทำ browser authorization และ approve interaction จาก terminal
78
- 6. scan tools และตรวจ surface ที่ client รายงาน
99
+ ## Remote Config ไม่เกินหนึ่งชั่วโมง
79
100
 
80
- ## หลายโปรเจกต์
101
+ Remote Config ใช้ได้เฉพาะเมื่อเลือก Tunnel และ DODO-owned Tunnel กำลังรันอยู่:
81
102
 
82
- หนึ่ง DODO process มี active workspace เดียว ถ้าต้องการทำงานพร้อมกันหลายโปรเจกต์ ให้เปิดหลาย DODO processes ด้วย port, public origin และ config directory ที่แยกกัน แต่ละ managed tunnel ต้องใช้ metrics port ที่ไม่ซ้ำ ระบบจะไม่ให้ process หนึ่ง takeover root ที่อีก process กำลัง serve
103
+ ```bash
104
+ dodo --web
105
+ # Remote Config: https://mcp.example.com/config
106
+ # Pairing code (shown once): XXXX-XXXX-XXXX-XXXX
107
+
108
+ dodo web --status
109
+ dodo web --close
110
+ ```
111
+
112
+ `/config` ตอบ 404 ตามค่าเริ่มต้น คำสั่ง `dodo --web` เปิดหรือเปลี่ยน lease ผ่าน
113
+ authenticated private IPC โดยไม่ restart MCP และไม่รับ Tunnel token ผ่าน IPC URL ไม่มี
114
+ query/fragment secret Pairing code มีอายุสั้นและใช้ได้ครั้งเดียว หลังจับคู่ browser ได้
115
+ cookie แบบ `Secure`, `HttpOnly`, `SameSite=Strict`, `Path=/config` Lease ปิดเองภายใน
116
+ หนึ่งชั่วโมง การเปิดใหม่ยกเลิก code/session เดิม
117
+
118
+ Remote Config proxy ไปยัง Local Config loopback โดยคง Host/Origin/proxy-header checks,
119
+ private capability, rate limit, workspace ID/epoch และ owner policy เดิม การปิด lease
120
+ ไม่หยุด MCP หรือ Tunnel และไม่มี MCP tool สำหรับเปิดหน้า owner นี้
83
121
 
84
- ## Security
122
+ ## ตรวจสถานะและแก้ปัญหา
123
+
124
+ ```bash
125
+ dodo tunnel status
126
+ dodo tunnel doctor
127
+ dodo tunnel logs --lines 100
128
+ dodo tunnel stop
129
+ ```
130
+
131
+ `status` ไม่อ่าน credential ส่วน `doctor` ตรวจ executable, credential, local health,
132
+ Tunnel readiness และ public health แยกกัน Public health ที่ผ่านพิสูจน์เพียงว่า origin
133
+ ตอบ DODO health ไม่ได้พิสูจน์ว่า AI client เชื่อมต่อแล้ว `stop` ส่งสัญญาณเฉพาะ live
134
+ child handle ที่ supervisor ปัจจุบันสร้าง ไม่ค้นหรือ kill process ตามชื่อ/PID เก่า
135
+
136
+ ## ตั้งค่า MCP client
137
+
138
+ 1. ใช้ `https://mcp.example.com/mcp`
139
+ 2. เลือก OAuth
140
+ 3. คัดลอก exact callback URL จาก client
141
+ 4. ลงทะเบียนด้วย `dodo auth add-client --redirect-uri <exact-callback>`
142
+ 5. ทำ browser authorization และ approve interaction จาก owner terminal
143
+ 6. scan tools และตรวจ surface ที่ client รายงาน
85
144
 
86
- ห้ามปิด OAuth, ใช้ Tunnel token แทน MCP OAuth, ส่ง token ใน URL หรือเปิด CORS กว้าง Tunnel provider ไม่ได้แทน owner consent ของ DODO และ DODO ไม่เรียก Cloudflare API, ไม่จัดการ DNS, ไม่สร้าง/ลบ Tunnel และไม่เปิด firewall
145
+ หนึ่ง Tunnel รองรับ project registry ทั้ง installation ไม่ต้องสร้าง Tunnel ต่อโปรเจกต์
146
+ แต่ทุก target ยังตรวจ OAuth identity, target ACL/scopes, workspace identity/epoch,
147
+ trust/approval, path/secret guards, sandbox และ expected hash ตามเดิม
package/docs/WINDOWS.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # DODO Windows Candidate — Deferred
2
2
 
3
- DODO MCP 1.0.1 ยังไม่ประกาศ native Windows support โค้ดและ tests สำหรับ Windows
3
+ DODO MCP 1.0.3 ยังไม่ประกาศ native Windows support โค้ดและ tests สำหรับ Windows
4
4
  บางส่วนมีอยู่เป็น candidate สำหรับ phase สุดท้าย แต่ยังไม่มีหลักฐานจาก Windows 11 จริง
5
5
  และ Linux Docker ไม่สามารถใช้แทนหลักฐาน Windows ได้
6
6
 
@@ -1,10 +1,16 @@
1
1
  # ADR-031 — Owner-selected Cloudflare Tunnel supervision
2
2
 
3
- **Status:** accepted (implemented)
3
+ **Status:** superseded in credential/lifecycle selection by ADR-048. Process ownership,
4
+ readiness, redaction and bounded-stop decisions remain accepted.
4
5
 
5
6
  **Decision.** DODO starts only an owner-selected, trusted `cloudflared` executable owned by the current HTTP process. `dodo start` can attach it immediately; the authenticated Local Config can start or stop the same process-owned runtime. The owner still creates the remotely-managed Tunnel, public hostname and DNS in Cloudflare.
6
7
 
7
- The default flow uses a run-scoped token entered through a hidden terminal prompt or the authenticated loopback Local Config. The token is never persisted in global config or an OS credential store. A process-owned `TunnelRuntime` forwards it through the managed child's dedicated environment and stops that child with DODO; it is absent from argv, logs, MCP, audit and job environments. Explicit secure credential references remain supported only by advanced standalone tunnel commands for compatibility.
8
+ The original flow used a run-scoped token entered through a hidden terminal prompt or
9
+ the authenticated loopback Local Config. ADR-048 replaces that credential flow with one
10
+ persistent Local/Tunnel selection and a reviewed credential reference. The invariant
11
+ retained from this ADR is that a process-owned `TunnelRuntime` forwards the resolved
12
+ credential through the child environment, stops that child with DODO and keeps the
13
+ credential out of argv, logs, MCP, audit and job environments.
8
14
 
9
15
  The supervisor uses a separate authenticated singleton owner IPC endpoint, loopback readiness, bounded restarts, private bounded/redacted logs and live child handles for termination. It never signals a saved PID or searches by process name. Local Config and the metrics listener remain loopback-only and are not part of the public route.
10
16
 
@@ -29,9 +29,9 @@ in-flight drain, running-job refusal, resource teardown และ fresh workspac
29
29
  data plane หากล้มเหลว launcher ยังคงปิด data plane ไม่มี half-switched state
30
30
 
31
31
  `dodo --cli` เป็น terminal menu ที่เรียก owner operations เดิม ไม่ใช่ MCP tool
32
- เมนูไม่เปลี่ยน OAuth grant, access mode หรือ OS permission เมนูเริ่มปกติเรียก hidden
33
- run-scoped Tunnel prompt เดียวกับ `dodo start` ส่วนเมนู local-only ส่ง override สำหรับ
34
- รอบนั้น Token ไม่ผ่าน readline menu, argv, config หรือ OS credential provider
32
+ เมนูไม่เปลี่ยน OAuth grant, access mode หรือ OS permission เมนูเริ่ม DODO ตาม
33
+ persistent `connectionMode` ที่เจ้าของบันทึกไว้ การเลือก/เก็บ Tunnel credential ใช้
34
+ Local Config หรือ `dodo tunnel configure` ตาม ADR-048 และไม่ผ่าน readline menu/argv
35
35
 
36
36
  STDIO คง contract `dodo stdio --root PATH` เพราะ client เป็นเจ้าของ subprocess
37
37
  lifecycle และต้องกำหนด root อย่างชัดเจน
@@ -0,0 +1,47 @@
1
+ # ADR-047 — Temporary Remote Config on the tunneled listener
2
+
3
+ ## Status
4
+
5
+ Accepted in DODO MCP 1.0.2.
6
+
7
+ ## Context
8
+
9
+ The MCP/OAuth listener is already tunneled from `127.0.0.1:21730`, while Local Config
10
+ is intentionally bound to `127.0.0.1:21731`. Owners sometimes need to configure DODO
11
+ from another device. Permanently routing 21731 or placing its bearer capability in a
12
+ public URL would break the owner control boundary.
13
+
14
+ ## Decision
15
+
16
+ `dodo --web` opens a process-memory lease at `<public-origin>/config` for at most one
17
+ hour. The route is 404 before and after the lease. A random one-time pairing code is
18
+ shown only in the owner terminal; the server stores only its digest. Successful pairing
19
+ issues a random session cookie scoped to `/config` with Secure, HttpOnly and SameSite
20
+ Strict attributes. The server stores only the session digest.
21
+
22
+ The public handler is a bounded reverse proxy to the existing loopback Local Config.
23
+ It strips client credentials and proxy headers, injects the private loopback capability,
24
+ and leaves Local Config's workspace/epoch checks, validation, policy and audit as the
25
+ authority. Pairing never grants MCP scopes, project access, trust or approvals. No MCP
26
+ tool can open the lease.
27
+
28
+ ADR-048 supersedes the temporary-token handoff: current source opens Remote Config only
29
+ when the saved connection mode is Tunnel and the DODO-owned Tunnel is already running.
30
+ Owner IPC cannot carry a Tunnel credential or switch the connection mode. Closing or
31
+ expiry still affects only Remote Config; MCP, OAuth and Tunnel continue running.
32
+
33
+ ## Consequences
34
+
35
+ - The owner can choose local-only MCP, tunneled MCP, or tunneled MCP plus temporary
36
+ Remote Config without rebinding either listener.
37
+ - Reopening issues a new code/session and invalidates the previous pair.
38
+ - A live Cloudflare route and public HTTPS origin remain owner prerequisites.
39
+ - Anyone with both the public URL and current one-time code can pair during its short
40
+ validity, so owners must treat the code as a temporary secret and close the lease
41
+ when finished.
42
+
43
+ ## Evidence
44
+
45
+ - `tests/security/remoteConfig.test.ts`
46
+ - `tests/integration/remoteConfigUi.test.ts`
47
+ - `tests/integration/cli.test.ts`
@@ -0,0 +1,58 @@
1
+ # ADR-048 — Persistent exclusive Local/Tunnel connection mode
2
+
3
+ ## Status
4
+
5
+ Accepted in source; unreleased.
6
+
7
+ ## Context
8
+
9
+ The prior launch flow asked for a run-scoped Cloudflare Tunnel token each time and could
10
+ start locally when the owner omitted it. That made the advertised endpoint depend on an
11
+ interactive answer and allowed a configuration mistake to change the connection path.
12
+ Owners need one installation-wide choice: always use the DODO Tunnel or always use the
13
+ local endpoint until they explicitly change it.
14
+
15
+ ## Decision
16
+
17
+ Global owner config stores one authoritative `connectionMode`: `local` or `tunnel`.
18
+ Legacy `mode` and `startWithDodo` fields are accepted only during load-time migration
19
+ and are removed from newly saved config.
20
+
21
+ In Local mode DODO advertises `http://127.0.0.1:<port>/mcp`, uses the loopback origin as
22
+ its OAuth issuer/resource and never starts `cloudflared`. In Tunnel mode DODO advertises
23
+ the configured public HTTPS origin, starts one process-owned supervisor on every
24
+ `dodo start`, waits for readiness and stops that child with DODO. Missing credentials,
25
+ executable failure or readiness failure aborts startup; there is no Local fallback.
26
+
27
+ The standard credential path is a reviewed OS store: macOS Keychain, Windows Credential
28
+ Manager or Linux Secret Service. Global config stores only an opaque reference. Explicit
29
+ environment/private-file references remain for owner-controlled headless deployments.
30
+ The token reaches `cloudflared` through its dedicated child environment and never enters
31
+ argv, config, logs, audit, browser storage, MCP output or MCP-job environments.
32
+
33
+ Local Config may select the mode and accept a write-only token after its existing owner,
34
+ Host, Origin, proxy-header, rate and workspace-context checks. The backend writes it to
35
+ the OS store and returns only presence/provider. A saved change requires restart and the
36
+ UI distinguishes saved mode from the active runtime endpoint.
37
+
38
+ Remote Config remains a bounded one-hour `/config` lease. It is available only while
39
+ Tunnel mode and the DODO-owned supervisor are active. Installation IPC cannot transmit a
40
+ Tunnel token or change mode, and public MCP routes expose no owner control operation.
41
+
42
+ ## Consequences
43
+
44
+ - Startup and endpoint selection are deterministic and auditable.
45
+ - Tunnel mode fails closed instead of silently changing how clients reach DODO.
46
+ - Credential unlock may still require an owner/OS interaction at startup.
47
+ - The loopback MCP listener remains present as the private Tunnel upstream, but the
48
+ public URL is the only advertised active MCP endpoint in Tunnel mode.
49
+ - Switching modes or public origin requires a DODO restart and client reconnect/rescan.
50
+
51
+ ## Evidence
52
+
53
+ - `tests/integration/connectionMode.test.ts`
54
+ - `tests/security/tunnelCredentials.test.ts`
55
+ - `tests/security/localConfig.test.ts`
56
+ - `tests/integration/tunnelSupervisor.test.ts`
57
+ - `tests/security/remoteConfig.test.ts`
58
+ - `tests/integration/remoteConfigUi.test.ts`
@@ -62,3 +62,5 @@ decisions; ADR-011+ resolve implementation ambiguities without reducing security
62
62
  - [044 — Global launcher and interactive owner menu](044-global-launcher-and-cli-menu.md)
63
63
  - [045 — Explicit project runtimes and provider-backed sub-agents](045-ai-providers-multiproject.md)
64
64
  - [046 — Personal add-and-use mode](046-personal-access-mode.md)
65
+ - [047 — Temporary Remote Config on the tunneled listener](047-temporary-remote-config.md)
66
+ - [048 — Persistent exclusive Local/Tunnel connection mode](048-persistent-connection-mode.md)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dodo-mcp",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "author": "Arthittakun",
5
5
  "description": "DODO — local-first MCP server with multi-project coding, AI providers, sub-agents, context, memory, media and workflow tools; embedded OAuth and workspace permissions.",
6
6
  "type": "module",
@@ -40,6 +40,8 @@
40
40
  "docs/RUNTIME.md",
41
41
  "docs/RELEASE_1.0.0.md",
42
42
  "docs/RELEASE_1.0.1.md",
43
+ "docs/RELEASE_1.0.2.md",
44
+ "docs/RELEASE_1.0.3.md",
43
45
  "docs/RELEASE_NOTES.md",
44
46
  "docs/SCHEDULES.md",
45
47
  "docs/SECURITY.md",
@@ -359,25 +359,20 @@
359
359
  },
360
360
  "tunnel": {
361
361
  "default": {
362
- "mode": "external",
363
- "startWithDodo": true,
362
+ "connectionMode": "local",
364
363
  "metricsPort": 21732,
365
364
  "maxRestarts": 2
366
365
  },
367
366
  "type": "object",
368
367
  "properties": {
369
- "mode": {
370
- "default": "external",
368
+ "connectionMode": {
369
+ "default": "local",
371
370
  "type": "string",
372
371
  "enum": [
373
- "external",
374
- "managed"
372
+ "local",
373
+ "tunnel"
375
374
  ]
376
375
  },
377
- "startWithDodo": {
378
- "default": true,
379
- "type": "boolean"
380
- },
381
376
  "credentialRef": {
382
377
  "oneOf": [
383
378
  {
@@ -456,8 +451,7 @@
456
451
  }
457
452
  },
458
453
  "required": [
459
- "mode",
460
- "startWithDodo",
454
+ "connectionMode",
461
455
  "metricsPort",
462
456
  "maxRestarts"
463
457
  ],