machine-bridge-mcp 3.0.0-beta.197 → 3.0.0-beta.199
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 +112 -10
- package/README.md +20 -5
- package/SECURITY.md +3 -3
- package/SUPPORT.md +2 -0
- package/bin/machine-mcp.mjs +2 -2
- package/browser-extension/broker-auth.js +5 -3
- package/browser-extension/browser-operation-contract.js +92 -0
- package/browser-extension/browser-operations.js +64 -139
- package/browser-extension/manifest.json +1 -2
- package/browser-extension/page-automation.js +50 -28
- package/browser-extension/pairing-bootstrap.js +6 -7
- package/browser-extension/pairing.js +14 -4
- package/browser-extension/service-worker.js +9 -13
- package/docs/AGENT_CONTEXT.md +3 -16
- package/docs/AUDIT.md +11 -6
- package/docs/CLIENTS.md +2 -11
- package/docs/COMPUTER_USE.md +9 -9
- package/docs/GETTING_STARTED.md +1 -10
- package/docs/LOCAL_AUTHORIZATION.md +3 -3
- package/docs/LOCAL_AUTOMATION.md +6 -2
- package/docs/LOGGING.md +16 -9
- package/docs/MANAGED_JOBS.md +8 -8
- package/docs/MULTI_ACCOUNT.md +2 -2
- package/docs/OPERATIONS.md +42 -24
- package/docs/OVERVIEW.md +1 -1
- package/docs/PRIVACY.md +5 -1
- package/docs/THREAT_MODEL.md +5 -3
- package/docs/TOOL_REFERENCE.md +4 -8
- package/docs/UPGRADING.md +20 -4
- package/mbm.cmd +3 -3
- package/native/macos/MachineBridgeTrustBroker.swift +52 -8
- package/package.json +7 -6
- package/scripts/consumer-package-security.mjs +80 -51
- package/scripts/github-release.mjs +4 -4
- package/scripts/hardened-npm-session.mjs +2 -0
- package/scripts/install-published-prerelease.mjs +1 -1
- package/scripts/prerelease-activation.mjs +22 -9
- package/scripts/privacy-check.mjs +99 -8
- package/scripts/promotion-digest.mjs +11 -7
- package/scripts/publish-npm.mjs +14 -16
- package/scripts/release-acceptance.mjs +12 -8
- package/scripts/release-candidate-manifest.mjs +12 -9
- package/scripts/release-channel.mjs +1 -1
- package/scripts/release-impact-check.mjs +2 -1
- package/scripts/release-oauth-canary-core.mjs +16 -9
- package/scripts/release-oauth-canary-evidence.mjs +10 -8
- package/scripts/release-soak.mjs +14 -11
- package/scripts/start-release-candidate.mjs +1 -1
- package/src/local/account-access.mjs +2 -1
- package/src/local/account-admin.mjs +22 -12
- package/src/local/agent-context-projection.mjs +4 -1
- package/src/local/agent-context.mjs +11 -10
- package/src/local/agent-skill-discovery.mjs +49 -14
- package/src/local/agent-text-file.mjs +38 -13
- package/src/local/app-automation.mjs +71 -120
- package/src/local/authority-context.mjs +10 -6
- package/src/local/browser-bridge.mjs +2 -9
- package/src/local/browser-broker-auth.mjs +9 -8
- package/src/local/browser-broker-server.mjs +8 -2
- package/src/local/browser-command.mjs +3 -1
- package/src/local/browser-computer-observation-service.mjs +10 -17
- package/src/local/browser-extension-identity.mjs +4 -2
- package/src/local/browser-operation-service.mjs +9 -14
- package/src/local/browser-pairing-grant.mjs +35 -35
- package/src/local/browser-pairing-http.mjs +2 -2
- package/src/local/browser-pairing-launch.mjs +4 -4
- package/src/local/browser-pairing-store.mjs +64 -66
- package/src/local/call-authority.mjs +14 -5
- package/src/local/cf-authentication.mjs +8 -7
- package/src/local/cli-account-admin.mjs +1 -2
- package/src/local/cli-local-admin.mjs +38 -12
- package/src/local/cli-policy.mjs +1 -1
- package/src/local/cli.mjs +18 -23
- package/src/local/computer-use-arguments.mjs +2 -6
- package/src/local/computer-use-observation-contract.mjs +53 -13
- package/src/local/computer-use-observation.mjs +19 -9
- package/src/local/computer-use.mjs +7 -4
- package/src/local/daemon-http-relay-connection.mjs +9 -6
- package/src/local/daemon-http-relay-request.mjs +8 -1
- package/src/local/daemon-http-relay-sequence.mjs +6 -4
- package/src/local/delegated-process-sandbox.mjs +28 -6
- package/src/local/device-identity.mjs +35 -22
- package/src/local/durable-process-spec.mjs +2 -6
- package/src/local/exclusive-publication-recovery.mjs +44 -1
- package/src/local/full-access-test.mjs +11 -5
- package/src/local/git-metadata-tree-safety.mjs +20 -20
- package/src/local/git-version.mjs +11 -0
- package/src/local/hardened-npm-download.mjs +2 -2
- package/src/local/hardened-npm-launcher.mjs +48 -0
- package/src/local/hardened-npm.mjs +9 -7
- package/src/local/job-runner.mjs +61 -47
- package/src/local/loopback-health.mjs +5 -0
- package/src/local/macos-trust-broker.mjs +30 -25
- package/src/local/managed-job-active-child.mjs +10 -8
- package/src/local/managed-job-dependencies.mjs +4 -4
- package/src/local/managed-job-listing.mjs +2 -2
- package/src/local/managed-job-lock.mjs +20 -17
- package/src/local/managed-job-output-redaction.mjs +21 -10
- package/src/local/managed-job-plan.mjs +4 -2
- package/src/local/managed-job-projection.mjs +8 -2
- package/src/local/managed-job-recovery-history.mjs +49 -0
- package/src/local/managed-job-relaunch.mjs +8 -7
- package/src/local/managed-job-retention-policy.mjs +5 -5
- package/src/local/managed-job-runner-claim.mjs +17 -12
- package/src/local/managed-job-runner-liveness.mjs +14 -5
- package/src/local/managed-job-runner.mjs +11 -11
- package/src/local/managed-job-state-validation.mjs +2 -0
- package/src/local/managed-job-terminal-maintenance.mjs +3 -2
- package/src/local/managed-job-terminal.mjs +4 -3
- package/src/local/managed-job-transient-recovery-capacity.mjs +7 -7
- package/src/local/managed-jobs.mjs +33 -22
- package/src/local/npm-audit-report.mjs +21 -0
- package/src/local/npm-environment.mjs +19 -25
- package/src/local/observability.mjs +4 -7
- package/src/local/operation-risk.mjs +12 -0
- package/src/local/owner-state-lock.mjs +4 -4
- package/src/local/process-contract.mjs +9 -0
- package/src/local/process-execution.mjs +14 -12
- package/src/local/process-identity.mjs +25 -8
- package/src/local/process-sessions.mjs +7 -4
- package/src/local/process-tracker.mjs +20 -8
- package/src/local/process-tree-ownership.mjs +1 -1
- package/src/local/relay-connection-support.mjs +2 -1
- package/src/local/relay-connection.mjs +1 -2
- package/src/local/remote-configuration.mjs +4 -2
- package/src/local/resource-admission.mjs +13 -6
- package/src/local/resource-lease-accounting.mjs +1 -1
- package/src/local/resource-operations.mjs +2 -2
- package/src/local/resource-process-ancestry.mjs +1 -1
- package/src/local/resource-request-contract.mjs +3 -2
- package/src/local/resource-staging-recovery.mjs +11 -8
- package/src/local/resource-transaction-lock.mjs +4 -4
- package/src/local/resource-waiters.mjs +8 -9
- package/src/local/runtime-activation.mjs +13 -8
- package/src/local/runtime-diagnostic-projection.mjs +1 -1
- package/src/local/runtime-package-identity.mjs +9 -8
- package/src/local/runtime-relay-connection-options.mjs +13 -6
- package/src/local/runtime.mjs +9 -4
- package/src/local/secure-file.mjs +4 -4
- package/src/local/security-audit-activity.mjs +22 -8
- package/src/local/security-audit-beta198-migration.mjs +50 -0
- package/src/local/security-audit-dispatch.mjs +23 -11
- package/src/local/security-audit-gate.mjs +38 -0
- package/src/local/security-audit-log.mjs +20 -16
- package/src/local/security-audit-state.mjs +57 -20
- package/src/local/security-audit-storage.mjs +21 -12
- package/src/local/service-convergence.mjs +1 -1
- package/src/local/service-environment.mjs +4 -4
- package/src/local/service-ownership.mjs +1 -1
- package/src/local/shell.mjs +2 -1
- package/src/local/ssh-key.mjs +15 -19
- package/src/local/state-inventory.mjs +4 -4
- package/src/local/state-root-owned-namespaces.mjs +0 -11
- package/src/local/state.mjs +38 -47
- package/src/local/stdio.mjs +2 -2
- package/src/local/tool-executor.mjs +23 -6
- package/src/local/windows-service.mjs +13 -7
- package/src/local/worker-deployment-fingerprint.mjs +45 -14
- package/src/local/worker-deployment.mjs +40 -18
- package/src/local/worker-health.mjs +7 -4
- package/src/local/worker-identity-contract.mjs +14 -0
- package/src/local/worker-secret-file.mjs +18 -6
- package/src/local/workspace-file-service.mjs +4 -4
- package/src/local/workspace-file-transaction.mjs +5 -5
- package/src/local/workspace-profile-migration.mjs +5 -4
- package/src/local/wrangler-toolchain/package-lock.json +118 -118
- package/src/local/wrangler-toolchain/package.json +3 -1
- package/src/local/wrangler-toolchain-installed-tree.mjs +42 -0
- package/src/local/wrangler-toolchain-verification.mjs +19 -33
- package/src/local/wrangler-toolchain.mjs +52 -45
- package/src/shared/activation-recovery.mjs +2 -2
- package/src/shared/admin-auth.mjs +6 -7
- package/src/shared/authority-revocation.mjs +8 -7
- package/src/shared/daemon-auth.mjs +6 -7
- package/src/shared/device-session-auth.mjs +8 -7
- package/src/shared/log-redaction.mjs +15 -2
- package/src/shared/sensitive-value-patterns.mjs +2 -2
- package/src/shared/server-metadata.json +3 -3
- package/src/shared/tool-argument-validation.d.mts +1 -1
- package/src/shared/tool-catalog.json +4 -8
- package/src/worker/access.ts +2 -1
- package/src/worker/account-admin.ts +7 -3
- package/src/worker/authority-revocations.ts +50 -16
- package/src/worker/daemon-auth.ts +8 -8
- package/src/worker/daemon-http-channel.ts +18 -9
- package/src/worker/daemon-http-controller.ts +24 -7
- package/src/worker/daemon-http-protocol.ts +1 -1
- package/src/worker/daemon-liveness.ts +9 -3
- package/src/worker/daemon-ready-dispatch.ts +2 -0
- package/src/worker/daemon-ready-waiters.ts +1 -1
- package/src/worker/daemon-registry.ts +1 -1
- package/src/worker/daemon-relay-diagnostics.ts +1 -0
- package/src/worker/daemon-socket-attachment.ts +1 -1
- package/src/worker/device-session-verifier.ts +11 -7
- package/src/worker/dpop.ts +11 -6
- package/src/worker/http.ts +5 -1
- package/src/worker/index.ts +42 -41
- package/src/worker/managed-job-hosted-authority.ts +3 -3
- package/src/worker/mcp-controller.ts +3 -3
- package/src/worker/mcp-job-monitor-store.ts +2 -2
- package/src/worker/mcp-job-monitor-ui.ts +5 -5
- package/src/worker/mcp-response-stream.ts +13 -14
- package/src/worker/{mcp-stale-schema-compat.ts → mcp-stale-schema-guidance.ts} +1 -1
- package/src/worker/mcp-subscription-capacity.ts +3 -3
- package/src/worker/oauth-client-admin.ts +2 -4
- package/src/worker/oauth-client-contract.ts +3 -4
- package/src/worker/oauth-controller.ts +33 -54
- package/src/worker/oauth-refresh-exchange.ts +13 -6
- package/src/worker/oauth-refresh-families.ts +26 -15
- package/src/worker/oauth-refresh-persistence.ts +6 -17
- package/src/worker/oauth-state.ts +18 -42
- package/src/worker/oauth-store-validation.ts +14 -10
- package/src/worker/oauth-token-issuance.ts +6 -9
- package/src/worker/oauth-tokens.ts +46 -7
- package/src/worker/observability.ts +5 -10
- package/src/worker/pending-call-capacity.ts +1 -1
- package/src/worker/worker-continuity-evidence.ts +5 -6
- package/src/worker/worker-entry.ts +1 -1
- package/src/worker/worker-rate-limit-key.ts +1 -1
- package/src/local/resource-legacy-staging-recovery.mjs +0 -71
package/CHANGELOG.md
CHANGED
|
@@ -1,16 +1,118 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## 3.0.0-beta.
|
|
3
|
+
## 3.0.0-beta.199 - 2026-10-08
|
|
4
4
|
|
|
5
|
-
-
|
|
6
|
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
5
|
+
- Resolve GHSA-wq5f-xc86-pv6w (CVE-2026-96889) by pinning Sharp 0.35.5 in both the source checkout and the isolated Cloudflare deployment toolchain, including their independent dependency locks and lifecycle-script allowlists.
|
|
6
|
+
- Require the installed private toolchain dependency tree and its integrity marker to match the patched Sharp version; reject vulnerable installed versions before deployment tooling is used.
|
|
7
|
+
- Raise the consumer-package Sharp minimum to the first patched version and add vulnerable-version and patched-version regression cases.
|
|
8
|
+
- Fail closed on malformed private toolchain npm versions, dependency-tree metadata and coerced package versions; avoid reflecting raw private npm diagnostics in errors.
|
|
9
|
+
- Preserve fail-closed npm audit, supply-chain verification and OpenSSF Scorecard review gates without adding a vulnerability exception.
|
|
10
|
+
- Remove the obsolete boolean `computer_act.include_post_screenshot` compatibility option; require `post_screenshot=auto|always|never`, reject obsolete inputs, and advance the tool schema generation.
|
|
11
|
+
- Replace destructive state-root removal advice on obsolete persisted schemas with preservation and supported upgrade/backup recovery guidance.
|
|
12
|
+
- Keep managed-job fatal operational diagnostics to a fixed error classification, avoiding arbitrary exception-message disclosure to logs.
|
|
13
|
+
- Extend that fixed-diagnostic boundary through runner startup before the job claim is confirmed, so missing or unreadable initial state cannot expose private paths, raw filesystem errors or exception stacks.
|
|
14
|
+
- Require exact JSON types for persisted process ownership, runner claims, active-child claims, resource transaction locks, leases and waiters; reject string/array coercion before liveness, recovery, cleanup, termination or critical-section decisions.
|
|
15
|
+
- Require exact string credentials, integer ports and string broker endpoints for current browser-pairing state; arrays and numeric-string ports no longer acquire pairing or authentication authority through JavaScript conversion.
|
|
16
|
+
- Remove the retired browser-pairing migration marker from the current persisted model and advance the pairing schema to version 3, while preserving one exact one-way beta.198 stable-state upgrade that retains both credentials and the selected port; pending beta.198 migrations and older, malformed, coercible or extra-field envelopes remain fail closed.
|
|
17
|
+
- Remove the redundant managed-job result `recovered` compatibility boolean; recovery outcome is represented only by the terminal status enum, and interrupted recovery never replays main steps.
|
|
18
|
+
- Require Git 2.50 or later for reachable-history privacy scanning instead of carrying an older `rev-list` output parser; fail before scanning when the trusted Git version is too old or malformed.
|
|
19
|
+
- Reject non-string OAuth grant types, authorization codes, PKCE verifiers, client identifiers, redirect URIs, resources, scopes and refresh tokens before credential lookup or consumption.
|
|
20
|
+
- Apply the Sharp 0.35.5 minimum independently to consumer CycloneDX validation as well as the installed dependency tree, including malformed and prerelease version rejection.
|
|
21
|
+
- Reject coercible legacy resource-staging publisher PID evidence instead of accepting strings, arrays or booleans as process identities; retain the staging files on malformed ownership.
|
|
22
|
+
- Refuse staging reclamation when the publisher PID encoded in a current-format filename exceeds safe integer bounds, rather than treating invalid process identity as permission to delete.
|
|
23
|
+
- Remove the retired version-2 capability-lease/release-task cleanup compatibility surface from current documentation and state-root removal; unsupported older state is preserved and rejected instead of interpreted by the current runtime.
|
|
24
|
+
- Reject non-string browser extension IDs, origins, and manifest public keys instead of allowing arrays or other values to acquire extension identity through JavaScript string coercion.
|
|
25
|
+
- Treat synchronous security-audit worker transport failures as terminal worker failures, settle all pending audit promises, mark health unavailable, and account for records that could not be persisted instead of leaving queued callers hanging.
|
|
26
|
+
- Stop interpreting retired schema-v1 Worker continuity evidence; unsupported persisted continuity schemas now fail closed to empty current-schema diagnostics instead of partially migrating historical counters.
|
|
27
|
+
- Reject non-string persisted daemon socket roles instead of allowing arrays or other values to pass role validation through JavaScript string coercion.
|
|
28
|
+
- Add a random per-daemon pseudonym correlation epoch to new security-audit events so incident review can identify runtime-key rotation boundaries without creating stable cross-restart principal or target identifiers.
|
|
29
|
+
- Remove the redundant browser-extension `tabs` permission; existing `<all_urls>` host authority already supplies tab metadata for scriptable pages, while restricted browser-internal pages now expose less metadata to the extension.
|
|
30
|
+
- Route npm publication cleanup warnings through the shared release diagnostic sanitizer so a successful publication cannot leak local home paths or credential-shaped values through best-effort cleanup messages.
|
|
31
|
+
- Mark browser-derived page text, accessibility labels, DOM attributes, and screenshots as untrusted data in remote MCP server instructions so webpage prompt injection is not presented as authorization or higher-precedence guidance.
|
|
32
|
+
- Repair the privacy regression fixture so Git-version boundary assertions use Node's strict assertion API instead of the test file's boolean-only helper, restoring coverage-gate execution of the privacy suite.
|
|
33
|
+
- Remove schema-v1 migration rationale from the always-injected MCP server instructions now that the runtime is current-schema-only; historical rationale remains in release history instead of consuming every client context.
|
|
34
|
+
- Reject Git version components outside JavaScript safe-integer bounds so malformed trusted-Git version output cannot satisfy the privacy-history minimum through numeric infinity coercion.
|
|
35
|
+
- Keep promotion-digest npm pack metadata type-exact by rejecting non-string paths and non-integer file modes instead of normalizing coercible values into release evidence.
|
|
36
|
+
- Replace stale fixed beta.1 commands in the release guide with an explicit next-version variable and a soak-confirmation command derived from the current package version, preventing copy/paste of an obsolete prerelease identifier.
|
|
37
|
+
- Reject non-string local account-admin lookup targets instead of allowing arrays or other values to select a privileged account through string coercion.
|
|
38
|
+
- Require persisted resource lease, waiter, transaction-lock, and newly bound child PIDs to be positive safe integers so precision-losing process identities fail as integrity errors instead of being silently reclaimed or bound.
|
|
39
|
+
- Apply the same safe-integer PID boundary to the shared owner-state lock primitive so malformed toolchain and maintenance ownership evidence cannot be reclaimed after precision loss.
|
|
40
|
+
- Preserve an in-flight browser pairing exchange after invalid final challenge, nonce, or proof attempts; the one-time grant is now consumed only after the final HMAC proof succeeds, preventing unauthenticated failures from deleting a legitimate pending exchange.
|
|
41
|
+
- Require service network/TLS environment values to remain native strings on capture and reload so malformed persisted arrays or numbers cannot be coerced into active proxy or certificate configuration.
|
|
42
|
+
- Make shared device-session and daemon-authentication transcript builders type-exact so arrays, numeric strings, or other coercible values cannot acquire signed protocol semantics if a future caller omits its own pre-validation.
|
|
43
|
+
- Keep process-tree ownership snapshots type-exact by accepting only positive safe-integer child PIDs, matching persisted active-child and lock ownership boundaries.
|
|
44
|
+
- Advance security-audit state to schema 3 while retaining one exact, one-way beta.198 schema-1 data migration: prior event objects and hashes remain unchanged under a schema-3 envelope with an explicit legacy-prefix count, while all newly appended events require current pseudonym, operation-correlation, and settlement fields.
|
|
45
|
+
- Split browser operation scalar/response-contract validation into a fixed preloaded extension module and lower the browser-operations responsibility budget, removing its near-limit 1900-line concentration without changing dispatch semantics.
|
|
46
|
+
- Stop reclaiming malformed daemon/startup/maintenance and managed-job lock files by age alone. Current lock publication is complete-before-visible, so malformed owner metadata is now retained as an integrity blocker; only structurally valid owners with provably stale process identity are automatically reclaimed.
|
|
47
|
+
- Keep browser pairing types exact after the URL parsing boundary: the service worker and HMAC bootstrap now require native integer ports, string grants, and numeric clocks instead of accepting coercible arrays or numeric strings.
|
|
48
|
+
- Gate remote non-read-only dispatch on durable security-audit health and a persisted `dispatch_intent`. Intent and terminal records share a runtime-private operation reference; the intent deliberately proves only that the authorized pre-dispatch boundary was reached, so a timeout cannot be misread as proof that the handler started.
|
|
49
|
+
- Make the audit pre-dispatch gate consume an explicit local side-effect classification rather than MCP annotation metadata, and regression-check public `readOnlyHint` values against that security classification so catalog drift cannot silently weaken the gate.
|
|
50
|
+
- Expose whether managed-job recovery attribution is complete without inventing beta.198 history: beta.199 recovery entries must form a contiguous suffix ending at the current attempt count, while prior-version attempt counts with no persisted reason/time remain explicitly `recovery_history_complete=false`.
|
|
51
|
+
- Keep daemon relay targets type-exact from persisted/runtime configuration through WebSocket and HTTPS fallback: non-string Worker URLs are rejected rather than being coerced into authenticated network origins, and malformed HTTP relay URLs use one fixed error classification.
|
|
52
|
+
- Apply the same exact-string Worker origin/name boundary to deployment health verification so malformed persisted arrays cannot be normalized into healthy Worker identity evidence.
|
|
53
|
+
- Keep current security-audit hashes, pseudonym epochs, timestamps, and numeric counters type-exact; arrays and numeric strings no longer become valid-looking audit evidence through regular-expression or numeric coercion.
|
|
54
|
+
- Tighten both current and beta.198 audit readers to the actual writer image: canonical ISO timestamps, bounded token/text forms, and non-negative safe-integer duration/byte counters. Hash-consistent values that no supported writer could emit are rejected as integrity failures.
|
|
55
|
+
- Reject precision-losing process IDs in resource ancestry sampling and lease accounting so malformed `ps`/PowerShell evidence cannot collapse distinct PIDs into one JavaScript number.
|
|
56
|
+
- Retire pre-beta.198 temporary Worker-secret filename compatibility. Cleanup now recognizes only the current PID + creation-time + process-start-time + 12-hex nonce shape; obsolete lookalike secret files are retained as explicit integrity blockers instead of being weakly attributed or silently ignored.
|
|
57
|
+
- Keep local device-root/session authentication primitives type-exact: public roots now pass shared canonical P-256 validation, authentication clocks require native finite numbers, and session-signing transcripts require native strings rather than coercible values.
|
|
58
|
+
- Preserve security-audit evidence types end to end: the daemon no longer coerces injected audit clocks before persistence, and recent-activity projection no longer normalizes string/array timestamps or byte counters into valid-looking evidence.
|
|
59
|
+
- Keep managed-job retention timestamps type-exact before destructive maintenance: malformed staged `created_at` values no longer gain expiry authority through string coercion, and terminal retention ordering only parses native timestamp strings.
|
|
60
|
+
- Make account-administration authentication transcripts and local request construction type-exact: origin/method/path/hash/key/nonce fields no longer accept coercible values, signed body bytes must already be a string, and display names are rejected rather than stringified.
|
|
61
|
+
- Require daemon candidate/readiness/liveness timestamps to be native canonical ISO strings before they can establish freshness or alarm deadlines; JavaScript-coercible arrays and alternate timestamp spellings no longer become liveness evidence.
|
|
62
|
+
- Document the existing `file:` browser-automation privacy boundary explicitly: when Chromium file-URL access is enabled, local file-backed tabs are data-bearing automation surfaces rather than ordinary harmless tab metadata.
|
|
63
|
+
- Add an explicit offline `machine-mcp browser reset` revocation path. It serializes through state maintenance, refuses active runtime/job ownership, rechecks that the loopback broker is stopped, and atomically rotates both broker HMAC credentials without exposing their replacements.
|
|
64
|
+
- Correct the browser-pairing storage model in the threat/automation docs: authoritative broker credentials remain in owner-only Machine Bridge state, while the extension stores its extension-token copy in Chromium local extension storage for restart persistence and may retain an unusable stale copy after local state reset.
|
|
65
|
+
- Keep the macOS Secure Enclave broker boundary type-exact: configured/provisioned broker paths and signing transcripts now require native strings, preventing coercible arrays or objects from acquiring trust-broker path or signature semantics.
|
|
66
|
+
- Redact browser pairing grants and managed-job recovery/control credentials by URL parameter name at the final logging boundary, including extension/runtime token parameters, while leaving harmless names such as `grant_type` intact.
|
|
67
|
+
- Keep browser pairing grant and launch clocks/ports type-exact and internally consistent: native safe integers are required, one wall-clock sample governs each bootstrap transition, and grant creation rejects timestamps that cannot produce the protocol's canonical 13-digit expiry.
|
|
68
|
+
- Tighten process-identity evidence to the current writer/probe image: snapshot PIDs must be safe integers, snapshot start times must already be numeric, and persisted owner timestamps must be canonical ISO strings rather than merely parseable dates.
|
|
69
|
+
- When browser history-state cleanup fails on the ephemeral pairing page, replace the current location with the token-free `/pair` URL before stopping; pairing still fails closed, but the short-lived grant no longer remains indefinitely in the visible fragment/history entry.
|
|
70
|
+
- Clear pairing bootstrap material from the content-script closure immediately after successful or ordinary failed pairing; retain it only for the explicit short-lived manual-repair branch that still needs the same grant.
|
|
71
|
+
- On POSIX, synchronize the security-audit parent directory after atomic state replacement before acknowledging a batch, so a pre-dispatch intent is not reported persisted solely because the temporary file contents reached storage before the rename metadata.
|
|
72
|
+
- Require the signed DPoP `htm` claim to match the normalized expected HTTP method exactly instead of uppercasing the proof claim, aligning the verifier with RFC 9449-style exact method binding and mature OAuth implementations.
|
|
73
|
+
- Clarify incident diagnostics so recent audit `pending/unknown` effect-settlement counts are not mistaken for a live termination queue; current process termination remains a separate runtime snapshot.
|
|
74
|
+
- Restore the local CLI browser-pairing launcher helper after compatibility-copy cleanup removed its imports while leaving the default dependency reference in place; CLI startup and `--version` no longer fail before argument dispatch.
|
|
75
|
+
- Update the page-automation VM regression harness for browser-global Web Crypto and automation API version 6, keeping the production document epoch on `crypto.randomUUID()` instead of weakening it for Node test contexts.
|
|
76
|
+
- Remove stale version-2 lease and schema-1 migration rationale from current architecture, operations, and release guidance now that those compatibility readers and surfaces are no longer supported.
|
|
77
|
+
- Expand the privacy publication scan beyond Git-visible files to every literal `package.json#files` tree, closing a gap where Git-ignored files such as `src/local/*.local` were still included by `npm pack` but escaped the privacy gate.
|
|
78
|
+
- Require canonical current account roles in every persisted OAuth authority record and remove the online bad-role repair branch; malformed or non-canonical role state now fails the current-schema boundary instead of being rewritten at runtime.
|
|
79
|
+
- Remove the unreachable local-admin external-launch helper and its process-spawn/browser-launch dependencies, leaving the current pairing launch path owned only by the browser pairing service.
|
|
80
|
+
- Reject prototype-shaped or otherwise malformed account IDs before indexing the persisted account table, and require runtime tool-handler dispatch to use own properties only.
|
|
81
|
+
- Extend the publication privacy gate across npm's implicit README/LICENSE/package/main/bin/man surfaces, closing a second gap where a Git-ignored root README could still be auto-included by `npm pack`.
|
|
82
|
+
- Document the residual same-user ancestor-directory race for ordinary workspace file mutations instead of overstating path canonicalization as an atomic cross-platform directory-handle boundary.
|
|
83
|
+
- Document that the owner-only autostart network snapshot can persist embedded proxy authentication and private certificate paths even though those values are excluded from status output and logs.
|
|
84
|
+
- Reject malformed hosted managed-job dependency IDs before capability verification instead of string-coercing array or other non-string values and deferring rejection to the daemon plan parser.
|
|
85
|
+
- Keep OAuth, admin, and daemon edge throttling bound to the caller network identity even when an attacker adds arbitrary Authorization headers; credential-scoped rate-limit buckets are now reserved for the MCP endpoint that actually authenticates with those credentials.
|
|
86
|
+
- Clarify the local-resource confidentiality boundary: browser/application mutation results do not echo injected resource content, while the destination receives it and later explicit value/source/screenshot observations may expose content the destination now holds.
|
|
87
|
+
- Keep security-audit evidence type-exact: malformed non-string target hashes, principal identifiers, roles, tools, outcomes, and risk categories now fall back to null/unknown evidence instead of being string-coerced into valid-looking audit records.
|
|
88
|
+
- Enforce exact string types for browser pairing grants, challenges, proofs, and credentials, and require an integer pairing port instead of accepting equivalent values through JavaScript coercion.
|
|
89
|
+
- Enforce exact string types for resource-request family and contention identities so malformed persisted/request metadata cannot cross the coordinator contract through JavaScript coercion.
|
|
90
|
+
- Require exact string resource names and resource-file paths in managed-job planning, keeping resource aliases and persisted plan evidence type-exact with the rest of the job schema.
|
|
91
|
+
- Keep MCP subscription and pending-read per-account capacity keys type-exact instead of normalizing non-string runtime values into account identities.
|
|
92
|
+
- Correct multi-account protocol documentation so removed initialization-era requests are described as rejected, not as a compatibility path.
|
|
93
|
+
- Validate relay account/client/family identity again at the in-flight call attribution boundary instead of coercing malformed principals into revocation metadata.
|
|
94
|
+
- Reject unknown in-flight principal kinds instead of misattributing them as local calls, preserving fail-closed revocation and interruption attribution.
|
|
95
|
+
- Persist at most three privacy-bounded managed-job recovery attribution records with fixed interruption reasons and exact timestamps, expose them with the strict recovery-attempt count, and reject coercible or malformed recovery metadata instead of using it to drive automatic relaunch.
|
|
96
|
+
- Require exact current Worker deployment identity types and formats for Worker names, OAuth token-version secrets, historical Worker names, and deployment fingerprints; arrays and other coercible values no longer become valid deployment identity through string conversion.
|
|
97
|
+
- Require runtime package entry, expected package identity, platform selector, and packaged manifest version to have their exact string types before service activation provenance is accepted; malformed manifest arrays can no longer stringify into a valid release version.
|
|
98
|
+
- Rename the fail-closed cached-schema helper from “compatibility” to “guidance” to make explicit that stale host schemas receive refresh instructions but never regain retired execution semantics.
|
|
99
|
+
- Remove the unused asynchronous `auditWarning` request-context mutation; audit persistence failures remain reported through the bounded security-audit failure reporter instead of writing dead state after a response may already have settled.
|
|
100
|
+
- Remove documentation and result-shape accommodation for older browser extensions: the current handshake already requires the exact packaged extension and `computer_observation_v1`, and missing tab-metadata verification evidence now projects as unverified rather than a legacy tri-state.
|
|
101
|
+
- Remove the unwritten legacy `operation-authorization.lock` scan/migration path and parse the current `security-audit.lock` with its actual owner-state contract, restoring live PID attribution in state inventory.
|
|
102
|
+
- Make the current OAuth store contract genuinely current-only: reject one-character legacy account names and clients missing the current DCR registration identity/revision instead of keeping them authorization-readable, and remove stale-registration status from the admin projection.
|
|
103
|
+
- Fail a managed-job step immediately when a still-live spawned child has no verifiable process-start identity; the runner terminates that child instead of allowing an active claim that crash recovery could only classify as ambiguous.
|
|
104
|
+
- Extend private Wrangler toolchain reuse verification to the executable install-script dependencies `esbuild` and `workerd`, and bump the audit marker schema so existing cached installs are revalidated under the stronger contract.
|
|
105
|
+
- Remove the obsolete browser setup note for migrating older local-candidate extension paths; current setup documents only the stable unpacked-extension path.
|
|
106
|
+
- Split managed-job recovery history accounting out of general state validation so recovery attribution has a dedicated bounded module instead of expanding the core status validator beyond its responsibility boundary.
|
|
107
|
+
- Reject multiply linked package files while computing the promotion digest so an alternate hard-link path cannot mutate candidate content behind the verified package path.
|
|
108
|
+
- Centralize Worker-name and OAuth token-version validation in one current identity contract instead of duplicating security-sensitive regular expressions across state, deployment fingerprinting, health checks, and state inventory.
|
|
109
|
+
- Retain temporary Worker-secret files when filename ownership PID/timestamp evidence exceeds safe-integer bounds instead of treating malformed process identity as reclaimable stale state; legacy secret cleanup remains only to remove credential residue safely.
|
|
110
|
+
- Validate tool arguments after tool/role authority gating but before relay operation risk/path authorization, so malformed remote inputs cannot trigger local path classification or other operation-level inspection before schema rejection.
|
|
111
|
+
- Require hardened-npm artifact descriptors and explicit proxy/NO_PROXY overrides to use native string/integer types, preventing coercible arrays or numeric strings from becoming supply-chain identity or network-routing configuration.
|
|
112
|
+
- Extend managed-job resource-output defense-in-depth redaction to exact Base64URL encodings, while documenting that arbitrary transformed or application-specific representations still require `capture_output: "discard"` when credentials may be echoed.
|
|
113
|
+
- Record a privacy-safe error class when a successfully delivered transient-process result cannot clear its stronger pending-recovery marker, preserving the user result while making temporary quota/retention over-protection diagnosable.
|
|
114
|
+
- Keep runtime shutdown on the ownership-checked process-tree supervisor: forced drain now uses zero-additional-grace supervised escalation, preserves already-pending ownership checks, and waits for retained descendant-escalation settlement instead of replacing it with an unverified direct group `SIGKILL`.
|
|
13
115
|
|
|
14
116
|
## Historical releases
|
|
15
117
|
|
|
16
|
-
Historical prerelease entries remain available from Git tags and repository
|
|
118
|
+
Historical prerelease entries remain available from Git tags and repository history. From a source checkout, inspect the changelog of a prior revision with `git show <revision>:CHANGELOG.md`. This current file intentionally describes only the active candidate and the history location.
|
package/README.md
CHANGED
|
@@ -25,7 +25,7 @@ Support boundaries are defined in [SUPPORT.md](SUPPORT.md). Contribution, conduc
|
|
|
25
25
|
- bounded file, patch, Git, process, diagnostic, application, browser, and managed-job tools;
|
|
26
26
|
- account roles whose authority is intersected with the connected daemon policy;
|
|
27
27
|
- root-certified ephemeral daemon sessions, trusted OAuth client binding, refresh-family ownership, and non-escalatable account roles;
|
|
28
|
-
- structured, privacy-conscious lifecycle events, a worker-thread-isolated
|
|
28
|
+
- structured, privacy-conscious lifecycle events, a worker-thread-isolated self-verifying audit hash chain with documented same-user rewrite limits, and stable error codes;
|
|
29
29
|
- control-plane resilience through end-to-end reserved diagnostic capacity, event-loop-aware relay liveness, and explicit draining-process accounting;
|
|
30
30
|
- fail-closed state, lock, release, package, and supply-chain checks.
|
|
31
31
|
|
|
@@ -176,7 +176,13 @@ The shared source of truth is `src/shared/policy-contract.json`. The generated m
|
|
|
176
176
|
|
|
177
177
|
For routine remote health checks, prefer `server_info` with `detail: "summary"`; the empty/default call remains full diagnostics and, for owner/full callers, includes a privacy-bounded durable continuity summary that survives Worker isolate replacement without retaining identities, call IDs, tool arguments, results, endpoints, or close reasons. For routine workspace inventory, `project_overview` also accepts `detail: "summary"`; it preserves policy/tool counts and top-level names/types without repeating exact tool arrays, account identity, routing fingerprints, or per-entry paths/sizes. Its empty/default call likewise remains full for compatibility. For remote calls, `server_info.authorization.effective_policy` and, when exact membership is needed, the full projection's `effective_tools` are authoritative. Daemon policy and tools describe only the local capability ceiling before account-role and host-side filtering.
|
|
178
178
|
|
|
179
|
-
`tools/list` is the authenticated account's
|
|
179
|
+
`tools/list` is the authenticated account's discovery catalog. Both `server/discover` and `tools/list` advertise `ttlMs=0`, and every host-visible tool description carries `Tool schema generation N`. Remote discovery supports `tools.listChanged=true`: `subscriptions/listen` acknowledges `toolsListChanged`, emits a level-trigger `notifications/tools/list_changed`, and stays open until cancellation or its bounded lease expires. Clients re-fetch the catalog; the server cannot prove which generation a host cached. Discovery does not grant authority: calls still require the current account/ready-daemon intersection.
|
|
180
|
+
|
|
181
|
+
WebSocket is preferred. An actually dispatched Ping has a ten-second Pong deadline, followed by one independent fifteen-second application-confirmation window. A protocol Pong or explicit application `pong` during that second stage preserves WSS. Local event-loop stalls use a separate recovery path. WSS connect attempts have a thirty-second outer budget. Healthy WSS keeps signed HTTPS fallback stopped; suspicion permits standby prewarming, and actual loss permits takeover of that exact generation. The daemon sends `resume_calls_ack.missing_ids` only after replacement readiness and only with fail-closed proof that absent ownership means non-execution. A possibly executed call is never automatically replayed. See [architecture](https://github.com/YuLeiFuYun/machine-bridge-mcp/blob/main/docs/ARCHITECTURE.md#daemon-reconnect-and-replacement) for transport sequencing, deadlines, and diagnostics.
|
|
182
|
+
|
|
183
|
+
Planned shutdown sends `daemon_draining` before relay close. An interrupted `read_job` returns `recovery.mode=read_same_job`; preserve its `job_id` and `recovery_key` and read that same job after reconnect. Replacement does not transfer ordinary executing calls to a new daemon.
|
|
184
|
+
|
|
185
|
+
Hosted synchronous calls otherwise retain their ordinary 20-second execution plus separate five-second Worker settlement margin. Configurable browser/application tools use 20-second defaults; compound `computer_observe` / `computer_act` retain 30-second defaults, with a 45-second explicit maximum. Durable process acceptance and continuation have separate budgets below. These limits do not establish external host receipt or a whole-assistant-turn deadline.
|
|
180
186
|
|
|
181
187
|
`full` is the daemon capability ceiling. An authenticated owner may exercise it without per-operation approval IDs. Delegated reviewer, editor, and operator accounts remain inside immutable role ceilings; out-of-role operations are denied rather than converted into a temporary elevation workflow. Process sessions, retained output, and managed jobs are additionally bound to account, client, and refresh-token family. See [local authorization](docs/LOCAL_AUTHORIZATION.md).
|
|
182
188
|
|
|
@@ -197,7 +203,15 @@ For stateful GUI trajectories, owner/full callers can use the higher-level `comp
|
|
|
197
203
|
|
|
198
204
|
## Durable work and local resources
|
|
199
205
|
|
|
200
|
-
Remote request-owned foreground work uses the hosted reply-safe budgets described above; configurable browser/application calls may explicitly request at most 45 seconds, while remote `exec_command`, `run_process`, and `run_local_command` are durable one-step jobs with a 10-second acceptance envelope and an independent 1–600-second child execution budget after admission. A continuous process that legitimately needs more than 600 seconds must use `start_job`: managed-job main/finally steps default to 600 seconds and may explicitly request up to 21,600 seconds (six hours), with resource admission occurring before that execution timer begins. The Worker retains separate settlement ownership for five additional seconds, but neither that margin nor its internal stream metrics prove that an external MCP host consumed the terminal frame. Keep unrelated mutations and validation independently terminal, but batch one coherent non-interactive command sequence into a repository umbrella command or multi-step `start_job` instead of creating one host-visible one-step job per tiny probe. A timeout is a protocol result, not proof that descendant cleanup has already completed; a remote owner can inspect `diagnose_runtime.runtime.processes`, while local stdio exposes `server_info.runtime.processes`. Non-owner accounts receive authority-scoped readiness rather than machine-wide process activity. Remote process sessions are for interactive stdin or incremental output, not a substitute for ordinary durable work: hosted `read_process` reports `status_polling_mode=paced_followup`, caps the actual output/exit blocking wait at one second, and paces a repeated would-block read inside the fifteen-second cooldown within that same MCP call until output/exit or the cooldown boundary instead of returning a rapid running checkpoint. When the current task needs more output or terminal state, the same session may be read again in the same assistant response without busy-looping. Non-interactive work should use durable `run_process`/`read_job`; multi-step, cleanup-sensitive, or daemon-restart-surviving workflows should use managed jobs, which persist ordered argv steps and `finally_steps` under owner-only local state and continue across an MCP disconnect. Hosted durable acceptance returns `job_id`, `recovery_key`, and `control_key`: preserve them together, use the read capability for `read_job` and `depends_on`, and use the control capability for `cancel_job`; a bare job ID is not remote recovery authority. On an MCP Apps-capable host, an active `start_job` may report `ui_monitor_candidate=true`, `ui_monitor_render_tool=render_job_monitor`, and a fresh random `ui_monitor_id`, but it remains a data/execution tool and carries no UI template. **Job polling ownership is not task ownership:** if remaining same-response work needs that job's terminal result, do not render the monitor merely to shorten the response; preserve `job_id` + `recovery_key` and continue bounded server-paced `read_job` calls. Render the monitor only when the current task can deliberately transfer that job's status polling without needing its terminal result for remaining same-response work. When intentionally rendering, preserve the start-result ID, call `render_job_monitor` once with the exact accepted `job_id` + `recovery_key` + `ui_monitor_id`, and do not depend on the render result being planner-visible. Then issue one model-visible `read_job` with the same three values. A matching claimed View on an ordinary job reports `ui_monitor_claimed=true`, `status_polling_mode=ui_monitor`, `host_turn_handoff_recommended=false`, and `follow_up_read_required=false`; those fields suppress duplicate model polling for that job only and do not require ending unrelated or still-actionable assistant work. The sole host-turn exception is an explicitly persisted `continuation_mode=task_supervisor`: only when that durable job's own reviewed plan already encapsulates all remaining noninteractive task work may a later active claimed read report `host_turn_handoff_recommended=true`. Machine Bridge can validate the persisted continuation declaration, active job state, and current monitor claim, but it cannot infer conversation-level task completeness; that completeness remains an owner/caller semantic assertion. That handoff is owned by the durable supervisor, not by the Job Monitor, and grants the View no execution authority. The monitor then uses app-only `read_job_monitor` for fixed 40-second status waits instead of emitting background model-visible `read_job` events. Each app-origin status call has a 60-second response watchdog; missing/transport-failed and explicitly retryable status responses receive at most six automatic retries with 1/2/4/8/15/30-second backoff. Exhausted or non-retryable failures pause for inspection, and no retry resubmits the durable job. An older View claim cannot transfer a newer turn because its monitor ID differs. Unsupported hosts, or hosts that can render MCP Apps but cannot proxy app-origin server tools, retain the existing contract: active relay-origin `read_job` reports `status_polling_mode=bounded_followup` and `host_turn_handoff_recommended=false`. Its hosted default is a 40-second server-side long-poll, so an unchanged long job occupies one bounded live MCP response rather than forcing rapid host-side checkpoints. Terminal settlement returns on the next bounded five-second internal poll; nonterminal status/phase/dependency progress is coalesced for at least 30 seconds by default, and `current_step`-only churn does not wake the hosted call. `wait_ms=0` is available only when an immediate checkpoint is actually wanted, and public hosted `wait_ms` is capped at 60 seconds; longer work continues through another paced read of the same `job_id` plus its preserved `recovery_key`. At the default, a synthetic 100-minute unchanged job has an anti-amplification ceiling of 150 status reads, while continuously changing nonterminal progress is separately bounded by the 30-second coalescing floor; those are density estimates rather than proof of aggregate same-response host lifetime. A known job may be followed through paced same-response `read_job` calls while those calls continue to be accepted and the task still needs the result; after an actual host/tool boundary, later recovery must continue from the same `job_id`. Completed one-step process carriers are lower-priority terminal retention than explicit managed jobs, so removable helper history is reclaimed first under the 512-state durable cap. A hosted helper whose current response still requires `read_job` continuation is non-evictable during the fixed 24-hour undelivered-result grace because Machine Bridge explicitly told the caller to follow that durable handle. If the 512-state store is full and every retained slot is non-evictable, the existing typed retryable `limit_exceeded` backpressure rejects the incoming job instead of deleting promised recovery evidence. Once Machine Bridge produces a terminal hosted response for the helper—during initial settlement or a later `read_job`—the private marker drops to the shorter thirty-minute bounded newest-16 transient delivery reserve because only outer response-delivery uncertainty remains; an undelivered marker older than 24 hours likewise stops hard protection and becomes ordinary eviction material. For relay non-owner one-step process carriers, acceptance additionally caps unresolved pending recovery at 16 records per account ID plus account version across all OAuth clients and refresh families; changing client/family does not reset that fairness bound. The check is serialized by the same root capacity transaction before new status publication or process launch and uses retained job state rather than a separate counter. A securely valid persisted same-key job can still be recovered at quota because it adds no reservation, but an empty/statusless deterministic directory is only an incomplete publication and must pass quota again. Hosted terminal delivery or the existing 24-hour terminal expiry releases the account slot. Owner/local work is not subject to this account quota, and the hosted inventory does not expose raw account-quota counts or principal identifiers. The daemon/local `list_jobs.jobs` inventory keeps a 50-record durable-first primary window and `recent_process_recovery` remains capped at 16 additional authority-visible handles for local administration. The hosted Worker projection deliberately removes those job IDs/names/handles and exposes aggregate retained/capacity/activity state only, so one hosted conversation cannot discover another workflow's durable handle through shared owner inventory. Hosted recovery therefore depends on the capability returned by the original acceptance; legacy or lost-capability jobs require local CLI/stdio administration. Owner/local `capacity` diagnostics expose only coarse `durable_terminal` and `transient_terminal` counts; this improves recovery visibility without pretending that Worker acknowledgement proves an external host rendered the final assistant message. Long cross-job workflows can declare `depends_on`: the dependent job remains pre-execution `queued/dependency_wait` without spawning its main child until all upstream jobs succeed, and an upstream failure settles `dependency_failed` instead of leaving a file-poll loop waiting for an artifact that can never appear. Active/staged dependency plans pin referenced retained results until the dependency-bearing plan is terminal. A valid `job_id` that is no longer retained returns typed `not_found`; that absence is not proof that its underlying side effect never executed. `list_jobs` remains inventory rather than a substitute polling loop, and `server_info`/`diagnose_runtime` remain diagnostic surfaces rather than alternate wait channels. Elapsed minutes are not evidence that an external host deadline is near; return the durable recovery identifier for a later turn only after an actual host/tool boundary is observed, external input or authorization is required, or the user explicitly requested a checkpoint.
|
|
206
|
+
Remote `exec_command`, `run_process`, and `run_local_command` require a caller-held `idempotency_key` and become principal-bound one-step jobs before execution. Their acceptance envelope is ten seconds; child execution is independently bounded to 1–600 seconds after resource admission. Use `start_job` for a continuous command needing more time: main/finally steps default to 600 seconds and may request up to 21,600 seconds (six hours). Batch coherent non-interactive work into one plan or repository umbrella command. Process sessions serve interactive stdin/incremental output and retain daemon-lifetime ownership; hosted `read_process` reports `status_polling_mode=paced_followup`, with a one-second actual blocking cap and a fifteen-second would-block cooldown.
|
|
207
|
+
|
|
208
|
+
Preserve `job_id`, `recovery_key`, and `control_key` from acceptance. Reads/dependencies require the recovery capability; cancellation requires the control capability. A bare ID is not hosted authority. When terminal state is needed, continue bounded same-response `read_job` follow-up: active reads report `status_polling_mode=bounded_followup` and `host_turn_handoff_recommended=false`. The default long-poll is 40 seconds, terminal checks are bounded to five-second intervals, and nonterminal progress is coalesced for at least 30 seconds; step-name changes alone do not wake a hosted read. Explicit `wait_ms=0` requests a checkpoint and the public maximum is 60 seconds. Do not busy-loop or substitute diagnostic calls for known-job reads. Elapsed minutes do not justify handoff; after an actual host/tool boundary, recover the same job instead of repeating its side effect.
|
|
209
|
+
|
|
210
|
+
An active `start_job` may offer a Job Monitor on an MCP Apps-capable host. Polling ownership is separate from task ownership: if remaining work needs the result, keep following `read_job`. Otherwise render once with the accepted `job_id`, `recovery_key`, and `ui_monitor_id`, then read with those same values to verify the View claim. A claimed ordinary job reports `status_polling_mode=ui_monitor`, `follow_up_read_required=false`, and `host_turn_handoff_recommended=false`. Only an explicit `continuation_mode=task_supervisor` plan that already owns all remaining noninteractive work may recommend handoff; completeness remains an owner/caller semantic assertion. Monitor failures never resubmit execution.
|
|
211
|
+
|
|
212
|
+
The retained-state cap is 512. A hosted helper awaiting its promised terminal response is non-evictable during the fixed 24-hour undelivered-result grace; a store with no removable slot rejects new work with retryable `limit_exceeded`. After terminal delivery, helpers have a bounded newest-16/thirty-minute reserve and lower eviction priority than explicit durable history. Delegated one-step pending recovery is capped at 16 records per account ID plus account version across all OAuth clients and refresh families, checked before new state or process launch; a valid same-key replay adds no slot, while a statusless directory must pass admission again. Local CLI/stdio retains detailed inventory; hosted `list_jobs` exposes aggregates, so lost capabilities require local administration. A retained-record `not_found` does not prove non-execution.
|
|
213
|
+
|
|
214
|
+
Jobs can declare `depends_on`: the dependent remains `queued/dependency_wait` before main execution, upstream failure yields `dependency_failed`, and active/staged dependencies protect referenced results. Register secrets as local resource aliases and put idempotent cleanup in `finally_steps`. Timeout or cancellation alone does not prove process-tree settlement. See [managed jobs](docs/MANAGED_JOBS.md) for examples, retention, recovery, and privacy boundaries.
|
|
201
215
|
|
|
202
216
|
The repository-tracked `workflow-bundle.json` is the project-native lifecycle authority for local readiness. Its `project-native:npm:check:fast` and `project-native:npm:check:full` stages must remain `sandbox: required`; a machine-local or untracked lifecycle file must not silently lower high-risk containment. Release architecture checks enforce both Git tracking and those required Seatbelt modes, so the job-local continuation contract above is validated under the same repository-owned verification authority used for the candidate.
|
|
203
217
|
|
|
@@ -207,7 +221,7 @@ On macOS, authorized remote activity uses a bounded idle-sleep assertion so ordi
|
|
|
207
221
|
|
|
208
222
|
When `run_process` or `exec_command` returns a child exit code and bounded stdout/stderr, the local process did run. For nested tools such as `ssh`, a remote forced-command usage message or command allowlist is therefore evidence from the target-side authorization layer, not evidence that Machine Bridge blocked process execution. Diagnose and change the narrowest failing layer instead of widening the `full` profile, which already removes Machine Bridge's own shell and path restrictions.
|
|
209
223
|
|
|
210
|
-
Credentials and files can be registered by alias
|
|
224
|
+
Credentials and files can be registered by alias so the injection operation does not place their contents in MCP arguments or echo them in its result:
|
|
211
225
|
|
|
212
226
|
```sh
|
|
213
227
|
machine-mcp resource add maintenance-key ~/.ssh/example_maintenance_ed25519
|
|
@@ -216,6 +230,7 @@ machine-mcp job submit plan.json
|
|
|
216
230
|
```
|
|
217
231
|
|
|
218
232
|
See [docs/MANAGED_JOBS.md](docs/MANAGED_JOBS.md) for integrity checks, recovery, redaction, cleanup, and residual risks.
|
|
233
|
+
For browser or application injection, the destination receives the content by design and can copy, render, or transmit it; later observations of that destination can therefore expose content it now holds.
|
|
219
234
|
|
|
220
235
|
## Operations
|
|
221
236
|
|
|
@@ -229,7 +244,7 @@ machine-mcp workspace show|set|reset
|
|
|
229
244
|
machine-mcp idle-sleep show|set MODE
|
|
230
245
|
machine-mcp service status|install|start|stop|uninstall
|
|
231
246
|
machine-mcp account list|clients|revoke-client|add|role|enable|disable|rotate-password|remove
|
|
232
|
-
machine-mcp browser status|setup|pair|path
|
|
247
|
+
machine-mcp browser status|setup|pair|reset|path
|
|
233
248
|
machine-mcp resource add|list|check|remove
|
|
234
249
|
machine-mcp job submit|inspect|list|read|cancel
|
|
235
250
|
machine-mcp rotate-secrets
|
package/SECURITY.md
CHANGED
|
@@ -71,7 +71,7 @@ The roles are:
|
|
|
71
71
|
- `operator`: workspace-confined mutation and direct process execution;
|
|
72
72
|
- `owner`: complete bridge authority within the daemon policy ceiling.
|
|
73
73
|
|
|
74
|
-
No approval ID, refresh token, reconnect, client registration
|
|
74
|
+
No approval ID, refresh token, reconnect, or client registration can expand a role. Out-of-role operations fail with `authorization_denied`.
|
|
75
75
|
|
|
76
76
|
The Worker filters the stable discovery catalog by account role. Discovery is not authorization: each call is separately intersected with the current end-to-end-ready daemon capability ceiling, and the local runtime independently recomputes the role and policy boundary before dispatch. Account disablement, role change, password rotation, account removal, client revocation, token-version rotation, and refresh-family replay invalidate the appropriate credentials.
|
|
77
77
|
|
|
@@ -113,7 +113,7 @@ Remote daemon authentication uses a long-term device root and a root-certified e
|
|
|
113
113
|
|
|
114
114
|
The default provider on every platform, including macOS, is an owner-only portable P-256 root. Its private JWK is exportable by the daemon OS user and is reported as such in `server_info`. The root signs a 24-hour ephemeral session certificate at daemon startup; the ephemeral private key remains in memory and signs WebSocket preflight, Worker challenge, reconnect, and account-administration requests. Portable startup does not access Keychain and does not request user presence.
|
|
115
115
|
|
|
116
|
-
macOS can opt into a non-exportable Secure Enclave root only through `MBM_MACOS_TRUST_BROKER`. The configured executable must be an app-like broker signed by an Apple development or distribution identity with provisioning-profile-validated data-protection Keychain entitlements. Machine Bridge validates its canonical path, file mutability, strict code signature, signing identifier, Team ID, protocol responses, and a real create/delete Secure Enclave key probe before enrollment. The broker identity remains bound to the root and is revalidated before later use.
|
|
116
|
+
macOS can opt into a non-exportable Secure Enclave root only through `MBM_MACOS_TRUST_BROKER`. The configured executable must be an app-like broker signed by an Apple development or distribution identity with provisioning-profile-validated data-protection Keychain entitlements. Machine Bridge validates its canonical path, file mutability, strict code signature, signing identifier, Team ID, protocol responses, and a real create/delete Secure Enclave key probe before enrollment. The broker identity remains bound to the root and is revalidated before later use. The native broker verifies the returned key's Secure Enclave token, private-key class, EC type, and 256-bit size before use or export. Only an explicit key-not-found result permits creation; other lookup failures preserve existing state. Deletion binds the validated key reference, and signing input is bounded before Keychain access.
|
|
117
117
|
|
|
118
118
|
The prompt frequency is therefore:
|
|
119
119
|
|
|
@@ -189,7 +189,7 @@ After trusted input dispatch begins, an ambiguous failure is reported as unknown
|
|
|
189
189
|
|
|
190
190
|
Computer Use screenshots share the ordinary MCP result-size boundary. When an image would make an observation result too large, the image is omitted before the snapshot ID is published and pixel-action authority is disabled. When a post-action image would overflow the same boundary, it is removed from the returned/stored post snapshot while the already-established mutation settlement and bounded continuation handle are preserved; the result never turns that dispatched action into a generic retryable size failure. Semantic state may still be returned. The experimental macOS background visual backend is disabled unless explicitly configured and successfully probed; Accessibility, Automation, Screen Recording, and the operating system remain independent enforcement boundaries.
|
|
191
191
|
|
|
192
|
-
Local resources may be injected without
|
|
192
|
+
Local resources may be injected without placing their bytes in the mutating MCP arguments or echoing them in that mutation result, but the destination page or application still receives them. Once delivered, the destination can copy, render, or transmit the value; a later explicit value inspection, screenshot, page source read, or other observation may therefore expose content the destination now holds. In particular, raw serialized HTML may contain hidden bootstrap/session/account/authentication values that are not visible in the rendered page; `browser_get_source` therefore should be used only when raw markup is required, and `browser_inspect_page` should normally keep `include_values=false`.
|
|
193
193
|
|
|
194
194
|
## Network egress boundaries
|
|
195
195
|
|
package/SUPPORT.md
CHANGED
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
The supported runtime is the pinned Node.js 26 and npm 12 baseline on current GitHub-hosted Linux, macOS, and Windows environments. Older Node/npm releases and obsolete MCP protocol versions are not compatibility targets.
|
|
6
6
|
|
|
7
|
+
Source-release privacy tooling requires Git 2.50 or later. The reachable-history scanner intentionally relies on the machine-parsable NUL-delimited `git rev-list` object format introduced in Git 2.50 and does not carry an older-output compatibility parser.
|
|
8
|
+
|
|
7
9
|
The project supports the current package version and a direct upgrade from the immediately preceding published version when both use the current local state and policy schemas. See [Upgrading](docs/UPGRADING.md).
|
|
8
10
|
|
|
9
11
|
## Where to ask
|
package/bin/machine-mcp.mjs
CHANGED
|
@@ -4,8 +4,8 @@ import { sanitizeLogText } from "../src/local/log.mjs";
|
|
|
4
4
|
|
|
5
5
|
main().catch(error => {
|
|
6
6
|
const message = process.env.MBM_DEBUG === "1"
|
|
7
|
-
?
|
|
7
|
+
? (error?.stack || error?.message || String(error))
|
|
8
8
|
: (error?.message || String(error));
|
|
9
|
-
console.error(`Error: ${message}`);
|
|
9
|
+
console.error(`Error: ${sanitizeLogText(message)}`);
|
|
10
10
|
process.exitCode = 1;
|
|
11
11
|
});
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
const BROKER_AUTH_REQUEST_VALUE = "machine-bridge-browser-v2";
|
|
7
7
|
|
|
8
8
|
async function extensionProtocol(endpoint, token) {
|
|
9
|
-
if (!TOKEN.test(
|
|
9
|
+
if (typeof token !== "string" || !TOKEN.test(token)) throw new Error("browser pairing token is invalid");
|
|
10
10
|
const parsed = parseEndpoint(endpoint);
|
|
11
11
|
if (!parsed) throw new Error("browser broker endpoint is invalid");
|
|
12
12
|
const clientChallenge = randomBase64Url(24);
|
|
@@ -27,8 +27,9 @@
|
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
function parseEndpoint(value) {
|
|
30
|
+
if (typeof value !== "string") return null;
|
|
30
31
|
let parsed;
|
|
31
|
-
try { parsed = new URL(
|
|
32
|
+
try { parsed = new URL(value); } catch { return null; }
|
|
32
33
|
const port = Number(parsed.port);
|
|
33
34
|
if (parsed.protocol !== "ws:" || parsed.hostname !== "127.0.0.1" || parsed.pathname !== "/extension"
|
|
34
35
|
|| parsed.username || parsed.password || parsed.search || parsed.hash
|
|
@@ -40,8 +41,9 @@
|
|
|
40
41
|
return parsed ? `http://127.0.0.1:${parsed.port}/pair` : "";
|
|
41
42
|
}
|
|
42
43
|
function parsePairingPage(value) {
|
|
44
|
+
if (typeof value !== "string") return null;
|
|
43
45
|
let parsed;
|
|
44
|
-
try { parsed = new URL(
|
|
46
|
+
try { parsed = new URL(value); } catch { return null; }
|
|
45
47
|
const port = Number(parsed.port);
|
|
46
48
|
if (parsed.protocol !== "http:" || parsed.hostname !== "127.0.0.1" || parsed.pathname !== "/pair"
|
|
47
49
|
|| parsed.username || parsed.password || parsed.search || parsed.hash
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
(() => {
|
|
2
|
+
if (globalThis.__machineBridgeBrowserOperationContract) return;
|
|
3
|
+
const MUTATING_METHODS = new Set([
|
|
4
|
+
"manage_tabs", "point_action", "backend_node_action", "action", "fill_form", "upload_files", "screenshot",
|
|
5
|
+
]);
|
|
6
|
+
const MUTATION_RESULT_SETTLEMENT_UNKNOWN = "browser mutation may have completed; the action outcome is unknown because its result could not be delivered. Inspect browser state before retrying.";
|
|
7
|
+
function exactFiniteNumber(value) {
|
|
8
|
+
return typeof value === "number" && Number.isFinite(value) ? (Object.is(value, -0) ? 0 : value) : null;
|
|
9
|
+
}
|
|
10
|
+
function exactPositiveFiniteNumber(value) {
|
|
11
|
+
const number = exactFiniteNumber(value);
|
|
12
|
+
return number !== null && number > 0 ? number : null;
|
|
13
|
+
}
|
|
14
|
+
function exactNormalizedCoordinate(value) {
|
|
15
|
+
return typeof value === "number" && Number.isFinite(value) && value >= 0 && value < 1
|
|
16
|
+
? (Object.is(value, -0) ? 0 : value) : null;
|
|
17
|
+
}
|
|
18
|
+
function exactPositiveInteger(value) { return Number.isSafeInteger(value) && value > 0 ? value : null; }
|
|
19
|
+
function exactNonNegativeInteger(value) { return Number.isSafeInteger(value) && value >= 0 ? value : null; }
|
|
20
|
+
function exactBoolean(value, label, fallback) {
|
|
21
|
+
if (value === undefined) return fallback;
|
|
22
|
+
if (typeof value !== "boolean") throw new Error(`${label} must be boolean before dispatch`);
|
|
23
|
+
return value;
|
|
24
|
+
}
|
|
25
|
+
function exactBoundedInteger(value, label, fallback, min, max) {
|
|
26
|
+
if (value === undefined) return fallback;
|
|
27
|
+
if (!Number.isSafeInteger(value) || value < min || value > max) throw new Error(`${label} is invalid before dispatch`);
|
|
28
|
+
return value;
|
|
29
|
+
}
|
|
30
|
+
function exactImageFormat(value, label, fallback = "png") {
|
|
31
|
+
if (value === undefined) return fallback;
|
|
32
|
+
if (typeof value !== "string" || !["png", "jpeg"].includes(value)) throw new Error(`${label} is invalid before dispatch`);
|
|
33
|
+
return value;
|
|
34
|
+
}
|
|
35
|
+
function exactOptionalSha256(value, label) {
|
|
36
|
+
if (value === undefined || value === "") return "";
|
|
37
|
+
if (typeof value !== "string" || !/^[A-Fa-f0-9]{64}$/.test(value)) throw new Error(`${label} is invalid before dispatch`);
|
|
38
|
+
return value.toLowerCase();
|
|
39
|
+
}
|
|
40
|
+
function exactOptionalText(value, label, maxLength) {
|
|
41
|
+
if (value === undefined || value === "") return "";
|
|
42
|
+
if (typeof value !== "string" || value.includes("\0") || value.length > maxLength) throw new Error(`${label} is invalid before dispatch`);
|
|
43
|
+
return value;
|
|
44
|
+
}
|
|
45
|
+
function exactOptionalAuthorityString(value, maxLength) {
|
|
46
|
+
if (value === undefined || value === "") return "";
|
|
47
|
+
if (typeof value !== "string" || value.length > maxLength || value.includes("\0")) {
|
|
48
|
+
throw new Error("snapshot authority string is invalid before dispatch");
|
|
49
|
+
}
|
|
50
|
+
return value;
|
|
51
|
+
}
|
|
52
|
+
function requiredSnapshotAuthorityString(value, label, maxLength) {
|
|
53
|
+
const text = exactOptionalAuthorityString(value, maxLength);
|
|
54
|
+
if (!text) throw new Error(`${label} is required before dispatch`);
|
|
55
|
+
return text;
|
|
56
|
+
}
|
|
57
|
+
function requiredSnapshotViewport(value) {
|
|
58
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) throw new Error("visual snapshot viewport is required before dispatch");
|
|
59
|
+
const output = {};
|
|
60
|
+
for (const key of ["width", "height", "scale"]) {
|
|
61
|
+
const number = exactPositiveFiniteNumber(value[key]);
|
|
62
|
+
if (number === null) throw new Error(`visual snapshot viewport ${key} is invalid before dispatch`);
|
|
63
|
+
output[key] = number;
|
|
64
|
+
}
|
|
65
|
+
return output;
|
|
66
|
+
}
|
|
67
|
+
function methodMayMutate(method) { return typeof method === "string" && MUTATING_METHODS.has(method); }
|
|
68
|
+
function responsePayload({ id, ok, result, error = "", method = "", maxBytes }) {
|
|
69
|
+
const mutatingSuccess = ok === true && methodMayMutate(method);
|
|
70
|
+
let payload;
|
|
71
|
+
try { payload = JSON.stringify({ type: "response", id, ok, ...(ok ? { result } : { error }) }); }
|
|
72
|
+
catch {
|
|
73
|
+
payload = JSON.stringify({ type: "response", id, ok: false,
|
|
74
|
+
error: mutatingSuccess ? MUTATION_RESULT_SETTLEMENT_UNKNOWN : "browser result could not be serialized" });
|
|
75
|
+
}
|
|
76
|
+
const resultLimit = Number.isSafeInteger(maxBytes) && maxBytes > 0 ? maxBytes : 0;
|
|
77
|
+
if (resultLimit === 0 || new TextEncoder().encode(payload).byteLength > resultLimit) {
|
|
78
|
+
payload = JSON.stringify({ type: "response", id, ok: false,
|
|
79
|
+
error: mutatingSuccess ? MUTATION_RESULT_SETTLEMENT_UNKNOWN : "browser result exceeds maximum size" });
|
|
80
|
+
}
|
|
81
|
+
return payload;
|
|
82
|
+
}
|
|
83
|
+
Object.defineProperty(globalThis, "__machineBridgeBrowserOperationContract", {
|
|
84
|
+
value: Object.freeze({
|
|
85
|
+
exactFiniteNumber, exactPositiveFiniteNumber, exactNormalizedCoordinate, exactPositiveInteger,
|
|
86
|
+
exactNonNegativeInteger, exactBoolean, exactBoundedInteger, exactImageFormat, exactOptionalSha256,
|
|
87
|
+
exactOptionalText, requiredSnapshotAuthorityString, requiredSnapshotViewport,
|
|
88
|
+
exactOptionalAuthorityString, methodMayMutate, responsePayload,
|
|
89
|
+
}),
|
|
90
|
+
configurable: false,
|
|
91
|
+
});
|
|
92
|
+
})();
|