machine-bridge-mcp 1.2.10 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (73) hide show
  1. package/CHANGELOG.md +23 -1
  2. package/CONTRIBUTING.md +1 -1
  3. package/README.md +11 -6
  4. package/browser-extension/manifest.json +2 -2
  5. package/docs/ARCHITECTURE.md +23 -11
  6. package/docs/AUDIT.md +37 -3
  7. package/docs/CLIENTS.md +2 -2
  8. package/docs/ENGINEERING.md +2 -2
  9. package/docs/GETTING_STARTED.md +1 -1
  10. package/docs/LOCAL_AUTHORIZATION.md +111 -0
  11. package/docs/LOGGING.md +7 -5
  12. package/docs/MULTI_ACCOUNT.md +5 -5
  13. package/docs/OPERATIONS.md +33 -7
  14. package/docs/OVERVIEW.md +12 -8
  15. package/docs/PROJECT_STANDARDS.md +1 -1
  16. package/docs/RELEASING.md +4 -4
  17. package/docs/TESTING.md +9 -2
  18. package/docs/THREAT_MODEL.md +7 -6
  19. package/docs/TOOL_REFERENCE.md +4 -4
  20. package/docs/UPGRADING.md +10 -6
  21. package/package.json +8 -2
  22. package/scripts/check-plan.mjs +5 -0
  23. package/scripts/check-runner.mjs +75 -0
  24. package/scripts/coverage-check.mjs +1 -1
  25. package/scripts/github-backlog.mjs +116 -0
  26. package/scripts/github-push.mjs +4 -0
  27. package/scripts/local-release-acceptance.mjs +2 -2
  28. package/scripts/release-acceptance.mjs +36 -17
  29. package/scripts/run-checks.mjs +11 -21
  30. package/src/local/account-admin.mjs +28 -3
  31. package/src/local/bounded-output.mjs +20 -3
  32. package/src/local/cli-approval.mjs +117 -0
  33. package/src/local/cli-options.mjs +8 -2
  34. package/src/local/cli.mjs +13 -2
  35. package/src/local/device-identity.mjs +108 -0
  36. package/src/local/errors.mjs +5 -1
  37. package/src/local/execution-limits.mjs +6 -1
  38. package/src/local/operation-authorization.mjs +366 -0
  39. package/src/local/operation-risk.mjs +221 -0
  40. package/src/local/operation-state-lock.mjs +92 -0
  41. package/src/local/process-execution.mjs +94 -14
  42. package/src/local/process-output-stream.mjs +82 -0
  43. package/src/local/process-result-projection.mjs +25 -0
  44. package/src/local/process-sessions.mjs +37 -51
  45. package/src/local/relay-connection.mjs +12 -5
  46. package/src/local/runtime-relay.mjs +13 -5
  47. package/src/local/runtime.mjs +14 -7
  48. package/src/local/secure-file.mjs +24 -4
  49. package/src/local/state.mjs +9 -3
  50. package/src/local/tool-executor.mjs +4 -2
  51. package/src/local/tools.mjs +4 -9
  52. package/src/local/worker-deployment.mjs +4 -3
  53. package/src/local/worker-secret-file.mjs +2 -1
  54. package/src/shared/admin-auth.d.mts +10 -0
  55. package/src/shared/admin-auth.mjs +36 -0
  56. package/src/shared/daemon-auth.d.mts +20 -0
  57. package/src/shared/daemon-auth.mjs +55 -0
  58. package/src/shared/result-projection.d.mts +6 -0
  59. package/src/shared/result-projection.json +4 -0
  60. package/src/shared/result-projection.mjs +50 -0
  61. package/src/shared/server-metadata.json +1 -0
  62. package/src/shared/tool-catalog.json +4 -4
  63. package/src/worker/account-admin.ts +73 -4
  64. package/src/worker/daemon-auth.ts +205 -0
  65. package/src/worker/daemon-sockets.ts +15 -2
  66. package/src/worker/errors.ts +18 -4
  67. package/src/worker/index.ts +59 -10
  68. package/src/worker/mcp-jsonrpc.ts +4 -2
  69. package/src/worker/nonce-store.ts +79 -0
  70. package/src/worker/oauth-controller.ts +11 -6
  71. package/src/worker/oauth-refresh-families.ts +172 -0
  72. package/src/worker/oauth-state.ts +48 -3
  73. package/src/worker/oauth-tokens.ts +49 -40
package/CHANGELOG.md CHANGED
@@ -1,5 +1,27 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.0.0 - 2026-07-21
4
+
5
+ ### Device identity and usable local transaction authorization
6
+
7
+ - Replace the long-lived daemon bearer secret with an enrolled P-256 device identity. Every WebSocket upgrade now requires a signed short-lived preflight bound to Worker origin, package version, nonce, and timestamp. Its nonce is consumed once through bounded transactional state, preventing both unauthenticated candidate churn and replay of a captured preflight. The Worker then issues a fresh challenge whose signature also binds the daemon instance before tools may be advertised or a verified incumbent may be replaced.
8
+ - Fix Windows authorization tests to avoid asserting POSIX mode bits on NTFS, and record the exact expiring CodeQL assessment for the mandatory 256-bit machine-generated account credential verifier.
9
+ - Let authenticated owner sessions execute directly within the daemon policy ceiling without terminal approval, while retaining local capability leases for high-impact operations from delegated non-owner accounts. The canonical `full` profile is unchanged. Workspace-contained reads and ordinary edits, project inspection, Git, and diagnostics remain automatic. Because the extension controls an existing logged-in browser profile, one `browser-session` lease covers profile reads and actions instead of prompting per click; registered-resource input and file upload retain an independent `data-export` boundary. Compound operations must satisfy every applicable scope, so browser uploads require both scopes and protected-resource desktop input requires both application control and data export. Remote process control and continuation, outside-workspace or sensitive reads/writes, managed-job listing/output/mutation, credential operations, and application inspection/control run uninterrupted once the bound account and OAuth client hold the required time-bounded scopes. The CLI supports scoped leases and an explicit at-most-eight-hour `--full` automation window.
10
+ - Store only bounded identity/scope/time metadata and SHA-256 target digests in owner-only approval state; validate every persisted record and fail closed on malformed state. Canonicalize every patch destination, including `Move to` and symbolic-link ancestors, before classifying it, and reject final symbolic-link overwrites so classification and execution share the real target. Serialize daemon and CLI approval mutations with an owner-only process-identity lock so concurrent pending, grant, approval, revocation, and clear operations cannot silently overwrite one another. A catalog-completeness test forces every current and future tool through explicit risk review. Relay authorization now carries the authenticated OAuth client identity so leases cannot cross clients.
11
+ - Reduce access-token lifetime from 30 days to 15 minutes. Refresh tokens now have a 14-day idle limit and a 30-day family limit; reuse of a rotated refresh token revokes the complete family, including active access tokens. Consumed-token and revoked-family replay records are hard-bounded, oldest-first pruned, and record-schema validated. The first device enrollment also rotates the deployment token version so pre-2.0 credentials cannot survive the upgrade.
12
+ - Replace the account-management network bearer with per-request HMAC-SHA-256 authentication bound to Worker origin, HTTP method, path, body hash, timestamp, and random nonce. Transactional Durable Object nonce state rejects replay and fails closed when malformed; the local CLI workflow is unchanged.
13
+ - Split operation-risk classification, lease persistence, daemon authentication, refresh-family persistence, and administration authentication into focused domain modules with behavior-level tests. Document capability ceilings, transaction authorization, upgrade/rollback boundaries, residual same-user risk, and the controls deliberately left external.
14
+ - This is a coordinated Worker/daemon protocol upgrade. Version 2.0.0 components must be deployed and run together; the removed daemon bearer protocol is not retained as a compatibility bypass.
15
+
16
+ ## 1.2.11 - 2026-07-20
17
+
18
+ ### Bounded output continuation and repository backlog gate
19
+
20
+ - Stop large one-shot process responses from overwhelming MCP hosts. `run_process`, `run_local_command`, and `exec_command` now inline only a bounded stdout/stderr preview, retain up to 1 MiB per stream in a temporary completed process session, and return an `output_session_id` for paged `read_process` continuation. Nonzero exits keep their human error message bounded while preserving structured continuation details.
21
+ - Avoid protocol-level payload duplication by replacing the text mirror of large object results with a compact field summary while retaining the authoritative object in `structuredContent`. The fast/platform/full check runner suppresses successful child noise, preserves bounded head/tail diagnostics on failure, and supports explicit `MBM_CHECK_VERBOSE=1` streaming. Coverage cleanup now retries concurrent late V8 coverage-file writes instead of failing after the thresholds already passed.
22
+ - Resolve the release-acceptance file race by opening no-follow descriptors first, checking descriptor/path identity, and reading acceptance records and tarballs through bounded descriptors. Remove the temporary CodeQL exception and add deterministic path-replacement and symbolic-link regressions.
23
+ - Add a guarded GitHub backlog pre-push check. `npm run github:push` now blocks unrelated open pull requests and open issues not covered by a standard closing keyword in the current branch commits; the current branch PR remains updateable. Add unit and integration coverage for output paging, compact MCP projection, bounded check diagnostics, and backlog enforcement.
24
+
3
25
  ## 1.2.10 - 2026-07-20
4
26
 
5
27
  ### Relay interruption recovery
@@ -11,7 +33,7 @@
11
33
  ## 1.2.9 - 2026-07-18
12
34
 
13
35
  - Repair cross-platform release infrastructure found by PR CI: the layered check runner now invokes the pinned npm CLI through Node instead of spawning `npm.cmd`, and `release:accept` computes and locally validates the portable Git-content digest through a temporary index so CI can verify accepted package content across merge commits without mutating the maintainer index.
14
- - Correct the release handoff: add `npm run release:candidate:start -- --allow-worker-deploy` for an isolated local installation plus explicitly authorized in-place candidate Worker deployment, require owner-started and agent-observed live verification before acceptance, allow the coding agent to record acceptance and complete commit/push/tag/GitHub Release work, and add `npm run release` as the canonical source-release command. npm publication and Worker deployment remain owner-operated.
36
+ - Correct the release handoff: add `npm run release:candidate:start -- --allow-worker-deploy` for an isolated local installation plus explicitly authorized in-place candidate Worker deployment, require owner-authorized and agent-operated live verification before acceptance, allow the coding agent to record acceptance and complete commit/push/tag/GitHub Release work, and add `npm run release` as the canonical source-release command. npm publication and Worker deployment remain owner-operated.
15
37
 
16
38
  ### Architecture headroom, verification feedback, and threat model
17
39
 
package/CONTRIBUTING.md CHANGED
@@ -44,7 +44,7 @@ Repository-only infrastructure changes, such as a `.github/` workflow update, do
44
44
  6. give the repository owner `npm run release:candidate:start -- --allow-worker-deploy`; the owner explicitly authorizes the in-place candidate Worker deployment, starts the exact candidate locally, and leaves it running;
45
45
  7. verify the live candidate through Machine Bridge, including Worker version/hash, remote health, relay readiness, exact local version, and representative functionality relevant to the change;
46
46
  8. after observed verification succeeds, have the coding agent run the exact `release:accept` command printed by the candidate tool, creating `release-acceptance/v<version>.json`;
47
- 9. commit the acceptance record and push the clean non-`main` branch only with `npm run github:push`.
47
+ 9. resolve every open issue and pull request before pushing. The current branch may cover an issue only with a standard closing keyword such as `Closes #47`; unrelated open PRs block the push. Commit the acceptance record and push the clean non-`main` branch only with `npm run github:push`, which paginates and enforces that backlog contract.
48
48
 
49
49
  Automated checks do not authorize step 8. The coding agent may record acceptance only after it has observed the owner-started candidate operating successfully. Any packaged-file change after acceptance changes the npm tarball hash and requires a regenerated candidate and another observed live verification.
50
50
 
package/README.md CHANGED
@@ -3,7 +3,7 @@
3
3
  `machine-bridge-mcp` exposes one local workspace to MCP clients through a shared, policy-controlled runtime. Hosted clients connect through an OAuth-protected Cloudflare Worker relay; local clients may launch the same runtime over stdio.
4
4
 
5
5
  > [!WARNING]
6
- > The default `full` profile can read and modify any file available to the local OS user, run shell commands, inherit the parent environment, and control the Chromium profile containing the packaged extension. It is **not** an operating-system sandbox. Use a narrower profile or an isolated OS account, VM, or container for untrusted clients, repositories, or instructions.
6
+ > The default `full` profile retains every local-user capability: unrestricted files, shell commands, the parent environment, browser automation, applications, resources, and jobs. It is **not** an operating-system sandbox. An authenticated owner may use that ceiling directly without terminal approval. Delegated non-owner accounts require bounded local capability leases for high-impact operations. Use a narrower profile or an isolated OS account, VM, or container for mutually untrusted workloads.
7
7
 
8
8
  ## Choose a path
9
9
 
@@ -24,6 +24,7 @@ Support boundaries are defined in [SUPPORT.md](SUPPORT.md). Repository participa
24
24
  - policy profiles with shared local/Worker enforcement contracts;
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
+ - device-signed daemon transport, interruption-free authenticated owner authority, and account/client-bound capability leases for delegated high-impact transactions;
27
28
  - structured, privacy-conscious lifecycle events and stable error codes;
28
29
  - fail-closed state, lock, release, package, and supply-chain checks.
29
30
 
@@ -33,7 +34,8 @@ The remote Worker authenticates and relays requests. It cannot directly read loc
33
34
  Hosted MCP client
34
35
  -> HTTPS + OAuth 2.1 / PKCE
35
36
  -> Cloudflare Worker + Durable Object
36
- -> authenticated outbound WebSocket
37
+ -> P-256 device-authenticated outbound WebSocket
38
+ -> local transaction authorization
37
39
  -> local runtime
38
40
 
39
41
  Local MCP client
@@ -153,6 +155,8 @@ The shared source of truth is `src/shared/policy-contract.json`. The generated m
153
155
 
154
156
  For remote calls, `server_info.authorization.effective_policy` and `effective_tools` are authoritative. Daemon policy and tools describe only the local capability ceiling before account-role and host-side filtering.
155
157
 
158
+ `full` is the daemon capability ceiling. An authenticated owner account may exercise it directly without approval IDs or terminal commands. Delegated reviewer, editor, and operator accounts remain constrained by their role profile, and their high-impact operations require account/client-bound, time-bounded local leases. Use `machine-mcp approval` only when administering those delegated-account leases. See [local transaction authorization](docs/LOCAL_AUTHORIZATION.md).
159
+
156
160
  ## Browser and application automation
157
161
 
158
162
  Under canonical `full`, Machine Bridge can discover and operate supported local applications and can control the Chromium profile into which the packaged extension is loaded.
@@ -191,6 +195,7 @@ machine-mcp doctor
191
195
  machine-mcp workspace show|set|reset
192
196
  machine-mcp service status|install|start|stop|uninstall
193
197
  machine-mcp account list|add|role|enable|disable|rotate-password|remove
198
+ machine-mcp approval list|approve|grant|revoke|clear
194
199
  machine-mcp browser status|setup|pair|path
195
200
  machine-mcp resource add|list|check|remove
196
201
  machine-mcp job submit|inspect|approve|list|read|cancel
@@ -217,7 +222,7 @@ Major groups include:
217
222
 
218
223
  ```sh
219
224
  npm ci
220
- npm run check:fast # local feedback: static, unit, architecture, policy, lifecycle
225
+ npm run check:fast # quiet-success local feedback; set MBM_CHECK_VERBOSE=1 only for live child logs
221
226
  npm run check # complete suite, equivalent to check:full
222
227
  npm run worker:dry-run
223
228
  npm audit --audit-level=high
@@ -235,13 +240,13 @@ A package change requires a version bump, matching changelog section, complete v
235
240
 
236
241
  ```sh
237
242
  npm run release:candidate
238
- # The repository owner runs this and leaves the candidate in the foreground:
243
+ # After explicit owner authorization in the conversation, the coding agent starts and verifies:
239
244
  npm run release:candidate:start -- --allow-worker-deploy
240
- # The coding agent verifies the live candidate, records acceptance, pushes, merges, then runs:
245
+ # The coding agent records acceptance, pushes, merges, then runs:
241
246
  npm run release
242
247
  ```
243
248
 
244
- The owner action is to explicitly authorize and start the exact candidate, including an in-place update of the configured same-name Worker when required. The coding agent must observe the deployed Worker version/hash, remote health, relay readiness, connected candidate version, and representative functionality before recording acceptance; automated checks alone are insufficient. npm publication and Worker deployment remain separate owner-operated live actions. See [docs/RELEASING.md](docs/RELEASING.md).
249
+ The owner action is to explicitly authorize the exact candidate and any in-place update of the configured same-name Worker in the active conversation; the coding agent performs the startup and verification through Machine Bridge. The coding agent must observe the deployed Worker version/hash, remote health, relay readiness, connected candidate version, and representative functionality before recording acceptance; automated checks alone are insufficient. npm publication and Worker deployment remain separate owner-operated live actions. See [docs/RELEASING.md](docs/RELEASING.md).
245
250
 
246
251
  ## Documentation
247
252
 
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "manifest_version": 3,
3
3
  "name": "Machine Bridge Browser",
4
- "version": "1.2.10",
4
+ "version": "2.0.0",
5
5
  "description": "Connects the current Chromium browser profile to the local Machine Bridge runtime for user-authorized automation.",
6
6
  "permissions": [
7
7
  "tabs",
@@ -30,5 +30,5 @@
30
30
  "action": {
31
31
  "default_title": "Machine Bridge Browser"
32
32
  },
33
- "version_name": "1.2.10"
33
+ "version_name": "2.0.0"
34
34
  }
@@ -93,6 +93,8 @@ Policy revision 5 is loaded from one shared JSON contract by both local and Work
93
93
 
94
94
  Named profiles are normalized to their complete capability sets. In particular, `full` always means write enabled, shell execution, unrestricted paths, complete parent environment, absolute-path display, and every catalog tool. CLI flags that alter an individual capability deliberately change the profile identity to `custom`. Policy revision 5 defines the only accepted persisted policy shape. Named profiles are normalized to their canonical capability sets, and persisted data from another revision is rejected rather than interpreted. Compound availability requirements come from the shared contract.
95
95
 
96
+ Remote authority has a separate final layer. After the Worker intersects account role with daemon policy and the local runtime rechecks that role, `OperationAuthorizer` permits an authenticated owner directly within the daemon policy ceiling. For delegated non-owner accounts it classifies the normalized operation effect. Workspace-contained reads and ordinary edits, project inspection, and non-content browser/application status proceed automatically. Higher-impact delegated effects may require several independent scopes: browser upload combines profile access with data export, application resource input combines application control with data export, and external sensitive paths combine location and sensitivity scopes. Existing account/client-bound leases may satisfy those scopes independently; only missing scopes become a short-lived local pending approval. A `full` lease covers every transaction scope for at most eight hours but does not alter the canonical saved `full` policy. Risk classification is a pure domain module. Lease persistence is a separate owner-only, bounded, atomically replaced state boundary, and an owner-only process-identity lock serializes daemon and CLI mutations so concurrent approval activity cannot lose updates. Every patch destination is canonicalized through the execution resolver before classification. See [LOCAL_AUTHORIZATION.md](LOCAL_AUTHORIZATION.md).
97
+
96
98
  The full-only `generate_ssh_key_resource` operation is implemented locally. The Worker only filters and relays its shared catalog definition. Local generation uses `ssh-keygen`, verifies public/private correspondence, registers the private file through the same owner-only state transaction as the CLI, and rolls back a newly created pair if state persistence fails.
97
99
 
98
100
  `full-test` constructs a local runtime with canonical full policy and executes real operations in disposable directories. It is an acceptance test for the local implementation, not a probe that changes remote systems or bypasses host policy.
@@ -114,7 +116,12 @@ All requests for a deployed Worker route to one named Durable Object. It owns:
114
116
 
115
117
  The Worker verifies OAuth, validates MCP envelopes and optional protocol headers, converts `tools/call` into WebSocket messages, correlates cancellation by access-token hash and JSON-RPC ID, and formats text/structured/image results. Pending calls also bind the incoming request `AbortSignal`: an HTTP client disconnect removes the pending indexes and sends a best-effort daemon cancellation. The deployment contract explicitly enables Cloudflare `enable_request_signal` and `request_signal_passthrough` so the signal reaches the named Durable Object. It has no local filesystem or process API.
116
118
 
117
- The primary OAuth store separates client registrations and named accounts from authorization codes and access-token records. A separate Durable Object key owns refresh-token records so refresh support can be added without rewriting the live primary-store schema. A `client_id` identifies an MCP application and redirect URIs; account records identify the authorized human or service identity. Codes, access tokens, and refresh tokens bind client ID, account ID, account version, role, scope, resource, token version, and expiration. Only hashes of bearer tokens are persisted, and every public-client refresh invalidates the presented refresh token while returning a replacement. The Worker intersects the role with the active daemon policy before advertising or relaying tools, and the local runtime validates the relayed role again. One bridge-specific Durable Object and one local runtime remain the normal topology for a workspace/trust domain; see [MULTI_ACCOUNT.md](MULTI_ACCOUNT.md).
119
+
120
+ ### Daemon device authentication
121
+
122
+ The local daemon owns a P-256 device identity. Worker deployment receives only the public JWK; the private JWK remains in owner-only local state. Every WebSocket attempt first carries a short-lived signed preflight transcript bound to Worker origin, package version, nonce, and timestamp. The nonce is consumed once through bounded Durable Object transaction state before upgrade, so neither an unauthenticated client nor a captured signed preflight can accumulate or replace daemon candidates. After upgrade, the Worker issues a fresh challenge and accepts daemon tools only after a second signature binds the challenge, Worker origin, package version, daemon instance ID, and timestamp. End-to-end readiness still requires an ordinary relay probe before a verified candidate replaces the incumbent. The removed long-lived `X-Bridge-Token` protocol is not retained as a fallback.
123
+
124
+ The primary OAuth store separates client registrations and named accounts from authorization codes and access-token records. A separate versioned Durable Object key owns refresh-token families, consumed-token markers, and family revocation. A `client_id` identifies an MCP application and redirect URIs; account records identify the authorized human or service identity. Codes and tokens bind client ID, account ID, account version, role, scope, resource, deployment token version, family identity, and expiration where applicable. Only hashes of bearer tokens are persisted. Access tokens last fifteen minutes; refresh tokens rotate, expire after fourteen idle days or thirty absolute family days, and reuse of a consumed refresh token revokes the complete family, including active access tokens. The Worker carries the authenticated client ID with every relayed call so local leases cannot cross clients. One bridge-specific Durable Object and one local runtime remain the normal topology for a workspace/trust domain; see [MULTI_ACCOUNT.md](MULTI_ACCOUNT.md).
118
125
 
119
126
  The daemon attachment deliberately omits workspace path/name/hash and process ID. Explicit authenticated tools may return workspace metadata according to local path-display policy.
120
127
 
@@ -131,14 +138,15 @@ The platform adapters normalize launchd, systemd, and Windows Scheduled Task ope
131
138
  ```mermaid
132
139
  flowchart LR
133
140
  C[Remote MCP client] -->|HTTPS + OAuth bearer token| W[Worker / Durable Object]
134
- W -->|authenticated bounded WebSocket calls| R[Local runtime]
141
+ W -->|device-authenticated bounded WebSocket calls| R[Local runtime]
135
142
  L[Local MCP client] -->|stdio JSON-RPC| R
136
- R -->|canonical workspace tools| F[Selected workspace]
137
- R -->|optional direct/shell processes| P[Local user / OS / network]
138
- R -->|structured Accessibility actions| A[Local applications]
139
- R -->|authenticated loopback broker| B[Existing-profile browser extension]
143
+ R --> T[Local transaction gate]
144
+ T -->|canonical workspace tools| F[Selected workspace]
145
+ T -->|leased direct/shell processes| P[Local user / OS / network]
146
+ T -->|leased Accessibility actions| A[Local applications]
147
+ T -->|authenticated loopback broker| B[Existing-profile browser extension]
140
148
  B -->|DOM and visible UI authority| WB[Web pages and browser tabs]
141
- R -->|durable accepted plan| J[Detached managed-job runner]
149
+ T -->|leased durable accepted plan| J[Detached managed-job runner]
142
150
  J -->|private copies| LR[Local resource files]
143
151
  J -->|argv/stdin/env| P
144
152
  CLI[CLI + owner-only state] --> W
@@ -185,7 +193,7 @@ The workspace is canonicalized and compared with targets through consistent plat
185
193
 
186
194
  Path behavior is profile-dependent. The default `full` profile permits unrestricted direct filesystem paths and returns absolute paths. The `agent`, `edit`, and `review` profiles enforce canonical workspace containment and return workspace-relative paths. Error strings redact canonical and common platform-alias forms of workspace, runtime, and home paths whenever absolute path display is disabled. Access scope and path display are independent: unrestricted access with path display disabled returns relative workspace paths and opaque external-path identifiers.
187
195
 
188
- Symbolic-link destinations and non-regular write targets are rejected. Existing bounded reads add final-component `O_NOFOLLOW` where supported. Recursive walkers do not follow symbolic-link directories. Because portable Node.js lacks descriptor-relative `openat` traversal for every operation, parent-directory replacement by hostile same-user code remains an external-isolation concern.
196
+ Symbolic-link destinations and non-regular write targets are rejected. Write paths always canonicalize their nearest existing ancestor, including under `full`, so transaction classification and execution agree on the real destination while unrestricted-path authority remains intact. Patch move destinations are included in that classification. Existing bounded reads add final-component `O_NOFOLLOW` where supported. Recursive walkers do not follow symbolic-link directories. Because portable Node.js lacks descriptor-relative `openat` traversal for every operation, parent-directory replacement by hostile same-user code remains an external-isolation concern.
189
197
 
190
198
  ## Mutation model
191
199
 
@@ -213,7 +221,11 @@ This is a process-level transaction, not a filesystem-wide atomic transaction ac
213
221
 
214
222
  The default `full` profile passes the complete parent environment. Isolated environment mode, used by the narrower named profiles unless overridden, creates private runtime HOME, temporary, and cache directories and passes only a small set of path/locale/platform variables. It reduces accidental credential inheritance but cannot prevent explicit access to known filesystem paths, credential stores, network services, or other user resources.
215
223
 
216
- `execution-limits.mjs` is the shared source for local tool-call concurrency, one-shot process timeout/stdin/output limits, and process-session count/stdin/output/retention limits. `server_info.runtime.execution_guardrails` reports those enforced limits together with explicit `not-enforced` values for CPU quota, memory quota, and network isolation. One-shot calls have bounded output and timeouts. Startup-lock waits, daemon takeover, process-session reads, managed-job recovery handoff, browser/page waits, application-cache freshness, and in-memory duration metrics use monotonic elapsed time, so wall-clock correction cannot extend or prematurely terminate their configured duration. Persisted timestamps and retention/credential expiry continue to use wall time. Process sessions retain bounded byte buffers with monotonic offsets, accept bounded stdin, support short output/exit waits, and are capped per runtime. Valid UTF-8 is returned as text; byte slices that are not valid UTF-8 also include lossless base64 data. Session IDs are random. Sessions are memory-only and are killed on runtime stop, remote disconnect, or daemon replacement.
224
+ `execution-limits.mjs` is the shared source for local tool-call concurrency, one-shot process timeout/stdin/output limits, and process-session count/stdin/output/retention limits. `server_info.runtime.execution_guardrails` reports those enforced limits together with explicit `not-enforced` values for CPU quota, memory quota, and network isolation. Public one-shot commands inline at most 32 KiB per stream. When either stream exceeds that preview, the runtime keeps up to 1 MiB per stream in a closed in-memory process session for thirty minutes and returns an `output_session_id`; `read_process` then reads monotonic byte-offset pages. The oldest exited session is evicted before an active session is refused, so continuation retention is explicitly best effort rather than durable. The continuation stores command basename and cwd metadata but not argv or shell text. It is memory-only and disappears on runtime stop or daemon replacement.
225
+
226
+ Large object results use `structuredContent` as the authoritative representation. The human text mirror is complete only below the shared 16 KiB projection threshold; above it, both local stdio and the Worker return the same compact byte-count/field summary instead of serializing the object a second time. `process-result-projection.mjs`, `process-output-stream.mjs`, and the shared result projector keep lifecycle, byte retention, and MCP presentation as separate boundaries.
227
+
228
+ Startup-lock waits, daemon takeover, process-session reads, managed-job recovery handoff, browser/page waits, application-cache freshness, and in-memory duration metrics use monotonic elapsed time, so wall-clock correction cannot extend or prematurely terminate their configured duration. Persisted timestamps and retention/credential expiry continue to use wall time. Process sessions retain bounded byte buffers with monotonic offsets, accept bounded stdin, support short output/exit waits, and are capped per runtime. Valid UTF-8 is returned as text; byte slices that are not valid UTF-8 also include lossless base64 data. Head/tail previews trim incomplete UTF-8 boundary code points instead of introducing replacement characters. Session IDs are random. Running sessions are killed on runtime stop, remote disconnect, or daemon replacement.
217
229
 
218
230
  Child processes run in a separate process group where supported. Timeout, cancellation, disconnect, and replacement send termination to process trees, with a referenced forced-escalation timer that remains alive even when the direct child exits before a resistant descendant. Windows uses tree-aware task termination.
219
231
 
@@ -253,7 +265,7 @@ Removal first acquires a state-root maintenance lock that blocks new profile/sta
253
265
 
254
266
  OAuth metadata is pruned on access. Expired authorization codes, access tokens, refresh tokens, old throttling records, and inactive clients without active credentials are removed. Account disablement, role or password changes, removal, and deployment-wide token-version rotation make both token classes unusable; stale refresh records are deleted when the refresh store is next read. Source identities are deployment-keyed HMAC values, not stored source addresses or reversible unsalted hashes.
255
267
 
256
- Browser-origin handling separates CORS response sharing from protocol authentication. Preflight succeeds only for the Worker's own origin, a fixed first-party set for ChatGPT (`https://chatgpt.com`, `https://chat.openai.com`) and Grok (`https://grok.com`, `https://x.com`), or optional exact comma-separated additions from `MBM_ALLOWED_ORIGINS`; wildcard and `null` origins are not granted CORS access. Actual requests are routed without using `Origin` as an authentication boundary, and `Access-Control-Allow-Origin` is added only when that exact predicate passes. This permits OAuth top-level navigation and form submission from opaque or client-specific containers while exact redirect/resource binding, PKCE, account credentials, bearer tokens, and private admin/daemon secrets enforce authority. The authorization document's `form-action` policy is generated only after request validation and contains `'self'` plus the exact origin of that validated redirect URI. If that validated origin is Microsoft `consent.azure-apim.net`, the policy also admits `https://*.consent.azure-apim.net` and the exact `https://copilotstudio.microsoft.com` origin; Power Platform receives the authorization code at its global consent endpoint, redirects to a regional endpoint, and then returns to Copilot Studio, while Chromium applies `form-action` across the complete redirect chain. No other callback receives either exception. This allows successful callback navigation without opening form submission to unrelated origins. Hosted Claude remote connectors originate from Anthropic infrastructure and Copilot Studio uses Power Platform connectivity, so their server-to-server requests do not require browser CORS entries; adding those brands to the browser allowlist would expand response sharing without enabling either integration.
268
+ Browser-origin handling separates CORS response sharing from protocol authentication. Preflight succeeds only for the Worker's own origin, a fixed first-party set for ChatGPT (`https://chatgpt.com`, `https://chat.openai.com`) and Grok (`https://grok.com`, `https://x.com`), or optional exact comma-separated additions from `MBM_ALLOWED_ORIGINS`; wildcard and `null` origins are not granted CORS access. Actual requests are routed without using `Origin` as an authentication boundary, and `Access-Control-Allow-Origin` is added only when that exact predicate passes. This permits OAuth top-level navigation and form submission from opaque or client-specific containers while exact redirect/resource binding, PKCE, account credentials, short-lived bearer tokens, signed administration requests, and the daemon device identity enforce authority. The authorization document's `form-action` policy is generated only after request validation and contains `'self'` plus the exact origin of that validated redirect URI. If that validated origin is Microsoft `consent.azure-apim.net`, the policy also admits `https://*.consent.azure-apim.net` and the exact `https://copilotstudio.microsoft.com` origin; Power Platform receives the authorization code at its global consent endpoint, redirects to a regional endpoint, and then returns to Copilot Studio, while Chromium applies `form-action` across the complete redirect chain. No other callback receives either exception. This allows successful callback navigation without opening form submission to unrelated origins. Hosted Claude remote connectors originate from Anthropic infrastructure and Copilot Studio uses Power Platform connectivity, so their server-to-server requests do not require browser CORS entries; adding those brands to the browser allowlist would expand response sharing without enabling either integration.
257
269
 
258
270
  ## Observability
259
271
 
@@ -265,7 +277,7 @@ Cloudflare sampling is size control rather than an audit log. The project intent
265
277
 
266
278
  ## Release integrity
267
279
 
268
- Repository-local automated checks are necessary but cannot prove that the maintainer's ordinary installation path works. `scripts/local-release-acceptance.mjs` builds the exact npm tarball, while `scripts/start-release-candidate.mjs` installs that tarball into an ignored isolated prefix and starts it in the foreground. The helper requires `--allow-worker-deploy` because normal startup may update the configured same-name Worker before relay verification. The repository owner starts the candidate; the coding agent verifies its live Worker version/hash, health, relay connection, local version, readiness, and representative behavior through Machine Bridge before recording both npm hashes outside the package. `scripts/github-push.mjs`, pull-request CI, and `scripts/github-release.mjs` rebuild the package and reject any mismatch. The release helper also requires `HEAD === origin/main`; it cannot silently push `main`.
280
+ Repository-local automated checks are necessary but cannot prove that the maintainer's ordinary installation path works. `scripts/local-release-acceptance.mjs` builds the exact npm tarball, while `scripts/start-release-candidate.mjs` installs that tarball into an ignored isolated prefix and starts it in the foreground. The helper requires `--allow-worker-deploy` because normal startup may update the configured same-name Worker before relay verification. After explicit owner authorization in the active conversation, the coding agent starts the candidate and verifies its live Worker version/hash, health, relay connection, local version, readiness, and representative behavior through Machine Bridge before recording both npm hashes outside the package. `scripts/github-push.mjs`, pull-request CI, and `scripts/github-release.mjs` rebuild the package and reject any mismatch. The release helper also requires `HEAD === origin/main`; it cannot silently push `main`.
269
281
 
270
282
  Cross-platform evidence remains independent. `scripts/github-release.mjs` queries CI, CodeQL, Governance, and Scorecard for the exact `origin/main` commit and requires the newest push-triggered run for each workflow to be completed with `success` before it creates or verifies a version tag, GitHub Release, or package asset. Pull-request runs, older successful runs, pending runs, and successful runs for another SHA do not satisfy the gate. The workflow selection policy is isolated in `scripts/release-ci.mjs` and tested independently.
271
283
 
package/docs/AUDIT.md CHANGED
@@ -1,5 +1,37 @@
1
1
  # Security and privacy audit notes
2
2
 
3
+ ## 2026-07-21 version 2.0.0 remote-authority audit
4
+
5
+ The critical question was whether canonical `full` could retain its automation value without allowing one long-lived remote bearer to remain equivalent to unattended local-user control. The pre-2.0 implementation correctly intersected account role with the daemon capability ceiling, but the final authorization decision was still tool-granular: once `exec_command`, unrestricted paths, browser tools, or managed jobs were exposed, the original arguments reached the handler without a local effect-level transaction boundary. The daemon WebSocket also authenticated with one long-lived shared bearer, access tokens lasted thirty days, and account administration sent its long-lived secret directly as a network bearer.
6
+
7
+ Version 2.0.0 separates four authorities that were previously too close: daemon device identity, OAuth account/client identity, the canonical capability ceiling, and delegated local transaction authority. `full` remains the complete catalog, unrestricted local-user paths, shell, browser/application automation, managed jobs, absolute paths, and complete parent environment. The explicit owner decision is that an authenticated owner uses this ceiling without a second terminal approval step. Delegated accounts retain lease-based transaction control. Normal project work remains deliberately frictionless: workspace-contained reads and edits, project inspection, Git, and diagnostics are automatic. A later catalog-wide review rejected automatic browser reads: the extension controls the user's existing logged-in profile, so tab URLs, titles, source, screenshots, form input, and actions now share one reusable `browser-session` lease rather than prompting per operation. Registered-resource input and file upload use the separate `data-export` boundary. Other higher-impact remote effects require a local lease bound to account, OAuth client, one or more scopes, and expiration. Compound effects retain every boundary: browser uploads require both `browser-session` and `data-export`, protected-resource application input requires `application-control` and `data-export`, and an external credential path requires both external and sensitive path scopes. Existing leases may satisfy those scopes independently; one pending approval grants only the missing scopes or can be elevated locally to an explicit temporary `full` window capped at eight hours. Neither form changes the persisted profile nor can be remotely extended. Pending records retain only bounded metadata and a target digest, not command text or user content.
8
+
9
+ The daemon transport now uses an enrolled P-256 identity. An initial challenge-only design was rejected during review because unauthenticated network clients could still create and replace candidate sockets until challenge failure, producing an availability attack. The final protocol requires a signed preflight before WebSocket upgrade and a second Worker challenge before tool advertisement. Both transcripts bind the Worker origin and package version; the challenge additionally binds daemon instance identity. Signed preflight nonces are consumed once through bounded Durable Object transaction state, closing the short replay window without requiring fragile sub-minute clock synchronization. Only the public JWK is deployed. The private JWK remains in owner-only local state. The readiness probe and verified-incumbent handover remain independent requirements.
10
+
11
+ OAuth access tokens now last fifteen minutes. Refresh tokens rotate, expire after fourteen idle days or thirty absolute family days, and leave a consumed-token marker. Consumed markers and revoked-family records are strictly capped and pruned oldest-first, and every stored record is schema-validated before use, preventing replay defense from becoming unbounded Durable Object state. Reusing a consumed token revokes all access and refresh records in that family. Because merely changing the daemon protocol would not invalidate an already issued thirty-day access token, first device enrollment also rotates the deployment-wide token version. This deliberately requires every pre-2.0 client to authorize again while ordinary subsequent startup preserves credentials.
12
+
13
+ Account administration keeps the local operator workflow but no longer transmits the administration key as a bearer. Each request signs Worker origin, method, path, body SHA-256, timestamp, and random nonce with HMAC-SHA-256. The Worker validates the body against the signed hash and consumes the nonce once through the same bounded transactional nonce component used by daemon preflight. A repeated request is rejected, and malformed nonce state fails closed rather than being silently reset.
14
+
15
+ The review found and corrected several additional boundary gaps. Protecting only shell/process launch still allowed direct file tools to exfiltrate outside-workspace or credential-sensitive data, and leaving process/job output reads automatic weakened the meaning of their originating lease. Treating every in-workspace write as harmless failed when the workspace was a home directory or when a target was a Git hook, Git configuration, live environment file, startup file, credential path, or service persistence location. A later catalog pass found that returning only one scope could let a `data-export` lease bypass the independent browser or application session boundary. Patch classification initially omitted `Move to` destinations and used lexical paths, allowing a workspace symbolic-link ancestor to reach an external target. The final classifier composes all required scopes, canonicalizes every patch path through the same resolver used by execution, includes browser pairing and application inspection, and is checked against the complete tool catalog so a newly added tool cannot silently escape review. Finally, daemon-created pending requests and separate CLI approval mutations originally had only an in-process queue; an owner-only process-identity lock now serializes cross-process changes. Risk classification, lock ownership, and lease persistence remain separate modules, and persisted lease/pending records are validated field by field before participating in authorization.
16
+
17
+ Regression evidence includes P-256 preflight/challenge generation and tamper/wrong-key/expiry rejection; real Worker candidate and readiness integration; fifteen-minute access tokens and whole-family revocation after refresh replay; signed administration success, legacy bearer rejection, nonce replay rejection, and malformed-state rejection; account/client lease isolation, automatic workspace behavior, scoped/full windows, compound browser/application authority, external-sensitive path composition, sensitive writes, process/job continuation, symbolic-link and patch-move targets, cross-process mutation serialization, owner-only state, and malformed-record rejection. The complete repository gates remain authoritative before release.
18
+
19
+ Residual limits are explicit. Device and administration private material still live in owner-only cross-platform state rather than macOS Secure Enclave/Keychain; a determined same-user process can read or alter that state and can interfere with the daemon. `full` still passes the complete parent environment by contract once process authority is locally leased. Machine Bridge still does not provide kernel CPU/memory quotas, syscall filtering, or network egress isolation. DPoP remains optional future work because current hosted MCP clients do not uniformly support it. Logs remain privacy-oriented operational telemetry, not a remote tamper-proof forensic ledger. Signed/notarized root-owned distribution, external audit anchoring, and hard sandboxing remain separate distribution/deployment work rather than being simulated in application code.
20
+
21
+ No Worker deployment, daemon/service replacement, global installation, credential rotation in live state, push, tag, npm publication, or GitHub Release is performed by these source changes.
22
+
23
+ ## 2026-07-20 version 1.2.11 bounded-result and release-file audit
24
+
25
+ The reported web truncation was not one defect. One-shot command tools retained up to 512 KiB independently for stdout and stderr, returned no continuation handle after dropping their middle, and generic MCP framing serialized object results once as text and again as `structuredContent`. The layered verification runner then inherited every child stream, so a long successful gate could accumulate output even though only its final status mattered. Nonzero process exits also promoted the retained stderr preview into the human error message. These independent amplification points made host-side response truncation frequent and made locally truncated command output unrecoverable.
26
+
27
+ Version 1.2.11 separates preview, retention, and presentation. Public one-shot commands inline 32 KiB per stream and retain up to 1 MiB per stream in an exited in-memory process session. The response supplies a random `output_session_id` and bounded `read_process` parameters; reads use monotonic offsets, disclose aged-out data, and preserve invalid byte slices with base64. Failed commands keep a bounded message and carry the same continuation only in exposed structured details. Large object results have one authoritative `structuredContent` value and a short shared text projection rather than a second full JSON serialization. UTF-8 head/tail previews no longer split Chinese or emoji code points. Green verification tasks suppress child noise; failures retain bounded head and tail, while explicit verbose mode remains available.
28
+
29
+ The only open security issue concerned `lstat` followed by path-based reads of release acceptance JSON and candidate tarballs. Both now open with no-follow semantics, validate the descriptor as a regular file, compare descriptor and path identity, enforce a pre-allocation size ceiling, and read through the descriptor. Deterministic tests replace the path after open and prove the descriptor remains authoritative; symlink records and tarballs fail closed. The temporary CodeQL file-race exception is removed. The guarded push path now also paginates the complete GitHub issue/PR backlog and refuses unrelated open PRs or issues not closed by the current branch, making repository backlog completion an executable release condition rather than a conversational promise.
30
+
31
+ Repeated full-gate execution also exposed a separate test-infrastructure race: V8 could finish writing a coverage file just after the last fixture process exited, causing recursive cleanup to fail with `ENOTEMPTY` even though every threshold passed. Coverage cleanup now uses bounded retry/backoff, the contract is architecture-tested, and three consecutive coverage runs pass.
32
+
33
+ Privacy review found no new durable output store. Continuation bytes live only in the daemon heap, share the existing eight-session/30-minute bounds, are omitted from logs and acceptance records, and disappear on runtime stop. Complete and production dependency audits remain at zero vulnerabilities; registry signatures, attestations, package file modes, generated references, and reachable Git history privacy checks are independently verified.
34
+
3
35
  ## 2026-07-20 version 1.2.10 transient-relay recovery audit
4
36
 
5
37
  Repeated ChatGPT web executions were terminating even though the local daemon and Worker were healthy immediately afterward. The causal defect was not the tool implementation or its configured timeout. A brief WebSocket loss—made more likely when the daemon relay traverses an environment-selected local proxy—was promoted into a terminal request failure at both ends: the Worker rejected every pending call assigned to the closed socket, while the local runtime cancelled all relay-origin calls and killed their ordinary child processes. Automatic WebSocket reconnection restored future traffic but could not recover the request already abandoned by both state machines.
@@ -12,11 +44,11 @@ Regression evidence covers three layers: deterministic pending-registry detach/r
12
44
 
13
45
  ## 2026-07-18 version 1.2.9 interactive release-handoff correction
14
46
 
15
- The release-integrity mechanism correctly bound acceptance to exact package bytes, but the operational role split was wrong. Documentation and repository guards required the owner to run the acceptance command personally and prohibited the coding agent from recording it. That contradicted the established working process: the owner should perform the one action the agent cannot perform honestly—start the exact candidate on the maintainer machine—while the agent should connect to that candidate, verify its version, readiness, and representative functionality, then record acceptance and complete source release operations.
47
+ The release-integrity mechanism correctly bound acceptance to exact package bytes, but the operational role split was wrong. Earlier documentation required the owner to execute candidate and approval commands in a terminal. The explicit owner decision is now that authorization is given in the active conversation; the coding agent starts the exact candidate through Machine Bridge, verifies its version, readiness, deployment identity, and representative functionality, then records acceptance and completes source release operations. This removes terminal authorization as a user-facing dependency without allowing automated tests alone to manufacture acceptance.
16
48
 
17
- Version 1.2.9 codifies that flow. `npm run release:candidate:start -- --allow-worker-deploy` verifies and installs the pending tarball into an ignored isolated prefix and launches it in the foreground without replacing the normal global installation. The explicit flag acknowledges that normal candidate startup may update the configured same-name Worker in place. The owner runs that command and leaves the process active. The coding agent must observe the live candidate through Machine Bridge; automated checks or an unobserved process are insufficient. After successful observation, the agent records the new `owner-started-agent-verified-local-candidate` marker, commits, pushes through the guarded command, completes the pull request, and runs `npm run release` to create or verify the annotated tag and GitHub Release. The old 1.2.8 owner-recorded marker remains accepted only for historical compatibility.
49
+ Version 1.2.9 codifies that flow. `npm run release:candidate:start -- --allow-worker-deploy` verifies and installs the pending tarball into an ignored isolated prefix and launches it in the foreground without replacing the normal global installation. The explicit flag acknowledges that normal candidate startup may update the configured same-name Worker in place. After explicit owner authorization in the active conversation, the coding agent runs that command through Machine Bridge and must observe the live candidate through Machine Bridge; automated checks or an unobserved process are insufficient. After successful observation, the agent records the new `owner-authorized-agent-operated-local-candidate` marker, commits, pushes through the guarded command, completes the pull request, and runs `npm run release` to create or verify the annotated tag and GitHub Release. The old 1.2.8 owner-recorded marker remains accepted only for historical compatibility.
18
50
 
19
- npm publication and Worker deployment remain owner-operated live release actions. The coding agent does not perform either as part of `npm run release`. Any packaged-byte change after candidate preparation invalidates the pending or recorded acceptance and requires another owner-started, agent-observed candidate run.
51
+ npm publication and Worker deployment remain owner-operated live release actions. The coding agent does not perform either as part of `npm run release`. Any packaged-byte change after candidate preparation invalidates the pending or recorded acceptance and requires another owner-authorized, agent-operated and observed candidate run.
20
52
 
21
53
  The first PR run exposed two release-infrastructure defects that local macOS validation could not certify. The layered runner spawned `npm.cmd` directly and failed with `EINVAL` under Node 26 on Windows; it now executes the integrity-pinned npm CLI through the current Node binary on every platform. The acceptance recorder also omitted the portable Git-content digest required by CI. It now constructs a temporary Git index from `HEAD` plus the complete working tree, computes the same canonical package digest used by CI, deletes the temporary index without changing the maintainer staging area, and makes local acceptance verification reject a missing digest. Both failures invalidate the earlier candidate and require a regenerated, re-observed candidate before release.
22
54
 
@@ -78,6 +110,8 @@ A second defect existed one layer earlier. The Worker already handled MCP `notif
78
110
 
79
111
  Logging was reviewed as a user interface rather than a serialization dump. Human mode now uses the supplied natural-language explanation and omits the redundant stable event identifier; newline-delimited JSON retains that identifier for ingestion. Tool starts, outcomes, cancellation, timing, and late-result disposal remain debug-only. Worker observability adds an `unmatched_results` counter for a result that arrives after its pending record has been removed, preserving anomaly evidence without storing tool arguments, command text, paths, or result content.
80
112
 
113
+ The PR security gate also classified the generated account credential verifier as `js/insufficient-password-hash`. The account credential contract forbids human-chosen passwords and generates 256 random bits; the Worker validates the exact generated-token format and stores a verifier using an independent 128-bit salt plus HMAC-SHA-256. This does not have the low-entropy offline-guessing risk modeled by the rule. The exact rule/path pair is therefore explicitly accepted through 2026-10-21, when the project must re-evaluate CodeQL behavior or adopt a portable slow KDF. The exception is fail-closed, path-specific, expiring, and covered by the architecture inventory.
114
+
81
115
  The broader pass rechecked architecture boundaries, cancellation and reconnect branches, release/configuration contracts, privacy redaction, package version convergence, TypeScript and checked-JavaScript contracts, lint, Worker integration, and documentation drift. An additional deployment defect was found during fault reproduction: request cancellation support was implemented in code but the required Cloudflare compatibility flags were absent, so the signal could not reach the Durable Object in production. Architecture tests now make both flags non-optional. Version 1.2.2 retains local state schema 6 and policy revision 5; normal startup must converge the Worker and daemon versions, and the unpacked browser extension must be reloaded. No live deployment, credential rotation, global installation, daemon replacement, npm publication, tag, or GitHub Release is performed by this source change.
82
116
 
83
117
  ## 2026-07-16 version 1.2.1 fail-closed input-contract audit
package/docs/CLIENTS.md CHANGED
@@ -136,7 +136,7 @@ Several OAuth clients and named accounts can coexist. Accounts have independent
136
136
 
137
137
  ## Profile guidance
138
138
 
139
- - `full` is the default and prioritizes immediate usability. It is a canonical contract exposing every catalog tool, shell execution, unrestricted direct filesystem paths, absolute path output, and the full parent environment. Any individual narrowing is represented as `custom`.
139
+ - `full` is the default capability ceiling and prioritizes immediate usability. It is a canonical contract exposing every catalog tool, shell execution, unrestricted direct filesystem paths, absolute path output, and the full parent environment. Any individual narrowing is represented as `custom`. Remote high-impact transaction leases are a separate final authorization layer and do not change the profile identity.
140
140
  - `agent` retains file mutation and direct process execution but removes shell parsing, confines direct filesystem tools to the workspace, and isolates the process environment.
141
141
  - `edit` permits deterministic file mutation without process execution.
142
142
  - `review` is read-only and workspace-confined.
@@ -155,7 +155,7 @@ For SSH automation, prefer `generate_ssh_key_resource` under canonical full, or
155
155
 
156
156
  ## Host-side safety rules
157
157
 
158
- The local `full` profile controls Machine Bridge's own tool catalog, path resolver, path display, process environment, and shell availability. It does not control the MCP host's model policy, approval UI, connector gateway, or platform execution filters.
158
+ The local `full` profile controls Machine Bridge's own tool catalog, path resolver, path display, process environment, and shell availability. For remote transport, the local transaction gate additionally controls when high-impact effects may run. It does not control the MCP host's model policy, approval UI, connector gateway, or platform execution filters. See [LOCAL_AUTHORIZATION.md](LOCAL_AUTHORIZATION.md).
159
159
 
160
160
  Machine Bridge itself does not block files because their names look sensitive. In remote mode, first inspect `server_info.authorization.effective_policy` and `effective_tools`; `daemon.policy` is only the local ceiling. If the effective profile is `full` and the effective tool is present but a direct call is still rejected before a structured result, the host/connector may have blocked delivery. If `diagnose_runtime` responds but its fixed process or shell probe fails, the likely source is local OS policy, endpoint-security software, permissions, or shell configuration. Changing `--profile`, `--unrestricted-paths`, or `--absolute-paths` cannot override either layer.
161
161
 
@@ -9,7 +9,7 @@ This document records project-wide decisions that must survive individual fixes,
9
9
  3. **Machine Bridge authority and host authority are separate.** `full` removes Machine Bridge's own policy, path, shell, and environment restrictions. It cannot override an MCP host, connector gateway, operating system, endpoint-security product, cloud IAM, remote authentication, or `sudo`.
10
10
  4. **Publication surfaces contain no real environment metadata.** Source, tests, fixtures, examples, release notes, filenames, package contents, tags, and release assets use synthetic identifiers and reserved example domains.
11
11
  5. **Secrets are never operational log data.** Tool arguments, command text, stdin, stdout, stderr, file content, OAuth bodies, credentials, and local resource values are not logged.
12
- 6. **A release is one owner-started, agent-verified package with successful cross-platform evidence.** The repository owner starts the exact npm candidate on the maintainer machine; the coding agent verifies the connected candidate version, readiness, and representative functionality through Machine Bridge and then records the package hashes before the first GitHub push. Package metadata, Worker version, browser-extension version/name, acceptance record, Git tag, GitHub Release, npm version, documentation, and deployed health version must agree, and the exact `origin/main` commit must have completed successful push-triggered CI, CodeQL, Governance, and OpenSSF Scorecard runs before a tag or release is created.
12
+ 6. **A release is one owner-authorized, agent-operated and verified package with successful cross-platform evidence.** The repository owner explicitly authorizes the exact npm candidate and any same-name Worker update in the active conversation; the coding agent starts that candidate through Machine Bridge, verifies the connected candidate version, readiness, and representative functionality, and then records the package hashes before the first GitHub push. Package metadata, Worker version, browser-extension version/name, acceptance record, Git tag, GitHub Release, npm version, documentation, and deployed health version must agree, and the exact `origin/main` commit must have completed successful push-triggered CI, CodeQL, Governance, and OpenSSF Scorecard runs before a tag or release is created.
13
13
  7. **Generic local automation is structured, not arbitrary evaluation.** Browser/application features may expose broad user authority under canonical `full`, but must not accept caller-provided JavaScript, AppleScript, JXA, or extension code.
14
14
  8. **Daily-browser integration uses the existing profile.** The supported primary browser path is the packaged authenticated extension and machine-level loopback broker, preserving current tabs/login state; a separate automation profile is not an equivalent replacement.
15
15
  9. **Pairing and resource secrets are not conversation or log data.** Tokens and injected local-resource values must not be returned, embedded in URLs, or written to operational logs.
@@ -24,7 +24,7 @@ A proposed change that conflicts with an invariant requires an explicit owner de
24
24
 
25
25
  ## Change and release-operation ownership
26
26
 
27
- Repository implementation, interactive candidate acceptance, source release completion, and live release operations are separate responsibilities. Under `AGENTS.md`, coding automation may edit, test, and commit locally, generate an exact candidate tarball, and give the owner `npm run release:candidate:start -- --allow-worker-deploy`. The owner explicitly authorizes the in-place update of the configured same-name Worker, starts that candidate, and leaves it running. The agent verifies the deployed Worker version/hash, remote health, relay readiness, and connected local candidate through Machine Bridge and may then record `release-acceptance/v<version>.json`, push only through `npm run github:push`, complete the pull request through local `git`/`gh`, and create the annotated version tag plus final GitHub Release with `npm run release` after the accepted package hash and exact `main` checks pass. Automated checks without an observed live candidate do not authorize acceptance. `npm run release` never pushes `main`. Automation must not publish or alter npm packages, install globally outside the isolated candidate prefix, deploy or reconfigure a Worker as a release operation, rotate credentials, mutate live deployment state, or replace daemon/service state without explicit user authorization.
27
+ Repository implementation, interactive candidate acceptance, source release completion, and live release operations are separate responsibilities. Under `AGENTS.md`, coding automation may edit, test, and commit locally, generate an exact candidate tarball, and, after explicit owner authorization in the active conversation, start the exact isolated candidate through Machine Bridge. The agent verifies the deployed Worker version/hash, remote health, relay readiness, and connected local candidate through Machine Bridge and may then record `release-acceptance/v<version>.json`, push only through `npm run github:push`, complete the pull request through local `git`/`gh`, and create the annotated version tag plus final GitHub Release with `npm run release` after the accepted package hash and exact `main` checks pass. The owner is not required to copy approval IDs or run release-authorization commands in a terminal. Automated checks without an observed live candidate do not authorize acceptance. `npm run release` never pushes `main`. Automation must not publish or alter npm packages, install globally outside the isolated candidate prefix, deploy or reconfigure a Worker as a release operation, rotate credentials, mutate live deployment state, or replace daemon/service state without explicit user authorization.
28
28
 
29
29
  The normal handoff is: the repository owner publishes the reviewed npm version, then runs `npm install -g --omit=optional --allow-scripts=esbuild,workerd,sharp,fsevents machine-bridge-mcp@latest && machine-mcp`. The npm command updates the global CLI but cannot hot-reload an existing Node process. The subsequent normal foreground startup validates the Worker deployment hash, expected version, and health, requests shutdown of an active autostart daemon, waits a bounded interval for its lock, redeploys when necessary, and then takes over with the installed version. Live operations require explicit authorization even when they appear to be the obvious next release step.
30
30
 
@@ -16,7 +16,7 @@ Remote and stdio modes use the same local runtime and policy model. The remote W
16
16
 
17
17
  ## 2. Understand the authority you are granting
18
18
 
19
- A new workspace uses the `full` profile unless another profile is selected explicitly. `full` exposes the complete tool catalog, shell execution, paths outside the selected workspace, absolute paths, and the complete parent process environment. It is intended for a trusted owner using a trusted MCP host.
19
+ A new workspace uses the `full` profile unless another profile is selected explicitly. `full` preserves the complete tool catalog, shell execution, paths outside the selected workspace, absolute paths, browser/application automation, and the complete parent process environment. It is intended for a trusted owner using a trusted MCP host. An authenticated owner account may use the `full` ceiling directly without terminal approval. Delegated non-owner accounts require local time-bounded capability leases for high-impact effects; their normal workspace reads/edits and project inspection remain automatic. See [LOCAL_AUTHORIZATION.md](LOCAL_AUTHORIZATION.md).
20
20
 
21
21
  For a first connection to an unfamiliar host, start with a narrower profile:
22
22
 
@@ -0,0 +1,111 @@
1
+ # Local transaction authorization
2
+
3
+ Machine Bridge separates **available capability** from **current remote authority**.
4
+
5
+ The canonical `full` profile still exposes the complete tool catalog, unrestricted local-user paths, shell execution, browser and application automation, managed jobs, absolute paths, and the complete parent environment. It remains the default for a trusted owner. Version 2.0 does not redefine or narrow that profile.
6
+
7
+ An authenticated `owner` account activates the capabilities permitted by the daemon policy ceiling without a second terminal approval step. This preserves uninterrupted owner automation while retaining OAuth identity binding, account-version revocation, device authentication, and the daemon policy as hard boundaries.
8
+
9
+ For delegated `reviewer`, `editor`, and `operator` accounts, the local daemon applies a final transaction gate after the Worker and local runtime have checked the account role and daemon policy. Ordinary project work remains automatic; an operation that crosses a consequential boundary requires a short-lived local capability lease.
10
+
11
+ Local stdio calls are not affected by this remote transaction gate. They remain governed by the selected local policy and the MCP host's own approval model.
12
+
13
+ ## What remains automatic
14
+
15
+ Remote calls do not require a lease for:
16
+
17
+ - project discovery, status, diagnostics, ordinary metadata, and owner-configured Machine Bridge Agent guidance intended for MCP clients;
18
+ - reads inside the selected workspace, except credential-sensitive paths;
19
+ - ordinary source and configuration writes inside the selected workspace, excluding credential- and persistence-sensitive targets;
20
+ - transactional patches whose source, destination, and move targets remain inside the selected workspace and outside sensitive targets;
21
+ - Git inspection;
22
+ - browser broker status without reading profile content;
23
+ - installed-application discovery without inspecting an application UI;
24
+ - registered-resource metadata inspection.
25
+
26
+ This is the normal delegated-account coding and review path. A trusted owner is not interrupted by local approval prompts. For non-owner browser work, one reusable profile-session lease covers the session rather than prompting per page read, field, or click.
27
+
28
+ ## What requires a lease
29
+
30
+ The daemon requests local authorization for these remote effects:
31
+
32
+ | Scope | Examples |
33
+ |---|---|
34
+ | `shell` | shell commands, direct process launch, process output continuation, interactive process input or termination, registered local commands |
35
+ | `external-read` | reading, searching, imaging, or inspecting a path outside the selected workspace |
36
+ | `sensitive-read` | reading credential-sensitive locations or names such as SSH/AWS/Keychain state, `.env`, tokens, secrets, or private keys |
37
+ | `external-write` | writing, editing, patching, or moving a patch target outside the selected workspace |
38
+ | `sensitive-write` | writing credentials, live `.env` files, SSH/privilege files, shell startup files, Git hooks, LaunchAgents/LaunchDaemons, or other persistence-sensitive paths even when they are inside the selected workspace |
39
+ | `browser-session` | listing or reading tabs, source, screenshots, waits, navigation, form input, clicks, submission, tab management, or extension pairing in the existing browser profile |
40
+ | `data-export` | uploading a file, inserting a registered local resource into a browser or desktop application, or filling an explicitly sensitive browser field |
41
+ | `persistent-job` | staging, starting, listing, reading output from, or cancelling a managed job |
42
+ | `application-control` | opening, inspecting, or operating a desktop application |
43
+ | `credential-operation` | generating an SSH key resource |
44
+ | `full` | all of the above for one explicit temporary automation window |
45
+
46
+ Classification uses canonicalized paths and bounded operation metadata. A single operation may require more than one scope: for example, a browser upload requires both `browser-session` and `data-export`, and an external credential read requires both `external-read` and `sensitive-read`. Existing leases may satisfy those scopes independently. A pending approval contains only the scopes still missing, and approving it creates one compound lease for those missing scopes. Pending records contain a SHA-256 target digest, not command text, file contents, form values, or uploaded bytes.
47
+
48
+ Write targets are canonicalized through their nearest existing ancestor even under `full`. A path that enters an in-workspace symbolic-link directory is classified by the real destination, while overwriting a final symbolic link is rejected. Patch `Move to` destinations are classified alongside add/update/delete paths. This preserves unrestricted-path capability without allowing path aliases to weaken the lease boundary.
49
+
50
+ The browser boundary is intentionally session-granular. The packaged extension controls whichever Chromium profile the user loaded it into; Machine Bridge cannot prove that the profile is isolated. Requiring one `browser-session` lease protects tab metadata and authenticated page content without degrading into per-click prompts. Once granted, ordinary browser reads, navigation, filling, and clicks proceed continuously until expiry. Exporting registered local data remains separately gated.
51
+
52
+ ## Optional delegated-account approval flow
53
+
54
+ Owner requests never create pending approval IDs. When a lease is missing for a non-owner account, the tool call fails with `local_approval_required` and a command containing a random pending approval ID. List pending requests and active leases locally:
55
+
56
+ ```sh
57
+ machine-mcp --workspace /path/to/project approval list
58
+ ```
59
+
60
+ Approve only the requested scope for one hour:
61
+
62
+ ```sh
63
+ machine-mcp --workspace /path/to/project approval approve APPROVAL_ID --duration 1h
64
+ ```
65
+
66
+ Then retry the original task. Further matching operations from the same account and OAuth client run without interruption until the lease expires.
67
+
68
+ For a trusted, intensive automation session, convert the same pending request into an explicit temporary `full` window:
69
+
70
+ ```sh
71
+ machine-mcp --workspace /path/to/project approval approve APPROVAL_ID --full
72
+ ```
73
+
74
+ `--full` defaults to eight hours and cannot exceed eight hours. It is local, account-bound, client-bound, time-bounded, and revocable. It does not change the saved policy profile or extend itself remotely.
75
+
76
+ A specific scope can also be granted in advance. Account and OAuth client IDs are available from `approval list --json` after the client has produced a pending request. Wildcards are supported only when entered explicitly:
77
+
78
+ ```sh
79
+ machine-mcp --workspace /path/to/project approval grant shell \
80
+ --account ACCOUNT_ID --client CLIENT_ID --duration 2h
81
+ ```
82
+
83
+ ## Revocation
84
+
85
+ Revoke one lease:
86
+
87
+ ```sh
88
+ machine-mcp --workspace /path/to/project approval revoke LEASE_ID
89
+ ```
90
+
91
+ Revoke every active remote capability lease for the workspace:
92
+
93
+ ```sh
94
+ machine-mcp --workspace /path/to/project approval clear
95
+ ```
96
+
97
+ Stopping the daemon prevents execution while it is offline, but leases remain in owner-only state until expiry or explicit revocation. Incident response should therefore stop the daemon, clear leases, revoke or rotate affected OAuth credentials, and inspect account/client state.
98
+
99
+ ## Binding and storage
100
+
101
+ Every lease binds:
102
+
103
+ - account ID;
104
+ - OAuth client ID;
105
+ - one or more explicitly approved scopes, or the standalone `full` scope;
106
+ - creation and expiration times;
107
+ - an optional source pending-approval ID.
108
+
109
+ Normal scopes may last at most twelve hours. `full` may last at most eight hours. Pending requests expire after ten minutes. Lease and pending files are size-bounded, owner-only, atomically replaced, schema-validated, and validated record by record. Daemon-created pending requests and separate CLI approval/revocation processes serialize mutations through an owner-only process-identity lock, preventing lost updates during concurrent use. Malformed state or lock metadata fails closed.
110
+
111
+ This is an application-level control, not an OS sandbox. A process already running as the same OS user can interfere with local files and memory. Use a separate OS account, VM, or container when the client, repository, or instructions are mutually untrusted.
package/docs/LOGGING.md CHANGED
@@ -12,7 +12,7 @@ Logs should answer:
12
12
  4. Is an infrastructure, protocol, deployment, or local service problem requiring action?
13
13
  5. When debug logging is explicitly enabled, which bounded implementation event should be correlated?
14
14
 
15
- Logs are not a command history, content audit trail, or substitute for MCP client approvals.
15
+ Logs are not a command history or content audit trail. Local capability-lease state provides authorization evidence without recording command text or content, but it is not a tamper-proof forensic ledger and is not a substitute for OS isolation.
16
16
 
17
17
  ## Levels
18
18
 
@@ -77,11 +77,13 @@ With `--verbose`, the same incident additionally includes bounded structured fie
77
77
 
78
78
  ## Tool and result-delivery events
79
79
 
80
- All per-tool starts, successes, failures, cancellations, timing, and expected late-result disposal are debug-only. The MCP response already reports the outcome to the caller; duplicating routine tool traffic at default levels creates noise and can reveal activity patterns.
80
+ All per-tool starts, successes, failures, cancellations, timing, and expected late-result disposal are debug-only. The MCP response already reports the outcome to the caller; duplicating routine tool traffic at default levels creates noise and can reveal activity patterns. Completed one-shot output continuations are not written to daemon logs or disk; their bounded stdout/stderr remains only in the daemon process-session memory and expires with normal session retention.
81
+
82
+ The layered repository check runner follows the same noise rule. Green child-task output is discarded after the child exits; only task name and elapsed time are printed. Failed tasks expose bounded head/tail stdout and stderr diagnostics. `MBM_CHECK_VERBOSE=1` is an explicit operator choice to stream raw child output and is not used by default or CI.
81
83
 
82
84
  A completed local result is normally sent on the ready relay connection. If that socket disappears, the runtime queues the bounded result envelope during the thirty-second same-daemon reconnect window rather than logging a terminal delivery failure. Debug output records only a shortened call ID and queue/reconnect counts. After the same daemon process completes readiness, replay emits one recovery event; a different process cannot inherit the result. Explicit caller cancellation suppresses eventual delivery. If the relay does not recover before the grace deadline, ordinary calls are cancelled, queued results are discarded, and the existing outage state machine determines whether the persistent failure warrants a warning. Tool arguments, commands, and result content are never logged.
83
85
 
84
- Debug per-tool fields may include tool name, duration, coarse outcome class, and a shortened random call identifier. The identifier is for correlating adjacent local events and is not a stable audit identifier.
86
+ Debug per-tool fields may include tool name, duration, coarse outcome class, and a shortened random call identifier. The identifier is for correlating adjacent local events and is not a stable audit identifier. Authorization failures expose a random approval ID, scope, and expiry to the caller; daemon logs still omit normalized targets and request arguments.
85
87
 
86
88
  ## Data that is never logged
87
89
 
@@ -91,7 +93,7 @@ The implementation omits:
91
93
  - stdin, stdout, and stderr;
92
94
  - file, patch, image, and temporary-file content;
93
95
  - OAuth request bodies;
94
- - account passwords, account-administration secrets, daemon secrets, authorization codes, access tokens, and refresh tokens;
96
+ - account passwords, account-administration HMAC keys/signatures, daemon device private keys/signatures, authorization codes, access tokens, refresh tokens, and capability-lease target material;
95
97
  - registered resource values and source paths;
96
98
  - browser pairing tokens, page URLs/source, DOM metadata, form values, uploaded file bytes, and screenshots;
97
99
  - application names, Accessibility trees, selectors, and entered values;
@@ -136,7 +138,7 @@ Each managed job has owner-only runner diagnostic logs. Child-step output is ret
136
138
 
137
139
  ## MCP host boundary
138
140
 
139
- Machine Bridge does not classify filenames as sensitive and does not block credential-looking names under canonical `full`. An MCP host, connector, model provider, desktop application, operating system, or endpoint-security layer may independently reject a request before it reaches Machine Bridge.
141
+ Canonical `full` does not remove tools based on filenames. For remote execution, the transaction classifier treats credential-sensitive path segments and basenames as a lease boundary while preserving the underlying capability. An MCP host, connector, model provider, desktop application, operating system, or endpoint-security layer may independently reject a request before it reaches Machine Bridge.
140
142
 
141
143
  Use `server_info`, `project_overview`, `machine-mcp status`, `machine-mcp doctor`, and `diagnose_runtime` to distinguish local policy from host-side enforcement. Capability-routing status is returned on demand rather than written as task logs; it stores a runtime-keyed task fingerprint, not raw task text. Changing the Machine Bridge profile cannot override another layer.
142
144