machine-bridge-mcp 0.17.0 → 0.18.0

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 (44) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/README.md +21 -12
  3. package/SECURITY.md +17 -9
  4. package/browser-extension/manifest.json +2 -2
  5. package/docs/ARCHITECTURE.md +8 -6
  6. package/docs/AUDIT.md +3 -3
  7. package/docs/CLIENTS.md +3 -1
  8. package/docs/GETTING_STARTED.md +404 -0
  9. package/docs/LOGGING.md +2 -2
  10. package/docs/MANAGED_JOBS.md +1 -1
  11. package/docs/MULTI_ACCOUNT.md +123 -0
  12. package/docs/OPERATIONS.md +4 -4
  13. package/docs/POLICY_REFERENCE.md +1 -1
  14. package/docs/PRIVACY.md +1 -1
  15. package/docs/PROJECT_STANDARDS.md +1 -1
  16. package/docs/TESTING.md +4 -4
  17. package/package.json +4 -3
  18. package/src/local/account-access.mjs +37 -0
  19. package/src/local/account-admin.mjs +105 -0
  20. package/src/local/bounded-output.mjs +50 -0
  21. package/src/local/call-registry.mjs +10 -0
  22. package/src/local/cli-account-admin.mjs +79 -0
  23. package/src/local/cli-options.mjs +9 -4
  24. package/src/local/cli-policy.mjs +7 -28
  25. package/src/local/cli.mjs +48 -78
  26. package/src/local/daemon-process.mjs +3 -2
  27. package/src/local/errors.mjs +0 -14
  28. package/src/local/log.mjs +1 -1
  29. package/src/local/managed-jobs.mjs +1 -3
  30. package/src/local/process-execution.mjs +90 -60
  31. package/src/local/relay-connection.mjs +11 -3
  32. package/src/local/runtime.mjs +27 -2
  33. package/src/local/service.mjs +7 -26
  34. package/src/local/shell.mjs +17 -29
  35. package/src/local/state-inventory.mjs +2 -2
  36. package/src/local/state.mjs +30 -85
  37. package/src/local/tool-executor.mjs +8 -2
  38. package/src/shared/access-contract.json +23 -0
  39. package/src/shared/policy-contract.json +30 -7
  40. package/src/worker/access.ts +46 -0
  41. package/src/worker/account-admin.ts +103 -0
  42. package/src/worker/index.ts +75 -48
  43. package/src/worker/oauth-state.ts +184 -2
  44. package/src/worker/tool-catalog.ts +13 -0
@@ -0,0 +1,404 @@
1
+ # Installation and first-use guide
2
+
3
+ This guide covers a clean installation, the first remote ChatGPT connection, local stdio clients, browser setup, routine operation, upgrades, troubleshooting, and removal.
4
+
5
+ ## 1. Decide which connection mode you need
6
+
7
+ Machine Bridge has two transports. Choose one before installing:
8
+
9
+ | Requirement | Use | Cloudflare required | Process that must remain available |
10
+ |---|---|---:|---|
11
+ | ChatGPT web, a hosted agent, or another device must reach this computer | Remote HTTPS/OAuth | Yes | Machine Bridge daemon or installed login service |
12
+ | Claude Desktop, Cursor, Codex, ChatGPT Desktop, or another local host runs on this computer | Local stdio | No | The MCP host launches Machine Bridge as a subprocess |
13
+ | The local coding client already has equivalent file, Git, patch, and terminal tools | Neither | No | None |
14
+
15
+ Remote and stdio modes use the same local runtime and policy model. The remote Worker authenticates and relays calls; it does not read files or execute processes by itself.
16
+
17
+ ## 2. Understand the authority you are granting
18
+
19
+ A new workspace uses the `full` profile unless another profile is selected explicitly. `full` exposes the complete tool catalog, shell execution, paths outside the selected workspace, absolute paths, and the complete parent process environment. It is intended for a trusted owner using a trusted MCP host.
20
+
21
+ For a first connection to an unfamiliar host, start with a narrower profile:
22
+
23
+ ```sh
24
+ machine-mcp --workspace /path/to/project --profile review
25
+ ```
26
+
27
+ Profiles:
28
+
29
+ | Profile | Reads | File changes | Direct processes | Shell | Direct filesystem scope |
30
+ |---|---:|---:|---:|---:|---|
31
+ | `full` | Yes | Yes | Yes | Yes | Unrestricted |
32
+ | `agent` | Yes | Yes | Yes | No | Selected workspace |
33
+ | `edit` | Yes | Yes | No | No | Selected workspace |
34
+ | `review` | Yes | No | No | No | Selected workspace |
35
+
36
+ A profile is a Machine Bridge policy ceiling. The MCP host, operating system, endpoint-security software, container, and cloud platform may impose additional restrictions.
37
+
38
+ ## 3. Prerequisites
39
+
40
+ Required for every installation:
41
+
42
+ - Node.js 26 or newer;
43
+ - npm 12 or newer;
44
+ - macOS, Linux, or Windows;
45
+ - a local user account permitted to read or modify the selected workspace.
46
+
47
+ Remote mode additionally requires:
48
+
49
+ - a Cloudflare account permitted to deploy Workers and Durable Objects;
50
+ - a browser available for Wrangler sign-in;
51
+ - outbound HTTPS and WebSocket access from the computer running the daemon.
52
+
53
+ Browser automation additionally requires:
54
+
55
+ - a Chromium-based browser that can load an unpacked Manifest V3 extension;
56
+ - the extension loaded into the browser profile whose tabs and login state should be controlled.
57
+
58
+ Check the installed runtimes:
59
+
60
+ ```sh
61
+ node --version
62
+ npm --version
63
+ ```
64
+
65
+ Do not continue with an older runtime. This repository intentionally rejects unsupported Node/npm versions.
66
+
67
+ ## 4. Install the released package
68
+
69
+ Use a temporary package-free directory. An older npm front-end can inspect metadata in the current directory before it launches npm 12; the isolated bootstrap prevents an unrelated project configuration from breaking installation.
70
+
71
+ ### macOS and Linux
72
+
73
+ ```sh
74
+ install_dir="$(mktemp -d)"
75
+ (
76
+ cd "$install_dir"
77
+ npx --yes npm@12.0.1 install --global npm@12.0.1
78
+ npx --yes npm@12.0.1 install --global --omit=optional --allow-scripts=esbuild,workerd,sharp,fsevents machine-bridge-mcp@latest
79
+ )
80
+ rm -rf "$install_dir"
81
+
82
+ npm --version
83
+ machine-mcp doctor
84
+ ```
85
+
86
+ ### Windows Command Prompt
87
+
88
+ ```bat
89
+ set "MBM_INSTALL_DIR=%TEMP%\machine-bridge-mcp-install-%RANDOM%-%RANDOM%"
90
+ mkdir "%MBM_INSTALL_DIR%"
91
+ pushd "%MBM_INSTALL_DIR%"
92
+ npx --yes npm@12.0.1 install --global npm@12.0.1
93
+ npx --yes npm@12.0.1 install --global --omit=optional --allow-scripts=esbuild,workerd,sharp,fsevents machine-bridge-mcp@latest
94
+ popd
95
+ rmdir /s /q "%MBM_INSTALL_DIR%"
96
+
97
+ npm --version
98
+ machine-mcp doctor
99
+ ```
100
+
101
+ Reopen the terminal if `npm --version` still resolves to an older global installation.
102
+
103
+ ### Run from a source checkout
104
+
105
+ macOS/Linux:
106
+
107
+ ```sh
108
+ git clone https://github.com/YuLeiFuYun/machine-bridge-mcp.git
109
+ cd machine-bridge-mcp
110
+ npm install
111
+ ./mbm
112
+ ```
113
+
114
+ Windows Command Prompt:
115
+
116
+ ```bat
117
+ git clone https://github.com/YuLeiFuYun/machine-bridge-mcp.git
118
+ cd machine-bridge-mcp
119
+ npm install
120
+ .\mbm.cmd
121
+ ```
122
+
123
+ Source installs are for development or review. Released installations should normally use the global package command above.
124
+
125
+ ## 5. Interpret the initial doctor result
126
+
127
+ Run:
128
+
129
+ ```sh
130
+ machine-mcp doctor
131
+ ```
132
+
133
+ The command checks the local runtime, package layout, Wrangler availability, Cloudflare login state, and known Worker health. Before the first remote deployment, a missing Cloudflare login or Worker is expected; local runtime failures are not.
134
+
135
+ Common installation failures:
136
+
137
+ | Message or symptom | Meaning | Corrective action |
138
+ |---|---|---|
139
+ | `Unknown cli config "--allow-scripts"` | npm 11 or older executed the install | Repeat the isolated npm 12 bootstrap |
140
+ | `Invalid property "node"` or `Invalid property "devEngines.node"` | An old npm parser inspected incompatible project metadata | Run the install from a new empty temporary directory |
141
+ | `machine-mcp: command not found` | The global npm binary directory is not on `PATH` | Reopen the terminal, inspect `npm prefix -g`, and add its binary directory to `PATH` |
142
+ | Native package install warning | npm did not approve reviewed native build scripts | Use the documented `--allow-scripts` and `--omit=optional` command exactly |
143
+
144
+ ## 6. First remote start
145
+
146
+ Choose the exact workspace to expose. Prefer an explicit path rather than relying on the current directory:
147
+
148
+ ```sh
149
+ machine-mcp --workspace /path/to/project
150
+ ```
151
+
152
+ For a narrower first run:
153
+
154
+ ```sh
155
+ machine-mcp --workspace /path/to/project --profile review
156
+ ```
157
+
158
+ The first start performs these operations:
159
+
160
+ 1. canonicalizes and remembers the workspace;
161
+ 2. creates owner-only per-workspace state and credentials;
162
+ 3. opens the Wrangler/Cloudflare sign-in flow when required;
163
+ 4. deploys a Worker and Durable Object for that workspace;
164
+ 5. installs a platform-native login service unless `--no-autostart` is supplied;
165
+ 6. starts an outbound authenticated WebSocket from the local daemon to the Worker;
166
+ 7. creates the initial `owner` account when no account exists and prints its generated password once;
167
+ 8. prints the remote `/mcp` URL.
168
+
169
+ The foreground command remains attached to the terminal. Keep it running while testing. The remote Worker cannot execute local tools when no authenticated daemon is connected.
170
+
171
+ To run only in the background after setup:
172
+
173
+ ```sh
174
+ machine-mcp service start
175
+ machine-mcp service status
176
+ ```
177
+
178
+ To avoid installing autostart during a temporary test:
179
+
180
+ ```sh
181
+ machine-mcp --workspace /path/to/project --no-autostart
182
+ ```
183
+
184
+ ## 7. Connect ChatGPT
185
+
186
+ Machine Bridge prints the remote endpoint and, only when creating the first account, a one-time owner password:
187
+
188
+ ```text
189
+ MCP Server URL: https://<worker>.<account>.workers.dev/mcp
190
+ Account: owner
191
+ Password: account_password_...
192
+ ```
193
+
194
+ Save the generated password in a password manager. It is not stored locally or shown again. Do not place it in a repository, issue, screenshot, chat message, shell history note, or shared document.
195
+
196
+ Current ChatGPT developer-mode flow, as documented in OpenAI's [Connect from ChatGPT](https://developers.openai.com/apps-sdk/deploy/connect-chatgpt) guide:
197
+
198
+ 1. Open ChatGPT settings.
199
+ 2. Under **Security and login**, enable **Developer mode** if the account or workspace permits it.
200
+ 3. Open **Plugins** and create a developer-mode app.
201
+ 4. Enter a descriptive name and the exact printed `/mcp` URL.
202
+ 5. Start the connection.
203
+ 6. On the Machine Bridge authorization page, verify the displayed client name, redirect URI, and resource.
204
+ 7. Enter the Machine Bridge account name and password only after those values are recognized.
205
+ 8. Create a new chat and enable the app for that conversation.
206
+
207
+ ChatGPT navigation labels can change. The invariant is that the client must connect to the public `/mcp` endpoint and complete the OAuth authorization page served by the Worker.
208
+
209
+ ## 8. Verify the first connection
210
+
211
+ Ask the MCP host to call these tools in order:
212
+
213
+ 1. `server_info` — proves the request reached the Worker and reports whether a daemon is authenticated;
214
+ 2. `project_overview` — proves the relay reached the selected local runtime;
215
+ 3. `diagnose_runtime` — runs fixed policy, filesystem, process, shell, managed-job, and resource probes permitted by the active profile.
216
+
217
+ For an explicit local acceptance test of the canonical `full` profile:
218
+
219
+ ```sh
220
+ machine-mcp full-test --workspace /path/to/project
221
+ ```
222
+
223
+ A successful local test does not prove that a hosted MCP client exposes every advertised tool. Hosts can independently hide tools or require approvals.
224
+
225
+ ## 9. Configure a local stdio client
226
+
227
+ Generate configurations rather than manually guessing the Node path or package entrypoint:
228
+
229
+ ```sh
230
+ machine-mcp client-config --client all --workspace /path/to/project
231
+ ```
232
+
233
+ Use a narrower policy when appropriate:
234
+
235
+ ```sh
236
+ machine-mcp client-config --client all --workspace /path/to/project --profile agent
237
+ machine-mcp client-config --client all --workspace /path/to/project --profile edit
238
+ machine-mcp client-config --client all --workspace /path/to/project --profile review
239
+ ```
240
+
241
+ The generated command uses absolute executable paths so GUI applications do not depend on a shell-specific `PATH`.
242
+
243
+ A generic stdio invocation is:
244
+
245
+ ```sh
246
+ machine-mcp stdio --workspace /path/to/project --profile full
247
+ ```
248
+
249
+ The host owns the model, conversation, tool-selection loop, and approvals. Machine Bridge supplies tools; it is not a model provider.
250
+
251
+ ## 10. Enable existing-browser automation
252
+
253
+ Run once after installation:
254
+
255
+ ```sh
256
+ machine-mcp browser setup
257
+ machine-mcp browser status
258
+ ```
259
+
260
+ Then:
261
+
262
+ 1. Open the extensions page in the Chromium profile you actually use.
263
+ 2. Enable Developer mode.
264
+ 3. Load the unpacked extension directory printed by `browser setup`.
265
+ 4. Complete the local pairing page.
266
+ 5. Confirm that `machine-mcp browser status` reports the expected packaged version, protocol, and an authenticated connection.
267
+
268
+ Machine Bridge does not launch a separate browser profile. The extension controls the profile into which it was loaded, including its open tabs and logged-in sessions. Reload the unpacked extension after every Machine Bridge upgrade.
269
+
270
+ ## 11. Routine commands
271
+
272
+ ```sh
273
+ machine-mcp status
274
+ machine-mcp doctor
275
+ machine-mcp workspace show
276
+ machine-mcp service status
277
+ machine-mcp browser status
278
+ machine-mcp account list
279
+ ```
280
+
281
+ Useful operational actions:
282
+
283
+ ```sh
284
+ machine-mcp service stop
285
+ machine-mcp service start
286
+ machine-mcp rotate-secrets
287
+ machine-mcp account add alice reviewer
288
+ machine-mcp account rotate-password alice
289
+ machine-mcp resource list
290
+ machine-mcp job list
291
+ ```
292
+
293
+ `rotate-secrets` invalidates every account access token and requires all clients to authorize again. For targeted revocation, disable an account, change its role, rotate its password, or remove it.
294
+
295
+ ## 12. Work with more than one workspace
296
+
297
+ Each canonical workspace receives independent state, policy, credentials, Worker name, resource registry, job directory, startup lock, and daemon lock. Select workspaces explicitly:
298
+
299
+ ```sh
300
+ machine-mcp --workspace /path/to/project-a
301
+ machine-mcp --workspace /path/to/project-b
302
+ ```
303
+
304
+ Do not point two logical trust domains at one broad parent directory merely to reduce configuration. A workspace is an authorization boundary for confined profiles and an operational identity even when `full` permits paths outside it.
305
+
306
+ ## 13. Upgrade
307
+
308
+ Repeat the isolated global installation, then start Machine Bridge in the target workspace:
309
+
310
+ ```sh
311
+ install_dir="$(mktemp -d)"
312
+ (
313
+ cd "$install_dir"
314
+ npx --yes npm@12.0.1 install --global npm@12.0.1
315
+ npx --yes npm@12.0.1 install --global --omit=optional --allow-scripts=esbuild,workerd,sharp,fsevents machine-bridge-mcp@latest
316
+ )
317
+ rm -rf "$install_dir"
318
+
319
+ machine-mcp --workspace /path/to/project --verbose
320
+ ```
321
+
322
+ The foreground start performs bounded takeover of an earlier verified daemon when safe. If it refuses:
323
+
324
+ ```sh
325
+ machine-mcp service status
326
+ machine-mcp service stop
327
+ machine-mcp --workspace /path/to/project --verbose
328
+ ```
329
+
330
+ After upgrading, reload the unpacked browser extension before relying on browser tools.
331
+
332
+ ## 14. Troubleshooting by layer
333
+
334
+ Use the first failing layer rather than changing unrelated settings.
335
+
336
+ ### Package or runtime layer
337
+
338
+ ```sh
339
+ node --version
340
+ npm --version
341
+ which machine-mcp # macOS/Linux
342
+ where machine-mcp # Windows
343
+ machine-mcp doctor
344
+ ```
345
+
346
+ ### Cloudflare/deployment layer
347
+
348
+ ```sh
349
+ machine-mcp doctor
350
+ machine-mcp status
351
+ machine-mcp --workspace /path/to/project --force-worker --verbose
352
+ ```
353
+
354
+ A forced deployment changes cloud state. Use it only when the normal deployment hash/health reconciliation is insufficient.
355
+
356
+ ### Local service/daemon layer
357
+
358
+ ```sh
359
+ machine-mcp service status
360
+ machine-mcp service stop
361
+ machine-mcp service start
362
+ ```
363
+
364
+ A connected Worker with no authenticated daemon can authorize OAuth successfully but cannot execute local tools.
365
+
366
+ ### MCP-host layer
367
+
368
+ If `server_info` or `project_overview` never returns a structured Machine Bridge result, the host may not have delivered the call. Check the host's plugin/app enablement, tool permissions, approval UI, and connection status.
369
+
370
+ ### Operating-system layer
371
+
372
+ If `diagnose_runtime` reaches the daemon but a probe fails, inspect filesystem permissions, macOS TCC/SIP, Windows ACLs, endpoint-security policy, shell availability, or container restrictions. Changing Machine Bridge to `full` cannot override the operating system.
373
+
374
+ ### Browser layer
375
+
376
+ ```sh
377
+ machine-mcp browser status
378
+ machine-mcp browser setup
379
+ ```
380
+
381
+ Verify that the extension is loaded in the intended profile, its version matches the installed package, and its badge reports authenticated readiness.
382
+
383
+ For detailed recovery procedures, see [OPERATIONS.md](OPERATIONS.md).
384
+
385
+ ## 15. Remove Machine Bridge
386
+
387
+ Remove the selected deployment, service, and local state:
388
+
389
+ ```sh
390
+ machine-mcp uninstall
391
+ npm uninstall -g machine-bridge-mcp
392
+ ```
393
+
394
+ Keep the deployed Worker while removing local state and autostart:
395
+
396
+ ```sh
397
+ machine-mcp uninstall --keep-worker
398
+ ```
399
+
400
+ Uninstall is fail-closed. If a verified daemon, active managed job, unreadable lock, or service removal cannot be resolved safely, Machine Bridge retains state for diagnosis instead of deleting only part of the installation.
401
+
402
+ ## 16. Before sharing access
403
+
404
+ The current release supports named accounts with targeted revocation and four roles: `reviewer`, `editor`, `operator`, and `owner`. Roles are enforced in the Worker and local runtime, but all accounts still share one daemon and OS user. Use separate deployments and external isolation for mutually untrusted users or hard tenant boundaries. Read [MULTI_ACCOUNT.md](MULTI_ACCOUNT.md) before sharing a deployment.
package/docs/LOGGING.md CHANGED
@@ -79,7 +79,7 @@ The implementation omits:
79
79
  - stdin, stdout, and stderr;
80
80
  - file, patch, image, and temporary-file content;
81
81
  - OAuth request bodies;
82
- - connection passwords, daemon secrets, authorization codes, and access tokens;
82
+ - account passwords, account-administration secrets, daemon secrets, authorization codes, and access tokens;
83
83
  - registered resource values and source paths;
84
84
  - browser pairing tokens, page URLs/source, DOM metadata, form values, uploaded file bytes, and screenshots;
85
85
  - application names, Accessibility trees, selectors, and entered values;
@@ -118,7 +118,7 @@ logs/daemon.err.log
118
118
 
119
119
  Existing files are opened without following symbolic links where supported and tail-trimmed on UTF-8/line boundaries before startup. Background services use `warn`, so ordinary tool traffic and brief relay interruptions do not cause sustained growth.
120
120
 
121
- The log format has an explicit schema marker. When the schema changes, the daemon copies a bounded tail of each prior active log to an owner-only `daemon.out.legacy.log` or `daemon.err.legacy.log` snapshot, clears the active file, and advances the marker. The migration is idempotent and keeps historical transport-format lines visibly separate from current behavior.
121
+ The log format has an explicit schema marker. If the marker differs from the current format, the daemon clears the active files before startup and writes the current marker. Runtime code recognizes only `daemon.out.log` and `daemon.err.log`; it does not parse or archive other log formats.
122
122
 
123
123
  Each managed job has owner-only runner diagnostic logs. Child-step output is retained only in bounded, redacted job results according to `capture_output`; it is not copied into daemon or runner operational logs.
124
124
 
@@ -277,7 +277,7 @@ Per-workspace jobs are stored below the owner-only profile directory. Active job
277
277
 
278
278
  Retained public job data contains bounded status and redacted results. Up to 50 jobs are retained for up to seven days. Private runtime copies are removed after the finally phase. Runner stdout/stderr log files contain only runner-level diagnostics; step output is not written to those operational logs.
279
279
 
280
- The detached runner records both PID and process start time. Recovery therefore rejects a reused PID instead of treating an unrelated process as the active runner; legacy numeric PID files remain readable during migration. Recovery-lock handoff preserves a random ownership token, and the runner removes only a lock whose PID, token, and file snapshot still match. Timeout and cancellation terminate the process group/tree, retain a referenced forced-escalation timer, and clean descendants that ignore graceful termination before the runner exits.
280
+ The detached runner records a structured owner record containing PID and process start time. Recovery rejects a reused PID instead of treating an unrelated process as the active runner. Numeric-only runner records are invalid. Recovery-lock handoff preserves a random ownership token, and the runner removes only a lock whose PID, token, and file snapshot still match. Timeout and cancellation terminate the process group/tree, retain a referenced forced-escalation timer, and clean descendants that ignore graceful termination before the runner exits.
281
281
 
282
282
  Missing job JSON is distinct from unreadable or invalid job JSON. Permission, type, symbolic-link, size, UTF-8, I/O, and parse failures retain the job directory, produce an `unreadable` status for listing, skip automatic recovery/pruning, and block uninstall until an operator inspects the state.
283
283
 
@@ -0,0 +1,123 @@
1
+ # Multi-account authorization and tenancy
2
+
3
+ ## What isolation means
4
+
5
+ Machine Bridge remote mode supports several named accounts on one workspace Worker. Each account has an independent password, role, active state, version, OAuth authorization codes, and access tokens. Account changes revoke only that account's outstanding credentials.
6
+
7
+ This is application-level authorization, not operating-system isolation. All accounts ultimately reach one local daemon running as one OS user. Roles limit which Machine Bridge tools can be listed and invoked; they do not create separate filesystems, browser profiles, process namespaces, keychains, network identities, or kernel security boundaries.
8
+
9
+ Use separate OS accounts, containers, VMs, state roots, Workers, and workspaces when users are mutually untrusted or require hard isolation.
10
+
11
+ ## Roles
12
+
13
+ | Role | Effective local profile | Typical use |
14
+ |---|---|---|
15
+ | `reviewer` | `review` | Read-only workspace, Git, image, resource, and job inspection |
16
+ | `editor` | `edit` | Reviewer access plus deterministic file mutation |
17
+ | `operator` | `agent` | Editor access plus workspace-confined direct process execution and sessions |
18
+ | `owner` | `full` | Complete bridge authority, including shell, unrestricted paths, browser, applications, resources, jobs, and account administration |
19
+
20
+ The effective tool set is the intersection of:
21
+
22
+ 1. the account role;
23
+ 2. the policy advertised by the connected local daemon;
24
+ 3. the tools actually available from that daemon.
25
+
26
+ The Worker filters `tools/list` and rejects unauthorized calls before relay. Every accepted relay call also carries `account_id`, `account_version`, and `role`; the local runtime validates the role again before dispatch. The shared policy contract remains the single source of capability semantics.
27
+
28
+ ## Account lifecycle
29
+
30
+ List accounts:
31
+
32
+ ```sh
33
+ machine-mcp account list
34
+ ```
35
+
36
+ Create an account. The generated password is displayed once and is not stored locally:
37
+
38
+ ```sh
39
+ machine-mcp account add alice reviewer
40
+ machine-mcp account add build-bot operator
41
+ ```
42
+
43
+ Change role or active state:
44
+
45
+ ```sh
46
+ machine-mcp account role alice editor
47
+ machine-mcp account disable build-bot
48
+ machine-mcp account enable build-bot
49
+ ```
50
+
51
+ Rotate one account's password:
52
+
53
+ ```sh
54
+ machine-mcp account rotate-password alice
55
+ ```
56
+
57
+ Remove an account:
58
+
59
+ ```sh
60
+ machine-mcp account remove alice
61
+ machine-mcp account remove alice --yes
62
+ ```
63
+
64
+ The final active owner cannot be disabled, demoted, or removed. This prevents an accidental administrative lockout.
65
+
66
+ ## OAuth model
67
+
68
+ An OAuth `client_id` identifies client software and its registered redirect URIs. It is not an account. One OAuth client may be authorized by several Machine Bridge accounts, and one account may authorize several OAuth clients.
69
+
70
+ An authorization code records:
71
+
72
+ - OAuth client ID;
73
+ - account ID and account version;
74
+ - role;
75
+ - redirect URI;
76
+ - PKCE S256 challenge;
77
+ - scope and protected resource;
78
+ - expiration.
79
+
80
+ An access-token record contains the same account binding plus the deployment-wide token version. Token values are stored only as SHA-256 lookup keys. Account passwords are CLI-generated 256-bit tokens. The Worker stores independent salted HMAC-SHA-256 verifiers and rejects arbitrary human-chosen passwords; the token entropy, rather than a CPU-intensive dictionary-hardening loop, provides offline-guessing resistance within the Worker CPU budget.
81
+
82
+ At each authenticated request, the Worker verifies that the account still exists, is active, and has the same version and role recorded in the token. A password rotation, role change, suspension, or removal increments or removes that account state and invalidates only its codes and tokens.
83
+
84
+ `machine-mcp rotate-secrets` is intentionally broader. It rotates the account-administration secret, daemon secret, and deployment-wide token version, invalidating every account token and requiring all clients to authorize again.
85
+
86
+ ## Administrative boundary
87
+
88
+ Account administration is not exposed as an MCP tool. It uses an owner-only local secret to call private Worker administration endpoints from the CLI. The administration secret is stored only in owner-protected local state and Cloudflare Worker secrets; it is never printed by `status`, sent through MCP, or used as an account password.
89
+
90
+ The first start of a new deployment creates an `owner` account automatically and prints its generated password once. Subsequent starts do not display account passwords.
91
+
92
+ ## Concurrency and revocation
93
+
94
+ Pending calls remain bound to the OAuth access token and JSON-RPC request ID. Duplicate in-flight IDs under one token are rejected. Account revocation blocks new requests immediately; calls already relayed remain subject to ordinary cancellation, deadlines, relay disconnect cleanup, and local role validation.
95
+
96
+ Remote relay loss cancels every relay-owned local call and terminates its child process tree. Process promises settle on cancellation even when a child does not emit `close`; process ownership remains tracked until actual exit.
97
+
98
+ ## Audit and privacy
99
+
100
+ Operational metrics identify tools and stable error classes, not account passwords, bearer tokens, arguments, command text, file content, or results. `server_info` may return the authenticated account ID, role, and version to that account so the client can verify its authorization context.
101
+
102
+ Account names and display names are operator-selected metadata. Do not use secrets, email addresses, customer identifiers, or other unnecessary personal data in account names.
103
+
104
+ ## Deployment topology
105
+
106
+ One workspace normally maps to one Worker name, one Durable Object instance, one local state profile, and one active daemon. Multiple accounts share this topology and its OS trust boundary.
107
+
108
+ Use a separate deployment when any of these differ:
109
+
110
+ - OS user or machine owner;
111
+ - workspace trust domain;
112
+ - browser/profile trust;
113
+ - network or credential-store authority;
114
+ - billing or incident-response boundary;
115
+ - requirement for hard tenant isolation.
116
+
117
+ Do not place several unrelated machines or mutually untrusted teams behind one broad owner deployment merely to reduce administration.
118
+
119
+ ## Security limits
120
+
121
+ A `reviewer` cannot invoke mutation or process tools through Machine Bridge, but data readable by the local OS user may still be exposed by a bug, compromised dependency, or incorrectly broad workspace. An `operator` can run interpreters, package managers, compilers, and repository scripts; direct argv execution therefore remains powerful even without a shell. An `owner` has effectively the authority of the local OS user and any browser or Accessibility permissions granted to the runtime.
122
+
123
+ Roles are useful defense in depth and targeted revocation. They are not substitutes for least-privilege OS design, sandboxing, endpoint security, Cloudflare account protection, or careful review of untrusted repositories and instructions.
@@ -67,7 +67,7 @@ Application UI inspection/actions require Accessibility permission for the Node/
67
67
 
68
68
  `machine-mcp` is a foreground command. It remains attached to the terminal, defaults to `info` logging, and stops on `Ctrl+C`. `machine-mcp service start` launches the installed platform service in the background and returns to the shell; that service uses `warn` logging.
69
69
 
70
- A global npm install changes the CLI files on disk but does not replace an already running Node process. Startup and other state-changing CLI operations use a token/process-identity lock and wait up to 30 seconds for a normal concurrent startup to finish; a short launchd/systemd overlap is therefore serialized rather than reported immediately as an error. On a normal foreground start, Machine Bridge unloads the platform service and then independently examines the workspace daemon lock. This second path handles a detached/orphan `--daemon-only` process that launchd/systemd/task scheduling no longer tracks, including legacy locks without mode/version metadata. Before sending `SIGTERM`, Machine Bridge verifies PID and process start time plus the live command line, entrypoint, canonical workspace, canonical state root, and daemon-only flag. It waits up to 15 seconds for both the PID and lock to disappear and does not add a later forced-kill escalation. On POSIX, a daemon that handles and ignores `SIGTERM` therefore reaches the bounded timeout and remains alive. Node maps the supported termination signals to immediate process termination on Windows, so the same verified-daemon stop normally completes rather than exercising the POSIX timeout branch. A foreground or unverifiable process is left untouched; stop a foreground instance with `Ctrl+C`.
70
+ A global npm install changes the CLI files on disk but does not replace an already running Node process. Startup and other state-changing CLI operations use a token/process-identity lock and wait up to 30 seconds for a normal concurrent startup to finish; a short launchd/systemd overlap is therefore serialized rather than reported immediately as an error. On a normal foreground start, Machine Bridge unloads the platform service and then independently examines the workspace daemon lock. This second path handles a detached/orphan `--daemon-only` process that launchd, systemd, or Task Scheduler no longer tracks. Only current lock records containing service mode, version, PID, process start time, entrypoint, workspace, and state root are eligible for takeover. Before sending `SIGTERM`, Machine Bridge verifies PID and process start time plus the live command line, entrypoint, canonical workspace, canonical state root, and daemon-only flag. It waits up to 15 seconds for both the PID and lock to disappear and does not add a later forced-kill escalation. On POSIX, a daemon that handles and ignores `SIGTERM` therefore reaches the bounded timeout and remains alive. Node maps the supported termination signals to immediate process termination on Windows, so the same verified-daemon stop normally completes rather than exercising the POSIX timeout branch. A foreground or unverifiable process is left untouched; stop a foreground instance with `Ctrl+C`.
71
71
 
72
72
  `machine-mcp service status [WORKSPACE]` reports two independent layers: the platform service (`active`) and `workspace_daemon`, plus `effective_active` and `orphaned_workspace_daemon` summary flags. On macOS it is possible for launchd to report inactive while a prior Node process remains alive with parent PID 1; that is an orphan-daemon condition, not proof that the daemon stopped. `service stop` unloads the provider when present and then terminates only a verified service-style workspace daemon. `service uninstall` and full uninstall are ordered fail-closed operations: provider stop → verified daemon stop(s) → definition removal. A failed or ambiguous stop leaves definitions and state intact. If takeover reaches its deadline, run:
73
73
 
@@ -91,7 +91,7 @@ npm --version
91
91
  machine-mcp --verbose
92
92
  ```
93
93
 
94
- `Unknown cli config "--allow-scripts"` proves the package installation ran under npm 11 or older. `Invalid property "node"` or `Invalid property "devEngines.node"` means npm parsed a legacy `devEngines` object; inspect the npm debug log to identify its source rather than assuming it belongs to Machine Bridge. The published package declares both Node.js 26 and npm 12 in `engines`, and `machine-mcp doctor` checks both active versions. Keep `--omit=optional` in the install command. Without it npm may resolve optional `fsevents` and warn that its install script was not included in `allowScripts`; Machine Bridge does not require that development-time watcher at runtime.
94
+ `Unknown cli config "--allow-scripts"` proves the package installation ran under npm 11 or older. `Invalid property "node"` or `Invalid property "devEngines.node"` means npm parsed an outdated `devEngines` object; inspect the npm debug log to identify its source rather than assuming it belongs to Machine Bridge. The published package declares both Node.js 26 and npm 12 in `engines`, and `machine-mcp doctor` checks both active versions. Keep `--omit=optional` in the install command. Without it npm may resolve optional `fsevents` and warn that its install script was not included in `allowScripts`; Machine Bridge does not require that development-time watcher at runtime.
95
95
 
96
96
  ## State-root safety and removal
97
97
 
@@ -108,7 +108,7 @@ Uninstall acquires a state-root `maintenance.lock` that blocks new profile/state
108
108
  Stable errors include `policy_denied`, `invalid_request`, `timeout`, `cancelled`, `network_error`, `unavailable`, `limit_exceeded`, and `integrity_error`, with retryability metadata. Diagnose by code first; free-form messages are guidance, not an API contract.
109
109
 
110
110
 
111
- Windows Scheduled Task command lines use CRT-compatible quoting, including trailing backslashes and drive-root paths. Remote autostart definitions prefer a stable PATH alias that resolves to the currently running Node executable and persist a sanitized absolute-only service `PATH` containing the Node/CLI directories, the installer's absolute PATH entries, and platform defaults. This avoids versioned Homebrew-style paths becoming invalid after upgrades and prevents launchd/systemd from falling back to a minimal system-only PATH. Re-run `machine-mcp service install` after changing Node installation families or PATH layout. A service-style `--daemon-only` start that finds the same workspace daemon already running is an idempotent no-op: it exits successfully without repeating warnings or readiness output; explicit policy/secret/change requests still report that changes were not applied. Autostart logs are stored under the state root in `logs/daemon.out.log` and `logs/daemon.err.log`. Installed services pass `--log-level warn --log-format json`, so each active line is a bounded JSON event suitable for ingestion. Files are owner-only where supported and tail-trimmed before daemon startup. On a logging-schema upgrade, bounded prior content is moved into `daemon.out.legacy.log` and `daemon.err.legacy.log`; use the active filenames when diagnosing current behavior.
111
+ Windows Scheduled Task command lines use CRT-compatible quoting, including trailing backslashes and drive-root paths. Remote autostart definitions prefer a stable PATH alias that resolves to the currently running Node executable and persist a sanitized absolute-only service `PATH` containing the Node/CLI directories, the installer's absolute PATH entries, and platform defaults. This avoids versioned Homebrew-style paths becoming invalid after upgrades and prevents launchd/systemd from falling back to a minimal system-only PATH. Re-run `machine-mcp service install` after changing Node installation families or PATH layout. A service-style `--daemon-only` start that finds the same workspace daemon already running is an idempotent no-op: it exits successfully without repeating warnings or readiness output; explicit policy/secret/change requests still report that changes were not applied. Autostart logs are stored under the state root in `logs/daemon.out.log` and `logs/daemon.err.log`. Installed services pass `--log-level warn --log-format json`, so each active line is a bounded JSON event suitable for ingestion. Files are owner-only where supported and tail-trimmed before daemon startup. If the log schema marker does not match the current format, the active files are cleared before startup and the current marker is written. Runtime code reads and maintains only the active filenames.
112
112
 
113
113
  Logging is level-based:
114
114
 
@@ -214,7 +214,7 @@ Defense-in-depth limits include:
214
214
 
215
215
  ## Upgrade behavior
216
216
 
217
- Policy revision 4 makes named profiles canonical and evaluates compound tool requirements from the shared contract. A state entry labelled `full` is repaired to writes, direct processes, process sessions, shell execution, unrestricted direct filesystem paths, absolute path output, the complete parent environment, and the complete tool catalog. CLI capability overrides are stored as `custom`. The exact pre-0.4 implicit-default shape is still migrated to the current `full` default; explicit restrictive and identified custom profiles remain preserved.
217
+ Policy revision 5 makes named profiles canonical and evaluates compound tool requirements from the shared contract. A state entry labelled `full` means writes, direct processes, process sessions, shell execution, unrestricted direct filesystem paths, absolute path output, the complete parent environment, and the complete tool catalog. CLI capability overrides are stored as `custom`. Persisted policies from another revision are rejected rather than interpreted.
218
218
 
219
219
  `full` removes Machine Bridge's own profile/path/environment/shell denials and makes the complete catalog available to the relay. It does not force a connector host to expose every relayed tool, and the server cannot see the host's final subset. It also does not override operating-system access controls, endpoint security, remote authentication, cloud IAM, `sudo`, or independent MCP-host/platform policy.
220
220
 
@@ -2,7 +2,7 @@
2
2
 
3
3
  > Generated from `src/shared/policy-contract.json` and `src/shared/tool-catalog.json`. Do not edit this file manually.
4
4
 
5
- Policy revision: **4**. Default profile: **full**.
5
+ Policy revision: **5**. Default profile: **full**.
6
6
 
7
7
  ## Canonical profiles
8
8
 
package/docs/PRIVACY.md CHANGED
@@ -13,7 +13,7 @@ npm run privacy:history
13
13
 
14
14
  `privacy:check` scans tracked and unignored new UTF-8 files and relative names for generic/encrypted/algorithm-specific private-key headers, AWS/GitHub/GitLab/npm/Slack/Google/live-payment/API token forms, JWT-shaped bearer values, embedded-credential URLs, absolute user-home paths, non-example email/`user@host` identifiers, credential-shaped filenames, and locally configured private identifiers. A tracked `.npmrc` is parsed: non-secret repository settings such as `engine-strict=true` are allowed, while authentication/identity keys, environment interpolation, and embedded credentials fail closed. Publication-surface symbolic links are rejected rather than followed. Binary, invalid UTF-8, and files above the bounded scanner limit require explicit manual review instead of being silently skipped. Findings report only file, line, and rule; the matched value is never printed.
15
15
 
16
- `privacy:history` first runs the same current-tree checks, then scans every reachable historical blob path and bounded UTF-8 blob plus every reachable commit message. It catches values that were committed and later deleted. Standard public Dependabot signing trailers are ignored only in that exact commit-message context; the same non-example address remains disallowed in ordinary files. The local `.privacy-denylist` is also applied to history, so a developer may discover legacy identifiers that CI cannot know. Such a finding is real publication history, but ordinary commits cannot erase it.
16
+ `privacy:history` first runs the same current-tree checks, then scans every reachable historical blob path and bounded UTF-8 blob plus every reachable commit message. It catches values that were committed and later deleted. Standard public Dependabot signing trailers are ignored only in that exact commit-message context; the same non-example address remains disallowed in ordinary files. The local `.privacy-denylist` is also applied to history, so a developer may discover older identifiers that CI cannot know. Such a finding is real publication history, but ordinary commits cannot erase it.
17
17
 
18
18
  Git author and committer identity headers are canonical Git metadata rather than blob or commit-message content and are not automatically rejected. Audit them separately with `git log`; changing historical identity metadata requires a coordinated history rewrite and force-update of affected refs.
19
19
 
@@ -36,7 +36,7 @@ Commit and squash-merge subjects follow Conventional Commits:
36
36
  <type>[optional scope][optional !]: <imperative description>
37
37
  ```
38
38
 
39
- Allowed types are `feat`, `fix`, `docs`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `security`, `release`, and `revert`. `feat` and `fix` carry their normal semantic-version meaning. A breaking change uses `!` and explains migration impact in the body or a `BREAKING CHANGE:` footer.
39
+ Allowed types are `feat`, `fix`, `docs`, `refactor`, `perf`, `test`, `build`, `ci`, `chore`, `security`, `release`, and `revert`. `feat` and `fix` carry their normal semantic-version meaning. A breaking change uses `!` and explains upgrade impact in the body or a `BREAKING CHANGE:` footer.
40
40
 
41
41
  A good change explains both **what changed and why**. Commits should be logically coherent, but intermediate branch history may be amended because the final pull request is squash-merged. Pull-request titles must satisfy the same format because they become the `main` commit subject.
42
42