machine-bridge-mcp 0.2.5 → 0.3.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.
package/CHANGELOG.md ADDED
@@ -0,0 +1,60 @@
1
+ # Changelog
2
+
3
+ ## 0.3.3 - 2026-07-10
4
+
5
+ ### Security
6
+
7
+ - Add an exact-version npm `allowScripts` policy for the reviewed Wrangler runtime/build binaries (`esbuild@0.28.1`, `sharp@0.34.5`, and `workerd@1.20260708.1`) and explicitly deny the optional `fsevents` install script. Dependency upgrades therefore require renewed script approval.
8
+
9
+ ### Changed
10
+
11
+ - Exclude the development-only Worker integration test from the published npm package while retaining it in the repository and CI.
12
+
13
+ ## 0.3.2 - 2026-07-10
14
+
15
+ ### Fixed
16
+
17
+ - Keep the healthy daemon active until a replacement connection completes its authenticated `hello` handshake; enforce the candidate deadline with a Durable Object alarm so it survives WebSocket hibernation, and reject stale or non-handshaking candidates without disrupting the active connection.
18
+ - Send the candidate acknowledgement before closing the previous daemon, expire failed acknowledgements, clean up duplicate authenticated sockets on subsequent handshakes, and reject pre-handshake messages without changing active tool metadata.
19
+
20
+ ## 0.3.1 - 2026-07-10
21
+
22
+ ### Security
23
+
24
+ - Replace reversible unsalted source-address hashes used for OAuth registration limits and password throttling with deployment-keyed HMAC identifiers; stop persisting User-Agent-derived identities, remove legacy unsalted identifiers during store migration, and fail closed if no identity key is configured.
25
+
26
+ ### Fixed
27
+
28
+ - Preserve timeout escalation after the direct child exits so a process-group `SIGKILL` still removes descendants that ignore `SIGTERM`; add a regression test for the orphan-process boundary.
29
+ - Replace legacy unsalted source-identity hashes with deployment-keyed HMAC identifiers, prune legacy stored identifiers, and add integration coverage for registration quotas and login throttling.
30
+
31
+ ## 0.3.0 - 2026-07-10
32
+
33
+ ### Security
34
+
35
+ - Confine filesystem tools to the canonical workspace by default; add explicit `--unrestricted-paths` opt-in and symbolic-link escape protection.
36
+ - Make `write_file` bounded and atomic, reject symbolic-link/non-regular destinations, and enforce optimistic hashes consistently.
37
+ - Add recursive log-field and token redaction, control-character neutralization, owner-only service logs, and bounded log trimming.
38
+ - Harden OAuth with strict PKCE/resource/client/redirect validation, validated client disclosure on the consent page, password-failure throttling, bounded dynamic registration, per-source and per-client limits, inactive-client cleanup, strict HTTP methods/UTF-8 handling, and security response headers.
39
+ - Minimize unauthenticated and relay metadata disclosure: remove workspace name/path hash/process ID from the daemon handshake, reduce default observability sampling, and bind pending calls to their originating daemon socket.
40
+ - Bound Worker/daemon request sizes and concurrency; serialize OAuth mutations; isolate WebSocket generations; serialize per-workspace startup/deploy/rotation operations; terminate timed-out or disconnected child processes and bound Wrangler subprocesses.
41
+
42
+ ### Changed
43
+
44
+ - Raise the minimum Node.js version to 22 because the current Wrangler release no longer runs on Node 20; require the installed Wrangler dependency instead of falling back to an implicit network `npx` execution.
45
+ - Remove the experimental local OpenAI-compatible `/v1` API and MCP sampling proxy.
46
+ - Detect nested Git repositories for `git_status` and `git_diff`, while disabling repository-configured external diff, text conversion, and filesystem-monitor hooks for bridge Git operations.
47
+ - Use atomic local state/config writes, bounded corrupt-state backups, strict adoption/migration of dedicated state roots, and guarded state-root deletion during uninstall, including refusal while active daemon/startup locks remain.
48
+ - Reject unknown, duplicate, malformed, and command-inapplicable CLI options; fix boolean options consuming positional workspaces.
49
+ - Verify the expected Worker version before accepting a deployment hash.
50
+ - Reduce the default Worker request-body limit to 8 MiB; bound command length, directory results, recursive traversal, and path-result payloads; provide exact-origin CORS while rejecting implicit loopback browser origins.
51
+ - Update Wrangler to 4.110.x, TypeScript to 5.9.x, and Node type definitions to the supported Node 22 API line.
52
+
53
+ ### Tests and documentation
54
+
55
+ - Expand self-tests across path boundaries, symlinks, writes, UTF-8, nested Git, environment isolation, locking, CLI parsing, state recovery/removal guards, logging, service logs, and Worker hardening invariants.
56
+ - Add a live local Worker OAuth/MCP integration test, verified Node 22/24 compatibility, a Worker build dry-run, dependency audit, security policy, and architecture document.
57
+
58
+ ## 0.2.5
59
+
60
+ - Previous release.
package/README.md CHANGED
@@ -1,154 +1,150 @@
1
1
  # machine-bridge-mcp
2
2
 
3
- `machine-bridge-mcp` turns your machine into a Remote MCP server through a small hosted relay and a local outbound daemon.
3
+ `machine-bridge-mcp` exposes a selected local workspace to Remote MCP clients through a Cloudflare Worker relay and an outbound-only local daemon.
4
4
 
5
- The recommended deployment command is short and stable for autostart:
5
+ ```text
6
+ Remote MCP client -> HTTPS/OAuth -> Cloudflare Worker + Durable Object
7
+ ^
8
+ | outbound WebSocket
9
+ |
10
+ local daemon -> workspace/files/shell
11
+ ```
12
+
13
+ No inbound port is opened on the local machine. The Worker does not read local files or execute commands; it authenticates MCP clients and relays bounded tool calls to the daemon.
14
+
15
+ ## Install and start
16
+
17
+ Recommended global installation:
6
18
 
7
19
  ```zsh
8
- npm install -g machine-bridge-mcp@latest && machine-mcp
20
+ npm install -g machine-bridge-mcp@latest
21
+ machine-mcp
9
22
  ```
10
23
 
11
- No-global-install alternative:
24
+ Without a global installation:
12
25
 
13
26
  ```zsh
14
27
  npx machine-bridge-mcp@latest
15
28
  ```
16
29
 
17
- Source checkout:
30
+ From a source checkout:
18
31
 
19
32
  ```zsh
33
+ npm install
20
34
  ./mbm # macOS/Linux
21
35
  .\mbm.cmd # Windows cmd
22
36
  ```
23
37
 
24
- ## What it does on first run
38
+ On first run, the CLI:
25
39
 
26
- 1. Asks for a workspace path. Press Enter to use the current directory.
27
- 2. Remembers that workspace for later runs.
28
- 3. Generates a stable MCP connection password and daemon secret.
29
- 4. Checks `wrangler whoami`; if needed, opens `wrangler login`.
30
- 5. Deploys the hosted Worker relay with `wrangler deploy --secrets-file`.
31
- 6. Installs login autostart for the local daemon and default local API.
32
- 7. Starts the local daemon and local OpenAI-compatible API.
33
- 8. Prints MCP connection details on first run, plus local API settings:
40
+ 1. Selects and remembers a workspace.
41
+ 2. Generates an MCP connection password, daemon secret, and OAuth token version.
42
+ 3. Authenticates Wrangler if required.
43
+ 4. Deploys a per-workspace Worker.
44
+ 5. Installs a login autostart entry unless `--no-autostart` is used.
45
+ 6. Starts the outbound local daemon.
46
+ 7. Prints the Remote MCP URL and connection password.
34
47
 
35
- ```text
36
- MCP Server URL: https://<worker>.<account>.workers.dev/mcp
37
- MCP connection password: mcp_password_...
38
- API Base URL: http://127.0.0.1:8765/v1
39
- API key: local_api_key_...
40
- ```
48
+ Keep the foreground process running for the current session. The installed service handles later logins.
41
49
 
42
- Keep the foreground process running for the current session. The installed autostart entry keeps the daemon and local API available after future logins.
50
+ ## Connect an MCP client
43
51
 
44
- The command is safe to run repeatedly:
52
+ Use the values printed by `machine-mcp`:
45
53
 
46
- ```zsh
47
- npm install -g machine-bridge-mcp@latest && machine-mcp
54
+ ```text
55
+ MCP Server URL: https://<worker>.<account>.workers.dev/mcp
56
+ MCP connection password: mcp_password_...
48
57
  ```
49
58
 
50
- On repeat runs, the CLI reuses existing state and secrets unless you request rotation, skips Worker redeploys when the deployed Worker is healthy and Worker source/config/secrets are unchanged, refreshes the autostart entry, stops any currently loaded autostart daemon before starting the foreground daemon, and refuses to start a second daemon for the same workspace if another foreground instance is already running. Local-only package, CLI, logging, and local API changes do not by themselves force a Worker redeploy.
51
-
52
- MCP connection details are printed on first run, after secret rotation, when the MCP URL changes, or when you explicitly pass `--print-mcp-credentials`. Routine runs print that MCP details are unchanged.
59
+ The password is shown on first setup, after secret rotation, when the MCP URL changes, or with `--print-mcp-credentials`. Use `--no-print-credentials` to redact it in terminal output.
53
60
 
54
- ## Local OpenAI-compatible API
61
+ The former experimental local OpenAI-compatible `/v1` API has been removed. Use the Remote MCP endpoint directly.
55
62
 
56
- The project exposes two connected integration surfaces:
63
+ ## Security defaults
57
64
 
58
- - **ChatGPT web / ChatGPT apps:** use the Remote MCP Server URL and MCP connection password printed by `machine-mcp`. In this mode, ChatGPT calls tools on your machine through the Worker + local daemon bridge.
59
- - **Desktop clients such as Cherry Studio, Chatbox, or Continue:** use the optional local OpenAI-compatible `/v1` API. `POST /v1/chat/completions` is backed by MCP sampling: the local API asks the hosted Worker to send `sampling/createMessage` to the already-connected ChatGPT MCP client, then wraps the MCP sampling result as an OpenAI-compatible chat completion response.
65
+ Version 0.3.0 changes the default filesystem boundary:
60
66
 
61
- No separate model API setup is required or used in this path; the local API never asks for a model base URL or model API key. Generation depends on the ChatGPT-side MCP client actually being connected and able to receive server-to-client sampling requests. If ChatGPT is not connected, has no open MCP stream for server-to-client messages, or did not advertise the MCP `sampling` capability, generation returns an explicit OpenAI-shaped error saying that the missing piece is the MCP client stream or sampling capability.
67
+ - Relative paths are resolved from the selected workspace.
68
+ - Reads, writes, directory traversal, searches, and Git operations are confined to that workspace by default.
69
+ - Symbolic links cannot be used to escape the workspace boundary.
70
+ - `write_file` and `exec_command` remain enabled by default.
71
+ - Shell commands receive a minimal environment by default.
72
+ - `write_file` is limited to 5 MiB, writes atomically, and refuses to overwrite symbolic links.
73
+ - Files that look sensitive, including `.env` and key files, are readable when they are inside the selected workspace. The bridge does not infer sensitivity from filenames.
62
74
 
63
- Start the normal daemon and local API:
75
+ A narrower session:
64
76
 
65
77
  ```zsh
66
- machine-mcp
78
+ machine-mcp --no-write --no-exec
67
79
  ```
68
80
 
69
- Start only the local API from remembered state:
81
+ Explicitly permit filesystem paths outside the workspace:
70
82
 
71
83
  ```zsh
72
- machine-mcp api
84
+ machine-mcp --unrestricted-paths
73
85
  ```
74
86
 
75
- Disable the default local API for a daemon run:
87
+ Pass the complete parent process environment to shell commands only when required:
76
88
 
77
89
  ```zsh
78
- machine-mcp --no-api
90
+ machine-mcp --full-env
79
91
  ```
80
92
 
81
- When the API is running, the CLI prints client settings like:
82
-
83
- ```text
84
- API Base URL: http://127.0.0.1:8765/v1
85
- API key: local_api_key_...
86
- Client type: OpenAI-compatible
87
- Model: chatgpt-mcp
88
- Backend: ChatGPT MCP sampling via the connected ChatGPT app
89
- ```
93
+ `--unrestricted-paths` and `--full-env` materially increase the data-exposure boundary. See [SECURITY.md](SECURITY.md) before enabling them.
90
94
 
91
- Use the API Base URL, API key, and model in your desktop client. Separately, connect ChatGPT to the printed MCP Server URL/password so the Worker has an MCP client stream that can receive `sampling/createMessage`.
95
+ ## Commands
92
96
 
93
- If port `8765` conflicts with another local app, choose a different port explicitly:
94
-
95
- ```zsh
96
- machine-mcp --api-port 8766
97
- machine-mcp api --api-port 8766
98
- ```
99
-
100
- `--port` is also accepted on the `api` command:
101
-
102
- ```zsh
103
- machine-mcp api --port 8766
97
+ ```text
98
+ machine-mcp [start options]
99
+ machine-mcp workspace show
100
+ machine-mcp workspace set [PATH]
101
+ machine-mcp service status|install|start|stop|uninstall
102
+ machine-mcp status
103
+ machine-mcp doctor
104
+ machine-mcp rotate-secrets
105
+ machine-mcp uninstall [--keep-worker] [--yes]
104
106
  ```
105
107
 
106
- By default, the local API binds to `127.0.0.1`, starts with `machine-mcp`, and stores a per-workspace local API key in the same owner-only state profile used by the MCP credentials. Explicit `--api-host`, `--api-port`, and `--api-model` values are persisted for the workspace so autostart uses the same API settings. `--api-model` only controls the local model id advertised by `GET /v1/models`; the actual model is chosen by the connected MCP client, and any different `model` value in a chat-completions request is passed as an MCP model preference hint.
107
-
108
- Rotate the local desktop-client API key with:
108
+ Important start options:
109
109
 
110
- ```zsh
111
- machine-mcp api --rotate-api-key
110
+ ```text
111
+ --workspace PATH
112
+ --worker-name NAME
113
+ --force-worker
114
+ --rotate-secrets
115
+ --daemon-only
116
+ --no-autostart
117
+ --no-print-credentials
118
+ --print-mcp-credentials
119
+ --no-write
120
+ --no-exec
121
+ --full-env
122
+ --unrestricted-paths
123
+ --state-dir DIR
124
+ --json
112
125
  ```
113
126
 
114
- Environment variables supported for the current process: `MBM_API_HOST`, `MBM_API_PORT`, `MBM_API_KEY`, and `MBM_API_MODEL`. Environment overrides are not persisted to state; use `--api-host`, `--api-port`, `--api-key`, or `--api-model` when you want a setting saved for future runs and autostart.
115
-
116
- Supported local API routes:
127
+ Unknown, duplicate, malformed, and command-inapplicable options are rejected. Boolean options do not consume a following positional workspace path; use `--option=false` when an explicit false value is needed.
117
128
 
118
- - `GET /health` without authentication
119
- - `GET /v1/models` with `Authorization: Bearer <local_api_key>` or `x-api-key`
120
- - `POST /v1/chat/completions`
121
-
122
- `POST /v1/responses`, `POST /v1/completions`, and `POST /v1/embeddings` return `501 unsupported_endpoint`; MCP sampling is a chat-message path and does not expose embeddings or the full Responses API. Logs record route, status, latency, and safe configuration metadata; request and response bodies and API keys are not logged.
123
-
124
- ## Re-select workspace
129
+ ## Workspace selection
125
130
 
126
131
  ```zsh
127
132
  machine-mcp workspace set
128
- ```
129
-
130
- Or provide it directly:
131
-
132
- ```zsh
133
- machine-mcp workspace set /path/to/new/default
134
- ```
135
-
136
- Show the remembered workspace:
137
-
138
- ```zsh
133
+ machine-mcp workspace set /path/to/project
139
134
  machine-mcp workspace show
135
+ machine-mcp workspace reset
140
136
  ```
141
137
 
142
- ## Autostart service
138
+ Each canonical workspace path receives an independent profile, Worker name, secret set, and daemon lock.
139
+
140
+ ## Autostart
143
141
 
144
- Supported platforms:
142
+ Supported providers:
145
143
 
146
144
  - macOS: user LaunchAgent
147
- - Linux: `systemd --user` with best-effort `loginctl enable-linger`
145
+ - Linux: `systemd --user`, with best-effort user lingering
148
146
  - Windows: Scheduled Task at logon
149
147
 
150
- Commands:
151
-
152
148
  ```zsh
153
149
  machine-mcp service status
154
150
  machine-mcp service install
@@ -157,66 +153,16 @@ machine-mcp service stop
157
153
  machine-mcp service uninstall
158
154
  ```
159
155
 
160
- `start` installs autostart by default. Skip that behavior with:
156
+ Autostart runs `--daemon-only --no-print-credentials --quiet`. The selected write, execution, environment, and path-boundary policies are persisted in the service definition. Service log files are owner-only where supported and are trimmed at daemon startup to prevent unbounded growth.
161
157
 
162
- ```zsh
163
- machine-mcp --no-autostart
164
- ```
165
-
166
- Autostart runs the daemon with `--daemon-only --no-print-credentials`, so service logs do not contain the MCP connection password. If you start with `--no-write`, `--no-exec`, or `--full-env`, those policy flags are preserved in the autostart entry. macOS/Linux service definitions restart only on process failure; a normal duplicate-instance exit is not treated as a crash loop.
167
-
168
- ## Secrets rotation
158
+ ## Secret rotation
169
159
 
170
160
  ```zsh
171
161
  machine-mcp rotate-secrets
172
162
  machine-mcp
173
163
  ```
174
164
 
175
- `rotate-secrets` creates a new MCP connection password, daemon secret, and OAuth token version. The next deploy rejects previously issued OAuth access tokens.
176
-
177
- ## Uninstall
178
-
179
- Delete known deployed Worker(s), remove autostart entries, and remove local state:
180
-
181
- ```zsh
182
- machine-mcp uninstall
183
- ```
184
-
185
- Non-interactive:
186
-
187
- ```zsh
188
- machine-mcp uninstall --yes
189
- ```
190
-
191
- Keep the deployed Worker but remove local state/autostart:
192
-
193
- ```zsh
194
- machine-mcp uninstall --keep-worker
195
- ```
196
-
197
- If installed globally, remove the npm package afterwards:
198
-
199
- ```zsh
200
- npm uninstall -g machine-bridge-mcp
201
- ```
202
-
203
- ## Defaults and permissions
204
-
205
- This project optimizes for easy use with official Remote MCP clients:
206
-
207
- - `write_file` is enabled by default.
208
- - `exec_command` is enabled by default.
209
- - Absolute paths are allowed.
210
- - Parent-directory paths such as `../other-project/file.ts` are allowed.
211
- - Sensitive-looking files such as `.env`, private keys, token files, and dot-directories are not hidden by default.
212
- - Relative paths use the selected workspace as cwd.
213
- - Shell commands run with a minimal environment by default; use `--full-env` to pass the parent process environment.
214
-
215
- Narrower session:
216
-
217
- ```zsh
218
- machine-mcp --no-write --no-exec
219
- ```
165
+ The first command stops the installed autostart service, refuses to proceed if another foreground daemon remains active, and then rotates the MCP password, daemon secret, and OAuth token version in local state. The second redeploys the Worker; previously issued OAuth access tokens then fail validation.
220
166
 
221
167
  ## MCP tools
222
168
 
@@ -232,6 +178,8 @@ machine-mcp --no-write --no-exec
232
178
  - `git_diff`
233
179
  - `exec_command`
234
180
 
181
+ Tool availability reflects daemon policy after the daemon handshake. `git_status` and `git_diff` detect the repository containing the requested path, including nested repositories.
182
+
235
183
  ## State and logs
236
184
 
237
185
  Default state roots:
@@ -240,54 +188,78 @@ Default state roots:
240
188
  - Linux with `XDG_STATE_HOME`: `$XDG_STATE_HOME/machine-bridge-mcp`
241
189
  - Windows: `%APPDATA%\machine-bridge-mcp`
242
190
 
243
- State contains the MCP password, daemon secret, and local API key. Status/doctor output redacts secrets. The normal foreground `start` command prints the MCP password only when a ChatGPT app is likely to need reconnection: first run, secret rotation, MCP URL changes, or `--print-mcp-credentials`. The local API base URL and API key print on normal foreground starts because desktop AI clients need them. Use `--no-print-credentials` to redact console credentials. State files, temporary Worker secret files, lock files, and log directories are created under the user state root with owner-only permissions where the platform supports POSIX modes.
191
+ Override the root with `--state-dir DIR`. A new custom root must be empty; a non-empty unmarked root must contain recognizable legacy Machine Bridge state before it is adopted. Legacy text markers are migrated to the current structured marker. Uninstall refuses to recursively delete filesystem roots, the home directory, the current/package directory, source trees, recorded workspaces, unrelated files, or an unrecognized state root.
192
+
193
+ State contains credentials and therefore uses owner-only permissions where supported. State/config writes use temporary files, `fsync`, and atomic rename. Corrupt state files are retained as bounded `.corrupt-*` backups rather than silently overwritten. Status and doctor output redact secrets and personal Wrangler output; structured logs fully redact credential-like fields and neutralize control characters.
194
+
195
+ Temporary Wrangler secret files are created under the owner-only profile directory and removed in a `finally` block. Stale files older than one hour are cleaned before deployment.
196
+
197
+ ## Worker protections
198
+
199
+ The Worker currently enforces:
200
+
201
+ - OAuth authorization code flow with PKCE S256.
202
+ - Hashed access-token storage and token-version revocation.
203
+ - Exact resource, client, and redirect URI binding.
204
+ - Bounded registration metadata, clients, authorization codes, tokens, and failed-login records.
205
+ - Per-source dynamic-client registration limits and password-failure throttling.
206
+ - A consent page that displays the validated client and redirect URI.
207
+ - Request-body and concurrent daemon-call limits.
208
+ - Same-origin browser access by default, exact configured-origin CORS/preflight support, and rejection of unlisted cross-origin requests.
209
+ - `no-store`, content-type protection, CSP, frame denial, and referrer suppression on authorization responses.
210
+ - Minimal public health output; live daemon/workspace details require an authenticated MCP call.
211
+
212
+ Browser requests are same-origin by default. Additional browser origins can be listed exactly in `MBM_ALLOWED_ORIGINS`; the Worker then returns matching CORS preflight and response headers. Loopback OAuth redirect URIs do not implicitly authorize loopback browser origins. Do not use wildcards or `null`.
244
213
 
245
- The Worker rejects browser requests with an `Origin` header unless the origin is the Worker itself or a loopback HTTP origin. To allow additional browser-based MCP clients, set `MBM_ALLOWED_ORIGINS` to a comma-separated list of exact origins in `wrangler.jsonc` or Cloudflare Worker settings.
214
+ ## Uninstall
246
215
 
247
- Override state root:
216
+ Delete known Workers, autostart entries, and local state:
248
217
 
249
218
  ```zsh
250
- machine-mcp --state-dir /path/to/state
219
+ machine-mcp uninstall
251
220
  ```
252
221
 
253
- ## Architecture
254
-
255
- ```mermaid
256
- flowchart LR
257
- C["ChatGPT / MCP client"] -- "HTTPS /mcp + OAuth" --> W["Hosted Worker relay"]
258
- C -- "GET SSE stream for server-to-client MCP" --> W
259
- W --> DO["Durable Object broker"]
260
- D["Local daemon"] -- "outbound WebSocket" --> W
261
- D --> M["Local filesystem and shell"]
262
- API["Local /v1/chat/completions"] -- "POST /api/mcp/sampling" --> W
263
- DO -- "sampling/createMessage" --> C
264
- CLI["machine-mcp CLI"] --> API
265
- CLI --> W
266
- CLI --> D
267
- CLI --> S["Autostart service"]
222
+ Non-interactive:
223
+
224
+ ```zsh
225
+ machine-mcp uninstall --yes
268
226
  ```
269
227
 
270
- Why this architecture:
228
+ Keep deployed Workers while removing local state and autostart:
271
229
 
272
- - No inbound local port is exposed to the internet.
273
- - No local tunnel process is required.
274
- - The public MCP URL is stable after deployment.
275
- - The Worker stores OAuth client/code/token metadata and relays tool calls.
276
- - The local daemon is the only process touching files or executing commands.
277
- - The local `/v1` API binds to loopback by default, starts automatically with the daemon, and can be disabled with `--no-api`.
278
- - Autostart keeps the daemon and local API alive across logins without requiring MCP clients to change URLs.
230
+ ```zsh
231
+ machine-mcp uninstall --keep-worker
232
+ ```
279
233
 
280
- ## Development
234
+ Remove a global npm installation separately:
281
235
 
282
236
  ```zsh
283
- npm install
284
- npm run check
237
+ npm uninstall -g machine-bridge-mcp
285
238
  ```
286
239
 
287
- `npm run check` generates Worker runtime types, type-checks the Worker, checks local JS syntax, and runs daemon self-tests.
240
+ ## Architecture and failure behavior
241
+
242
+ See [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) for trust boundaries, request lifecycles, limits, reconnect behavior, and the rationale for the Worker/Durable Object/daemon split.
288
243
 
289
- Worker build dry-run:
244
+ Operationally:
245
+
246
+ - A newer daemon connection replaces the older socket. The relay handshake does not upload the local workspace name, path hash, or process ID.
247
+ - Pending calls are bound to the socket that received them, so a stale socket cannot complete or cancel calls on the replacement connection; active child processes are terminated when that connection is lost or replaced.
248
+ - The daemon reconnects after transient Worker/network failures using bounded exponential backoff with jitter, and terminates active child processes when the active relay socket closes.
249
+ - Tool calls and Wrangler subprocesses have bounded execution time and output; timed-out commands terminate their process tree where the platform permits.
250
+ - Separate per-workspace startup and daemon locks prevent overlapping deploy/rotation operations and duplicate local daemons.
251
+ - Uninstall stops and removes autostart first, refuses to proceed while an active startup or daemon process owns a lock, and preserves local state when Worker deletion fails so the operation can be retried.
252
+ - Worker health checks verify the expected package/Worker version before a deployment hash is accepted.
253
+
254
+ ## Development
290
255
 
291
256
  ```zsh
292
- npx wrangler deploy --dry-run
257
+ npm ci
258
+ npm run check
259
+ npm run worker:dry-run
260
+ npm audit --omit=dev --audit-level=high
293
261
  ```
262
+
263
+ `npm run check` generates Worker types, type-checks the Worker, checks all local JavaScript entry points, and runs regression tests for path confinement, symbolic-link escapes, atomic writes, UTF-8 handling, nested Git repositories, minimal environments, daemon locking, CLI parsing, state recovery/removal guards, log redaction, log trimming, Worker hardening guards, and a live local OAuth/MCP flow through Wrangler.
264
+
265
+ The release checks are validated on Node.js 22 and 24. Node.js 22 or newer is required by the current Wrangler toolchain. See [CHANGELOG.md](CHANGELOG.md) for release notes.
package/SECURITY.md ADDED
@@ -0,0 +1,84 @@
1
+ # Security policy
2
+
3
+ ## Supported versions
4
+
5
+ Security fixes are applied to the latest released version. Upgrade before reporting an issue that is already fixed on `main`.
6
+
7
+ ## Reporting a vulnerability
8
+
9
+ Do not open a public issue for an undisclosed vulnerability. Use GitHub's private vulnerability reporting feature for this repository. Include:
10
+
11
+ - affected version and operating system;
12
+ - deployment mode and relevant policy flags;
13
+ - a minimal reproduction;
14
+ - expected and observed impact;
15
+ - whether credentials, filesystem data, or command execution were exposed.
16
+
17
+ Do not include live MCP passwords, daemon secrets, OAuth tokens, Cloudflare credentials, private keys, or unrelated local files. Rotate any credential used in a reproduction.
18
+
19
+ ## Trust model
20
+
21
+ The following components are trusted:
22
+
23
+ 1. The local user account running the daemon.
24
+ 2. The installed package/source checkout.
25
+ 3. The user's Cloudflare account and deployed Worker.
26
+ 4. An MCP client that has completed OAuth authorization with the connection password.
27
+
28
+ The Worker is a relay and authorization boundary. The local daemon is the filesystem and command-execution boundary. A successfully authorized MCP client can invoke every tool enabled by the daemon policy and can receive the resulting file contents and command output.
29
+
30
+ This project does not provide a sandbox for shell commands. `exec_command` has the operating-system permissions of the local user. Workspace path confinement limits filesystem tools, but it cannot constrain commands executed by the shell. Use `--no-exec` when shell access is unnecessary.
31
+
32
+ ## Primary risks and controls
33
+
34
+ ### Credential disclosure
35
+
36
+ Local state contains the MCP password and daemon secret. State, lock, temporary secret, and service-log files use owner-only permissions where the platform supports them. State writes are atomic. Logs recursively and fully redact common credential fields and known token formats.
37
+
38
+ Terminal output can intentionally display the MCP password. Avoid recording first-run output in shared shell history, CI logs, screen recordings, or support tickets. Use `--no-print-credentials` for recorded sessions.
39
+
40
+ ### Filesystem exposure
41
+
42
+ Filesystem tools are confined to the canonical selected workspace by default, including symbolic-link resolution. `--unrestricted-paths` exposes absolute and parent paths and should be used only for a deliberate, short-lived session.
43
+
44
+ Sensitive filenames are not automatically blocked inside the workspace. Select the narrowest practical workspace and use `--no-write` for review-only sessions.
45
+
46
+ ### Command execution
47
+
48
+ `exec_command` is enabled by default but can be disabled with `--no-exec`. Commands run with a minimal environment unless `--full-env` is supplied. Minimal environment handling reduces accidental environment-secret disclosure; it does not sandbox filesystem, network, process, or credential-store access available to the user account.
49
+
50
+ ### OAuth and public endpoints
51
+
52
+ OAuth uses authorization codes, PKCE S256, exact redirect/resource binding, hashed access-token storage, expiring codes/tokens, and a version value that revokes existing tokens after rotation. Client registration, codes, tokens, pending calls, body sizes, and failed password attempts are bounded.
53
+
54
+ The authorization page displays the validated client name and redirect URI. Do not enter the connection password unless you initiated the connection and recognize both values. Browser-originated requests are same-origin by default; exact additional origins require `MBM_ALLOWED_ORIGINS` and receive explicit CORS headers. Loopback redirect URIs do not grant browser-origin access.
55
+
56
+ Public health and metadata endpoints do not expose live daemon policy or connection status. The daemon handshake does not upload the local workspace name, path hash, or process ID. Authenticated `server_info` exposes only bounded operational metadata needed to diagnose the relay.
57
+
58
+ ### Relay and denial of service
59
+
60
+ Only one daemon socket is active. New connections replace old ones. Pending calls are socket-bound and concurrency-limited. Request and output sizes are bounded. Dynamic registration is limited globally and per keyed-HMAC source identity; the source IP is not stored directly and inactive clients expire.
61
+
62
+ These controls reduce accidental exhaustion and simple abuse. The CLI uses the package-installed Wrangler binary and does not fall back to downloading an unpinned command through `npx`. They do not replace Cloudflare account protections, rate-limiting/WAF rules, or cost alerts for an internet-facing deployment.
63
+
64
+ ## Hardening checklist
65
+
66
+ For a high-sensitivity workspace:
67
+
68
+ ```zsh
69
+ machine-mcp --workspace /narrow/project --no-write --no-exec --no-print-credentials
70
+ ```
71
+
72
+ Also:
73
+
74
+ - keep the local OS and Node.js supported and patched;
75
+ - enable MFA on the Cloudflare and GitHub accounts;
76
+ - do not set `MBM_ALLOWED_ORIGINS` broadly;
77
+ - avoid `--full-env` and `--unrestricted-paths`;
78
+ - rotate secrets after suspected disclosure;
79
+ - inspect `machine-mcp status` and `machine-mcp service status`;
80
+ - uninstall the Worker and local state when the bridge is no longer needed.
81
+
82
+ ## Out of scope
83
+
84
+ The project cannot prevent an authorized MCP client from requesting sensitive data that the enabled tools can access. It also cannot make arbitrary shell commands safe. Model-level prompt injection and unsafe operator approval remain outside the transport's security guarantees; use narrow workspaces and restrictive policy flags as the primary controls.