machine-bridge-mcp 0.6.2 → 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 +71 -0
- package/CONTRIBUTING.md +31 -0
- package/README.md +20 -17
- package/SECURITY.md +11 -7
- package/docs/ARCHITECTURE.md +14 -12
- package/docs/CLIENTS.md +2 -2
- package/docs/ENGINEERING.md +159 -0
- package/docs/LOGGING.md +65 -26
- package/docs/MANAGED_JOBS.md +14 -13
- package/docs/OPERATIONS.md +15 -6
- package/docs/PRIVACY.md +43 -0
- package/docs/RELEASING.md +9 -4
- package/docs/TESTING.md +23 -1
- package/package.json +35 -8
- package/scripts/network-retry.mjs +47 -0
- package/scripts/privacy-check.mjs +177 -0
- package/scripts/release-impact-check.mjs +78 -0
- package/src/local/cli.mjs +369 -272
- package/src/local/full-access-test.mjs +2 -2
- package/src/local/job-runner.mjs +73 -31
- package/src/local/log.mjs +28 -2
- package/src/local/managed-jobs.mjs +194 -125
- package/src/local/process-sessions.mjs +5 -5
- package/src/local/relay-connection.mjs +495 -0
- package/src/local/{daemon.mjs → runtime.mjs} +188 -198
- package/src/local/secure-file.mjs +27 -0
- package/src/local/service.mjs +128 -30
- package/src/local/shell.mjs +1 -1
- package/src/local/ssh-key.mjs +41 -21
- package/src/local/state.mjs +7 -5
- package/src/local/stdio.mjs +157 -81
- package/src/shared/server-metadata.json +1 -0
- package/src/shared/tool-catalog.json +5 -1
- package/src/worker/index.ts +45 -21
- package/wrangler.jsonc +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,76 @@
|
|
|
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
|
+
|
|
32
|
+
## 0.7.1 - 2026-07-11
|
|
33
|
+
|
|
34
|
+
### Fixed
|
|
35
|
+
|
|
36
|
+
- Persist a sanitized absolute-only command `PATH` in launchd and systemd service definitions, always including the stable Node and CLI directories plus platform defaults. This restores Homebrew/npm/git command resolution for background canonical `full` daemons without accepting empty or relative PATH entries.
|
|
37
|
+
- Align `doctor` with the declared sole runtime baseline by requiring Node.js 26 instead of reporting Node.js 22-25 as supported.
|
|
38
|
+
|
|
39
|
+
### Tests and documentation
|
|
40
|
+
|
|
41
|
+
- Add service-definition regression tests for PATH preservation, duplicate removal, relative-entry rejection, and launchd/systemd emission; document why `machine-mcp service install` should be rerun after PATH layout changes.
|
|
42
|
+
|
|
43
|
+
## 0.7.0 - 2026-07-11
|
|
44
|
+
|
|
45
|
+
### Runtime and dependencies
|
|
46
|
+
|
|
47
|
+
- Raise the sole supported runtime baseline to Node.js 26 and npm 12, add exact local version files and strict engine checks, update Node type definitions to 26.1.1, and run the cross-platform CI suite on Node 26, and disable setup-node's automatic package-manager cache, and bootstrap npm 12 outside the repository before strict project engine checks apply.
|
|
48
|
+
- Confirm Wrangler 4.110.0, ws 8.21.0, and TypeScript 7.0.2 are current; retain exact reviewed dependency versions and zero known audit findings.
|
|
49
|
+
- Accept both legacy array and npm 12 keyed-object `npm pack --json` metadata, invoke the active npm CLI through Node on Windows, keep generated CI SBOM files outside the repository privacy/publication surface, and retry only classified transient GitHub network failures with Git forced to HTTP/1.1. Release creation verifies server state after an ambiguous response before proceeding.
|
|
50
|
+
|
|
51
|
+
### Security and privacy
|
|
52
|
+
|
|
53
|
+
- Replace private environment-derived aliases in public examples with synthetic identifiers and add a repository privacy gate covering tracked and unignored new files, file names, common credential forms, local home paths, SSH host identifiers, and an ignored machine-specific denylist without echoing matched values. Reject publication-surface symbolic links instead of following their targets.
|
|
54
|
+
- Hide local resource and generated-key paths by default; require explicit `--show-paths` or `expose_paths=true`, return only bare SSH fingerprints, and broaden operational-log redaction for paths, email addresses, token forms, key headers, and Unicode display controls.
|
|
55
|
+
- Separate unrestricted filesystem access from absolute-path display, dynamically redact requested external paths from tool errors, harden autostart files/logs against permissive modes and symbolic links, canonicalize OAuth redirect URIs, and remove bidirectional/zero-width controls from authorization-page display text.
|
|
56
|
+
|
|
57
|
+
### Correctness and durability
|
|
58
|
+
|
|
59
|
+
- Verify staged managed-job hashes during inspection, approval, and runner startup; serialize approve/cancel transitions and reclaim stale transition/recovery locks even after PID reuse; reject direct execution of unapproved staged plans; remove stale runner claims during recovery; and redact registered-resource source-path aliases from retained output.
|
|
60
|
+
- Clear delayed force-kill timers after process exit, enforce the stdio line limit during incremental reads, reject oversized local WebSocket payloads before string conversion, reject ID-less `tools/call` notifications instead of silently executing them, and accept actual collision-suffixed corrupt-config backups during guarded state removal.
|
|
61
|
+
- Make autostart prefer a stable PATH alias that resolves to the active Node executable instead of a versioned package-manager Cellar path, preventing minor Node upgrades from leaving launchd/systemd definitions pointing at removed binaries.
|
|
62
|
+
- Open and trim managed-job runner diagnostic logs through no-follow file descriptors, rejecting symbolic-link targets instead of following them.
|
|
63
|
+
|
|
64
|
+
### Release governance and diagnostics
|
|
65
|
+
|
|
66
|
+
- Add a release-impact gate requiring a newer package version and matching CHANGELOG section for every tracked or nonignored repository change after the latest version tag. Document that reviewed changes must be pushed to GitHub and followed by a matching npm release.
|
|
67
|
+
- Clarify in `server_info`, runtime diagnostics, and operations documentation that canonical `full` controls the local daemon and relay catalog only. A connector host can expose a smaller subset, and that host-side subset is not observable or overrideable by Machine Bridge.
|
|
68
|
+
|
|
69
|
+
### Tests and documentation
|
|
70
|
+
|
|
71
|
+
- Add regression coverage for privacy scanning of new files, clean/parseable package manifests and sensitive-artifact exclusion, default path omission and explicit disclosure, hidden unrestricted paths, plan tampering, direct staged-runner invocation, transition locks, source-path redaction, symlink log targets, corrupt-state cleanup, Unicode authorization spoofing, oversized stdio recovery, ID-less tool calls, and comment-free SSH fingerprints.
|
|
72
|
+
- Make release packaging resilient to lifecycle output by using silent JSON mode, and document repository privacy incident response, immutable history/package limitations, explicit path disclosure, plan integrity enforcement, same-user filesystem race residuals, and privacy checks in the release process.
|
|
73
|
+
|
|
3
74
|
## 0.6.2 - 2026-07-10
|
|
4
75
|
|
|
5
76
|
### Fixed
|
package/CONTRIBUTING.md
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# Contributing and release discipline
|
|
2
|
+
|
|
3
|
+
This repository treats every Git-tracked or nonignored repository file as release-relevant, including source code, tests, scripts, examples, documentation, CI configuration, ignore rules, and package metadata.
|
|
4
|
+
|
|
5
|
+
## Required for every release-relevant change
|
|
6
|
+
|
|
7
|
+
Before a change is merged to `main`:
|
|
8
|
+
|
|
9
|
+
1. bump `package.json` to a version newer than the latest reachable `v*` tag;
|
|
10
|
+
2. add the matching dated section to `CHANGELOG.md`;
|
|
11
|
+
3. run `npm run check`, `npm audit`, `npm audit --omit=dev`, and `npm run worker:dry-run`;
|
|
12
|
+
4. inspect the complete diff and the npm package manifest;
|
|
13
|
+
5. push the reviewed commit to GitHub;
|
|
14
|
+
6. create the matching Git tag and GitHub Release;
|
|
15
|
+
7. publish the same version to npm.
|
|
16
|
+
|
|
17
|
+
`npm run release-impact:check` enforces the version and changelog parts. It fails when release-relevant files changed after the latest version tag but the package version was not advanced.
|
|
18
|
+
|
|
19
|
+
A privacy or security correction is always release-relevant. Removing a private identifier only from the current branch is insufficient: publish a replacement npm version, update GitHub, and deprecate or unpublish the affected npm version when policy and authentication permit.
|
|
20
|
+
|
|
21
|
+
## Privacy
|
|
22
|
+
|
|
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
|
@@ -45,13 +45,13 @@ This default prioritizes usability, not least privilege. `run_process` and proce
|
|
|
45
45
|
|
|
46
46
|
## Install
|
|
47
47
|
|
|
48
|
-
Node.js
|
|
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
|
|
|
@@ -132,7 +132,7 @@ Important distinctions:
|
|
|
132
132
|
- `--absolute-paths` changes returned path metadata; it does not independently grant additional access.
|
|
133
133
|
- In isolated-environment profiles, commands receive private HOME, temp, and cache directories plus a small set of platform variables.
|
|
134
134
|
- The server has no filename blacklist. Under `full`, direct read tools may read any UTF-8 regular file that the local OS user can access, including files outside the workspace and names such as `.env`, `passwords.txt`, or private-key files.
|
|
135
|
-
- Maximum local policy does not override operating-system permissions, macOS TCC/SIP, Windows ACLs, container boundaries, or independent safety rules imposed by the MCP host/platform. A host-generated “sensitive file” denial is outside this server's enforcement layer.
|
|
135
|
+
- Maximum local policy does not override operating-system permissions, macOS TCC/SIP, Windows ACLs, container boundaries, or independent safety rules imposed by the MCP host/platform. `full` means the local daemon and relay advertise the complete catalog; a connector may still expose only a subset to a particular session. Machine Bridge cannot observe or override that host-side subset. A host-generated “sensitive file” denial is outside this server's enforcement layer.
|
|
136
136
|
|
|
137
137
|
## Diagnose host, bridge, and local execution failures
|
|
138
138
|
|
|
@@ -169,18 +169,19 @@ When the host blocks execution-class tools but still permits state mutation, `st
|
|
|
169
169
|
Register credential/key files locally without sending their contents through MCP:
|
|
170
170
|
|
|
171
171
|
```sh
|
|
172
|
-
chmod 600 ~/.ssh/
|
|
173
|
-
machine-mcp resource add
|
|
174
|
-
machine-mcp resource list
|
|
172
|
+
chmod 600 ~/.ssh/example_maintenance_ed25519
|
|
173
|
+
machine-mcp resource add maintenance-key ~/.ssh/example_maintenance_ed25519
|
|
174
|
+
machine-mcp resource list # paths omitted by default
|
|
175
|
+
machine-mcp resource list --show-paths # explicit local-only path disclosure
|
|
175
176
|
```
|
|
176
177
|
|
|
177
178
|
Generate an Ed25519 key and register its private file in one operation:
|
|
178
179
|
|
|
179
180
|
```sh
|
|
180
|
-
machine-mcp resource generate-ssh-key
|
|
181
|
+
machine-mcp resource generate-ssh-key maintenance-key ~/.ssh/machine-mcp-example-maint-ed25519
|
|
181
182
|
```
|
|
182
183
|
|
|
183
|
-
Under canonical `full`, an authorized MCP host can invoke `generate_ssh_key_resource` with the same semantics. The tool is idempotent, verifies that existing public/private files match, enforces local file modes where supported, and returns
|
|
184
|
+
Under canonical `full`, an authorized MCP host can invoke `generate_ssh_key_resource` with the same semantics. The tool is idempotent, verifies that existing public/private files match, enforces local file modes where supported, and returns the public fingerprint, key type, and registration status—not private key bytes or local paths. Pass `expose_paths=true` only when the caller genuinely needs those paths.
|
|
184
185
|
|
|
185
186
|
A job refers to the alias rather than the value:
|
|
186
187
|
|
|
@@ -192,8 +193,8 @@ A job refers to the alias rather than the value:
|
|
|
192
193
|
"argv": [
|
|
193
194
|
"ssh",
|
|
194
195
|
"-i",
|
|
195
|
-
"{{resource:
|
|
196
|
-
"
|
|
196
|
+
"{{resource:maintenance-key}}",
|
|
197
|
+
"admin@server.example",
|
|
197
198
|
"sh",
|
|
198
199
|
"-s"
|
|
199
200
|
],
|
|
@@ -202,7 +203,7 @@ A job refers to the alias rather than the value:
|
|
|
202
203
|
],
|
|
203
204
|
"finally_steps": [
|
|
204
205
|
{
|
|
205
|
-
"argv": ["ssh", "-i", "{{resource:
|
|
206
|
+
"argv": ["ssh", "-i", "{{resource:maintenance-key}}", "admin@server.example", "rm", "-f", "/tmp/helper"],
|
|
206
207
|
"allow_failure": true
|
|
207
208
|
}
|
|
208
209
|
]
|
|
@@ -258,7 +259,7 @@ Repository-configured external diff, text conversion, and filesystem-monitor hel
|
|
|
258
259
|
|
|
259
260
|
- `diagnose_runtime` — fixed layered probes; no user-controlled command input
|
|
260
261
|
- `list_local_resources` — aliases and validation status without paths or values
|
|
261
|
-
- `generate_ssh_key_resource` — canonical-full-only Ed25519 generation and private-file
|
|
262
|
+
- `generate_ssh_key_resource` — canonical-full-only Ed25519 generation and private-file registration; private bytes and local paths are omitted by default
|
|
262
263
|
- `stage_job` — persist a validated plan for later local approval without executing it
|
|
263
264
|
- `start_job` — detached ordered argv steps, private temporary files, and finally steps
|
|
264
265
|
- `list_jobs`
|
|
@@ -313,7 +314,7 @@ Remote mode supports:
|
|
|
313
314
|
- Linux `systemd --user`, with best-effort lingering;
|
|
314
315
|
- Windows Scheduled Task at logon.
|
|
315
316
|
|
|
316
|
-
The service definition contains neither credentials nor a duplicate policy. It loads the selected policy from owner-only local state and uses the documented `full` default if policy state is absent. Background services run at log level `warn`: relay, protocol, and service problems are retained, while all per-tool success/failure/cancellation/timing events remain debug-only. Logs are owner-only where supported and bounded by tail trimming.
|
|
317
|
+
The service definition contains neither credentials nor a duplicate policy. It loads the selected policy from owner-only local state and uses the documented `full` default if policy state is absent. launchd/systemd definitions persist a sanitized absolute-only PATH captured during installation, including the stable Node/CLI directories, so background `full` mode does not lose Homebrew or other developer command locations. Background services run at log level `warn`: relay, protocol, and service problems are retained, while all per-tool success/failure/cancellation/timing events remain debug-only. Logs are owner-only where supported and bounded by tail trimming.
|
|
317
318
|
|
|
318
319
|
## Secret rotation
|
|
319
320
|
|
|
@@ -334,7 +335,7 @@ Default state roots:
|
|
|
334
335
|
|
|
335
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.
|
|
336
337
|
|
|
337
|
-
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).
|
|
338
339
|
|
|
339
340
|
## Development and verification
|
|
340
341
|
|
|
@@ -347,9 +348,9 @@ npm audit --omit=dev --audit-level=high
|
|
|
347
348
|
npm pack --dry-run
|
|
348
349
|
```
|
|
349
350
|
|
|
350
|
-
`npm run check` covers 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.
|
|
351
352
|
|
|
352
|
-
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).
|
|
353
354
|
|
|
354
355
|
## Uninstall
|
|
355
356
|
|
|
@@ -363,3 +364,5 @@ Use `--keep-worker` to retain deployed Workers while removing local state and au
|
|
|
363
364
|
## License
|
|
364
365
|
|
|
365
366
|
MIT
|
|
367
|
+
|
|
368
|
+
See [repository privacy hygiene](docs/PRIVACY.md) and [contribution/release discipline](CONTRIBUTING.md) before committing. Every release-relevant code, test, script, configuration, or documentation change must be pushed to GitHub with a new version and followed by a matching npm release.
|
package/SECURITY.md
CHANGED
|
@@ -17,6 +17,12 @@ Do not open a public issue for an undisclosed vulnerability. Use GitHub private
|
|
|
17
17
|
|
|
18
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.
|
|
19
19
|
|
|
20
|
+
## Repository and documentation privacy
|
|
21
|
+
|
|
22
|
+
Tests, examples, documentation, release notes, and package metadata are publication surfaces. Use only synthetic hostnames, resource aliases, usernames, paths, and project names. Run `npm run privacy:check` before committing or publishing, and maintain private local identifiers in the ignored `.privacy-denylist`; the scanner never prints matched values. See [Repository privacy hygiene](docs/PRIVACY.md).
|
|
23
|
+
|
|
24
|
+
Removing a value from the current branch does not remove it from Git history, caches, forks, or an already published npm package. Rotate any exposed credential immediately and coordinate destructive history rewriting separately when its risk is justified.
|
|
25
|
+
|
|
20
26
|
## Core trust model
|
|
21
27
|
|
|
22
28
|
Trusted components are:
|
|
@@ -53,7 +59,7 @@ The default `full` profile returns absolute paths. Narrower profiles return work
|
|
|
53
59
|
|
|
54
60
|
The server does not maintain a sensitive-filename blacklist. Under `full`, direct read tools may access any UTF-8 regular file available to the local OS user, including files outside the selected workspace and names such as `.env`, password stores, private keys, credentials, database dumps, and production configuration. Narrower profiles confine direct filesystem tools but do not classify names inside that boundary.
|
|
55
61
|
|
|
56
|
-
Maximum local policy does not bypass Unix permissions, Windows ACLs, macOS TCC/SIP, container/VM boundaries, endpoint-security controls, shell restrictions, or security decisions made by the MCP host/platform. A host-side refusal is independent of Machine Bridge and cannot be disabled by local policy configuration. `diagnose_runtime` and `machine-mcp doctor` use fixed probes to distinguish requests that reached the daemon from local filesystem/process/shell failures; they cannot inspect a call blocked before delivery.
|
|
62
|
+
Maximum local policy does not bypass Unix permissions, Windows ACLs, macOS TCC/SIP, container/VM boundaries, endpoint-security controls, shell restrictions, or security decisions made by the MCP host/platform. `full` guarantees the complete local daemon and relay-advertised catalog, not the set a connector host chooses to expose to one session. The server cannot observe that host-side subset. A host-side refusal is independent of Machine Bridge and cannot be disabled by local policy configuration. `diagnose_runtime` and `machine-mcp doctor` use fixed probes to distinguish requests that reached the daemon from local filesystem/process/shell failures; they cannot inspect a call blocked before delivery.
|
|
57
63
|
|
|
58
64
|
Processes are not confined by the filesystem-tool resolver. They can access paths, networks, processes, credential stores, and devices available to the local user.
|
|
59
65
|
|
|
@@ -67,9 +73,7 @@ A canonical `full` profile guarantees that Machine Bridge itself will not reject
|
|
|
67
73
|
|
|
68
74
|
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.
|
|
69
75
|
|
|
70
|
-
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.
|
|
71
|
-
|
|
72
|
-
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.
|
|
76
|
+
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. On platforms without descriptor-relative path traversal, a malicious process running as the same local account can still race filesystem or parent-directory replacement between validation and commit. These controls do not claim protection against a hostile same-user namespace, nor do they make a multi-directory patch power-loss atomic.
|
|
73
77
|
|
|
74
78
|
## Credential exposure
|
|
75
79
|
|
|
@@ -81,7 +85,7 @@ The default `full` profile passes the complete parent environment. Narrower prof
|
|
|
81
85
|
|
|
82
86
|
## Local resources and managed jobs
|
|
83
87
|
|
|
84
|
-
Local resources are registered through the operator-controlled CLI or, only under canonical `full`, through `generate_ssh_key_resource`. State stores canonical paths and metadata
|
|
88
|
+
Local resources are registered through the operator-controlled CLI or, only under canonical `full`, through `generate_ssh_key_resource`. State stores canonical paths, bounded registration-time aliases used only for redaction, and metadata—not file contents. Public status and resource results omit those aliases. The SSH generator creates or reuses an unencrypted automation key, verifies that the public and private files match, rejects symbolic links/incomplete pairs, applies `0600`/`0644` modes where supported, and returns no private bytes. Resource CLI output and `generate_ssh_key_resource` omit local paths by default; path disclosure requires `--show-paths` or `expose_paths=true`. Unix-like registration rejects group/other-readable private resource files unless explicitly overridden. Portable mode checks do not fully describe Windows ACLs or extended Unix ACLs; the operator remains responsible for platform permissions.
|
|
85
89
|
|
|
86
90
|
At job acceptance, referenced resources are bounded and hashed. The detached runner reopens and verifies each hash before copying it to a private `0600` runtime file. Resource copies are removed after the finally phase. A changed or unavailable resource fails closed.
|
|
87
91
|
|
|
@@ -97,7 +101,7 @@ Managed jobs accept arbitrary argv and therefore retain local-user authority. Th
|
|
|
97
101
|
|
|
98
102
|
Active plans are owner-only and may temporarily contain argv, non-secret stdin, temporary helper content, environment overrides, and resource source paths for crash recovery. A terminal runner deletes the full plan. Status and bounded results remain for up to seven days/50 jobs. If a runner crashes before terminal commit, the plan remains until recovery or retention cleanup.
|
|
99
103
|
|
|
100
|
-
Exact resource
|
|
104
|
+
Exact canonical and registration-time resource path aliases, exact resource bytes interpreted as text, and bounded exact base64/hex forms are redacted from retained output. This cannot detect partial, transformed, encrypted, compressed, or application-specific encodings. It also cannot redact unrelated secrets inherited through the full parent environment. Use `capture_output: "discard"` whenever a process may echo credentials, and never place a secret directly in argv, ordinary env, stdin, temporary-file content, or a JSON plan.
|
|
101
105
|
|
|
102
106
|
`finally_steps` run after ordinary success, failure, timeout, and cancellation. Cancellation uses an owner-only marker rather than signaling the runner process itself, so the coordinator remains alive to execute cleanup consistently across platforms. A dead runner is detected on the next daemon or local job-CLI start; stale private resource copies are removed and cleanup is retried. This is best effort. Power loss, disk failure, permanent loss of credentials/network access, SIGKILL without later recovery, or security software denying the cleanup executable can prevent cleanup. Finally steps must be idempotent and safe to repeat. Automatic recovery is capped at three attempts so persistent endpoint-security or executable-policy denial cannot create an endless launch loop. Uninstall refuses to remove local state while managed jobs are active; operators must inspect or cancel them first.
|
|
103
107
|
|
|
@@ -157,7 +161,7 @@ machine-mcp --workspace /narrow/project --profile edit --no-print-credentials
|
|
|
157
161
|
|
|
158
162
|
Also:
|
|
159
163
|
|
|
160
|
-
- patch the OS and use
|
|
164
|
+
- patch the OS and use the repository-pinned Node.js 26/npm 12 baseline;
|
|
161
165
|
- enable MFA on Cloudflare, GitHub, and npm accounts;
|
|
162
166
|
- do not configure broad CORS origins;
|
|
163
167
|
- select `agent`, `edit`, or `review` instead of the default `full` when broad authority is unnecessary;
|
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
|
|
|
@@ -85,7 +85,7 @@ The daemon attachment deliberately omits workspace path/name/hash and process ID
|
|
|
85
85
|
|
|
86
86
|
### Autostart layer
|
|
87
87
|
|
|
88
|
-
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
|
|
88
|
+
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. launchd/systemd definitions contain the workspace/state-root selectors, a `warn` log-level setting, and a sanitized absolute-only PATH captured at installation so background `full` mode can resolve the same developer tools without accepting relative PATH entries.
|
|
89
89
|
|
|
90
90
|
## Trust boundaries
|
|
91
91
|
|
|
@@ -105,7 +105,7 @@ flowchart LR
|
|
|
105
105
|
CLI --> S[Autostart provider]
|
|
106
106
|
```
|
|
107
107
|
|
|
108
|
-
Remote OAuth determines which client may call tools. Local stdio access relies on the local process and configuration boundary. Policy determines which tools
|
|
108
|
+
Remote OAuth determines which client may call tools. Local stdio access relies on the local process and configuration boundary. Policy determines which tools the local daemon and relay advertise. A connector host can independently present a smaller tool subset to a session; this post-relay filtering is outside the protocol state visible to Machine Bridge. Canonical resolution limits direct filesystem tools. Processes retain local-user authority and can escape workspace constraints through their own code or system calls.
|
|
109
109
|
|
|
110
110
|
## Remote request lifecycle
|
|
111
111
|
|
|
@@ -131,15 +131,15 @@ Duplicate in-flight JSON-RPC IDs for the same access token are rejected so cance
|
|
|
131
131
|
2. The server negotiates one of the supported MCP versions.
|
|
132
132
|
3. Tool discovery is generated from the same catalog and policy used by remote mode.
|
|
133
133
|
4. Each call receives an internal random call ID used only for cancellation and process tracking.
|
|
134
|
-
5. Results are emitted as JSON-RPC on stdout; logs remain on stderr.
|
|
135
|
-
6. Duplicate in-flight request IDs are rejected.
|
|
134
|
+
5. Input is parsed as incrementally bounded newline-delimited JSON-RPC, so an oversized line is discarded before unbounded buffering and the next line can still be processed. Results are emitted as JSON-RPC on stdout; logs remain on stderr.
|
|
135
|
+
6. Duplicate in-flight request IDs are rejected, and `tools/call` requires a non-null request ID so write or execution calls cannot run as unacknowledged JSON-RPC notifications.
|
|
136
136
|
7. Closing stdin cancels pending calls, terminates ordinary active processes/process sessions, and removes the transport runtime directory. Previously accepted managed jobs continue in their persistent per-workspace job directories.
|
|
137
137
|
|
|
138
138
|
## Filesystem resolution and privacy
|
|
139
139
|
|
|
140
140
|
The workspace is canonicalized and compared with targets through consistent platform-native/async `realpath` representations. Existing targets must remain inside the workspace unless the active policy is unrestricted. New targets walk to the nearest existing ancestor, validate its canonical path, and reconstruct the destination below that canonical ancestor.
|
|
141
141
|
|
|
142
|
-
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.
|
|
142
|
+
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.
|
|
143
143
|
|
|
144
144
|
Symbolic-link destinations and non-regular write targets are rejected. Recursive walkers do not follow symbolic-link directories.
|
|
145
145
|
|
|
@@ -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
|
|
|
@@ -193,9 +195,9 @@ OAuth metadata is pruned on access. Expired codes/tokens, old throttling records
|
|
|
193
195
|
|
|
194
196
|
## Observability
|
|
195
197
|
|
|
196
|
-
Public health exposes only server identity and version. Authenticated `server_info` exposes bounded runtime status, policy origin/revision, managed-job counts,
|
|
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
|
|
package/docs/CLIENTS.md
CHANGED
|
@@ -131,9 +131,9 @@ Before connecting a host, verify the local implementation directly:
|
|
|
131
131
|
machine-mcp full-test --workspace /path/to/project
|
|
132
132
|
```
|
|
133
133
|
|
|
134
|
-
A passing result proves that Machine Bridge and the local OS allowed its temporary file/process/shell/key/job probes at that time. It does not prove that a hosted MCP connector will deliver a later request or that a cloud/remote account will authorize it.
|
|
134
|
+
A passing result proves that Machine Bridge and the local OS allowed its temporary file/process/shell/key/job probes at that time. It does not prove that a hosted MCP connector will expose every relay-advertised tool, deliver a later request, or that a cloud/remote account will authorize it.
|
|
135
135
|
|
|
136
|
-
For SSH automation, prefer `generate_ssh_key_resource` under canonical full, or `machine-mcp resource generate-ssh-key` from the terminal. The private key remains a local resource;
|
|
136
|
+
For SSH automation, prefer `generate_ssh_key_resource` under canonical full, or `machine-mcp resource generate-ssh-key` from the terminal. The private key remains a local resource; private bytes and local paths are omitted by default, while metadata and the bare public fingerprint are returned. Paths require an explicit disclosure option. Installation of the public key into Google OS Login or a remote `authorized_keys` file remains an explicit external action.
|
|
137
137
|
|
|
138
138
|
## Host-side safety rules
|
|
139
139
|
|
|
@@ -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.
|