machine-bridge-mcp 0.4.2 → 0.6.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,56 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.0 - 2026-07-10
4
+
5
+ ### Added
6
+
7
+ - Add `diagnose_runtime`, a fixed-input layered diagnostic that distinguishes requests reaching the daemon from Machine Bridge policy, local filesystem, process-spawn, shell, managed-job storage, and registered-resource failures. `machine-mcp doctor` runs the same local probes.
8
+ - Add operator-registered local file resources. Credentials and other local-only files can be referenced by alias and injected into managed steps through a private copied path, stdin, or an environment variable without sending file contents or source paths through MCP.
9
+ - Add durable managed jobs with ordered argv steps, bounded output, job-scoped temporary files, detached execution, cancellation, idempotent `finally_steps`, dead-runner detection, bounded recovery, and local CLI inspection/cancellation.
10
+ - Add two-phase handoff: `stage_job` persists a validated non-running plan for operator review, and `machine-mcp job approve JOB_ID` provides explicit local authorization when a host blocks execution-class tools.
11
+ - Add a local JSON fallback with `machine-mcp job submit plan.json` for situations where the MCP host cannot deliver an execution request.
12
+
13
+ ### Security and privacy
14
+
15
+ - Keep resource values out of MCP plans and results. Referenced files are reopened, bounded, hashed at acceptance, verified before use, copied with owner-only permissions, and removed after cleanup.
16
+ - Redact exact resource paths, exact UTF-8 values, and bounded exact base64/hex forms from retained output. Add `capture_output: "discard"` for credential-consuming commands where output must not be retained.
17
+ - Delete active job plans, runner PID files, temporary helper contents, argv, stdin, environment overrides, resource source paths, and hashes after terminal commit. Retain only bounded status/redacted results for up to seven days and 50 jobs.
18
+ - Enforce canonical managed-job cwd containment in restricted profiles, bounded per-job resources/temporary files/output, no-follow plan/resource reads, owner-only job state, bounded runner diagnostics, recovery mutual exclusion, and a three-attempt automatic recovery limit.
19
+ - Refuse uninstall while detached jobs remain active. Later profile changes affect new submissions; accepted running jobs require explicit cancellation.
20
+ - Clarify that managed jobs are durability and local-authorization mechanisms, not a bypass for MCP-host, operating-system, or endpoint-security policy.
21
+
22
+ ### Operations and tests
23
+
24
+ - Add local resource commands: `resource add`, `list`, `check`, and `remove`.
25
+ - Add local managed-job commands: `job submit`, `inspect`, `approve`, `list`, `read`, and `cancel`. Approval is interactive by default; `--yes` is required for non-interactive JSON approval.
26
+ - Add state schema version 5 for resource registry metadata while redacting resource source paths from normal status output.
27
+ - Add regression coverage for staging/approval/cancel-before-start, stdio disconnect survival, resource replacement races and output redaction, job-scoped helper cleanup, failure/timeout/cancellation finally paths, concurrent recovery, corrupt plans, output budgets, local CLI fallback, uninstall refusal, and cross-profile tool exposure.
28
+ - Add a dedicated managed-jobs/resource operations and threat-model guide.
29
+
30
+ ## 0.5.0 - 2026-07-10
31
+
32
+ ### Changed
33
+
34
+ - Replace routine per-tool success chatter with explicit log levels. Foreground mode defaults to `info`, autostart uses `warn`, fast successful calls are debug-only, and successful calls over 30 seconds remain visible as slow-call events. `--verbose` maps to `debug`; `--quiet` maps to `error`; `--log-level` accepts `error`, `warn`, `info`, or `debug`.
35
+ - Record policy origin and revision in state and daemon metadata. The exact legacy implicit-default policy shape is migrated once to the current maximum-permission `full` profile, while explicit named profiles and identified custom policies are preserved.
36
+ - Redact JSON connection credentials and standalone secret-rotation output by default. Printing the client connection password now requires the explicit reconnect flag; the daemon secret is never printed in full.
37
+ - Centralize server name, MCP protocol versions, and instructions in shared metadata consumed by both Worker and local transports.
38
+ - Reject obsolete removed-local-API flags instead of silently accepting them.
39
+
40
+ ### Security and privacy
41
+
42
+ - Upgrade and pin `ws` to 8.21.0 to address current memory-disclosure and denial-of-service advisories; pin the reviewed Wrangler runtime version.
43
+ - Bind text, image, and search reads to an opened file handle, enforce limits against that handle, and avoid `stat`/read growth races.
44
+ - Bound state, configuration, marker, lock, service-command, log-message, and structured-log sizes. Reject symbolic-link state files, use no-follow reads where supported, and make service-log tail trimming UTF-8 and line safe.
45
+ - Remove stale temporary Worker secret files when their owner process is gone, not only after an age threshold.
46
+ - Document and expose that Machine Bridge has no sensitive-filename blacklist. Local `full` permits any OS-readable UTF-8 regular file, but cannot override operating-system or independent MCP-host/platform policy.
47
+
48
+ ### Tests and operations
49
+
50
+ - Add regression coverage for legacy policy migration, sensitive-looking files outside the workspace, inherited full-profile environment, default daemon/stdio log suppression, log bounding, state write bounds, service warning-level configuration, and shared metadata drift.
51
+ - Audit both complete and production-only dependency graphs in GitHub Actions.
52
+ - Document npm's scoped install-script approval for Wrangler native dependencies and add a dedicated logging/observability reference.
53
+
3
54
  ## 0.4.2 - 2026-07-10
4
55
 
5
56
  ### Fixed
package/README.md CHANGED
@@ -21,18 +21,18 @@ Local clients such as Claude Desktop, Cursor, and Codex CLI
21
21
  local runtime
22
22
  ```
23
23
 
24
- The remote Worker authenticates and relays calls. It cannot directly read local files or start local processes. File, Git, image, patch, and process operations execute in the local runtime.
24
+ The remote Worker authenticates and relays calls. It cannot directly read local files or start local processes. File, Git, image, patch, process, diagnostic, and managed-job operations execute in the local runtime.
25
25
 
26
26
  ## Default behavior and policy profiles
27
27
 
28
28
  A newly selected workspace starts with the maximum-permission `full` profile for low-friction operation:
29
29
 
30
- - all read, write, edit, patch, image, Git, direct-process, process-session, and shell tools are available;
30
+ - all read, write, edit, patch, image, Git, diagnostic, direct-process, process-session, managed-job, and shell tools are available;
31
31
  - direct filesystem tools may use paths outside the selected workspace;
32
32
  - tool results may return absolute paths;
33
33
  - child processes inherit the complete parent environment.
34
34
 
35
- Existing workspace state keeps its saved policy during upgrade. Use `--profile full` to explicitly move an older workspace to the new maximum-permission behavior.
35
+ Policy state records whether it came from the default, an explicit named profile, or custom overrides. Version 0.5 migrates the exact pre-0.4 implicit-default shape to `full` once; explicit restrictive profiles and identified custom policies remain unchanged.
36
36
 
37
37
  | Profile | File edits | Direct argv processes | Shell commands | Filesystem scope | Process environment |
38
38
  |---|---:|---:|---:|---|---|
@@ -48,10 +48,12 @@ This default prioritizes usability, not least privilege. `run_process` and proce
48
48
  Node.js 22 or newer is required.
49
49
 
50
50
  ```sh
51
- npm install -g machine-bridge-mcp@latest
51
+ npm install -g --allow-scripts=esbuild,workerd,sharp machine-bridge-mcp@latest
52
52
  ```
53
53
 
54
- From a source checkout:
54
+ Recent npm releases may otherwise warn that Wrangler's native dependencies (`esbuild`, `workerd`, and `sharp`) have install scripts awaiting approval. The scoped command above approves only those packages for this installation; it does not change the user's global npm policy. A plain install can still work when compatible prebuilt artifacts are already available, but `machine-mcp doctor` is the authoritative runtime check.
55
+
56
+ From a source checkout, the checked-in exact-version `allowScripts` policy approves the reviewed native dependencies:
55
57
 
56
58
  ```sh
57
59
  npm install
@@ -118,6 +120,9 @@ The default is `full`. Narrow or customize it with explicit flags:
118
120
  --full-env
119
121
  --unrestricted-paths
120
122
  --absolute-paths
123
+ --log-level error|warn|info|debug
124
+ --verbose
125
+ --quiet
121
126
  ```
122
127
 
123
128
  Important distinctions:
@@ -126,7 +131,82 @@ Important distinctions:
126
131
  - `--unrestricted-paths=false`, `--absolute-paths=false`, and `--full-env=false` can narrow those individual settings.
127
132
  - `--absolute-paths` changes returned path metadata; it does not independently grant additional access.
128
133
  - In isolated-environment profiles, commands receive private HOME, temp, and cache directories plus a small set of platform variables.
129
- - Files with sensitive-looking names are not automatically blocked inside the workspace. A workspace `.env` remains readable when read tools are enabled.
134
+ - The server has no filename blacklist. Under `full`, direct read tools may read any UTF-8 regular file that the local OS user can access, including files outside the workspace and names such as `.env`, `passwords.txt`, or private-key files.
135
+ - Maximum local policy does not override operating-system permissions, macOS TCC/SIP, Windows ACLs, container boundaries, or independent safety rules imposed by the MCP host/platform. A host-generated “sensitive file” denial is outside this server's enforcement layer.
136
+
137
+ ## Diagnose host, bridge, and local execution failures
138
+
139
+ A displayed `full` policy proves only that Machine Bridge has enabled its own capabilities. Execution can still be denied by the MCP host/connector, macOS TCC/SIP, Unix permissions, Windows ACLs, shell policy, or endpoint-security software.
140
+
141
+ Use:
142
+
143
+ ```text
144
+ diagnose_runtime
145
+ ```
146
+
147
+ or locally:
148
+
149
+ ```sh
150
+ machine-mcp doctor
151
+ ```
152
+
153
+ A successful `diagnose_runtime` response proves that request reached the local daemon. It then reports fixed probes for Machine Bridge policy, private filesystem access, direct process spawning, shell execution, managed-job storage, and registered resources. If the host blocks the tool call before any structured response, the server cannot diagnose that request because it never received it.
154
+
155
+ ## Managed jobs and local resources
156
+
157
+ Long, remote, multi-step, or cleanup-sensitive work should not depend on a sequence of later MCP calls remaining available. `start_job` durably accepts ordered argv steps plus `finally_steps`, then launches an independent local runner. It continues after MCP disconnects or later host-side tool refusals.
158
+
159
+ When the host blocks execution-class tools but still permits state mutation, `stage_job` stores the same validated plan without starting any process. The operator can review it with `machine-mcp job inspect JOB_ID` and explicitly authorize execution with `machine-mcp job approve JOB_ID`. Cancelling a staged plan does not run main or finally steps.
160
+
161
+ Register credential/key files locally without sending their contents through MCP:
162
+
163
+ ```sh
164
+ chmod 600 ~/.ssh/racknerd_ed25519
165
+ machine-mcp resource add racknerd-key ~/.ssh/racknerd_ed25519
166
+ machine-mcp resource list
167
+ ```
168
+
169
+ A job refers to the alias rather than the value:
170
+
171
+ ```json
172
+ {
173
+ "name": "remote maintenance",
174
+ "steps": [
175
+ {
176
+ "argv": [
177
+ "ssh",
178
+ "-i",
179
+ "{{resource:racknerd-key}}",
180
+ "root@example",
181
+ "sh",
182
+ "-s"
183
+ ],
184
+ "stdin": "set -eu\n# remote repair commands\n"
185
+ }
186
+ ],
187
+ "finally_steps": [
188
+ {
189
+ "argv": ["ssh", "-i", "{{resource:racknerd-key}}", "root@example", "rm", "-f", "/tmp/helper"],
190
+ "allow_failure": true
191
+ }
192
+ ]
193
+ }
194
+ ```
195
+
196
+ Prefer sending a remote script through stdin so no remote helper file is created. For local helpers, use job-scoped `temporary_files` and `{{temp:name}}`; the private job runtime is removed after cleanup.
197
+
198
+ Resources can be injected by private copied path (`{{resource:name}}`), `stdin_resource`, or `env_resources`. Use `capture_output: "discard"` for commands that may echo credentials. Exact resource values and common exact encodings are redacted from retained results, but transformed or partial-secret detection is not guaranteed.
199
+
200
+ If the MCP host later blocks all execution tools, use the local fallback:
201
+
202
+ ```sh
203
+ machine-mcp job list
204
+ machine-mcp job read JOB_ID
205
+ machine-mcp job cancel JOB_ID
206
+ machine-mcp job submit plan.json
207
+ ```
208
+
209
+ Finally steps and restart recovery are best effort and should be idempotent. See [docs/MANAGED_JOBS.md](docs/MANAGED_JOBS.md) for lifecycle, security limits, plan format, and diagnosis guidance.
130
210
 
131
211
  ## Tools
132
212
 
@@ -158,6 +238,18 @@ The exact `tools/list` response reflects the active local policy. Definitions co
158
238
 
159
239
  Repository-configured external diff, text conversion, and filesystem-monitor helpers are disabled for bridge Git inspection.
160
240
 
241
+ ### Diagnostics and durable work
242
+
243
+ - `diagnose_runtime` — fixed layered probes; no user-controlled command input
244
+ - `list_local_resources` — aliases and validation status without paths or values
245
+ - `stage_job` — persist a validated plan for later local approval without executing it
246
+ - `start_job` — detached ordered argv steps, private temporary files, and finally steps
247
+ - `list_jobs`
248
+ - `read_job`
249
+ - `cancel_job`
250
+
251
+ Managed jobs are non-interactive and persist independently of the MCP connection. Process sessions remain interactive and memory-only.
252
+
161
253
  ### Processes
162
254
 
163
255
  - `run_process` — one-shot argv execution without a shell
@@ -188,6 +280,9 @@ machine-mcp service status|install|start|stop|uninstall
188
280
  machine-mcp status
189
281
  machine-mcp doctor
190
282
  machine-mcp rotate-secrets
283
+ machine-mcp resource add|list|check|remove
284
+ machine-mcp job submit|inspect|approve|list|read|cancel
285
+ machine-mcp --print-mcp-credentials
191
286
  machine-mcp uninstall [--keep-worker] [--yes]
192
287
  ```
193
288
 
@@ -201,16 +296,16 @@ Remote mode supports:
201
296
  - Linux `systemd --user`, with best-effort lingering;
202
297
  - Windows Scheduled Task at logon.
203
298
 
204
- The service definition contains neither credentials nor a duplicate policy. It loads the selected policy from owner-only local state and uses the documented `full` default if policy state is absent. Service logs are owner-only where supported and trimmed before daemon startup.
299
+ The service definition contains neither credentials nor a duplicate policy. It loads the selected policy from owner-only local state and uses the documented `full` default if policy state is absent. Background services run at log level `warn`: failures and connection problems are retained, while routine successful tool calls are omitted. Logs are owner-only where supported and bounded by tail trimming.
205
300
 
206
301
  ## Secret rotation
207
302
 
208
303
  ```sh
209
- machine-mcp rotate-secrets --no-print-credentials
210
- machine-mcp
304
+ machine-mcp rotate-secrets
305
+ machine-mcp --print-mcp-credentials
211
306
  ```
212
307
 
213
- Rotation stops the installed service, refuses to proceed while another foreground daemon owns the workspace lock, rotates the MCP password, daemon secret, and OAuth token version, and requires redeployment. Previously issued access tokens then fail validation.
308
+ Rotation stops the installed service, refuses to proceed while another foreground daemon owns the workspace lock, rotates the MCP password, daemon secret, and OAuth token version, and requires redeployment. Rotated values are redacted by default; only the client connection password can be printed through the explicit reconnect flag. Previously issued access tokens then fail validation.
214
309
 
215
310
  ## State and observability
216
311
 
@@ -220,9 +315,9 @@ Default state roots:
220
315
  - Linux with `XDG_STATE_HOME`: `$XDG_STATE_HOME/machine-bridge-mcp`
221
316
  - Windows: `%APPDATA%\machine-bridge-mcp`
222
317
 
223
- State/config writes use owner-only temporary files, flushes, and atomic rename. Malformed state is retained as a bounded corrupt backup before reconstruction. Uninstall validates markers, canonical paths, active locks, workspace/source exclusions, and known contents before recursive deletion.
318
+ State/config writes use owner-only temporary files, flushes, and atomic rename. Malformed state is retained as a bounded corrupt backup before reconstruction. Resource source paths are redacted from `status` output. Active managed-job plans are owner-only and are deleted after a terminal result; bounded redacted results are retained temporarily. Uninstall validates markers, canonical paths, active locks, workspace/source exclusions, and known contents before recursive deletion.
224
319
 
225
- Operational logs record bounded metadata such as component, tool name, shortened call ID, duration, outcome, and error class. They do not intentionally log file contents, patch bodies, command strings, stdin, OAuth passwords, access tokens, or daemon secrets. See [docs/OPERATIONS.md](docs/OPERATIONS.md).
320
+ Default foreground logs show startup/deployment transitions, relay connectivity, warnings/errors, and successful calls that exceed 30 seconds. Routine successful tool calls and correlation IDs appear only at `--log-level debug` or `--verbose`. Background services use `warn`. Log messages and structured fields are bounded, secret-like keys and known token formats are redacted, and tool arguments/results are not written. See [docs/LOGGING.md](docs/LOGGING.md) and [docs/OPERATIONS.md](docs/OPERATIONS.md).
226
321
 
227
322
  ## Development and verification
228
323
 
@@ -230,13 +325,14 @@ Operational logs record bounded metadata such as component, tool name, shortened
230
325
  npm ci
231
326
  npm run check
232
327
  npm run worker:dry-run
328
+ npm audit --audit-level=high
233
329
  npm audit --omit=dev --audit-level=high
234
330
  npm pack --dry-run
235
331
  ```
236
332
 
237
- `npm run check` covers generated Worker types, TypeScript, JavaScript syntax, the shared tool catalog, local path/write/process/state/log/service invariants, a live stdio MCP flow, and a live local OAuth/Worker/WebSocket/MCP flow. GitHub Actions runs the suite on Linux, macOS, and Windows with supported Node versions.
333
+ `npm run check` covers generated Worker types, TypeScript, JavaScript syntax, the shared tool catalog, local path/write/process/state/log/service invariants, managed-job detachment/resource redaction/finally/cancellation/recovery, a live stdio MCP flow, and a live local OAuth/Worker/WebSocket/MCP flow. GitHub Actions runs the suite on Linux, macOS, and Windows with supported Node versions.
238
334
 
239
- See [docs/TESTING.md](docs/TESTING.md), [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), and [SECURITY.md](SECURITY.md).
335
+ See [docs/MANAGED_JOBS.md](docs/MANAGED_JOBS.md), [docs/TESTING.md](docs/TESTING.md), [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), and [SECURITY.md](SECURITY.md).
240
336
 
241
337
  ## Uninstall
242
338
 
package/SECURITY.md CHANGED
@@ -51,7 +51,9 @@ Direct filesystem scope is profile-dependent. The default `full` profile is unre
51
51
 
52
52
  The default `full` profile returns absolute paths. Narrower profiles return workspace-relative paths to reduce username and directory-layout disclosure. Path display and access scope remain separate controls.
53
53
 
54
- Sensitive-looking names are not blocked inside the workspace. Files such as `.env`, private keys, credentials, database dumps, and production configuration remain readable when they are in the selected workspace. Choose the narrowest practical workspace.
54
+ The server does not maintain a sensitive-filename blacklist. Under `full`, direct read tools may access any UTF-8 regular file available to the local OS user, including files outside the selected workspace and names such as `.env`, password stores, private keys, credentials, database dumps, and production configuration. Narrower profiles confine direct filesystem tools but do not classify names inside that boundary.
55
+
56
+ Maximum local policy does not bypass Unix permissions, Windows ACLs, macOS TCC/SIP, container/VM boundaries, endpoint-security controls, shell restrictions, or security decisions made by the MCP host/platform. A host-side refusal is independent of Machine Bridge and cannot be disabled by local policy configuration. `diagnose_runtime` and `machine-mcp doctor` use fixed probes to distinguish requests that reached the daemon from local filesystem/process/shell failures; they cannot inspect a call blocked before delivery.
55
57
 
56
58
  Processes are not confined by the filesystem-tool resolver. They can access paths, networks, processes, credential stores, and devices available to the local user.
57
59
 
@@ -67,10 +69,36 @@ These controls do not defend against a malicious process running under the same
67
69
 
68
70
  Local state contains the MCP connection password and daemon secret. State, lock, temporary secret, runtime, and service-log files use owner-only permissions where supported. State writes are atomic. Logs recursively redact known credential fields and token formats and neutralize control characters.
69
71
 
70
- First-run or explicit credential output can intentionally display the MCP password. Avoid shared terminal logs, shell recordings, screenshots, CI output, or support tickets. Use `--no-print-credentials` for recorded sessions.
72
+ First-run or explicit reconnect output can intentionally display the MCP connection password. JSON output and standalone secret rotation redact credentials by default; printing requires the explicit reconnect flag. The daemon secret is never printed in full. Avoid shared terminal logs, shell recordings, screenshots, CI output, or support tickets.
71
73
 
72
74
  The default `full` profile passes the complete parent environment. Narrower profiles replace HOME, temp, and common cache paths and do not pass arbitrary parent variables. The isolated mode reduces accidental environment-secret leakage; it does not prevent code from explicitly accessing known resources.
73
75
 
76
+ ## Local resources and managed jobs
77
+
78
+ Local resources are registered only through the operator-controlled CLI. State stores canonical paths and metadata, not file contents. Unix-like registration rejects group/other-readable files unless explicitly overridden. Portable mode checks do not fully describe Windows ACLs or extended Unix ACLs; the operator remains responsible for platform permissions.
79
+
80
+ At job acceptance, referenced resources are bounded and hashed. The detached runner reopens and verifies each hash before copying it to a private `0600` runtime file. Resource copies are removed after the finally phase. A changed or unavailable resource fails closed.
81
+
82
+ Resource injection modes have different exposure:
83
+
84
+ - private file-path substitution is generally preferred;
85
+ - stdin avoids process arguments and environment variables;
86
+ - environment injection can be visible to same-user process inspection and inherited child processes.
87
+
88
+ Managed jobs accept arbitrary argv and therefore retain local-user authority. They are a durability mechanism, not a sandbox or an authorization bypass. A running job snapshots execution authority and environment mode at acceptance. Later profile changes affect new jobs but do not silently revoke an accepted running job; explicitly cancel active jobs when revoking authority. The initial job submission remains subject to MCP-host approval and platform safety policy; each child remains subject to local OS and endpoint-security policy.
89
+
90
+ `stage_job` is non-executing and requires write capability. Local `machine-mcp job approve` is a separate operator authorization and may launch a staged plan even when the MCP profile itself has no execution capability. Operators must review the stored plan before approval. Cancelling before approval runs neither main nor finally steps.
91
+
92
+ Active plans are owner-only and may temporarily contain argv, non-secret stdin, temporary helper content, environment overrides, and resource source paths for crash recovery. A terminal runner deletes the full plan. Status and bounded results remain for up to seven days/50 jobs. If a runner crashes before terminal commit, the plan remains until recovery or retention cleanup.
93
+
94
+ Exact resource paths, exact resource bytes interpreted as text, and bounded exact base64/hex forms are redacted from retained output. This cannot detect partial, transformed, encrypted, compressed, or application-specific encodings. It also cannot redact unrelated secrets inherited through the full parent environment. Use `capture_output: "discard"` whenever a process may echo credentials, and never place a secret directly in argv, ordinary env, stdin, temporary-file content, or a JSON plan.
95
+
96
+ `finally_steps` run after ordinary success, failure, timeout, and cancellation. Cancellation uses an owner-only marker rather than signaling the runner process itself, so the coordinator remains alive to execute cleanup consistently across platforms. A dead runner is detected on the next daemon or local job-CLI start; stale private resource copies are removed and cleanup is retried. This is best effort. Power loss, disk failure, permanent loss of credentials/network access, SIGKILL without later recovery, or security software denying the cleanup executable can prevent cleanup. Finally steps must be idempotent and safe to repeat. Automatic recovery is capped at three attempts so persistent endpoint-security or executable-policy denial cannot create an endless launch loop. Uninstall refuses to remove local state while managed jobs are active; operators must inspect or cancel them first.
97
+
98
+ Job-scoped `temporary_files` should be used instead of loose helper scripts. They are materialized only below the private job runtime. Remote scripts should preferably be sent through a process stdin instead of written to the remote filesystem.
99
+
100
+ Runner diagnostic logs are owner-only and do not receive child stdout/stderr. Step output is stored only in bounded job results according to the selected capture mode.
101
+
74
102
  ## OAuth and public endpoints
75
103
 
76
104
  Remote mode uses authorization code flow with PKCE S256, exact redirect/resource/client binding, expiring authorization codes and access tokens, hashed token storage, token-version revocation, and bounded dynamic client registration.
@@ -95,15 +123,17 @@ Process-session IDs are random and valid only inside one runtime. Output buffers
95
123
 
96
124
  Sessions use pipes, not a PTY. Do not assume terminal-oriented programs will behave safely or correctly. Process output may contain secrets; it is returned to the authorized client but is not intentionally written to operational logs.
97
125
 
126
+ Process sessions are interactive and intentionally die with runtime disconnect/replacement. Managed jobs are separate detached processes with owner-only persistent state and best-effort finally/recovery semantics.
127
+
98
128
  ## Images and rich content
99
129
 
100
130
  `view_image` accepts only signature-validated PNG, JPEG, GIF, and WebP under the size cap. SVG is intentionally excluded because it is active document content rather than a simple raster image. Image bytes pass through the Worker in remote mode and are visible to the authorized MCP client.
101
131
 
102
132
  ## Logs and privacy
103
133
 
104
- Operational logs record coarse metadata and error classes, not tool arguments, command text, stdin, file/patch contents, or outputs. Unexpected Worker exceptions are reduced to error classes. Git author email is omitted from `git_log` unless explicitly requested.
134
+ Default operational logs record startup/connection transitions, failed calls, slow-call duration, and coarse error classes. Routine successful calls and shortened correlation IDs are debug-only. Tool arguments, command text, stdin, file/patch contents, and outputs are omitted. Messages and fields are bounded; unexpected daemon and Worker exceptions are reduced to error classes. Git author email is omitted from `git_log` unless explicitly requested.
105
135
 
106
- No logging policy can prevent data from being returned to a client that explicitly invokes an enabled tool. The Worker necessarily relays remote tool arguments and results; this is not end-to-end encryption against the user's Cloudflare execution environment.
136
+ No logging policy can prevent data from being returned to a client that explicitly invokes an enabled tool. The Worker necessarily relays remote tool arguments and results; this is not end-to-end encryption against the user's Cloudflare execution environment. Managed-job result files may contain remote command output and are owner-only local data, not operational logs.
107
137
 
108
138
  ## Hardening checklist
109
139
 
@@ -124,13 +154,15 @@ Also:
124
154
  - patch the OS and use supported Node.js releases;
125
155
  - enable MFA on Cloudflare, GitHub, and npm accounts;
126
156
  - do not configure broad CORS origins;
127
- - avoid `--full-env`, `--unrestricted-paths`, and `--absolute-paths` unless required;
157
+ - select `agent`, `edit`, or `review` instead of the default `full` when broad authority is unnecessary;
128
158
  - inspect client names and OAuth redirect URIs;
129
159
  - rotate secrets after suspected disclosure;
130
160
  - inspect `status`, `doctor`, and service status;
161
+ - register credential files as local resources instead of reading them into a model conversation;
162
+ - use `capture_output: "discard"` for credential-consuming steps and idempotent finally steps for cleanup;
131
163
  - remove the Worker and state when remote access is no longer needed;
132
164
  - use external OS isolation for untrusted code.
133
165
 
134
166
  ## Out of scope
135
167
 
136
- The project cannot prevent an authorized client from requesting data accessible to enabled tools, make arbitrary local executables safe, identify all sensitive content, or neutralize model prompt injection. Operator approval and narrow capability selection remain primary controls.
168
+ The project cannot prevent an authorized client from requesting data accessible to enabled tools, make arbitrary local executables safe, identify all sensitive content, guarantee cleanup across every power/storage/security failure, override MCP-host or endpoint-security policy, or neutralize model prompt injection. Operator approval, local resource hygiene, idempotent cleanup, and narrow capability selection remain primary controls.
@@ -12,15 +12,15 @@ No transport is treated as a sandbox. Both transports invoke the same local runt
12
12
 
13
13
  ## Components
14
14
 
15
- ### Shared tool catalog
15
+ ### Shared protocol metadata and tool catalog
16
16
 
17
- `src/shared/tool-catalog.json` is the single source of truth for tool names, descriptions, input schemas, annotations, and policy availability. The Worker and stdio runtime import it directly. A catalog test rejects duplicate names, unknown availability classes, open schemas, missing annotations, profile drift, and a second hand-maintained Worker catalog.
17
+ `src/shared/server-metadata.json` is the single source of truth for server identity, MCP protocol versions, and host instructions. `src/shared/tool-catalog.json` is the single source of truth for tool names, descriptions, input schemas, annotations, and policy availability. The Worker and local runtime import both directly. Catalog tests reject metadata drift, duplicate names, unknown availability classes, open schemas, missing annotations, profile drift, and second hand-maintained Worker definitions.
18
18
 
19
19
  ### CLI and state layer
20
20
 
21
21
  The CLI canonicalizes workspaces, resolves policy profiles, maintains per-workspace state and credentials, serializes startup/deploy/rotation with locks, deploys the Worker, installs optional platform-native autostart, and starts either remote daemon or stdio mode.
22
22
 
23
- A canonical workspace receives an independent profile, Worker name, secret set, daemon/startup locks, and state file. State schema version 3 removes obsolete local API state and records the expanded policy model.
23
+ A canonical workspace receives an independent profile, Worker name, secret set, resource registry, managed-job directory, daemon/startup locks, and state file. State schema version 5 records policy origin/revision and local resource metadata in addition to the capability fields.
24
24
 
25
25
  ### Local runtime
26
26
 
@@ -30,15 +30,37 @@ A canonical workspace receives an independent profile, Worker name, secret set,
30
30
  - file, text search, image, patch, and Git operations;
31
31
  - direct and shell process execution;
32
32
  - process-session buffers and stdin lifecycle;
33
+ - layered fixed runtime diagnostics;
34
+ - local resource aliases and detached managed-job coordination;
33
35
  - mutation serialization;
34
36
  - child-process tracking and cancellation;
35
37
  - output, traversal, concurrency, and time limits.
36
38
 
37
39
  Remote mode attaches WebSocket connection/reconnect behavior. Stdio mode invokes the same runtime directly.
38
40
 
41
+ ### Managed job runner
42
+
43
+ `ManagedJobManager` persists bounded per-workspace job envelopes below the owner-only profile directory. `start_job` validates the complete plan, snapshots referenced resource metadata/hashes, writes an owner-only plan/status, and launches `job-runner.mjs` as a detached process with runner-level logs redirected to owner-only files. `stage_job` performs the same acceptance validation but writes a non-running `staged` envelope; only local `job approve` transitions it to queued and launches the runner.
44
+
45
+ The runner:
46
+
47
+ - materializes registered resources as private `0600` copies after hash verification;
48
+ - materializes bounded job-scoped temporary files;
49
+ - substitutes resource/temp/runtime/workspace placeholders;
50
+ - executes ordered argv steps with bounded output and timeout/process-tree termination;
51
+ - polls an owner-only cancellation marker, terminates the current child tree, and keeps the runner alive for finally steps;
52
+ - attempts ordered finally steps after success, failure, timeout, or cancellation;
53
+ - removes private runtime copies;
54
+ - writes bounded redacted results and terminal status;
55
+ - deletes the full execution plan and runner PID file after terminal commit.
56
+
57
+ Running managed jobs do not belong to an MCP socket or daemon call ID and snapshot the accepted environment mode/resources. Later profile changes govern new direct submissions; accepted running jobs require explicit cancellation. Staged plans launch no process and require explicit local approval. Daemon disconnect/replacement does not terminate them. Dead runner PIDs are detected on the next daemon or local job-CLI start; stale private runtime data is removed and the finally phase is retried in recovery mode. Recovery deliberately reruns all finally steps, so cleanup must be idempotent.
58
+
59
+ Local resource registrations remain in owner-only state and are reloaded for every new job. MCP-visible resource inventory includes aliases and validation status but never source paths, hashes, or contents.
60
+
39
61
  ### Stdio MCP server
40
62
 
41
- The stdio server implements newline-delimited JSON-RPC over stdin/stdout. It negotiates supported MCP versions, advertises policy-filtered tools, returns text plus structured content, supports native image content, maps cancellation notifications to runtime call IDs, and sends logs only to stderr.
63
+ The stdio server implements newline-delimited JSON-RPC over stdin/stdout. It negotiates supported MCP versions, advertises policy-filtered tools, returns text plus structured content, supports native image content, maps cancellation notifications to runtime call IDs, and sends level-filtered logs only to stderr.
42
64
 
43
65
  ### Cloudflare Worker and Durable Object
44
66
 
@@ -55,7 +77,7 @@ The daemon attachment deliberately omits workspace path/name/hash and process ID
55
77
 
56
78
  ### Autostart layer
57
79
 
58
- The service layer emits launchd, systemd-user, or Windows Scheduled Task definitions. Credentials are not embedded in service definitions; the daemon loads owner-only state. The exact policy is stored in owner-only state; platform service definitions contain only the workspace and state-root selectors.
80
+ The service layer emits launchd, systemd-user, or Windows Scheduled Task definitions. Credentials are not embedded in service definitions; the daemon loads owner-only state. The exact policy is stored in owner-only state; platform service definitions contain only the workspace/state-root selectors and a `warn` log-level setting.
59
81
 
60
82
  ## Trust boundaries
61
83
 
@@ -66,8 +88,12 @@ flowchart LR
66
88
  L[Local MCP client] -->|stdio JSON-RPC| R
67
89
  R -->|canonical workspace tools| F[Selected workspace]
68
90
  R -->|optional direct/shell processes| P[Local user / OS / network]
69
- CLI[CLI + state] --> W
91
+ R -->|durable accepted plan| J[Detached managed-job runner]
92
+ J -->|private copies| LR[Local resource files]
93
+ J -->|argv/stdin/env| P
94
+ CLI[CLI + owner-only state] --> W
70
95
  CLI --> R
96
+ CLI --> J
71
97
  CLI --> S[Autostart provider]
72
98
  ```
73
99
 
@@ -86,7 +112,8 @@ Remote OAuth determines which client may call tools. Local stdio access relies o
86
112
  9. `tools/call` receives a random relay call ID and is bound to the current socket and authenticated client request key.
87
113
  10. The runtime validates policy and arguments, executes the tool, and returns a bounded result.
88
114
  11. The Durable Object accepts a result only from the socket that received that call.
89
- 12. A matching cancellation notification removes the pending call, tells the daemon to cancel, and terminates any child processes bound to it.
115
+ 12. A matching cancellation notification removes the pending call, tells the daemon to cancel, and terminates any ordinary child processes bound to it.
116
+ 13. `start_job` is different: after durable acceptance, the detached runner is no longer bound to the relay call or socket. Later cancellation uses `cancel_job` or the local CLI.
90
117
 
91
118
  Duplicate in-flight JSON-RPC IDs for the same access token are rejected so cancellation cannot ambiguously target multiple calls.
92
119
 
@@ -98,11 +125,11 @@ Duplicate in-flight JSON-RPC IDs for the same access token are rejected so cance
98
125
  4. Each call receives an internal random call ID used only for cancellation and process tracking.
99
126
  5. Results are emitted as JSON-RPC on stdout; logs remain on stderr.
100
127
  6. Duplicate in-flight request IDs are rejected.
101
- 7. Closing stdin cancels pending calls, terminates active processes, and removes the isolated runtime directory.
128
+ 7. Closing stdin cancels pending calls, terminates ordinary active processes/process sessions, and removes the transport runtime directory. Previously accepted managed jobs continue in their persistent per-workspace job directories.
102
129
 
103
130
  ## Filesystem resolution and privacy
104
131
 
105
- The workspace is canonicalized once. Existing targets use `realpath`; the result must remain inside the workspace unless unrestricted mode is explicit. New targets walk to the nearest existing ancestor and validate its canonical path.
132
+ The workspace is canonicalized and compared with targets through consistent platform-native/async `realpath` representations. Existing targets must remain inside the workspace unless the active policy is unrestricted. New targets walk to the nearest existing ancestor, validate its canonical path, and reconstruct the destination below that canonical ancestor.
106
133
 
107
134
  Path behavior is profile-dependent. The default `full` profile permits unrestricted direct filesystem paths and returns absolute paths. The `agent`, `edit`, and `review` profiles enforce canonical workspace containment and return workspace-relative paths. Error strings redact canonical and common platform-alias forms of workspace, runtime, and home paths whenever absolute path display is disabled.
108
135
 
@@ -136,7 +163,9 @@ The default `full` profile passes the complete parent environment. Isolated envi
136
163
 
137
164
  One-shot calls have bounded output and timeouts. Process sessions retain bounded byte buffers with monotonic offsets, accept bounded stdin, support short output/exit waits, and are capped per runtime. Valid UTF-8 is returned as text; byte slices that are not valid UTF-8 also include lossless base64 data. Session IDs are random. Sessions are memory-only and are killed on runtime stop, remote disconnect, or daemon replacement.
138
165
 
139
- Child processes run in a separate process group where supported. Timeout, cancellation, disconnect, and replacement send termination to the process tree, with forced escalation for timeout/disconnect paths.
166
+ Child processes run in a separate process group where supported. Timeout, cancellation, disconnect, and replacement send termination to ordinary process trees, with forced escalation for timeout/disconnect paths.
167
+
168
+ Managed jobs use the same argv/environment primitives but a different lifecycle. Each job is capped at 16 main and 16 finally steps, 50 retained jobs, 64 registered resources, 8 MiB of referenced resource bytes, 512 KiB of temporary-file content, and bounded per-step output. They are non-interactive. Resource paths/stdin/environment are injected only inside the runner. Exact resource output redaction is defense in depth; discard capture is the strong option when a command may echo credentials.
140
169
 
141
170
  ## Daemon reconnect and replacement
142
171
 
@@ -146,7 +175,9 @@ Pending calls retain their originating socket reference. A stale socket cannot c
146
175
 
147
176
  ## Persistence
148
177
 
149
- Local state and global config are owner-only, versioned, and written through temporary files, flushes, and atomic rename. Malformed state becomes a bounded `.corrupt-*` backup. Custom roots are adopted only when empty or recognizable as legacy Machine Bridge state.
178
+ Local state and global config are owner-only, versioned, size-bounded, and written through temporary files, flushes, and atomic rename. Reads reject symbolic links and use no-follow descriptors where supported. Malformed or oversized state becomes a bounded-count `.corrupt-*` backup. Resource paths are omitted from redacted status output. Custom roots are adopted only when empty or recognizable as legacy Machine Bridge state.
179
+
180
+ Active managed jobs persist an owner-only plan, status, runner PID, and bounded runner diagnostics. Terminal jobs delete the full plan and retain only bounded status/redacted results for up to seven days. This balances crash cleanup with minimization of scripts, stdin, argv, environment overrides, and resource source paths.
150
181
 
151
182
  Removal validates the state marker, canonical target, known contents, active locks, filesystem root/home/current/package/workspace/source exclusions, and Worker deletion outcome before recursive deletion.
152
183
 
@@ -154,16 +185,20 @@ OAuth metadata is pruned on access. Expired codes/tokens, old throttling records
154
185
 
155
186
  ## Observability
156
187
 
157
- Public health exposes only server identity and version. Authenticated `server_info` exposes bounded runtime status. Local operational logs record tool identity, shortened random call ID, duration, outcome, and coarse error class, but not arguments or outputs. Worker unexpected-error logs contain endpoint and error class, not raw exception text.
188
+ Public health exposes only server identity and version. Authenticated `server_info` exposes bounded runtime status, policy origin/revision, managed-job counts, and resource alias names without paths or values. `diagnose_runtime` runs fixed local probes and explicitly reports that its own request reached the daemon.
189
+
190
+ Foreground logging defaults to `info`; autostart uses `warn`. Routine successful calls and shortened random correlation IDs are debug-only. `info` retains deployment/connection transitions and successful calls slower than 30 seconds. Failures log tool name, duration, and coarse error class without arguments or outputs. Unexpected local and Worker errors are reduced to classes in normal logs. Messages, strings, arrays, object depth/key counts, and serialized fields are bounded.
158
191
 
159
- Cloudflare sampling is size control rather than an audit log. The project intentionally does not claim complete forensic logging.
192
+ Cloudflare sampling is size control rather than an audit log. The project intentionally does not claim complete forensic logging. See [LOGGING.md](LOGGING.md).
160
193
 
161
194
  ## Explicit non-goals
162
195
 
163
196
  - operating-system sandboxing of arbitrary executables;
164
197
  - preventing an authorized client from requesting data available to enabled tools;
165
- - automatically deciding which workspace files are sensitive;
166
- - surviving daemon restart with process sessions;
198
+ - automatically deciding which local files are sensitive or overriding MCP-host/platform safety policy;
199
+ - surviving daemon restart with process sessions (managed jobs are the separate durable mechanism);
200
+ - guaranteed finally cleanup across permanent power, disk, credential, network, or endpoint-security failure;
201
+ - bypassing MCP-host, connector, operating-system, or endpoint-security policy;
167
202
  - PTY/terminal emulation;
168
203
  - model-level prompt-injection prevention;
169
204
  - multi-user tenancy in one Worker deployment.
package/docs/CLIENTS.md CHANGED
@@ -35,7 +35,8 @@ The stdio mode exists for narrower cases:
35
35
  2. **Transport parity.** The local client can use essentially the same Machine Bridge runtime that remote ChatGPT access uses, without maintaining a second implementation.
36
36
  3. **No remote relay for same-machine use.** The host launches the server directly, so no Cloudflare Worker, public URL, OAuth flow, or network round trip is needed.
37
37
  4. **Capabilities missing from a particular host.** Exact edit semantics, transactional multi-file patches, retained process sessions, image results, or the project's policy profiles may be useful even when the host has basic shell tools.
38
- 5. **Testing and interoperability.** stdio is a standard MCP transport and provides a direct way to validate the runtime independently of the Worker.
38
+ 5. **Durable managed jobs.** The same detached job/resource/finally semantics are available to local and remote hosts, while local CLI remains an operator fallback.
39
+ 6. **Testing and interoperability.** stdio is a standard MCP transport and provides a direct way to validate the runtime independently of the Worker.
39
40
 
40
41
  It is therefore an optional compatibility and reuse surface, not a replacement for the native agent tooling in Claude Desktop, Cursor, or Codex.
41
42
 
@@ -123,3 +124,19 @@ Enter the printed `/mcp` URL in the remote MCP connector. During OAuth authoriza
123
124
  - `review` is read-only and workspace-confined.
124
125
 
125
126
  A client configuration is an authorization decision. Anyone who controls an authorized host can invoke every tool exposed by the selected profile.
127
+
128
+ ## Host-side safety rules
129
+
130
+ The local `full` profile controls Machine Bridge's own tool catalog, path resolver, path display, process environment, and shell availability. It does not control the MCP host's model policy, approval UI, connector gateway, or platform execution filters.
131
+
132
+ Machine Bridge itself does not block files because their names look sensitive. If `server_info` reports `full` and a direct call is still rejected before a structured tool result, the host/connector may have blocked delivery. If `diagnose_runtime` responds but its fixed process or shell probe fails, the likely source is local OS policy, endpoint-security software, permissions, or shell configuration. Changing `--profile`, `--unrestricted-paths`, or `--absolute-paths` cannot override either layer.
133
+
134
+ Do not attempt to evade a host refusal by renaming, encoding, or switching to another arbitrary execution tool. Instead:
135
+
136
+ 1. register credentials locally as resource aliases so their values never enter MCP arguments;
137
+ 2. submit a complete `start_job` plan before the workflow depends on later cleanup calls, or use `stage_job` plus local `job approve` when execution-class tools are unavailable;
138
+ 3. use job-scoped temporary files or remote stdin scripts;
139
+ 4. put idempotent cleanup in `finally_steps`;
140
+ 5. inspect/cancel through `machine-mcp job ...` if the host later denies tools.
141
+
142
+ The initial `start_job` request is still subject to host approval. If it is blocked, the operator can review and submit the same JSON plan locally with `machine-mcp job submit plan.json`. See [MANAGED_JOBS.md](MANAGED_JOBS.md).