machine-bridge-mcp 0.3.3 → 0.4.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 +34 -0
- package/README.md +158 -172
- package/SECURITY.md +90 -40
- package/docs/ARCHITECTURE.md +126 -59
- package/docs/CLIENTS.md +72 -0
- package/docs/OPERATIONS.md +81 -0
- package/docs/RELEASING.md +62 -0
- package/docs/TESTING.md +51 -0
- package/docs/examples/github-actions-ci.yml +53 -0
- package/package.json +18 -8
- package/src/local/cli.mjs +152 -51
- package/src/local/daemon.mjs +597 -301
- package/src/local/patch.mjs +140 -0
- package/src/local/process-sessions.mjs +352 -0
- package/src/local/service.mjs +5 -16
- package/src/local/shell.mjs +22 -5
- package/src/local/state.mjs +1 -1
- package/src/local/stdio.mjs +194 -0
- package/src/local/tools.mjs +96 -0
- package/src/shared/tool-catalog.json +638 -0
- package/src/worker/index.ts +151 -162
- package/tsconfig.json +4 -2
- package/src/local/self-test.mjs +0 -227
package/SECURITY.md
CHANGED
|
@@ -2,83 +2,133 @@
|
|
|
2
2
|
|
|
3
3
|
## Supported versions
|
|
4
4
|
|
|
5
|
-
Security fixes are applied to the latest released version. Upgrade before reporting an issue
|
|
5
|
+
Security fixes are applied to the latest released version. Upgrade before reporting an issue already fixed on `main`.
|
|
6
6
|
|
|
7
7
|
## Reporting a vulnerability
|
|
8
8
|
|
|
9
|
-
Do not open a public issue for an undisclosed vulnerability. Use GitHub
|
|
9
|
+
Do not open a public issue for an undisclosed vulnerability. Use GitHub private vulnerability reporting for this repository. Include:
|
|
10
10
|
|
|
11
11
|
- affected version and operating system;
|
|
12
|
-
-
|
|
13
|
-
-
|
|
12
|
+
- remote or stdio transport;
|
|
13
|
+
- selected profile and relevant flags;
|
|
14
|
+
- minimal reproduction;
|
|
14
15
|
- expected and observed impact;
|
|
15
|
-
- whether credentials, filesystem data, or
|
|
16
|
+
- whether credentials, filesystem data, process authority, or network access were exposed.
|
|
16
17
|
|
|
17
|
-
Do not include live MCP passwords, daemon secrets, OAuth tokens, Cloudflare credentials, private keys, or unrelated local files. Rotate
|
|
18
|
+
Do not include live MCP passwords, daemon secrets, OAuth tokens, Cloudflare credentials, private keys, or unrelated local files. Rotate credentials used in a reproduction.
|
|
18
19
|
|
|
19
|
-
##
|
|
20
|
+
## Core trust model
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
Trusted components are:
|
|
22
23
|
|
|
23
|
-
1.
|
|
24
|
-
2.
|
|
25
|
-
3.
|
|
26
|
-
4.
|
|
24
|
+
1. the local OS user running the runtime;
|
|
25
|
+
2. the installed package/source checkout and its dependencies;
|
|
26
|
+
3. for remote mode, the user's Cloudflare account and deployed Worker;
|
|
27
|
+
4. an MCP client explicitly authorized through OAuth or launched locally through trusted stdio configuration.
|
|
27
28
|
|
|
28
|
-
|
|
29
|
+
An authorized client can invoke every tool exposed by the selected profile and receive its results.
|
|
29
30
|
|
|
30
|
-
|
|
31
|
+
The Worker is a remote authentication and relay boundary. The local runtime is the filesystem and process boundary. Stdio bypasses the Worker and relies on local process/configuration trust.
|
|
31
32
|
|
|
32
|
-
##
|
|
33
|
+
## Profiles are capability sets, not sandboxes
|
|
33
34
|
|
|
34
|
-
|
|
35
|
+
- `review` exposes read-only workspace/Git/image tools.
|
|
36
|
+
- `edit` additionally exposes write, exact edit, and patch tools.
|
|
37
|
+
- `agent` additionally exposes direct argv execution and process sessions.
|
|
38
|
+
- `full` additionally exposes arbitrary shell commands.
|
|
35
39
|
|
|
36
|
-
|
|
40
|
+
`run_process` avoids shell parsing, which removes one injection class, but an executable such as `node`, `python`, a package manager, compiler, test runner, or repository script can execute arbitrary code. Direct mode therefore has effectively broad local-user authority once an attacker controls argv or executed code.
|
|
37
41
|
|
|
38
|
-
|
|
42
|
+
`exec_command` has both executable authority and shell expansion. Use `--no-exec` or `review`/`edit` when process execution is unnecessary.
|
|
39
43
|
|
|
40
|
-
|
|
44
|
+
For untrusted repositories or instructions, run the bridge inside a disposable VM/container or under a dedicated low-privilege OS account. On macOS and Windows, this external isolation is especially important. The project does not claim an in-process OS sandbox.
|
|
41
45
|
|
|
42
|
-
Filesystem
|
|
46
|
+
## Filesystem exposure
|
|
43
47
|
|
|
44
|
-
|
|
48
|
+
Direct filesystem tools are confined to the canonical selected workspace by default, including symbolic-link resolution. `--unrestricted-paths` expands this boundary and should be used only deliberately.
|
|
45
49
|
|
|
46
|
-
|
|
50
|
+
Returned paths are relative by default to reduce username and directory-layout disclosure. `--absolute-paths` changes metadata exposure without increasing direct access authority.
|
|
47
51
|
|
|
48
|
-
|
|
52
|
+
Sensitive-looking names are not blocked inside the workspace. Files such as `.env`, private keys, credentials, database dumps, and production configuration remain readable when they are in the selected workspace. Choose the narrowest practical workspace.
|
|
49
53
|
|
|
50
|
-
|
|
54
|
+
Processes are not confined by the filesystem-tool resolver. They can access paths, networks, processes, credential stores, and devices available to the local user.
|
|
51
55
|
|
|
52
|
-
|
|
56
|
+
## Mutation integrity
|
|
53
57
|
|
|
54
|
-
|
|
58
|
+
Writes are bounded, reject symbolic-link/non-regular destinations, use same-directory staging, and commit atomically per file. Create-only commit fails if a concurrent destination appears. Expected hashes and exact edits reduce accidental stale overwrites.
|
|
55
59
|
|
|
56
|
-
|
|
60
|
+
Patch operations prevalidate all paths/content, reject canonical collisions, recheck source hashes and destination absence, serialize bridge mutations, maintain backups, and roll back on ordinary commit errors.
|
|
57
61
|
|
|
58
|
-
|
|
62
|
+
These controls do not defend against a malicious process running under the same user racing filesystem metadata, nor do they make a multi-directory patch power-loss atomic.
|
|
59
63
|
|
|
60
|
-
|
|
64
|
+
## Credential exposure
|
|
61
65
|
|
|
62
|
-
|
|
66
|
+
Local state contains the MCP connection password and daemon secret. State, lock, temporary secret, runtime, and service-log files use owner-only permissions where supported. State writes are atomic. Logs recursively redact known credential fields and token formats and neutralize control characters.
|
|
67
|
+
|
|
68
|
+
First-run or explicit credential output can intentionally display the MCP password. Avoid shared terminal logs, shell recordings, screenshots, CI output, or support tickets. Use `--no-print-credentials` for recorded sessions.
|
|
69
|
+
|
|
70
|
+
Minimal process environment replaces HOME, temp, and common cache paths and does not pass arbitrary parent variables. It reduces accidental environment-secret leakage; it does not prevent code from explicitly accessing known resources. `--full-env` materially increases exposure.
|
|
71
|
+
|
|
72
|
+
## OAuth and public endpoints
|
|
73
|
+
|
|
74
|
+
Remote mode uses authorization code flow with PKCE S256, exact redirect/resource/client binding, expiring authorization codes and access tokens, hashed token storage, token-version revocation, and bounded dynamic client registration.
|
|
75
|
+
|
|
76
|
+
The authorization page displays the validated client name and redirect URI. Enter the connection password only after initiating the connection and recognizing both values.
|
|
77
|
+
|
|
78
|
+
Password failures and registrations are limited by deployment-keyed HMAC source identity. Browser requests are same-origin unless an exact origin is listed in `MBM_ALLOWED_ORIGINS`; loopback OAuth redirect permission does not grant browser-origin access.
|
|
79
|
+
|
|
80
|
+
Public health and metadata do not expose live workspace or daemon status. The daemon handshake omits workspace path/name/hash and process ID.
|
|
81
|
+
|
|
82
|
+
## Relay and denial of service
|
|
83
|
+
|
|
84
|
+
Only one authenticated daemon is active. Candidates have a handshake deadline and cannot displace the current daemon before success. Pending calls are socket-bound, client-request-bound, concurrency-limited, size-limited, and timed out. Duplicate in-flight request IDs for one access token are rejected.
|
|
85
|
+
|
|
86
|
+
Request bodies, WebSocket messages, tool outputs, traversals, sessions, stdin writes, OAuth records, clients, codes, tokens, and failure identities are bounded. Disconnect/replacement terminates active child process trees.
|
|
87
|
+
|
|
88
|
+
These controls reduce accidental exhaustion and simple abuse. They do not replace Cloudflare account MFA, WAF/rate limits, usage alerts, or cost controls for an internet-facing Worker.
|
|
89
|
+
|
|
90
|
+
## Process sessions
|
|
91
|
+
|
|
92
|
+
Process-session IDs are random and valid only inside one runtime. Output buffers and session counts are bounded. Sessions are killed on runtime stop or remote connection loss/replacement and expire from retained state after exit.
|
|
93
|
+
|
|
94
|
+
Sessions use pipes, not a PTY. Do not assume terminal-oriented programs will behave safely or correctly. Process output may contain secrets; it is returned to the authorized client but is not intentionally written to operational logs.
|
|
95
|
+
|
|
96
|
+
## Images and rich content
|
|
97
|
+
|
|
98
|
+
`view_image` accepts only signature-validated PNG, JPEG, GIF, and WebP under the size cap. SVG is intentionally excluded because it is active document content rather than a simple raster image. Image bytes pass through the Worker in remote mode and are visible to the authorized MCP client.
|
|
99
|
+
|
|
100
|
+
## Logs and privacy
|
|
101
|
+
|
|
102
|
+
Operational logs record coarse metadata and error classes, not tool arguments, command text, stdin, file/patch contents, or outputs. Unexpected Worker exceptions are reduced to error classes. Git author email is omitted from `git_log` unless explicitly requested.
|
|
103
|
+
|
|
104
|
+
No logging policy can prevent data from being returned to a client that explicitly invokes an enabled tool. The Worker necessarily relays remote tool arguments and results; this is not end-to-end encryption against the user's Cloudflare execution environment.
|
|
63
105
|
|
|
64
106
|
## Hardening checklist
|
|
65
107
|
|
|
66
|
-
For
|
|
108
|
+
For sensitive review:
|
|
109
|
+
|
|
110
|
+
```sh
|
|
111
|
+
machine-mcp --workspace /narrow/project --profile review --no-print-credentials
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
For controlled editing without execution:
|
|
67
115
|
|
|
68
|
-
```
|
|
69
|
-
machine-mcp --workspace /narrow/project --
|
|
116
|
+
```sh
|
|
117
|
+
machine-mcp --workspace /narrow/project --profile edit --no-print-credentials
|
|
70
118
|
```
|
|
71
119
|
|
|
72
120
|
Also:
|
|
73
121
|
|
|
74
|
-
-
|
|
75
|
-
- enable MFA on
|
|
76
|
-
- do not
|
|
77
|
-
- avoid `--full-env
|
|
122
|
+
- patch the OS and use supported Node.js releases;
|
|
123
|
+
- enable MFA on Cloudflare, GitHub, and npm accounts;
|
|
124
|
+
- do not configure broad CORS origins;
|
|
125
|
+
- avoid `--full-env`, `--unrestricted-paths`, and `--absolute-paths` unless required;
|
|
126
|
+
- inspect client names and OAuth redirect URIs;
|
|
78
127
|
- rotate secrets after suspected disclosure;
|
|
79
|
-
- inspect `
|
|
80
|
-
-
|
|
128
|
+
- inspect `status`, `doctor`, and service status;
|
|
129
|
+
- remove the Worker and state when remote access is no longer needed;
|
|
130
|
+
- use external OS isolation for untrusted code.
|
|
81
131
|
|
|
82
132
|
## Out of scope
|
|
83
133
|
|
|
84
|
-
The project cannot prevent an authorized
|
|
134
|
+
The project cannot prevent an authorized client from requesting data accessible to enabled tools, make arbitrary local executables safe, identify all sensitive content, or neutralize model prompt injection. Operator approval and narrow capability selection remain primary controls.
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -1,102 +1,169 @@
|
|
|
1
1
|
# Architecture
|
|
2
2
|
|
|
3
|
+
## Design goal
|
|
4
|
+
|
|
5
|
+
The system separates three questions that are often conflated:
|
|
6
|
+
|
|
7
|
+
1. **Who may request a tool?** Remote OAuth or local process ownership.
|
|
8
|
+
2. **Which tools exist?** The selected local policy profile.
|
|
9
|
+
3. **What authority does a tool have?** Canonical workspace boundaries for direct filesystem tools and local-user authority for processes.
|
|
10
|
+
|
|
11
|
+
No transport is treated as a sandbox. Both transports invoke the same local runtime.
|
|
12
|
+
|
|
3
13
|
## Components
|
|
4
14
|
|
|
5
|
-
###
|
|
15
|
+
### Shared tool catalog
|
|
6
16
|
|
|
7
|
-
|
|
17
|
+
`src/shared/tool-catalog.json` is the single source of truth for tool names, descriptions, input schemas, annotations, and policy availability. The Worker and stdio runtime import it directly. A catalog test rejects duplicate names, unknown availability classes, open schemas, missing annotations, profile drift, and a second hand-maintained Worker catalog.
|
|
8
18
|
|
|
9
|
-
###
|
|
19
|
+
### CLI and state layer
|
|
20
|
+
|
|
21
|
+
The CLI canonicalizes workspaces, resolves policy profiles, maintains per-workspace state and credentials, serializes startup/deploy/rotation with locks, deploys the Worker, installs optional platform-native autostart, and starts either remote daemon or stdio mode.
|
|
10
22
|
|
|
11
|
-
|
|
23
|
+
A canonical workspace receives an independent profile, Worker name, secret set, daemon/startup locks, and state file. State schema version 3 removes obsolete local API state and records the expanded policy model.
|
|
12
24
|
|
|
13
|
-
|
|
14
|
-
- the active daemon WebSocket and a minimal attachment containing connection time, enabled tools, and policy booleans;
|
|
15
|
-
- the bounded in-memory map of pending daemon calls.
|
|
25
|
+
### Local runtime
|
|
16
26
|
|
|
17
|
-
|
|
27
|
+
`LocalDaemon` is transport-independent despite its historical name. It owns:
|
|
18
28
|
|
|
19
|
-
|
|
29
|
+
- canonical path resolution and display-path privacy;
|
|
30
|
+
- file, text search, image, patch, and Git operations;
|
|
31
|
+
- direct and shell process execution;
|
|
32
|
+
- process-session buffers and stdin lifecycle;
|
|
33
|
+
- mutation serialization;
|
|
34
|
+
- child-process tracking and cancellation;
|
|
35
|
+
- output, traversal, concurrency, and time limits.
|
|
20
36
|
|
|
21
|
-
|
|
37
|
+
Remote mode attaches WebSocket connection/reconnect behavior. Stdio mode invokes the same runtime directly.
|
|
22
38
|
|
|
23
|
-
|
|
39
|
+
### Stdio MCP server
|
|
24
40
|
|
|
25
|
-
|
|
41
|
+
The stdio server implements newline-delimited JSON-RPC over stdin/stdout. It negotiates supported MCP versions, advertises policy-filtered tools, returns text plus structured content, supports native image content, maps cancellation notifications to runtime call IDs, and sends logs only to stderr.
|
|
42
|
+
|
|
43
|
+
### Cloudflare Worker and Durable Object
|
|
26
44
|
|
|
27
|
-
|
|
45
|
+
All requests for a deployed Worker route to one named Durable Object. It owns:
|
|
46
|
+
|
|
47
|
+
- OAuth clients, authorization codes, hashed access-token records, and throttling metadata;
|
|
48
|
+
- one active authenticated daemon WebSocket plus bounded candidate sockets;
|
|
49
|
+
- policy/tool metadata attached to the active socket;
|
|
50
|
+
- a bounded in-memory map of pending daemon calls.
|
|
51
|
+
|
|
52
|
+
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. It has no local filesystem or process API.
|
|
53
|
+
|
|
54
|
+
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.
|
|
55
|
+
|
|
56
|
+
### Autostart layer
|
|
57
|
+
|
|
58
|
+
The service layer emits launchd, systemd-user, or Windows Scheduled Task definitions. Credentials are not embedded in service definitions; the daemon loads owner-only state. The exact policy is stored in owner-only state; platform service definitions contain only the workspace and state-root selectors.
|
|
28
59
|
|
|
29
60
|
## Trust boundaries
|
|
30
61
|
|
|
31
62
|
```mermaid
|
|
32
63
|
flowchart LR
|
|
33
|
-
C[Remote MCP client] -->|HTTPS + OAuth bearer token| W[Worker]
|
|
34
|
-
W -->|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
64
|
+
C[Remote MCP client] -->|HTTPS + OAuth bearer token| W[Worker / Durable Object]
|
|
65
|
+
W -->|authenticated bounded WebSocket calls| R[Local runtime]
|
|
66
|
+
L[Local MCP client] -->|stdio JSON-RPC| R
|
|
67
|
+
R -->|canonical workspace tools| F[Selected workspace]
|
|
68
|
+
R -->|optional direct/shell processes| P[Local user / OS / network]
|
|
69
|
+
CLI[CLI + state] --> W
|
|
70
|
+
CLI --> R
|
|
71
|
+
CLI --> S[Autostart provider]
|
|
41
72
|
```
|
|
42
73
|
|
|
43
|
-
|
|
74
|
+
Remote OAuth determines which client may call tools. Local stdio access relies on the local process and configuration boundary. Policy determines which tools are advertised. Canonical resolution limits direct filesystem tools. Processes retain local-user authority and can escape workspace constraints through their own code or system calls.
|
|
44
75
|
|
|
45
|
-
##
|
|
76
|
+
## Remote request lifecycle
|
|
46
77
|
|
|
47
|
-
1. The MCP client discovers
|
|
78
|
+
1. The MCP client discovers protected-resource and authorization-server metadata.
|
|
48
79
|
2. It dynamically registers bounded redirect metadata.
|
|
49
|
-
3. The
|
|
50
|
-
4. The user verifies
|
|
51
|
-
5.
|
|
52
|
-
6. A valid verifier exchanges the code for
|
|
53
|
-
7.
|
|
54
|
-
8.
|
|
55
|
-
9.
|
|
80
|
+
3. The Worker validates authorization parameters before displaying a password form.
|
|
81
|
+
4. The user verifies client name and redirect URI and enters the connection password.
|
|
82
|
+
5. The Worker creates a five-minute code bound to client, redirect, resource, scope, and PKCE challenge.
|
|
83
|
+
6. A valid verifier exchanges the one-time code for an expiring bearer token; only its hash is stored.
|
|
84
|
+
7. The MCP client initializes and negotiates a supported protocol version.
|
|
85
|
+
8. `tools/list` is derived from the active daemon handshake; without a daemon, only `server_info` is advertised.
|
|
86
|
+
9. `tools/call` receives a random relay call ID and is bound to the current socket and authenticated client request key.
|
|
87
|
+
10. The runtime validates policy and arguments, executes the tool, and returns a bounded result.
|
|
88
|
+
11. The Durable Object accepts a result only from the socket that received that call.
|
|
89
|
+
12. A matching cancellation notification removes the pending call, tells the daemon to cancel, and terminates any child processes bound to it.
|
|
90
|
+
|
|
91
|
+
Duplicate in-flight JSON-RPC IDs for the same access token are rejected so cancellation cannot ambiguously target multiple calls.
|
|
92
|
+
|
|
93
|
+
## Stdio request lifecycle
|
|
94
|
+
|
|
95
|
+
1. The local client launches `machine-mcp stdio` with a workspace and profile.
|
|
96
|
+
2. The server negotiates one of the supported MCP versions.
|
|
97
|
+
3. Tool discovery is generated from the same catalog and policy used by remote mode.
|
|
98
|
+
4. Each call receives an internal random call ID used only for cancellation and process tracking.
|
|
99
|
+
5. Results are emitted as JSON-RPC on stdout; logs remain on stderr.
|
|
100
|
+
6. Duplicate in-flight request IDs are rejected.
|
|
101
|
+
7. Closing stdin cancels pending calls, terminates active processes, and removes the isolated runtime directory.
|
|
102
|
+
|
|
103
|
+
## Filesystem resolution and privacy
|
|
104
|
+
|
|
105
|
+
The workspace is canonicalized once. Existing targets use `realpath`; the result must remain inside the workspace unless unrestricted mode is explicit. New targets walk to the nearest existing ancestor and validate its canonical path.
|
|
106
|
+
|
|
107
|
+
Results use workspace-relative paths by default. Error strings redact canonical and common platform-alias forms of workspace, runtime, and home paths. Absolute paths are available only through an explicit display policy or unrestricted mode.
|
|
108
|
+
|
|
109
|
+
Symbolic-link destinations and non-regular write targets are rejected. Recursive walkers do not follow symbolic-link directories.
|
|
110
|
+
|
|
111
|
+
## Mutation model
|
|
112
|
+
|
|
113
|
+
All bridge mutations are serialized in one runtime queue.
|
|
114
|
+
|
|
115
|
+
### Whole-file write
|
|
116
|
+
|
|
117
|
+
A same-directory temporary file is created with restrictive permissions. Existing mode is preserved when replacing a regular file. Expected hashes are rechecked immediately before commit. Create-only uses a hard-link commit that fails atomically if the destination appeared concurrently.
|
|
118
|
+
|
|
119
|
+
### Exact edit
|
|
120
|
+
|
|
121
|
+
The current UTF-8 content is hashed. The old fragment must exist and, unless `replace_all` is set, occur exactly once. The resulting file is bounded and committed only if the original hash is unchanged.
|
|
122
|
+
|
|
123
|
+
### Patch transaction
|
|
124
|
+
|
|
125
|
+
The patch parser accepts a strict Begin/End envelope with add, update, move, and delete operations. It rejects malformed hunks, absent or ambiguous context, duplicate textual paths, and different paths that resolve to the same filesystem location.
|
|
126
|
+
|
|
127
|
+
Before commit, all sources and targets are resolved and validated, updated content is computed, and temporary files are staged. Source hashes and destination absence are rechecked. Existing sources are renamed to backups, staged files are committed, and any failure rolls back committed operations in reverse order. Backups are deleted only after full success.
|
|
56
128
|
|
|
57
|
-
|
|
129
|
+
This is a process-level transaction, not a filesystem-wide atomic transaction across multiple directories. Sudden power loss can still interrupt a multi-file commit; retained backup/temp naming makes recovery identifiable.
|
|
58
130
|
|
|
59
|
-
##
|
|
131
|
+
## Process model
|
|
60
132
|
|
|
61
|
-
|
|
133
|
+
`run_process` and process sessions use argv arrays and do not invoke a shell. `exec_command` invokes the platform shell and is available only in `shell` mode. Both inherit local-user OS authority.
|
|
62
134
|
|
|
63
|
-
|
|
135
|
+
Minimal environment mode 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.
|
|
64
136
|
|
|
65
|
-
|
|
137
|
+
One-shot calls have bounded output and timeouts. Process sessions retain bounded byte buffers with monotonic offsets, accept bounded stdin, support short output/exit waits, and are capped per runtime. Valid UTF-8 is returned as text; byte slices that are not valid UTF-8 also include lossless base64 data. Session IDs are random. Sessions are memory-only and are killed on runtime stop, remote disconnect, or daemon replacement.
|
|
66
138
|
|
|
67
|
-
|
|
139
|
+
Child processes run in a separate process group where supported. Timeout, cancellation, disconnect, and replacement send termination to the process tree, with forced escalation for timeout/disconnect paths.
|
|
68
140
|
|
|
69
|
-
##
|
|
141
|
+
## Daemon reconnect and replacement
|
|
70
142
|
|
|
71
|
-
|
|
143
|
+
The daemon sends heartbeats and reconnects with bounded exponential backoff and jitter. Only one socket is active. A new connection is a bounded candidate until it authenticates and completes `hello`; a Durable Object alarm enforces the deadline across hibernation. Only a completed candidate replaces the old socket.
|
|
72
144
|
|
|
73
|
-
|
|
74
|
-
- OAuth body: 64 KiB.
|
|
75
|
-
- Local WebSocket message: 8 MiB.
|
|
76
|
-
- `write_file` content: 5 MiB.
|
|
77
|
-
- `exec_command` command text: 64 KiB.
|
|
78
|
-
- Direct directory results: 10,000 entries and 4 MiB of path metadata.
|
|
79
|
-
- Recursive walks: 200,000 visited entries; path-list results are capped at 4 MiB.
|
|
80
|
-
- Captured process output: 512 KiB per stream by default.
|
|
81
|
-
- Local simultaneous tool calls: 16.
|
|
82
|
-
- Worker pending daemon calls: 32.
|
|
83
|
-
- Command timeout: 1-600 seconds.
|
|
84
|
-
- Dynamic clients, redirect URIs, codes, tokens, failed login identities, and per-client records: bounded constants in Worker source.
|
|
145
|
+
Pending calls retain their originating socket reference. A stale socket cannot complete or cancel replacement-socket calls. Closing a socket rejects only calls assigned to it.
|
|
85
146
|
|
|
86
147
|
## Persistence
|
|
87
148
|
|
|
88
|
-
|
|
149
|
+
Local state and global config are owner-only, versioned, and written through temporary files, flushes, and atomic rename. Malformed state becomes a bounded `.corrupt-*` backup. Custom roots are adopted only when empty or recognizable as legacy Machine Bridge state.
|
|
89
150
|
|
|
90
|
-
|
|
151
|
+
Removal validates the state marker, canonical target, known contents, active locks, filesystem root/home/current/package/workspace/source exclusions, and Worker deletion outcome before recursive deletion.
|
|
91
152
|
|
|
92
|
-
|
|
153
|
+
OAuth metadata is pruned on access. Expired codes/tokens, old throttling records, and inactive clients without active credentials are removed. Source identities are deployment-keyed HMAC values, not stored source addresses or reversible unsalted hashes.
|
|
93
154
|
|
|
94
|
-
## Observability
|
|
155
|
+
## Observability
|
|
95
156
|
|
|
96
|
-
Public health
|
|
157
|
+
Public health exposes only server identity and version. Authenticated `server_info` exposes bounded runtime status. Local operational logs record tool identity, shortened random call ID, duration, outcome, and coarse error class, but not arguments or outputs. Worker unexpected-error logs contain endpoint and error class, not raw exception text.
|
|
97
158
|
|
|
98
|
-
|
|
159
|
+
Cloudflare sampling is size control rather than an audit log. The project intentionally does not claim complete forensic logging.
|
|
99
160
|
|
|
100
|
-
##
|
|
161
|
+
## Explicit non-goals
|
|
101
162
|
|
|
102
|
-
|
|
163
|
+
- operating-system sandboxing of arbitrary executables;
|
|
164
|
+
- preventing an authorized client from requesting data available to enabled tools;
|
|
165
|
+
- automatically deciding which workspace files are sensitive;
|
|
166
|
+
- surviving daemon restart with process sessions;
|
|
167
|
+
- PTY/terminal emulation;
|
|
168
|
+
- model-level prompt-injection prevention;
|
|
169
|
+
- multi-user tenancy in one Worker deployment.
|
package/docs/CLIENTS.md
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Client configuration
|
|
2
|
+
|
|
3
|
+
## Choosing a transport
|
|
4
|
+
|
|
5
|
+
Use remote HTTPS/OAuth when the client cannot launch a local stdio process or must reach the machine from another device. Use stdio when the client runs on the same machine and supports local MCP processes.
|
|
6
|
+
|
|
7
|
+
| Client class | Recommended transport | Reason |
|
|
8
|
+
|---|---|---|
|
|
9
|
+
| ChatGPT remote connector / Developer Mode | Remote Worker | Public HTTPS endpoint and OAuth authorization |
|
|
10
|
+
| Claude Desktop | stdio | Local process transport; no cloud relay required |
|
|
11
|
+
| Cursor | stdio | Local process transport and direct workspace lifecycle |
|
|
12
|
+
| Codex CLI | stdio | Local MCP server configuration |
|
|
13
|
+
| Other MCP clients | stdio when local; remote when necessary | Minimize the trust and network boundary |
|
|
14
|
+
|
|
15
|
+
## Generate configuration
|
|
16
|
+
|
|
17
|
+
```sh
|
|
18
|
+
machine-mcp client-config --client all --workspace /path/to/project --profile agent
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
The generated command uses the current Node executable and installed entry script as absolute paths so it does not depend on GUI application PATH configuration.
|
|
22
|
+
|
|
23
|
+
## JSON stdio configuration
|
|
24
|
+
|
|
25
|
+
Claude Desktop, Cursor, and many generic clients use a shape similar to:
|
|
26
|
+
|
|
27
|
+
```json
|
|
28
|
+
{
|
|
29
|
+
"mcpServers": {
|
|
30
|
+
"machine-bridge": {
|
|
31
|
+
"command": "/absolute/path/to/node",
|
|
32
|
+
"args": [
|
|
33
|
+
"/absolute/path/to/machine-mcp.mjs",
|
|
34
|
+
"stdio",
|
|
35
|
+
"--workspace",
|
|
36
|
+
"/path/to/project",
|
|
37
|
+
"--profile",
|
|
38
|
+
"agent"
|
|
39
|
+
]
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Use the exact output of `client-config`; application-specific configuration locations change over time.
|
|
46
|
+
|
|
47
|
+
## Codex CLI TOML
|
|
48
|
+
|
|
49
|
+
```toml
|
|
50
|
+
[mcp_servers.machine_bridge]
|
|
51
|
+
command = "/absolute/path/to/node"
|
|
52
|
+
args = ["/absolute/path/to/machine-mcp.mjs", "stdio", "--workspace", "/path/to/project", "--profile", "agent"]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## ChatGPT remote connection
|
|
56
|
+
|
|
57
|
+
Run:
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
machine-mcp --workspace /path/to/project --profile review
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Enter the printed `/mcp` URL in the remote MCP connector. During OAuth authorization, verify the displayed client name and redirect URI before entering the connection password.
|
|
64
|
+
|
|
65
|
+
## Profile guidance
|
|
66
|
+
|
|
67
|
+
- Begin with `review` for an unfamiliar client or repository.
|
|
68
|
+
- Use `edit` when the client needs deterministic file mutation but no process execution.
|
|
69
|
+
- Use `agent` for normal coding work. It permits direct executables and process sessions, which remain powerful.
|
|
70
|
+
- Use `full` only when shell syntax is required and the client/instructions are trusted.
|
|
71
|
+
|
|
72
|
+
A client configuration is an authorization decision. Anyone who controls an authorized client can invoke every tool exposed by its selected profile.
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Operations
|
|
2
|
+
|
|
3
|
+
## Health and diagnosis
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
machine-mcp status
|
|
7
|
+
machine-mcp doctor
|
|
8
|
+
machine-mcp service status
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`status` prints redacted profile state and verifies the deployed Worker version. `doctor` checks Node.js, the package-installed Wrangler binary, Cloudflare login, and Worker health. Public `/healthz` output contains only server identity and version; daemon details require an authenticated `server_info` call.
|
|
12
|
+
|
|
13
|
+
## Logs
|
|
14
|
+
|
|
15
|
+
Remote autostart logs are stored under the state root in `logs/daemon.out.log` and `logs/daemon.err.log`. Files are owner-only where supported and tail-trimmed before daemon startup.
|
|
16
|
+
|
|
17
|
+
Structured operational events may include:
|
|
18
|
+
|
|
19
|
+
- component and severity;
|
|
20
|
+
- tool name;
|
|
21
|
+
- shortened random call identifier;
|
|
22
|
+
- duration and success/failure;
|
|
23
|
+
- coarse error class;
|
|
24
|
+
- connection/reconnect status.
|
|
25
|
+
|
|
26
|
+
They intentionally omit:
|
|
27
|
+
|
|
28
|
+
- file contents and image data;
|
|
29
|
+
- write, edit, or patch payloads;
|
|
30
|
+
- command argv, shell text, stdin, stdout, and stderr;
|
|
31
|
+
- MCP connection passwords, daemon secrets, authorization codes, and access tokens;
|
|
32
|
+
- full request bodies.
|
|
33
|
+
|
|
34
|
+
Unexpected Worker failures log endpoint path and error class rather than raw exception messages. Cloudflare observability is sampled and is not a complete audit log.
|
|
35
|
+
|
|
36
|
+
## Reconnect and replacement
|
|
37
|
+
|
|
38
|
+
The daemon sends heartbeats and reconnects with bounded exponential backoff and jitter. A new socket remains a candidate until it authenticates and sends a valid `hello`; only then does it replace the previous daemon.
|
|
39
|
+
|
|
40
|
+
Pending calls are bound to the socket that received them. Results from another socket are ignored. A lost or replaced socket rejects only its own pending calls and terminates locally tracked child process trees. Process sessions are in-memory and do not survive daemon restart or replacement.
|
|
41
|
+
|
|
42
|
+
## Limits
|
|
43
|
+
|
|
44
|
+
Defense-in-depth limits include:
|
|
45
|
+
|
|
46
|
+
- Worker MCP body: 8 MiB by default, hard cap 16 MiB;
|
|
47
|
+
- OAuth body: 64 KiB;
|
|
48
|
+
- daemon WebSocket message: 8 MiB;
|
|
49
|
+
- text writes and patch envelopes: 5 MiB;
|
|
50
|
+
- images: 4 MiB before base64 encoding;
|
|
51
|
+
- shell/argv envelope: 64 KiB;
|
|
52
|
+
- captured one-shot output: 512 KiB per stream by default;
|
|
53
|
+
- process-session retained output: 1 MiB per stream, with lossless base64 fallback for non-UTF-8 slices;
|
|
54
|
+
- process sessions: 8 retained per runtime;
|
|
55
|
+
- process stdin write: 64 KiB per call;
|
|
56
|
+
- local simultaneous tool calls: 16;
|
|
57
|
+
- Worker pending daemon calls: 32;
|
|
58
|
+
- command timeout: 1–600 seconds;
|
|
59
|
+
- process-session read wait: at most 30 seconds;
|
|
60
|
+
- direct directory result: 10,000 entries and 4 MiB of path metadata;
|
|
61
|
+
- recursive walk: 200,000 visited entries.
|
|
62
|
+
|
|
63
|
+
## Upgrade behavior
|
|
64
|
+
|
|
65
|
+
Version 0.4 changes the default for newly selected workspaces to `review`. Existing state retains its previous write and execution settings. To intentionally migrate an old profile:
|
|
66
|
+
|
|
67
|
+
```sh
|
|
68
|
+
machine-mcp --workspace /path/to/project --profile agent
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
A remote policy change is saved locally, propagated in the daemon handshake, and persisted in the autostart definition.
|
|
72
|
+
|
|
73
|
+
## Incident response
|
|
74
|
+
|
|
75
|
+
After suspected credential or client compromise:
|
|
76
|
+
|
|
77
|
+
1. stop foreground and autostart daemons;
|
|
78
|
+
2. run `machine-mcp rotate-secrets --no-print-credentials`;
|
|
79
|
+
3. restart without broad flags and redeploy;
|
|
80
|
+
4. inspect Cloudflare account access, Worker configuration, local state permissions, and service logs;
|
|
81
|
+
5. remove the Worker and local state if continued remote access is unnecessary.
|