Skip to content

Add the Windows Sandbox state-aware lifecycle + host daemon (Phase 2)#578

Open
MGudgin wants to merge 2 commits into
user/gudge/wsb-phase1-one-shotfrom
user/gudge/wsb-phase2-state-aware
Open

Add the Windows Sandbox state-aware lifecycle + host daemon (Phase 2)#578
MGudgin wants to merge 2 commits into
user/gudge/wsb-phase1-one-shotfrom
user/gudge/wsb-phase2-state-aware

Conversation

@MGudgin

@MGudgin MGudgin commented Jun 26, 2026

Copy link
Copy Markdown
Member

Note

Stacked on #577 (which is stacked on #576). This PR's base is
user/gudge/wsb-phase1-one-shot, so the diff shows only the Phase 2 commit.
Merge order: #576 -> #577 -> this PR. GitHub auto-retargets the base to main
as each parent merges and its branch is deleted.


Summary

Phase 2 of the Windows Sandbox rewrite stack: add the state-aware lifecycle
(provision -> start -> exec* -> stop -> deprovision) for Windows Sandbox on top
of the Phase 1 one-shot rewrite, holding a single live VM across separate
wxc-exec phase processes behind a persistent detached host-side daemon. The SDK
follows in Phase 3.

Details

  • windows_sandbox_lifecycle: re-enables state_aware.rs -- the
    StatefulSandboxBackend impl on WindowsSandboxRunner. provision validates +
    snapshots the filesystem policy and mints wsb:<token>; start spawns the
    detached daemon (which boots the VM and holds the guest control connection);
    exec relays a script over the held connection; stop/deprovision tear down.
    A process-global named mutex serialises start/stop/deprovision; the single host
    VM slot is enforced.
  • Daemon rewrite (wxc_windows_sandbox_daemon): control_server.rs serves a
    nonce-authenticated localhost line protocol (PING/STOP/EXEC), each
    connection on its own task so a long exec can't block a STOP. Auth nonce via
    stdin; sandbox token via --token. Re-added to the workspace members (dropped
    in Phase 1); the legacy pipe_server/sandbox_vm/tcp_bridge/rendezvous
    modules are deleted (rendezvous now lives in the lifecycle crate).
  • wxc dispatch: run_state_aware routes the wsb: prefix to
    WindowsSandboxRunner with the same --experimental gate as one-shot;
    state_aware_dispatch learns the wsb -> WindowsSandbox prefix mapping.
  • Docs: the full dual-mode rewrites land here with the behavior they describe --
    windows-sandbox.md/windows-sandbox-reference.md, schema.md (dual-mode
    backend row + state-aware envelope section), the state-aware API doc, and
    copilot-instructions.md. While touching schema.md I reconciled its version
    table to the canonical schemas/schema-version.json (>=0.4, <=0.8; stable
    0.7.0-alpha; state-aware 0.6.0-alpha) -- the rewrite had left it at the pre-0.8
    <=0.7.
  • Tests: adds run_windows_sandbox_state_aware_tests.ps1 (state-aware E2E).

Tests

  • cargo check --workspace --all-targets, cargo clippy --workspace --all-targets -- -D warnings, and cargo fmt --all -- --check: clean.
  • cargo test: windows_sandbox_lifecycle 157 (incl. the state-aware suite),
    wxc_windows_sandbox_daemon 23, wxc_common 364 (incl. the new wsb resolver
    test), wxc 13 -- all passed.
  • node scripts/versioning/check-schema-versions.js: green.

Related Issues

Microsoft Reviewers: Open in CodeFlow

@MGudgin MGudgin requested a review from a team as a code owner June 26, 2026 19:08
@MGudgin MGudgin force-pushed the user/gudge/wsb-phase2-state-aware branch from 91e0bab to 5d411b0 Compare June 26, 2026 19:26
@MGudgin MGudgin force-pushed the user/gudge/wsb-phase1-one-shot branch from 9d5d7e5 to e3ccb02 Compare July 7, 2026 04:04
@MGudgin MGudgin force-pushed the user/gudge/wsb-phase2-state-aware branch 2 times, most recently from 569651e to 1830960 Compare July 7, 2026 17:16
The wire spec and the SDK observably disagree on *which* error fires for an
unrecognised prefix, and this is by design:

| Source | Behaviour for an unrecognised `sandboxId` prefix |

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(Very small) NIT: One of your previous PR's you did not add a u to honor. Pick a spelling style and stick with it.

}

// WindowsSandbox holds a single active sandbox behind a persistent host-side
// daemon. It has no Entra/`user` bundle. Filesystem policy is honored at

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why back-ticks?

// IsolationSession returns no metadata for start, stop, deprovision
};
windows_sandbox?: Record<never, never>;
// ^ WindowsSandbox returns no metadata for any phase (keyof never -> undefined)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why the ^?

Comment thread docs/windows-sandbox/windows-sandbox.md Outdated

## State-aware lifecycle

In addition to the one-shot path above, Windows Sandbox supports the **state-aware lifecycle** — a multi-call `provision → start → exec* → stop → deprovision` flow that holds a single live VM across separate `wxc-exec` phase processes. This mirrors the cross-backend state-aware API (see [`docs/state-aware-lifecycle/mxc-state-aware-sandbox-api.md`](../state-aware-lifecycle/mxc-state-aware-sandbox-api.md)) and is the integrated, long-lived counterpart to the disposable one-shot runner.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does Windows Sandbox support any other flows besides the state-aware lifecycle?

/// The connection was lost or left in an indeterminate state by a failed
/// exec or reconnect. Every subsequent `EXEC` deterministically returns
/// `ERR <reason>`; `STOP`/teardown still work.
Poisoned(String),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider changing poisoned to a description not a violent. Maybe ConnectionLostOrIndeterminateState

Consideing the comments say or should this be split into two enums?

/// EXEC. The caller writes `OK\n` and proceeds.
Admit,
/// Another EXEC currently holds the slot mutex. The caller writes
/// `ERR busy\n` and returns. No state is mutated.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Future developers might not know to modify this comment if they change the behavior of the caller. Or if the state is changed. Consider not putting implementation details in comments so far away from the source code.

// Bounded-wait acquire: wait up to
// PREAUTH_PERMIT_WAIT for a permit so a transient
// make-j-style burst that briefly exceeds
// MAX_CONCURRENT_PREAUTH does not surface as a spurious

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

spurious learned a new word today. :)

@MGudgin MGudgin force-pushed the user/gudge/wsb-phase1-one-shot branch from 8e2cb9b to 22cfc41 Compare July 11, 2026 20:17
@MGudgin MGudgin force-pushed the user/gudge/wsb-phase2-state-aware branch from 1830960 to 9728f3d Compare July 11, 2026 20:17
@MGudgin MGudgin force-pushed the user/gudge/wsb-phase1-one-shot branch from 22cfc41 to d140ea9 Compare July 11, 2026 20:23
@MGudgin MGudgin force-pushed the user/gudge/wsb-phase2-state-aware branch from 9728f3d to e7957bc Compare July 11, 2026 20:23
@MGudgin MGudgin force-pushed the user/gudge/wsb-phase1-one-shot branch 2 times, most recently from daa4c4a to 153e927 Compare July 13, 2026 19:54
@MGudgin MGudgin force-pushed the user/gudge/wsb-phase1-one-shot branch from 5c49b06 to 11367a7 Compare July 13, 2026 20:49
@MGudgin MGudgin force-pushed the user/gudge/wsb-phase2-state-aware branch from e7957bc to e562263 Compare July 13, 2026 21:17
This PR rewrites the Windows Sandbox one-shot backend onto a fresh-VM lifecycle with an authenticated host-to-guest protocol and ownership-scoped teardown, and fixes non-elevated feature detection.

Details

* Adds `windows_sandbox_lifecycle` for policy validation, launch, rendezvous, execution, crash recovery, and guaranteed teardown of each disposable VM.
* Authenticates every TCP channel with a per-launch nonce and channel role, and hardens guest process-tree cleanup and firewall isolation.
* Routes the runner through `mxc_engine`, removes the host Python and warm-daemon dependencies, and warns only when non-default legacy daemon settings are ignored.
* Rejects an existing Tokio runtime before host mutation or stdin-thread creation, avoiding a blocking thread leak on the error path.
* Treats the state-aware daemon as optional in the Phase 1 SDK package; later stack phases build and require it.
* Updates one-shot tests and documentation for the supported filesystem/network policy, protocol, security model, and debugging workflow.

Tests

* `cargo fmt --all -- --check`
* `cargo check --workspace --all-targets`
* `cargo clippy --workspace --all-targets -- -D warnings`
* `cargo test --workspace --exclude wxc_host_prep`
* `cargo check -p mxc_engine -p wxc --all-features`
* `npm run build` (from `sdk`)

Fixes #560

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c03d0923-5577-4c56-b2df-a314428f5164
Generated-with: claude-opus-4.8
Generated-with: gpt-5.6-sol
@MGudgin MGudgin force-pushed the user/gudge/wsb-phase1-one-shot branch from 11367a7 to 501432b Compare July 13, 2026 21:25
This PR adds the multi-invocation Windows Sandbox lifecycle on top of the authenticated one-shot backend.

Details

* Implements provision, start, repeated single-flight exec, stop, and deprovision through `WindowsSandboxRunner` and durable owner-only records.
* Adds a detached host daemon that owns the live VM, authenticates localhost IPC, reconnects guest data channels, and reclaims only provably-owned orphans.
* Routes state-aware backend selection through `mxc_engine` with experimental gating, keeping `wxc-exec` as a thin CLI shell.
* Enforces terminal-exit ordering with a daemon-local release token that can be created only after restoring and unlocking the guest slot.
* Adds state-aware documentation and Windows Sandbox lifecycle E2E coverage.

Tests

* `cargo fmt --all -- --check`
* `cargo check --workspace --all-targets`
* `cargo clippy --workspace --all-targets -- -D warnings`
* `cargo test --workspace --exclude wxc_host_prep`
* `cargo check -p mxc_engine -p wxc --all-features`

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Copilot-Session: c03d0923-5577-4c56-b2df-a314428f5164
Generated-with: claude-opus-4.8
Generated-with: gpt-5.6-sol
@MGudgin MGudgin force-pushed the user/gudge/wsb-phase2-state-aware branch from e562263 to b29b708 Compare July 13, 2026 21:28
@MGudgin MGudgin force-pushed the user/gudge/wsb-phase1-one-shot branch from 501432b to 1124ea8 Compare July 14, 2026 17:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants