machine-bridge-mcp 1.2.11 → 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.
- package/CHANGELOG.md +14 -1
- package/README.md +10 -5
- package/browser-extension/manifest.json +2 -2
- package/docs/ARCHITECTURE.md +18 -10
- package/docs/AUDIT.md +25 -3
- package/docs/CLIENTS.md +2 -2
- package/docs/ENGINEERING.md +2 -2
- package/docs/GETTING_STARTED.md +1 -1
- package/docs/LOCAL_AUTHORIZATION.md +111 -0
- package/docs/LOGGING.md +4 -4
- package/docs/MULTI_ACCOUNT.md +5 -5
- package/docs/OPERATIONS.md +30 -5
- package/docs/OVERVIEW.md +12 -8
- package/docs/PROJECT_STANDARDS.md +1 -1
- package/docs/RELEASING.md +3 -3
- package/docs/TESTING.md +5 -2
- package/docs/THREAT_MODEL.md +7 -6
- package/docs/UPGRADING.md +10 -6
- package/package.json +4 -2
- package/scripts/check-plan.mjs +2 -0
- package/scripts/local-release-acceptance.mjs +2 -2
- package/scripts/release-acceptance.mjs +7 -4
- package/src/local/account-admin.mjs +28 -3
- package/src/local/cli-approval.mjs +117 -0
- package/src/local/cli-options.mjs +8 -2
- package/src/local/cli.mjs +13 -2
- package/src/local/device-identity.mjs +108 -0
- package/src/local/operation-authorization.mjs +366 -0
- package/src/local/operation-risk.mjs +221 -0
- package/src/local/operation-state-lock.mjs +92 -0
- package/src/local/relay-connection.mjs +12 -5
- package/src/local/runtime-relay.mjs +13 -5
- package/src/local/runtime.mjs +13 -7
- package/src/local/state.mjs +9 -3
- package/src/local/tool-executor.mjs +4 -2
- package/src/local/worker-deployment.mjs +4 -3
- package/src/local/worker-secret-file.mjs +2 -1
- package/src/shared/admin-auth.d.mts +10 -0
- package/src/shared/admin-auth.mjs +36 -0
- package/src/shared/daemon-auth.d.mts +20 -0
- package/src/shared/daemon-auth.mjs +55 -0
- package/src/worker/account-admin.ts +73 -4
- package/src/worker/daemon-auth.ts +205 -0
- package/src/worker/daemon-sockets.ts +15 -2
- package/src/worker/index.ts +59 -10
- package/src/worker/nonce-store.ts +79 -0
- package/src/worker/oauth-controller.ts +11 -6
- package/src/worker/oauth-refresh-families.ts +172 -0
- package/src/worker/oauth-state.ts +48 -3
- package/src/worker/oauth-tokens.ts +49 -40
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,18 @@
|
|
|
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
|
+
|
|
3
16
|
## 1.2.11 - 2026-07-20
|
|
4
17
|
|
|
5
18
|
### Bounded output continuation and repository backlog gate
|
|
@@ -20,7 +33,7 @@
|
|
|
20
33
|
## 1.2.9 - 2026-07-18
|
|
21
34
|
|
|
22
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.
|
|
23
|
-
- 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-
|
|
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.
|
|
24
37
|
|
|
25
38
|
### Architecture headroom, verification feedback, and threat model
|
|
26
39
|
|
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
|
|
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
|
|
@@ -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
|
-
#
|
|
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
|
|
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
|
|
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": "
|
|
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": "
|
|
33
|
+
"version_name": "2.0.0"
|
|
34
34
|
}
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -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
|
-
|
|
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
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -257,7 +265,7 @@ Removal first acquires a state-root maintenance lock that blocks new profile/sta
|
|
|
257
265
|
|
|
258
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.
|
|
259
267
|
|
|
260
|
-
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
|
|
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.
|
|
261
269
|
|
|
262
270
|
## Observability
|
|
263
271
|
|
|
@@ -269,7 +277,7 @@ Cloudflare sampling is size control rather than an audit log. The project intent
|
|
|
269
277
|
|
|
270
278
|
## Release integrity
|
|
271
279
|
|
|
272
|
-
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.
|
|
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`.
|
|
273
281
|
|
|
274
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.
|
|
275
283
|
|
package/docs/AUDIT.md
CHANGED
|
@@ -1,5 +1,25 @@
|
|
|
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
|
+
|
|
3
23
|
## 2026-07-20 version 1.2.11 bounded-result and release-file audit
|
|
4
24
|
|
|
5
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.
|
|
@@ -24,11 +44,11 @@ Regression evidence covers three layers: deterministic pending-registry detach/r
|
|
|
24
44
|
|
|
25
45
|
## 2026-07-18 version 1.2.9 interactive release-handoff correction
|
|
26
46
|
|
|
27
|
-
The release-integrity mechanism correctly bound acceptance to exact package bytes, but the operational role split was wrong.
|
|
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.
|
|
28
48
|
|
|
29
|
-
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.
|
|
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.
|
|
30
50
|
|
|
31
|
-
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-
|
|
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.
|
|
32
52
|
|
|
33
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.
|
|
34
54
|
|
|
@@ -90,6 +110,8 @@ A second defect existed one layer earlier. The Worker already handled MCP `notif
|
|
|
90
110
|
|
|
91
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.
|
|
92
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
|
+
|
|
93
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.
|
|
94
116
|
|
|
95
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
|
|
package/docs/ENGINEERING.md
CHANGED
|
@@ -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-
|
|
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
|
|
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
|
|
package/docs/GETTING_STARTED.md
CHANGED
|
@@ -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`
|
|
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
|
|
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
|
|
|
@@ -83,7 +83,7 @@ The layered repository check runner follows the same noise rule. Green child-tas
|
|
|
83
83
|
|
|
84
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.
|
|
85
85
|
|
|
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.
|
|
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.
|
|
87
87
|
|
|
88
88
|
## Data that is never logged
|
|
89
89
|
|
|
@@ -93,7 +93,7 @@ The implementation omits:
|
|
|
93
93
|
- stdin, stdout, and stderr;
|
|
94
94
|
- file, patch, image, and temporary-file content;
|
|
95
95
|
- OAuth request bodies;
|
|
96
|
-
- account passwords, account-administration
|
|
96
|
+
- account passwords, account-administration HMAC keys/signatures, daemon device private keys/signatures, authorization codes, access tokens, refresh tokens, and capability-lease target material;
|
|
97
97
|
- registered resource values and source paths;
|
|
98
98
|
- browser pairing tokens, page URLs/source, DOM metadata, form values, uploaded file bytes, and screenshots;
|
|
99
99
|
- application names, Accessibility trees, selectors, and entered values;
|
|
@@ -138,7 +138,7 @@ Each managed job has owner-only runner diagnostic logs. Child-step output is ret
|
|
|
138
138
|
|
|
139
139
|
## MCP host boundary
|
|
140
140
|
|
|
141
|
-
|
|
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.
|
|
142
142
|
|
|
143
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.
|
|
144
144
|
|
package/docs/MULTI_ACCOUNT.md
CHANGED
|
@@ -15,7 +15,7 @@ Use separate OS accounts, containers, VMs, state roots, Workers, and workspaces
|
|
|
15
15
|
| `reviewer` | `review` | Read-only workspace, Git, image, resource, and job inspection |
|
|
16
16
|
| `editor` | `edit` | Reviewer access plus deterministic file mutation |
|
|
17
17
|
| `operator` | `agent` | Editor access plus workspace-confined direct process execution and sessions |
|
|
18
|
-
| `owner` | `full` | Complete
|
|
18
|
+
| `owner` | `full` | Complete capability ceiling; high-impact remote transactions additionally consume a local account/client-bound lease |
|
|
19
19
|
|
|
20
20
|
The effective tool set is the intersection of:
|
|
21
21
|
|
|
@@ -23,7 +23,7 @@ The effective tool set is the intersection of:
|
|
|
23
23
|
2. the policy advertised by the connected local daemon;
|
|
24
24
|
3. the tools actually available from that daemon.
|
|
25
25
|
|
|
26
|
-
The Worker filters `tools/list` and rejects unauthorized calls before relay. Every accepted relay call
|
|
26
|
+
For execution, a fourth local layer distinguishes trusted owner automation from delegated access. An authenticated owner may use the daemon policy ceiling directly. A high-impact call from a reviewer, editor, or operator must also match an active capability lease bound to its `account_id` and OAuth `client_id`. This transaction layer does not remove tools from canonical `full`; it preserves least privilege for delegated credentials without interrupting the owner workflow. The Worker filters `tools/list` and rejects unauthorized calls before relay. Every accepted relay call carries `account_id`, `account_version`, `client_id`, and `role`; the local runtime validates them again before dispatch. See [LOCAL_AUTHORIZATION.md](LOCAL_AUTHORIZATION.md).
|
|
27
27
|
|
|
28
28
|
Authenticated `server_info` deliberately reports both layers. `authorization.effective_policy` and `authorization.effective_tools` are authoritative for the current account. The nested `daemon.policy` and `daemon.tools` fields are only the local capability ceiling before account-role filtering; a `full` daemon does not make an `editor` account full. Remote `project_overview` uses the effective values at top-level `policy` and `tools` and preserves the daemon values as `daemonPolicy` and `daemonTools`.
|
|
29
29
|
|
|
@@ -79,15 +79,15 @@ An authorization code records:
|
|
|
79
79
|
- scope and protected resource;
|
|
80
80
|
- expiration.
|
|
81
81
|
|
|
82
|
-
An access-token record contains the same account binding plus the deployment-wide token version. Token values are stored only as SHA-256 lookup keys. Account passwords are CLI-generated 256-bit tokens. The Worker stores independent salted HMAC-SHA-256 verifiers and rejects arbitrary human-chosen passwords; the token entropy, rather than a CPU-intensive dictionary-hardening loop, provides offline-guessing resistance within the Worker CPU budget.
|
|
82
|
+
An access-token record contains the same account binding plus the deployment-wide token version and refresh-family identity. Token values are stored only as SHA-256 lookup keys. Access tokens last fifteen minutes. Refresh tokens rotate on every use, have a fourteen-day idle limit and thirty-day family limit, and leave a bounded consumed-token marker; replay of a consumed token revokes every remaining refresh and access token in that family. Account passwords are CLI-generated 256-bit tokens. The Worker stores independent salted HMAC-SHA-256 verifiers and rejects arbitrary human-chosen passwords; the token entropy, rather than a CPU-intensive dictionary-hardening loop, provides offline-guessing resistance within the Worker CPU budget.
|
|
83
83
|
|
|
84
84
|
At each authenticated request, the Worker verifies that the account still exists, is active, and has the same version and role recorded in the token. A password rotation, role change, suspension, or removal increments or removes that account state and invalidates only its codes and tokens.
|
|
85
85
|
|
|
86
|
-
`machine-mcp rotate-secrets` is intentionally broader. It rotates the account-administration
|
|
86
|
+
`machine-mcp rotate-secrets` is intentionally broader. It rotates the account-administration HMAC key, daemon device identity, and deployment-wide token version, invalidating every account token, requiring all clients to authorize again, and requiring the matching Worker/daemon deployment to converge.
|
|
87
87
|
|
|
88
88
|
## Administrative boundary
|
|
89
89
|
|
|
90
|
-
Account administration is not exposed as an MCP tool. It uses an owner-only local
|
|
90
|
+
Account administration is not exposed as an MCP tool. It uses an owner-only local HMAC key to sign each CLI request over method, path, body SHA-256, timestamp, and random nonce. The key is stored only in owner-protected local state and Cloudflare Worker secrets and is never transmitted as a network bearer. The Worker consumes each nonce once through bounded Durable Object transaction state and rejects replay or malformed state. The key is never printed by `status`, sent through MCP, or used as an account password.
|
|
91
91
|
|
|
92
92
|
The first start of a new deployment creates an `owner` account automatically and prints its generated password once. Subsequent starts do not display account passwords.
|
|
93
93
|
|
package/docs/OPERATIONS.md
CHANGED
|
@@ -293,15 +293,40 @@ machine-mcp --workspace /path/to/project --profile agent
|
|
|
293
293
|
|
|
294
294
|
A remote policy change is saved locally, propagated in the daemon handshake, and loaded by autostart from owner-only state.
|
|
295
295
|
|
|
296
|
+
### Remote authority and optional delegated-account leases
|
|
297
|
+
|
|
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:
|
|
299
|
+
|
|
300
|
+
```sh
|
|
301
|
+
machine-mcp --workspace /path/to/project approval list
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
Approve the requested scope for ordinary work, or explicitly open a temporary full automation window:
|
|
305
|
+
|
|
306
|
+
```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
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Revoke one lease or all leases:
|
|
312
|
+
|
|
313
|
+
```sh
|
|
314
|
+
machine-mcp --workspace /path/to/project approval revoke LEASE_ID
|
|
315
|
+
machine-mcp --workspace /path/to/project approval clear
|
|
316
|
+
```
|
|
317
|
+
|
|
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).
|
|
319
|
+
|
|
296
320
|
## Incident response
|
|
297
321
|
|
|
298
322
|
After suspected credential or client compromise:
|
|
299
323
|
|
|
300
324
|
1. stop foreground and autostart daemons;
|
|
301
|
-
2. run `machine-mcp
|
|
302
|
-
3.
|
|
303
|
-
4.
|
|
304
|
-
5.
|
|
305
|
-
6.
|
|
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.
|
|
306
331
|
|
|
307
332
|
The detailed 0.12.0 audit record and residual operational limits are in [AUDIT.md](AUDIT.md).
|