machine-bridge-mcp 0.7.1 → 0.8.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 +29 -0
- package/CONTRIBUTING.md +8 -0
- package/README.md +5 -5
- package/docs/ARCHITECTURE.md +8 -6
- package/docs/ENGINEERING.md +159 -0
- package/docs/LOGGING.md +63 -28
- package/docs/OPERATIONS.md +6 -0
- package/docs/PRIVACY.md +6 -0
- package/docs/TESTING.md +8 -2
- package/package.json +8 -4
- package/src/local/cli.mjs +339 -280
- package/src/local/full-access-test.mjs +2 -2
- package/src/local/job-runner.mjs +3 -18
- package/src/local/log.mjs +2 -0
- package/src/local/managed-jobs.mjs +57 -77
- package/src/local/relay-connection.mjs +495 -0
- package/src/local/{daemon.mjs → runtime.mjs} +141 -188
- package/src/local/secure-file.mjs +27 -0
- package/src/local/ssh-key.mjs +37 -19
- package/src/local/stdio.mjs +86 -70
- package/src/worker/index.ts +11 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.8.0 - 2026-07-11
|
|
4
|
+
|
|
5
|
+
### Architecture
|
|
6
|
+
|
|
7
|
+
- Rename the transport-independent tool engine from `LocalDaemon`/`daemon.mjs` to `LocalRuntime`/`runtime.mjs`, reserving daemon terminology for the background process and relay attachment.
|
|
8
|
+
- Extract authenticated WebSocket lifecycle into `relay-connection.mjs`, separating transport state, handshake readiness, heartbeat liveness, reconnect backoff, and outage observability from local file/Git/process execution.
|
|
9
|
+
- Add `docs/ENGINEERING.md` as the normative record for product invariants, architectural boundaries, logging semantics, resilience, testing, documentation, and public-versus-local project knowledge. Record the owner-required default `full` profile as an explicit invariant.
|
|
10
|
+
|
|
11
|
+
### Logging and operator experience
|
|
12
|
+
|
|
13
|
+
- Replace raw default-level relay close output such as `{"code":1006,"reason":""}` with a state-transition policy: brief self-healing interruptions are debug-only, sustained outages produce one rate-limited actionable warning, and recovery produces one duration/attempt summary.
|
|
14
|
+
- Keep WebSocket close codes, reason strings, retry delays, heartbeat details, and brief recoveries at debug level. Expand coarse operational error classification for network and authentication failures; treat authentication and relay identity/version mismatch as immediate actionable fatal errors rather than retryable network outages.
|
|
15
|
+
- Add an ignored `.project-local/` area for machine-specific maintenance notes while keeping reusable decisions in tracked documentation and credentials out of both.
|
|
16
|
+
|
|
17
|
+
### Reliability and correctness
|
|
18
|
+
|
|
19
|
+
- Treat a WebSocket `open` event only as transport availability; authenticated relay readiness now requires `hello_ack`. Remove the startup path that printed `Remote MCP bridge is ready` after a timeout even when authentication had not completed.
|
|
20
|
+
- Terminate and retry candidates that do not acknowledge the daemon handshake, detect silent half-open connections through inbound heartbeat timeouts, and preserve bounded exponential reconnect backoff.
|
|
21
|
+
- Handle Cloudflare Durable Object `webSocketError` as well as `webSocketClose`, immediately rejecting pending calls bound to the failed socket through one idempotent cleanup path.
|
|
22
|
+
- Consolidate managed-job transition/recovery locking into one stale-PID-aware primitive and manager/runner regular-file reads into one no-follow bounded helper, with direct regression tests.
|
|
23
|
+
- Replace mechanical runtime/CLI switch and conditional routing with catalog-checked handler tables and named command phases while preserving explicit patch, runner, and recovery state machines.
|
|
24
|
+
- Replace technical Worker health warning details at default level with a user-facing reason while retaining the raw health code at debug.
|
|
25
|
+
|
|
26
|
+
### Installation, tests, and documentation
|
|
27
|
+
|
|
28
|
+
- Use the empirically verified npm 12 global install command with `--omit=optional` and the reviewed `esbuild,workerd,sharp,fsevents` script names. Add an isolated tarball/global-install smoke test that rejects blocked-script warnings, verifies `fsevents` is absent from the installed runtime, and executes the installed CLI.
|
|
29
|
+
- Add deterministic relay lifecycle tests for authenticated readiness, brief-interruption suppression, persistent-outage escalation, recovery summaries, handshake/heartbeat timeout, transport construction/error paths, supersession, acknowledgement identity/version mismatch, and close-code classification. Add architecture/documentation regression checks for module cycles, obsolete naming, broken links, invisible ASCII controls, and retained engineering invariants.
|
|
30
|
+
- Update architecture, operations, logging, privacy, testing, contribution, README, and CLI guidance to match the implemented behavior.
|
|
31
|
+
|
|
3
32
|
## 0.7.1 - 2026-07-11
|
|
4
33
|
|
|
5
34
|
### Fixed
|
package/CONTRIBUTING.md
CHANGED
|
@@ -21,3 +21,11 @@ A privacy or security correction is always release-relevant. Removing a private
|
|
|
21
21
|
## Privacy
|
|
22
22
|
|
|
23
23
|
Use only synthetic names, reserved example domains, and generic paths. Maintain private local identifiers in the ignored `.privacy-denylist` and run `npm run privacy:check` before committing.
|
|
24
|
+
|
|
25
|
+
## Engineering standards
|
|
26
|
+
|
|
27
|
+
Read [docs/ENGINEERING.md](docs/ENGINEERING.md) before changing architecture, policy, logging, persistence, transport lifecycle, or release behavior. The default `full` profile is an explicit product invariant and must not be narrowed by an unrelated change.
|
|
28
|
+
|
|
29
|
+
A log change is behavior: test its level, repetition policy, privacy fields, and recovery message. A transport change must distinguish low-level connectivity from authenticated readiness and test timeout/reconnect branches deterministically.
|
|
30
|
+
|
|
31
|
+
Reusable decisions belong in tracked documentation. Keep only machine-specific observations in the ignored `.project-local/` directory, and never store credentials there.
|
package/README.md
CHANGED
|
@@ -48,10 +48,10 @@ This default prioritizes usability, not least privilege. `run_process` and proce
|
|
|
48
48
|
Node.js 26 or newer and npm 12 or newer are required. The repository pins the active development versions in `.node-version`, `.nvmrc`, and `packageManager`; project installs fail on older Node runtimes.
|
|
49
49
|
|
|
50
50
|
```sh
|
|
51
|
-
npm install -g --allow-scripts=esbuild,workerd,sharp machine-bridge-mcp@latest
|
|
51
|
+
npm install -g --omit=optional --allow-scripts=esbuild,workerd,sharp,fsevents machine-bridge-mcp@latest
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
Recent npm releases may otherwise warn that Wrangler's native dependencies (`esbuild`, `workerd`, and `sharp`) have install scripts awaiting approval. The scoped command
|
|
54
|
+
Recent npm releases may otherwise warn that Wrangler's native dependencies (`esbuild`, `workerd`, and `sharp`) have install scripts awaiting approval. The scoped command approves the reviewed native script names that npm 12 evaluates during global resolution while `--omit=optional` keeps optional `fsevents` out of the installed runtime. `fsevents` is used for development-time filesystem watching rather than Machine Bridge runtime or deployment. It does not change the user's global npm policy. `machine-mcp doctor` remains the authoritative runtime check.
|
|
55
55
|
|
|
56
56
|
From a source checkout, the checked-in exact-version `allowScripts` policy approves the reviewed native dependencies:
|
|
57
57
|
|
|
@@ -335,7 +335,7 @@ Default state roots:
|
|
|
335
335
|
|
|
336
336
|
State/config writes use owner-only temporary files, flushes, and atomic rename. Malformed state is retained as a bounded corrupt backup before reconstruction. Resource source paths are redacted from `status` output. Active managed-job plans are owner-only and are deleted after a terminal result; bounded redacted results are retained temporarily. Uninstall validates markers, canonical paths, active locks, workspace/source exclusions, and known contents before recursive deletion.
|
|
337
337
|
|
|
338
|
-
Default foreground logs
|
|
338
|
+
Default foreground logs report authenticated relay readiness, persistent degradation, and recovery summaries rather than raw WebSocket callbacks. Brief self-healing disconnects and close codes/reasons are debug-only. Every per-tool event—including success, failure, cancellation, and slow-call timing—also appears only at `--log-level debug` or `--verbose`. Background services use `warn`, so ordinary tool outcomes and brief network changes do not fill daemon logs. Log messages and structured fields are bounded, secret-like keys and known token formats are redacted, and tool arguments/results are not written. See [docs/LOGGING.md](docs/LOGGING.md) and [docs/OPERATIONS.md](docs/OPERATIONS.md).
|
|
339
339
|
|
|
340
340
|
## Development and verification
|
|
341
341
|
|
|
@@ -348,9 +348,9 @@ npm audit --omit=dev --audit-level=high
|
|
|
348
348
|
npm pack --dry-run
|
|
349
349
|
```
|
|
350
350
|
|
|
351
|
-
`npm run check` covers privacy and release-impact gates, generated Worker types, TypeScript, JavaScript syntax,
|
|
351
|
+
`npm run check` covers privacy and release-impact gates, architecture/link invariants, generated Worker types, TypeScript, JavaScript syntax, catalog-to-runtime handler parity, deterministic relay lifecycle and secure-file tests, local path/write/process/state/log/service invariants, Ed25519/RSA generation and key-pair validation, real-machine `full` sandbox acceptance, a clean npm package-manifest/sensitive-artifact check, managed-job integrity/redaction/finally/cancellation/recovery, a live stdio MCP flow, and a live local OAuth/Worker/WebSocket/MCP flow. GitHub Actions runs the suite on Linux, macOS, and Windows with the pinned Node 26/npm 12 baseline; macOS and package-audit jobs also exercise the documented isolated global installation.
|
|
352
352
|
|
|
353
|
-
See [docs/MANAGED_JOBS.md](docs/MANAGED_JOBS.md), [docs/TESTING.md](docs/TESTING.md), [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), and [SECURITY.md](SECURITY.md).
|
|
353
|
+
See [docs/MANAGED_JOBS.md](docs/MANAGED_JOBS.md), [docs/TESTING.md](docs/TESTING.md), [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md), [docs/ENGINEERING.md](docs/ENGINEERING.md), and [SECURITY.md](SECURITY.md).
|
|
354
354
|
|
|
355
355
|
## Uninstall
|
|
356
356
|
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -24,7 +24,7 @@ A canonical workspace receives an independent profile, Worker name, secret set,
|
|
|
24
24
|
|
|
25
25
|
### Local runtime
|
|
26
26
|
|
|
27
|
-
`
|
|
27
|
+
`LocalRuntime` is the transport-independent local tool engine. It owns:
|
|
28
28
|
|
|
29
29
|
- canonical path resolution and display-path privacy;
|
|
30
30
|
- file, text search, image, patch, and Git operations;
|
|
@@ -36,7 +36,7 @@ A canonical workspace receives an independent profile, Worker name, secret set,
|
|
|
36
36
|
- child-process tracking and cancellation;
|
|
37
37
|
- output, traversal, concurrency, and time limits.
|
|
38
38
|
|
|
39
|
-
|
|
39
|
+
`RelayConnection` owns remote WebSocket transport, authenticated `hello_ack` readiness, heartbeat liveness, reconnect backoff, and outage logging. Stdio mode invokes `LocalRuntime` directly without that adapter.
|
|
40
40
|
|
|
41
41
|
### Managed job runner
|
|
42
42
|
|
|
@@ -177,13 +177,15 @@ Managed jobs use the same argv/environment primitives but a different lifecycle.
|
|
|
177
177
|
|
|
178
178
|
## Daemon reconnect and replacement
|
|
179
179
|
|
|
180
|
-
The
|
|
180
|
+
The local `RelayConnection` treats transport open, authenticated readiness, and outage recovery as separate states. It sends `hello` after WebSocket open and reports readiness only after `hello_ack`. A missing acknowledgement reaches a local handshake deadline and the candidate is terminated. Once ready, application heartbeats require inbound activity; a silent half-open socket is terminated and reconnected.
|
|
181
181
|
|
|
182
|
-
|
|
182
|
+
Reconnect uses bounded exponential backoff with jitter. Brief self-healing interruptions are debug-only. An unresolved outage is promoted to a rate-limited warning after a grace period, and recovery produces one summary. Raw close codes and reason strings remain debug-only.
|
|
183
|
+
|
|
184
|
+
The Worker also treats a new connection as 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. 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.
|
|
183
185
|
|
|
184
186
|
## Persistence
|
|
185
187
|
|
|
186
|
-
Local state and global config are owner-only, versioned, size-bounded, and written through temporary files, flushes, and atomic rename. State, managed-job manager, and detached runner commits share a bounded retrying atomic-replace primitive for transient Windows `EPERM`, `EACCES`, `EBUSY`, and `ENOTEMPTY` sharing failures.
|
|
188
|
+
Local state and global config are owner-only, versioned, size-bounded, and written through temporary files, flushes, and atomic rename. State, managed-job manager, and detached runner commits share a bounded retrying atomic-replace primitive for transient Windows `EPERM`, `EACCES`, `EBUSY`, and `ENOTEMPTY` sharing failures. Managed-job manager and detached runner use one shared no-follow, size-bounded regular-file reader; transition and recovery locks use one PID-lock primitive with bounded stale-lock reclamation and optional runner handoff. Malformed or oversized state becomes a bounded-count `.corrupt-*` backup. Resource paths are omitted from redacted status output. Custom roots are adopted only when empty or recognizable as legacy Machine Bridge state.
|
|
187
189
|
|
|
188
190
|
Active managed jobs persist an owner-only plan, status, runner PID, and bounded runner diagnostics. Terminal jobs delete the full plan and retain only bounded status/redacted results for up to seven days. This balances crash cleanup with minimization of scripts, stdin, argv, environment overrides, and resource source paths.
|
|
189
191
|
|
|
@@ -195,7 +197,7 @@ OAuth metadata is pruned on access. Expired codes/tokens, old throttling records
|
|
|
195
197
|
|
|
196
198
|
Public health exposes only server identity and version. Authenticated `server_info` exposes bounded runtime status, policy origin/revision, managed-job counts, resource alias names without paths or values, and the daemon/relay-advertised tool counts. It explicitly reports that the host-exposed subset is unknown to the server. `diagnose_runtime` runs fixed local probes and explicitly reports that its own request reached the daemon.
|
|
197
199
|
|
|
198
|
-
Foreground logging defaults to `info`; autostart uses `warn`.
|
|
200
|
+
Foreground logging defaults to `info`; autostart uses `warn`. Authenticated readiness, persistent degradation, and recovery are user-visible state transitions. Brief relay interruptions, raw transport close details, retry timing, and all per-tool starts/successes/failures/cancellations/durations are debug-only. Unexpected local and Worker infrastructure errors are reduced to classes. Messages, strings, arrays, object depth/key counts, and serialized fields are bounded.
|
|
199
201
|
|
|
200
202
|
Cloudflare sampling is size control rather than an audit log. The project intentionally does not claim complete forensic logging. See [LOGGING.md](LOGGING.md).
|
|
201
203
|
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Engineering principles and maintenance rules
|
|
2
|
+
|
|
3
|
+
This document records project-wide decisions that must survive individual fixes, releases, and maintainers. It is normative for repository changes. Machine-specific observations belong in the ignored `.project-local/` directory instead.
|
|
4
|
+
|
|
5
|
+
## Product and security invariants
|
|
6
|
+
|
|
7
|
+
1. **The default profile is intentionally `full`.** This is an explicit product-owner decision prioritizing usability. It must not be changed to a narrower default as an incidental security cleanup. Documentation must continue to explain the authority and risks clearly.
|
|
8
|
+
2. **Named profiles are canonical contracts.** A state labelled `full`, `agent`, `edit`, or `review` must match the complete capability set for that name. Per-capability overrides produce `custom` rather than a misleading named profile.
|
|
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
|
+
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
|
+
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 version across all surfaces.** Package metadata, Worker version, Git tag, GitHub Release, npm version, documentation, and deployed health version must agree before a release is considered complete.
|
|
13
|
+
|
|
14
|
+
A proposed change that conflicts with an invariant requires an explicit owner decision and corresponding documentation update. It must not be hidden inside an unrelated refactor.
|
|
15
|
+
|
|
16
|
+
## Architectural boundaries
|
|
17
|
+
|
|
18
|
+
The preferred dependency direction is:
|
|
19
|
+
|
|
20
|
+
```text
|
|
21
|
+
shared metadata and pure policy
|
|
22
|
+
|
|
|
23
|
+
local runtime domain modules Worker domain modules
|
|
24
|
+
| |
|
|
25
|
+
stdio adapter / relay connection HTTP + OAuth + WebSocket adapter
|
|
26
|
+
| |
|
|
27
|
+
CLI, service, release, and deployment orchestration
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Rules:
|
|
31
|
+
|
|
32
|
+
- Transport lifecycle, domain execution, persistence, and presentation are separate modules.
|
|
33
|
+
- Pure classification and normalization functions are exported and tested directly when practical.
|
|
34
|
+
- Adapters may translate data but should not duplicate policy or schemas.
|
|
35
|
+
- State transitions are explicit; readiness is not inferred from a lower-level event. For example, an open WebSocket is not an authenticated relay until `hello_ack` is received.
|
|
36
|
+
- Every externally controlled input is bounded before expensive allocation, traversal, parsing, storage, or execution.
|
|
37
|
+
- Repository text must not contain invisible ASCII controls other than tab, CR, and LF; architecture tests enforce this even when JavaScript syntax remains valid.
|
|
38
|
+
- Persistent mutations use owner-only files, bounded reads, atomic replacement, and integrity checks appropriate to the data.
|
|
39
|
+
- Retry is limited to classified transient failures. Authentication, authorization, validation, integrity, and policy errors fail immediately.
|
|
40
|
+
- Cleanup-only catches may be best effort, but primary failures must not be silently discarded.
|
|
41
|
+
- New work should not increase an already broad orchestration module when the behavior has an independent lifecycle or test surface. Extract the domain first.
|
|
42
|
+
|
|
43
|
+
`runtime.mjs` owns local tool semantics. `relay-connection.mjs` owns authenticated relay connection lifecycle. The CLI orchestrates them; it must not become the second implementation of either.
|
|
44
|
+
|
|
45
|
+
## Logging contract
|
|
46
|
+
|
|
47
|
+
Operational logs are a user interface, not a dump of protocol events.
|
|
48
|
+
|
|
49
|
+
### Default-level rules
|
|
50
|
+
|
|
51
|
+
- Describe user-relevant state and automatic remediation in plain language.
|
|
52
|
+
- Prefer state transitions and recovery summaries over event pairs.
|
|
53
|
+
- Suppress brief self-healing transport interruptions from `info` and `warn`.
|
|
54
|
+
- Escalate a connection outage only after it remains unresolved for the configured grace period.
|
|
55
|
+
- Rate-limit repeated degradation warnings and emit one recovery summary after a visible outage.
|
|
56
|
+
- Do not expose WebSocket close codes, empty reason strings, stack traces, raw URLs with credentials, or internal identifiers at default levels.
|
|
57
|
+
- Include fields only when they help a user decide what happened or what to do next.
|
|
58
|
+
|
|
59
|
+
### Level meanings
|
|
60
|
+
|
|
61
|
+
- `error`: the requested operation or long-lived service cannot continue without intervention.
|
|
62
|
+
- `warn`: persistent degradation, rejected protocol data, supersession, or a service problem requiring attention.
|
|
63
|
+
- `info`: successful startup, authenticated readiness, deployment, and recovery from a previously visible degradation.
|
|
64
|
+
- `debug`: raw transport codes/reasons, retry timing, correlation identifiers, per-tool outcomes, and implementation diagnostics.
|
|
65
|
+
|
|
66
|
+
A warning must answer at least one of these questions: what is degraded, whether recovery is automatic, and what action is required if recovery fails.
|
|
67
|
+
|
|
68
|
+
### Review test
|
|
69
|
+
|
|
70
|
+
Before adding a default-level log, ask:
|
|
71
|
+
|
|
72
|
+
1. Is this a user-visible state change rather than an implementation callback?
|
|
73
|
+
2. Is the severity correct if the system recovers automatically within seconds?
|
|
74
|
+
3. Is every field understandable without consulting a protocol specification?
|
|
75
|
+
4. Could the message reveal a path, identity, command, content, or credential?
|
|
76
|
+
5. Will this line remain useful if the event repeats one hundred times?
|
|
77
|
+
|
|
78
|
+
See [LOGGING.md](LOGGING.md) for the concrete event policy.
|
|
79
|
+
|
|
80
|
+
## Complexity and state-machine exceptions
|
|
81
|
+
|
|
82
|
+
Mechanical routing, option validation, and output formatting should be table-driven or split into named handlers. Catalog-to-handler parity must be executable, not inferred from a switch statement.
|
|
83
|
+
|
|
84
|
+
A higher branch count is acceptable only when the function is an explicit state machine whose ordering is part of the safety argument, such as patch parsing, detached job execution, or recovery reconciliation. Such functions require focused transition and failure-path tests. Do not fragment them merely to satisfy a numeric complexity threshold; do not use this exception for ordinary command dispatch or mixed responsibilities.
|
|
85
|
+
|
|
86
|
+
## Error handling and resilience
|
|
87
|
+
|
|
88
|
+
- Preserve a stable coarse error class for automation and a concise human message for operators.
|
|
89
|
+
- Do not retry an operation merely because it failed; retry only when the error is positively classified as transient and the operation is idempotent or server state is checked after ambiguity.
|
|
90
|
+
- When a remote write may have succeeded before the response was lost, query authoritative state before repeating it.
|
|
91
|
+
- Timeouts must terminate the relevant process or connection and clear delayed escalation timers after exit.
|
|
92
|
+
- Half-open connections need liveness detection, not only periodic writes.
|
|
93
|
+
- Lock reclamation must consider both process liveness and absolute age to defend against PID reuse.
|
|
94
|
+
- A recovery path must be bounded and converge to a terminal state rather than retry forever.
|
|
95
|
+
|
|
96
|
+
## Testing rules
|
|
97
|
+
|
|
98
|
+
Every defect fix includes a regression test that fails for the original reason. Prefer two layers when applicable:
|
|
99
|
+
|
|
100
|
+
1. a deterministic test of the extracted policy or lifecycle;
|
|
101
|
+
2. an integration test proving the adapter uses it correctly.
|
|
102
|
+
|
|
103
|
+
The required matrix includes:
|
|
104
|
+
|
|
105
|
+
- Linux, macOS, and Windows on the pinned Node/npm baseline;
|
|
106
|
+
- privacy and release-impact gates;
|
|
107
|
+
- package-manifest and sensitive-artifact inspection;
|
|
108
|
+
- type and syntax checks;
|
|
109
|
+
- local runtime and real `full` acceptance tests;
|
|
110
|
+
- stdio JSON-RPC integration;
|
|
111
|
+
- Worker OAuth/WebSocket/MCP integration;
|
|
112
|
+
- managed-job integrity, recovery, cancellation, cleanup, and redaction;
|
|
113
|
+
- dependency audit, registry signatures/attestations, SBOM, and Worker dry run.
|
|
114
|
+
|
|
115
|
+
Cross-platform tests must not depend on shell syntax, case-sensitive Windows paths, Unix-only executable shims, or timing races when a deterministic scheduler can be injected.
|
|
116
|
+
|
|
117
|
+
## Documentation rules
|
|
118
|
+
|
|
119
|
+
- README explains the supported user path and major risks.
|
|
120
|
+
- ARCHITECTURE describes boundaries, trust, and state machines.
|
|
121
|
+
- SECURITY states guarantees and explicit non-guarantees.
|
|
122
|
+
- LOGGING defines operator-facing event semantics.
|
|
123
|
+
- OPERATIONS contains diagnosis and recovery procedures.
|
|
124
|
+
- TESTING records executable coverage and regression expectations.
|
|
125
|
+
- CHANGELOG records externally relevant changes, including documentation and workflow changes.
|
|
126
|
+
|
|
127
|
+
Documentation that claims a guarantee must identify the code or test enforcing it. Do not document an aspirational behavior as implemented.
|
|
128
|
+
|
|
129
|
+
## Public and local project knowledge
|
|
130
|
+
|
|
131
|
+
Reusable decisions, invariants, incident lessons, and review rules belong in tracked documentation. Hiding them in local notes causes repeated mistakes.
|
|
132
|
+
|
|
133
|
+
Use the ignored `.project-local/` directory only for machine-specific material such as:
|
|
134
|
+
|
|
135
|
+
- local package-manager or service layout;
|
|
136
|
+
- temporary release blockers and authentication state;
|
|
137
|
+
- private deployment identifiers;
|
|
138
|
+
- one-machine recovery steps;
|
|
139
|
+
- observations that still require validation before becoming a general rule.
|
|
140
|
+
|
|
141
|
+
Do not store passwords, tokens, private keys, authorization URLs, or copied secret-bearing logs there. Use `.privacy-denylist` for private vocabulary that the scanner should reject from publication surfaces.
|
|
142
|
+
|
|
143
|
+
When a local observation becomes generally true, move the sanitized lesson into tracked documentation and delete the stale local note.
|
|
144
|
+
|
|
145
|
+
## Review checklist
|
|
146
|
+
|
|
147
|
+
A thorough review asks:
|
|
148
|
+
|
|
149
|
+
- Can the module name and boundary be understood without historical context?
|
|
150
|
+
- Is there one authoritative implementation of each policy and schema?
|
|
151
|
+
- Are all success, failure, cancellation, timeout, disconnect, restart, and recovery branches bounded and tested?
|
|
152
|
+
- Are logs actionable, rate-limited, and privacy-preserving?
|
|
153
|
+
- Are persistent files atomic, owner-only, size-bounded, and symlink-aware?
|
|
154
|
+
- Can a stale PID, stale socket, duplicate request, partial write, or ambiguous remote response violate integrity?
|
|
155
|
+
- Are package, CI, Worker, service, and release behavior tested on every supported platform?
|
|
156
|
+
- Does the complete diff contain any real identifier, path, host, account, or credential-shaped value?
|
|
157
|
+
- Does the change require a new npm version and deployment?
|
|
158
|
+
|
|
159
|
+
The goal is not cleverness or minimum line count. The goal is explicit boundaries, small state machines, predictable failure behavior, and tests that make future regressions difficult.
|
package/docs/LOGGING.md
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
# Logging and observability
|
|
2
2
|
|
|
3
|
+
Operational logs are a user-facing diagnosis surface. They report meaningful service state without becoming a transcript of user activity or a dump of transport callbacks.
|
|
4
|
+
|
|
3
5
|
## Goals
|
|
4
6
|
|
|
5
|
-
|
|
7
|
+
Logs should answer:
|
|
6
8
|
|
|
7
|
-
1. Did deployment and relay
|
|
8
|
-
2. Is
|
|
9
|
-
3.
|
|
10
|
-
4.
|
|
9
|
+
1. Did deployment and authenticated relay readiness succeed?
|
|
10
|
+
2. Is a service persistently degraded, and is recovery automatic?
|
|
11
|
+
3. Did the service recover, and how long was it unavailable?
|
|
12
|
+
4. Is an infrastructure, protocol, deployment, or local service problem requiring action?
|
|
13
|
+
5. When debug logging is explicitly enabled, which bounded implementation event should be correlated?
|
|
11
14
|
|
|
12
15
|
Logs are not a command history, content audit trail, or substitute for MCP client approvals.
|
|
13
16
|
|
|
@@ -21,57 +24,89 @@ The CLI accepts:
|
|
|
21
24
|
--verbose # alias for debug
|
|
22
25
|
```
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
Foreground mode defaults to `info`. Platform autostart services use `warn`.
|
|
25
28
|
|
|
26
29
|
| Level | Intended events |
|
|
27
30
|
|---|---|
|
|
28
|
-
| `error` |
|
|
29
|
-
| `warn` |
|
|
30
|
-
| `info` | Deployment/startup transitions and
|
|
31
|
-
| `debug` |
|
|
31
|
+
| `error` | The operation or service cannot continue without intervention |
|
|
32
|
+
| `warn` | Persistent relay outage, malformed/oversized protocol data, supersession, or service/autostart degradation |
|
|
33
|
+
| `info` | Deployment/startup transitions, authenticated relay readiness, and recovery after a visible outage |
|
|
34
|
+
| `debug` | Transport open/close codes and reasons, brief self-healing interruptions, retry timing, per-tool outcomes, and shortened correlation IDs |
|
|
35
|
+
|
|
36
|
+
## Relay connection event policy
|
|
37
|
+
|
|
38
|
+
A TCP/WebSocket `open` event is only transport availability. The daemon is reported as connected only after the Worker returns `hello_ack` for the authenticated `hello` message.
|
|
39
|
+
|
|
40
|
+
Brief network interruptions are expected on laptop network changes, Worker deployment, proxy rotation, and ordinary internet transport. They are handled as follows:
|
|
32
41
|
|
|
33
|
-
|
|
42
|
+
- the raw close code, close reason, error class, connected duration, and retry delay are debug-only;
|
|
43
|
+
- a brief interruption that reconnects within the grace period produces no `info` or `warn` line;
|
|
44
|
+
- an outage that remains unresolved for 10 seconds produces one rate-limited warning stating that automatic reconnection is in progress;
|
|
45
|
+
- relay identity/version mismatch and authentication rejection are non-transient: they produce an immediate actionable error and terminate that daemon instead of entering the reconnect loop;
|
|
46
|
+
- repeated warnings are rate-limited;
|
|
47
|
+
- recovery after a visible outage produces one information summary with outage duration and attempt count;
|
|
48
|
+
- an authenticated replacement is a distinct warning and permanently stops the older daemon;
|
|
49
|
+
- failure to receive `hello_ack` within the handshake deadline terminates the candidate socket and retries;
|
|
50
|
+
- lack of inbound heartbeat activity terminates a half-open socket and reconnects.
|
|
34
51
|
|
|
35
|
-
|
|
52
|
+
A WebSocket close code such as `1006` means the transport ended without a normal close handshake. It is useful for debug diagnosis but not useful as the default user message. Default logs therefore describe the effect and recovery behavior rather than printing `{"code":1006,"reason":""}`.
|
|
36
53
|
|
|
37
|
-
|
|
54
|
+
Examples:
|
|
55
|
+
|
|
56
|
+
```text
|
|
57
|
+
[info] daemon: remote relay connected
|
|
58
|
+
[warn] daemon: remote relay is unavailable; automatic reconnection is still in progress {"outage_seconds":12,"attempts":3,"cause":"connection interrupted"}
|
|
59
|
+
[info] daemon: remote relay connection restored {"outage_seconds":18,"attempts":4}
|
|
60
|
+
```
|
|
38
61
|
|
|
39
|
-
|
|
62
|
+
With `--verbose`, the same incident may additionally include bounded transport diagnostics.
|
|
40
63
|
|
|
41
|
-
|
|
42
|
-
|
|
64
|
+
## Tool events
|
|
65
|
+
|
|
66
|
+
All per-tool starts, successes, failures, cancellations, and timing are debug-only. The MCP response already reports the outcome to the caller; duplicating routine tool traffic at default levels creates noise and can reveal activity patterns.
|
|
67
|
+
|
|
68
|
+
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.
|
|
69
|
+
|
|
70
|
+
## Data that is never logged
|
|
71
|
+
|
|
72
|
+
The implementation omits:
|
|
73
|
+
|
|
74
|
+
- tool arguments and command text or argv;
|
|
43
75
|
- stdin, stdout, and stderr;
|
|
44
|
-
- file, patch,
|
|
76
|
+
- file, patch, image, and temporary-file content;
|
|
45
77
|
- OAuth request bodies;
|
|
46
|
-
- connection passwords, daemon secrets, authorization codes, and access tokens
|
|
78
|
+
- connection passwords, daemon secrets, authorization codes, and access tokens;
|
|
79
|
+
- registered resource values and source paths.
|
|
47
80
|
|
|
48
|
-
Unexpected
|
|
81
|
+
Unexpected infrastructure failures are reduced to coarse error classes in normal logs. Client-facing tool errors may contain more detail according to the active path-display policy, but those details are not copied into operational logs.
|
|
49
82
|
|
|
50
83
|
## Bounding and redaction
|
|
51
84
|
|
|
52
|
-
Messages, strings, object depth, object key counts, array item counts, and serialized field payloads are bounded. Control characters are neutralized. Fields with secret-like names and known token formats are recursively redacted.
|
|
85
|
+
Messages, strings, object depth, object key counts, array item counts, and serialized field payloads are bounded. Control characters and Unicode display controls are neutralized. Fields with secret-like names, path-like keys, and known token formats are recursively redacted.
|
|
53
86
|
|
|
54
|
-
This is defense in depth, not content classification.
|
|
87
|
+
This is defense in depth, not content classification. Unknown secret formats can evade pattern matching, which is why tool arguments and outputs are omitted rather than merely filtered.
|
|
55
88
|
|
|
56
89
|
## Files
|
|
57
90
|
|
|
58
|
-
Autostart logs are stored below the state root:
|
|
91
|
+
Autostart logs are stored below the owner-only state root:
|
|
59
92
|
|
|
60
93
|
```text
|
|
61
94
|
logs/daemon.out.log
|
|
62
95
|
logs/daemon.err.log
|
|
63
96
|
```
|
|
64
97
|
|
|
65
|
-
|
|
98
|
+
Existing files are opened without following symbolic links where supported and tail-trimmed on UTF-8/line boundaries before startup. Background services use `warn`, so ordinary tool traffic and brief relay interruptions do not cause sustained growth.
|
|
66
99
|
|
|
67
|
-
Each managed job
|
|
100
|
+
Each managed job has owner-only runner diagnostic logs. Child-step output is retained only in bounded, redacted job results according to `capture_output`; it is not copied into daemon or runner operational logs.
|
|
68
101
|
|
|
69
102
|
## MCP host boundary
|
|
70
103
|
|
|
71
|
-
Machine Bridge does not classify filenames as sensitive and does not block
|
|
104
|
+
Machine Bridge does not classify filenames as sensitive and does not block credential-looking names under canonical `full`. An MCP host, connector, model provider, desktop application, operating system, or endpoint-security layer may independently reject a request before it reaches Machine Bridge.
|
|
105
|
+
|
|
106
|
+
Use `server_info`, `machine-mcp status`, `machine-mcp doctor`, and `diagnose_runtime` to distinguish local policy from host-side enforcement. Changing the Machine Bridge profile cannot override another layer.
|
|
72
107
|
|
|
73
|
-
|
|
108
|
+
## Adding or changing logs
|
|
74
109
|
|
|
75
|
-
|
|
110
|
+
A default-level message must be actionable, privacy-preserving, and resistant to repetition. Add a regression test for severity and field visibility. Raw protocol values belong at debug unless a user can act on them without external documentation.
|
|
76
111
|
|
|
77
|
-
|
|
112
|
+
See [ENGINEERING.md](ENGINEERING.md) for the project-wide review rules.
|
package/docs/OPERATIONS.md
CHANGED
|
@@ -24,6 +24,12 @@ machine-mcp service status
|
|
|
24
24
|
|
|
25
25
|
A successful diagnostic result applies only to that probe. An MCP host can still deny a later call based on its own request context. This is expected layering, not a defect in the `full` profile: `full` removes Machine Bridge's own denials, while host delivery remains independent.
|
|
26
26
|
|
|
27
|
+
### Relay interruption messages
|
|
28
|
+
|
|
29
|
+
A brief relay interruption is retried automatically and is visible only with `--verbose`. Default logs do not print raw WebSocket values such as `code=1006` with an empty reason. If a transient outage persists for 10 seconds, one warning is emitted and repeated warnings are rate-limited; a later recovery produces one summary with outage duration and attempt count. Identity/version mismatch and authentication rejection are not retried as ordinary network faults: the daemon emits an immediate actionable error and exits, requiring upgrade/redeployment or credential repair.
|
|
30
|
+
|
|
31
|
+
Use `--verbose` only when close codes, close reasons, heartbeat timeouts, and retry delays are needed for diagnosis. A close code of 1006 means the transport ended without a normal close handshake; it does not by itself identify the cause.
|
|
32
|
+
|
|
27
33
|
## Logs
|
|
28
34
|
|
|
29
35
|
Remote autostart definitions prefer a stable PATH alias that resolves to the currently running Node executable and persist a sanitized absolute-only service `PATH` containing the Node/CLI directories, the installer's absolute PATH entries, and platform defaults. This avoids versioned Homebrew-style paths becoming invalid after upgrades and prevents launchd/systemd from falling back to a minimal system-only PATH. Re-run `machine-mcp service install` after changing Node installation families or PATH layout. 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.
|
package/docs/PRIVACY.md
CHANGED
|
@@ -21,6 +21,12 @@ chmod 600 .privacy-denylist
|
|
|
21
21
|
|
|
22
22
|
Add one identifier per line. The denylist is deliberately local and must never be committed. CI still runs the built-in generic checks; a developer's local check adds their private vocabulary.
|
|
23
23
|
|
|
24
|
+
## Local maintenance notes
|
|
25
|
+
|
|
26
|
+
Machine-specific operational notes may be kept under the ignored `.project-local/` directory. Use it for temporary environment state and one-machine recovery observations, not for reusable engineering decisions. General lessons belong in tracked documentation such as `ENGINEERING.md`.
|
|
27
|
+
|
|
28
|
+
Ignored does not mean safe for secrets: do not store passwords, tokens, private keys, authorization URLs, or copied secret-bearing logs there. `.privacy-denylist` remains the dedicated local vocabulary gate.
|
|
29
|
+
|
|
24
30
|
## Review rules
|
|
25
31
|
|
|
26
32
|
Before committing or publishing:
|
package/docs/TESTING.md
CHANGED
|
@@ -37,6 +37,8 @@ The suite includes:
|
|
|
37
37
|
- guarded state-root removal, schema migration, policy-origin persistence, and legacy implicit-default migration;
|
|
38
38
|
- no filename-based sensitive-file denial under unrestricted policy;
|
|
39
39
|
- log redaction, control-character handling, message/field bounds, suppression of both successful and failed per-tool events outside debug, and service warning-level configuration;
|
|
40
|
+
- deterministic relay connection lifecycle coverage for transport construction/error, authenticated `hello_ack` readiness, identity/version mismatch, handshake and heartbeat timeout, brief-outage suppression, sustained-outage escalation, recovery summaries, and supersession;
|
|
41
|
+
- shared no-follow bounded-file reads for normal files, over-limit data, directories, and symbolic links;
|
|
40
42
|
- CLI parsing, policy profiles, and client configuration boundaries;
|
|
41
43
|
- live stdio MCP initialization, discovery, calls, rich content, sessions, cancellation, managed-job acceptance, and a detached job/finally phase that survives stdio shutdown;
|
|
42
44
|
- live local Worker OAuth registration, consent, PKCE, token replay rejection, throttling, CORS, protocol negotiation, dynamic tool advertisement, rich content, daemon replacement, and cancellation.
|
|
@@ -54,7 +56,7 @@ npm run version:check
|
|
|
54
56
|
npm run release-impact:check
|
|
55
57
|
```
|
|
56
58
|
|
|
57
|
-
GitHub Actions executes the main suite on Linux, macOS, and Windows using the pinned Node 26/npm 12 baseline. Because Node 26 currently bundles npm 11, CI explicitly disables setup-node's automatic package-manager cache and upgrades npm from the runner temporary directory before any project-local npm command can trigger strict `devEngines`. Checkout fetches version tags so the release-impact gate can compare the branch with the latest release. A separate package-audit job audits both the complete dependency graph and the production-only graph, verifies registry signatures and attestations, validates a CycloneDX SBOM written under the runner temporary directory, then performs a dry-run package build. Dependency and GitHub Actions updates are monitored by Dependabot.
|
|
59
|
+
GitHub Actions executes the main suite on Linux, macOS, and Windows using the pinned Node 26/npm 12 baseline. Because Node 26 currently bundles npm 11, CI explicitly disables setup-node's automatic package-manager cache and upgrades npm from the runner temporary directory before any project-local npm command can trigger strict `devEngines`. Checkout fetches version tags so the release-impact gate can compare the branch with the latest release. A separate package-audit job audits both the complete dependency graph and the production-only graph, verifies registry signatures and attestations, validates a CycloneDX SBOM written under the runner temporary directory, exercises the documented isolated global installation, then performs a dry-run package build. The macOS matrix job also runs the installation smoke test because Wrangler's optional `fsevents` resolution is platform-specific. Dependency and GitHub Actions updates are monitored by Dependabot.
|
|
58
60
|
|
|
59
61
|
## Test design rules
|
|
60
62
|
|
|
@@ -74,6 +76,10 @@ Run `npm run privacy:check` before committing and before packaging. Developers s
|
|
|
74
76
|
|
|
75
77
|
## Package manifest
|
|
76
78
|
|
|
77
|
-
`npm run package:test` executes a real silent `npm pack --dry-run --json`, requires clean parseable JSON, rejects sensitive local artifacts and credential-like file classes, and verifies that privacy guidance,
|
|
79
|
+
`npm run package:test` executes a real silent `npm pack --dry-run --json`, requires clean parseable JSON, rejects sensitive local artifacts and credential-like file classes, and verifies that privacy/engineering guidance, the runtime/relay/secure-file modules, contribution discipline, and privacy/release-impact checkers are present in the published package. `npm run install:test` packs the real tarball, installs it into an isolated global prefix with the documented npm 12 options, rejects blocked-script warnings, confirms optional `fsevents` is absent, and runs the installed CLI.
|
|
78
80
|
|
|
79
81
|
The stdio integration test also sends an oversized line, verifies bounded rejection, and confirms that the next valid request is still processed.
|
|
82
|
+
|
|
83
|
+
## Architecture and documentation regression checks
|
|
84
|
+
|
|
85
|
+
`npm run architecture:test` rejects local-module dependency cycles, missing relative imports, obsolete `LocalDaemon`/`daemon.mjs` naming, broken relative Markdown links, invisible ASCII control bytes in repository text, removal of the owner-required default-`full` engineering invariant, and accidental publication of `.project-local/` notes.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "machine-bridge-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.8.0",
|
|
4
4
|
"description": "Cross-client MCP bridge for canonical local automation, files, Git, SSH resources, managed jobs, and processes over stdio or an OAuth-protected remote relay.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -43,8 +43,8 @@
|
|
|
43
43
|
"prepublishOnly": "npm run check && npm run version:check && npm run release:check",
|
|
44
44
|
"worker:types": "wrangler types src/worker/worker-configuration.d.ts",
|
|
45
45
|
"typecheck": "npm run worker:types && tsc -p tsconfig.json --noEmit",
|
|
46
|
-
"syntax": "sh -n mbm && node --check bin/machine-mcp.mjs && node --check src/local/cli.mjs && node --check src/local/
|
|
47
|
-
"check": "npm run privacy:check && npm run privacy:test && npm run release-impact:check && npm run release-impact:test && npm run network-retry:test && npm run typecheck && npm run syntax && npm run catalog:test && npm run self-test && npm run atomic-fs:test && npm run package:test && npm run ssh-key:test && npm run full-access:test && npm run managed-jobs:test && npm run stdio:integration-test && npm run worker:integration-test",
|
|
46
|
+
"syntax": "sh -n mbm && node --check bin/machine-mcp.mjs && node --check src/local/cli.mjs && node --check src/local/runtime.mjs && node --check src/local/relay-connection.mjs && node --check src/local/secure-file.mjs && node --check src/local/patch.mjs && node --check src/local/process-sessions.mjs && node --check src/local/tools.mjs && node --check src/local/stdio.mjs && node --check src/local/state.mjs && node --check src/local/shell.mjs && node --check src/local/service.mjs && node --check src/local/log.mjs && node --check src/local/atomic-fs.mjs && node --check src/local/ssh-key.mjs && node --check src/local/resource-operations.mjs && node --check src/local/full-access-test.mjs && node --check src/local/managed-jobs.mjs && node --check src/local/job-runner.mjs && node --check scripts/sync-worker-version.mjs && node --check scripts/privacy-check.mjs && node --check scripts/release-impact-check.mjs && node --check scripts/network-retry.mjs && node --check scripts/github-release.mjs && node --check tests/worker-integration-test.mjs && node --check tests/stdio-integration-test.mjs && node --check tests/catalog-test.mjs && node --check tests/local-self-test.mjs && node --check tests/runtime-self-test.mjs && node --check tests/managed-jobs-test.mjs && node --check tests/ssh-key-test.mjs && node --check tests/full-access-test.mjs && node --check tests/atomic-fs-test.mjs && node --check tests/package-test.mjs && node --check tests/release-impact-test.mjs && node --check tests/privacy-test.mjs && node --check tests/network-retry-test.mjs && node --check tests/relay-connection-test.mjs && node --check tests/install-smoke-test.mjs && node --check tests/secure-file-test.mjs && node --check tests/architecture-test.mjs",
|
|
47
|
+
"check": "npm run privacy:check && npm run privacy:test && npm run release-impact:check && npm run release-impact:test && npm run network-retry:test && npm run relay:test && npm run secure-file:test && npm run architecture:test && npm run typecheck && npm run syntax && npm run catalog:test && npm run self-test && npm run atomic-fs:test && npm run package:test && npm run ssh-key:test && npm run full-access:test && npm run managed-jobs:test && npm run stdio:integration-test && npm run worker:integration-test",
|
|
48
48
|
"worker:dry-run": "wrangler deploy --dry-run",
|
|
49
49
|
"worker:integration-test": "node tests/worker-integration-test.mjs",
|
|
50
50
|
"release:check": "node scripts/github-release.mjs --check",
|
|
@@ -61,7 +61,11 @@
|
|
|
61
61
|
"release-impact:check": "node scripts/release-impact-check.mjs",
|
|
62
62
|
"release-impact:test": "node tests/release-impact-test.mjs",
|
|
63
63
|
"privacy:test": "node tests/privacy-test.mjs",
|
|
64
|
-
"network-retry:test": "node tests/network-retry-test.mjs"
|
|
64
|
+
"network-retry:test": "node tests/network-retry-test.mjs",
|
|
65
|
+
"relay:test": "node tests/relay-connection-test.mjs",
|
|
66
|
+
"install:test": "node tests/install-smoke-test.mjs",
|
|
67
|
+
"secure-file:test": "node tests/secure-file-test.mjs",
|
|
68
|
+
"architecture:test": "node tests/architecture-test.mjs"
|
|
65
69
|
},
|
|
66
70
|
"dependencies": {
|
|
67
71
|
"wrangler": "4.110.0",
|