machine-bridge-mcp 3.0.0-beta.103 → 3.0.0-beta.104
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 +11 -0
- package/README.md +2 -2
- package/browser-extension/manifest.json +1 -1
- package/docs/ARCHITECTURE.md +6 -6
- package/docs/AUDIT.md +18 -0
- package/docs/CLIENTS.md +2 -2
- package/docs/LOGGING.md +4 -4
- package/docs/MANAGED_JOBS.md +2 -0
- package/docs/MULTI_ACCOUNT.md +1 -1
- package/docs/OPERATIONS.md +7 -7
- package/docs/PRIVACY.md +4 -2
- package/docs/TESTING.md +6 -6
- package/docs/TOOL_REFERENCE.md +12 -12
- package/package.json +1 -1
- package/scripts/generate-worker-types.mjs +8 -4
- package/src/local/account-admin.mjs +2 -2
- package/src/local/daemon-http-relay-auth.mjs +2 -2
- package/src/local/daemon-http-relay-connection.mjs +105 -15
- package/src/local/device-identity.mjs +11 -9
- package/src/local/device-root-provider.mjs +1 -1
- package/src/local/managed-job-hosted-status.mjs +25 -0
- package/src/local/managed-job-listing.mjs +42 -0
- package/src/local/managed-jobs.mjs +9 -27
- package/src/local/process-session-read.mjs +43 -0
- package/src/local/process-session-remote-poll.mjs +31 -0
- package/src/local/process-sessions.mjs +5 -21
- package/src/local/relay-connect-timing.mjs +66 -0
- package/src/local/relay-connection-classification.mjs +11 -1
- package/src/local/relay-connection-support.mjs +11 -2
- package/src/local/relay-connection.mjs +80 -64
- package/src/local/relay-diagnostics.mjs +20 -12
- package/src/local/relay-heartbeat-options.mjs +16 -0
- package/src/local/relay-heartbeat-stall.mjs +44 -0
- package/src/local/relay-heartbeat-transport-state.mjs +30 -0
- package/src/local/relay-heartbeat.mjs +42 -46
- package/src/local/relay-inbound-state.mjs +41 -0
- package/src/local/relay-liveness-actions.mjs +66 -0
- package/src/local/relay-liveness.mjs +38 -40
- package/src/local/relay-peer-diagnostics.mjs +51 -1
- package/src/local/relay-probe-deadline.mjs +44 -0
- package/src/local/relay-probe-dispatch-metrics.mjs +22 -0
- package/src/local/relay-probe-dispatch.mjs +38 -0
- package/src/local/relay-transport-confirmation.mjs +79 -0
- package/src/local/relay-transport-error-state.mjs +48 -0
- package/src/local/relay-transport-probe-send.mjs +27 -0
- package/src/local/relay-transport-probe.mjs +26 -0
- package/src/local/resilient-relay-connection.mjs +28 -5
- package/src/local/resource-admission-diagnostic-error.mjs +21 -0
- package/src/local/resource-admission-diagnostics.mjs +3 -5
- package/src/local/runtime-diagnostics.mjs +1 -0
- package/src/local/runtime-relay-control.mjs +7 -4
- package/src/shared/relay-contract.json +7 -1
- package/src/shared/server-metadata.json +3 -2
- package/src/shared/tool-catalog.json +12 -12
- package/src/worker/daemon-http-controller.ts +5 -5
- package/src/worker/daemon-http-protocol.ts +7 -3
- package/src/worker/daemon-recovery-budget.ts +8 -0
- package/src/worker/daemon-relay-diagnostics.ts +64 -0
- package/src/worker/daemon-socket-attachment.ts +1 -1
- package/src/worker/errors.ts +2 -1
- package/src/worker/index.ts +14 -5
- package/src/worker/mcp-stale-schema-compat.ts +3 -1
- package/src/worker/server-info-tool-delivery.ts +2 -1
- package/src/worker/tool-catalog.ts +15 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 3.0.0-beta.104 - 2026-08-19
|
|
4
|
+
|
|
5
|
+
- Supersede beta.103 after owner use again reproduced two host-visible continuity failures: a conversation could remain with no assistant response while Machine Bridge activity continued, and the client could separately report “message send timed out.” The current incident does not match beta.103's idle-sleep defect: the launchd daemon remained live with one run, the macOS idle-sleep assertion was active, and the contemporaneous daemon warning log contained no relay outage. The retained security-audit window instead shows 497 settled tool calls in 25 minutes, including 68 `read_process` calls and 91 `read_job` calls; many process reads consumed the complete five-second remote wait, while repeated managed-job status reads kept foreground reasoning attached to work that was already durable. This proves the no-response interval was amplified by same-turn polling composition rather than by one hung MCP call.
|
|
6
|
+
- Bound that polling amplifier at discovery, rolling-upgrade, and runtime layers. Remote `read_process.wait_ms` is now capped at one second, and the daemon independently enforces the same cap for relay-origin reads. A running process session records the completion of a remote blocking wait and converts another would-block read inside a fixed fifteen-second cooldown into an immediate status read. Every relay-origin result is explicitly a `status_polling_mode=checkpoint`; the blocking-only fields are named `blocking_poll_throttled` and `next_blocking_poll_after_ms`, and `host_turn_handoff_recommended=true` for any still-running session. Remote `start_process`/`read_process` guidance reserves sessions for interactive stdin or incremental output and limits a hosted assistant response to one live-session checkpoint regardless of whether that checkpoint returned output. The maximum-only stale-schema compatibility response follows the same rule instead of telling an already-open client to “use short polling,” so a cached pre-beta.104 5-second schema cannot reintroduce the old loop through its recovery message. Local stdio/CLI process-session waits remain unchanged.
|
|
7
|
+
- Make durable-job status explicitly yield the hosted turn. Remote `start_job`, durable one-step process tools, `read_job`, and the shared server guidance now describe acceptance/status reads as checkpoints rather than a wait loop: after at most one active-job status read in an assistant response, callers should return the `job_id`/status/current phase and stop polling until a later user turn or explicit request. Relay-origin `read_job` results reinforce that contract with `status_polling_mode=checkpoint` and `host_turn_handoff_recommended=true` while the job remains active; relay-origin `list_jobs` is also an inventory checkpoint and recommends handoff when its already-authorized returned set contains active work, closing the listing-as-polling bypass. Local reads/listings do not gain hosted-only metadata.
|
|
8
|
+
- Re-audit the recurrent “message send timed out” symptom without inventing an SSE fix. Machine Bridge cannot observe a client message submission that fails before an MCP request reaches the Worker. Source tracing also disproves an initial five-second tool-stream silence: the Durable Object's `jsonRpcResponseStream()` synchronously enqueues `: connected` before the tool Promise settles, and the outer proxy begins pumping that already-open stream immediately. An attempted extra outer first-byte heartbeat was therefore removed rather than retained as an unproven fix. The server-side repair in beta.104 is the demonstrated hosted-turn polling/handoff defect above; a host-side send timeout that occurs before MCP dispatch still requires host/client evidence.
|
|
9
|
+
- Tighten the independent beta.104 review beyond the original incident. Process-session read orchestration is extracted from the near-limit lifecycle manager into `process-session-read.mjs`, remote blocking-poll policy remains in `process-session-remote-poll.mjs`, and after the async wait returns the manager re-checks cancellation before hosted projection is finalized so a Promise-boundary cancel is not lost and final `running` state, handoff, and cooldown cannot disagree across an exit race. Hosted managed-job handoff policy lives in `managed-job-hosted-status.mjs`, and directory scanning/authority-visible list projection lives in `managed-job-listing.mjs` rather than bloating the persistence/lifecycle manager. Wrangler type generation now passes a workspace-relative output path and rejects targets outside its working directory so generated comments do not embed the local username/workspace path; stale ignored `.wrangler` type artifacts carrying older path comments are removed, and regenerated files remain outside the package surface. A live WSS→HTTPS fallback episode also exposed a diagnostic bug: pending calls wait only until the smaller of reconnect grace and their original absolute deadline, but the old error always said “reconnect grace expired.” Beta.104 now reports when the original call deadline is the actual limiter without widening or pausing that deadline. A later full-verification burst exposed a separate diagnostic-only resource-coordinator issue: a five-second snapshot that lost a healthy, changing transaction-lock race was surfaced as generic `execution_failed`. Beta.104 now reports transaction/staging contention as retryable `unavailable` with `reason=coordinator_busy` and `snapshot_available=false`; admission limits, fairness, lock ownership, and resource-pressure decisions are unchanged. Independent wording review also removed the generic configurable-foreground advice to “use process sessions or managed jobs for longer work” from both the shared tool catalog and the Worker projection: longer browser/application workflows are now described as independently terminal foreground calls, while durable process/job routing is reserved for process-based work and `start_process` remains interactive-I/O only. The generated 54-tool reference is rebuilt from that corrected source and the architecture gate rejects the obsolete advice in either catalog or reference output. Fast verification also exposed a pre-existing clock inconsistency in device-session construction: `createDeviceSessionIdentity(..., now)` generated a certificate from the explicit clock but final validation silently used real wall time. Beta.104 now threads the same `now` through finalization (including the secure-root provider), so historical/synthetic-clock authentication tests remain deterministic without changing production default time semantics.
|
|
10
|
+
- Continue the WSS root-cause audit rather than leaving transport instability as unexplained. The old transport watchdog both measured the ten-second Pong deadline from the previous inbound frame and treated `ws.ping()` returning as proof that the control frame had been sent. Because `ws` can queue Ping behind data compression/socket backpressure, either behavior could make local `terminate()` produce `relay_transport_timeout`/1006 without granting an actually transmitted probe its full response window. Beta.104 now models queued Ping dispatch separately: only the sender write callback starts the ten-second Pong deadline; protocol Pong or explicit application `pong` observed while that callback is pending can satisfy only that exact dispatch token if the callback later completes inside its bound; unrelated inbound is receive-side evidence only and cannot excuse a stuck daemon-to-Worker send path; local send-queue no-progress has a separate thirty-second bound and distinct `relay_transport_send_timeout`; bounded dispatch delay/timeout age and `bufferedAmount` expose only numeric backlog evidence. The HTTPS fallback also previously authorized takeover only by daemon `instance_id`; an already-dispatched stale takeover could arrive after a newer same-instance WSS recovered and retire the new socket. Worker welcome now carries its per-socket `connection_id`, same-version clients require it, and fallback takeover is signed/bound to that exact disconnected generation; stale targets stay standby. A third deterministic continuity defect existed at the credential boundary: the root-signed daemon session intentionally expires after 24 hours, but the daemon reused the same session forever, so the next WSS/HTTPS reconnect after expiry could retry permanently with unusable credentials. Expiry now has stable `device_session_expired`/`relay_device_session_expired` classification; runtime cleanup precedes nonzero exit, allowing existing launchd/systemd/Windows supervision to mint a fresh session on restart without extending the 24-hour security boundary (manual daemons require restart; Secure Enclave mode retains user-presence semantics). Relay diagnostics retain current and last-failed DNS/TCP/TLS/upgrade milestones, strict privacy-safe transport reasons, authenticated/ready context, and HTTPS last-error evidence after recovery. A later live beta.103 outage was `relay_transport_error`/`network_error` for about 274 seconds across 16 attempts; HTTPS fallback and WSS recovered within roughly 0.4 seconds of one another, the local event loop had no stall, and the OS route was VPN/TUN. That strongly supports a shared system-network episode, but beta.103 discarded the exact failed-stage/fallback errno evidence, so the historical event is not attributed more narrowly to a specific DNS/TCP/TLS/VPN/Cloudflare component.
|
|
11
|
+
- Follow the live WSS evidence one layer further instead of accepting the remaining watchdog as harmless. August 19 beta.103 logs contain repeated ready-channel `relay_transport_timeout` episodes after roughly 12–15 seconds of inbound silence, while later successful WSS connection attempts themselves took about 13.8 and 14.4 seconds and one network-error episode required 16 retries over roughly 4.5 minutes. Cloudflare handles protocol Ping/Pong below the Durable Object message handler, and local event-loop diagnostics did not show a matching stall, so a missed protocol Pong is evidence of an impaired persistent transport path, not proof that Worker JavaScript failed to run. The external trigger still cannot be assigned honestly to one VPN/TUN implementation, edge, ISP, DNS/TCP/TLS layer, or upstream device, but the Machine Bridge amplification is deterministic: one ten-second Pong miss immediately hard-terminated WSS, and the old fifteen-second connect ceiling sat almost on top of observed successful upgrades. Beta.104 now treats one missed dispatched Pong as `relay.transport.suspect`, sends one ready-only JSON heartbeat during a separate fifteen-second confirmation window, and prewarms signed HTTPS in standby without takeover. A later protocol Pong or explicit JSON application `pong` records recovery and keeps WSS; ordinary inbound tool/control traffic cannot prove the outbound direction and does not clear suspicion. Only a second-stage miss closes it. Send completions are fenced to the exact WSS generation so a late callback from a superseded socket cannot poison the replacement confirmation/error state. A real disconnect upgrades the prewarm to exact-generation takeover and aborts any obsolete in-flight standby request first; successful standby prewarm runs at five-second cadence, while request/session/protocol failures use bounded one/two/four/five-second backoff instead of retrying near the 750 ms hard floor. WSS connect attempts receive a thirty-second outer budget while fallback still starts independently. Application liveness is separately gated on verified readiness so probing sockets never receive a heartbeat type the Worker probing state forbids. HTTPS status now distinguishes `last_success_at` from true `last_ready_at`, and relay diagnostics expose bounded confirmation/dispatch-recovery timing plus `https_fallback_warming` so a future incident can distinguish avoided false positives from confirmed two-stage black holes.
|
|
12
|
+
- Add red/green regressions for remote blocking-poll cooldown/handoff metadata, relay-origin `read_job`/`list_jobs` handoff metadata, the one-second hosted poll schema and timeout budget, stale-schema anti-loop guidance, reconnect-deadline cause classification, relative Wrangler type-output paths, generation-bound WSS/HTTPS takeover, queued-versus-dispatched Ping deadlines, local send-timeout classification even under unrelated inbound traffic, protocol/application-Pong-only bidirectional proof, stale prior-generation send-callback isolation, HTTPS standby/failure backoff and takeover-timer preemption, privacy-safe transport reasons, HTTPS post-recovery last-error retention, 24-hour device-session expiry restart behavior, and shared hosted instructions that reject same-response waiting through job/process/diagnostic status surfaces. These packaged runtime/Worker/documentation changes invalidate beta.103 release evidence; beta.104 requires fresh frozen verification and a new explicit owner authorization before any persistent activation or release acceptance.
|
|
13
|
+
|
|
3
14
|
## 3.0.0-beta.103 - 2026-08-18
|
|
4
15
|
|
|
5
16
|
- Supersede beta.102 after owner use still reproduced a severe whole-control-plane interruption. Live beta.102 evidence separates this event from the signed HTTPS fallback: the final `PreventUserIdleSystemSleep` assertion ended, macOS entered ordinary `Idle Sleep` about five seconds later for roughly 632 seconds, and the daemon reported an aligned event-loop pause of roughly 626 seconds while launchd retained one continuously running service process. A suspended host cannot execute either WSS or HTTPS recovery. The confirmed product defect is therefore the daemon-side post-activity power lease: beta.102's five-minute grace was shorter than a normal multi-turn ChatGPT reasoning/wait interval and could release immediately before the operating system chose Idle Sleep.
|
package/README.md
CHANGED
|
@@ -172,7 +172,7 @@ The shared source of truth is `src/shared/policy-contract.json`. The generated m
|
|
|
172
172
|
|
|
173
173
|
For routine remote health checks, prefer `server_info` with `detail: "summary"`; the empty/default call remains full diagnostics. For routine workspace inventory, `project_overview` also accepts `detail: "summary"`; it preserves policy/tool counts and top-level names/types without repeating exact tool arrays, account identity, routing fingerprints, or per-entry paths/sizes. Its empty/default call likewise remains full for compatibility. For remote calls, `server_info.authorization.effective_policy` and, when exact membership is needed, the full projection's `effective_tools` are authoritative. Daemon policy and tools describe only the local capability ceiling before account-role and host-side filtering.
|
|
174
174
|
|
|
175
|
-
`tools/list` is a stable discovery catalog for the authenticated account role. A brief relay interruption does not withdraw tool definitions or require a tools-list-changed notification. Discovery is not authority: every `tools/call` is still intersected with the current end-to-end-ready daemon policy and tool ceiling, and fails retryably with `unavailable` when no daemon is ready. `server_info.tool_delivery` distinguishes the stable advertised catalog from the currently effective daemon/account intersection. WebSocket is the preferred daemon transport: it
|
|
175
|
+
`tools/list` is a stable discovery catalog for the authenticated account role. A brief relay interruption does not withdraw tool definitions or require a tools-list-changed notification. Discovery is not authority: every `tools/call` is still intersected with the current end-to-end-ready daemon policy and tool ceiling, and fails retryably with `unavailable` when no daemon is ready. `server_info.tool_delivery` distinguishes the stable advertised catalog from the currently effective daemon/account intersection. WebSocket is the preferred daemon transport: it requests a protocol-level Ping after five seconds, gives an actually dispatched Ping its full ten-second Pong deadline, then uses one independent fifteen-second application-confirmation window before a ready WSS may be terminated as a transport black hole. A protocol Pong or explicit application `pong` during that second stage preserves WSS; ordinary tool/control inbound remains receive-side evidence and cannot clear transport suspicion, while local event-loop stalls cancel remote suspicion and use the separate recovery-grace path. The periodic application heartbeat remains twenty-five/seventy-five seconds after end-to-end readiness, and the Worker keeps a wider ninety-second fallback. WSS connect attempts have a thirty-second outer budget. Signed HTTPS is independent of that budget: on first-stage WSS suspicion the same root-certified ephemeral device identity prewarms HTTPS in standby, and a real WSS loss promotes that path to exact-generation takeover while aborting any obsolete standby request. Fallback requests bind the fixed route/origin/server/version, a short-lived nonce, timestamp, and exact body hash; they use a seven-second request deadline, twelve-second liveness window, one-second ordinary poll cadence, and a 750 ms minimum request-start interval. The first authenticated exchange enters probing immediately, so verified readiness requires at most two bounded exchanges rather than a separate challenge round trip. Candidate → probing → verified-ready handover prevents the Worker from dispatching until the daemon has processed `ready_ack` and returned sequenced `https_ready`; a same-instance takeover may retire a Worker-side zombie WSS only after the signed candidate preconditions pass. Both directions use bounded contiguous transport sequences, so a lost HTTP response retransmits the same transport envelope and duplicates are discarded before business handling; this does not restore MCP sessions, recovery GET, `Last-Event-ID`, or public result persistence. Same-instance `resume_calls` / `resume_calls_ack` remains authoritative for in-flight ownership. The daemon sends `resume_calls_ack.missing_ids` only after replacement readiness and only for IDs absent from both its active-call set and unacknowledged-result ledger. Those proven-undelivered calls may be retransmitted with the same call ID, arguments, authority, and a reduced timeout inside the original deadline; a call that may have executed is never automatically replayed. A new call may wait up to fifteen seconds for a verified daemon channel, but measured recovery time is deducted from that call's original execution budget instead of extending the hosted foreground envelope. Hosted synchronous calls otherwise retain their ordinary 20-second execution plus separate five-second Worker settlement margin; configurable browser/application tools retain 20-second ordinary defaults, compound `computer_observe` / `computer_act` retain 30-second defaults, and the explicit remote maximum remains 45 seconds. Remote `exec_command`, `run_process`, and `run_local_command` require a caller-held `idempotency_key`, commit a principal-bound one-step managed job, and remain recoverable with a `read_job` status checkpoint; an active checkpoint yields the hosted turn instead of being polled to terminal state. `start_process` remains daemon-lifetime interactive state, while hosted `read_process` is a single live-session status checkpoint per assistant response: a blocking wait lasts at most one second, repeated would-block waits are throttled by the fifteen-second cooldown, and any result with `running=true` hands the turn back even when new output was returned. The durable process façade preserves account/tool authority and delegated workspace sandbox rather than expanding privileges.
|
|
176
176
|
|
|
177
177
|
`full` is the daemon capability ceiling. An authenticated owner may exercise it without per-operation approval IDs. Delegated reviewer, editor, and operator accounts remain inside immutable role ceilings; out-of-role operations are denied rather than converted into a temporary elevation workflow. Process sessions, retained output, and managed jobs are additionally bound to account, client, and refresh-token family. See [local authorization](docs/LOCAL_AUTHORIZATION.md).
|
|
178
178
|
|
|
@@ -193,7 +193,7 @@ For stateful GUI trajectories, owner/full callers can use the higher-level `comp
|
|
|
193
193
|
|
|
194
194
|
## Durable work and local resources
|
|
195
195
|
|
|
196
|
-
Remote request-owned foreground work uses the hosted reply-safe budgets described above; configurable browser/application calls may explicitly request at most 45 seconds, while remote `exec_command`, `run_process`, and `run_local_command` are durable one-step jobs with a 10-second acceptance envelope and an independent 1–600-second child execution budget after admission. The Worker retains separate settlement ownership for five additional seconds, but neither that margin nor its internal stream metrics prove that an external MCP host consumed the terminal frame. Keep mutations and validation in independently terminal calls. A timeout is a protocol result, not proof that descendant cleanup has already completed; a remote owner can inspect `diagnose_runtime.runtime.processes`, while local stdio exposes `server_info.runtime.processes`. Non-owner accounts receive authority-scoped readiness rather than machine-wide process activity.
|
|
196
|
+
Remote request-owned foreground work uses the hosted reply-safe budgets described above; configurable browser/application calls may explicitly request at most 45 seconds, while remote `exec_command`, `run_process`, and `run_local_command` are durable one-step jobs with a 10-second acceptance envelope and an independent 1–600-second child execution budget after admission. The Worker retains separate settlement ownership for five additional seconds, but neither that margin nor its internal stream metrics prove that an external MCP host consumed the terminal frame. Keep mutations and validation in independently terminal calls. A timeout is a protocol result, not proof that descendant cleanup has already completed; a remote owner can inspect `diagnose_runtime.runtime.processes`, while local stdio exposes `server_info.runtime.processes`. Non-owner accounts receive authority-scoped readiness rather than machine-wide process activity. Remote process sessions are for interactive stdin or incremental output, not a substitute for waiting synchronously on ordinary long work: hosted `read_process` is a status checkpoint, its blocking wait is at most one second, repeated would-block waits on the same live session are throttled for fifteen seconds, and a hosted assistant response should call it at most once for a live session before returning any `running=true` session/progress to the user, even when that checkpoint returned new output. Non-interactive work should use durable `run_process`/`read_job`; multi-step, cleanup-sensitive, or daemon-restart-surviving workflows should use managed jobs, which persist ordered argv steps and `finally_steps` under owner-only local state and continue across an MCP disconnect. Durable acceptance is also a hosted-turn handoff: if one `read_job` checkpoint still reports an active job, return its `job_id`, status, and current phase to the user rather than polling it to terminal state in the same assistant response. `list_jobs` is an inventory checkpoint under the same rule, not a substitute polling loop for waiting on active work. More generally, hosted read-only status/diagnostic surfaces such as `server_info` or `diagnose_runtime` are evidence checkpoints rather than alternate wait loops for the same background state.
|
|
197
197
|
|
|
198
198
|
On macOS, authorized remote activity uses a bounded idle-sleep assertion so ordinary system Idle Sleep does not suspend an active remote workflow. Relay handlers share the assertion for their execution lifetime plus a fixed thirty-minute rolling inactivity grace; each new authorized remote activity cancels a pending release and restarts the full grace after the last concurrent handler settles. An admitted remote process session extends daemon-side ownership until its child settles, and an account-backed managed-job runner owns a runner-bound assertion from confirmed claim through terminal persistence. Local managed jobs do not acquire the remote-continuity assertion. These protections do not override explicit sleep or lid-close behavior.
|
|
199
199
|
|
|
@@ -30,6 +30,6 @@
|
|
|
30
30
|
"action": {
|
|
31
31
|
"default_title": "Machine Bridge Browser"
|
|
32
32
|
},
|
|
33
|
-
"version_name": "3.0.0-beta.
|
|
33
|
+
"version_name": "3.0.0-beta.104",
|
|
34
34
|
"key": "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxryYkpZhq8+VAQLHcGS9BAHQcyKX8RHGIpIwvtIVRU/rcOcE0bNdnM0aZJ/h6xWQsGDHlhvjT2+1aJaAn/9k8473BRWajzVXld961CdHYVFVHoce2hHiSJ0xydWrHMMZhAm0mN0UzjEpgZ0tMw209efcZHIvSwuxhteZMRy4kyiVjwFlOf5oXFCxRuCJnPj3AK9CmCf4XgEBuPIJ0TZmjGHOOdBvJmbCNnAWXYEo5/mf7MfCGhV4IJ1hNuhpoNQfOFKMUcw9/v/IpT62XpfXdGYTfGYCmCjC+gntK1spbkr2P4/2+sYMQtLpse71mpSNGXfcf3abU55Vpn+gncSxRQIDAQAB"
|
|
35
35
|
}
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -44,7 +44,7 @@ A canonical workspace receives an independent profile, Worker name, secret set,
|
|
|
44
44
|
- `browser-request-registry.mjs`, `browser-broker-routes.mjs`, `browser-broker-server.mjs`, and `browser-bridge-http.mjs` separate direct request ownership, runtime-client proxy routing, authenticated loopback WebSocket upgrades/listening, and loopback HTTP handling from broker startup and extension handover;
|
|
45
45
|
- managed jobs, local resources, application automation, browser automation, and snapshot-bound Computer Use remain separate managers.
|
|
46
46
|
|
|
47
|
-
Architecture tests cap the orchestration module and each extracted service independently and reject a return of low-level process, patch, diagnostic, capability-scoring, heartbeat-policy, or audit-storage logic to `LocalRuntime`. `RelayConnection` owns remote WebSocket transport, `hello_ack` authentication, end-to-end `relay_probe`/`ready_ack` readiness, reconnect backoff, outage logging, and a monotonically increasing in-memory transport generation. `RelayLiveness` owns the two-layer
|
|
47
|
+
Architecture tests cap the orchestration module and each extracted service independently and reject a return of low-level process, patch, diagnostic, capability-scoring, heartbeat-policy, or audit-storage logic to `LocalRuntime`. `RelayConnection` owns remote WebSocket transport, `hello_ack` authentication, end-to-end `relay_probe`/`ready_ack` readiness, reconnect backoff, outage logging, and a monotonically increasing in-memory transport generation. `RelayLiveness` owns the two-layer connection liveness policy: protocol Ping remains the first-stage half-open detector, but one missed dispatched Pong on a ready socket enters a bounded application-confirmation state rather than immediately terminating WSS; the independent periodic application heartbeat still refreshes Worker daemon activity and runs only after verified readiness. `RelayHeartbeatMonitor` remains the shared timer/recovery loop beneath that policy; `RelayHeartbeatStall` owns expected-versus-actual timer-lag diagnostics and wall-clock stall timestamps, `RelayProbeDeadline` owns the transport probe's dispatch-relative response deadline, and `RelayTransportConfirmation` owns the second-stage confirmation window so delayed local scheduling, sender backpressure, one transient persistent-flow stall, and a confirmed two-stage black hole remain distinguishable. The generation still protects the pre-ready probe and prevents arbitrary use of a stale socket. An explicit stop while the first connection is still waiting for readiness settles that pending start as cancelled/no-readiness rather than leaving its Promise unresolved; `LocalRuntime` also refuses to overwrite a concurrent `stopping`/`stopped` lifecycle with a late relay success or failure. Ordinary tool calls additionally bind to an ephemeral identifier generated once per local daemon process. If a ready socket drops, the Worker detaches its pending calls for at most the shared two-minute reconnect grace; only a replacement socket that presents the same daemon-process identifier and completes the full readiness probe can reclaim them. The local runtime keeps those calls alive and queues completed results during the same bounded interval, then replays them after readiness. A different process instance, an explicit cancellation, or grace expiry cannot receive those results. Stdio mode invokes `LocalRuntime` directly without that adapter.
|
|
48
48
|
|
|
49
49
|
The control plane has explicit availability budgets at both admission layers. The Worker admits thirty ordinary pending daemon calls and reserves two additional slots for `diagnose_runtime`/`list_roots`; one serialized admission gate and the transient pending-call registry enforce that per-tool capacity. There is no second durable MCP call pool to merge into the accounting. Pending operation/reconnect delays are positive finite safe integers bounded by the shared relay contract before any timer or Durable Object alarm deadline is armed; malformed internal timing cannot become an infinite timer. The local relay independently rejects malformed call IDs, tool-name shapes, authorization field types, and timeout values before dispatch instead of coercing them into a usable envelope. The local runtime independently admits fourteen ordinary tools and reserves two control slots; the total ceilings remain thirty-two and sixteen. A timed-out or cancelled process call settles at the protocol boundary before operating-system cleanup necessarily completes, so `process-tracker.mjs` keeps that process under a draining call until `close` and reports pending escalation supervision. Neither process ownership inspection nor security-audit persistence performs synchronous process creation or disk `fsync` on the daemon event loop.
|
|
50
50
|
|
|
@@ -255,11 +255,11 @@ Fixed implementation-owned Git operations are not routed through that arbitrary-
|
|
|
255
255
|
|
|
256
256
|
The default `full` profile passes the complete parent environment. Isolated environment mode, used by the narrower named profiles unless overridden, creates private runtime HOME, temporary, and cache directories and passes only a small set of path/locale/platform variables. It reduces accidental credential inheritance but cannot prevent explicit access to known filesystem paths, credential stores, network services, or other user resources.
|
|
257
257
|
|
|
258
|
-
`execution-limits.mjs` is the shared source for local tool-call concurrency, one-shot process timeout/stdin/output limits, and process-session count/stdin/output/retention limits. Remote-owner `diagnose_runtime.runtime.execution_guardrails` reports those enforced limits, while local stdio `server_info.runtime.execution_guardrails` exposes the same contract together with explicit `not-enforced` values for CPU quota, memory quota, and network isolation. Public one-shot commands inline at most 32 KiB per stream. When either stream exceeds that preview, the runtime keeps up to 1 MiB per stream in a closed in-memory process session for thirty minutes and returns an `output_session_id`; `read_process` then reads monotonic byte-offset pages. The oldest exited session is evicted before an active session is refused, so continuation retention is explicitly best effort rather than durable. The continuation stores command basename and cwd metadata but not argv or shell text. It is memory-only and disappears on runtime stop or daemon replacement.
|
|
258
|
+
`execution-limits.mjs` is the shared source for local tool-call concurrency, one-shot process timeout/stdin/output limits, and process-session count/stdin/output/retention limits. Remote-owner `diagnose_runtime.runtime.execution_guardrails` reports those enforced limits, while local stdio `server_info.runtime.execution_guardrails` exposes the same contract together with explicit `not-enforced` values for CPU quota, memory quota, and network isolation. Public one-shot commands inline at most 32 KiB per stream. When either stream exceeds that preview, the runtime keeps up to 1 MiB per stream in a closed in-memory process session for thirty minutes and returns an `output_session_id`; `read_process` then reads monotonic byte-offset pages. Relay-origin process reads are separately reply-safe status checkpoints. `process-session-read.mjs` owns read/wait/output orchestration while `process-session-remote-poll.mjs` owns only hosted projection/cooldown policy, leaving `process-sessions.mjs` focused on session lifecycle and ownership. The async read helper returns its internal plan/output snapshot before hosted projection is finalized; after the await the manager re-checks cancellation, then completes projection synchronously, so cancellation at the Promise boundary is not lost and a child exit there cannot produce `running=false` with stale handoff/cooldown metadata. The Worker and daemon cap one hosted blocking wait at one second; a live session that has just consumed a remote blocking read enters a fifteen-second would-block cooldown during which another blocking wait returns immediate status. Every relay-origin read identifies `status_polling_mode=checkpoint`, and a `running=true` result recommends handing the hosted turn back regardless of whether the checkpoint also returned new output. The cooldown fields are explicitly blocking-only (`blocking_poll_throttled`, `next_blocking_poll_after_ms`) so a zero-wait status read is not misrepresented as globally rate-limited. This state is per in-memory process session, does not alter local stdio/CLI wait behavior, and is intentionally a hosted-turn anti-polling bound rather than durable scheduling. The oldest exited session is evicted before an active session is refused, so continuation retention is explicitly best effort rather than durable. The continuation stores command basename and cwd metadata but not argv or shell text. It is memory-only and disappears on runtime stop or daemon replacement.
|
|
259
259
|
|
|
260
|
-
Resource admission is a separate cooperative boundary, not an OS quota. `resource-foreground-wait.mjs` owns the ordinary process-start wait budget: a one-shot foreground call defaults to 20% of its execution timeout with a two-second floor, ten-second ceiling, and never more than the complete execution timeout. Owner-local process-session startup defaults to a ten-second cooperative wait, while relay-origin `start_process` performs one admission attempt without queueing by default so known host pressure is returned before the request-owned response budget is consumed. An explicit configured override remains authoritative for controlled diagnostics/tests. `LocalRuntime` leaves this default unconfigured in production so the services actually apply it, while explicit overrides are validated and capped at thirty minutes. These admission waits occur before process spawn and do not enlarge an outer relay deadline; cancellation propagates through the same coordinator wait. Detached managed-job steps use the shared durable-delivery admission ceiling instead: the runner may wait up to thirty minutes before spawn, child execution timeout starts only after admission succeeds, and persisted job status reports `current_phase=resource_admission` during that pre-spawn interval. Owner diagnostics separately expose the Boolean `waiters.drain_active`, computed by the same fairness selection state machine, so Green host/resource pressure is not confused with an aged protected waiter intentionally reserving a drain window. A fixed CPU request that exceeds the machine's best-case priority-specific launch window is structurally incapable of becoming admissible while the configured CPU headroom remains in force; it therefore returns non-retryable `cpu_request_exceeds_launch_window` before entering the retry sleep loop instead of masquerading as transient `cpu_pressure_window`. Explicit worker counts/argv are never rewritten and the headroom is not relaxed; elastic/unbounded requests retain their pressure-aware fitting behavior. `resource-command-profile.mjs` classifies known light operations, bounded adaptive unknowns, and known CPU/I/O/mixed build families. `resource-script-classification.mjs` classifies only an actually executed shell/Node/Python script operand or package-manager script name, and `resource-shell-analysis.mjs` owns conservative shell token/segment parsing. This lets direct orchestration roots reserve startup capacity before descendant fan-out without letting an unrelated argument such as a test filename or release note impersonate a heavy script. The arbitrary-process zero-resource allowlist is intentionally identity-bound: `resource-light-command.mjs` accepts only a small set of standard absolute executables for constant/output, process-table, uptime, and sleep probes. Bare PATH-resolved names and every arbitrary shell invocation remain adaptive even when their apparent command is cheap, because executable resolution, startup configuration, repository configuration, path behavior, options, helpers, or input size can change the actual work. Caller-controlled Git, filesystem metadata/query commands, lookup helpers, recursive/search/file-stream processors, `find`, application launchers, and script interpreters therefore never receive zero-resource admission from basename alone. Implementation-owned Git/diagnostic probes use their separate fixed-argv/internal boundary and do not depend on this allowlist. `resource-admission.mjs` persists per-user owner-only leases and waiters outside workspace state; ownership is bound to PID plus process-start identity and, where supported, the isolated process group. Releasing the caller-side lease does not delete a bound POSIX reservation while that isolated process group still exists: the persisted lease remains until ordinary pruning observes the group gone, so a detached descendant cannot become unaccounted merely because its direct caller returned. Independent nested process roots retain their own full durable leases for crash recovery, but live accounting does not blindly add an orchestration envelope and every child reservation. `resource-process-ancestry.mjs` samples the live parent graph through `resource-process-ancestry-cache.mjs`; the async cache coalesces concurrent requests and retains a completed snapshot for one second so admission retries do not repeatedly enumerate the complete process table. If ancestry cannot be sampled, accounting falls back to conservative full summation. `resource-lease-accounting.mjs` forms an ephemeral lease forest whose effective vector is the component-wise maximum of a node's own envelope and the sum of its direct lease children. A pending nested request is charged only for the additional vector it contributes to that forest. Same-key contention is exempted only for the request's actual ancestor lease chain; siblings and unrelated roots still serialize.
|
|
260
|
+
Resource admission is a separate cooperative boundary, not an OS quota. `resource-foreground-wait.mjs` owns the ordinary process-start wait budget: a one-shot foreground call defaults to 20% of its execution timeout with a two-second floor, ten-second ceiling, and never more than the complete execution timeout. Owner-local process-session startup defaults to a ten-second cooperative wait, while relay-origin `start_process` performs one admission attempt without queueing by default so known host pressure is returned before the request-owned response budget is consumed. An explicit configured override remains authoritative for controlled diagnostics/tests. `LocalRuntime` leaves this default unconfigured in production so the services actually apply it, while explicit overrides are validated and capped at thirty minutes. These admission waits occur before process spawn and do not enlarge an outer relay deadline; cancellation propagates through the same coordinator wait. Detached managed-job steps use the shared durable-delivery admission ceiling instead: the runner may wait up to thirty minutes before spawn, child execution timeout starts only after admission succeeds, and persisted job status reports `current_phase=resource_admission` during that pre-spawn interval. Hosted status inspection is a checkpoint rather than foreground waiting: `managed-job-hosted-status.mjs` owns the relay-only projection, so relay-origin `read_job` reports `status_polling_mode=checkpoint` plus `host_turn_handoff_recommended=true` for active jobs while local job reads retain their prior shape. Worker tool guidance tells callers to stop polling after one active-job read in the same assistant response; relay-origin `list_jobs` is also an inventory checkpoint whose handoff recommendation is computed only from the already-authorized returned job set, closing the listing-as-polling bypass without exposing another principal's jobs or machine-wide scheduler state. Ordinary managed-job result/status projection remains separate from this hosted scheduling policy. Owner diagnostics separately expose the Boolean `waiters.drain_active`, computed by the same fairness selection state machine, so Green host/resource pressure is not confused with an aged protected waiter intentionally reserving a drain window. A fixed CPU request that exceeds the machine's best-case priority-specific launch window is structurally incapable of becoming admissible while the configured CPU headroom remains in force; it therefore returns non-retryable `cpu_request_exceeds_launch_window` before entering the retry sleep loop instead of masquerading as transient `cpu_pressure_window`. Explicit worker counts/argv are never rewritten and the headroom is not relaxed; elastic/unbounded requests retain their pressure-aware fitting behavior. `resource-command-profile.mjs` classifies known light operations, bounded adaptive unknowns, and known CPU/I/O/mixed build families. `resource-script-classification.mjs` classifies only an actually executed shell/Node/Python script operand or package-manager script name, and `resource-shell-analysis.mjs` owns conservative shell token/segment parsing. This lets direct orchestration roots reserve startup capacity before descendant fan-out without letting an unrelated argument such as a test filename or release note impersonate a heavy script. The arbitrary-process zero-resource allowlist is intentionally identity-bound: `resource-light-command.mjs` accepts only a small set of standard absolute executables for constant/output, process-table, uptime, and sleep probes. Bare PATH-resolved names and every arbitrary shell invocation remain adaptive even when their apparent command is cheap, because executable resolution, startup configuration, repository configuration, path behavior, options, helpers, or input size can change the actual work. Caller-controlled Git, filesystem metadata/query commands, lookup helpers, recursive/search/file-stream processors, `find`, application launchers, and script interpreters therefore never receive zero-resource admission from basename alone. Implementation-owned Git/diagnostic probes use their separate fixed-argv/internal boundary and do not depend on this allowlist. `resource-admission.mjs` persists per-user owner-only leases and waiters outside workspace state; ownership is bound to PID plus process-start identity and, where supported, the isolated process group. Releasing the caller-side lease does not delete a bound POSIX reservation while that isolated process group still exists: the persisted lease remains until ordinary pruning observes the group gone, so a detached descendant cannot become unaccounted merely because its direct caller returned. Independent nested process roots retain their own full durable leases for crash recovery, but live accounting does not blindly add an orchestration envelope and every child reservation. `resource-process-ancestry.mjs` samples the live parent graph through `resource-process-ancestry-cache.mjs`; the async cache coalesces concurrent requests and retains a completed snapshot for one second so admission retries do not repeatedly enumerate the complete process table. If ancestry cannot be sampled, accounting falls back to conservative full summation. `resource-lease-accounting.mjs` forms an ephemeral lease forest whose effective vector is the component-wise maximum of a node's own envelope and the sum of its direct lease children. A pending nested request is charged only for the additional vector it contributes to that forest. Same-key contention is exempted only for the request's actual ancestor lease chain; siblings and unrelated roots still serialize.
|
|
261
261
|
|
|
262
|
-
Live host-pressure observation is deliberately asynchronous. `resource-probe-command.mjs` owns bounded child-probe transport, `resource-host-darwin.mjs` runs Darwin memory, VM, disk, and thermal probes concurrently, and `resource-host-snapshot.mjs` composes those results without blocking the daemon event loop on `ps`, `memory_pressure`, `vm_stat`, `iostat`, or `pmset`. Process-start identity used when binding a spawned lease is also sampled asynchronously before the coordinator file lock is taken. `resource-host-cache.mjs` treats a same-project general host snapshot as fresh for 500 milliseconds while retaining successful I/O evidence for up to five seconds; a stale general sample can therefore refresh cheap CPU/memory/load data without waiting for another one-second `iostat` interval. Timestamps represent sample completion rather than sample start. `resource-admission-policy.mjs` evaluates effective CPU, memory, I/O, disk-reserve, startup-window, and host-pressure values without imposing global serialism. macOS uses memory/pageout/swapout/disk/thermal observations; Linux adds `MemAvailable` and optional PSI avg10 observations through `resource-host-linux.mjs`. The soft free-disk floor is `min(80 GiB, max(8 GiB, 15% of volume))`; the post-reservation hard floor is `min(50 GiB, max(5 GiB, 10% of volume))`. Disk-only hard-red pressure has one narrow self-recovery exception: only an internally classified direct standard absolute deletion executable with the exact small `disk-reclaim` envelope may enter using Yellow capacity limits, and only when `disk_free_headroom_critical` is the sole critical reason. PATH-resolved or shell-composed deletion cannot claim that class, and thermal, memory, PSI, CPU/load, or other Red evidence still blocks it. Diagnostics expose observed busy CPU next to reserved CPU and their ratio as a mismatch signal, not as process attribution. `resource-waiters.mjs` normally skips an older request that does not fit, but a sufficiently aged rank-zero waiter blocked by coordinator-owned project/CPU/I/O/memory capacity can enter a protected drain phase if it is structurally feasible after current leases disappear. Fixed implementation-owned probes bypass this coordinator so diagnostics/recovery remain usable during resource pressure.
|
|
262
|
+
Live host-pressure observation is deliberately asynchronous. `resource-probe-command.mjs` owns bounded child-probe transport, `resource-host-darwin.mjs` runs Darwin memory, VM, disk, and thermal probes concurrently, and `resource-host-snapshot.mjs` composes those results without blocking the daemon event loop on `ps`, `memory_pressure`, `vm_stat`, `iostat`, or `pmset`. Process-start identity used when binding a spawned lease is also sampled asynchronously before the coordinator file lock is taken. `resource-host-cache.mjs` treats a same-project general host snapshot as fresh for 500 milliseconds while retaining successful I/O evidence for up to five seconds; a stale general sample can therefore refresh cheap CPU/memory/load data without waiting for another one-second `iostat` interval. Timestamps represent sample completion rather than sample start. `resource-admission-policy.mjs` evaluates effective CPU, memory, I/O, disk-reserve, startup-window, and host-pressure values without imposing global serialism. macOS uses memory/pageout/swapout/disk/thermal observations; Linux adds `MemAvailable` and optional PSI avg10 observations through `resource-host-linux.mjs`. The soft free-disk floor is `min(80 GiB, max(8 GiB, 15% of volume))`; the post-reservation hard floor is `min(50 GiB, max(5 GiB, 10% of volume))`. Disk-only hard-red pressure has one narrow self-recovery exception: only an internally classified direct standard absolute deletion executable with the exact small `disk-reclaim` envelope may enter using Yellow capacity limits, and only when `disk_free_headroom_critical` is the sole critical reason. PATH-resolved or shell-composed deletion cannot claim that class, and thermal, memory, PSI, CPU/load, or other Red evidence still blocks it. Diagnostics expose observed busy CPU next to reserved CPU and their ratio as a mismatch signal, not as process attribution. `resource-admission-diagnostics.mjs` owns the privacy-safe snapshot/check projection, while `resource-admission-diagnostic-error.mjs` separately classifies bounded transaction/staging-lock contention as retryable coordinator-busy snapshot unavailability instead of inflating the diagnostics module or mislabeling contention as process execution failure. `resource-waiters.mjs` normally skips an older request that does not fit, but a sufficiently aged rank-zero waiter blocked by coordinator-owned project/CPU/I/O/memory capacity can enter a protected drain phase if it is structurally feasible after current leases disappear. Fixed implementation-owned probes bypass this coordinator so diagnostics/recovery remain usable during resource pressure.
|
|
263
263
|
|
|
264
264
|
The same protocol is intentionally language-neutral: compatible workflow controllers may use the same per-user coordinator root, schema, lease ownership, contention keys, and waiter ordering. This permits Machine Bridge and workflow-bundle to coordinate independent process trees without a new daemon. `resource-project-key.mjs` canonicalizes the existing filesystem path before deriving the v1 project-contention hash, so path aliases such as macOS `/var` versus `/private/var` and symlinked workspace roots cannot create different mutex identities across implementations; Windows additionally normalizes separator form, path case, and extended-path prefixes before hashing. Lease/waiter records contain resource families, numeric reservations, hashes, timestamps, and process ownership but never argv, shell text, or raw project paths. The anonymous `project_hash` is derived from the same canonical project identity as contention and is diagnostic correlation only; it is not authorization or admission authority. The coordinator is fail-closed on malformed persistent authority.
|
|
265
265
|
|
|
@@ -293,7 +293,7 @@ Worker-name mutation is a separate identity transition. Existing state rejects a
|
|
|
293
293
|
|
|
294
294
|
The local `RelayConnection` treats proxy selection, transport construction, WebSocket open, authentication, end-to-end readiness, and outage recovery as separate states. The shared proxy module maps WebSocket targets to standard HTTP(S) environment-proxy resolution, honors `NO_PROXY`, rejects non-HTTP(S) proxy schemes, and creates the proxy agent without exposing its URL or credentials. Invalid proxy configuration is a fatal configuration error rather than a retryable outage.
|
|
295
295
|
|
|
296
|
-
A connection-attempt deadline terminates sockets stuck in `CONNECTING`. After open, the daemon sends `hello`; `hello_ack` establishes an authenticated relay generation and starts liveness monitoring, but does not resolve startup or advertise readiness. The Worker then sends a random `relay_probe`. Its result must traverse the local runtime dispatcher and `sendForSession` on that generation before `ready_ack` marks the connection usable. The daemon rejects ordinary tool calls before that state and rejects a premature readiness acknowledgement without locally recorded probe delivery. Independent handshake and readiness deadlines terminate candidates that authenticate but cannot return results. Once authenticated, `RelayLiveness` sends a protocol-level WebSocket Ping every five seconds
|
|
296
|
+
A connection-attempt deadline terminates sockets stuck in `CONNECTING`. After open, the daemon sends `hello`; `hello_ack` establishes an authenticated relay generation and starts liveness monitoring, but does not resolve startup or advertise readiness. The Worker then sends a random `relay_probe`. Its result must traverse the local runtime dispatcher and `sendForSession` on that generation before `ready_ack` marks the connection usable. The daemon rejects ordinary tool calls before that state and rejects a premature readiness acknowledgement without locally recorded probe delivery. Independent handshake and readiness deadlines terminate candidates that authenticate but cannot return results. Once authenticated, `RelayLiveness` sends a protocol-level WebSocket Ping every five seconds. The full ten-second Pong deadline begins only after the sender write callback proves the control frame left the local queue. Ping/application-send completions are fenced to the exact current WebSocket generation, so a callback from a superseded socket cannot mutate the replacement connection; a Ping callback that never completes has its own thirty-second local dispatch bound and becomes `relay_transport_send_timeout` even when the receive direction remains active. Reaching that first-stage deadline no longer immediately kills a ready WSS: `relay-transport-confirmation.mjs` opens one bounded fifteen-second application-confirmation window and emits the existing JSON heartbeat only after the connection is fully ready. A later protocol Pong or the explicit JSON application `pong` is bidirectional transport proof and clears suspicion; unrelated application messages update receive-side liveness only and cannot prove that daemon-to-Worker writes are succeeding. `ResilientRelayConnection` concurrently prewarms signed HTTPS in standby without Worker-side takeover; confirmed WSS recovery stops that prewarm, while a real disconnect upgrades it to exact-generation takeover and preempts any stale standby request. A scheduling-responsive true black hole therefore remains bounded to one five-second probe interval plus ten-second Pong response and fifteen-second independent confirmation, while a single ten-to-fifteen-second persistent-flow stall no longer becomes an avoidable reconnect storm. A detected local event-loop stall cancels remote suspicion and follows the separate recovery-grace branch rather than being counted as network failure. Transport Ping remains active during authenticated probing, but application heartbeat/confirmation is gated on verified readiness because the Worker probing state accepts only the readiness-probe result. Same-instance reconnect also performs explicit call-ownership reconciliation: the Worker sends its still-waiting IDs; the daemon snapshots the union of active calls and unacknowledged results, completes replacement-channel readiness, then returns `resume_calls_ack.missing_ids` only for IDs absent from that ownership union. Those IDs alone may receive one same-ID transport redelivery inside the original remaining deadline; if that cannot be done safely they settle retryably with `side_effects_started=false`. Active calls and retained terminal results continue on existing ownership, and no possibly executed tool call is automatically replayed. A separate JSON application heartbeat remains at twenty-five seconds and retains a seventy-five-second application-silence timeout, so protocol-level Pong cannot mask a Worker application path that has stopped responding. On the Worker, authenticated application-heartbeat activity refresh is synchronous and its JSON `pong` is queued before Durable Object alarm inspection or mutation; heartbeat and terminal-result paths then own one explicit coalesced alarm schedule. Persistent deadline work therefore cannot sit in front of application-liveness acknowledgement or be scheduled twice through a hidden touch helper. The Worker's ninety-second daemon-liveness deadline remains an independent wider fallback across Durable Object hibernation. Worker error codes `daemon_transport_error` and `daemon_liveness_timeout` are connection-recovery signals, not protocol incompatibility: they terminate only the current socket, run normal disconnect cleanup, and enter bounded reconnect. Local protocol-watchdog expiry is classified separately as `relay_transport_timeout`; local application-silence expiry retains `relay_heartbeat_timeout`. The same Worker classification is applied to the close frame if the preceding error frame is lost. Failure to send `hello`, or failure to deliver a readiness-probe result because its socket/session ended, follows the same transport-recovery path rather than being promoted to authentication or protocol failure. Unknown Worker errors, authentication rejection, malformed readiness sequencing, and identity/version mismatch remain fatal. Outage reminders run on their own exponential-backoff timer rather than depending on another transport callback. Each new authenticated hello also carries a schema-versioned, bounded summary of the immediately preceding local reconnect episode, including `previous_ready_inbound_silence_ms` so the silent half-open interval before close is not collapsed into the shorter close-to-ready outage duration. The Worker sanitizes that summary into the probing attachment; promotion to a ready daemon marks the episode recovered, canonicalizes timestamps, and retains only enumerated coarse error classes and bounded timing. Authenticated `server_info.daemon.relay_transport` therefore makes sub-warning-threshold interruptions diagnosable after recovery without claiming that the ready connection is still in outage or promoting every brief close to default logs.
|
|
297
297
|
|
|
298
298
|
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.
|
|
299
299
|
|
|
@@ -350,7 +350,7 @@ Third-party workflow actions are pinned to immutable commit SHAs. Dependabot gro
|
|
|
350
350
|
|
|
351
351
|
### Recovery and delivery boundaries
|
|
352
352
|
|
|
353
|
-
The relay connection owns transport lifecycle, `relay-liveness.mjs` owns
|
|
353
|
+
The relay connection owns transport lifecycle, while liveness policy is decomposed instead of accumulating in that class: `relay-liveness.mjs` composes transport and application monitors; `relay-liveness-actions.mjs` owns suspect/recovered/hard-failure effects; `relay-heartbeat.mjs` owns timer and event-loop-stall scheduling; `relay-heartbeat-transport-state.mjs` advances transport probe/confirmation phases; `relay-heartbeat-options.mjs` normalizes bounded timing; `relay-probe-dispatch.mjs` owns sender-dispatch identity/race state and `relay-probe-dispatch-metrics.mjs` its bounded diagnostics; `relay-probe-deadline.mjs` owns the post-dispatch Pong deadline; `relay-transport-confirmation.mjs` owns the second-stage application-confirmation state; `relay-transport-probe.mjs` owns socket-generation fencing plus bounded sender backlog while `relay-transport-probe-send.mjs` owns low-level Ping/fallback dispatch. `relay-transport-error-state.mjs`, `relay-heartbeat-stall.mjs`, `relay-connect-timing.mjs`, `relay-connection-classification.mjs`, and `relay-diagnostics.mjs` retain their error, stall, connect-timing, classification, and public-projection responsibilities. Application proxy selection is intentionally separated from the operating-system network path. Reconnect backoff is bounded at fifteen seconds, but each WebSocket connect attempt now has a thirty-second per-attempt connect budget because live successful upgrades were observed within roughly fourteen seconds of the former fifteen-second cutoff. Transport liveness requests a protocol probe after five seconds. `ws.ping()` queueing is not treated as dispatch: the local sender has a separate thirty-second no-progress bound, and only its write callback starts the full ten-second Pong response deadline. If fresh inbound traffic arrives while that callback is delayed, the logical dispatch retires at its deadline rather than letting a stale callback arm a future timeout. If a dispatched Ping reaches its ten-second deadline, a ready connection receives a separate fifteen-second application confirmation before `relay_transport_timeout` is allowed to terminate it; HTTPS is prewarmed during that suspect interval and becomes exact-generation takeover only after actual WSS loss. The twenty-five-second periodic application heartbeat / seventy-five-second application watchdog and ninety-second Worker daemon-liveness fallback remain separate checks with separate purposes. Worker-issued `connection_id` is part of the same-version welcome contract and binds an HTTPS fallback takeover to the exact disconnected WebSocket generation, so a delayed takeover request cannot retire a newer same-instance WebSocket that has already recovered. The root-signed daemon session remains intentionally bounded to 24 hours; expiry is a terminal runtime condition rather than a transport retry. Runtime cleanup precedes nonzero exit, after which installed service supervision obtains a new session on daemon restart instead of extending the certificate lifetime or looping forever with expired WSS/HTTPS credentials.
|
|
354
354
|
|
|
355
355
|
Exclusive lock publication is itself a two-name hard-link commit: after the complete staging inode is linked at the final lock path and before the private staging alias is removed, `nlink` is transiently two. Process/startup locks, owner-state locks, managed-job transition/recovery locks, browser pairing creation, and managed-job runner claims are the only current consumers allowed to bridge that internal window with the shared fixed multiple-link retry. The retry never turns a persistent multiply-linked ownership file into valid evidence. Runner claims use the same no-follow path-identity/single-link read boundary as other managed-job ownership state.
|
|
356
356
|
|
package/docs/AUDIT.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Security and privacy audit notes
|
|
2
2
|
|
|
3
|
+
## 2026-08-19 beta.104 hosted-turn polling continuity correction
|
|
4
|
+
|
|
5
|
+
**Current live evidence falsifies another sleep/relay-only diagnosis.** During the newly reported no-response interval, the beta.103 launchd daemon remained the same live service process, macOS still held the daemon's `PreventUserIdleSystemSleep` assertion, and the contemporaneous daemon warning log did not record a relay outage. The only retained August 19 relay warning preceded the incident and recovered after about 26 seconds. The local security-audit chain is more discriminating: from `2026-08-19T02:20Z` through `02:45Z` it records 497 settled Machine Bridge calls, with no individual request hanging for minutes. It does record 68 `read_process` calls, many consuming exactly the old 5,000 ms hosted wait, plus 91 `read_job` calls. The largest gap between audit settlements in that interval was under twenty seconds. Therefore the user-visible “no response” state was not one unresolved MCP request; the server contract allowed a hosted assistant turn to be monopolized by repeated status polling, with blocking process reads providing the strongest per-call delay and rapid managed-job reads keeping the foreground turn attached to work that was already durable.
|
|
6
|
+
|
|
7
|
+
**The causal amplifier existed in both wording and enforcement.** Beta.91 deliberately reduced remote `read_process` from 30 seconds to five seconds, but its current Worker description still said “poll again.” Process sessions were also recommended broadly for long work even though remote `run_process` had since become durable-first. A model following that contract could start a non-interactive process and repeatedly spend five seconds in `read_process` until exit. Each poll settled correctly, so transport deadlines and SSE heartbeats did not fire as errors; the defect was composition across many individually valid calls. Beta.104 changes the remote contract rather than merely shortening another global timeout. Hosted `read_process.wait_ms` is capped at one second in the Worker schema and independently clamped in the local relay-origin handler. After one remote read actually blocks while the child remains running, that session enters a fixed fifteen-second blocking-poll cooldown; another would-block read during the cooldown returns immediately. Every relay-origin result is explicitly a `status_polling_mode=checkpoint`; `host_turn_handoff_recommended` is true for any still-running session, while `blocking_poll_throttled` and `next_blocking_poll_after_ms` describe only the blocking-wait cooldown. Tool guidance reserves process sessions for interactive stdin/incremental-output use, says to perform at most one live-session `read_process` checkpoint in one hosted assistant response even when that checkpoint returned output, and routes non-interactive work to durable `run_process`/`read_job`. A final rolling-upgrade review found that the maximum-only stale-schema compatibility response still said “use short polling”; that path is especially relevant immediately after lowering the advertised maximum because an already-open host may retain the old five-second schema. Beta.104 gives stale `read_process` callers the same one-live-session-checkpoint/handoff/durable-work guidance, including the case where output is already available, and never suggests repeated short polling. Owner-local stdio/CLI waits retain the existing 30-second local schema and behavior.
|
|
8
|
+
|
|
9
|
+
**Durable acceptance must also end foreground waiting.** The same audit window contains 91 `read_job` calls. Those calls are individually fast and are not classified as an MCP hang, but a durable job exists specifically so its execution is independent of the initiating response stream. Repeatedly reading its status in one assistant response defeats that separation at the orchestration layer. Beta.104 therefore changes the hosted contracts for `start_job`, remote durable one-step process tools, and `read_job`: one active-job status read is a checkpoint, not a loop; if the job remains active/non-terminal (`queued`, `running`, `cleaning`, or interrupted recovery state), the caller returns the `job_id`, status, and current phase to the user and stops polling until a later user turn or explicit request. Relay-origin `read_job` results also expose `status_polling_mode=checkpoint` and `host_turn_handoff_recommended` while preserving local read shape unchanged. Independent review found one bypass: `list_jobs` exposes job lifecycle status too, so an agent could replace repeated `read_job` calls with repeated listings. Beta.104 therefore makes relay-origin `list_jobs` an inventory checkpoint under the same handoff rule; it computes handoff only from the already-authorized returned job set, while local listings retain their prior shape. Shared hosted instructions also state the general invariant that read-only status/diagnostic surfaces (`server_info`, `diagnose_runtime`, job/process status) are evidence checkpoints rather than substitute wait loops for the same background state, so changing which status tool is called cannot legitimately reintroduce same-response waiting. This metadata is principal-scoped through the existing managed-job ownership gate and contains no command, path, resource, or cross-workload scheduling identity.
|
|
10
|
+
|
|
11
|
+
**“Message send timed out” remains partly outside the server’s observable boundary.** Machine Bridge cannot observe a client message submission that fails before an MCP request reaches the Worker, so this audit does not claim that every host UI “message send timed out” event is caused or fixed inside this repository. A proposed outer-proxy first-byte heartbeat was explicitly rejected after tracing the real tool stream: `jsonRpcResponseStream()` synchronously enqueues the SSE comment `: connected` when the Durable Object response body is created, before the tool Promise settles, and `mcp-response-proxy.ts` immediately pumps that already-open upstream body. The synthetic silent-upstream fixture that motivated an extra outer heartbeat therefore did not represent the normal `tools/call` path. Beta.104 removes that speculative change. The demonstrated server-side mechanism that can keep a conversation unavailable for a new visible exchange is instead the hosted assistant turn remaining attached to repeated `read_process`/`read_job` polling after the underlying work has become session- or job-owned; the checkpoint/handoff repair closes that mechanism. A send timeout that occurs with no preceding active assistant/tool turn or before any MCP request reaches the Worker still requires host/client-side evidence.
|
|
12
|
+
|
|
13
|
+
**Independent architecture/privacy review found additional hardening opportunities beyond the original incident.** First, `process-sessions.mjs` and `managed-jobs.mjs` had both drifted to within a handful of lines of their explicit responsibility ceilings. Beta.104 moves read/wait/output orchestration to `process-session-read.mjs`, keeps the remote blocking-poll policy in `process-session-remote-poll.mjs`, and finalizes hosted process projection synchronously after the async read returns so an exit at that boundary cannot produce a settled `running=false` summary with stale handoff/cooldown metadata. It also moves hosted managed-job handoff policy into `managed-job-hosted-status.mjs` and directory scanning/authority-visible listing projection into `managed-job-listing.mjs`; neither lifecycle manager nor the ordinary managed-job result projection needs host-policy/list-formatting logic to stay under its architecture limit. The same instruction review also found the generic configurable-foreground advice “use process sessions or managed jobs for longer work” in both the shared tool catalog and the Worker projection. That is semantically wrong for browser/application operations and capable of reintroducing process-session overuse even if one projection layer is corrected. Beta.104 removes the advice at the shared source and Worker layers, regenerates the 54-tool reference, tells browser/application callers to split longer workflows into independently terminal foreground calls, limits durable process/job routing to process-based work, and keeps `start_process` interactive-I/O only. Architecture checks reject the obsolete advice in both the live catalog source and generated reference. Second, a live beta.103 WSS outage during this review recovered through the authenticated HTTPS fallback with no local event-loop stall and Green resource pressure, but an in-flight read failed with “reconnect grace expired.” Source tracing showed pending calls actually wait for `min(reconnect grace, original remaining call deadline)`; because hosted tool deadlines are shorter than the full reconnect grace, the old message could mislabel ordinary deadline exhaustion as a reconnect-grace failure. Beta.104 distinguishes those causes without pausing or enlarging the original deadline. Later, after full verification created heavy shared-host contention, `diagnose_runtime` briefly projected `resource_admission={healthy:false,error_class:execution_failed}` while the transaction-lock owner was demonstrably changing between live processes; a subsequent snapshot returned normal Red pressure with active leases/waiters. The coordinator was contended rather than corrupt. Beta.104 therefore maps bounded transaction/staging snapshot contention to retryable `unavailable`, `reason=coordinator_busy`, and `snapshot_available=false`, while leaving the admission policy, lock protocol, pressure limits, and fairness behavior unchanged. Third, ignored Wrangler-generated type files can embed the local workspace path in their generator comment when Wrangler receives an absolute output argument. They are excluded from Git and the npm publication surface, so this was not a published-secret finding, but it is unnecessary local metadata exposure if generated artifacts are copied or archived. The generator now supplies a working-directory-relative output and refuses targets outside that working directory; the package/privacy gates continue to exclude `.wrangler` artifacts. Fast verification also exposed a deterministic-time defect in device-session construction: callers could supply `now` to create a certificate, but final validation silently switched back to real wall time, so historical/synthetic clocks eventually expired at creation time. Beta.104 threads the same explicit clock through finalization, including the secure-root provider; production callers that omit `now` still use `Date.now()`. Review of runtime debug logging and the security-audit projection found no new content leak: routine tool logs remain argument-free, audit principal identifiers are keyed fingerprints, and `content_logged=false` remains the persisted audit contract.
|
|
14
|
+
|
|
15
|
+
**Deeper WSS review found three server-created continuity defects in addition to a genuine residual network-failure class.** First, the transport contract advertises a five-second protocol probe interval plus a ten-second Pong response timeout, but the old generic heartbeat monitor measured the ten seconds from the preceding inbound frame. With no inbound traffic it sent the first protocol Ping at five seconds and then terminated the socket at ten seconds total, so the nominal ten-second Pong timeout gave that probe only about five seconds to receive a response. A red regression reproduced the premature termination. Beta.104 now separates local Ping queueing from actual dispatch: the full ten-second Pong deadline starts only when the `ws` sender write callback confirms that the Ping left the local queue. Bidirectional proof that arrives while Ping dispatch is pending—protocol Pong or the explicit application `pong`—is remembered only for that dispatch token: if the local write callback completes inside the thirty-second dispatch budget, its later completion cannot arm a stale future Pong timeout. Unrelated application inbound does not satisfy that token because it proves only the Worker-to-daemon receive direction. Because data-frame compression/backpressure can legitimately delay a control frame, the local dispatch phase has a separate thirty-second bound rather than consuming the remote-response deadline; if the write callback still has not completed at that bound it is `relay_transport_send_timeout` even when unrelated inbound traffic continues, while an actually dispatched Ping with no response remains `relay_transport_timeout`. Sender `bufferedAmount` plus dispatch delay/timeout age are retained only as bounded numeric evidence to distinguish local outbound backlog from a missing remote response. Cloudflare protocol Pong handling does not require the Durable Object JavaScript handler, so Durable Object hibernation is not used as an explanation for that missing response. Second, HTTPS fallback takeover was bound only to daemon `instance_id`. A previously dispatched takeover could therefore arrive after a newer same-instance WSS had already recovered and retire that newer socket. The Worker already generated a per-WebSocket connection identity internally; beta.104 now includes that `connection_id` in the welcome contract, requires it in same-version handshake validation, signs it into fallback takeover requests, and permits HTTPS takeover only when the exact targeted ready WebSocket generation is still the sole incumbent. A Worker integration regression proves a stale takeover returns standby and leaves the newer WSS ready. Third, the daemon's root-signed session certificate has an intentional 24-hour lifetime, but the runtime previously created it once at daemon startup and reused that same expired session for every later WSS/HTTPS reconnect. An established socket could survive beyond expiry, yet the next disconnect after 24 hours made both preflight and signed fallback authentication unusable; the old reconnect loop never renewed the session and could therefore remain permanently offline. Beta.104 gives local expiry a stable `device_session_expired` code and treats it as `relay_device_session_expired` fatal continuity state rather than a retryable transport/authentication error. Runtime cleanup runs first, then installed launchd/systemd/Windows supervision restarts the nonzero daemon so the default portable root signs a fresh 24-hour session; manual daemons require operator restart, and Secure Enclave mode preserves its existing user-presence requirement for a new daemon start instead of weakening certificate lifetime. The review also corrected an observability defect in which event-loop-stall timestamps converted monotonic `performance.now()` values into wall-clock dates, and adds privacy-bounded current-attempt plus last-failed DNS/TCP/TLS/upgrade milestone timing, transport-error authenticated/ready context, bounded probe-dispatch/backlog evidence, and Worker close code/clean-close metadata so a future residual outage can be localized without logging peer close reasons, addresses, DNS answers, endpoints, or credentials. A later live beta.103 episode remained independently classified as `relay_transport_error` / `network_error`, lasted roughly 274 seconds across 16 reconnect attempts, and then recovered with a final successful WebSocket connect in roughly 1.35 seconds. The signed HTTPS fallback's final successful poll was observed within roughly 0.4 seconds of WSS readiness, while the local event loop had no stall and the OS route was carried by a VPN/TUN. That is strong evidence of a shared system-network episode rather than a WSS-only server rejection, but beta.103 both overwrote failed-attempt stage timing after recovery and cleared fallback `last_transport_error_class` on each later success, so the exact historical errno cannot be recovered honestly. Beta.104 retains the last failed connect separately, preserves fallback last-error evidence after successful polls, and carries only strict allowlisted reasons such as `connection_timeout`, `network_unreachable`, bounded DNS/TLS classes, or `unknown`; until candidate evidence records one of those reasons, the residual episode is not assigned more narrowly to DNS, TCP, TLS, a particular VPN node, Cloudflare, or another upstream component.
|
|
16
|
+
|
|
17
|
+
**The remaining WSS instability had a second deterministic amplifier, not a second proven physical root cause.** A fresh review of the August 19 beta.103 warning stream found repeated ready-channel `relay_transport_timeout` episodes after about 12–15 seconds without inbound transport proof, plus successful later WSS connection attempts that took about 13.8 and 14.4 seconds and a separate roughly 4.5-minute/16-attempt `network_error` recovery episode. The daemon's event-loop diagnostics did not show a corresponding local scheduling stall. This establishes two facts without overclaiming a third: the persistent WSS path can genuinely become impaired, and beta.103's ten-second single-strike Pong policy plus fifteen-second connect ceiling made that impairment more disruptive; the available application evidence still does not identify which VPN/TUN implementation, route, edge, ISP, DNS/TCP/TLS component, or upstream device initiated the impairment. Beta.104 therefore does not relabel a missing Pong as healthy. Instead, after a dispatched Ping reaches its full ten-second response deadline, a ready WSS enters a separate fifteen-second application-confirmation state. The daemon emits one JSON heartbeat through the same socket and starts the signed HTTPS path only in standby. A subsequent protocol Pong or explicit JSON application `pong` clears suspicion, stops standby prewarm, and records bounded `relay.transport.recovered` timing; ordinary tool/control inbound does not, because receive-side traffic alone cannot prove the daemon-to-Worker direction. Only a second-stage miss records `relay.transport.confirmation_failed` and permits hard termination as `relay_transport_timeout`. If WSS closes first, the standby fallback is promoted to the existing exact-generation takeover; any old in-flight standby request is aborted so its seven-second request deadline cannot delay takeover. WSS connect attempts receive a thirty-second outer budget while fallback activation remains independent. The same review found and closed two state/observability traps: application heartbeat/confirmation is now gated on fully verified readiness rather than merely `hello_ack` authentication because the Worker probing state accepts only the readiness-probe result, and HTTPS `last_ready_at` no longer aliases every successful standby request. Fallback diagnostics now expose separate `last_success_at`, true `last_ready_at`, and bridge-level `https_fallback_warming`. A Ping write callback that completes inside the dispatch budget can consume only proof bound to that same round; if the callback itself remains stuck for thirty seconds it is a send failure regardless of unrelated inbound traffic. The same adversarial review found two additional generation/recovery bugs in the beta.104 candidate and fixed them before release: a delayed application-send callback from a superseded WSS previously lacked a socket-generation fence and could record an error against, or reset confirmation state belonging to, the replacement connection; and the signed HTTPS standby/failure loop carried a `consecutiveFailures` counter that did not affect scheduling, so successful standby prewarm and fast request/protocol failures could retry near the 750 ms hard floor. Send completions are now exact-generation fenced, confirmation callbacks are token-owned, standby prewarm uses a five-second cadence, and request/session/protocol failures use bounded one/two/four/five-second backoff while exact-generation takeover can preempt a later timer without bypassing the hard minimum request interval. These changes target the proven product amplification and leave only the separately observed long `network_error` episode's external physical trigger unresolved until candidate/live telemetry classifies DNS/TCP/TLS/system-route evidence more narrowly.
|
|
18
|
+
|
|
19
|
+
**Regression and release boundary.** Test-first changes bind the demonstrated failure surfaces: process-session coverage requires a zero-wait relay checkpoint not to arm the blocking cooldown, a stale/oversized blocking read to clamp to one second, a repeated would-block read to return immediately with precisely named blocking-only cooldown/handoff metadata, natural exit during a checkpoint to clear handoff/cooldown, `wait_for_exit` to retain the one-second clamp even when output already exists, the post-await completion step to use the final session state rather than a stale pre-exit snapshot, and a microtask-scheduled cancellation between helper resolution and manager continuation to be observed by the manager's post-await cancellation re-check; managed-job coverage requires active relay-origin `read_job` and visible-active `list_jobs` projections to carry checkpoint/handoff metadata while local reads/listings retain their prior shape; Worker runtime coverage requires the one-second schema/budget, global/process/job anti-loop descriptions, unambiguous `server_info.tool_delivery` blocking-poll field names, and the distinction between original-deadline and true reconnect-grace expiry; MCP-controller coverage requires a cached pre-beta.104 five-second `read_process` request to fail before dispatch with one-live-session-checkpoint/durable-work guidance instead of “short polling”; Worker-type generation coverage requires Wrangler to receive a relative output path and rejects escape outside its working directory; package coverage requires the extracted runtime modules to ship while ignored `.wrangler` artifacts remain excluded; and the architecture release contract rejects reintroduction of obsolete polling guidance or ambiguous field names. The proposed extra outer SSE heartbeat and its synthetic regression were removed after tracing proved the real `tools/call` stream already emits `: connected` before tool settlement. These are packaged runtime/Worker/documentation changes, so beta.103 verification and any acceptance evidence cannot authorize beta.104. Persistent activation still requires explicit owner authorization after a fresh frozen verification/candidate cycle.
|
|
20
|
+
|
|
3
21
|
## 2026-08-18 beta.103 idle-sleep continuity correction
|
|
4
22
|
|
|
5
23
|
**Live beta.102 disproved the five-minute post-activity lease.** After beta.102 had passed candidate activation, live acceptance, cross-platform PR checks, and merge, owner use still produced a severe whole-control-plane interruption. The retained relay state showed several recovered transport episodes, but the longest contemporaneous event was not an awake network black hole: macOS power records show the final `PreventUserIdleSystemSleep` assertion disappearing and ordinary `Idle Sleep` beginning about five seconds later for roughly 632 seconds. The daemon recorded an aligned roughly 626-second event-loop pause and long inbound silence while launchd retained one continuously running service process with no termination. This proves the host was suspended rather than the daemon crashing. Because both preferred WSS and signed HTTPS fallback execute in that suspended userspace, transport redundancy cannot recover the machine while it is asleep.
|
package/docs/CLIENTS.md
CHANGED
|
@@ -163,7 +163,7 @@ The local `full` profile controls Machine Bridge's own tool catalog, path resolv
|
|
|
163
163
|
|
|
164
164
|
Host-rendered tool invocation chips, status lines, or labels such as “called tool” are presentation owned by the MCP host, not Machine Bridge log records. Machine Bridge can suppress its own routine per-tool logs outside debug and can reduce unnecessary calls, but it cannot hide or restyle host-owned tool indicators. A host-side UI setting, if one exists, is the only layer that can change that presentation. Prefer fewer, coarser calls and avoid unnecessary capability-resolution calls when direct tools already suffice.
|
|
165
165
|
|
|
166
|
-
Hosts may also cache MCP discovery metadata and input schemas independently of the running server, and may keep conversation/surface app-routing state separately from the workspace-level app definition. For routine health checks, call `server_info` with `detail: "summary"`; it keeps the effective policy/count, account role without account ID, daemon readiness/relay state, pending/socket capacity, and foreground/settlement limits while omitting OAuth metadata, exact tool arrays, and full observability. The empty/default call returns the current `full` projection and is the cold path when an exact effective-tool list, account identity, or detailed counters are required. `server_info.tool_delivery` reports the current remote foreground maximum, process one-shot maximum, ordinary daemon-tool ceiling, process-poll wait
|
|
166
|
+
Hosts may also cache MCP discovery metadata and input schemas independently of the running server, and may keep conversation/surface app-routing state separately from the workspace-level app definition. For routine health checks, call `server_info` with `detail: "summary"`; it keeps the effective policy/count, account role without account ID, daemon readiness/relay state, pending/socket capacity, and foreground/settlement limits while omitting OAuth metadata, exact tool arrays, and full observability. The empty/default call returns the current `full` projection and is the cold path when an exact effective-tool list, account identity, or detailed counters are required. `server_info.tool_delivery` reports the current remote foreground maximum, process one-shot maximum, ordinary daemon-tool ceiling, process blocking-poll wait/cooldown ceilings, and Worker settlement overhead. If an already-open host still advertises older/larger values, refresh/review the workspace app or reconnect/recreate the connector so the host refreshes discovery; the Worker handles known maximum-only version skew as a structured no-side-effect tool error, but Machine Bridge cannot invalidate a host-owned schema/action cache from inside an already cached tool definition.
|
|
167
167
|
|
|
168
168
|
For routine workspace inventory, prefer `project_overview` with `detail: "summary"`. It keeps workspace/Git identity, effective and daemon policy/tool counts, compact capability-routing status, and up to 40 top-level names/types, while omitting account ID, exact tool arrays, routing fingerprints, and repeated entry paths/sizes. Request full detail only when those exact diagnostic fields are needed. As with `server_info`, summary is a presentation projection after authorization, not a lower-authority mode.
|
|
169
169
|
|
|
@@ -172,7 +172,7 @@ Machine Bridge itself does not block files because their names look sensitive. I
|
|
|
172
172
|
|
|
173
173
|
Expected file-operation failures arrive as ordinary MCP tool-error results, not JSON-RPC transport failures. Clients should branch first on `structuredContent.error.code`, then optionally on the bounded `details.reason`. For example, `conflict/already_exists`, `conflict/hash_mismatch`, `conflict/text_ambiguous`, and `conflict/context_not_found` require a fresh read and reconciliation; `not_found/text_not_found` means the requested edit fragment is absent; `invalid_request` means the request or patch syntax must change. Do not log or display tool arguments to reconstruct diagnostics: public error details intentionally omit paths, file content, edit fragments, and compared hashes.
|
|
174
174
|
|
|
175
|
-
Remote configurable foreground tools
|
|
175
|
+
Remote configurable browser/application foreground tools default to 20 seconds, compound `computer_observe`/`computer_act` default to 30 seconds, and all configurable foreground tools advertise a 45-second maximum. Those values bound daemon execution; the Worker records its settlement deadline five seconds later. Remote `exec_command`, `run_process`, and `run_local_command` use a separate durable 10-second acceptance contract with independently bounded detached execution. Hosted `read_process` is a live-session checkpoint: a blocking wait is at most one second, repeated would-block waits are suppressed during the blocking cooldown, and a `running=true` result ends process polling for the current assistant response even when output was returned. Admission and transport latency may consume part of a request interval, and none of these values guarantees host receipt. Missing or role-hidden tools, non-object arguments, and requests above current limits fail at the shared Worker schema boundary before daemon dispatch; schema failures include `side_effects_started=false`. SSE-capable current requests receive the same pre-dispatch validation as JSON responses and never allocate a recovery stream. Do not treat this as a retry invitation for the same oversized mutation, and do not attempt to evade a host refusal by renaming, encoding, or switching to another arbitrary execution tool. A durable acceptance or active `read_job` result is also a hosted-turn handoff: use at most one active-job status checkpoint in the current assistant response, return the job status to the user, and stop polling until a later turn or explicit request. `list_jobs` is an inventory checkpoint under the same rule and must not be repeated in the current response as a waiting mechanism. The same hosted principle applies to read-only status/diagnostic surfaces such as `server_info` and `diagnose_runtime`: use them to capture current evidence, not as substitute polling loops for waiting on the same background state. Instead:
|
|
176
176
|
|
|
177
177
|
1. register credentials locally as resource aliases so their values never enter MCP arguments;
|
|
178
178
|
2. submit a complete owner-authorized `start_job` plan before the workflow depends on later cleanup calls; `stage_job` is only a non-running draft, while an explicit local operator may use `machine-mcp job submit PLAN.json`;
|
package/docs/LOGGING.md
CHANGED
|
@@ -65,11 +65,11 @@ Brief network interruptions are expected on laptop network changes, Worker deplo
|
|
|
65
65
|
- recovery after a visible outage produces one information summary with a human-readable duration and attempt count; exact seconds and error classes remain debug-only;
|
|
66
66
|
- a verified replacement is a distinct warning and permanently stops the older daemon;
|
|
67
67
|
- failure to receive `hello_ack` within the handshake deadline, or `ready_ack` within the independent end-to-end readiness deadline, terminates the candidate socket and retries;
|
|
68
|
-
-
|
|
69
|
-
- a separate twenty-five-second application heartbeat refreshes Worker daemon activity and retains a seventy-five-second application-silence timeout;
|
|
68
|
+
- authenticated transports request protocol-level WebSocket Ping every five seconds. A sender callback starts the full ten-second Pong deadline only after actual local dispatch. If that deadline expires on a fully ready WSS, beta.104 records `relay.transport.suspect`, opens one fifteen-second application-confirmation window, sends a JSON heartbeat, and prewarms HTTPS in standby instead of killing the socket immediately. A later protocol Pong or explicit JSON application `pong` records `relay.transport.recovered` and preserves WSS; ordinary inbound tool/control traffic remains receive-side evidence and cannot clear transport suspicion. Only confirmation expiry records `relay.transport.confirmation_failed` and closes as `relay_transport_timeout`. A thirty-second local Ping-dispatch failure remains the distinct `relay.transport.send_timeout` / `relay_transport_send_timeout` path even if unrelated inbound traffic continues. Send-completion callbacks are exact-WebSocket-generation fenced, so a callback from a superseded socket cannot mutate current relay diagnostics or confirmation state. These anomaly events contain only bounded timing/state fields;
|
|
69
|
+
- a separate periodic twenty-five-second application heartbeat refreshes Worker daemon activity and retains a seventy-five-second application-silence timeout; it begins only after end-to-end readiness, so authenticated probing cannot send a message type that the Worker probing state does not accept. Protocol-level Pong therefore cannot mask a Worker application path that has stopped replying. The Worker queues the heartbeat's JSON `pong` before Durable Object alarm inspection or mutation, then performs one explicit coalesced schedule, so storage latency is not allowed to sit ahead of application-liveness acknowledgement;
|
|
70
70
|
- a late local transport-watchdog tick is classified as `runtime.event_loop.stall`, sends a fresh transport probe, and defers disconnect for a bounded recovery interval instead of being mislabeled as immediate remote failure; a macOS sleep/wake interval may legitimately produce this warning without a daemon fault. An authorized, schema-valid remote tool call enters the bounded macOS idle-sleep guard only after policy/account/operation authorization and argument validation succeed; the shared assertion remains active for the full handler lifetime, remote process sessions extend it through child settlement, and the fixed thirty-minute rolling inactivity grace starts only after the last owned daemon-side activity settles, with each new authorized activity cancelling pending release and restarting the full grace after settlement. If the fixed macOS assertion child cannot be established, `runtime.idle_sleep_guard.unavailable` records only a coarse `error_class`, never argv, paths, PID, tool name, tool content, session identity, or job identity. Remote account managed-job runners use the same fixed assertion primitive but own it themselves only after runner-claim confirmation and persisted account ownership validation; local managed jobs do not acquire this remote-continuity assertion. The remote runner fallback stderr diagnostic is the fixed text `managed job idle-sleep assertion unavailable` plus a sanitized coarse `error_class`, without job name/id, workspace path, argv, environment, or captured output.
|
|
71
71
|
|
|
72
|
-
A WebSocket close code such as `1006` means the transport ended without a normal close handshake. If it recovers inside ten seconds, the warning-level service log is intentionally silent and the authenticated `daemon.relay_transport` snapshot is the post-event evidence surface. It is useful for debug diagnosis but not useful as the default user message. It is not evidence that the daemon process restarted. Worker `daemon_transport_error` / `daemon_liveness_timeout` messages and their 1012 close frames are likewise retryable connection conditions, not upgrade instructions. Only an unknown/incompatible Worker error, authentication failure, or identity/version mismatch may produce the fatal protocol/configuration log and daemon exit. Default logs therefore describe the affected layer, duration, classification, and recovery behavior rather than printing raw close envelopes.
|
|
72
|
+
A WebSocket close code such as `1006` means the transport ended without a normal close handshake, but it does not identify who initiated termination: Machine Bridge's own liveness recovery calls `terminate()` when a transport/send timeout is confirmed, and that local hard close can surface as 1006. Diagnose the cause from `last_close_category`, transport-confirmation/send-timeout evidence, and retained network milestones rather than treating 1006 itself as proof of a remote/network-initiated close. If it recovers inside ten seconds, the warning-level service log is intentionally silent and the authenticated `daemon.relay_transport` snapshot is the post-event evidence surface. It is useful for debug diagnosis but not useful as the default user message. It is not evidence that the daemon process restarted. Worker `daemon_transport_error` / `daemon_liveness_timeout` messages and their 1012 close frames are likewise retryable connection conditions, not upgrade instructions. Only an unknown/incompatible Worker error, authentication failure, or identity/version mismatch may produce the fatal protocol/configuration log and daemon exit. Default logs therefore describe the affected layer, duration, classification, and recovery behavior rather than printing raw close envelopes.
|
|
73
73
|
|
|
74
74
|
Streamed-call diagnostics are deliberately coarse. MCP `2026-07-28` request/response-stream ownership and the Worker pending-call registry are memory-only; there is no protocol-session, terminal-result, subscriber, or replay store. Worker event counters are scoped to the current isolate and say so in `metric_scope`, while `server_info` exposes only bounded current pending-call/capacity state. These diagnostics do not prove public SSE consumption or MCP-host receipt. Logs and `server_info` must not include tool arguments, terminal results, command text, request keys, account identifiers, raw call IDs, raw connection generations, mirrored parameter values, private paths, or subscriber payloads. A stale-generation result is counted as unmatched rather than logged with its envelope.
|
|
75
75
|
|
|
@@ -153,7 +153,7 @@ Each managed job has owner-only runner diagnostic logs. Child-step output is ret
|
|
|
153
153
|
|
|
154
154
|
`network_route` describes only Machine Bridge's application-level proxy decision. `system-network-stack` does **not** mean a direct physical path: an operating-system VPN, TUN, packet tunnel, DNS interceptor, or endpoint-security product may still carry the connection. `network_route_scope` therefore remains `application-proxy-selection-only`.
|
|
155
155
|
|
|
156
|
-
During an outage, remote-owner `diagnose_runtime.runtime.relay` and local stdio `server_info.runtime.relay` expose bounded live fields: outage count/start/duration, attempts, last close category/code, coarse transport error class
|
|
156
|
+
During an outage, remote-owner `diagnose_runtime.runtime.relay` and local stdio `server_info.runtime.relay` expose bounded live fields: outage count/start/duration, attempts, last close category/code, coarse transport error class plus strict allowlisted `last_transport_error_reason`, last disconnect/ready time, prior ready duration, prior ready inbound-silence duration, the thirty-second WSS connect budget, bounded `last_connect_milestones_ms`, transport-probe queue/dispatch/Pong state, second-stage transport-confirmation timing/recovery state, bounded sender backlog bytes, HTTPS fallback active/warming state, and next retry timing. The fallback status separates `last_success_at` (any successful signed HTTP exchange, including standby) from `last_ready_at` (only verified ready ownership), so a successful standby poll can no longer be misread as a completed failover. Connect milestones are relative durations only and never include hostnames, addresses, DNS answers, certificates, proxy endpoints, or credentials. The current-attempt milestones and separate `last_failed_connect_*` fields are retained independently so a successful retry cannot erase the immediately preceding failed DNS/TCP/TLS/upgrade evidence; `last_transport_error_ready` and `last_transport_error_authenticated` state whether the retained transport error occurred after channel authentication/readiness. Worker-side `daemon.websocket.closed` records only a bounded close code plus `was_clean`; raw peer close reasons are deliberately omitted. An already-dispatched Worker call detached from a failed channel is retained only until the smaller of reconnect grace and that call's original remaining absolute deadline. If it settles without rebinding, the public bounded error distinguishes `original call deadline expired during reconnect` from a true full `reconnect grace expired`; neither diagnostic includes tool arguments, paths, account identity, endpoint data, or result content, and the distinction does not extend the hosted deadline. Resource-coordinator snapshot contention is likewise classified separately from execution failure: when the bounded diagnostic cannot acquire a transaction/staging lock, it reports retryable `unavailable`, `reason=coordinator_busy`, and `snapshot_available=false`. This says the diagnostic snapshot was unavailable under contention; it does not relax admission policy or claim that the underlying host pressure is Green. `outage_duration_ms` is the close-to-ready recovery interval; `last_ready_inbound_silence_ms` is the pre-close interval since the preceding ready transport last proved inbound activity. After recovery, authenticated remote `server_info.daemon.relay_transport` retains the bounded preceding episode supplied during the current connection handshake, including `previous_ready_inbound_silence_ms` and brief interruptions below the default warning threshold. Promotion to a ready socket sets `outage_active=false`, extends the outage duration through actual readiness, preserves the preceding healthy-ready duration and inbound-silence evidence across failed candidates, canonicalizes timestamps, and accepts only enumerated coarse operational error classes; it does not claim that the recovered connection remains in outage. The `local_authority_revocation_retry` category is deliberately not diagnosed as a network failure: a sustained warning directs the operator to local authority, process-session, and managed-job state while the retained Worker revocation retries on reconnection; ordinary transport categories retain network/Worker troubleshooting guidance. On macOS, `diagnose_runtime` may also return a coarse default-route class, `operating_system_interception` boolean, and privacy-bounded `runtime.idle_sleep_guard` state (`supported`, `enabled`, `active`, `grace_ms`, `last_error_class`). That diagnostic is returned on demand and is not promoted to default logs; interface names, IP addresses, DNS answers, proxy endpoints/credentials, Worker endpoints, tool arguments, and results remain absent. `relay.outage.active` and `relay.outage.recovered` carry the existing safe relay fields.
|
|
157
157
|
|
|
158
158
|
Schema 4 is strict NDJSON. Before daemon startup, both active log files are opened as owner-only regular single-link files. A schema change clears both only after validation and commits the marker only after the transition succeeds. A symlink, multiple-hard-link inode, permission error, or marker-write failure blocks startup rather than mixing formats or repeatedly erasing evidence.
|
|
159
159
|
|
package/docs/MANAGED_JOBS.md
CHANGED
|
@@ -234,6 +234,8 @@ read_job
|
|
|
234
234
|
cancel_job
|
|
235
235
|
```
|
|
236
236
|
|
|
237
|
+
In hosted remote use, successful `start_job` acceptance hands execution to durable background ownership; it is not a reason to keep the current assistant response open. Use `read_job` at most once as an active-job status checkpoint in that response, and use `list_jobs` as a one-shot inventory checkpoint rather than a substitute wait loop. If the returned job remains active/non-terminal, return its `job_id`, status, and current phase to the user and inspect it again only in a later user turn or on an explicit independent request. Local terminal inspection keeps its normal operator-driven behavior.
|
|
238
|
+
|
|
237
239
|
From the local terminal:
|
|
238
240
|
|
|
239
241
|
```sh
|
package/docs/MULTI_ACCOUNT.md
CHANGED
|
@@ -39,7 +39,7 @@ List accounts:
|
|
|
39
39
|
machine-mcp account list
|
|
40
40
|
```
|
|
41
41
|
|
|
42
|
-
Create an account. The generated password is displayed once:
|
|
42
|
+
Create an account. The generated password is displayed once. Treat this stdout as a secret; JSON/automation output that contains the one-time password must not be copied to shared logs or support artifacts:
|
|
43
43
|
|
|
44
44
|
```sh
|
|
45
45
|
machine-mcp account add alice reviewer
|
package/docs/OPERATIONS.md
CHANGED
|
@@ -87,11 +87,11 @@ After the host path recovers, compare authenticated `server_info`, `machine-mcp
|
|
|
87
87
|
|
|
88
88
|
### Relay interruption messages
|
|
89
89
|
|
|
90
|
-
A reconnect warning proves a transport interruption, not a daemon crash. Compare daemon PID and process start time with `connected_at`, `last_seen_at`, `daemon.relay_transport.last_close_category`, `last_close_code`, `outage_count`, `outage_attempts`, `previous_ready_inbound_silence_ms`, and the coarse network-route class. While no daemon channel is ready, `server_info.daemon.previous_connection` retains only the last verified channel's transport, connected/last-seen/disconnected timestamps, and sanitized relay diagnostics; it excludes policy, tools, account identity, daemon instance/connection identity, call IDs, arguments, and results, and it never participates in routing or authorization. `outage_duration_ms` measures the close-to-ready recovery episode; `previous_ready_inbound_silence_ms` measures how long the preceding ready socket had stopped producing inbound transport proof before it actually closed. The second value is therefore the field that exposes a black-holed OPEN WebSocket whose visible reconnect later completes quickly. A VPN/TUN UI may remain “connected” while its upstream route is unusable. Local OS logs can be compared with the exact `last_disconnected_at` timestamp, but an interface-quality change or tunnel-process correlation is not by itself proof of which product, node, edge, or upstream failed. Machine Bridge reports only coarse route/proxy classes and never sends or logs interface names, addresses, DNS answers, proxy credentials, or Worker secrets.
|
|
90
|
+
A reconnect warning proves a transport interruption, not a daemon crash. Compare daemon PID and process start time with `connected_at`, `last_seen_at`, `daemon.relay_transport.last_close_category`, `last_close_code`, `outage_count`, `outage_attempts`, `previous_ready_inbound_silence_ms`, `last_connect_milestones_ms`, and the coarse network-route class. `last_connect_milestones_ms` contains only bounded relative timings for the most recent connection attempt phases such as DNS resolution, TCP connect, TLS establishment, HTTP rejection, and WebSocket open; `last_failed_connect_stage`, `last_failed_connect_duration_ms`, `last_failed_connect_milestones_ms`, and `last_failed_connect_http_status` retain the most recent failed attempt even after a later retry succeeds. `last_transport_error_ready` and `last_transport_error_authenticated` distinguish failure of an already-established channel from a pre-readiness connection failure. `last_transport_error_reason` is a strict privacy-safe allowlist (`connection_reset`, `connection_timeout`, `network_unreachable`, bounded DNS/TLS classes, or `unknown`) rather than the raw operating-system message. The signed HTTPS fallback retains its last error class/reason after a later successful poll while resetting the current `http_poll_failures` count, so post-recovery diagnosis can determine whether WSS and HTTPS failed through the same system-network episode. None of these fields contains a hostname, address, DNS answer, certificate, close reason, or proxy endpoint. While no daemon channel is ready, `server_info.daemon.previous_connection` retains only the last verified channel's transport, connected/last-seen/disconnected timestamps, and sanitized relay diagnostics; it excludes policy, tools, account identity, daemon instance/connection identity, call IDs, arguments, and results, and it never participates in routing or authorization. `outage_duration_ms` measures the close-to-ready recovery episode; `previous_ready_inbound_silence_ms` measures how long the preceding ready socket had stopped producing inbound transport proof before it actually closed. The second value is therefore the field that exposes a black-holed OPEN WebSocket whose visible reconnect later completes quickly. A VPN/TUN UI may remain “connected” while its upstream route is unusable. Local OS logs can be compared with the exact `last_disconnected_at` timestamp, but an interface-quality change or tunnel-process correlation is not by itself proof of which product, node, edge, or upstream failed. Machine Bridge reports only coarse route/proxy classes and never sends or logs interface names, addresses, DNS answers, proxy credentials, or Worker secrets.
|
|
91
91
|
|
|
92
|
-
Brief retryable outages recover automatically. On a verified current daemon channel, `server_info.daemon.relay_transport.outage_active=false`; retained fields describe the immediately preceding transport episode rather than claiming a current outage. WebSocket remains preferred and
|
|
92
|
+
Brief retryable outages recover automatically. On a verified current daemon channel, `server_info.daemon.relay_transport.outage_active=false`; retained fields describe the immediately preceding transport episode rather than claiming a current outage. WebSocket remains preferred and requests a protocol-level probe after five seconds. Calling `ws.ping()` only queues the control frame; it is not treated as remote-probe dispatch until the WebSocket sender's write callback confirms that the Ping actually left the local send queue. The local sender has a separate thirty-second bounded dispatch window, while a confirmed Ping retains its full ten-second Pong deadline. This deliberately prevents compression/backpressure or a slow local socket queue from spending the remote-response budget before any probe was transmitted. A protocol Pong that arrives while a Ping callback is still pending records bidirectional proof for that dispatch round; if the local write callback then completes inside the thirty-second dispatch budget, it does not arm a stale future Pong deadline. Unrelated application inbound is receive-side evidence only and cannot prove the daemon-to-Worker direction. A local queue whose Ping write callback still has not completed after thirty seconds is classified as `relay_transport_send_timeout` even if unrelated inbound traffic continues. One dispatched Ping that reaches the ten-second response deadline no longer hard-kills an otherwise ready WSS: beta.104 enters a fifteen-second `transport_confirmation_pending` window, sends the existing JSON application heartbeat as an independent path check, and prewarms signed HTTPS in standby without taking ownership away from the still-ready WSS. A protocol Pong or the explicit JSON application `pong` clears suspicion and stops standby prewarm; ordinary tool/control traffic does not, because it proves only the Worker-to-daemon receive direction. Only a second-stage confirmation window that receives no application `pong` becomes `relay_transport_timeout` and terminates the WSS. This keeps a true black hole bounded while no longer amplifying a roughly ten-to-fifteen-second persistent-flow stall into an immediate reconnect storm. `heartbeat.probe_dispatch_*`, `heartbeat.transport_confirmation_*`, and bounded sender-backlog fields distinguish local send delay, first-stage response loss, successful second-stage recovery, and confirmed two-stage failure. The separate periodic JSON application heartbeat remains twenty-five seconds with a seventy-five-second application-silence timeout, begins only after verified relay readiness, and the Worker keeps a wider ninety-second WebSocket liveness fallback. This is a detection/recovery bound, not a guarantee that a degraded network can complete another WebSocket handshake inside the same interval. WebSocket connect attempts now have a thirty-second outer budget: live beta.103 evidence included successful upgrades taking roughly 13.8–14.4 seconds, too close to the previous fifteen-second cutoff to justify treating that cutoff as a reliable network-failure discriminator. The daemon also explicitly disables client `permessage-deflate`: the relay carries bounded control/JSON traffic, while `ws` enables compression by default on clients and compression adds sender-state/CPU overhead that can queue later frames; the stability path does not need that optional negotiation. The fallback still begins independently rather than waiting thirty seconds for WSS. On first-stage WSS liveness suspicion, the same root-certified ephemeral daemon identity prewarms signed HTTPS in standby; if WSS proves live during the second-stage confirmation, that standby poller stops. If the WSS actually disconnects, fallback switches to exact-generation takeover immediately; an in-flight standby request is aborted and replaced rather than being allowed to consume up to its own request deadline before takeover can start. That in-memory session certificate intentionally has a 24-hour maximum lifetime. It is valid for ordinary reconnects during that lifetime, but it is not silently extended: if a later WSS reconnect/authentication attempt discovers that the daemon session has expired, beta.104 terminates the runtime with `relay_device_session_expired` instead of retrying forever with unusable credentials. Installed launchd/systemd/Windows service supervision restarts the failed daemon and obtains a fresh root-signed session after normal runtime cleanup; the default portable root does this without user interaction. A manually run daemon must be restarted by its operator, and a configured Secure Enclave root retains its existing user-presence requirement when the new daemon start signs the replacement session. Each fallback request has a seven-second deadline, ordinary one-second ready poll cadence, five-second standby-prewarm cadence, bounded one/two/four/five-second retry backoff after request or protocol/session failures, a 750 ms hard minimum request-start interval, and a twelve-second liveness window; a new daemon-backed call waits at most fifteen seconds for some verified daemon channel, and the measured wait is deducted from that call's original execution budget. After an established WSS disappears, the daemon explicitly marks its signed HTTP request as a takeover of the Worker-issued `connection_id` for that exact disconnected WebSocket generation. Once candidate preconditions pass, HTTPS may retire only that targeted same-instance zombie WSS that the Worker has not yet observed closing. If a newer same-instance WSS is already ready before the HTTP request arrives, the old generation no longer matches and the stale takeover remains standby instead of retiring the recovered socket. During a Worker-first beta.103→beta.104 rolling window, a legacy beta.103 fallback request that has `takeover_websocket=true` but no generation id is still parsed for compatibility, but it is not allowed to retire any ready WebSocket; it remains standby until no verified WSS is incumbent. Malformed, stale, wrongly targeted, or different-instance requests cannot preempt a healthy incumbent. During replacement, the daemon reconciles `resume_calls`, processes `ready_ack`, proves local readiness, and only then returns `resume_calls_ack.missing_ids`. A missing ID therefore proves both that the same daemon has no active/unacknowledged-result ownership for that call and that the replacement channel is ready. If the initiating MCP response is still open and at least one second remains in the original execution budget, the Worker transparently retransmits exactly that same call ID, arguments, authority, and a reduced timeout. If that safe redelivery cannot be accepted, the call falls back to retryable `unavailable` with `side_effects_started=false`. Calls that may have executed, retained terminal results, different-daemon calls, and ambiguous mutations are never automatically replayed. Compare outage intervals with sleep/wake records and `diagnose_runtime.runtime.relay.heartbeat` before classifying them as active network faults; local stdio `server_info.runtime.relay.heartbeat` exposes the same state. On macOS, an authorized, schema-valid remote tool call also enters a bounded idle-sleep guard with a fixed thirty-minute rolling inactivity grace; the service does not depend on shell-only environment overrides that launchd would not persist as configuration, and relay heartbeats do not count as user activity. Authorized relay handlers hold one shared `/usr/bin/caffeinate -i -w <daemon-pid>` assertion for their full execution lifetime; concurrent handlers share the child, the thirty-minute default inactivity grace begins only after the last one settles, and a new authorized handler cancels any pending release timer so the full grace restarts after that activity settles. A remote `start_process` extends the same assertion only after resource admission succeeds and keeps it until the session child settles, so a long process session is not reduced to the handler grace window. Remote account managed-job runners independently hold `/usr/bin/caffeinate -i -w <runner-pid>` after their ownership claim is confirmed and persisted ownership identifies an account-backed job, then retain it through admission, steps, cleanup, and terminal persistence; local managed jobs do not acquire this remote-continuity assertion, and daemon reconnect/replacement does not own the remote runner protection. `diagnose_runtime.runtime.idle_sleep_guard` reports only daemon-side supported/enabled/active/grace/error-class state; it intentionally does not enumerate process-session or job identities. Runtime shutdown terminates process sessions before releasing the daemon guard. None of these assertions claim to prevent explicit sleep or lid-close sleep. A nonzero `event_loop_stall_count` with a large `max_event_loop_lag_ms` means the local daemon was not scheduled promptly; a short stall enters recovery grace, sends a fresh transport probe, and deliberately postpones disconnect. A large stall that aligns with `pmset` Sleep/Wake is suspension evidence, while a large `previous_ready_inbound_silence_ms` without a matching local stall is stronger evidence of a half-open/network-path interruption before close. Use `--verbose` only when close codes, liveness deadlines, and retry delays are required.
|
|
93
93
|
|
|
94
|
-
A foreground MCP response is not durable delivery. Hosted synchronous calls reserve room for Worker and host settlement instead of occupying the complete interaction window: ordinary daemon-backed tools default to 20 seconds of remote execution plus a separate five-second Worker settlement margin; ordinary configurable browser/application foreground tools also default to 20 seconds, while compound `computer_observe` and `computer_act` default to 30 seconds; all configurable browser/application foreground tools retain their explicit 45-second maximum. Remote `exec_command`, `run_process`, and `run_local_command` no longer keep the child process inside that response lifetime. Each remote process request must carry a unique caller-held `idempotency_key` before dispatch; reuse that same key only when recovering an ambiguous acceptance response. The daemon commits the authorized operation as a principal-bound one-step managed job, launches it with interactive resource-admission priority, and returns a `job_id` plus `read_job` recovery metadata inside a 10-second acceptance budget; the Worker keeps a separate five-second settlement margin. If that acceptance response is lost to settlement timeout, HTTP response cancellation, or relay reconnect expiry after dispatch, the public error remains non-retryable for generic callers but carries the original key and the explicit recovery action `retry_same_tool_arguments_with_same_idempotency_key`; this reconciles against the retained job instead of authorizing a blind duplicate. The detached child may execute for up to 600 seconds after admission, but the managed runner can separately wait up to thirty minutes for cooperative machine-user resource admission before the child is spawned; the child execution deadline begins only after that admission succeeds. The shared ceiling is exposed machine-readably as `server_info.tool_delivery.managed_job_resource_admission_wait_max_ms`, because the same pre-spawn boundary applies to ordinary durable process jobs and owner `start_job` steps rather than to process tools alone. While the runner is in this pre-spawn state, `read_job.current_phase` is `resource_admission`; no command has started yet. An owner can correlate a long-running status at that phase with `diagnose_runtime.runtime.resource_admission` rather than interpreting it as a slow child process; a delegated non-owner should treat the phase itself as evidence that the child has not spawned, retain the same `job_id`, and avoid blind replay rather than attempting the owner-only machine-wide diagnostic. After admission, the phase returns to `steps`, `finally_steps`, or `recovery-cleanup` as appropriate. Completed step records preserve `duration_ms` as the total orchestration duration. Local/owner reads additionally expose `resource_admission_ms` as the pre-spawn portion so a delayed successful child can be distinguished from slow execution after the fact; delegated non-owner reads omit that machine-user scheduling timing rather than turning shared-host contention into a more precise cross-workload signal. The detached job survives MCP disconnect, relay reconnect, daemon restart, or service replacement. Non-owner process authority is unchanged: automatic durable execution still uses the delegated workspace sandbox and does not grant owner-only `start_job`. If a cached host schema omits the now-required key, the Worker rejects before daemon dispatch with a normal no-side-effect tool error and requests a `tools/list` refresh rather than surfacing a protocol-only validation failure. `start_process` remains the explicit daemon-lifetime path when interactive stdin or session-style incremental output is required, but hosted calls use a 10-second execution / 15-second settlement envelope and do not queue behind resource pressure: the first failed admission returns retryable `unavailable`; owner-local callers retain the cooperative wait.
|
|
94
|
+
A foreground MCP response is not durable delivery. Hosted synchronous calls reserve room for Worker and host settlement instead of occupying the complete interaction window: ordinary daemon-backed tools default to 20 seconds of remote execution plus a separate five-second Worker settlement margin; ordinary configurable browser/application foreground tools also default to 20 seconds, while compound `computer_observe` and `computer_act` default to 30 seconds; all configurable browser/application foreground tools retain their explicit 45-second maximum. Remote `exec_command`, `run_process`, and `run_local_command` no longer keep the child process inside that response lifetime. Each remote process request must carry a unique caller-held `idempotency_key` before dispatch; reuse that same key only when recovering an ambiguous acceptance response. The daemon commits the authorized operation as a principal-bound one-step managed job, launches it with interactive resource-admission priority, and returns a `job_id` plus `read_job` recovery metadata inside a 10-second acceptance budget; the Worker keeps a separate five-second settlement margin. If that acceptance response is lost to settlement timeout, HTTP response cancellation, or relay reconnect expiry after dispatch, the public error remains non-retryable for generic callers but carries the original key and the explicit recovery action `retry_same_tool_arguments_with_same_idempotency_key`; this reconciles against the retained job instead of authorizing a blind duplicate. The detached child may execute for up to 600 seconds after admission, but the managed runner can separately wait up to thirty minutes for cooperative machine-user resource admission before the child is spawned; the child execution deadline begins only after that admission succeeds. The shared ceiling is exposed machine-readably as `server_info.tool_delivery.managed_job_resource_admission_wait_max_ms`, because the same pre-spawn boundary applies to ordinary durable process jobs and owner `start_job` steps rather than to process tools alone. While the runner is in this pre-spawn state, `read_job.current_phase` is `resource_admission`; no command has started yet. An owner can correlate a long-running status at that phase with `diagnose_runtime.runtime.resource_admission` rather than interpreting it as a slow child process; a delegated non-owner should treat the phase itself as evidence that the child has not spawned, retain the same `job_id`, and avoid blind replay rather than attempting the owner-only machine-wide diagnostic. After admission, the phase returns to `steps`, `finally_steps`, or `recovery-cleanup` as appropriate. Completed step records preserve `duration_ms` as the total orchestration duration. Local/owner reads additionally expose `resource_admission_ms` as the pre-spawn portion so a delayed successful child can be distinguished from slow execution after the fact; delegated non-owner reads omit that machine-user scheduling timing rather than turning shared-host contention into a more precise cross-workload signal. The detached job survives MCP disconnect, relay reconnect, daemon restart, or service replacement. Non-owner process authority is unchanged: automatic durable execution still uses the delegated workspace sandbox and does not grant owner-only `start_job`. If a cached host schema omits the now-required key, the Worker rejects before daemon dispatch with a normal no-side-effect tool error and requests a `tools/list` refresh rather than surfacing a protocol-only validation failure. `start_process` remains the explicit daemon-lifetime path when interactive stdin or session-style incremental output is required, but hosted calls use a 10-second execution / 15-second settlement envelope and do not queue behind resource pressure: the first failed admission returns retryable `unavailable`; owner-local callers retain the cooperative wait. Hosted `read_process` is a live-session status checkpoint: a blocking wait lasts at most one second, and one checkpoint per assistant response is the hosted boundary. If it returns `running=true`, the caller returns the current session/progress instead of polling again even when new output was returned; another would-block remote wait inside the fifteen-second blocking cooldown returns immediately. The result names that distinction explicitly with `status_polling_mode=checkpoint`, `blocking_poll_throttled`, and `next_blocking_poll_after_ms`. A new hosted call waits at most fifteen seconds for daemon readiness, but that wait is charged against the call's existing execution budget; an in-flight disconnect likewise never pauses or extends the original absolute deadline. Pending-call reconnect retention is also bounded by the smaller of reconnect grace and that original remaining deadline, and diagnostics distinguish `original call deadline expired during reconnect` from a true full `reconnect grace expired` rather than labeling both cases as the latter. Owner-local stdio/CLI calls retain their synchronous local contract because they do not depend on a hosted response stream. Keep unrelated mutations and verification independently terminal, and never infer task success merely because a durable launch was accepted. Treat `read_job` as a checkpoint: if one hosted read still reports an active job, return its `job_id`, status, and current phase and stop polling until a later user turn or explicit request; only a terminal status is task-completion evidence. `list_jobs` is a one-shot inventory checkpoint under the same hosted-turn rule; its relay result recommends handoff when the returned visible set contains active work, and repeated listings must not be used to wait for completion. The same principle applies to read-only status/diagnostic surfaces such as `server_info` and `diagnose_runtime`: capture current evidence once rather than substituting another status tool as a wait loop for the same background state.
|
|
95
95
|
|
|
96
96
|
The daemon honors `HTTPS_PROXY`/`HTTP_PROXY` and `NO_PROXY` through standard environment-proxy resolution for remote Worker health and relay traffic. `wss:` targets use HTTPS proxy selection and `ws:` targets use HTTP proxy selection. Only HTTP and HTTPS proxy URLs are accepted. Invalid URLs or unsupported protocols fail startup with corrective guidance instead of entering the reconnect loop. Remote-owner `diagnose_runtime.runtime.relay.network_route` reports this value, while local stdio `server_info.runtime.relay.network_route` reports `system-network-stack`, `application-http-proxy`, or `invalid-application-proxy-configuration`. This field describes only Machine Bridge application-level proxy selection: an operating-system VPN/TUN may still intercept `system-network-stack` traffic. `network_route_scope`, outage timestamps/durations, close category/code, transport error class, and next retry timing make that distinction explicit; proxy endpoints and credentials are never returned or logged. The browser-broker CLI health probe is a separate loopback-only path: it accepts only canonical `127.0.0.1`, uses direct Node HTTP with no proxy agent, and does not depend on `NO_PROXY`.
|
|
97
97
|
|
|
@@ -342,9 +342,9 @@ Defense-in-depth limits include:
|
|
|
342
342
|
- process stdin write: 64 KiB per call;
|
|
343
343
|
- local simultaneous tool calls: 16 total, with 14 ordinary slots and two reserved for bounded control-plane diagnosis/recovery;
|
|
344
344
|
- Worker pending daemon calls: 32 total, with 30 ordinary slots and two reserved for bounded control-plane diagnosis/recovery;
|
|
345
|
-
- synchronous remote timeout schema: ordinary daemon-backed tools default to 20 seconds, ordinary configurable browser/application operations default to 20 seconds, compound `computer_observe`/`computer_act` default to 30 seconds, all configurable browser/application operations retain their 45-second explicit maximum, and remote `exec_command`, `run_process`, and `run_local_command` use a 10-second durable-acceptance window while their detached execution timeout may be 1–600 seconds. The Worker still uses a separate five-second settlement margin. Hosted `start_process` has a 10-second execution / 15-second settlement envelope and performs resource admission without queueing;
|
|
346
|
-
- tool-schema rollout is backward-compatible for lowered maximums: if a host has cached an older `tools/list` definition that still permits a configurable foreground timeout above 45 seconds or `read_process.wait_ms` above
|
|
347
|
-
- process-session read wait: local calls may wait at most 30 seconds; hosted remote projection caps each wait at
|
|
345
|
+
- synchronous remote timeout schema: ordinary daemon-backed tools default to 20 seconds, ordinary configurable browser/application operations default to 20 seconds, compound `computer_observe`/`computer_act` default to 30 seconds, all configurable browser/application operations retain their 45-second explicit maximum, and remote `exec_command`, `run_process`, and `run_local_command` use a 10-second durable-acceptance window while their detached execution timeout may be 1–600 seconds. The Worker still uses a separate five-second settlement margin. Hosted `start_process` has a 10-second execution / 15-second settlement envelope and performs resource admission without queueing; process sessions are reserved for interactive stdin or incremental output, and one live-session `read_process` checkpoint per assistant response must hand any `running=true` session back instead of polling to completion, regardless of whether that checkpoint returned output. Use durable process tools or `start_job` for background work that must survive an MCP disconnect, daemon restart, or service replacement;
|
|
346
|
+
- tool-schema rollout is backward-compatible for lowered maximums: if a host has cached an older `tools/list` definition that still permits a configurable foreground timeout above 45 seconds or `read_process.wait_ms` above 1000, the Worker rejects that value before daemon dispatch as an MCP tool error (`invalid_request`, no side effects, schema refresh recommended) rather than a JSON-RPC protocol error. A stale `read_process` rejection explicitly directs the caller to one live-session checkpoint plus handoff/durable routing rather than repeated short polling, including the case where new output is available. Durable process execution is a separate 1–600-second contract and is not subject to the removed 30-second foreground ceiling. Genuinely malformed argument types/fields and unrelated schema violations remain protocol errors;
|
|
347
|
+
- process-session read wait: local calls may wait at most 30 seconds; hosted remote projection caps each blocking wait at 1 second, marks every relay-origin read as `status_polling_mode=checkpoint`, and a still-running session that consumed one remote blocking wait enters a 15-second would-block cooldown in which another blocking request returns immediate status. `blocking_poll_throttled` and `next_blocking_poll_after_ms` describe only that blocking-wait cooldown; the process and retained output session are not shortened by those hosted-turn limits;
|
|
348
348
|
- direct directory result: 10,000 entries and 4 MiB of path metadata;
|
|
349
349
|
- recursive walk: 200,000 visited entries;
|
|
350
350
|
- managed jobs: 50 retained; terminal results normally up to seven days with oldest-terminal capacity eviction, staged drafts 24 hours;
|
|
@@ -359,7 +359,7 @@ Defense-in-depth limits include:
|
|
|
359
359
|
|
|
360
360
|
The list above describes bounded application resources, not OS quotas. CPU time shares, resident-memory ceilings, syscall sandboxes, and egress policy must be imposed by the account/container/VM that runs Machine Bridge. A remote owner can check `diagnose_runtime.runtime.execution_guardrails.operating_system_enforcement`; local stdio exposes the same contract at `server_info.runtime.execution_guardrails.operating_system_enforcement`. Non-owner remote accounts do not receive machine-wide diagnostics. Current in-process values are intentionally `not-enforced` rather than inferred from timeouts or output limits.
|
|
361
361
|
|
|
362
|
-
Machine Bridge additionally uses cooperative machine-user resource admission for process roots. This does not change the OS-enforcement statement above. For a remote owner, `diagnose_runtime.runtime.resource_admission` reports the current Green/Yellow/Red pressure state, aggregate CPU/I/O/memory/disk reservations, waiter counts, the number of aged protected waiters, and `waiters.drain_active`; it does not expose command text or project paths. `pressure.state=green` means the sampled host/reservation pressure is within limits, not that fairness can admit every queued root immediately: `drain_active=true` means an aged feasible protected waiter currently outranks fitting backfill and is intentionally reserving a drain window until blocking coordinator-owned reservations clear. Non-owner accounts do not receive this machine-wide pressure/activity projection. Interactive MCP execution waits at most about two seconds for heavy/adaptive capacity and then returns a retryable `unavailable` error with `reason=resource_admission`; detached managed jobs wait inside their runner instead of occupying an MCP call slot. Light control/read operations remain available. Waiters use priority-aware bounded exponential backoff with jitter rather than synchronized fixed-cadence polling; an explicit lease release, stale-lease prune, owned waiter removal after cancellation/timeout, or stale/expired-waiter prune inside the same daemon interrupts that timer immediately so local capacity or fairness changes are reevaluated without waiting for the next poll, while cross-process changes retain the bounded retry path. Stale waiter removal revalidates ownership and filesystem identity and fails closed if the record changes before deletion. Host probes are single-flight per anonymous project scope inside one daemon; CPU/mixed/adaptive work uses the quick pressure path, while fresh blocking I/O probes are reserved for I/O-dominant or unbounded work. Quick host evidence is reused wholesale for at most 500ms so a completed CPU burst cannot consume most of the interactive admission-wait budget. CPU cumulative counters are machine-global, so a recent anchor may be reused across project scopes for at most two seconds to avoid a needless cold sample; older anchors force a fresh 50ms CPU window so a long idle interval cannot smear current pressure into a stale average. Disk headroom and cached I/O evidence remain project/filesystem scoped and never cross that boundary. Process ancestry is sampled only when a lease exists and nested-envelope accounting can use it.
|
|
362
|
+
Machine Bridge additionally uses cooperative machine-user resource admission for process roots. This does not change the OS-enforcement statement above. For a remote owner, `diagnose_runtime.runtime.resource_admission` normally reports the current Green/Yellow/Red pressure state, aggregate CPU/I/O/memory/disk reservations, waiter counts, the number of aged protected waiters, and `waiters.drain_active`; it does not expose command text or project paths. If the bounded diagnostic cannot acquire the coordinator transaction/staging lock while other processes are actively changing it, the projection instead reports `healthy=false`, `snapshot_available=false`, retryable `error_class=unavailable`, and `reason=coordinator_busy`; this is an unavailable snapshot under contention, not a generic execution failure or evidence that the coordinator state is corrupt. `pressure.state=green` means the sampled host/reservation pressure is within limits, not that fairness can admit every queued root immediately: `drain_active=true` means an aged feasible protected waiter currently outranks fitting backfill and is intentionally reserving a drain window until blocking coordinator-owned reservations clear. Non-owner accounts do not receive this machine-wide pressure/activity projection. Interactive MCP execution waits at most about two seconds for heavy/adaptive capacity and then returns a retryable `unavailable` error with `reason=resource_admission`; detached managed jobs wait inside their runner instead of occupying an MCP call slot. Light control/read operations remain available. Waiters use priority-aware bounded exponential backoff with jitter rather than synchronized fixed-cadence polling; an explicit lease release, stale-lease prune, owned waiter removal after cancellation/timeout, or stale/expired-waiter prune inside the same daemon interrupts that timer immediately so local capacity or fairness changes are reevaluated without waiting for the next poll, while cross-process changes retain the bounded retry path. Stale waiter removal revalidates ownership and filesystem identity and fails closed if the record changes before deletion. Host probes are single-flight per anonymous project scope inside one daemon; CPU/mixed/adaptive work uses the quick pressure path, while fresh blocking I/O probes are reserved for I/O-dominant or unbounded work. Quick host evidence is reused wholesale for at most 500ms so a completed CPU burst cannot consume most of the interactive admission-wait budget. CPU cumulative counters are machine-global, so a recent anchor may be reused across project scopes for at most two seconds to avoid a needless cold sample; older anchors force a fresh 50ms CPU window so a long idle interval cannot smear current pressure into a stale average. Disk headroom and cached I/O evidence remain project/filesystem scoped and never cross that boundary. Process ancestry is sampled only when a lease exists and nested-envelope accounting can use it.
|
|
363
363
|
|
|
364
364
|
Backfill remains work-conserving until an aged rank-zero request is both feasible in principle and blocked by current coordinator-owned project/CPU/I/O/memory/disk reservations; only that case stops admitting later fitting work long enough for capacity to drain. Nested heavy roots are accounted as a live process-ancestry forest rather than as an unconditional sum: an orchestration parent is an envelope, direct lease children may consume that envelope, and only excess child demand increases the reservation. Each child still owns a complete durable lease, so if the parent disappears while a detached child survives, the child's full request becomes independently chargeable on the next accounting pass. A nested request may ignore same-key contention only from its actual ancestor lease chain; same-key siblings still serialize. If process ancestry cannot be sampled, the coordinator intentionally falls back to full summation. CPU admission combines measured busy cores with only reservation that started after the measurement or is still provisional; reservation already visible in the host sample is not charged twice. When admission policy expands a request for safety—for example, mapping unknown/unbounded CPU fan-out to the current CPU admission limit—the same full reservation is materialized into the durable lease rather than reverting to the smaller profile hint after admission, so subsequent roots and surviving nested children see the capacity that was actually approved. Yellow pressure is dimension-scoped rather than a global tax: CPU busy/CPU PSI tightens CPU limits, disk throughput/I/O PSI/soft disk headroom tightens I/O limits, and memory/pageout/swap/memory PSI tightens memory limits. `heavy_root_count` remains visible as advisory density telemetry but does not, by itself, reduce unrelated resource limits; actual reservations and host bottleneck signals remain authoritative. On macOS, load average is treated as runnable-thread backlog and does not tighten admission without corroborating CPU/I/O pressure. Disk headroom uses proportional floors with bounded absolute caps rather than fixed minimum free-space requirements, so small volumes are not permanently classified as pressured while large volumes still retain a bounded safety reserve. On Linux, `MemAvailable` and optional PSI avg10 values supplement reservations: missing PSI stays unknown, 10% recent stalls tighten admission, and severe sustained memory-full pressure stops new heavy roots. These are scheduling signals, not cgroup limits or kill policy.
|
|
365
365
|
|
package/docs/PRIVACY.md
CHANGED
|
@@ -34,7 +34,9 @@ Add one identifier per line. The denylist is deliberately local and must never b
|
|
|
34
34
|
|
|
35
35
|
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`.
|
|
36
36
|
|
|
37
|
-
Ignored does not mean safe for secrets: do not store passwords, tokens, private keys, authorization URLs, or copied secret-bearing logs there. Treat live Worker endpoints, absolute home paths, downloaded toolchains, browser captures, and other machine identifiers as retention-sensitive metadata even when they are not credentials. Prune reconstructible dependency/toolchain experiments, stale live-endpoint probes, obsolete candidate logs, and superseded workflow snapshots once their reusable conclusions have moved into tracked documentation; retain only the minimum one-machine evidence needed for an active release/recovery investigation. `.privacy-denylist` remains the dedicated local vocabulary gate.
|
|
37
|
+
Ignored does not mean safe for secrets: do not store passwords, tokens, private keys, authorization URLs, or copied secret-bearing logs there. Treat live Worker endpoints, absolute home paths, downloaded toolchains, browser captures, and other machine identifiers as retention-sensitive metadata even when they are not credentials. Wrangler-generated `.wrangler/` files are ignored and excluded from the npm publication surface, but generated comments may preserve the command argument used to create them; the repository generator therefore passes a working-directory-relative output path and rejects targets outside that working directory. Delete stale ignored generated artifacts before sharing or archiving a complete workspace rather than assuming `.gitignore` removes their metadata. Likewise, do not include the repository `.git/` directory in support/workspace archives: linked-worktree administration such as `.git/worktrees/*/gitdir` can legitimately contain absolute local filesystem paths even when tracked source and the npm package are clean. Prune reconstructible dependency/toolchain experiments, stale live-endpoint probes, obsolete candidate logs, and superseded workflow snapshots once their reusable conclusions have moved into tracked documentation; retain only the minimum one-machine evidence needed for an active release/recovery investigation. `.privacy-denylist` remains the dedicated local vocabulary gate.
|
|
38
|
+
|
|
39
|
+
Account creation and the first interactive non-`--daemon-only` startup may intentionally print a newly generated account password exactly once; JSON mode includes that one-time credential because it is provisioning output, not a diagnostic projection. Treat that stdout/JSON as secret material: do not pipe it to shared CI logs, support bundles, shell transcripts, or issue attachments. Background `--daemon-only` service startup does not provision or print an initial owner password, and operational logs/audit records do not copy account passwords.
|
|
38
40
|
|
|
39
41
|
`machine-mcp doctor` is the support-report path and projects bounded configuration facts instead of the complete local state. Successful Worker health is reported as reachability, while endpoint/name, workspace identity, device key material and stable key identifiers, resource paths, and similar local correlation values are omitted from its state summary. `machine-mcp status` is an owner-local diagnostic and intentionally remains more detailed. Review support output before publication because operating-system/version context and failure timing can still be identifying metadata.
|
|
40
42
|
|
|
@@ -95,4 +97,4 @@ MCP `2026-07-28` response streams are request-scoped, not persisted, and not res
|
|
|
95
97
|
|
|
96
98
|
During a brief relay interruption, the same local daemon process may retain an already-completed result envelope in memory until the Worker acknowledges it or the bounded reconnect window expires. This is relay-delivery continuity for an already-dispatched call, not a client-visible protocol session or replay store. Tool arguments and results remain absent from normal operational logs, and durable work that must survive process or machine replacement uses the managed-job subsystem instead.
|
|
97
99
|
|
|
98
|
-
The signed daemon HTTPS fallback does not change those persistence rules. It carries the same daemon control/tool envelopes over TLS when WSS is unavailable and retains only bounded in-memory transport queues on each side. An unacknowledged envelope may be retransmitted with the same transport sequence after response loss, but duplicate sequences are discarded before business handling; no public MCP replay identifier or persisted fallback result store is created. Fallback authentication headers contain a short-lived root-certified session certificate, one-time nonce, timestamp, body hash, and signature; they are validated but not written to operational logs. Browser broker ping/Pong carries only a bounded sequence number and is not content logging.
|
|
100
|
+
The signed daemon HTTPS fallback does not change those persistence rules. It carries the same daemon control/tool envelopes over TLS when WSS is unavailable and retains only bounded in-memory transport queues on each side. An unacknowledged envelope may be retransmitted with the same transport sequence after response loss, but duplicate sequences are discarded before business handling; no public MCP replay identifier or persisted fallback result store is created. Fallback authentication headers contain a short-lived root-certified session certificate, one-time nonce, timestamp, body hash, and signature; they are validated but not written to operational logs. Relay diagnosis retains only bounded numeric connect/probe timing, sender-backlog byte counts, and strict allowlisted transport reasons such as `connection_timeout` or `network_unreachable`; it never stores the raw socket error, hostname, address, DNS answer, certificate, VPN interface/node, proxy endpoint, arguments, or results. The fallback may retain its last coarse error class/reason after recovery so a later operator can correlate WSS and HTTPS failure windows, while current health remains represented separately by readiness and the current failure count. Browser broker ping/Pong carries only a bounded sequence number and is not content logging.
|