machine-bridge-mcp 0.11.1 → 0.12.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 +26 -0
- package/CONTRIBUTING.md +2 -2
- package/README.md +5 -5
- package/SECURITY.md +15 -5
- package/browser-extension/manifest.json +2 -2
- package/docs/ARCHITECTURE.md +14 -8
- package/docs/AUDIT.md +110 -0
- package/docs/ENGINEERING.md +14 -4
- package/docs/LOCAL_AUTOMATION.md +3 -3
- package/docs/LOGGING.md +5 -3
- package/docs/MANAGED_JOBS.md +5 -1
- package/docs/OPERATIONS.md +12 -4
- package/docs/PRIVACY.md +5 -3
- package/docs/RELEASING.md +1 -1
- package/docs/TESTING.md +11 -8
- package/package.json +7 -8
- package/scripts/github-release.mjs +368 -0
- package/scripts/privacy-check.mjs +66 -5
- package/scripts/release-state.mjs +10 -0
- package/scripts/syntax-check.mjs +52 -0
- package/src/local/app-automation.mjs +3 -1
- package/src/local/browser-bridge.mjs +94 -53
- package/src/local/cli.mjs +165 -81
- package/src/local/daemon-process.mjs +3 -67
- package/src/local/exclusive-file.mjs +94 -0
- package/src/local/job-runner.mjs +33 -17
- package/src/local/log.mjs +16 -1
- package/src/local/managed-jobs.mjs +187 -56
- package/src/local/process-identity.mjs +143 -0
- package/src/local/resource-operations.mjs +2 -3
- package/src/local/runtime.mjs +4 -2
- package/src/local/service-lifecycle.mjs +56 -0
- package/src/local/service.mjs +53 -25
- package/src/local/shell.mjs +23 -4
- package/src/local/state.mjs +228 -66
- package/src/worker/index.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,31 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.12.0 - 2026-07-12
|
|
4
|
+
|
|
5
|
+
### Locking, persistence, and process lifecycle
|
|
6
|
+
|
|
7
|
+
- Replace partial-write-prone final-path lock creation with fully written, flushed temporary files and atomic same-directory hard-link claims. Record ownership tokens and process start times, detect PID reuse, verify file identity before stale-lock removal, preserve recent malformed locks, and add bounded startup-lock waiting.
|
|
8
|
+
- Add a state-root maintenance lock for full uninstall, including checks in already constructed managed-job and browser managers. State roots must be disjoint from selected workspaces, including non-existent paths beneath canonicalized platform aliases; unreadable locks/jobs and ambiguous process identities now block destructive cleanup rather than being treated as inactive.
|
|
9
|
+
- Consolidate owner-only state, managed-job, runner, browser-pairing, and service-definition commits on one flushed atomic replacement primitive. Only successfully read invalid JSON is backed up; permission, symbolic-link, size, invalid UTF-8, and I/O failures propagate without silently reconstructing empty state.
|
|
10
|
+
- Persist managed-job runner process identity, protect transition/recovery locks with token and file-snapshot checks, hand recovery ownership to the runner without an unconditional delete, and keep timeout/cancellation escalation alive until resistant descendant process trees are terminated.
|
|
11
|
+
|
|
12
|
+
### Services, uninstall, and cross-platform behavior
|
|
13
|
+
|
|
14
|
+
- Extract one fail-closed service lifecycle: stop the platform service, stop every verified workspace daemon, and remove the service definition only after all stop phases succeed. Full uninstall scans every profile and rechecks managed jobs and process locks while holding maintenance ownership.
|
|
15
|
+
- Normalize launchd, systemd user services, and Windows Scheduled Tasks to a common success/failure contract so non-zero Linux/Windows stop results cannot be mistaken for success. Preserve service definitions and local state whenever stop, verification, definition removal, or remote Worker discovery is incomplete.
|
|
16
|
+
- Make CLI helper timeouts terminate process groups/trees rather than only direct children, including forced escalation for descendants that ignore graceful termination.
|
|
17
|
+
|
|
18
|
+
### Security, privacy, and local automation
|
|
19
|
+
|
|
20
|
+
- Expand repository privacy and log redaction coverage for npm authentication, common cloud/source-control/chat/payment tokens, JWT-shaped values, embedded-credential URLs, broader private-key headers, credential-shaped filenames, and non-example identities. Scanner read/traversal failures now fail closed without echoing matched values.
|
|
21
|
+
- Add final-component no-follow reads in restricted filesystem paths, reject NUL application action values, and normalize browser-upload filenames and MIME types to prevent deceptive metadata or downstream form parsing ambiguity.
|
|
22
|
+
- Close authenticated browser broker sockets with protocol-specific WebSocket codes for oversized, invalid UTF-8/JSON, or structurally invalid messages instead of silently retaining faulty clients.
|
|
23
|
+
|
|
24
|
+
### Tests, architecture, and documentation
|
|
25
|
+
|
|
26
|
+
- Replace the hand-maintained JavaScript syntax list with recursive entrypoint discovery; remove stale imports and duplicated lifecycle logic. Add executable concurrency, atomicity, PID-reuse, service-failure, process-tree, state-corruption, unsafe-root, unreadable-job, browser-protocol, upload-metadata, and privacy regression tests.
|
|
27
|
+
- Add `docs/AUDIT.md` and synchronize architecture, security, operations, logging, privacy, managed-job, local-automation, testing, contribution, and release documentation with the implemented fail-closed behavior and residual OS-level limitations.
|
|
28
|
+
|
|
3
29
|
## 0.11.1 - 2026-07-12
|
|
4
30
|
|
|
5
31
|
### Reliable macOS service and orphan-daemon takeover
|
package/CONTRIBUTING.md
CHANGED
|
@@ -17,7 +17,7 @@ The release operator then creates the matching Git tag and GitHub Release and pu
|
|
|
17
17
|
After npm publication, the standard machine update is:
|
|
18
18
|
|
|
19
19
|
```bash
|
|
20
|
-
npm install -g --allow-scripts=esbuild,workerd,sharp machine-bridge-mcp@latest && machine-mcp
|
|
20
|
+
npm install -g --omit=optional --allow-scripts=esbuild,workerd,sharp,fsevents machine-bridge-mcp@latest && machine-mcp
|
|
21
21
|
```
|
|
22
22
|
|
|
23
23
|
The npm command updates the global CLI. Normal `machine-mcp` startup checks the Worker deployment hash, expected version, and health, redeploys when necessary, and reconciles the daemon/autostart flow.
|
|
@@ -34,6 +34,6 @@ Use only synthetic names, reserved example domains, and generic paths. Maintain
|
|
|
34
34
|
|
|
35
35
|
Read [docs/ENGINEERING.md](docs/ENGINEERING.md) before changing architecture, policy, logging, persistence, transport lifecycle, or release behavior. The default `full` profile is an explicit product invariant and must not be narrowed by an unrelated change.
|
|
36
36
|
|
|
37
|
-
A log change is behavior: test its level, repetition policy, privacy fields, and recovery message. A transport change must distinguish low-level connectivity from authenticated readiness and test timeout/reconnect branches deterministically.
|
|
37
|
+
A log change is behavior: test its level, repetition policy, privacy fields, and recovery message. A transport change must distinguish low-level connectivity from authenticated readiness and test timeout/reconnect branches deterministically. Lock, state deletion, service lifecycle, detached process, and credential changes require behavior-level concurrency or fault-injection tests. Review [docs/AUDIT.md](docs/AUDIT.md) before changing those surfaces.
|
|
38
38
|
|
|
39
39
|
Reusable decisions belong in tracked documentation. Keep only machine-specific observations in the ignored `.project-local/` directory, and never store credentials there.
|
package/README.md
CHANGED
|
@@ -79,7 +79,7 @@ On first remote start, the CLI:
|
|
|
79
79
|
6. starts an outbound-only daemon connection;
|
|
80
80
|
7. prints the Remote MCP URL and connection password.
|
|
81
81
|
|
|
82
|
-
A normal `machine-mcp` invocation is a foreground start: it remains attached to the terminal and prints `info` logs. After a global package upgrade, the CLI unloads the platform service and also checks the workspace lock for a detached/orphan `--daemon-only` process that the service manager no longer tracks. It terminates only a process whose PID, entrypoint, canonical workspace, canonical state root, and daemon-only arguments all match, waits up to 15 seconds for the PID and lock to disappear, and then starts the installed version in the foreground. A genuine foreground or unverifiable conflict is left untouched and reported with actionable guidance. To run only in the background, use `machine-mcp service start`; inspect its owner-only logs under `~/.local/state/machine-bridge-mcp/logs/`.
|
|
82
|
+
A normal `machine-mcp` invocation is a foreground start: it remains attached to the terminal and prints `info` logs. Startup and other state-changing operations use an owner-token/process-identity lock and wait up to 30 seconds for an ordinary concurrent startup to finish instead of failing on a brief launchd/systemd race. After a global package upgrade, the CLI unloads the platform service and also checks the workspace lock for a detached/orphan `--daemon-only` process that the service manager no longer tracks. It terminates only a process whose PID, process start time, entrypoint, canonical workspace, canonical state root, and daemon-only arguments all match, waits up to 15 seconds for the PID and lock to disappear, and then starts the installed version in the foreground. A genuine foreground or unverifiable conflict is left untouched and reported with actionable guidance. To run only in the background, use `machine-mcp service start`; inspect its owner-only logs under `~/.local/state/machine-bridge-mcp/logs/`.
|
|
83
83
|
|
|
84
84
|
Recommended upgrade sequence:
|
|
85
85
|
|
|
@@ -422,7 +422,7 @@ Default state roots:
|
|
|
422
422
|
- Linux with `XDG_STATE_HOME`: `$XDG_STATE_HOME/machine-bridge-mcp`
|
|
423
423
|
- Windows: `%APPDATA%\machine-bridge-mcp`
|
|
424
424
|
|
|
425
|
-
State/config writes use owner-only temporary files,
|
|
425
|
+
State/config writes use owner-only temporary files, `fsync`, and atomic replacement. Exclusive locks are fully written before a same-directory hard-link claim becomes visible; lock ownership includes a token and process start time so PID reuse and lock replacement cannot silently transfer ownership. Only successfully read but invalid JSON is retained as a bounded corrupt backup; permission, symbolic-link, size, encoding, and I/O failures remain explicit. A custom state root must not equal, contain, or be contained by the selected workspace. 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 first acquires a state-root maintenance lock that blocks new profile/state operations and state-backed operations from already constructed managed-job/browser managers, stops the platform service and all verified workspace daemons before removing definitions, rechecks jobs and locks, then validates markers, canonical paths, workspace/source exclusions, and known contents before recursive deletion.
|
|
426
426
|
|
|
427
427
|
Default foreground logs report authenticated relay readiness, readable persistent-degradation summaries, and recovery rather than raw WebSocket callbacks or JSON field dumps. Brief self-healing disconnects and close codes/reasons are debug-only. Stalled connection attempts have a deadline, and sustained-outage reminders use autonomous exponential backoff. Every per-tool event—including success, failure, cancellation, and slow-call timing—also appears only at `--log-level debug` or `--verbose`. Background services use `warn`, so ordinary tool outcomes and brief network changes do not 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).
|
|
428
428
|
|
|
@@ -437,9 +437,9 @@ npm audit --omit=dev --audit-level=high
|
|
|
437
437
|
npm pack --dry-run
|
|
438
438
|
```
|
|
439
439
|
|
|
440
|
-
`npm run check` covers privacy and release-impact gates, architecture/link invariants, generated Worker types, TypeScript, JavaScript syntax, catalog-to-runtime handler parity, deterministic relay lifecycle and secure-file tests, local path/write/
|
|
440
|
+
`npm run check` covers privacy and release-impact gates, architecture/link invariants, generated Worker types, TypeScript, recursively discovered JavaScript syntax, catalog-to-runtime handler parity, deterministic relay lifecycle and secure-file tests, concurrent lock/atomic-replacement/PID-reuse fixtures, fail-closed service lifecycle tests, descendant process-tree termination, local path/write/state/log/service invariants, Ed25519/RSA generation and key-pair validation, real-machine `full` sandbox acceptance, a clean npm package-manifest/mode/sensitive-artifact check, managed-job integrity/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; macOS and package-audit jobs also exercise the documented isolated global installation.
|
|
441
441
|
|
|
442
|
-
See [docs/AGENT_CONTEXT.md](docs/AGENT_CONTEXT.md), [docs/LOCAL_AUTOMATION.md](docs/LOCAL_AUTOMATION.md), [docs/MANAGED_JOBS.md](docs/MANAGED_JOBS.md), [docs/TESTING.md](docs/TESTING.md), [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), [docs/ENGINEERING.md](docs/ENGINEERING.md), and [SECURITY.md](SECURITY.md).
|
|
442
|
+
The cross-cutting 0.12.0 review, corrected failure modes, and residual OS-level limits are recorded in [docs/AUDIT.md](docs/AUDIT.md). See also [docs/AGENT_CONTEXT.md](docs/AGENT_CONTEXT.md), [docs/LOCAL_AUTOMATION.md](docs/LOCAL_AUTOMATION.md), [docs/MANAGED_JOBS.md](docs/MANAGED_JOBS.md), [docs/TESTING.md](docs/TESTING.md), [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), [docs/ENGINEERING.md](docs/ENGINEERING.md), and [SECURITY.md](SECURITY.md).
|
|
443
443
|
|
|
444
444
|
## Uninstall
|
|
445
445
|
|
|
@@ -448,7 +448,7 @@ machine-mcp uninstall
|
|
|
448
448
|
npm uninstall -g machine-bridge-mcp
|
|
449
449
|
```
|
|
450
450
|
|
|
451
|
-
Use `--keep-worker` to retain deployed Workers while removing local state and autostart.
|
|
451
|
+
Use `--keep-worker` to retain deployed Workers while removing local state and autostart. Uninstall is fail-closed: if the platform service, a verified daemon, a managed job, an unreadable lock, or a service-definition removal cannot be resolved safely, service definitions and local state are retained for diagnosis instead of being partially deleted.
|
|
452
452
|
|
|
453
453
|
## License
|
|
454
454
|
|
package/SECURITY.md
CHANGED
|
@@ -95,13 +95,19 @@ A canonical `full` profile guarantees that Machine Bridge itself will not reject
|
|
|
95
95
|
|
|
96
96
|
## Mutation integrity
|
|
97
97
|
|
|
98
|
-
Writes are bounded, reject symbolic-link/non-regular destinations, use same-directory staging, and commit atomically per file. Create-only
|
|
98
|
+
Writes are bounded, reject symbolic-link/non-regular destinations, use same-directory staging, flush staged data, and commit atomically per file. Create-only and lock claims fully write a private temporary file before a same-directory hard-link claim becomes visible; a concurrent destination fails without exposing partial content. Replacement writes use `fsync` plus atomic rename/replacement. Expected hashes and exact edits reduce accidental stale overwrites.
|
|
99
99
|
|
|
100
|
-
|
|
100
|
+
Startup, daemon, managed-job transition, recovery, and runner claims record ownership tokens and process start time. Reclamation validates process identity and a file snapshot before removal, limiting PID-reuse and stale-owner races. Recent malformed claims are not deleted immediately. Ordinary startup/state changes wait a bounded interval for a concurrent operation instead of turning a brief service-manager race into an operator error.
|
|
101
|
+
|
|
102
|
+
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. Existing reads and writes use canonical containment checks and final-component no-follow opens where the platform supports `O_NOFOLLOW`. On platforms without portable descriptor-relative `openat` traversal, a malicious process running as the same local account can still race a parent-directory replacement between validation and open/commit. These controls do not claim protection against a hostile same-user namespace, nor do they make a multi-directory patch power-loss atomic.
|
|
103
|
+
|
|
104
|
+
A custom state root is rejected if it overlaps the selected workspace in either direction. Recursive state removal cross-checks the marker, global selection, profile state, daemon locks, active/unreadable locks, known directory shape, package/source/workspace exclusions, service removal, and managed-job state. Any unresolved condition fails closed and retains state.
|
|
101
105
|
|
|
102
106
|
## Credential exposure
|
|
103
107
|
|
|
104
|
-
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
|
|
108
|
+
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 flushed and atomically replaced. A read/type/permission/size/symbolic-link failure is not treated as empty or corrupt state; only successfully read invalid JSON is moved to a bounded corrupt backup.
|
|
109
|
+
|
|
110
|
+
Logs recursively redact known credential fields, private-key headers, npm/GitHub/GitLab/Slack/Google/AWS/live-payment/API token forms, JWT-shaped bearer values, embedded-credential URLs, email addresses, user-home paths, and control characters. These patterns are defense in depth; unknown, transformed, split, encrypted, or application-specific secret forms can still pass through an explicitly requested tool result.
|
|
105
111
|
|
|
106
112
|
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.
|
|
107
113
|
|
|
@@ -127,7 +133,7 @@ Active plans are owner-only and may temporarily contain argv, non-secret stdin,
|
|
|
127
133
|
|
|
128
134
|
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.
|
|
129
135
|
|
|
130
|
-
`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.
|
|
136
|
+
`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. Timeout/cancellation target the process group/tree and keep a forced-termination escalation alive even if the direct child exits before a resistant descendant. Runner identity includes process start time; recovery does not trust a reused PID. 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.
|
|
131
137
|
|
|
132
138
|
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.
|
|
133
139
|
|
|
@@ -165,7 +171,7 @@ Process sessions are interactive and intentionally die with runtime disconnect/r
|
|
|
165
171
|
|
|
166
172
|
## Logs and privacy
|
|
167
173
|
|
|
168
|
-
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.
|
|
174
|
+
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. Static operator guidance uses sanitized plain output; raw plain output is reserved for explicitly requested credentials or local paths. Git author email is omitted from `git_log` unless explicitly requested.
|
|
169
175
|
|
|
170
176
|
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.
|
|
171
177
|
|
|
@@ -188,6 +194,8 @@ Also:
|
|
|
188
194
|
- patch the OS and use the repository-pinned Node.js 26/npm 12 baseline;
|
|
189
195
|
- enable MFA on Cloudflare, GitHub, and npm accounts;
|
|
190
196
|
- do not configure broad CORS origins;
|
|
197
|
+
- keep the state root completely separate from every workspace; never point `--state-dir` at a project directory or one of its ancestors;
|
|
198
|
+
- treat an unreadable or malformed live lock as an incident to inspect, not a file to delete blindly;
|
|
191
199
|
- select `agent`, `edit`, or `review` instead of the default `full` when broad authority is unnecessary;
|
|
192
200
|
- inspect client names and OAuth redirect URIs;
|
|
193
201
|
- rotate secrets after suspected disclosure;
|
|
@@ -197,6 +205,8 @@ Also:
|
|
|
197
205
|
- remove the Worker and state when remote access is no longer needed;
|
|
198
206
|
- use external OS isolation for untrusted code.
|
|
199
207
|
|
|
208
|
+
The full cross-cutting review and residual limitations are recorded in [docs/AUDIT.md](docs/AUDIT.md).
|
|
209
|
+
|
|
200
210
|
## Out of scope
|
|
201
211
|
|
|
202
212
|
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.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"manifest_version": 3,
|
|
3
3
|
"name": "Machine Bridge Browser",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.12.0",
|
|
5
5
|
"description": "Connects the current Chromium browser profile to the local Machine Bridge runtime for user-authorized automation.",
|
|
6
6
|
"permissions": [
|
|
7
7
|
"tabs",
|
|
@@ -29,5 +29,5 @@
|
|
|
29
29
|
"action": {
|
|
30
30
|
"default_title": "Machine Bridge Browser"
|
|
31
31
|
},
|
|
32
|
-
"version_name": "0.
|
|
32
|
+
"version_name": "0.12.0"
|
|
33
33
|
}
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -18,9 +18,9 @@ No transport is treated as a sandbox. Both transports invoke the same local runt
|
|
|
18
18
|
|
|
19
19
|
### CLI and state layer
|
|
20
20
|
|
|
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.
|
|
21
|
+
The CLI canonicalizes workspaces, resolves policy profiles, maintains per-workspace state and credentials, serializes startup/deploy/rotation with process-identity 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, 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.
|
|
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. `exclusive-file.mjs` owns complete-before-visible exclusive claims and flushed atomic replacement. `process-identity.mjs` owns PID liveness, process start-time comparison, bounded command-line inspection, and PID-reuse classification. `service-lifecycle.mjs` owns the fail-closed stop-daemons-before-remove state machine shared by service removal and full uninstall.
|
|
24
24
|
|
|
25
25
|
### Local runtime
|
|
26
26
|
|
|
@@ -40,7 +40,7 @@ A canonical workspace receives an independent profile, Worker name, secret set,
|
|
|
40
40
|
|
|
41
41
|
`RelayConnection` owns remote WebSocket transport, authenticated `hello_ack` readiness, heartbeat liveness, reconnect backoff, and outage logging. Stdio mode invokes `LocalRuntime` directly without that adapter.
|
|
42
42
|
|
|
43
|
-
`daemon-process.mjs` owns workspace-daemon inspection and takeover. It distinguishes platform service state from the lock-owning Node process, canonicalizes workspace/state paths before
|
|
43
|
+
`daemon-process.mjs` owns workspace-daemon inspection and takeover. It distinguishes platform service state from the lock-owning Node process, validates PID and process-start identity, canonicalizes workspace/state paths before comparison, parses bounded process command lines without executing them, and sends `SIGTERM` only to a verified same-workspace `--daemon-only` process. CLI orchestration never treats a missing launchd/systemd job as proof that the process exited.
|
|
44
44
|
|
|
45
45
|
### Agent context and capability resolver
|
|
46
46
|
|
|
@@ -113,6 +113,8 @@ The daemon attachment deliberately omits workspace path/name/hash and process ID
|
|
|
113
113
|
|
|
114
114
|
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.
|
|
115
115
|
|
|
116
|
+
The platform adapters normalize launchd, systemd, and Windows Scheduled Task operations to one `{ok, provider}` result contract. Removal is not a provider-specific sequence: `service-lifecycle.mjs` first stops the provider, then every verified workspace daemon in scope, and only then removes the definition. A failed stop or unverifiable process prevents definition/state deletion.
|
|
117
|
+
|
|
116
118
|
## Trust boundaries
|
|
117
119
|
|
|
118
120
|
```mermaid
|
|
@@ -170,7 +172,7 @@ The workspace is canonicalized and compared with targets through consistent plat
|
|
|
170
172
|
|
|
171
173
|
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.
|
|
172
174
|
|
|
173
|
-
Symbolic-link destinations and non-regular write targets are rejected. Recursive walkers do not follow symbolic-link directories.
|
|
175
|
+
Symbolic-link destinations and non-regular write targets are rejected. Existing bounded reads add final-component `O_NOFOLLOW` where supported. Recursive walkers do not follow symbolic-link directories. Because portable Node.js lacks descriptor-relative `openat` traversal for every operation, parent-directory replacement by hostile same-user code remains an external-isolation concern.
|
|
174
176
|
|
|
175
177
|
## Mutation model
|
|
176
178
|
|
|
@@ -200,7 +202,7 @@ The default `full` profile passes the complete parent environment. Isolated envi
|
|
|
200
202
|
|
|
201
203
|
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.
|
|
202
204
|
|
|
203
|
-
Child processes run in a separate process group where supported. Timeout, cancellation, disconnect, and replacement send termination to
|
|
205
|
+
Child processes run in a separate process group where supported. Timeout, cancellation, disconnect, and replacement send termination to process trees, with a referenced forced-escalation timer that remains alive even when the direct child exits before a resistant descendant. Windows uses tree-aware task termination.
|
|
204
206
|
|
|
205
207
|
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.
|
|
206
208
|
|
|
@@ -214,11 +216,15 @@ The Worker also treats a new connection as a bounded candidate until it authenti
|
|
|
214
216
|
|
|
215
217
|
## Persistence
|
|
216
218
|
|
|
217
|
-
Local state and global config are owner-only, versioned, size-bounded
|
|
219
|
+
Local state and global config are owner-only, versioned, and size-bounded. Shared persistence primitives write a complete private temporary file, `fsync` it, and either hard-link it for an exclusive claim or atomically replace the destination. Machine-level browser pairing state is owner-only and shared across workspace runtimes through the local broker; its bearer token is not part of workspace state responses. State, managed-job manager, detached runner, browser pairing, and service definitions share the same flushed atomic-replacement primitive plus bounded retry for transient Windows sharing failures.
|
|
220
|
+
|
|
221
|
+
Process locks contain purpose, workspace, ownership token, lock time, and process start time. Stale removal rechecks device/inode/size/mtime and token so an old observer cannot delete a replacement lock. Recent malformed claims receive a grace period. Startup/state operations wait a bounded interval; daemon and runner identity remain process-lifetime locks. Managed-job transition and recovery locks use the same ownership/snapshot principles and support an atomic runner handoff.
|
|
222
|
+
|
|
223
|
+
Only successfully read but syntactically invalid JSON is moved to a bounded `.corrupt-*` backup. Permission, type, symbolic-link, size, encoding, and I/O failures propagate. A state root must be disjoint from its selected workspace. Resource paths are omitted from redacted status output. Custom roots are adopted only when empty or recognizable as legacy Machine Bridge state.
|
|
218
224
|
|
|
219
|
-
Active managed jobs persist an owner-only plan, status, runner
|
|
225
|
+
Active managed jobs persist an owner-only plan, status, runner process identity, 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.
|
|
220
226
|
|
|
221
|
-
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.
|
|
227
|
+
Removal first acquires a state-root maintenance lock that blocks new profile/state claims and state-backed operations from already constructed managed-job/browser managers, then stops the platform service and all known verified workspace daemons. It then validates the state marker, canonical target, known contents, active or unreadable locks, filesystem root/home/current/package/workspace/source exclusions, managed jobs, and Worker deletion outcome before recursive deletion. Any unresolved phase retains definitions and state.
|
|
222
228
|
|
|
223
229
|
OAuth metadata is pruned on access. Expired codes/tokens, old throttling records, and inactive clients without active credentials are removed. Source identities are deployment-keyed HMAC values, not stored source addresses or reversible unsalted hashes.
|
|
224
230
|
|
package/docs/AUDIT.md
ADDED
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Engineering and security audit
|
|
2
|
+
|
|
3
|
+
This document records the cross-cutting audit performed for version 0.12.0. It complements, but does not replace, the continuously enforced contracts in `SECURITY.md`, `docs/ENGINEERING.md`, and the test suite.
|
|
4
|
+
|
|
5
|
+
## Scope
|
|
6
|
+
|
|
7
|
+
The review covered:
|
|
8
|
+
|
|
9
|
+
- CLI startup, foreground/background takeover, state locks, service installation/removal, and full uninstall;
|
|
10
|
+
- canonical paths, state-root deletion, atomic persistence, symbolic links, permissions, and process identity;
|
|
11
|
+
- daemon/Worker transport, OAuth, relay replacement, request limits, and failure reporting;
|
|
12
|
+
- direct processes, shell execution, interactive sessions, managed jobs, cancellation, recovery, and cleanup;
|
|
13
|
+
- browser pairing, broker authentication, uploads, DOM actions, local application automation, and sensitive fields;
|
|
14
|
+
- logging, privacy scanning, package contents, release scripts, documentation consistency, CI, and dead code.
|
|
15
|
+
|
|
16
|
+
The audit used source review, state-machine analysis, hostile-input tests, concurrent-process fixtures, process-tree fixtures, package dry runs, dependency audits, and the existing remote/stdio integration suites.
|
|
17
|
+
|
|
18
|
+
## High-impact findings and corrections
|
|
19
|
+
|
|
20
|
+
### Lock and process identity
|
|
21
|
+
|
|
22
|
+
Process locks were previously created directly at their final path and then populated. A competitor could observe an empty or partial JSON file. Locks also relied primarily on PID liveness, which cannot distinguish PID reuse.
|
|
23
|
+
|
|
24
|
+
The runtime now:
|
|
25
|
+
|
|
26
|
+
- writes and `fsync`s a private temporary file before atomically claiming a lock with a same-directory hard link;
|
|
27
|
+
- records a random ownership token and process start time;
|
|
28
|
+
- validates PID liveness, process start time, lock age, purpose, workspace, and file identity;
|
|
29
|
+
- removes a stale lock only when its device, inode, size, modification time, and token still match the inspected snapshot;
|
|
30
|
+
- gives a recent malformed lock a grace period rather than deleting a possibly in-progress claim;
|
|
31
|
+
- waits a bounded interval for ordinary startup/state operations instead of failing immediately.
|
|
32
|
+
|
|
33
|
+
Managed-job transition, recovery, and runner identity follow the same principles while retaining compatibility with legacy numeric PID files.
|
|
34
|
+
|
|
35
|
+
### Service lifecycle
|
|
36
|
+
|
|
37
|
+
Service providers returned inconsistent result shapes, and removal could continue after an incomplete stop. Full uninstall considered too little profile state.
|
|
38
|
+
|
|
39
|
+
The lifecycle is now fail-closed and ordered:
|
|
40
|
+
|
|
41
|
+
1. stop the platform service;
|
|
42
|
+
2. stop every verified workspace daemon in scope;
|
|
43
|
+
3. confirm each stop result;
|
|
44
|
+
4. remove the service definition only after all stops succeed;
|
|
45
|
+
5. retain service definitions and state when any phase fails.
|
|
46
|
+
|
|
47
|
+
macOS, systemd, and Windows Scheduled Tasks now return a common `ok` contract. Full uninstall scans all known profiles and can recover workspace identity from daemon locks when a state file is unavailable.
|
|
48
|
+
|
|
49
|
+
### State persistence and deletion
|
|
50
|
+
|
|
51
|
+
State reads previously classified any read error as corrupt JSON and could continue with an empty state. A dangerously selected state root could also overlap a workspace.
|
|
52
|
+
|
|
53
|
+
The runtime now:
|
|
54
|
+
|
|
55
|
+
- backs up a file only after it was read successfully and its JSON content is actually invalid;
|
|
56
|
+
- propagates permission, type, symbolic-link, size, encoding, and I/O failures;
|
|
57
|
+
- uses one shared `fsync` plus atomic-replacement primitive for state, job, runner, browser-pairing, and service-definition writes;
|
|
58
|
+
- rejects a state root that overlaps the selected workspace in either direction before creating state;
|
|
59
|
+
- acquires a state-root maintenance lock to block new profile/state operations and state-backed operations from already constructed managed-job/browser managers, and cross-checks deletion against global workspace selection, profile state, daemon locks, state markers, and known directory shape;
|
|
60
|
+
- treats unreadable process locks as an uninstall blocker rather than evidence of inactivity.
|
|
61
|
+
|
|
62
|
+
### Process-tree cleanup
|
|
63
|
+
|
|
64
|
+
CLI helper commands and managed jobs could terminate the direct child while leaving descendants alive. Escalation timers could be cleared or unreferenced before killing a resistant descendant.
|
|
65
|
+
|
|
66
|
+
Timeout and cancellation now target the process group/tree, keep the escalation timer alive, and issue a bounded `SIGKILL`/Windows tree termination after graceful shutdown. Tests use descendants that deliberately ignore `SIGTERM`.
|
|
67
|
+
|
|
68
|
+
### Privacy and logging
|
|
69
|
+
|
|
70
|
+
The privacy gate did not inspect tracked npm authentication configuration or several common credential formats. Free-form log sanitization had corresponding gaps.
|
|
71
|
+
|
|
72
|
+
Checks and redaction now cover:
|
|
73
|
+
|
|
74
|
+
- generic, encrypted, OpenSSH, RSA, EC, and DSA private-key headers;
|
|
75
|
+
- npm, GitHub, GitLab, Slack, Google API, AWS, live payment, and common API-secret formats;
|
|
76
|
+
- JWT-shaped bearer values and URLs with embedded credentials;
|
|
77
|
+
- non-example email addresses and absolute user-home paths;
|
|
78
|
+
- credential-shaped publication filenames;
|
|
79
|
+
- authentication, identity, environment interpolation, and credential URLs in a tracked `.npmrc`.
|
|
80
|
+
|
|
81
|
+
Findings report only file, line, and rule. The matched value is never printed. A tracked `.npmrc` remains permitted for non-secret repository settings such as `engine-strict=true`.
|
|
82
|
+
|
|
83
|
+
### Local automation boundaries
|
|
84
|
+
|
|
85
|
+
Browser upload metadata now requires a safe single-component filename and canonical MIME type. Derived local filenames have controls and separators removed. Application action text rejects NUL bytes. Sensitive browser and Accessibility values remain excluded from results.
|
|
86
|
+
|
|
87
|
+
Restricted filesystem reads now add `O_NOFOLLOW` for the final path component in addition to canonical workspace containment.
|
|
88
|
+
|
|
89
|
+
### Test and release integrity
|
|
90
|
+
|
|
91
|
+
The hand-maintained syntax file list was replaced by a recursive scanner. Package scripts are checked for missing entrypoints, release helper scripts are packaged, npm package modes are validated, and installation guidance is checked across the CLI and documentation. Dedicated tests cover lock concurrency, atomic replacement, PID reuse, malformed locks, service removal ordering, process-tree termination, state read failures, unsafe state roots, browser upload metadata, and expanded privacy patterns.
|
|
92
|
+
|
|
93
|
+
## Reviewed areas without material code changes
|
|
94
|
+
|
|
95
|
+
The review also rechecked Worker OAuth/PKCE validation, exact redirect/resource binding, token hashing/version revocation, registration and failure limits, Durable Object serialization, daemon WebSocket authentication/replacement, CSP/no-store response headers, static MCP catalog parity, browser loopback Host/Origin/subprotocol validation, and fixed-code JXA/browser execution. Existing controls and integration coverage were retained.
|
|
96
|
+
|
|
97
|
+
## Residual limits
|
|
98
|
+
|
|
99
|
+
No review can prove the absence of all defects. Important residual boundaries are:
|
|
100
|
+
|
|
101
|
+
- Node.js cannot provide a portable `openat`-style directory capability for every filesystem operation. Canonical containment plus `O_NOFOLLOW` protects the final component, but hostile same-user code can still race parent-directory replacement. Use a dedicated low-privilege account, VM, or container for untrusted repositories.
|
|
102
|
+
- Same-user malicious processes can inspect memory, signal processes, alter browser profiles, or manipulate files the user can access. Machine Bridge is not an OS sandbox.
|
|
103
|
+
- Hard-link atomic lock claims require a local filesystem that supports same-directory hard links. Failure is explicit; the runtime does not fall back to a partial-write-prone lock.
|
|
104
|
+
- Service-manager behavior varies by OS version and local policy. Provider failures remain visible and fail closed, but operator intervention can still be required.
|
|
105
|
+
- Browser pages, application UIs, project instructions, skills, command manifests, and command output are untrusted content. Human/host approval remains necessary for consequential actions.
|
|
106
|
+
- The MCP host may filter tools, omit server instructions, or block calls before they reach Machine Bridge; the server cannot observe or override that boundary.
|
|
107
|
+
|
|
108
|
+
## Ongoing review rule
|
|
109
|
+
|
|
110
|
+
A future change to locks, state deletion, service lifecycle, detached processes, credentials, browser/app authority, package contents, or public transport must add a behavior-level regression test and update the applicable security/operations documentation. String-presence tests may supplement, but must not replace, executable state-transition tests.
|
package/docs/ENGINEERING.md
CHANGED
|
@@ -13,6 +13,9 @@ This document records project-wide decisions that must survive individual fixes,
|
|
|
13
13
|
7. **Generic local automation is structured, not arbitrary evaluation.** Browser/application features may expose broad user authority under canonical `full`, but must not accept caller-provided JavaScript, AppleScript, JXA, or extension code.
|
|
14
14
|
8. **Daily-browser integration uses the existing profile.** The supported primary browser path is the packaged authenticated extension and machine-level loopback broker, preserving current tabs/login state; a separate automation profile is not an equivalent replacement.
|
|
15
15
|
9. **Pairing and resource secrets are not conversation or log data.** Tokens and injected local-resource values must not be returned, embedded in URLs, or written to operational logs.
|
|
16
|
+
10. **Exclusive claims are complete before visible.** Never create a final lock/PID claim and then populate it. Use the shared exclusive-file primitive, ownership tokens, process-start identity, and snapshot-checked reclamation.
|
|
17
|
+
11. **Service and state removal are fail-closed state machines.** Stop the platform provider and every verified daemon before removing definitions or recursive state. An unreadable lock, failed stop, active job, or ambiguous identity retains state for diagnosis.
|
|
18
|
+
12. **Read failure is not empty state.** Permission, type, symbolic-link, size, encoding, and I/O errors must propagate. Corrupt backup/reconstruction applies only after a successful read proves that JSON content is invalid.
|
|
16
19
|
|
|
17
20
|
A proposed change that conflicts with an invariant requires an explicit owner decision and corresponding documentation update. It must not be hidden inside an unrelated refactor.
|
|
18
21
|
|
|
@@ -49,7 +52,9 @@ Rules:
|
|
|
49
52
|
- State transitions are explicit; readiness is not inferred from a lower-level event. For example, an open WebSocket is not an authenticated relay until `hello_ack` is received.
|
|
50
53
|
- Every externally controlled input is bounded before expensive allocation, traversal, parsing, storage, or execution.
|
|
51
54
|
- Repository text must not contain invisible ASCII controls other than tab, CR, and LF; architecture tests enforce this even when JavaScript syntax remains valid.
|
|
52
|
-
- Persistent mutations use owner-only files, bounded reads, atomic replacement, and integrity checks appropriate to the data.
|
|
55
|
+
- Persistent mutations use owner-only files, bounded no-follow reads, flushed atomic replacement, and integrity checks appropriate to the data.
|
|
56
|
+
- Exclusive locks use the shared complete-before-visible hard-link claim. Reclamation requires process identity plus a matching file snapshot/token; do not unlink a path merely because an earlier read looked stale.
|
|
57
|
+
- Service providers normalize success/failure to one result contract. Definition removal follows the shared platform-stop → verified-daemon-stop → remove order.
|
|
53
58
|
- Retry is limited to classified transient failures. Authentication, authorization, validation, integrity, and policy errors fail immediately.
|
|
54
59
|
- Cleanup-only catches may be best effort, but primary failures must not be silently discarded.
|
|
55
60
|
- New work should not increase an already broad orchestration module when the behavior has an independent lifecycle or test surface. Extract the domain first.
|
|
@@ -102,9 +107,10 @@ A higher branch count is acceptable only when the function is an explicit state
|
|
|
102
107
|
- Preserve a stable coarse error class for automation and a concise human message for operators.
|
|
103
108
|
- Do not retry an operation merely because it failed; retry only when the error is positively classified as transient and the operation is idempotent or server state is checked after ambiguity.
|
|
104
109
|
- When a remote write may have succeeded before the response was lost, query authoritative state before repeating it.
|
|
105
|
-
- Timeouts must terminate the relevant process
|
|
110
|
+
- Timeouts must terminate the relevant process tree. Forced escalation must remain referenced until resistant descendants are handled; do not clear it merely because the direct child exited.
|
|
106
111
|
- Half-open connections need liveness detection, not only periodic writes.
|
|
107
|
-
- Lock reclamation must consider
|
|
112
|
+
- Lock reclamation must consider process liveness, process start time, absolute age, ownership token, and file identity to defend against PID reuse and replacement races.
|
|
113
|
+
- State/config recovery may classify only parse/root-shape failures as corrupt content. It must not convert read failures into a new empty state.
|
|
108
114
|
- A recovery path must be bounded and converge to a terminal state rather than retry forever.
|
|
109
115
|
|
|
110
116
|
## Testing rules
|
|
@@ -119,7 +125,9 @@ The required matrix includes:
|
|
|
119
125
|
- Linux, macOS, and Windows on the pinned Node/npm baseline;
|
|
120
126
|
- privacy and release-impact gates;
|
|
121
127
|
- package-manifest and sensitive-artifact inspection;
|
|
122
|
-
- type and syntax checks;
|
|
128
|
+
- generated type checks and recursively discovered JavaScript/shell syntax checks;
|
|
129
|
+
- concurrent exclusive-lock/atomic-replacement tests, PID-reuse/age tests, and fail-closed service-lifecycle tests;
|
|
130
|
+
- real process-tree timeout/cancellation tests with descendants that ignore graceful termination;
|
|
123
131
|
- local runtime and real `full` acceptance tests;
|
|
124
132
|
- stdio JSON-RPC integration;
|
|
125
133
|
- Worker OAuth/WebSocket/MCP integration;
|
|
@@ -156,6 +164,8 @@ Do not store passwords, tokens, private keys, authorization URLs, or copied secr
|
|
|
156
164
|
|
|
157
165
|
When a local observation becomes generally true, move the sanitized lesson into tracked documentation and delete the stale local note.
|
|
158
166
|
|
|
167
|
+
The 0.12.0 cross-cutting audit and residual limits are recorded in [AUDIT.md](AUDIT.md).
|
|
168
|
+
|
|
159
169
|
## Review checklist
|
|
160
170
|
|
|
161
171
|
A thorough review asks:
|
package/docs/LOCAL_AUTOMATION.md
CHANGED
|
@@ -60,7 +60,7 @@ The broker is machine-global rather than workspace-global. One local owner liste
|
|
|
60
60
|
- `browser_inspect_page` returns bounded interactive-element metadata.
|
|
61
61
|
- `browser_action` performs one structured navigation or DOM action. Navigation accepts absolute `http`, `https`, or `file` URLs; script/data schemes are rejected.
|
|
62
62
|
- `browser_fill_form` fills up to 200 fields and can submit once.
|
|
63
|
-
- `browser_upload_files` sets a file input from up to eight registered local resources.
|
|
63
|
+
- `browser_upload_files` sets a file input from up to eight registered local resources. Caller filenames must be safe single-component names; derived names have controls and separators removed. MIME overrides must be canonical media types.
|
|
64
64
|
- `browser_screenshot` returns native MCP image content.
|
|
65
65
|
|
|
66
66
|
Selectors can use CSS, ID, field name, label text, visible text, ARIA/implicit role, placeholder, and a zero-based match index. The fixed page module traverses open Shadow DOM roots; closed shadow roots remain inaccessible. For frame-specific work, inspect all frames first and then pass `frame_id` to an action.
|
|
@@ -74,7 +74,7 @@ machine-mcp resource add account-password /path/to/owner-only/password.txt
|
|
|
74
74
|
machine-mcp resource add application-pdf /path/to/document.pdf
|
|
75
75
|
```
|
|
76
76
|
|
|
77
|
-
Then use `value_resource` for a text field or `resources` for `browser_upload_files`. The local daemon reads the resource only when executing the operation. Results report the alias and outcome, not the resource content. This reduces model-context and result exposure, but the destination page receives the value by design and can transmit it according to its own behavior.
|
|
77
|
+
Then use `value_resource` for a text field or `resources` for `browser_upload_files`. The local daemon reads the resource only when executing the operation. Results report the alias and outcome, not the resource content. Upload metadata is normalized before reaching the page so path separators, controls, deceptive relative names, and malformed MIME values are rejected. This reduces model-context and result exposure, but the destination page receives the value by design and can transmit it according to its own behavior.
|
|
78
78
|
|
|
79
79
|
## Application tools
|
|
80
80
|
|
|
@@ -83,7 +83,7 @@ Then use `value_resource` for a text field or `resources` for `browser_upload_fi
|
|
|
83
83
|
- `inspect_local_application` returns a bounded macOS Accessibility tree.
|
|
84
84
|
- `operate_local_application` performs a structured Accessibility action.
|
|
85
85
|
|
|
86
|
-
macOS UI inspection and actions require Accessibility permission for the Node/Machine Bridge process. Application discovery and opening are available on supported desktop platforms; structured UI inspection currently targets macOS. The application backend uses fixed JXA implementation code. The caller selects only an application, structured selector, action, and optional bounded value. `include_values` never returns values for secure-text roles or controls whose metadata indicates passwords, tokens, one-time codes, or payment-card secrets.
|
|
86
|
+
macOS UI inspection and actions require Accessibility permission for the Node/Machine Bridge process. Application discovery and opening are available on supported desktop platforms; structured UI inspection currently targets macOS. The application backend uses fixed JXA implementation code. The caller selects only an application, structured selector, action, and optional bounded value; NUL-containing action text is rejected. `include_values` never returns values for secure-text roles or controls whose metadata indicates passwords, tokens, one-time codes, or payment-card secrets.
|
|
87
87
|
|
|
88
88
|
## Capability discovery and automatic selection
|
|
89
89
|
|
package/docs/LOGGING.md
CHANGED
|
@@ -93,9 +93,11 @@ Application discovery and Accessibility operations follow the same rule: permiss
|
|
|
93
93
|
|
|
94
94
|
## Bounding and redaction
|
|
95
95
|
|
|
96
|
-
Messages, strings, object depth, object key counts, array item counts, and serialized field payloads are bounded. Control characters and Unicode display controls are neutralized. Fields with secret-like names
|
|
96
|
+
Messages, strings, object depth, object key counts, array item counts, and serialized field payloads are bounded. Control characters and Unicode display controls are neutralized. Fields with secret-like names and path-like keys are recursively redacted. Free-form sanitization covers generic private-key headers, AWS/GitHub/GitLab/npm/Slack/Google/live-payment/API token forms, JWT-shaped values, URLs with embedded credentials, email addresses, and user-home paths.
|
|
97
97
|
|
|
98
|
-
|
|
98
|
+
The logger exposes two intentional plain-output boundaries. `safePlain` sanitizes operational guidance and diagnostic text. Raw `plain` output is reserved for explicitly requested credentials or local paths whose display is the command's purpose; callers must not pass external exception text or tool content to it.
|
|
99
|
+
|
|
100
|
+
This is defense in depth, not content classification. Unknown, split, transformed, encrypted, or application-specific secret formats can evade pattern matching, which is why tool arguments and outputs are omitted rather than merely filtered.
|
|
99
101
|
|
|
100
102
|
## Files
|
|
101
103
|
|
|
@@ -120,6 +122,6 @@ Use `server_info`, `machine-mcp status`, `machine-mcp doctor`, and `diagnose_run
|
|
|
120
122
|
|
|
121
123
|
## Adding or changing logs
|
|
122
124
|
|
|
123
|
-
A default-level message must be actionable, privacy-preserving, and resistant to repetition. Add a regression test for severity and field visibility. Raw protocol values belong at debug unless a user can act on them without external documentation.
|
|
125
|
+
A default-level message must be actionable, privacy-preserving, and resistant to repetition. Add a regression test for severity and field visibility. Raw protocol values belong at debug unless a user can act on them without external documentation. New plain-output calls require explicit review of whether `safePlain` is sufficient.
|
|
124
126
|
|
|
125
127
|
See [ENGINEERING.md](ENGINEERING.md) for the project-wide review rules.
|
package/docs/MANAGED_JOBS.md
CHANGED
|
@@ -273,10 +273,14 @@ Never place a secret directly in `argv`, `env`, `stdin`, a temporary file's `con
|
|
|
273
273
|
|
|
274
274
|
## Persistence and privacy
|
|
275
275
|
|
|
276
|
-
Per-workspace jobs are stored below the owner-only profile directory. Active jobs retain an owner-only plan for crash recovery. After a terminal status is committed, the full plan is deleted, including argv, stdin, embedded temporary-file content, and resource source paths.
|
|
276
|
+
Per-workspace jobs are stored below the owner-only profile directory. Active jobs retain an owner-only plan for crash recovery. Plan, status, result, runner identity, and lock updates use flushed atomic replacement or complete-before-visible exclusive claims. Transition/recovery locks contain ownership tokens and process start time and are removed only when their file snapshot still matches. After a terminal status is committed, the full plan is deleted, including argv, stdin, embedded temporary-file content, and resource source paths.
|
|
277
277
|
|
|
278
278
|
Retained public job data contains bounded status and redacted results. Up to 50 jobs are retained for up to seven days. Private runtime copies are removed after the finally phase. Runner stdout/stderr log files contain only runner-level diagnostics; step output is not written to those operational logs.
|
|
279
279
|
|
|
280
|
+
The detached runner records both PID and process start time. Recovery therefore rejects a reused PID instead of treating an unrelated process as the active runner; legacy numeric PID files remain readable during migration. Recovery-lock handoff preserves a random ownership token, and the runner removes only a lock whose PID, token, and file snapshot still match. Timeout and cancellation terminate the process group/tree, retain a referenced forced-escalation timer, and clean descendants that ignore graceful termination before the runner exits.
|
|
281
|
+
|
|
282
|
+
Missing job JSON is distinct from unreadable or invalid job JSON. Permission, type, symbolic-link, size, UTF-8, I/O, and parse failures retain the job directory, produce an `unreadable` status for listing, skip automatic recovery/pruning, and block uninstall until an operator inspects the state.
|
|
283
|
+
|
|
280
284
|
Process sessions and managed jobs have different semantics:
|
|
281
285
|
|
|
282
286
|
- process sessions are interactive, memory-only, and die with the daemon connection;
|
package/docs/OPERATIONS.md
CHANGED
|
@@ -61,9 +61,9 @@ Application UI inspection/actions require Accessibility permission for the Node/
|
|
|
61
61
|
|
|
62
62
|
`machine-mcp` is a foreground command. It remains attached to the terminal, defaults to `info` logging, and stops on `Ctrl+C`. `machine-mcp service start` launches the installed platform service in the background and returns to the shell; that service uses `warn` logging.
|
|
63
63
|
|
|
64
|
-
A global npm install changes the CLI files on disk but does not replace an already running Node process. On a normal foreground start, Machine Bridge unloads the platform service and then independently examines the workspace daemon lock. This second path handles a detached/orphan `--daemon-only` process that launchd/systemd/task scheduling no longer tracks, including legacy locks without mode/version metadata. Before sending `SIGTERM`, Machine Bridge verifies the live command line
|
|
64
|
+
A global npm install changes the CLI files on disk but does not replace an already running Node process. Startup and other state-changing CLI operations use a token/process-identity lock and wait up to 30 seconds for a normal concurrent startup to finish; a short launchd/systemd overlap is therefore serialized rather than reported immediately as an error. On a normal foreground start, Machine Bridge unloads the platform service and then independently examines the workspace daemon lock. This second path handles a detached/orphan `--daemon-only` process that launchd/systemd/task scheduling no longer tracks, including legacy locks without mode/version metadata. Before sending `SIGTERM`, Machine Bridge verifies PID and process start time plus the live command line, entrypoint, canonical workspace, canonical state root, and daemon-only flag. It waits up to 15 seconds for both the PID and lock to disappear and never escalates a daemon takeover to a forced kill. A foreground or unverifiable process is left untouched; stop a foreground instance with `Ctrl+C`.
|
|
65
65
|
|
|
66
|
-
`machine-mcp service status [WORKSPACE]` reports two independent layers: the platform service (`active`) and `workspace_daemon`, plus `effective_active` and `orphaned_workspace_daemon` summary flags. On macOS it is possible for launchd to report inactive while a prior Node process remains alive with parent PID 1; that is an orphan-daemon condition, not proof that the daemon stopped. `service stop`
|
|
66
|
+
`machine-mcp service status [WORKSPACE]` reports two independent layers: the platform service (`active`) and `workspace_daemon`, plus `effective_active` and `orphaned_workspace_daemon` summary flags. On macOS it is possible for launchd to report inactive while a prior Node process remains alive with parent PID 1; that is an orphan-daemon condition, not proof that the daemon stopped. `service stop` unloads the provider when present and then terminates only a verified service-style workspace daemon. `service uninstall` and full uninstall are ordered fail-closed operations: provider stop → verified daemon stop(s) → definition removal. A failed or ambiguous stop leaves definitions and state intact. If takeover reaches its deadline, run:
|
|
67
67
|
|
|
68
68
|
```sh
|
|
69
69
|
machine-mcp service stop
|
|
@@ -80,6 +80,12 @@ machine-mcp --verbose
|
|
|
80
80
|
|
|
81
81
|
Keep `--omit=optional` in the install command. Without it npm may resolve optional `fsevents` and warn that its install script was not included in `allowScripts`; Machine Bridge does not require that development-time watcher at runtime.
|
|
82
82
|
|
|
83
|
+
## State-root safety and removal
|
|
84
|
+
|
|
85
|
+
The state root must be a dedicated directory and must not equal, contain, or be contained by the selected workspace. Do not point `--state-dir` at a project directory. State/config and lock files are owner-only, bounded, and committed through flushed atomic primitives. A permission, type, symbolic-link, size, encoding, or I/O failure is reported; only successfully read invalid JSON is moved to a bounded `.corrupt-*` backup.
|
|
86
|
+
|
|
87
|
+
Uninstall acquires a state-root `maintenance.lock` that blocks new profile/state operations and state-backed operations from already constructed managed-job/browser managers, then scans all known profiles, active managed jobs, daemon/startup locks, global workspace selection, profile state, daemon lock workspace metadata, the state marker, and directory shape. It rechecks jobs and locks after stopping services/daemons. An unreadable lock is treated as a blocker, not as inactivity. Do not manually delete a lock merely because it looks old; inspect the recorded PID and command first.
|
|
88
|
+
|
|
83
89
|
## Logs
|
|
84
90
|
|
|
85
91
|
Remote autostart definitions prefer a stable PATH alias that resolves to the currently running Node executable and persist a sanitized absolute-only service `PATH` containing the Node/CLI directories, the installer's absolute PATH entries, and platform defaults. This avoids versioned Homebrew-style paths becoming invalid after upgrades and prevents launchd/systemd from falling back to a minimal system-only PATH. Re-run `machine-mcp service install` after changing Node installation families or PATH layout. A service-style `--daemon-only` start that finds the same workspace daemon already running is an idempotent no-op: it exits successfully without repeating warnings or readiness output; explicit policy/secret/change requests still report that changes were not applied. Autostart logs are stored under the state root in `logs/daemon.out.log` and `logs/daemon.err.log`. Files are owner-only where supported and tail-trimmed before daemon startup. On a logging-schema upgrade, bounded prior content is moved into `daemon.out.legacy.log` and `daemon.err.legacy.log`; use the active filenames when diagnosing current behavior.
|
|
@@ -95,7 +101,7 @@ debug all per-tool starts/successes/failures/cancellations/timing, correlation
|
|
|
95
101
|
|
|
96
102
|
Foreground mode defaults to `info`; autostart uses `warn`. Use `--verbose` or `--log-level debug` only for diagnosis. `--quiet` is an alias for `--log-level error`.
|
|
97
103
|
|
|
98
|
-
Normal logs intentionally omit tool arguments, file/patch/image content, command text and argv, stdin/stdout/stderr, OAuth request bodies, connection credentials, authorization codes, and tokens. Unexpected daemon and Worker failures use coarse error classes rather than raw exception messages. Messages and structured fields are bounded and
|
|
104
|
+
Normal logs intentionally omit tool arguments, file/patch/image content, command text and argv, stdin/stdout/stderr, OAuth request bodies, connection credentials, authorization codes, and tokens. Unexpected daemon and Worker failures use coarse error classes rather than raw exception messages. Messages and structured fields are bounded and redact private-key headers, common access-token/API-key/JWT forms, embedded-credential URLs, email addresses, and user-home paths. Raw plain terminal output is reserved for explicitly requested credentials or local paths; operational guidance uses sanitized plain output.
|
|
99
105
|
|
|
100
106
|
See [LOGGING.md](LOGGING.md) for the event contract and MCP-host boundary. Cloudflare observability is sampled and is not a complete audit log.
|
|
101
107
|
|
|
@@ -215,6 +221,8 @@ After suspected credential or client compromise:
|
|
|
215
221
|
1. stop foreground and autostart daemons;
|
|
216
222
|
2. run `machine-mcp rotate-secrets`;
|
|
217
223
|
3. restart without broad flags and redeploy;
|
|
218
|
-
4. inspect Cloudflare account access, Worker configuration, local state/resource permissions, managed-job results, and service logs;
|
|
224
|
+
4. inspect Cloudflare account access, Worker configuration, local state/resource permissions, process-lock owners, managed-job results, and service logs;
|
|
219
225
|
5. cancel active managed jobs and remove compromised resource aliases;
|
|
220
226
|
6. remove the Worker and local state if continued remote access is unnecessary.
|
|
227
|
+
|
|
228
|
+
The detailed 0.12.0 audit record and residual operational limits are in [AUDIT.md](AUDIT.md).
|
package/docs/PRIVACY.md
CHANGED
|
@@ -10,7 +10,7 @@ Run:
|
|
|
10
10
|
npm run privacy:check
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
The check scans tracked and unignored new UTF-8 files and
|
|
13
|
+
The check scans tracked and unignored new UTF-8 files and relative names for generic/encrypted/algorithm-specific private-key headers, AWS/GitHub/GitLab/npm/Slack/Google/live-payment/API token forms, JWT-shaped bearer values, embedded-credential URLs, absolute user-home paths, non-example email/`user@host` identifiers, credential-shaped filenames, and locally configured private identifiers. A tracked `.npmrc` is parsed: non-secret repository settings such as `engine-strict=true` are allowed, while authentication/identity keys, environment interpolation, and embedded credentials fail closed. Publication-surface symbolic links are rejected rather than followed. Binary, invalid UTF-8, and files above the bounded scanner limit require explicit manual review instead of being silently skipped. Findings report only file, line, and rule; the matched value is never printed.
|
|
14
14
|
|
|
15
15
|
Maintain machine-specific names in an ignored owner-only file:
|
|
16
16
|
|
|
@@ -43,9 +43,11 @@ Before committing or publishing:
|
|
|
43
43
|
- use reserved example domains and neutral aliases;
|
|
44
44
|
- run `npm run privacy:check`, `npm run check`, and `npm pack --dry-run`;
|
|
45
45
|
- treat paths, host aliases, usernames, codenames, real browser URLs/page captures, application names tied to a user, and form data as private metadata even when they are not authentication secrets;
|
|
46
|
-
- keep browser pairing-state files and captured source/screenshots out of fixtures, documentation, support logs, and release assets
|
|
46
|
+
- keep browser pairing-state files and captured source/screenshots out of fixtures, documentation, support logs, and release assets;
|
|
47
|
+
- review any tracked `.npmrc` as configuration code and never commit authentication, registry identity, environment interpolation, cert/key paths, or credential-bearing URLs;
|
|
48
|
+
- inspect package modes and filenames as well as file contents—an empty `.env`, private-key filename, database, or log is still an inappropriate publication artifact.
|
|
47
49
|
|
|
48
|
-
The scanner is heuristic. It cannot identify every personal or organizational name, transformed value, image, archive, binary fixture, or data already present in Git history.
|
|
50
|
+
The scanner is heuristic. It cannot identify every personal or organizational name, split/transformed/encrypted value, image, archive, binary fixture, custom credential format, or data already present in Git history. Passing it is a gate, not proof that a publication contains no private data.
|
|
49
51
|
|
|
50
52
|
## Incident response
|
|
51
53
|
|
package/docs/RELEASING.md
CHANGED
|
@@ -22,7 +22,7 @@ The release invariant is:
|
|
|
22
22
|
|
|
23
23
|
2. Add the matching dated `CHANGELOG.md` section.
|
|
24
24
|
3. Run `npm run release-impact:check`, `npm run privacy:check`, `npm run check`, both dependency audits, `npm audit signatures`, and generate a CycloneDX `npm sbom`.
|
|
25
|
-
4. Inspect the complete diff and `npm pack --dry-run`, then commit and push all release changes to `main`.
|
|
25
|
+
4. Inspect the complete diff and `npm pack --dry-run`, including packaged file modes and every helper referenced by package scripts, then commit and push all release changes to `main`.
|
|
26
26
|
|
|
27
27
|
A privacy/security documentation correction is not “docs only” for release purposes. It requires a replacement npm version and, when appropriate, deprecation or unpublication of the affected version.
|
|
28
28
|
|