machine-bridge-mcp 0.6.0 → 0.7.1

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,88 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.7.1 - 2026-07-11
4
+
5
+ ### Fixed
6
+
7
+ - Persist a sanitized absolute-only command `PATH` in launchd and systemd service definitions, always including the stable Node and CLI directories plus platform defaults. This restores Homebrew/npm/git command resolution for background canonical `full` daemons without accepting empty or relative PATH entries.
8
+ - Align `doctor` with the declared sole runtime baseline by requiring Node.js 26 instead of reporting Node.js 22-25 as supported.
9
+
10
+ ### Tests and documentation
11
+
12
+ - Add service-definition regression tests for PATH preservation, duplicate removal, relative-entry rejection, and launchd/systemd emission; document why `machine-mcp service install` should be rerun after PATH layout changes.
13
+
14
+ ## 0.7.0 - 2026-07-11
15
+
16
+ ### Runtime and dependencies
17
+
18
+ - Raise the sole supported runtime baseline to Node.js 26 and npm 12, add exact local version files and strict engine checks, update Node type definitions to 26.1.1, and run the cross-platform CI suite on Node 26, and disable setup-node's automatic package-manager cache, and bootstrap npm 12 outside the repository before strict project engine checks apply.
19
+ - Confirm Wrangler 4.110.0, ws 8.21.0, and TypeScript 7.0.2 are current; retain exact reviewed dependency versions and zero known audit findings.
20
+ - Accept both legacy array and npm 12 keyed-object `npm pack --json` metadata, invoke the active npm CLI through Node on Windows, keep generated CI SBOM files outside the repository privacy/publication surface, and retry only classified transient GitHub network failures with Git forced to HTTP/1.1. Release creation verifies server state after an ambiguous response before proceeding.
21
+
22
+ ### Security and privacy
23
+
24
+ - Replace private environment-derived aliases in public examples with synthetic identifiers and add a repository privacy gate covering tracked and unignored new files, file names, common credential forms, local home paths, SSH host identifiers, and an ignored machine-specific denylist without echoing matched values. Reject publication-surface symbolic links instead of following their targets.
25
+ - Hide local resource and generated-key paths by default; require explicit `--show-paths` or `expose_paths=true`, return only bare SSH fingerprints, and broaden operational-log redaction for paths, email addresses, token forms, key headers, and Unicode display controls.
26
+ - Separate unrestricted filesystem access from absolute-path display, dynamically redact requested external paths from tool errors, harden autostart files/logs against permissive modes and symbolic links, canonicalize OAuth redirect URIs, and remove bidirectional/zero-width controls from authorization-page display text.
27
+
28
+ ### Correctness and durability
29
+
30
+ - Verify staged managed-job hashes during inspection, approval, and runner startup; serialize approve/cancel transitions and reclaim stale transition/recovery locks even after PID reuse; reject direct execution of unapproved staged plans; remove stale runner claims during recovery; and redact registered-resource source-path aliases from retained output.
31
+ - Clear delayed force-kill timers after process exit, enforce the stdio line limit during incremental reads, reject oversized local WebSocket payloads before string conversion, reject ID-less `tools/call` notifications instead of silently executing them, and accept actual collision-suffixed corrupt-config backups during guarded state removal.
32
+ - Make autostart prefer a stable PATH alias that resolves to the active Node executable instead of a versioned package-manager Cellar path, preventing minor Node upgrades from leaving launchd/systemd definitions pointing at removed binaries.
33
+ - Open and trim managed-job runner diagnostic logs through no-follow file descriptors, rejecting symbolic-link targets instead of following them.
34
+
35
+ ### Release governance and diagnostics
36
+
37
+ - Add a release-impact gate requiring a newer package version and matching CHANGELOG section for every tracked or nonignored repository change after the latest version tag. Document that reviewed changes must be pushed to GitHub and followed by a matching npm release.
38
+ - Clarify in `server_info`, runtime diagnostics, and operations documentation that canonical `full` controls the local daemon and relay catalog only. A connector host can expose a smaller subset, and that host-side subset is not observable or overrideable by Machine Bridge.
39
+
40
+ ### Tests and documentation
41
+
42
+ - Add regression coverage for privacy scanning of new files, clean/parseable package manifests and sensitive-artifact exclusion, default path omission and explicit disclosure, hidden unrestricted paths, plan tampering, direct staged-runner invocation, transition locks, source-path redaction, symlink log targets, corrupt-state cleanup, Unicode authorization spoofing, oversized stdio recovery, ID-less tool calls, and comment-free SSH fingerprints.
43
+ - Make release packaging resilient to lifecycle output by using silent JSON mode, and document repository privacy incident response, immutable history/package limitations, explicit path disclosure, plan integrity enforcement, same-user filesystem race residuals, and privacy checks in the release process.
44
+
45
+ ## 0.6.2 - 2026-07-10
46
+
47
+ ### Fixed
48
+
49
+ - Retry atomic JSON file replacement with bounded backoff for transient Windows sharing failures (`EPERM`, `EACCES`, `EBUSY`, and `ENOTEMPTY`). State, managed-job manager, and detached runner commits now use one shared implementation.
50
+ - Prevent an intermittent Windows `runner_failed` result caused by a transient status-file replacement failure after the same commit had passed the protected pull-request checks.
51
+ - Preserve immediate failure for non-transient filesystem errors and keep temporary-file cleanup behavior unchanged.
52
+ - Restore corrupt-state backup creation through the shared replacement primitive, add collision-resistant backup names, and verify that recovery preserves the original corrupt bytes.
53
+
54
+ ### Tests and diagnostics
55
+
56
+ - Add deterministic injected transient/non-transient atomic replacement tests.
57
+ - Run the full real-machine sandbox three consecutive times on Windows, exercising repeated detached runner, status, result, and finally-step commits.
58
+ - Include managed-job error and cleanup classifications in `full-test` output when a lifecycle check fails.
59
+
60
+ ## 0.6.1 - 2026-07-10
61
+
62
+ ### Full-profile contract and operator workflows
63
+
64
+ - Make named profiles canonical capability contracts. A stored `full` label is repaired to writes, shell execution, unrestricted paths, full parent environment, absolute path output, and the complete tool catalog; deliberate individual overrides remain `custom`. Advance the policy revision to 3 and expose the contract in `server_info` and `doctor`.
65
+ - Add `machine-mcp full-test`, a real local-machine acceptance suite using disposable directories. It verifies outside-workspace I/O, direct and shell execution, parent-environment inheritance, SSH key generation, sandbox `authorized_keys`, SSH client parsing, Google OS Login command availability, a non-mutating sudo probe, and detached finally cleanup without changing cloud or remote state.
66
+ - Add `machine-mcp resource generate-ssh-key NAME [PATH]` and the canonical-full-only MCP tool `generate_ssh_key_resource`. Both generate or reuse an Ed25519 pair locally, register the private file as a resource, and return only paths, modes, key type, and public fingerprint.
67
+
68
+ ### Logging
69
+
70
+ - Remove every ordinary per-tool event from default `info`/`warn` logs. Starts, successes, failures, cancellations, durations, slow calls, tool names, coarse outcome classes, and call correlation are now debug-only in both remote daemon and stdio transports.
71
+ - Keep default logs focused on deployment, connection, protocol, relay, service, and infrastructure health. Tool arguments, command text, inputs, outputs, and results remain omitted at all levels.
72
+
73
+ ### Security, correctness, and portability
74
+
75
+ - Verify that an existing SSH public key matches its private key and that the private key is usable non-interactively before reuse. Reject incomplete pairs and symbolic links, enforce owner-only private modes where supported, and never return private bytes.
76
+ - Fix the previously untested RSA key-generation argument order and add real RSA coverage.
77
+ - Centralize CLI and MCP key generation/registration in one locked state transaction. Roll back a newly created pair if state persistence fails, handle canonical path aliases, and reapply permissions after cross-filesystem installation.
78
+ - Validate the resource state layout before deriving its root and restrict the MCP key generator to the complete canonical `full` profile.
79
+ - Avoid reading user SSH configuration during `full-test`, canonicalize temporary path aliases, and report sudo/cloud prerequisites separately from core Machine Bridge capability.
80
+
81
+ ### Tests and documentation
82
+
83
+ - Add real Ed25519/RSA generation, reuse, mismatch, incomplete-pair, symlink, mode, private-content, CLI, stdio, Worker-policy, and real-machine full-profile regression coverage.
84
+ - Update logging, architecture, operations, client, managed-job, security, and testing documentation to match the canonical-full and debug-only per-tool event contracts.
85
+
3
86
  ## 0.6.0 - 2026-07-10
4
87
 
5
88
  ### Added
@@ -0,0 +1,23 @@
1
+ # Contributing and release discipline
2
+
3
+ This repository treats every Git-tracked or nonignored repository file as release-relevant, including source code, tests, scripts, examples, documentation, CI configuration, ignore rules, and package metadata.
4
+
5
+ ## Required for every release-relevant change
6
+
7
+ Before a change is merged to `main`:
8
+
9
+ 1. bump `package.json` to a version newer than the latest reachable `v*` tag;
10
+ 2. add the matching dated section to `CHANGELOG.md`;
11
+ 3. run `npm run check`, `npm audit`, `npm audit --omit=dev`, and `npm run worker:dry-run`;
12
+ 4. inspect the complete diff and the npm package manifest;
13
+ 5. push the reviewed commit to GitHub;
14
+ 6. create the matching Git tag and GitHub Release;
15
+ 7. publish the same version to npm.
16
+
17
+ `npm run release-impact:check` enforces the version and changelog parts. It fails when release-relevant files changed after the latest version tag but the package version was not advanced.
18
+
19
+ A privacy or security correction is always release-relevant. Removing a private identifier only from the current branch is insufficient: publish a replacement npm version, update GitHub, and deprecate or unpublish the affected npm version when policy and authentication permit.
20
+
21
+ ## Privacy
22
+
23
+ Use only synthetic names, reserved example domains, and generic paths. Maintain private local identifiers in the ignored `.privacy-denylist` and run `npm run privacy:check` before committing.
package/README.md CHANGED
@@ -32,7 +32,7 @@ A newly selected workspace starts with the maximum-permission `full` profile for
32
32
  - tool results may return absolute paths;
33
33
  - child processes inherit the complete parent environment.
34
34
 
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.
35
+ Policy state records whether it came from the default, an explicit named profile, or custom overrides. Named profiles are canonical contracts: a stored `full` profile is repaired on load to enable writes, shell execution, unrestricted paths, full parent environment, absolute path output, and the complete tool catalog. Any explicit per-capability narrowing is stored as `custom`. Policy revision 3 refreshes default/migrated state and preserves explicit restrictive/custom profiles.
36
36
 
37
37
  | Profile | File edits | Direct argv processes | Shell commands | Filesystem scope | Process environment |
38
38
  |---|---:|---:|---:|---|---|
@@ -45,7 +45,7 @@ This default prioritizes usability, not least privilege. `run_process` and proce
45
45
 
46
46
  ## Install
47
47
 
48
- Node.js 22 or newer is required.
48
+ Node.js 26 or newer and npm 12 or newer are required. The repository pins the active development versions in `.node-version`, `.nvmrc`, and `packageManager`; project installs fail on older Node runtimes.
49
49
 
50
50
  ```sh
51
51
  npm install -g --allow-scripts=esbuild,workerd,sharp machine-bridge-mcp@latest
@@ -132,7 +132,7 @@ Important distinctions:
132
132
  - `--absolute-paths` changes returned path metadata; it does not independently grant additional access.
133
133
  - In isolated-environment profiles, commands receive private HOME, temp, and cache directories plus a small set of platform variables.
134
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.
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. `full` means the local daemon and relay advertise the complete catalog; a connector may still expose only a subset to a particular session. Machine Bridge cannot observe or override that host-side subset. A host-generated “sensitive file” denial is outside this server's enforcement layer.
136
136
 
137
137
  ## Diagnose host, bridge, and local execution failures
138
138
 
@@ -152,6 +152,14 @@ machine-mcp doctor
152
152
 
153
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
154
 
155
+ Run a real local acceptance test for the canonical `full` contract:
156
+
157
+ ```sh
158
+ machine-mcp full-test --workspace /path/to/project
159
+ ```
160
+
161
+ The test uses a temporary sandbox to perform an outside-workspace read/write, direct process, shell command, parent-environment inheritance, Ed25519 generation, temporary `authorized_keys` write, SSH client check, Google Cloud OS Login command availability check, non-mutating `sudo -n true` probe, and detached managed-job/finally lifecycle. It does not add a cloud key, contact a remote maintenance host, modify a user account, or retain the generated key.
162
+
155
163
  ## Managed jobs and local resources
156
164
 
157
165
  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.
@@ -161,11 +169,20 @@ When the host blocks execution-class tools but still permits state mutation, `st
161
169
  Register credential/key files locally without sending their contents through MCP:
162
170
 
163
171
  ```sh
164
- chmod 600 ~/.ssh/racknerd_ed25519
165
- machine-mcp resource add racknerd-key ~/.ssh/racknerd_ed25519
166
- machine-mcp resource list
172
+ chmod 600 ~/.ssh/example_maintenance_ed25519
173
+ machine-mcp resource add maintenance-key ~/.ssh/example_maintenance_ed25519
174
+ machine-mcp resource list # paths omitted by default
175
+ machine-mcp resource list --show-paths # explicit local-only path disclosure
167
176
  ```
168
177
 
178
+ Generate an Ed25519 key and register its private file in one operation:
179
+
180
+ ```sh
181
+ machine-mcp resource generate-ssh-key maintenance-key ~/.ssh/machine-mcp-example-maint-ed25519
182
+ ```
183
+
184
+ Under canonical `full`, an authorized MCP host can invoke `generate_ssh_key_resource` with the same semantics. The tool is idempotent, verifies that existing public/private files match, enforces local file modes where supported, and returns the public fingerprint, key type, and registration status—not private key bytes or local paths. Pass `expose_paths=true` only when the caller genuinely needs those paths.
185
+
169
186
  A job refers to the alias rather than the value:
170
187
 
171
188
  ```json
@@ -176,8 +193,8 @@ A job refers to the alias rather than the value:
176
193
  "argv": [
177
194
  "ssh",
178
195
  "-i",
179
- "{{resource:racknerd-key}}",
180
- "root@example",
196
+ "{{resource:maintenance-key}}",
197
+ "admin@server.example",
181
198
  "sh",
182
199
  "-s"
183
200
  ],
@@ -186,7 +203,7 @@ A job refers to the alias rather than the value:
186
203
  ],
187
204
  "finally_steps": [
188
205
  {
189
- "argv": ["ssh", "-i", "{{resource:racknerd-key}}", "root@example", "rm", "-f", "/tmp/helper"],
206
+ "argv": ["ssh", "-i", "{{resource:maintenance-key}}", "admin@server.example", "rm", "-f", "/tmp/helper"],
190
207
  "allow_failure": true
191
208
  }
192
209
  ]
@@ -242,6 +259,7 @@ Repository-configured external diff, text conversion, and filesystem-monitor hel
242
259
 
243
260
  - `diagnose_runtime` — fixed layered probes; no user-controlled command input
244
261
  - `list_local_resources` — aliases and validation status without paths or values
262
+ - `generate_ssh_key_resource` — canonical-full-only Ed25519 generation and private-file registration; private bytes and local paths are omitted by default
245
263
  - `stage_job` — persist a validated plan for later local approval without executing it
246
264
  - `start_job` — detached ordered argv steps, private temporary files, and finally steps
247
265
  - `list_jobs`
@@ -296,7 +314,7 @@ Remote mode supports:
296
314
  - Linux `systemd --user`, with best-effort lingering;
297
315
  - Windows Scheduled Task at logon.
298
316
 
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.
317
+ 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. launchd/systemd definitions persist a sanitized absolute-only PATH captured during installation, including the stable Node/CLI directories, so background `full` mode does not lose Homebrew or other developer command locations. Background services run at log level `warn`: relay, protocol, and service problems are retained, while all per-tool success/failure/cancellation/timing events remain debug-only. Logs are owner-only where supported and bounded by tail trimming.
300
318
 
301
319
  ## Secret rotation
302
320
 
@@ -317,7 +335,7 @@ Default state roots:
317
335
 
318
336
  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.
319
337
 
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).
338
+ Default foreground logs show startup/deployment transitions, relay connectivity, and infrastructure warnings/errors. Every per-tool event—including success, failure, cancellation, and slow-call timing—appears only at `--log-level debug` or `--verbose`. Background services use `warn`, so ordinary tool outcomes never fill daemon logs. 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).
321
339
 
322
340
  ## Development and verification
323
341
 
@@ -330,7 +348,7 @@ npm audit --omit=dev --audit-level=high
330
348
  npm pack --dry-run
331
349
  ```
332
350
 
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.
351
+ `npm run check` covers privacy and release-impact gates, generated Worker types, TypeScript, JavaScript syntax, the shared tool catalog, local path/write/process/state/log/service invariants, Ed25519/RSA generation and key-pair validation, real-machine `full` sandbox acceptance, a clean npm package-manifest/sensitive-artifact check, 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 the pinned Node 26/npm 12 baseline.
334
352
 
335
353
  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).
336
354
 
@@ -346,3 +364,5 @@ Use `--keep-worker` to retain deployed Workers while removing local state and au
346
364
  ## License
347
365
 
348
366
  MIT
367
+
368
+ See [repository privacy hygiene](docs/PRIVACY.md) and [contribution/release discipline](CONTRIBUTING.md) before committing. Every release-relevant code, test, script, configuration, or documentation change must be pushed to GitHub with a new version and followed by a matching npm release.
package/SECURITY.md CHANGED
@@ -17,6 +17,12 @@ Do not open a public issue for an undisclosed vulnerability. Use GitHub private
17
17
 
18
18
  Do not include live MCP passwords, daemon secrets, OAuth tokens, Cloudflare credentials, private keys, or unrelated local files. Rotate credentials used in a reproduction.
19
19
 
20
+ ## Repository and documentation privacy
21
+
22
+ Tests, examples, documentation, release notes, and package metadata are publication surfaces. Use only synthetic hostnames, resource aliases, usernames, paths, and project names. Run `npm run privacy:check` before committing or publishing, and maintain private local identifiers in the ignored `.privacy-denylist`; the scanner never prints matched values. See [Repository privacy hygiene](docs/PRIVACY.md).
23
+
24
+ Removing a value from the current branch does not remove it from Git history, caches, forks, or an already published npm package. Rotate any exposed credential immediately and coordinate destructive history rewriting separately when its risk is justified.
25
+
20
26
  ## Core trust model
21
27
 
22
28
  Trusted components are:
@@ -32,9 +38,9 @@ The Worker is a remote authentication and relay boundary. The local runtime is t
32
38
 
33
39
  ## Profiles are capability sets, not sandboxes
34
40
 
35
- The default for newly selected workspaces is `full`, which prioritizes ease of use over least privilege.
41
+ The default for newly selected workspaces is `full`, which prioritizes ease of use over least privilege. Named profiles are canonical capability contracts. A stored `full` label is repaired on load to the complete maximum-permission field set and tool catalog; a deliberate per-capability override is represented as `custom`, not as a partially restricted `full`.
36
42
 
37
- - `full` exposes all tools, unrestricted direct filesystem paths, absolute path output, and the complete parent process environment.
43
+ - `full` exposes all tools, unrestricted direct filesystem paths, absolute path output, shell execution, and the complete parent process environment.
38
44
  - `agent` exposes file mutation, direct argv execution, and process sessions while keeping direct filesystem tools workspace-confined and the process environment isolated.
39
45
  - `edit` exposes read and mutation tools without process execution.
40
46
  - `review` exposes read-only workspace/Git/image tools.
@@ -53,17 +59,21 @@ The default `full` profile returns absolute paths. Narrower profiles return work
53
59
 
54
60
  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
61
 
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.
62
+ 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. `full` guarantees the complete local daemon and relay-advertised catalog, not the set a connector host chooses to expose to one session. The server cannot observe that host-side subset. 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.
57
63
 
58
64
  Processes are not confined by the filesystem-tool resolver. They can access paths, networks, processes, credential stores, and devices available to the local user.
59
65
 
66
+ ## Full-profile verification
67
+
68
+ `machine-mcp full-test` performs real local operations inside disposable temporary directories: unrestricted file access, direct and shell processes, inherited environment, SSH key generation/matching, a sandbox `authorized_keys` write, SSH client parsing, Google Cloud OS Login command discovery, a non-mutating sudo availability probe, and detached job cleanup. It makes no cloud or remote-server change and cannot prove that an MCP host will deliver a future request. The command verifies the Machine Bridge and local-machine portion of the authority chain only.
69
+
70
+ A canonical `full` profile guarantees that Machine Bridge itself will not reject a catalogued tool because of its local profile, path scope, environment mode, or shell mode. It cannot override the MCP host/connector, operating-system access controls, endpoint-security policy, remote authentication, cloud IAM, `sudo`, or service-side authorization.
71
+
60
72
  ## Mutation integrity
61
73
 
62
74
  Writes are bounded, reject symbolic-link/non-regular destinations, use same-directory staging, and commit atomically per file. Create-only commit fails if a concurrent destination appears. Expected hashes and exact edits reduce accidental stale overwrites.
63
75
 
64
- Patch operations prevalidate all paths/content, reject canonical collisions, recheck source hashes and destination absence, serialize bridge mutations, maintain backups, and roll back on ordinary commit errors.
65
-
66
- These controls do not defend against a malicious process running under the same user racing filesystem metadata, nor do they make a multi-directory patch power-loss atomic.
76
+ Patch operations prevalidate all paths/content, reject canonical collisions, recheck source hashes and destination absence, serialize bridge mutations, maintain backups, and roll back on ordinary commit errors. On platforms without descriptor-relative path traversal, a malicious process running as the same local account can still race filesystem or parent-directory replacement between validation and commit. These controls do not claim protection against a hostile same-user namespace, nor do they make a multi-directory patch power-loss atomic.
67
77
 
68
78
  ## Credential exposure
69
79
 
@@ -75,7 +85,7 @@ The default `full` profile passes the complete parent environment. Narrower prof
75
85
 
76
86
  ## Local resources and managed jobs
77
87
 
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.
88
+ Local resources are registered through the operator-controlled CLI or, only under canonical `full`, through `generate_ssh_key_resource`. State stores canonical paths, bounded registration-time aliases used only for redaction, and metadatanot file contents. Public status and resource results omit those aliases. The SSH generator creates or reuses an unencrypted automation key, verifies that the public and private files match, rejects symbolic links/incomplete pairs, applies `0600`/`0644` modes where supported, and returns no private bytes. Resource CLI output and `generate_ssh_key_resource` omit local paths by default; path disclosure requires `--show-paths` or `expose_paths=true`. Unix-like registration rejects group/other-readable private resource 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
89
 
80
90
  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
91
 
@@ -91,7 +101,7 @@ Managed jobs accept arbitrary argv and therefore retain local-user authority. Th
91
101
 
92
102
  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
103
 
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.
104
+ Exact canonical and registration-time resource path aliases, 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
105
 
96
106
  `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
107
 
@@ -131,7 +141,7 @@ Process sessions are interactive and intentionally die with runtime disconnect/r
131
141
 
132
142
  ## Logs and privacy
133
143
 
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.
144
+ Default operational logs record startup/deployment, relay, protocol, service, and infrastructure transitions. Every ordinary per-tool event—start, success, failure, cancellation, and duration—is debug-only. Tool arguments, command text, stdin, file/patch contents, and outputs are omitted. Messages and fields are bounded; unexpected daemon and Worker infrastructure exceptions are reduced to error classes. Git author email is omitted from `git_log` unless explicitly requested.
135
145
 
136
146
  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.
137
147
 
@@ -151,7 +161,7 @@ machine-mcp --workspace /narrow/project --profile edit --no-print-credentials
151
161
 
152
162
  Also:
153
163
 
154
- - patch the OS and use supported Node.js releases;
164
+ - patch the OS and use the repository-pinned Node.js 26/npm 12 baseline;
155
165
  - enable MFA on Cloudflare, GitHub, and npm accounts;
156
166
  - do not configure broad CORS origins;
157
167
  - select `agent`, `edit`, or `review` instead of the default `full` when broad authority is unnecessary;
@@ -58,6 +58,14 @@ Running managed jobs do not belong to an MCP socket or daemon call ID and snapsh
58
58
 
59
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
60
 
61
+ ### Canonical policy contracts
62
+
63
+ Named profiles are normalized to their complete capability sets. In particular, `full` always means write enabled, shell execution, unrestricted paths, complete parent environment, absolute-path display, and every catalog tool. CLI flags that alter an individual capability deliberately change the profile identity to `custom`. Policy revision 3 repairs stale or manually edited named-profile fields rather than allowing a misleading partially restricted `full` label.
64
+
65
+ The full-only `generate_ssh_key_resource` operation is implemented locally. The Worker only filters and relays its shared catalog definition. Local generation uses `ssh-keygen`, verifies public/private correspondence, registers the private file through the same owner-only state transaction as the CLI, and rolls back a newly created pair if state persistence fails.
66
+
67
+ `full-test` constructs a local runtime with canonical full policy and executes real operations in disposable directories. It is an acceptance test for the local implementation, not a probe that changes remote systems or bypasses host policy.
68
+
61
69
  ### Stdio MCP server
62
70
 
63
71
  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.
@@ -77,7 +85,7 @@ The daemon attachment deliberately omits workspace path/name/hash and process ID
77
85
 
78
86
  ### Autostart layer
79
87
 
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.
88
+ 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. launchd/systemd definitions contain the workspace/state-root selectors, a `warn` log-level setting, and a sanitized absolute-only PATH captured at installation so background `full` mode can resolve the same developer tools without accepting relative PATH entries.
81
89
 
82
90
  ## Trust boundaries
83
91
 
@@ -97,7 +105,7 @@ flowchart LR
97
105
  CLI --> S[Autostart provider]
98
106
  ```
99
107
 
100
- Remote OAuth determines which client may call tools. Local stdio access relies on the local process and configuration boundary. Policy determines which tools are advertised. Canonical resolution limits direct filesystem tools. Processes retain local-user authority and can escape workspace constraints through their own code or system calls.
108
+ Remote OAuth determines which client may call tools. Local stdio access relies on the local process and configuration boundary. Policy determines which tools the local daemon and relay advertise. A connector host can independently present a smaller tool subset to a session; this post-relay filtering is outside the protocol state visible to Machine Bridge. Canonical resolution limits direct filesystem tools. Processes retain local-user authority and can escape workspace constraints through their own code or system calls.
101
109
 
102
110
  ## Remote request lifecycle
103
111
 
@@ -123,15 +131,15 @@ Duplicate in-flight JSON-RPC IDs for the same access token are rejected so cance
123
131
  2. The server negotiates one of the supported MCP versions.
124
132
  3. Tool discovery is generated from the same catalog and policy used by remote mode.
125
133
  4. Each call receives an internal random call ID used only for cancellation and process tracking.
126
- 5. Results are emitted as JSON-RPC on stdout; logs remain on stderr.
127
- 6. Duplicate in-flight request IDs are rejected.
134
+ 5. Input is parsed as incrementally bounded newline-delimited JSON-RPC, so an oversized line is discarded before unbounded buffering and the next line can still be processed. Results are emitted as JSON-RPC on stdout; logs remain on stderr.
135
+ 6. Duplicate in-flight request IDs are rejected, and `tools/call` requires a non-null request ID so write or execution calls cannot run as unacknowledged JSON-RPC notifications.
128
136
  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.
129
137
 
130
138
  ## Filesystem resolution and privacy
131
139
 
132
140
  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.
133
141
 
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.
142
+ 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. Access scope and path display are independent: unrestricted access with path display disabled returns relative workspace paths and opaque external-path identifiers.
135
143
 
136
144
  Symbolic-link destinations and non-regular write targets are rejected. Recursive walkers do not follow symbolic-link directories.
137
145
 
@@ -175,7 +183,7 @@ Pending calls retain their originating socket reference. A stale socket cannot c
175
183
 
176
184
  ## Persistence
177
185
 
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.
186
+ Local state and global config are owner-only, versioned, size-bounded, and written through temporary files, flushes, and atomic rename. State, managed-job manager, and detached runner commits share a bounded retrying atomic-replace primitive for transient Windows `EPERM`, `EACCES`, `EBUSY`, and `ENOTEMPTY` sharing failures. 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
187
 
180
188
  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.
181
189
 
@@ -185,9 +193,9 @@ OAuth metadata is pruned on access. Expired codes/tokens, old throttling records
185
193
 
186
194
  ## Observability
187
195
 
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.
196
+ Public health exposes only server identity and version. Authenticated `server_info` exposes bounded runtime status, policy origin/revision, managed-job counts, resource alias names without paths or values, and the daemon/relay-advertised tool counts. It explicitly reports that the host-exposed subset is unknown to the server. `diagnose_runtime` runs fixed local probes and explicitly reports that its own request reached the daemon.
189
197
 
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.
198
+ Foreground logging defaults to `info`; autostart uses `warn`. All per-tool starts, successes, failures, cancellations, durations, and shortened random correlation IDs are debug-only. Normal logs retain deployment, connection, protocol, service, and infrastructure events rather than duplicating MCP tool outcomes. Unexpected local and Worker infrastructure errors are reduced to classes. Messages, strings, arrays, object depth/key counts, and serialized fields are bounded.
191
199
 
192
200
  Cloudflare sampling is size control rather than an audit log. The project intentionally does not claim complete forensic logging. See [LOGGING.md](LOGGING.md).
193
201
 
package/docs/CLIENTS.md CHANGED
@@ -118,13 +118,23 @@ Enter the printed `/mcp` URL in the remote MCP connector. During OAuth authoriza
118
118
 
119
119
  ## Profile guidance
120
120
 
121
- - `full` is the default and prioritizes immediate usability. It exposes shell execution, unrestricted direct filesystem paths, absolute path output, and the full parent environment.
121
+ - `full` is the default and prioritizes immediate usability. It is a canonical contract exposing every catalog tool, shell execution, unrestricted direct filesystem paths, absolute path output, and the full parent environment. Any individual narrowing is represented as `custom`.
122
122
  - `agent` retains file mutation and direct process execution but removes shell parsing, confines direct filesystem tools to the workspace, and isolates the process environment.
123
123
  - `edit` permits deterministic file mutation without process execution.
124
124
  - `review` is read-only and workspace-confined.
125
125
 
126
126
  A client configuration is an authorization decision. Anyone who controls an authorized host can invoke every tool exposed by the selected profile.
127
127
 
128
+ Before connecting a host, verify the local implementation directly:
129
+
130
+ ```sh
131
+ machine-mcp full-test --workspace /path/to/project
132
+ ```
133
+
134
+ A passing result proves that Machine Bridge and the local OS allowed its temporary file/process/shell/key/job probes at that time. It does not prove that a hosted MCP connector will expose every relay-advertised tool, deliver a later request, or that a cloud/remote account will authorize it.
135
+
136
+ For SSH automation, prefer `generate_ssh_key_resource` under canonical full, or `machine-mcp resource generate-ssh-key` from the terminal. The private key remains a local resource; private bytes and local paths are omitted by default, while metadata and the bare public fingerprint are returned. Paths require an explicit disclosure option. Installation of the public key into Google OS Login or a remote `authorized_keys` file remains an explicit external action.
137
+
128
138
  ## Host-side safety rules
129
139
 
130
140
  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.
package/docs/LOGGING.md CHANGED
@@ -6,8 +6,8 @@ Operational logs should answer four questions without becoming a transcript of u
6
6
 
7
7
  1. Did deployment and relay connection succeed?
8
8
  2. Is the daemon reconnecting, superseded, or unhealthy?
9
- 3. Did a tool call fail, time out, or become unusually slow?
10
- 4. Which component and coarse error class should be investigated?
9
+ 3. Is an infrastructure, protocol, deployment, or relay failure occurring?
10
+ 4. When debug logging is explicitly enabled, which bounded per-tool event should be correlated?
11
11
 
12
12
  Logs are not a command history, content audit trail, or substitute for MCP client approvals.
13
13
 
@@ -26,24 +26,15 @@ The default foreground level is `info`. Platform autostart services use `warn`.
26
26
  | Level | Intended events |
27
27
  |---|---|
28
28
  | `error` | Broken request handlers, relay transport errors, unrecoverable local failures |
29
- | `warn` | Failed tool calls, disconnections, malformed/oversized relay messages, superseded daemons |
30
- | `info` | Deployment/startup transitions, successful relay connection, successful calls slower than 30 seconds |
31
- | `debug` | Routine successful calls, shortened correlation IDs, reconnect timing, cancellation correlation |
29
+ | `warn` | Relay disconnections/send failures, malformed or oversized messages, superseded daemons, service/autostart problems |
30
+ | `info` | Deployment/startup transitions and successful relay connection |
31
+ | `debug` | Every per-tool start/success/failure/cancellation/timing event, shortened correlation IDs, reconnect timing |
32
32
 
33
- Routine successful calls are deliberately absent from `info`. A normal sequence of hundreds of tool invocations should not fill the user's terminal or service logs.
33
+ All per-tool outcomes are deliberately absent from `info`, `warn`, and `error`. The MCP response already reports success or failure to the caller; duplicating ordinary failures in daemon logs creates noise without adding operational information. A normal sequence of hundreds of successful or failed tool invocations should not fill the user's terminal or service logs.
34
34
 
35
35
  ## Event fields
36
36
 
37
- Default warning/error and slow-call events may include:
38
-
39
- - component;
40
- - tool name;
41
- - duration in milliseconds;
42
- - coarse error class;
43
- - relay close code;
44
- - bounded reconnect delay.
45
-
46
- Shortened random call identifiers are debug-only. They are useful only for correlating adjacent local events and are not stable audit identifiers.
37
+ Default operational events may include component, relay close code, bounded reconnect delay, provider name, or a coarse infrastructure error class. Per-tool fields—tool name, duration, outcome class, and shortened call identifier—are debug-only. Call identifiers are useful only for correlating adjacent local events and are not stable audit identifiers.
47
38
 
48
39
  The implementation intentionally omits:
49
40
 
@@ -54,7 +45,7 @@ The implementation intentionally omits:
54
45
  - OAuth request bodies;
55
46
  - connection passwords, daemon secrets, authorization codes, and access tokens.
56
47
 
57
- Unexpected daemon and Worker failures are reduced to coarse error classes in normal logs. Client-facing tool errors may contain more detail according to the active path-display policy, but those details are not copied into normal operational logs.
48
+ Unexpected daemon, service, and Worker infrastructure failures are reduced to coarse error classes in normal logs. Client-facing tool errors may contain more detail according to the active path-display policy, but ordinary tool failures and those details are not copied into normal operational logs.
58
49
 
59
50
  ## Bounding and redaction
60
51
 
@@ -71,7 +62,7 @@ logs/daemon.out.log
71
62
  logs/daemon.err.log
72
63
  ```
73
64
 
74
- They are owner-only where the platform supports Unix-style permissions. Existing files are tail-trimmed on UTF-8/line boundaries before daemon startup. Because background services use `warn`, normal successful tool traffic should not cause sustained log growth.
65
+ They are owner-only where the platform supports Unix-style permissions. Existing files are tail-trimmed on UTF-8/line boundaries before daemon startup. Because background services use `warn` and per-tool events are debug-only, neither successful nor failed ordinary tool traffic should cause sustained log growth.
75
66
 
76
67
  Each managed job also has owner-only `runner.out.log` and `runner.err.log` files for runner startup/crash diagnostics. Child-step stdout and stderr are captured into bounded job results according to `capture_output`; they are not copied into runner or daemon operational logs. Runner logs are retained with the bounded job directory and removed by job retention cleanup.
77
68
 
@@ -80,3 +71,7 @@ Each managed job also has owner-only `runner.out.log` and `runner.err.log` files
80
71
  Machine Bridge does not classify filenames as sensitive and does not block `.env`, password, key, or credential-looking names. The active local policy, local OS permissions, and endpoint-security controls determine what the server itself can read or execute.
81
72
 
82
73
  An MCP host, model provider, desktop application, or platform execution layer may independently deny a request involving credentials or sensitive files. Such a denial occurs before or outside Machine Bridge's file resolver and cannot be disabled by selecting the local `full` profile. Use `server_info`, `machine-mcp status`, and `machine-mcp doctor` to distinguish the active local policy from host-side enforcement.
74
+
75
+ ## Privacy filtering
76
+
77
+ Structured fields whose names indicate a local path, workspace, working directory, home, or state root are replaced with `<local-path>`. Free-form log text removes known home-directory prefixes, email addresses, common bearer/cloud/API token forms, private-key headers, control characters, and Unicode bidirectional/zero-width formatting controls. Explicit command output and JSON command results are not operational logs and retain their documented opt-in disclosure semantics.
@@ -46,10 +46,11 @@ A successful diagnostic response proves that particular request reached the loca
46
46
  Secrets and other local-only files should be registered from the user's terminal, not read into the model context:
47
47
 
48
48
  ```sh
49
- chmod 600 ~/.ssh/racknerd_ed25519
50
- machine-mcp resource add racknerd-key ~/.ssh/racknerd_ed25519
51
- machine-mcp resource list
52
- machine-mcp resource check racknerd-key
49
+ chmod 600 ~/.ssh/example_maintenance_ed25519
50
+ machine-mcp resource add maintenance-key ~/.ssh/example_maintenance_ed25519
51
+ machine-mcp resource list # paths omitted by default
52
+ machine-mcp resource list --show-paths # explicit local-only disclosure
53
+ machine-mcp resource check maintenance-key
53
54
  ```
54
55
 
55
56
  Registration stores only canonical path and bounded metadata in owner-only state. It reads the file locally to validate accessibility and size but does not print or send its contents. New jobs see registry changes immediately; the daemon does not need a restart.
@@ -59,11 +60,19 @@ Unix-like systems reject group/other-readable resource files by default. `--allo
59
60
  Remove an alias with:
60
61
 
61
62
  ```sh
62
- machine-mcp resource remove racknerd-key
63
+ machine-mcp resource remove maintenance-key
63
64
  ```
64
65
 
65
66
  Removing an alias affects new jobs. A job already accepted has an owner-only resource snapshot specification and continues independently.
66
67
 
68
+ Generate a new Ed25519 resource rather than exposing a private-key creation command through a model-generated shell string:
69
+
70
+ ```sh
71
+ machine-mcp resource generate-ssh-key maintenance-key ~/.ssh/machine-mcp-example-maint-ed25519
72
+ ```
73
+
74
+ Canonical-full MCP clients also receive `generate_ssh_key_resource`. It generates or reuses the pair locally, verifies correspondence, registers the private file, and returns neither private bytes nor local paths by default. Set `expose_paths=true` only for an explicit path-retrieval operation. Public-key installation in a cloud or remote account should be a separate reviewed step or managed job.
75
+
67
76
  ## Resource injection modes
68
77
 
69
78
  A managed step is argv-based and supports three local resource modes:
@@ -72,7 +81,7 @@ A managed step is argv-based and supports three local resource modes:
72
81
 
73
82
  ```json
74
83
  {
75
- "argv": ["ssh", "-i", "{{resource:racknerd-key}}", "root@example", "true"]
84
+ "argv": ["ssh", "-i", "{{resource:maintenance-key}}", "admin@server.example", "true"]
76
85
  }
77
86
  ```
78
87
 
@@ -144,8 +153,8 @@ For a remote shell program, avoid a remote helper entirely when possible:
144
153
  "argv": [
145
154
  "ssh",
146
155
  "-i",
147
- "{{resource:racknerd-key}}",
148
- "root@example",
156
+ "{{resource:maintenance-key}}",
157
+ "admin@server.example",
149
158
  "sh",
150
159
  "-s"
151
160
  ],
@@ -166,12 +175,12 @@ A job can contain ordered main steps and ordered `finally_steps`:
166
175
  "name": "remote maintenance",
167
176
  "steps": [
168
177
  {
169
- "argv": ["ssh", "root@example", "perform-maintenance"]
178
+ "argv": ["ssh", "admin@server.example", "perform-maintenance"]
170
179
  }
171
180
  ],
172
181
  "finally_steps": [
173
182
  {
174
- "argv": ["ssh", "root@example", "rm", "-f", "/tmp/maintenance-helper"],
183
+ "argv": ["ssh", "admin@server.example", "rm", "-f", "/tmp/maintenance-helper"],
175
184
  "allow_failure": true
176
185
  }
177
186
  ]
@@ -199,7 +208,7 @@ machine-mcp job approve JOB_ID
199
208
  machine-mcp job approve JOB_ID --yes
200
209
  ```
201
210
 
202
- `job inspect` displays the reviewable plan, including argv, ordinary environment overrides, stdin, temporary helper content, and finally steps, while omitting registered resource source paths and per-resource hashes. The overall `plan_sha256` can be compared with the value returned by `stage_job`.
211
+ `job inspect` displays the reviewable plan, including argv, ordinary environment overrides, stdin, temporary helper content, and finally steps, while omitting registered resource source paths and per-resource hashes. The overall `plan_sha256` is displayed for review and is revalidated atomically during approval and again by the runner before execution. A modified staged plan is rejected.
203
212
 
204
213
  Local approval is a new operator authorization. It intentionally does not depend on the current MCP execution profile: a plan staged under a write-capable profile can be reviewed and approved from the terminal even when the connector is not allowed to execute. The plan retains the filesystem scope and environment mode captured when it was staged.
205
214