machine-bridge-mcp 2.0.0 → 3.0.0-beta.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (143) hide show
  1. package/CHANGELOG.md +109 -0
  2. package/CONTRIBUTING.md +20 -25
  3. package/README.md +21 -14
  4. package/SECURITY.md +147 -123
  5. package/browser-extension/manifest.json +3 -2
  6. package/browser-extension/service-worker.js +31 -12
  7. package/docs/ARCHITECTURE.md +27 -13
  8. package/docs/AUDIT.md +85 -2
  9. package/docs/CLIENTS.md +2 -2
  10. package/docs/ENGINEERING.md +9 -3
  11. package/docs/GETTING_STARTED.md +10 -9
  12. package/docs/LOCAL_AUTHORIZATION.md +115 -72
  13. package/docs/LOGGING.md +12 -3
  14. package/docs/MANAGED_JOBS.md +24 -29
  15. package/docs/MULTI_ACCOUNT.md +98 -37
  16. package/docs/OPERATIONS.md +33 -22
  17. package/docs/OVERVIEW.md +7 -7
  18. package/docs/PROJECT_STANDARDS.md +9 -5
  19. package/docs/RELEASING.md +171 -64
  20. package/docs/TESTING.md +19 -7
  21. package/docs/THREAT_MODEL.md +138 -67
  22. package/docs/TOOL_REFERENCE.md +2 -2
  23. package/docs/UPGRADING.md +102 -18
  24. package/native/macos/MachineBridgeTrustBroker.swift +233 -0
  25. package/package.json +36 -5
  26. package/scripts/candidate-runtime-store.mjs +38 -0
  27. package/scripts/check-plan.mjs +19 -0
  28. package/scripts/check-runner.mjs +4 -1
  29. package/scripts/commit-message-check.mjs +5 -2
  30. package/scripts/coverage-check.mjs +30 -2
  31. package/scripts/github-push.mjs +11 -0
  32. package/scripts/github-release.mjs +38 -7
  33. package/scripts/install-published-prerelease.mjs +167 -0
  34. package/scripts/local-release-acceptance.mjs +23 -12
  35. package/scripts/npm-publication-policy.mjs +16 -0
  36. package/scripts/prerelease-activation.mjs +79 -0
  37. package/scripts/privacy-check.mjs +7 -5
  38. package/scripts/promotion-digest.mjs +121 -0
  39. package/scripts/publish-npm.mjs +51 -0
  40. package/scripts/published-release.mjs +87 -0
  41. package/scripts/release-acceptance.mjs +30 -1
  42. package/scripts/release-candidate-manifest.mjs +40 -0
  43. package/scripts/release-channel.mjs +106 -0
  44. package/scripts/release-impact-check.mjs +12 -22
  45. package/scripts/release-soak.mjs +237 -0
  46. package/scripts/start-release-candidate.mjs +113 -7
  47. package/src/local/account-access.mjs +6 -0
  48. package/src/local/account-admin.mjs +34 -16
  49. package/src/local/agent-context.mjs +45 -42
  50. package/src/local/app-automation.mjs +9 -4
  51. package/src/local/authority-context.mjs +106 -0
  52. package/src/local/browser-bridge-http.mjs +4 -1
  53. package/src/local/browser-bridge.mjs +14 -9
  54. package/src/local/browser-broker-routes.mjs +4 -0
  55. package/src/local/browser-broker-server.mjs +5 -4
  56. package/src/local/browser-extension-identity.mjs +50 -0
  57. package/src/local/browser-extension-protocol.mjs +9 -4
  58. package/src/local/browser-operation-service.mjs +7 -0
  59. package/src/local/browser-pairing-http.mjs +36 -0
  60. package/src/local/browser-pairing-store.mjs +56 -47
  61. package/src/local/cli-account-admin.mjs +41 -7
  62. package/src/local/cli-activate.mjs +90 -0
  63. package/src/local/cli-approval.mjs +14 -58
  64. package/src/local/cli-local-admin.mjs +5 -15
  65. package/src/local/cli-options.mjs +11 -7
  66. package/src/local/cli-service.mjs +35 -5
  67. package/src/local/cli.mjs +112 -58
  68. package/src/local/daemon-process.mjs +6 -0
  69. package/src/local/delegated-process-sandbox.mjs +147 -0
  70. package/src/local/device-identity.mjs +157 -48
  71. package/src/local/device-root-provider.mjs +87 -0
  72. package/src/local/errors.mjs +25 -3
  73. package/src/local/git-service.mjs +16 -15
  74. package/src/local/job-runner.mjs +35 -27
  75. package/src/local/log.mjs +14 -1
  76. package/src/local/macos-trust-broker.mjs +320 -0
  77. package/src/local/managed-job-lock.mjs +18 -0
  78. package/src/local/managed-job-projection.mjs +9 -1
  79. package/src/local/managed-job-runner.mjs +13 -1
  80. package/src/local/managed-job-storage.mjs +2 -1
  81. package/src/local/managed-job-terminal.mjs +138 -0
  82. package/src/local/managed-jobs.mjs +88 -32
  83. package/src/local/operation-authorization.mjs +75 -239
  84. package/src/local/operation-risk.mjs +28 -0
  85. package/src/local/operation-state-lock.mjs +7 -86
  86. package/src/local/owner-state-lock.mjs +104 -0
  87. package/src/local/policy.mjs +19 -0
  88. package/src/local/process-execution.mjs +21 -9
  89. package/src/local/process-sessions.mjs +27 -17
  90. package/src/local/process-tracker.mjs +30 -6
  91. package/src/local/process-tree-ownership.mjs +59 -0
  92. package/src/local/process-tree.mjs +22 -18
  93. package/src/local/relay-connection.mjs +47 -35
  94. package/src/local/relay-diagnostics.mjs +65 -0
  95. package/src/local/remote-configuration.mjs +48 -0
  96. package/src/local/runtime-activation.mjs +141 -0
  97. package/src/local/runtime-diagnostics.mjs +21 -0
  98. package/src/local/runtime-relay.mjs +9 -8
  99. package/src/local/runtime-reporting.mjs +8 -3
  100. package/src/local/runtime-tool-handlers.mjs +8 -8
  101. package/src/local/runtime.mjs +73 -39
  102. package/src/local/secure-file.mjs +5 -1
  103. package/src/local/security-audit-log.mjs +204 -0
  104. package/src/local/service-convergence.mjs +17 -1
  105. package/src/local/service-ownership.mjs +17 -0
  106. package/src/local/service-restart-handoff.mjs +50 -0
  107. package/src/local/service-restart-scheduler.mjs +49 -0
  108. package/src/local/service-status.mjs +47 -0
  109. package/src/local/service.mjs +142 -43
  110. package/src/local/state-inventory.mjs +21 -3
  111. package/src/local/state.mjs +155 -17
  112. package/src/local/stdio.mjs +5 -0
  113. package/src/local/tool-executor.mjs +39 -6
  114. package/src/local/tool-result-boundary.mjs +51 -0
  115. package/src/local/tools.mjs +1 -0
  116. package/src/local/trusted-executable.mjs +53 -0
  117. package/src/local/trusted-git-executable.mjs +34 -0
  118. package/src/local/trusted-github-cli.mjs +24 -0
  119. package/src/local/windows-service.mjs +20 -0
  120. package/src/local/worker-deployment.mjs +3 -4
  121. package/src/local/worker-secret-file.mjs +2 -2
  122. package/src/local/workspace-file-service.mjs +34 -31
  123. package/src/shared/admin-auth.d.mts +2 -1
  124. package/src/shared/admin-auth.mjs +3 -3
  125. package/src/shared/device-session-auth.d.mts +20 -0
  126. package/src/shared/device-session-auth.mjs +51 -0
  127. package/src/shared/server-metadata.json +1 -1
  128. package/src/shared/tool-catalog.json +2 -2
  129. package/src/worker/account-admin.ts +23 -22
  130. package/src/worker/authority.ts +9 -2
  131. package/src/worker/daemon-auth.ts +45 -67
  132. package/src/worker/daemon-sockets.ts +12 -2
  133. package/src/worker/device-session-verifier.ts +129 -0
  134. package/src/worker/dpop.ts +151 -0
  135. package/src/worker/http.ts +47 -16
  136. package/src/worker/index.ts +53 -16
  137. package/src/worker/nonce-store.ts +1 -5
  138. package/src/worker/oauth-client-admin.ts +45 -0
  139. package/src/worker/oauth-controller.ts +55 -6
  140. package/src/worker/oauth-refresh-families.ts +21 -7
  141. package/src/worker/oauth-state.ts +6 -0
  142. package/src/worker/oauth-tokens.ts +28 -8
  143. package/src/worker/websocket-protocol.ts +8 -2
@@ -57,11 +57,13 @@ Machine Bridge supports concurrent calls: the Worker admits up to 32 pending dae
57
57
 
58
58
  ### Relay interruption messages
59
59
 
60
+ A reconnect warning is evidence of a transport outage, not proof that the daemon process exited. Compare daemon PID/process start, `connected_at`, `last_seen_at`, `runtime.relay.last_disconnected_at`, close category/code, and outage count. A system VPN/TUN may remain shown as connected while its internal route is unavailable; Machine Bridge reports that route only as `system-network-stack` with application-proxy scope. The reconnect schedule now tops out at fifteen seconds.
61
+
60
62
  A brief relay interruption is retried automatically and is visible only with `--verbose`. Default logs do not print raw WebSocket values such as `code=1006` with an empty reason. If a transient outage persists for 10 seconds, the daemon emits a readable duration/cause/reconnect summary; later reminders use autonomous exponential backoff capped at 15 minutes, and recovery produces one readable summary. Each transport connection attempt also has a deadline, so a socket stuck in `CONNECTING` cannot freeze retries. Identity/version mismatch, authentication rejection, and unexpected protocol messages are not retried as ordinary network faults: the daemon emits an immediate actionable error and exits, requiring upgrade/redeployment or credential repair. Worker-side hello and end-to-end readiness timeouts remain retryable. Authentication is not reported as usable service readiness until a session-bound probe result returns.
61
63
 
62
64
  Use `--verbose` only when close codes, close reasons, heartbeat timeouts, and retry delays are needed for diagnosis. A close code of 1006 means the transport ended without a normal close handshake; it does not by itself identify the cause.
63
65
 
64
- The daemon honors `HTTPS_PROXY`/`HTTP_PROXY` and `NO_PROXY` through standard environment-proxy resolution for remote Worker health and relay traffic. `wss:` targets use HTTPS proxy selection and `ws:` targets use HTTP proxy selection. Only HTTP and HTTPS proxy URLs are accepted. Invalid URLs or unsupported protocols fail startup with corrective guidance instead of entering the reconnect loop. `server_info.runtime.relay.network_route` reports only `direct`, `proxy`, or `invalid-proxy-configuration`; proxy endpoints and credentials are never returned or logged. The browser-broker CLI health probe is a separate loopback-only path: it accepts only canonical `127.0.0.1`, uses direct Node HTTP with no proxy agent, and does not depend on `NO_PROXY`.
66
+ The daemon honors `HTTPS_PROXY`/`HTTP_PROXY` and `NO_PROXY` through standard environment-proxy resolution for remote Worker health and relay traffic. `wss:` targets use HTTPS proxy selection and `ws:` targets use HTTP proxy selection. Only HTTP and HTTPS proxy URLs are accepted. Invalid URLs or unsupported protocols fail startup with corrective guidance instead of entering the reconnect loop. `server_info.runtime.relay.network_route` reports `system-network-stack`, `application-http-proxy`, or `invalid-application-proxy-configuration`. This field describes only Machine Bridge application-level proxy selection: an operating-system VPN/TUN may still intercept `system-network-stack` traffic. `network_route_scope`, outage timestamps/durations, close category/code, transport error class, and next retry timing make that distinction explicit; proxy endpoints and credentials are never returned or logged. The browser-broker CLI health probe is a separate loopback-only path: it accepts only canonical `127.0.0.1`, uses direct Node HTTP with no proxy agent, and does not depend on `NO_PROXY`.
65
67
 
66
68
  ## Browser extension setup and diagnosis
67
69
 
@@ -123,9 +125,15 @@ machine-mcp --verbose
123
125
 
124
126
  After global installation, Windows users may open any `cmd.exe` window and run `machine-mcp`; they do not need to navigate to a project or package directory. On the first interactive start, the CLI asks for a workspace folder and displays `%USERPROFILE%\MachineBridge` as the default. Pressing Enter creates and remembers that folder. This avoids inheriting an arbitrary Command Prompt current directory such as `C:\Windows\System32`. Typing another folder in the prompt creates that folder when necessary. Explicit `--workspace PATH` remains a strict automation interface and requires an existing path.
125
127
 
126
- ## Version 1 upgrade convergence
128
+ ### Start, restart, and cross-workspace ownership
129
+
130
+ `service start` is an idempotent ensure-running operation. It does not stop a verified already-running daemon, so calling it through that daemon cannot destroy the control connection. `service restart` is separate: macOS and systemd-user use a detached service-manager handoff so the command can return before the old daemon exits. Windows restart is fail closed from inside the running task because Task Scheduler `/End` may terminate the helper with the daemon; use an independent terminal stop/start sequence until a behaviorally verified Windows handoff exists.
131
+
132
+ A workspace/state selector does not grant authority over the machine-global service label. Stop, restart, foreground takeover, secret rotation, and uninstall require the exact live verified `service` daemon ownership record. Service status returns only provider, installed/loaded/active state, PID, bounded run count, and classified termination state; provider environment dumps are never returned.
133
+
134
+ ## Current upgrade convergence
127
135
 
128
- Version 1 advertises only MCP protocol `2025-11-25`. Upgrade the MCP client/host if it cannot negotiate that version; Machine Bridge does not retain an obsolete protocol dispatcher. The current local state schema is unchanged from the final 0.18.x release, so do not delete the state root merely to upgrade.
136
+ The current release advertises only MCP protocol `2025-11-25`. Upgrade the MCP client or host if it cannot negotiate that version; Machine Bridge does not retain an obsolete protocol dispatcher. Version 3 also requires matching Worker, daemon, CLI, and browser-extension components and may perform a two-phase device-root migration. Preserve the state root and follow [UPGRADING.md](UPGRADING.md); do not delete state to force apparent convergence.
129
137
 
130
138
  Use this sequence:
131
139
 
@@ -154,7 +162,7 @@ Stable errors include `policy_denied`, `invalid_request`, `timeout`, `cancelled`
154
162
 
155
163
  Windows Task Scheduler limits the `/TR` action text, so the platform adapter writes a short private `service-launcher.cmd` under the state root rather than embedding the full Node, package, workspace, state, and logging argv in the task action. The launcher uses CRT-compatible argument quoting, sends stdout/stderr to the normal service logs, exits on a successful daemon exit, and restarts a nonzero exit after five seconds. Task creation is accepted only after a separate state query observes the task. Start, stop, and removal likewise query the Task Scheduler state through fixed PowerShell object properties instead of parsing localized `schtasks` messages; an installed `Ready` task is not mislabeled as currently running.
156
164
 
157
- The Windows trigger is current-user `ONLOGON` with `LIMITED` run level. After a reboot, signing in to that user is sufficient; no terminal command is required. Pre-login operation is intentionally not provided by the default design because it would require a different service-account/credential boundary. 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. A private allowlisted `service-environment.json` preserves `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`, matching lowercase forms, optional `ALL_PROXY`, Node proxy selection, and custom-CA path variables. Existing saved values survive an environment-free reinstall, while explicitly supplied values replace case-insensitive prior variants. Values may include proxy credentials, so the file stays in owner-only state and is never returned or logged; status exposes key names only. Re-run `machine-mcp service install` after changing Node installation families, PATH layout, proxy, or CA configuration. Custom Windows state paths used for autostart must also remain within the Task Scheduler path limit and must not contain a literal `%`.
165
+ The Windows trigger is current-user `ONLOGON` with `LIMITED` run level. After a reboot, signing in to that user is sufficient; no terminal command is required. Pre-login operation is intentionally not provided by the default design because it would require a different service-account/credential boundary. 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 current Node/package directories, the operator's inherited absolute PATH entries, and platform defaults. When installation runs through npm, all nested run-script prefixes through the final npm private `node-gyp-bin` marker are discarded; paths belonging to inactive candidate runtimes are also removed. This prevents prerelease activation from persisting source-repository shims or the prior runtime that activation subsequently prunes, while an ordinary user-supplied `node_modules/.bin` remains valid. A private allowlisted `service-environment.json` preserves `HTTP_PROXY`, `HTTPS_PROXY`, `NO_PROXY`, matching lowercase forms, optional `ALL_PROXY`, Node proxy selection, and custom-CA path variables. Existing saved values survive an environment-free reinstall, while explicitly supplied values replace case-insensitive prior variants. Values may include proxy credentials, so the file stays in owner-only state and is never returned or logged; status exposes key names only. Re-run `machine-mcp service install` after changing Node installation families, PATH layout, proxy, or CA configuration. Custom Windows state paths used for autostart must also remain within the Task Scheduler path limit and must not contain a literal `%`.
158
166
 
159
167
  A service-style `--daemon-only` start that finds the same workspace daemon already running is an idempotent no-op: it exits successfully without repeating warnings or readiness output; explicit policy/secret/change requests still report that changes were not applied. Autostart logs are stored under the state root in `logs/daemon.out.log` and `logs/daemon.err.log`. Installed services pass `--log-level warn --log-format json`, so each active line is a bounded JSON event suitable for ingestion. Files are owner-only where supported and tail-trimmed before daemon startup. If the log schema marker does not match the current format, the active files are cleared before startup and the current marker is written. Runtime code reads and maintains only the active filenames.
160
168
 
@@ -208,14 +216,13 @@ Inspect detached jobs even when the MCP host no longer permits execution tools:
208
216
  ```sh
209
217
  machine-mcp job list
210
218
  machine-mcp job inspect JOB_ID
211
- machine-mcp job approve JOB_ID [--yes]
212
219
  machine-mcp job cancel JOB_ID
213
220
  machine-mcp job submit plan.json
214
221
  ```
215
222
 
216
223
  Registry changes apply to newly submitted jobs without restarting the daemon. Active jobs use the resource snapshot accepted with their plan.
217
224
 
218
- Policy changes affect new direct submissions. Cancel accepted running jobs explicitly when revoking execution authority. A staged plan launches only after local `job approve`, which is an independent operator authorization. A managed job transitions through `queued`, `running`, `cleaning`, and a terminal status such as `succeeded`, `failed`, or `cancelled`. Cleanup-specific terminal variants report a failed finally phase. If a runner PID dies, the next daemon/job-CLI start marks the job interrupted, removes stale private runtime copies, and runs the finally phase in recovery mode. Automatic recovery is capped at three attempts; persistent failure becomes `recovery_exhausted`.
225
+ Policy changes affect new submissions. Cancel accepted running jobs explicitly when revoking execution authority. A staged plan is a non-running draft and has no terminal promotion path. A trusted owner uses `start_job`; an explicit local operator may submit a reviewed JSON plan with `job submit`. A managed job transitions through `queued`, `running`, `cleaning`, and a terminal status such as `succeeded`, `failed`, or `cancelled`. Cleanup-specific terminal variants report a failed finally phase. If a runner PID dies, the next daemon/job-CLI start marks the job interrupted, removes stale private runtime copies, and runs the finally phase in recovery mode. Automatic recovery is capped at three attempts; persistent failure becomes `recovery_exhausted`.
219
226
 
220
227
  Use job-scoped `temporary_files` for local helpers. For remote maintenance, prefer `ssh ... sh -s` with the remote script in step `stdin`; this avoids remote temporary scripts. Explicit remote cleanup belongs in idempotent `finally_steps`.
221
228
 
@@ -293,40 +300,44 @@ machine-mcp --workspace /path/to/project --profile agent
293
300
 
294
301
  A remote policy change is saved locally, propagated in the daemon handshake, and loaded by autostart from owner-only state.
295
302
 
296
- ### Remote authority and optional delegated-account leases
303
+ ### Remote authority and client trust
297
304
 
298
- The saved profile defines the capability ceiling. An authenticated owner account may use that ceiling directly and is never required to approve a pending ID in a terminal. Delegated reviewer, editor, and operator accounts still require bounded leases for consequential remote effects. Inspect their pending requests and active leases with:
305
+ The saved profile defines the daemon capability ceiling. Remote authority additionally requires the authenticated account role, trusted OAuth client binding, current account version and refresh family, and ownership of any long-lived object.
306
+
307
+ Inspect accounts and trusted clients with:
299
308
 
300
309
  ```sh
301
- machine-mcp --workspace /path/to/project approval list
310
+ machine-mcp --workspace /path/to/project account list
311
+ machine-mcp --workspace /path/to/project account clients
302
312
  ```
303
313
 
304
- Approve the requested scope for ordinary work, or explicitly open a temporary full automation window:
314
+ Revoke a compromised or stale client independently:
305
315
 
306
316
  ```sh
307
- machine-mcp --workspace /path/to/project approval approve APPROVAL_ID --duration 1h
308
- machine-mcp --workspace /path/to/project approval approve APPROVAL_ID --full
317
+ machine-mcp --workspace /path/to/project account revoke-client CLIENT_ID --yes
309
318
  ```
310
319
 
311
- Revoke one lease or all leases:
320
+ Delegated roles cannot be elevated by an approval ID or lease. Legacy version 2 leases may be inspected or removed only for migration cleanup:
312
321
 
313
322
  ```sh
323
+ machine-mcp --workspace /path/to/project approval list
314
324
  machine-mcp --workspace /path/to/project approval revoke LEASE_ID
315
- machine-mcp --workspace /path/to/project approval clear
325
+ machine-mcp --workspace /path/to/project approval clear --yes
316
326
  ```
317
327
 
318
- The pending and lease files are owner-only and contain identity bindings, scope, timestamps, and target digests rather than command or content text. Full details are in [LOCAL_AUTHORIZATION.md](LOCAL_AUTHORIZATION.md).
328
+ The current runtime never consumes legacy leases. Full details are in [LOCAL_AUTHORIZATION.md](LOCAL_AUTHORIZATION.md).
319
329
 
320
330
  ## Incident response
321
331
 
322
- After suspected credential or client compromise:
332
+ After suspected credential, client, or device compromise:
323
333
 
324
334
  1. stop foreground and autostart daemons;
325
- 2. run `machine-mcp approval clear` for every affected workspace;
326
- 3. disable or rotate the affected account, or run `machine-mcp rotate-secrets` for deployment-wide revocation;
327
- 4. restart without broad flags and redeploy;
328
- 5. inspect Cloudflare account access, Worker configuration, local state/resource permissions, process-lock owners, managed-job results, and service logs;
329
- 6. cancel active managed jobs and remove compromised resource aliases;
330
- 7. remove the Worker and local state if continued remote access is unnecessary.
335
+ 2. revoke the affected OAuth client, or disable/rotate the affected account;
336
+ 3. clear any legacy version 2 leases as migration hygiene;
337
+ 4. run `machine-mcp rotate-secrets` when device-root or deployment-wide token compromise is possible;
338
+ 5. restart with the intended profile, deploy the matching Worker, and reconnect only trusted clients;
339
+ 6. inspect Cloudflare access, Worker configuration, device-root provider, audit-chain health, local state/resource permissions, process-lock owners, managed-job results, and service logs;
340
+ 7. cancel active managed jobs and remove compromised resource aliases;
341
+ 8. remove the Worker and local state if continued remote access is unnecessary.
331
342
 
332
343
  The detailed 0.12.0 audit record and residual operational limits are in [AUDIT.md](AUDIT.md).
package/docs/OVERVIEW.md CHANGED
@@ -4,7 +4,7 @@ Machine Bridge is one local authority surface with two MCP transports. The archi
4
4
 
5
5
  1. **Who may request an operation?** Remote OAuth account/client identity and role, or the local process identity that launched stdio.
6
6
  2. **Which tools are exposed?** The shared tool catalog, policy profile, and account-role intersection.
7
- 3. **Is this remote effect currently authorized?** Direct authenticated-owner authority within the daemon ceiling, automatic delegated workspace-safe behavior, or an account/client-bound delegated capability lease.
7
+ 3. **Is this remote effect inside the current authority?** The daemon ceiling, account role, trusted client, account version, refresh family, operation invariants, and object owner must all agree.
8
8
  4. **What can an authorized tool actually do?** The local runtime, workspace/path rules, operating-system user authority, and platform security controls.
9
9
 
10
10
  Transport authentication does not create an operating-system sandbox. The local daemon executes with the authority of its OS user.
@@ -15,7 +15,7 @@ Transport authentication does not create an operating-system sandbox. The local
15
15
  flowchart LR
16
16
  HC[Hosted MCP client] -->|HTTPS + OAuth 2.1 / PKCE| W[Cloudflare Worker]
17
17
  W --> DO[Durable Object room]
18
- DO -->|P-256 device-authenticated WebSocket| R[LocalRuntime]
18
+ DO -->|root-certified ephemeral P-256 session| R[LocalRuntime]
19
19
 
20
20
  LC[Local MCP client] -->|stdio| S[stdio adapter]
21
21
  S --> R
@@ -42,9 +42,9 @@ flowchart LR
42
42
 
43
43
  1. The Worker validates OAuth client, token, account state, role, resource binding, and MCP session state.
44
44
  2. The Worker filters advertised tools by account role and the daemon-reported capability ceiling.
45
- 3. The Durable Object relays a bounded tool envelope carrying account and OAuth client identity over the device-authenticated daemon socket.
45
+ 3. The Durable Object relays a bounded tool envelope carrying account, account-version, OAuth-client, refresh-family, and role identity over the root-certified ephemeral daemon socket.
46
46
  4. The local runtime revalidates account authorization, policy, call lifecycle, timeout, and cancellation.
47
- 5. The local transaction gate directly permits authenticated owner work within the daemon ceiling; delegated accounts receive automatic workspace-safe behavior or require a matching bounded capability lease for a high-impact effect.
47
+ 5. The local authority gate permits work only inside the immutable role ceiling. Consequential effects are classified for hard enforcement and audit; they never create a temporary elevation path.
48
48
  6. The selected local service executes with the daemon OS user's authority.
49
49
 
50
50
  The Worker never receives ambient filesystem or process authority. It receives only explicit request/result messages.
@@ -68,7 +68,7 @@ Local and Worker code consume these contracts. Generated references and drift te
68
68
  `LocalRuntime` is an orchestrator, not a low-level implementation module. It composes:
69
69
 
70
70
  - policy and account authorization;
71
- - authenticated-owner direct authority plus delegated effect classification and local capability leases;
71
+ - request-scoped effective authority, hard role ceilings, operation classification, and principal-bound object ownership;
72
72
  - call registration, timeout, cancellation, and observability;
73
73
  - workspace and path services;
74
74
  - direct and shell process execution;
@@ -82,9 +82,9 @@ Low-level responsibilities remain in focused modules. Architecture tests enforce
82
82
 
83
83
  ## State and lifecycle
84
84
 
85
- Each canonical workspace has independent profile state, Worker identity, credentials, local capability leases, locks, service metadata, and managed jobs. State mutations use owner-only files where supported, bounded reads, atomic replacement, and process-identity-aware locks.
85
+ Each canonical workspace has independent profile state, Worker identity, trusted clients, account/token state, legacy-lease cleanup state, audit-chain state, locks, service metadata, and managed jobs. State mutations use owner-only files where supported, bounded reads, atomic replacement, and process-identity-aware locks.
86
86
 
87
- The relay distinguishes signed preflight, challenge authentication, readiness probing, and active service. A candidate daemon must prove possession of the enrolled P-256 key and complete an end-to-end probe before replacing an incumbent. Disconnect cancels relay-owned calls and terminates associated local processes.
87
+ The relay distinguishes a root-certified ephemeral session, signed preflight, challenge authentication, readiness probing, and active service. A candidate daemon must prove possession of the certified session key and complete an end-to-end probe before replacing an incumbent. Disconnect cancels relay-owned calls and terminates associated local processes.
88
88
 
89
89
  Managed jobs use a separate durable lifecycle. Their plans are integrity-bound, runners are process-identity checked, transitions are lock-protected, terminal plans are scrubbed, and recovery is bounded.
90
90
 
@@ -18,15 +18,19 @@ Branch names use a short category and purpose, for example `feat/browser-downloa
18
18
 
19
19
  Direct pushes to `main`, force pushes, and branch deletion are blocked by repository protection. An exception requires an incident record and an explicit owner decision.
20
20
 
21
- ### Completion ownership and local acceptance
21
+ ### Completion ownership, prerelease activation, and soak
22
22
 
23
- Repository automation owns implementation, local validation, observed candidate verification, acceptance recording, and pull-request completion. For every npm-package change, automation generates the exact candidate tarball with `npm run release:candidate` and stops before the first GitHub push. The repository owner explicitly authorizes the live candidate Worker update in the active conversation. The coding agent starts the exact candidate through Machine Bridge with `npm run release:candidate:start -- --allow-worker-deploy`, verifies the deployed Worker version/hash, remote health, relay readiness, connected local version, and representative functionality, then records a passing decision with `npm run release:accept -- --confirm "I VERIFIED machine-bridge-mcp <version> CANDIDATE ON THE OWNER MACHINE AND IT WORKS"`. No owner terminal approval command is part of the standard workflow. Automated checks or an unobserved process are not sufficient evidence.
23
+ Repository automation owns implementation, local validation, candidate preparation, observed verification, acceptance recording, and pull-request completion. Package work for version 3 or later must use a `dev`, `beta`, or `rc` version before stable promotion. Direct implementation-to-stable release is prohibited.
24
24
 
25
- The tracked `release-acceptance/v<version>.json` record binds the observed result to the npm tarball SHA-1 and SHA-512 integrity value. The record is excluded from the package, so a content-preserving squash merge does not invalidate it; any source, executable script, package metadata, or packaged-documentation change produces a different package hash and requires a regenerated candidate and another observed live verification. `npm run github:push`, pull-request CI, and `npm run release` verify the record. Raw direct pushes of release-relevant branches are prohibited.
25
+ `npm run release:candidate` creates the exact tarball. The repository owner executes `npm run release:candidate:activate -- --allow-worker-deploy`; the command installs that tarball under the private state root, updates the same-name Worker, verifies candidate relay readiness, replaces the login daemon, verifies the background handoff, and exits. The coding agent then performs observed live verification through Machine Bridge and records acceptance only after that observed success. No per-operation terminal approval is involved.
26
26
 
27
- After interactive acceptance, repository automation may push only through `npm run github:push`, open and complete the pull request, verify the resulting `main` commit, remove the merged branch, and run `npm run release`. Every npm-package change advances the package version and is completed by an annotated `v<version>` tag plus a final GitHub Release for the exact successful `main` commit. GitHub-only repository infrastructure changes that do not alter npm package contents do not require a synthetic npm version or local runtime acceptance, but they still require review and their applicable CI/security checks.
27
+ The tracked `release-acceptance/v<version>.json` binds npm hashes, a portable package digest, and a version-normalized promotion digest. Any packaged change invalidates acceptance. `npm run github:push`, CI, and source-release commands verify the record. Raw pushes of package branches are prohibited.
28
28
 
29
- The coding agent performs the acceptance-record, tag, and GitHub Release work only after observed live verification and exact-commit checks succeed. npm publication, Worker deployment, credential mutation, non-isolated global installation, and daemon/service replacement remain separate live operations performed by the repository owner.
29
+ The accepted prerelease is released with `npm run prerelease:release`; npm publication remains an explicit owner operation through `npm run prerelease:publish`. The owner installs the exact registry version with `npm run prerelease:install -- --allow-worker-deploy`. Formal soak begins only from this registry-verified activation. Minimum soak is seven days for major, three days for minor, and one day for patch releases. A blocking defect increments the prerelease number and restarts the soak interval.
30
+
31
+ Stable promotion requires a tracked `release-soak/v<stable>.json` and identical promotion-content digest. Only normalized release metadata may differ from the soaked prerelease. The stable candidate is activated and observed again before `npm run release`; npm stable publication remains an explicit owner operation through `npm run stable:publish`.
32
+
33
+ GitHub-only repository infrastructure changes that do not alter npm package contents do not require a synthetic version, candidate activation, or soak. They still require review and applicable checks.
30
34
 
31
35
  ### Local GitHub control plane
32
36
 
package/docs/RELEASING.md CHANGED
@@ -1,124 +1,231 @@
1
1
  # Release process
2
2
 
3
- The release invariant is:
3
+ Machine Bridge uses a mandatory prerelease-and-soak process. A release candidate is not a stable release, and a green automated suite is not evidence that a real Worker, daemon, browser extension, service manager, and ordinary client remain healthy over time.
4
4
 
5
- - the repository owner started the exact npm tarball represented by `release-acceptance/v<version>.json` on the maintainer machine, and the coding agent observed the live candidate through Machine Bridge before recording a passing result;
6
- - the current package produces the same SHA-1 and SHA-512 integrity values as that accepted tarball;
7
- - `main` points to the release commit, and that exact commit has completed successful push-triggered CI, CodeQL, Governance, and OpenSSF Scorecard runs;
8
- - `v<package version>` points to that same commit locally and on GitHub;
9
- - a final GitHub Release exists for the tag and contains the accepted npm tarball;
10
- - `package.json`, `package-lock.json`, the Worker-reported version, and `browser-extension/manifest.json` (`version`/`version_name`) agree;
11
- - every npm-package change since the prior version tag has a higher package version and matching CHANGELOG section;
12
- - the same accepted package content is present on GitHub and in a new npm version.
5
+ The invariant for version 3 and later is:
13
6
 
14
- A green automated suite is necessary but is not evidence that the maintainer's ordinary installation path works. The repository therefore separates automated validation from observed live candidate verification and binds both to the final package bytes.
7
+ 1. every package change is first assigned a `dev`, `beta`, or `rc` semantic version;
8
+ 2. the exact npm tarball is activated on the owner machine with one command that updates the same-name Worker and persistently replaces the login daemon;
9
+ 3. the coding agent verifies the connected candidate through Machine Bridge before recording package acceptance;
10
+ 4. the accepted prerelease is published under a non-`latest` npm dist-tag and as a GitHub Prerelease;
11
+ 5. the owner installs that exact published prerelease and uses it for the required soak period;
12
+ 6. every blocking defect produces a new prerelease version and restarts the soak clock;
13
+ 7. stable promotion is allowed only when the packaged functional content matches the soaked prerelease; only synchronized version metadata may differ;
14
+ 8. the exact stable commit must pass cross-platform CI, CodeQL, Governance, and OpenSSF Scorecard before the stable tag and final GitHub Release are created.
15
15
 
16
- ## Change classification
16
+ ## Release channels
17
17
 
18
- A change is **npm-package relevant** when it alters `package.json`, `package-lock.json`, or a path included by the package `files` manifest. Source, runtime scripts, browser extension files, shared contracts, shipped documentation, and package metadata therefore require a new version and local acceptance.
18
+ Supported versions and registry channels are:
19
19
 
20
- A repository-only change under paths such as `.github/` may be merged without a synthetic npm version when the package bytes are unchanged. It still requires review and all applicable GitHub checks. `scripts/release-impact-check.mjs` implements this distinction from the package manifest rather than from a duplicated path list.
20
+ | Version form | npm dist-tag | GitHub release type | Purpose |
21
+ |---|---|---|---|
22
+ | `x.y.z-dev.n` | `dev` | Prerelease | short-lived development integration |
23
+ | `x.y.z-beta.n` | `beta` | Prerelease | normal real-world testing channel |
24
+ | `x.y.z-rc.n` | `next` | Prerelease | optional final candidate after beta |
25
+ | `x.y.z` | `latest` | final release | stable users only |
21
26
 
22
- ## Prepare the candidate locally
27
+ Do not assign the final `x.y.z` version while implementation is still under candidate testing. A major or behaviorally risky change normally starts at `beta.1`. A release candidate may use `rc.n` after beta when a final freeze checkpoint is useful.
23
28
 
24
- 1. Set the new version without creating a tag. The npm version hook synchronizes the Worker and browser-extension versions:
29
+ Minimum published-prerelease soak periods are:
25
30
 
26
- ```sh
27
- npm version <version> --no-git-tag-version
28
- ```
31
+ - major release: seven days;
32
+ - minor release: three days;
33
+ - patch release: one day.
29
34
 
30
- 2. Add the matching dated `CHANGELOG.md` section and update affected documentation and audit notes.
31
- 3. Run the required dependency, privacy, Worker, and package checks while iterating.
32
- 4. Inspect the complete diff.
33
- 5. Generate the final candidate:
35
+ These are lower bounds, not automatic approval. Continue the soak when use has been too light to exercise the changed behavior.
34
36
 
35
- ```sh
36
- npm run release:candidate
37
- ```
37
+ ## 1. Prepare an exact prerelease candidate
38
38
 
39
- `release:candidate` runs the complete repository suite and then writes the exact npm tarball plus a bounded pending manifest under ignored `.release-candidate/`. Do not modify a packaged file after generating the candidate without regenerating and retesting it.
39
+ Complete implementation, tests, documentation, audit notes, and changelog. Set the prerelease version without creating a Git tag:
40
40
 
41
- ## Interactive local candidate acceptance
41
+ ```sh
42
+ npm version 3.0.0-beta.1 --no-git-tag-version
43
+ ```
44
+
45
+ The version hook synchronizes package metadata, Worker version, and browser-extension metadata.
46
+
47
+ Run the complete local gate and inspect the diff:
48
+
49
+ ```sh
50
+ npm run check
51
+ npm audit --audit-level=high
52
+ npm audit --omit=dev --audit-level=high
53
+ npm run worker:dry-run
54
+ npm pack --dry-run
55
+ ```
42
56
 
43
- The repository owner explicitly authorizes the exact `.release-candidate/*.tgz` artifact and any configured same-name Worker update in the active conversation. The coding agent then runs the candidate startup helper through Machine Bridge:
57
+ Generate the exact tarball:
44
58
 
45
59
  ```sh
46
- npm run release:candidate:start -- --allow-worker-deploy
60
+ npm run release:candidate
47
61
  ```
48
62
 
49
- The helper verifies the pending tarball hashes, installs it into the ignored `.release-candidate/runtime/` prefix without replacing the normal global installation, updates the configured same-name Worker when its version or deployment hash differs, and starts the installed candidate in the foreground. This is an in-place live candidate deployment, not an isolated staging Worker. The owner does not need to execute a terminal authorization command. The coding agent then verifies at minimum:
63
+ The candidate manifest records npm SHA-1/SHA-512 values and a promotion-content digest. Any packaged-file change invalidates the candidate.
50
64
 
51
- - the remote Worker reports the candidate version and the persisted deployment hash matches the candidate Worker bundle;
52
- - the remote health route succeeds without redirect;
53
- - the connected runtime reports the candidate package version;
54
- - relay readiness is healthy through the deployed Worker for the intended transport;
55
- - one representative read-only operation succeeds;
56
- - representative changed functionality succeeds, including browser, application, service, proxy, credential, or platform-specific behavior when relevant.
65
+ ## 2. Owner activates the exact candidate
57
66
 
58
- The owner does not run the acceptance command. After the coding agent has observed the live candidate passing those checks, the agent records the decision using the exact phrase printed by `release:candidate`, for example:
67
+ The coding agent must stop and present this command. The repository owner executes it:
59
68
 
60
69
  ```sh
61
- npm run release:accept -- --confirm "I VERIFIED machine-bridge-mcp 1.2.9 CANDIDATE ON THE OWNER MACHINE AND IT WORKS"
70
+ npm run release:candidate:activate -- --allow-worker-deploy
62
71
  ```
63
72
 
64
- This creates `release-acceptance/v<version>.json` containing package identity, npm tarball hashes, a portable Git-content digest, timestamp, result, and a fixed observed-verification marker. The portable digest is computed with a temporary Git index containing `HEAD` plus the complete working tree; the real staging area is not modified. The record does not store a personal name, machine path, command output, credential, or user content. It is intentionally excluded from the npm package, so adding it does not change the tested tarball.
73
+ The command:
74
+
75
+ - verifies the exact pending tarball;
76
+ - installs it under the owner-only Machine Bridge state root, separate from the normal global installation;
77
+ - stops only a verified existing service daemon;
78
+ - updates the configured same-name Worker;
79
+ - starts the candidate in-process and verifies device authentication plus relay readiness;
80
+ - installs the candidate as the login service runtime;
81
+ - performs a controlled foreground-to-service handoff;
82
+ - verifies that both the Worker and verified background daemon report the candidate version;
83
+ - exits while the background daemon continues running.
84
+
85
+ It may request one macOS user-presence or Touch ID operation to certify the daemon session key. It does not ask for per-tool approval.
86
+
87
+ The private candidate runtime is not stored under the Git checkout, so cleaning `.release-candidate/`, switching branches, or regenerating a candidate cannot delete the daemon currently under test. The previous global installation remains available as recovery information.
88
+
89
+ ## 3. Coding agent verifies the live candidate
90
+
91
+ After the owner command completes, the coding agent verifies through Machine Bridge:
65
92
 
66
- The command repacks the current tree and refuses to record acceptance if any packaged byte changed after candidate preparation. Automated tests alone, a prepared tarball, a process the agent did not observe, or an ambiguous owner response do not authorize acceptance. Version 1.2.8 owner-recorded acceptance and the 1.2.9 owner-started marker remain supported as historical formats; version 2.0.0 and later require explicit owner authorization plus agent-operated, agent-observed verification.
93
+ - `server_info` reports the exact candidate version;
94
+ - Worker health reports the same version and deployment identity;
95
+ - the connected daemon is the verified login-service process;
96
+ - relay readiness succeeds through the deployed Worker;
97
+ - a representative read succeeds;
98
+ - changed functionality and relevant failure paths succeed;
99
+ - browser, application, proxy, credential, service, and platform-specific behavior are tested when affected;
100
+ - logs contain no sensitive arguments, output, tokens, or raw user paths.
67
101
 
68
- ## Push and review
102
+ Only observed live evidence counts. A green unit suite, prepared tarball, unobserved process, or ambiguous response does not count.
69
103
 
70
- Commit the candidate changes and acceptance record, then push the branch only through:
104
+ After successful verification, the coding agent records acceptance using the exact phrase emitted by `release:candidate`:
105
+
106
+ ```sh
107
+ npm run release:accept -- --confirm "I VERIFIED machine-bridge-mcp <version> CANDIDATE ON THE OWNER MACHINE AND IT WORKS"
108
+ ```
109
+
110
+ The resulting `release-acceptance/v<version>.json` binds the exact tarball hashes, portable Git package digest, promotion-content digest, timestamp, and fixed verification marker. It contains no personal machine path, command output, account credential, or user content.
111
+
112
+ ## 4. Review and publish the prerelease
113
+
114
+ Commit the prerelease changes and acceptance record. Push only through:
71
115
 
72
116
  ```sh
73
117
  npm run github:push
74
118
  ```
75
119
 
76
- The command requires a clean non-`main` branch, fetches `origin/main`, paginates all open GitHub issues and pull requests, and refuses to push while an unrelated PR remains open or an issue is not covered by a standard closing keyword in `origin/main..HEAD`. An already-open PR for the current branch is allowed so it can be updated. The command then verifies that the acceptance record is tracked, rebuilds the npm package, compares both hashes, and only then executes a non-force push of the current branch. Direct pushes to `main` remain prohibited. Any packaged-file change after acceptance invalidates the hash and blocks the next push until the owner retests a regenerated candidate.
120
+ Create/update the pull request, satisfy required checks, squash-merge, fetch, and fast-forward local `main`.
77
121
 
78
- Open a pull request, satisfy the required checks, and squash-merge. Pull-request CI repeats the acceptance verification. A content-preserving squash changes the Git commit but not the package bytes, so the accepted hash remains valid.
122
+ Create the annotated prerelease tag, GitHub Prerelease, and exact tarball asset:
79
123
 
80
- ## Complete the GitHub source release
124
+ ```sh
125
+ npm run prerelease:release
126
+ ```
81
127
 
82
- From a clean, fast-forwarded `main` worktree whose `HEAD` exactly equals `origin/main`:
128
+ Publish npm through the repository-controlled channel command:
83
129
 
84
130
  ```sh
85
- npm run release
131
+ npm run prerelease:publish
86
132
  ```
87
133
 
88
- `npm run release` never pushes `main`. `release:publish` remains a compatibility alias. It verifies the interactive candidate acceptance, runs the complete project and version checks, requires successful exact-commit push runs for CI, CodeQL, Governance, and Scorecard, creates or verifies the annotated version tag, pushes only the version tag when absent, builds the npm tarball, creates or updates the final GitHub Release, uploads the tarball, and verifies the resulting state.
134
+ The command derives `dev`, `beta`, or `next` from the package version. `prepublishOnly` rejects an incorrect or implicit `latest` tag and rechecks the package, GitHub prerelease, exact commit, and candidate acceptance.
89
135
 
90
- To verify an already completed source release without changing anything:
136
+ ## 5. Activate the published prerelease and begin soak
137
+
138
+ From the exact accepted source checkout, the owner runs:
91
139
 
92
140
  ```sh
93
- npm run release:check
141
+ npm run prerelease:install -- --allow-worker-deploy
94
142
  ```
95
143
 
96
- To create GitHub Release records for historical remote tags that lack releases:
144
+ This command verifies that the npm registry tarball SHA-1/SHA-512 and dist-tag match the locally accepted candidate, installs that exact published version globally, updates the Worker and login daemon, verifies both versions, and writes an owner-only `npm-prerelease` activation record. The formal soak clock starts from this activation record, not from a local unpublished candidate.
145
+
146
+ Use the prerelease normally. Exercise the changed areas under real workloads. A crash, authorization anomaly, data-loss risk, repeated relay failure, incorrect service lifecycle, significant compatibility regression, or security/privacy defect is blocking.
147
+
148
+ When a blocking problem is found:
149
+
150
+ 1. fix it on a new branch;
151
+ 2. increment `beta.n` or `rc.n`;
152
+ 3. regenerate and reactivate the exact candidate;
153
+ 4. repeat live verification and prerelease publication;
154
+ 5. install the new published prerelease;
155
+ 6. restart the full minimum soak interval.
156
+
157
+ Never edit or replace an already published prerelease version.
158
+
159
+ ## 6. Record successful soak
160
+
161
+ After the minimum interval and adequate real use, the owner reports that no blocking issue remains. The coding agent records that explicit result with the exact phrase printed by the command, for example:
162
+
163
+ ```sh
164
+ npm run prerelease:soak:accept -- --confirm "I SOAK-TESTED machine-bridge-mcp 3.0.0-beta.1 FOR AT LEAST 7d WITH NO BLOCKING ISSUES"
165
+ ```
166
+
167
+ The command verifies:
168
+
169
+ - the installed activation came from the published npm prerelease, not a local candidate;
170
+ - the published npm hashes and dist-tag match the accepted tarball;
171
+ - the GitHub release is marked prerelease;
172
+ - the required elapsed time has passed;
173
+ - the promotion-content digest still matches;
174
+ - no blocking issue is recorded.
175
+
176
+ It writes `release-soak/v<stable-version>.json`. This record is reviewable, contains no private machine path, and is excluded from the npm package.
177
+
178
+ ## 7. Promote to stable
179
+
180
+ Create a stable-promotion branch. Change only synchronized release metadata from `x.y.z-beta.n` or `x.y.z-rc.n` to `x.y.z`, retain the same functional package content, add the tracked soak record, and update release wording only where it is normalized by the promotion digest.
181
+
182
+ Any source, runtime, dependency, packaged documentation, script, policy, browser-extension behavior, or Worker behavior change causes `release:soak:verify` to fail. Such a change requires a new prerelease and a new soak period.
183
+
184
+ Verify the promotion:
97
185
 
98
186
  ```sh
99
- npm run release:backfill
187
+ npm run release:soak:verify
188
+ npm run check
189
+ npm run release:candidate
100
190
  ```
101
191
 
102
- Backfill is historical metadata repair. It does not retroactively claim that releases predating the 1.2.8 acceptance policy had a recorded local acceptance.
192
+ The owner activates the exact stable candidate with the same persistent command:
103
193
 
104
- ## Publish npm
194
+ ```sh
195
+ npm run release:candidate:activate -- --allow-worker-deploy
196
+ ```
105
197
 
106
- Only after `release:check` succeeds:
198
+ The coding agent verifies the live stable candidate and records its exact acceptance. Then commit, push with `npm run github:push`, merge, and run:
107
199
 
108
200
  ```sh
109
- npm publish --access public
201
+ npm run release
202
+ npm run stable:publish
110
203
  ```
111
204
 
112
- `prepublishOnly` repeats the complete checks and GitHub synchronization check. npm publication remains a deliberate release-operator action and is not authorized by an ordinary source change.
205
+ `npm run release` creates the final annotated tag and GitHub Release only after the soak record, promotion digest, exact candidate acceptance, exact `origin/main`, and all required push-triggered checks pass. `stable:publish` always uses `latest` and repeats the same gates.
206
+
207
+ ## Rollback and recovery
208
+
209
+ Candidate and prerelease activation retain the previous global installation metadata, but rollback across a state or protocol migration is not assumed safe. If activation fails:
210
+
211
+ - preserve the state root and service logs;
212
+ - do not delete locks or edit versions by hand;
213
+ - inspect `machine-mcp service status`, `machine-mcp status`, and `machine-mcp doctor` using the intended runtime;
214
+ - fix forward when Worker/state protocol has changed;
215
+ - restore a complete pre-upgrade backup only when package, Worker, browser extension, service definition, and local state can be restored as one unit.
216
+
217
+ The activation state machine verifies candidate relay readiness before service handoff and cleans up its temporary runtime and locks on installation failure. A failure after the Worker has changed is reported explicitly rather than hidden by an unsafe automatic downgrade.
113
218
 
114
- ## Authentication and external controls
219
+ ## External credentials and controls
115
220
 
116
- Required local access:
221
+ Live npm publication, global installation, and Worker/service replacement require explicit owner authorization. Repository automation does not infer that authorization from a version change.
117
222
 
118
- - Git push access to `origin` for the accepted branch and version tag;
119
- - an authenticated GitHub CLI session with pull-request and release permission;
120
- - an npm account that owns the package or has maintainer permission for the separate registry publication step.
223
+ Required external controls remain:
121
224
 
122
- GitHub Actions remains a required cross-platform boundary. Missing, pending, cancelled, skipped, failed, pull-request-only, stale, or wrong-commit runs do not satisfy release publication. Observed local candidate acceptance and successful GitHub checks are complementary evidence; neither substitutes for the other.
225
+ - authenticated local `git`/`gh` access;
226
+ - npm package-owner or maintainer access;
227
+ - Cloudflare account protection;
228
+ - successful exact-commit CI, CodeQL, Governance, and Scorecard;
229
+ - protected npm publication credentials or trusted publishing when configured.
123
230
 
124
- The preferred registry target remains npm trusted publishing from a narrowly scoped GitHub Actions workflow using OIDC and a protected GitHub environment. Enabling it requires package-owner configuration in npm and a reviewed workflow change. Until then, never place an npm token in repository files, workflow YAML, logs, local acceptance records, or project notes.
231
+ Never place npm, GitHub, Cloudflare, OAuth, account, or device credentials in source files, workflow YAML, acceptance/soak records, logs, screenshots, or project notes.