You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maka.apache.org by wy...@apache.org on 2026/10/23 06:36:05 UTC

(maka) 02/05: fix(sandbox): retain native Windows filesystem guards through tree settlement

This is an automated email from the ASF dual-hosted git repository.

M4n5ter pushed a commit to branch rust-preview
in repository https://gitbox.apache.org/repos/asf/maka.git

commit 9814e365b029e9183a1a256c8ddeb54e2ed801c2
Author: M4n5ter <m4...@gmail.com>
AuthorDate: Fri Oct 23 14:13:44 2026 +0800

    fix(sandbox): retain native Windows filesystem guards through tree settlement
    
    Protect missing read boundaries and writable ancestors with the existing recoverable guard registry. Bind the actual named execution Job before launch, keep every backend staging object inside the protected registry, and observe helper exit before cancelled admission settles.
    
    Native permission, network, pipe, ConPTY, background and cancellation probes passed. Final alias probes remain to be rerun after correcting the fixture to the path-policy contract.
---
 platform/host-runtime/examples/windows_mxc.rs      | 577 ++++++++++++++++++---
 .../host-runtime/src/sandbox/windows/guards.rs     | 215 +++++++-
 .../host-runtime/src/sandbox/windows/launch.rs     | 146 ++++--
 platform/host-runtime/src/sandbox/windows/store.rs |   2 +-
 platform/process/src/bootstrap.rs                  |   2 +-
 platform/process/src/bootstrap/backend.rs          |  16 +-
 platform/process/src/bootstrap/mxc.rs              | 177 ++++---
 platform/process/src/bootstrap/mxc/lease.rs        |  58 +++
 platform/sandbox/README.md                         |  10 +-
 platform/sandbox/README.zh-CN.md                   |  10 +-
 platform/sandbox/src/filesystem.rs                 |  51 ++
 platform/sandbox/src/windows/acl/target.rs         |  37 +-
 platform/sandbox/src/windows/mxc/availability.rs   |  27 +-
 platform/sandbox/src/windows/mxc/policy.rs         |   8 +-
 .../engine/src/workspace/permissions.rs            |   8 +-
 15 files changed, 1103 insertions(+), 241 deletions(-)

diff --git a/platform/host-runtime/examples/windows_mxc.rs b/platform/host-runtime/examples/windows_mxc.rs
index 6afa7ed0eb..b113eeffdf 100644
--- a/platform/host-runtime/examples/windows_mxc.rs
+++ b/platform/host-runtime/examples/windows_mxc.rs
@@ -33,6 +33,7 @@ fn main() -> native::Result<()> {
 
 #[cfg(windows)]
 mod native {
+    use futures_util::FutureExt;
     use maka_agent_facts::terminal::TerminalSize;
     use maka_host_runtime::sandbox::windows::{self, Backend, Installation, Status};
     use maka_process::{
@@ -45,7 +46,7 @@ mod native {
     use serde_json::json;
     use std::{
         io::{self, BufRead, Read, Write},
-        net::{Ipv4Addr, Ipv6Addr, SocketAddr, TcpStream},
+        net::{Ipv4Addr, Ipv6Addr, SocketAddr, TcpStream, ToSocketAddrs, UdpSocket},
         os::windows::io::{AsRawHandle, FromRawHandle, OwnedHandle},
         path::{Path, PathBuf},
         process::Stdio,
@@ -107,6 +108,70 @@ mod native {
         backend: Arc<Backend>,
     }
 
+    #[derive(Debug)]
+    struct MetadataProbe {
+        directory: bool,
+        metadata: io::Result<std::fs::Metadata>,
+        create: io::Result<()>,
+        write: io::Result<()>,
+        delete: io::Result<()>,
+        rename: io::Result<()>,
+    }
+
+    impl MetadataProbe {
+        fn capture(path: &Path, directory: bool) -> Self {
+            let metadata = std::fs::metadata(path);
+            let create = if directory {
+                std::fs::create_dir(path)
+            } else {
+                std::fs::OpenOptions::new()
+                    .create_new(true)
+                    .write(true)
+                    .open(path)
+                    .map(drop)
+            };
+            let write = std::fs::write(
+                if directory {
+                    path.join("probe")
+                } else {
+                    path.to_owned()
+                },
+                "metadata-escaped",
+            );
+            let delete = if directory {
+                std::fs::remove_dir(path)
+            } else {
+                std::fs::remove_file(path)
+            };
+            let moved = path.with_file_name(format!(
+                "{}-moved",
+                path.file_name().unwrap().to_string_lossy()
+            ));
+            let rename = std::fs::rename(path, moved);
+            Self {
+                directory,
+                metadata,
+                create,
+                write,
+                delete,
+                rename,
+            }
+        }
+
+        fn protected(&self) -> bool {
+            self.metadata.as_ref().is_ok_and(|metadata| {
+                if self.directory {
+                    metadata.is_dir()
+                } else {
+                    metadata.is_file()
+                }
+            }) && self.create.is_err()
+                && self.write.as_ref().err().and_then(io::Error::raw_os_error) == Some(5)
+                && self.delete.is_err()
+                && self.rename.is_err()
+        }
+    }
+
     // Observe the real prepared proxy without retaining its owner or replacing
     // backend selection, policy compilation, admission or cleanup.
     struct ObservedBackend {
@@ -208,22 +273,45 @@ mod native {
         async fn capture(&self, command: Command) -> Result<String> {
             let mut spawned = pipe::spawn(command).await?;
             drop(spawned.stdin);
-            let mut output = String::new();
-            let mut errors = String::new();
+            let mut output = Vec::new();
+            let mut errors = Vec::new();
             let (status, out, err) = tokio::join!(
                 spawned.child.wait(),
-                spawned.stdout.read_to_string(&mut output),
-                spawned.stderr.read_to_string(&mut errors),
+                spawned.stdout.read_to_end(&mut output),
+                spawned.stderr.read_to_end(&mut errors),
             );
             out?;
             err?;
+            if let Err(error) = &status {
+                eprintln!(
+                    "process wait failed: {error}; stdout bytes {output:?}: {}; stderr bytes {errors:?}: {}",
+                    String::from_utf8_lossy(&output),
+                    String::from_utf8_lossy(&errors)
+                );
+            }
             assert_eq!(
                 status?.code(),
                 Some(0),
-                "stdout: {output}\nstderr: {errors}"
+                "stdout bytes: {output:?}\nstdout: {}\nstderr bytes: {errors:?}\nstderr: {}",
+                String::from_utf8_lossy(&output),
+                String::from_utf8_lossy(&errors)
             );
-            assert_eq!(errors, "");
-            Ok(output)
+            assert!(
+                errors.is_empty(),
+                "stderr bytes: {errors:?}\nstderr: {}",
+                String::from_utf8_lossy(&errors)
+            );
+            String::from_utf8(output).map_err(|error| {
+                io::Error::new(
+                    io::ErrorKind::InvalidData,
+                    format!(
+                        "stdout is not UTF-8: bytes {:?}; lossy {}",
+                        error.as_bytes(),
+                        String::from_utf8_lossy(error.as_bytes())
+                    ),
+                )
+                .into()
+            })
         }
     }
 
@@ -239,28 +327,65 @@ mod native {
         );
         assert_eq!(windows::status(&fixture.state)?, Status::NotRequired);
 
-        filesystem(&fixture).await?;
-        nested_filesystem(&fixture).await?;
-        missing_metadata(&fixture).await?;
-        filesystem_aliases(&fixture).await?;
-        protected_ancestor(&fixture).await?;
-        unsupported_reopening(&fixture).await?;
-        arguments_and_environment(&fixture).await?;
-        pipe_input(&fixture).await?;
-        cancel_hung_admission(&fixture).await?;
-        cancel_tree(&fixture).await?;
-        terminal(&fixture).await?;
-        background(&fixture).await?;
-        network(&fixture).await?;
-
-        assert_eq!(
-            Installation::new(&fixture.state).status()?,
-            Status::NotConfigured
-        );
-        assert!(
-            !fixture.state.exists(),
-            "MXC must not provision an account installation"
-        );
+        let mut failures = Vec::new();
+        macro_rules! run_case {
+            ($case:ident) => {
+                run_case!($case, $case(&fixture))
+            };
+            ($case:ident, $future:expr) => {
+                match std::panic::AssertUnwindSafe($future).catch_unwind().await {
+                    Ok(Ok(())) => {}
+                    Ok(Err(error)) => {
+                        eprintln!("FAIL {}: {error}", stringify!($case));
+                        failures.push(stringify!($case));
+                    }
+                    Err(payload) => {
+                        let message = payload
+                            .downcast_ref::<String>()
+                            .map(String::as_str)
+                            .or_else(|| payload.downcast_ref::<&str>().copied())
+                            .unwrap_or("non-string panic");
+                        eprintln!("FAIL {}: {message}", stringify!($case));
+                        failures.push(stringify!($case));
+                    }
+                }
+            };
+        }
+        run_case!(filesystem);
+        run_case!(default_deny);
+        run_case!(nested_filesystem);
+        run_case!(missing_metadata);
+        run_case!(filesystem_aliases);
+        run_case!(hardlink_conflict);
+        run_case!(protected_ancestor);
+        run_case!(guard_registry);
+        run_case!(unsupported_reopening);
+        run_case!(arguments_and_environment);
+        run_case!(pipe_input);
+        run_case!(cancel_hung_admission);
+        run_case!(cancel_tree);
+        run_case!(terminal);
+        run_case!(background);
+        run_case!(network);
+        run_case!(installation_unchanged, async {
+            assert_eq!(
+                Installation::new(&fixture.state).status()?,
+                Status::NotConfigured
+            );
+            assert!(
+                !fixture.state.exists(),
+                "MXC must not provision an account installation"
+            );
+            Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
+        });
+        if !failures.is_empty() {
+            return Err(format!(
+                "native Windows MXC failed {} cases: {}",
+                failures.len(),
+                failures.join(", ")
+            )
+            .into());
+        }
         println!(
             "PASS native Windows MXC: Host selection, filesystem, argv/environment, pipes, cancellation, ConPTY, background lifetime, network"
         );
@@ -274,6 +399,12 @@ mod native {
         std::fs::write(fixture.work.join("private/secret"), "private-original")?;
         let outside = fixture.directory.path().join("outside");
         std::fs::write(&outside, "outside-original")?;
+        for name in [".agents", ".maka", ".maka-workspace.json"] {
+            assert!(
+                !fixture.work.join(name).exists(),
+                "metadata must be missing before admission: {name}"
+            );
+        }
         let mut command = fixture.command("filesystem", Network::Denied)?;
         command.arg(&outside);
         assert_eq!(fixture.capture(command).await?, "filesystem-ok\n");
@@ -291,6 +422,7 @@ mod native {
             "private-original"
         );
         assert!(!fixture.work.join(".maka").exists());
+        assert!(!fixture.work.join(".maka-moved").exists());
 
         let readonly =
             fixture
@@ -312,6 +444,36 @@ mod native {
         Ok(())
     }
 
+    async fn default_deny(fixture: &Fixture) -> Result<()> {
+        let system_root =
+            PathBuf::from(std::env::var_os("SystemRoot").ok_or("missing SystemRoot")?);
+        let filesystem = Policy {
+            default: Access::Deny,
+            rules: vec![
+                Rule::subtree(&system_root, Access::Read),
+                Rule::subtree(&fixture.work, Access::Write),
+            ],
+            deny_globs: vec![],
+        };
+        assert_eq!(
+            filesystem.compile()?.access(&fixture.executable),
+            Access::Deny,
+            "the trusted fixture helper must be outside command grants"
+        );
+        let mut command = Command::new(system_root.join("System32/cmd.exe"), &fixture.work)
+            .with_backend(fixture.backend.clone())
+            .sandbox(&Sandbox::Managed {
+                filesystem,
+                network: Network::Denied,
+            })?;
+        command.raw_arg("/d /c echo DENY_DEFAULT_OK");
+        assert_eq!(fixture.capture(command).await?, "DENY_DEFAULT_OK\r\n");
+        println!(
+            "PASS default-deny executes an authorized command without granting its trusted helper"
+        );
+        Ok(())
+    }
+
     async fn nested_filesystem(fixture: &Fixture) -> Result<()> {
         let read_parent = fixture.directory.path().join("read-parent");
         let write_child = read_parent.join("write-child");
@@ -369,6 +531,12 @@ mod native {
         std::fs::write(&marker, "host-positive-control")?;
         std::fs::remove_file(&marker)?;
         filesystem.rules.push(Rule::exact(&marker, Access::Read));
+        for name in [".git", ".agents", ".maka", ".maka-workspace.json"] {
+            assert!(
+                !root.join(name).exists(),
+                "metadata must be missing before admission: {name}"
+            );
+        }
         let mut command = fixture
             .command("missing-metadata", Network::Denied)?
             .sandbox(&Sandbox::Managed {
@@ -386,20 +554,16 @@ mod native {
                 !root.join(name).exists(),
                 "created protected metadata: {name}"
             );
+            assert!(
+                !root.join(format!("{name}-moved")).exists(),
+                "renamed protected metadata: {name}"
+            );
         }
         println!("PASS missing .git/.agents/.maka and exact workspace marker cannot be created");
         Ok(())
     }
 
     async fn filesystem_aliases(fixture: &Fixture) -> Result<()> {
-        let outside = fixture.directory.path().join("hardlink-original");
-        let hardlink = fixture.work.join("hardlink-alias");
-        std::fs::write(&outside, "hardlink-original")?;
-        std::fs::hard_link(&outside, &hardlink)?;
-        std::fs::write(&hardlink, "host-hardlink-write")?;
-        assert_eq!(std::fs::read_to_string(&outside)?, "host-hardlink-write");
-        std::fs::write(&outside, "hardlink-original")?;
-
         let outside_directory = fixture.directory.path().join("junction-original");
         std::fs::create_dir(&outside_directory)?;
         std::fs::write(outside_directory.join("original"), "junction-original")?;
@@ -427,7 +591,6 @@ mod native {
                 .await?,
             "filesystem-aliases-ok\n"
         );
-        assert_eq!(std::fs::read_to_string(&outside)?, "hardlink-original");
         assert_eq!(
             std::fs::read_to_string(outside_directory.join("original"))?,
             "junction-original"
@@ -438,7 +601,54 @@ mod native {
         );
         std::fs::remove_dir(private_alias)?;
         std::fs::remove_dir(junction)?;
-        println!("PASS hardlink and junction aliases cannot reopen readonly or denied objects");
+        println!("PASS junction aliases cannot reopen readonly or denied targets");
+        Ok(())
+    }
+
+    async fn hardlink_conflict(fixture: &Fixture) -> Result<()> {
+        let outside = fixture.directory.path().join("hardlink-original");
+        let hardlink = fixture.work.join("hardlink-alias");
+        std::fs::write(&outside, "hardlink-original")?;
+        std::fs::hard_link(&outside, &hardlink)?;
+        std::fs::write(&hardlink, "host-hardlink-write")?;
+        assert_eq!(std::fs::read_to_string(&outside)?, "host-hardlink-write");
+        std::fs::write(&outside, "hardlink-original")?;
+
+        let system_root =
+            PathBuf::from(std::env::var_os("SystemRoot").ok_or("missing SystemRoot")?);
+        let readonly = Rule::exact(&outside, Access::Read);
+        let writable = Rule::exact(&hardlink, Access::Write);
+        let filesystem = Policy {
+            default: Access::Deny,
+            rules: vec![
+                Rule::subtree(system_root, Access::Read),
+                Rule::exact(&fixture.executable, Access::Read),
+                Rule::subtree(&fixture.work, Access::Read),
+                readonly.clone(),
+                writable.clone(),
+            ],
+            deny_globs: vec![],
+        };
+        let compiled = filesystem.compile()?;
+        assert!(compiled.policy().rules.contains(&readonly));
+        assert!(compiled.policy().rules.contains(&writable));
+        assert_eq!(compiled.access(&outside), Access::Read);
+        assert_eq!(compiled.access(&hardlink), Access::Write);
+
+        let mut command = Command::new(&fixture.executable, &fixture.work)
+            .with_backend(fixture.backend.clone())
+            .sandbox(&Sandbox::Managed {
+                filesystem,
+                network: Network::Denied,
+            })?;
+        command
+            .args(["__fixture", "hardlink-conflict"])
+            .arg(&outside)
+            .arg(&hardlink);
+        assert_eq!(fixture.capture(command).await?, "hardlink-conflict-ok\n");
+        assert_eq!(std::fs::read_to_string(&outside)?, "hardlink-original");
+        assert_eq!(std::fs::read_to_string(&hardlink)?, "hardlink-original");
+        println!("PASS explicitly listed hardlink aliases normalize to read-only");
         Ok(())
     }
 
@@ -470,13 +680,65 @@ mod native {
         Ok(())
     }
 
+    async fn guard_registry(fixture: &Fixture) -> Result<()> {
+        let home = maka_event_log::root::windows::account_home()?;
+        let registry = home.join(".maka-sandbox-guards");
+        maka_event_log::root::private_directory(&registry)?;
+        let id = uuid::Uuid::new_v4();
+        let canary = tempfile::Builder::new()
+            .prefix(&format!("fixture-{id}-"))
+            .tempfile_in(&registry)?
+            .into_temp_path();
+        // Close the Host file handle: denial must come from the execution policy,
+        // rather than a sharing violation on the canary.
+        std::fs::write(&canary, "host-positive-control")?;
+        assert_eq!(std::fs::read_to_string(&canary)?, "host-positive-control");
+        std::fs::write(&canary, "registry-original")?;
+        let probe = registry.join(format!("fixture-probe-{id}"));
+        let moved_canary = registry.join(format!("fixture-moved-{id}"));
+        let moved_registry = home.join(format!(".maka-sandbox-guards-fixture-{id}"));
+        let filesystem = Policy {
+            default: Access::Read,
+            rules: vec![Rule::subtree(home, Access::Write)],
+            deny_globs: vec![],
+        };
+        let mut command = fixture
+            .command("guard-registry", Network::Denied)?
+            .sandbox(&Sandbox::Managed {
+                filesystem,
+                network: Network::Denied,
+            })?;
+        command
+            .arg(&registry)
+            .arg(canary.as_os_str())
+            .arg(&probe)
+            .arg(&moved_registry)
+            .arg(&moved_canary);
+        assert_eq!(fixture.capture(command).await?, "guard-registry-ok\n");
+        assert_eq!(std::fs::read_to_string(&canary)?, "registry-original");
+        assert_eq!(
+            std::fs::read_to_string(fixture.work.join("registry-ordinary"))?,
+            "home-write-allowed"
+        );
+        assert!(!probe.exists());
+        assert!(!moved_canary.exists());
+        assert!(!moved_registry.exists());
+        println!("PASS home-wide Write cannot read, mutate or rename the guard registry");
+        Ok(())
+    }
+
     async fn unsupported_reopening(fixture: &Fixture) -> Result<()> {
         let reopened = fixture.work.join("private/reopened");
         std::fs::create_dir(&reopened)?;
-        let mut filesystem = fixture.filesystem_policy();
-        filesystem
-            .rules
-            .push(Rule::subtree(&reopened, Access::Write));
+        let filesystem = Policy {
+            default: Access::Read,
+            rules: vec![
+                Rule::subtree(&fixture.work, Access::Write),
+                Rule::subtree(fixture.work.join("private"), Access::Deny),
+                Rule::subtree(&reopened, Access::Write),
+            ],
+            deny_globs: vec![],
+        };
         assert!(matches!(
             maka_sandbox::windows::mxc::PreparedPolicy::new(&filesystem, &Network::Denied, &fixture.work),
             Err(maka_sandbox::Error::Unsupported(reason)) if reason == "native MXC cannot reopen a descendant of a denied path"
@@ -504,16 +766,25 @@ mod native {
     }
 
     fn junction_directory(link: &Path, target: &Path) -> Result<()> {
-        let cmd = PathBuf::from(std::env::var_os("SystemRoot").ok_or("missing SystemRoot")?)
-            .join("System32/cmd.exe");
-        let result = std::process::Command::new(cmd)
-            .args(["/d", "/c", "mklink", "/J"])
-            .arg(link)
-            .arg(target)
+        let powershell = PathBuf::from(std::env::var_os("SystemRoot").ok_or("missing SystemRoot")?)
+            .join(r"System32\WindowsPowerShell\v1.0\powershell.exe");
+        let result = std::process::Command::new(powershell)
+            .args([
+                "-NoLogo",
+                "-NoProfile",
+                "-NonInteractive",
+                "-Command",
+                "New-Item -ItemType Junction -Path $env:MAKA_MXC_JUNCTION_LINK -Target $env:MAKA_MXC_JUNCTION_TARGET -ErrorAction Stop | Out-Null",
+            ])
+            .env("MAKA_MXC_JUNCTION_LINK", link)
+            .env("MAKA_MXC_JUNCTION_TARGET", target)
             .output()?;
         assert!(
             result.status.success(),
-            "junction positive control failed: {}",
+            "junction positive control failed: stdout bytes {:?}: {}; stderr bytes {:?}: {}",
+            result.stdout,
+            String::from_utf8_lossy(&result.stdout),
+            result.stderr,
             String::from_utf8_lossy(&result.stderr)
         );
         Ok(())
@@ -544,6 +815,21 @@ mod native {
             .env("localappdata", r"Q:\poison");
         let output = fixture.capture(command).await?;
         let value: serde_json::Value = serde_json::from_str(output.trim())?;
+        let local = value["LOCALAPPDATA"]
+            .as_str()
+            .ok_or("missing SDK LOCALAPPDATA")?;
+        let packages = PathBuf::from(std::env::var("LOCALAPPDATA")?).join("Packages");
+        let relative = Path::new(local).strip_prefix(&packages).map_err(|_| {
+            format!("SDK LOCALAPPDATA is outside the Host Packages directory: {local:?}")
+        })?;
+        let parts: Vec<_> = relative.iter().collect();
+        assert_eq!(parts.len(), 2, "unexpected SDK profile: {local}");
+        assert_eq!(parts[1], std::ffi::OsStr::new("AC"));
+        let profile = parts[0]
+            .to_str()
+            .and_then(|name| name.strip_prefix("sandbox."))
+            .ok_or("SDK profile is not sandbox.UUID")?;
+        uuid::Uuid::parse_str(profile)?;
         assert_eq!(
             value,
             json!({
@@ -552,7 +838,7 @@ mod native {
                 "userprofile": null,
                 "SystemRoot": std::env::var("SystemRoot")?,
                 "SystemDrive": std::env::var("SystemDrive")?,
-                "LOCALAPPDATA": std::env::var("LOCALAPPDATA")?,
+                "LOCALAPPDATA": local,
             })
         );
         println!("PASS raw/quoted arguments and env_clear with poisoned startup locations");
@@ -597,12 +883,17 @@ mod native {
             .with_backend(backend)
             .with_startup_cancellation(cancellation.clone());
         let mut startup = tokio::spawn(async move { pipe::spawn(command).await });
+        let mut settled = false;
+        // Assertion failures must also cancel and join the real admission owner
+        // before TempDir tries to remove its still-running helper executable.
+        let result = std::panic::AssertUnwindSafe(async {
         let proxy = prepared.await?;
         // The listener belongs to the real production launch, not the observer.
         drop(tokio::net::TcpStream::connect(proxy).await?);
         let pids: [u32; 2] = loop {
             tokio::select! {
                 outcome = &mut startup => {
+                    settled = true;
                     match outcome? {
                         Ok(mut spawned) => {
                             spawned.child.terminate()?;
@@ -626,7 +917,9 @@ mod native {
         assert_running(&helper_process);
         assert_running(&descendant);
         cancellation.cancel();
-        let failure = match startup.await? {
+        let outcome = (&mut startup).await;
+        settled = true;
+        let failure = match outcome? {
             Err(failure) => failure,
             Ok(mut spawned) => {
                 spawned.child.terminate()?;
@@ -638,8 +931,8 @@ mod native {
             matches!(failure, maka_agent_facts::tools::ToolError::Failed(ref message) if message == "sandbox launch cancelled"),
             "{failure}"
         );
-        assert_exited(&helper_process);
-        assert_exited(&descendant);
+        assert_exited(&helper_process, "cancelled startup helper");
+        assert_exited(&descendant, "cancelled startup descendant");
         assert!(
             tokio::net::TcpStream::connect(proxy).await.is_err(),
             "cancelled startup retained its execution proxy"
@@ -651,7 +944,25 @@ mod native {
         println!(
             "PASS startup cancellation settles a hung trusted helper, its descendants and its real execution proxy"
         );
-        Ok(())
+        Ok::<(), Box<dyn std::error::Error + Send + Sync>>(())
+        }).catch_unwind().await;
+        cancellation.cancel();
+        if !settled {
+            match startup.await? {
+                Ok(mut spawned) => {
+                    spawned.child.terminate()?;
+                    spawned.child.wait().await?;
+                }
+                Err(maka_agent_facts::tools::ToolError::CleanupUnconfirmed(message)) => {
+                    return Err(message.into());
+                }
+                Err(_) => {}
+            }
+        }
+        match result {
+            Ok(result) => result,
+            Err(panic) => std::panic::resume_unwind(panic),
+        }
     }
 
     fn hung_helper() -> Result<()> {
@@ -712,7 +1023,7 @@ mod native {
         ));
         assert_eq!(captured.stdout, line);
         assert_eq!(captured.stderr, "");
-        assert_exited(&descendant);
+        assert_exited(&descendant, "foreground cancelled descendant");
         println!("PASS foreground cancellation settles the complete descendant tree");
         Ok(())
     }
@@ -762,7 +1073,7 @@ mod native {
         let (closed, drained) = tokio::join!(child.close(), drain(&io));
         closed?;
         drained?;
-        assert_exited(&descendant);
+        assert_exited(&descendant, "settled background descendant");
         println!(
             "PASS root exit keeps background processes isolated until explicit owner settlement"
         );
@@ -776,9 +1087,41 @@ mod native {
         for address in addresses {
             tokio::net::TcpStream::connect(address).await?;
         }
+        // UDP connect selects the native route without transmitting a packet.
+        // Codex host-loopback access includes the Host's own interface IP;
+        // distinguish that local service from an actual nonlocal destination.
+        let route = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0))?;
+        route.connect((Ipv4Addr::new(192, 0, 2, 1), 9))?;
+        let native_ip = route.local_addr()?.ip();
+        if native_ip.is_unspecified() || native_ip.is_loopback() {
+            return Err("native Windows routing did not select a non-loopback address".into());
+        }
+        let direct = tokio::net::TcpListener::bind((native_ip, 0)).await?;
+        let direct_address = direct.local_addr()?;
+        drop(tokio::net::TcpStream::connect(direct_address).await?);
+        let remote = ("example.com", 443)
+            .to_socket_addrs()?
+            .find(SocketAddr::is_ipv4)
+            .ok_or("example.com has no IPv4 address for the public TCP control")?;
+        assert!(
+            !remote.ip().is_loopback() && !remote.ip().is_unspecified() && remote.ip() != native_ip,
+            "public TCP control resolved to a local address: {remote}"
+        );
+        drop(
+            TcpStream::connect_timeout(&remote, Duration::from_secs(10)).map_err(|error| {
+                io::Error::other(format!(
+                    "public TCP positive control {remote} is unavailable: {error}"
+                ))
+            })?,
+        );
         let mut denied = fixture.command("network-denied", Network::Denied)?;
         denied.args(addresses.map(|address| address.to_string()));
-        assert_eq!(fixture.capture(denied).await?, "blocked-v4\nblocked-v6\n");
+        denied.arg(direct_address.to_string());
+        denied.arg(remote.to_string());
+        assert_eq!(
+            fixture.capture(denied).await?,
+            "blocked-v4\nblocked-v6\nblocked-direct\nblocked-remote\n"
+        );
 
         let allowed = tokio::net::TcpListener::bind((Ipv4Addr::LOCALHOST, 0)).await?;
         let destination = allowed.local_addr()?;
@@ -810,13 +1153,15 @@ mod native {
         )?;
         restricted.args(addresses.map(|address| address.to_string()));
         restricted.arg(destination.to_string());
+        restricted.arg(direct_address.to_string());
+        restricted.arg(remote.to_string());
         assert_eq!(
             fixture.capture(restricted).await?,
-            "loopback-v4\nloopback-v6\nproxy-allowed\nproxy-denied\n"
+            "loopback-v4\nloopback-v6\nlocal-interface\nblocked-remote\nproxy-allowed\nproxy-denied\n"
         );
         server.await??;
         println!(
-            "PASS denied IPv4/IPv6, Codex-compatible local loopback, destination-enforcing execution proxy"
+            "PASS local IPv4/IPv6/interface semantics, public direct egress blocked, destination-enforcing execution proxy"
         );
         Ok(())
     }
@@ -832,9 +1177,14 @@ mod native {
                     std::fs::read("private/secret").unwrap_err().raw_os_error(),
                     Some(5)
                 );
-                assert_eq!(
-                    std::fs::create_dir(".maka").unwrap_err().raw_os_error(),
-                    Some(5)
+                let guarded = MetadataProbe::capture(Path::new(".maka"), true);
+                if !guarded.protected() {
+                    println!("guarded .maka: {guarded:?}");
+                    io::stdout().flush()?;
+                }
+                assert!(
+                    guarded.protected(),
+                    "materialized .maka must reject child writes, deletion and rename"
                 );
                 println!("filesystem-ok");
             }
@@ -871,20 +1221,27 @@ mod native {
             Some("missing-metadata") => {
                 let root = Path::new(&args[1]);
                 std::fs::write(root.join("ordinary"), "ordinary-write")?;
+                let mut results = Vec::new();
                 for name in [".git", ".agents", ".maka"] {
-                    assert_eq!(
-                        std::fs::create_dir(root.join(name))
-                            .unwrap_err()
-                            .raw_os_error(),
-                        Some(5),
-                        "{name}"
-                    );
+                    results.push((name, MetadataProbe::capture(&root.join(name), true)));
+                }
+                let marker = MetadataProbe::capture(&root.join(".maka-workspace.json"), false);
+                let all_denied =
+                    results.iter().all(|(_, probe)| probe.protected()) && marker.protected();
+                if !all_denied {
+                    for (name, probe) in &results {
+                        println!("guarded {name}: {probe:?}");
+                    }
+                    println!("guarded .maka-workspace.json: {marker:?}");
+                    io::stdout().flush()?;
                 }
-                denied_write(&root.join(".maka-workspace.json"));
+                assert!(
+                    all_denied,
+                    "materialized metadata must exist, reject child writes/marker overwrite with ERROR_ACCESS_DENIED, and reject deletion/rename"
+                );
                 println!("missing-metadata-ok");
             }
             Some("filesystem-aliases") => {
-                denied_write(Path::new("hardlink-alias"));
                 denied_write(Path::new("junction-alias/original"));
                 denied_write(Path::new("private-alias/secret"));
                 assert_eq!(
@@ -895,6 +1252,14 @@ mod native {
                 );
                 println!("filesystem-aliases-ok");
             }
+            Some("hardlink-conflict") => {
+                assert_eq!(args.len(), 3, "both hardlink names must reach the worker");
+                for path in &args[1..] {
+                    assert_eq!(std::fs::read_to_string(path)?, "hardlink-original");
+                    denied_write(Path::new(path));
+                }
+                println!("hardlink-conflict-ok");
+            }
             Some("protected-ancestor") => {
                 std::fs::write("protected-ancestor/ordinary", "ordinary-write")?;
                 assert!(
@@ -904,6 +1269,52 @@ mod native {
                 denied_write(Path::new("protected-ancestor/metadata/original"));
                 println!("protected-ancestor-ok");
             }
+            Some("guard-registry") => {
+                let registry = Path::new(&args[1]);
+                let canary = Path::new(&args[2]);
+                let probe = Path::new(&args[3]);
+                let moved_registry = Path::new(&args[4]);
+                let moved_canary = Path::new(&args[5]);
+                std::fs::write("registry-ordinary", "home-write-allowed")?;
+                let read = std::fs::read(canary).map(drop);
+                let write = std::fs::write(canary, "registry-escaped");
+                let delete = std::fs::remove_file(canary);
+                let rename = std::fs::rename(canary, moved_canary);
+                if rename.is_ok() {
+                    std::fs::rename(moved_canary, canary)?;
+                }
+                let create = std::fs::OpenOptions::new()
+                    .create_new(true)
+                    .write(true)
+                    .open(probe)
+                    .map(drop);
+                if create.is_ok() {
+                    std::fs::remove_file(probe)?;
+                }
+                let rename_registry = std::fs::rename(registry, moved_registry);
+                if rename_registry.is_ok() {
+                    // Restore immediately if the protection under test failed.
+                    std::fs::rename(moved_registry, registry)?;
+                }
+                for (operation, result) in [
+                    ("read", read),
+                    ("write", write),
+                    ("delete", delete),
+                    ("rename canary", rename),
+                    ("create", create),
+                ] {
+                    assert_eq!(
+                        result.as_ref().err().and_then(io::Error::raw_os_error),
+                        Some(5),
+                        "home-wide Write bypassed registry denial: {operation}: {result:?}"
+                    );
+                }
+                assert!(
+                    rename_registry.is_err(),
+                    "home-wide Write renamed the protected registry"
+                );
+                println!("guard-registry-ok");
+            }
             Some("must-not-run") => {
                 std::fs::write("unexpected-execution", "policy-was-bypassed")?;
             }
@@ -969,7 +1380,12 @@ mod native {
                 }
             }
             Some("network-denied") => {
-                for (address, marker) in args[1..].iter().zip(["blocked-v4", "blocked-v6"]) {
+                for (address, marker) in args[1..].iter().zip([
+                    "blocked-v4",
+                    "blocked-v6",
+                    "blocked-direct",
+                    "blocked-remote",
+                ]) {
                     assert!(
                         TcpStream::connect_timeout(&address.parse()?, Duration::from_secs(10))
                             .is_err(),
@@ -983,6 +1399,14 @@ mod native {
                     TcpStream::connect_timeout(&address.parse()?, Duration::from_secs(10))?;
                     println!("{marker}");
                 }
+                TcpStream::connect_timeout(&args[4].parse()?, Duration::from_secs(10))?;
+                println!("local-interface");
+                assert!(
+                    TcpStream::connect_timeout(&args[5].parse()?, Duration::from_secs(10)).is_err(),
+                    "restricted networking bypassed its proxy to public destination: {}",
+                    args[5]
+                );
+                println!("blocked-remote");
                 let proxy = std::env::var("HTTP_PROXY")?;
                 let proxy = proxy
                     .strip_prefix("http://")
@@ -1054,11 +1478,12 @@ mod native {
         );
     }
 
-    fn assert_exited(process: &OwnedHandle) {
+    fn assert_exited(process: &OwnedHandle, label: &str) {
         // SAFETY: settlement has completed; this checks the exact retained process object.
         assert_eq!(
             unsafe { WaitForSingleObject(process.as_raw_handle(), 0) },
-            WAIT_OBJECT_0
+            WAIT_OBJECT_0,
+            "{label} must already be signalled at settlement"
         );
     }
 
diff --git a/platform/host-runtime/src/sandbox/windows/guards.rs b/platform/host-runtime/src/sandbox/windows/guards.rs
index d1f9cb9dbb..477a215ebc 100644
--- a/platform/host-runtime/src/sandbox/windows/guards.rs
+++ b/platform/host-runtime/src/sandbox/windows/guards.rs
@@ -23,7 +23,7 @@ use maka_event_log::root::{
     windows::{account_home, create_private_file, open_nofollow, publish_file, validate_private},
 };
 use maka_sandbox::{
-    filesystem::{Access, Compiled, Scope},
+    filesystem::{Access, Compiled, Rule, Scope},
     windows::{
         acl::{Removal, Target},
         ensure_drained,
@@ -33,9 +33,11 @@ use serde::{Deserialize, Serialize};
 use std::{
     fs::{self, File},
     io,
+    os::windows::io::AsRawHandle,
     path::{Path, PathBuf},
 };
 use uuid::Uuid;
+use windows_sys::Win32::Storage::FileSystem::{GetFinalPathNameByHandleW, VOLUME_NAME_GUID};
 
 #[derive(Serialize, Deserialize)]
 #[serde(deny_unknown_fields)]
@@ -55,8 +57,12 @@ pub(super) struct Guards {
     pins: Vec<File>,
 }
 
+pub(super) fn registry_root() -> io::Result<PathBuf> {
+    account_home().map(|home| home.join(".maka-sandbox-guards"))
+}
+
 pub(super) fn recover() -> io::Result<()> {
-    let root = account_home()?.join(".maka-sandbox-guards");
+    let root = registry_root()?;
     if !root.try_exists()? {
         return Ok(());
     }
@@ -65,22 +71,49 @@ pub(super) fn recover() -> io::Result<()> {
 }
 impl Guards {
     pub fn prepare(policy: &Compiled) -> io::Result<Self> {
-        if policy.policy().default != Access::Read
-            || !policy.policy().deny_globs.is_empty()
-            || policy
+        if policy.policy().default != Access::Read || !policy.policy().deny_globs.is_empty() {
+            return Err(io::Error::new(
+                io::ErrorKind::Unsupported,
+                "Windows account sandbox requires a read-default path snapshot",
+            ));
+        }
+        Self::prepare_rules(
+            policy,
+            policy
                 .policy()
                 .rules
                 .iter()
-                .any(|rule| rule.path.components().count() > 256)
-        {
+                .filter(|rule| rule.access != Access::Write)
+                .collect(),
+            false,
+        )
+    }
+
+    /// PSEC ignores missing grants and does not prevent renaming a writable
+    /// ancestor around a read-only carveout. Materialize only those boundaries;
+    /// native deny paths remain with the native enforcement owner.
+    pub fn prepare_native(policy: &Compiled) -> io::Result<Self> {
+        if !policy.policy().deny_globs.is_empty() {
             return Err(io::Error::new(
-                io::ErrorKind::Unsupported,
-                "Windows sandbox requires a bounded read-default path policy",
+                io::ErrorKind::InvalidInput,
+                "native filesystem guards require a process policy snapshot",
             ));
         }
+        let rules = policy
+            .policy()
+            .rules
+            .iter()
+            .filter(|rule| {
+                rule.access == Access::Read && policy.inherited_access(rule) == Access::Write
+            })
+            .collect::<Vec<_>>();
+        Self::prepare_rules(policy, rules, true)
+    }
+
+    fn prepare_rules(policy: &Compiled, rules: Vec<&Rule>, native: bool) -> io::Result<Self> {
         // Resolve through the account token, not USERPROFILE or a particular
         // State Root. Separate Hosts must coordinate on the same namespace.
-        let root = account_home()?.join(".maka-sandbox-guards");
+        let root = registry_root()?;
         maka_event_log::root::private_directory(&root)?;
         store::lease_file(&root.join("admission.lock"))?;
         let _gate = store::admission(&root)?;
@@ -92,16 +125,30 @@ impl Guards {
         let mut pins = Vec::new();
         let mut targets = Vec::new();
         let result = (|| {
-            for rule in policy
-                .policy()
-                .rules
-                .iter()
-                .filter(|rule| rule.access != Access::Write)
-            {
-                materialize(&root, &rule.path, rule.scope == Scope::Subtree)?;
-                let (target, pin) = Target::pin(&rule.path)?;
-                targets.push(target);
-                pins.push(pin);
+            let (target, pin) = Target::pin_read(&root)?;
+            preflight(&pin, &rules)?;
+            targets.push(target);
+            pins.push(pin);
+            if native {
+                // Native deny paths do not pin their writable ancestors. The
+                // registry namespace must stay stable for every native tree,
+                // including policies with no read-only carveouts.
+                pin_writable_ancestors(policy, &root, &mut targets, &mut pins)?;
+            }
+            for rule in rules {
+                materialize(&root, &rule.path, rule.scope == Scope::Subtree, native)?;
+                let (target, pin) = if native {
+                    Target::pin_read(&rule.path)?
+                } else {
+                    Target::pin(&rule.path)?
+                };
+                if !targets.contains(&target) {
+                    targets.push(target);
+                    pins.push(pin);
+                }
+                if native {
+                    pin_writable_ancestors(policy, &rule.path, &mut targets, &mut pins)?;
+                }
             }
             store::publish(&root, &format!("{name}.json"), &targets)
         })();
@@ -144,7 +191,7 @@ impl Guards {
     }
 }
 
-fn materialize(root: &Path, path: &Path, directory: bool) -> io::Result<()> {
+fn materialize(root: &Path, path: &Path, directory: bool, native: bool) -> io::Result<()> {
     match path.symlink_metadata() {
         Ok(_) => return Ok(()),
         Err(error) if error.kind() == io::ErrorKind::NotFound => {}
@@ -153,17 +200,22 @@ fn materialize(root: &Path, path: &Path, directory: bool) -> io::Result<()> {
     let parent = path
         .parent()
         .ok_or_else(|| io::Error::other("guard has no parent"))?;
-    materialize(root, parent, true)?;
-    let (_identity, _parent) = Target::pin(parent)?;
+    materialize(root, parent, true, native)?;
+    let (_identity, _parent) = if native {
+        Target::pin_read(parent)?
+    } else {
+        Target::pin(parent)?
+    };
     let id = Uuid::new_v4();
     let name = format!("guard-{id}");
     let pending = Pending {
-        source: parent.join(format!(".maka-guard-{id}")),
+        source: root.join(format!(".maka-guard-{id}")),
         destination: path.to_owned(),
         directory,
     };
-    // The staging name is never reused. Before publication it is private to
-    // this OS user; after publication recovery uses only its persistent file ID.
+    // Native peers share the Host's OS account, so their PSEC policy denies the
+    // entire registry. Account-backed peers are excluded by the private DACL.
+    // After publication recovery follows only the durable object identity.
     store::publish(root, &format!("{name}.pending.json"), &pending)?;
     if directory {
         maka_event_log::root::private_directory(&pending.source)?;
@@ -176,6 +228,104 @@ fn materialize(root: &Path, path: &Path, directory: bool) -> io::Result<()> {
     publish_file(&pending.source, path)
 }
 
+fn pin_writable_ancestors(
+    policy: &Compiled,
+    path: &Path,
+    targets: &mut Vec<Target>,
+    pins: &mut Vec<File>,
+) -> io::Result<()> {
+    for ancestor in path.ancestors().skip(1) {
+        if policy.access(ancestor) == Access::Write {
+            let (target, pin) = Target::pin_read(ancestor)?;
+            if !targets.contains(&target) {
+                targets.push(target);
+                pins.push(pin);
+            }
+        }
+    }
+    Ok(())
+}
+
+/// Publishing a placeholder is an atomic, no-replace rename. Check the actual
+/// volume identity of every missing boundary's existing parent before creating
+/// any staging objects, rather than relying on drive letters or copy fallback.
+fn preflight(registry: &File, rules: &[&Rule]) -> io::Result<()> {
+    let registry_volume = volume_identity(registry)?;
+    for rule in rules {
+        match rule.path.symlink_metadata() {
+            Ok(_) => continue,
+            Err(error) if error.kind() == io::ErrorKind::NotFound => {}
+            Err(error) => return Err(error),
+        }
+        for parent in rule.path.ancestors().skip(1) {
+            match parent.symlink_metadata() {
+                Ok(metadata) => {
+                    if !metadata.is_dir() {
+                        return Err(io::Error::new(
+                            io::ErrorKind::NotADirectory,
+                            "guard parent is not a directory",
+                        ));
+                    }
+                    let (_, file) = Target::pin_read(parent)?;
+                    if volume_identity(&file)? != registry_volume {
+                        return Err(io::Error::new(
+                            io::ErrorKind::Unsupported,
+                            "missing filesystem boundaries require atomic publication from the guard registry on the same volume",
+                        ));
+                    }
+                    break;
+                }
+                Err(error) if error.kind() == io::ErrorKind::NotFound => {}
+                Err(error) => return Err(error),
+            }
+        }
+    }
+    Ok(())
+}
+
+fn volume_identity(file: &File) -> io::Result<Uuid> {
+    // SAFETY: the first call only queries the required UTF-16 buffer length.
+    let capacity = unsafe {
+        GetFinalPathNameByHandleW(
+            file.as_raw_handle(),
+            std::ptr::null_mut(),
+            0,
+            VOLUME_NAME_GUID,
+        )
+    };
+    if capacity == 0 {
+        return Err(io::Error::last_os_error());
+    }
+    let mut buffer = vec![0u16; capacity as usize];
+    // SAFETY: valid held file and an output buffer with the reported capacity.
+    let length = unsafe {
+        GetFinalPathNameByHandleW(
+            file.as_raw_handle(),
+            buffer.as_mut_ptr(),
+            capacity,
+            VOLUME_NAME_GUID,
+        )
+    };
+    if length == 0 {
+        return Err(io::Error::last_os_error());
+    }
+    if length >= capacity {
+        return Err(io::Error::other(
+            "guard volume namespace changed during preparation",
+        ));
+    }
+    let name = String::from_utf16(&buffer[..length as usize]).map_err(io::Error::other)?;
+    name.strip_prefix(r"\\?\Volume{")
+        .and_then(|name| name.split_once(r"}\"))
+        .and_then(|(id, _)| id.parse().ok())
+        .ok_or_else(|| {
+            io::Error::new(
+                io::ErrorKind::Unsupported,
+                "guard publication requires a local volume identity",
+            )
+        })
+}
+
 fn active_targets(root: &Path) -> io::Result<Vec<Target>> {
     let mut active = Vec::new();
     for entry in fs::read_dir(root)? {
@@ -301,6 +451,15 @@ mod tests {
 
     #[test]
     fn protection_outlives_peer_settlement_and_a_lost_host_until_its_native_tree_exits() {
+        protection_lifetime(Guards::prepare);
+    }
+
+    #[test]
+    fn native_protection_outlives_peer_settlement_and_a_lost_host_until_its_tree_exits() {
+        protection_lifetime(Guards::prepare_native);
+    }
+
+    fn protection_lifetime(prepare: fn(&Compiled) -> io::Result<Guards>) {
         let directory = tempfile::tempdir().unwrap();
         let root = directory.path();
         let boundary = root.join("missing/protected");
@@ -314,8 +473,8 @@ mod tests {
         }
         .compile()
         .unwrap();
-        let first = Guards::prepare(&policy).unwrap();
-        let survivor = Guards::prepare(&policy).unwrap();
+        let first = prepare(&policy).unwrap();
+        let survivor = prepare(&policy).unwrap();
         first.finish().unwrap();
         assert!(
             boundary.is_dir(),
diff --git a/platform/host-runtime/src/sandbox/windows/launch.rs b/platform/host-runtime/src/sandbox/windows/launch.rs
index e2b4942bd2..b0c0e7de61 100644
--- a/platform/host-runtime/src/sandbox/windows/launch.rs
+++ b/platform/host-runtime/src/sandbox/windows/launch.rs
@@ -17,12 +17,16 @@
  * under the License.
  */
 
-use super::{Installation, guards::Guards, policy, store};
+use super::{
+    Installation,
+    guards::{self, Guards},
+    policy, store,
+};
 use maka_process::bootstrap::{self, Preparation};
 use maka_sandbox::{
     Network,
-    filesystem::Policy,
-    windows::{WriteCapability, mxc},
+    filesystem::{Access, Policy, Rule},
+    windows::{ExecutionJob, WriteCapability, mxc},
 };
 use std::{
     io,
@@ -37,6 +41,15 @@ pub struct Backend {
     state_root: PathBuf,
     helper: PathBuf,
 }
+
+enum NativePreparation {
+    Ready {
+        filesystem: Policy,
+        lease: bootstrap::MxcLease,
+    },
+    Unsupported(String),
+}
+
 impl Backend {
     pub fn new(state_root: &Path, helper: &Path) -> Self {
         Self {
@@ -56,17 +69,16 @@ impl bootstrap::Backend for Backend {
     ) -> Preparation {
         let backend = self.clone();
         Box::pin(async move {
-            let helper = backend.helper.clone();
             let policy_executable = executable.clone();
             let policy_cwd = cwd.clone();
             let policy_network = network.clone();
-            let (filesystem, mxc_unsupported) = tokio::task::spawn_blocking(move || {
+            let (filesystem, native) = tokio::task::spawn_blocking(move || {
                 let filesystem = filesystem
                     .compile()
                     .and_then(|policy| policy.process_snapshot())
                     .map_err(io::Error::other)?;
                 let compiled = filesystem.compile().map_err(io::Error::other)?;
-                if [&helper, &policy_executable, &policy_cwd]
+                if [&policy_executable, &policy_cwd]
                     .into_iter()
                     .any(|path| !compiled.access(path).can_read())
                 {
@@ -75,33 +87,46 @@ impl bootstrap::Backend for Backend {
                         "sandbox denies its working directory or executable",
                     ));
                 }
-                let mxc_unsupported =
-                    match mxc::PreparedPolicy::new(&filesystem, &policy_network, &policy_cwd) {
-                        Ok(_) => None,
-                        Err(maka_sandbox::Error::Unsupported(reason)) => Some(reason),
-                        Err(error) => return Err(io::Error::other(error)),
-                    };
-                Ok((filesystem, mxc_unsupported))
+                let native = prepare_native(
+                    &filesystem,
+                    &policy_network,
+                    &policy_executable,
+                    &policy_cwd,
+                )?;
+                Ok((filesystem, native))
             })
             .await
             .map_err(io::Error::other)??;
-            let Some(mxc_unsupported) = mxc_unsupported else {
-                // MXC owns isolation per command tree. Its proxy has no account
-                // slot or installation lifetime and remains with that same tree.
-                let (proxy_address, proxy) = if matches!(network, Network::Restricted { .. }) {
-                    let (address, proxy) =
-                        maka_network::proxy::Proxy::start(network.clone(), route).await?;
-                    (Some(address), Some(Arc::new(proxy)))
-                } else {
-                    (None, None)
-                };
-                return Ok(bootstrap::Launch::Mxc {
-                    executable: backend.helper,
+            let mxc_unsupported = match native {
+                NativePreparation::Ready {
                     filesystem,
-                    network,
-                    proxy,
-                    proxy_address,
-                });
+                    mut lease,
+                } => {
+                    // MXC owns isolation per command tree. Its proxy has no account
+                    // slot or installation lifetime and remains with that same tree.
+                    let (proxy_address, proxy) = if matches!(network, Network::Restricted { .. }) {
+                        let (address, proxy) =
+                            match maka_network::proxy::Proxy::start(network.clone(), route).await {
+                                Ok(proxy) => proxy,
+                                Err(error) => {
+                                    lease.finish().await.map_err(cleanup_error)?;
+                                    return Err(error);
+                                }
+                            };
+                        (Some(address), Some(Arc::new(proxy)))
+                    } else {
+                        (None, None)
+                    };
+                    return Ok(bootstrap::Launch::Mxc {
+                        executable: backend.helper,
+                        filesystem,
+                        network,
+                        proxy,
+                        proxy_address,
+                        lease,
+                    });
+                }
+                NativePreparation::Unsupported(reason) => reason,
             };
             let root = backend.state_root.clone();
             let helper = backend.helper.clone();
@@ -234,6 +259,69 @@ impl bootstrap::Backend for Backend {
     }
 }
 
+fn prepare_native(
+    filesystem: &Policy,
+    network: &Network,
+    executable: &Path,
+    cwd: &Path,
+) -> io::Result<NativePreparation> {
+    guards::recover().map_err(cleanup_error)?;
+    if !mxc::is_available() {
+        return Ok(NativePreparation::Unsupported(
+            "native Windows process security environments are unavailable".into(),
+        ));
+    }
+    let registry = guards::registry_root()?;
+    maka_event_log::root::private_directory(&registry)?;
+    let ceiling = Policy {
+        default: Access::Write,
+        rules: vec![Rule::subtree(registry, Access::Deny)],
+        deny_globs: Vec::new(),
+    };
+    let compiled = filesystem
+        .compile()
+        .and_then(|policy| policy.intersect(&ceiling.compile()?))
+        .map_err(io::Error::other)?;
+    if !compiled.access(executable).can_read() || !compiled.access(cwd).can_read() {
+        return Err(io::Error::new(
+            io::ErrorKind::PermissionDenied,
+            "sandbox denies its working directory or executable",
+        ));
+    }
+    let filesystem = compiled.policy().clone();
+    let mut guards = Some(match Guards::prepare_native(&compiled) {
+        Ok(guards) => guards,
+        Err(error) if error.kind() == io::ErrorKind::Unsupported => {
+            return Ok(NativePreparation::Unsupported(error.to_string()));
+        }
+        Err(error) => return Err(context("protect native filesystem boundaries", error)),
+    });
+    let prepared = (|| {
+        match mxc::PreparedPolicy::new(&filesystem, network, cwd) {
+            Ok(_) => {}
+            Err(maka_sandbox::Error::Unsupported(reason)) => {
+                return Ok(NativePreparation::Unsupported(reason));
+            }
+            Err(error) => return Err(io::Error::other(error)),
+        }
+        let id = uuid::Uuid::new_v4();
+        let job = ExecutionJob::create(id)?;
+        if let Some(guards) = &guards {
+            guards.bind(id)?;
+        }
+        let guards = guards.take();
+        let lease = bootstrap::MxcLease::new(job, move || match guards {
+            Some(guards) => guards.finish(),
+            None => Ok(()),
+        });
+        Ok(NativePreparation::Ready { filesystem, lease })
+    })();
+    if let Some(guards) = guards {
+        guards.finish().map_err(cleanup_error)?;
+    }
+    prepared
+}
+
 fn cleanup_error(error: impl std::fmt::Display) -> io::Error {
     io::Error::other(maka_agent_facts::tools::ToolError::CleanupUnconfirmed(
         error.to_string(),
diff --git a/platform/host-runtime/src/sandbox/windows/store.rs b/platform/host-runtime/src/sandbox/windows/store.rs
index 4ef065080e..da1edcf3a4 100644
--- a/platform/host-runtime/src/sandbox/windows/store.rs
+++ b/platform/host-runtime/src/sandbox/windows/store.rs
@@ -137,7 +137,7 @@ pub(super) fn shared_lease(path: &Path) -> io::Result<File> {
 }
 
 pub(super) fn remove(path: &Path) -> io::Result<()> {
-    let mut file = match open_nofollow(path, false) {
+    let file = match open_nofollow(path, false) {
         Ok(file) => file,
         Err(error) if error.kind() == io::ErrorKind::NotFound => return Ok(()),
         Err(error) => return Err(error),
diff --git a/platform/process/src/bootstrap.rs b/platform/process/src/bootstrap.rs
index f4a8c756b8..2cfae7c79b 100644
--- a/platform/process/src/bootstrap.rs
+++ b/platform/process/src/bootstrap.rs
@@ -29,7 +29,7 @@ pub use elevation::{
 };
 mod backend;
 pub(crate) mod mxc;
-pub use mxc::{HELPER as MXC_HELPER, serve as serve_mxc};
+pub use mxc::{HELPER as MXC_HELPER, Lease as MxcLease, serve as serve_mxc};
 mod logon;
 pub(crate) mod protocol;
 mod runner;
diff --git a/platform/process/src/bootstrap/backend.rs b/platform/process/src/bootstrap/backend.rs
index 98c984f308..3e8c1edf52 100644
--- a/platform/process/src/bootstrap/backend.rs
+++ b/platform/process/src/bootstrap/backend.rs
@@ -48,6 +48,7 @@ pub enum Launch {
         proxy_address: Option<std::net::SocketAddr>,
     },
     Mxc {
+        lease: super::MxcLease,
         executable: PathBuf,
         filesystem: Policy,
         network: Network,
@@ -66,11 +67,13 @@ impl Launch {
     pub(crate) async fn abort(self) -> io::Result<()> {
         match self {
             Self::Account { mut runner, .. } => runner.finish().await,
-            Self::Mxc { proxy, .. } => {
+            Self::Mxc {
+                mut lease, proxy, ..
+            } => {
                 if let Some(mut proxy) = proxy.and_then(std::sync::Arc::into_inner) {
                     proxy.close().await?;
                 }
-                Ok(())
+                lease.finish().await
             }
         }
     }
@@ -87,12 +90,13 @@ impl Launch {
                 ..
             } => runner.pipes(endpoint, command, capabilities).await,
             Self::Mxc {
+                lease,
                 executable,
                 filesystem,
                 network,
                 proxy,
                 ..
-            } => super::mxc::pipes(executable, filesystem, network, proxy, command).await,
+            } => super::mxc::pipes(executable, filesystem, network, proxy, lease, command).await,
         }
     }
 
@@ -109,12 +113,16 @@ impl Launch {
                 ..
             } => runner.terminal(endpoint, command, capabilities, size).await,
             Self::Mxc {
+                lease,
                 executable,
                 filesystem,
                 network,
                 proxy,
                 ..
-            } => super::mxc::terminal(executable, filesystem, network, proxy, command, size).await,
+            } => {
+                super::mxc::terminal(executable, filesystem, network, proxy, lease, command, size)
+                    .await
+            }
         }
     }
 }
diff --git a/platform/process/src/bootstrap/mxc.rs b/platform/process/src/bootstrap/mxc.rs
index 4ecda389de..822064fb08 100644
--- a/platform/process/src/bootstrap/mxc.rs
+++ b/platform/process/src/bootstrap/mxc.rs
@@ -20,6 +20,9 @@
 //! The helper owns MXC's SDK process until the execution owner settles its tree.
 //! It inherits the Host-created stdio or ConPTY; user requests cross only the
 //! authenticated local channel, never helper argv or process-global state.
+mod lease;
+pub use lease::Lease;
+
 use super::{
     Channel, Endpoint, channel,
     protocol::{Control, Controlled, Plan, Started},
@@ -73,6 +76,7 @@ struct Resources {
     job: Job,
     channel: Channel,
     proxy: Option<Arc<maka_network::proxy::Proxy>>,
+    lease: Lease,
     terminated: Arc<AtomicBool>,
 }
 impl Owner {
@@ -122,12 +126,16 @@ impl Resources {
         // Even a lost control channel must confirm tree exit before releasing
         // the execution's proxy. Never report successful cleanup after an error.
         self.job.terminate(130)?;
+        // Job accounting reaching zero is not a signalled process-object fence.
+        // Retain and observe the exact helper even after a broken control reply.
+        wait_process(&self.helper).await?;
         while !self.job.is_empty()? {
             tokio::time::sleep(Duration::from_millis(10)).await;
         }
         if let Some(mut proxy) = self.proxy.take().and_then(Arc::into_inner) {
             proxy.close().await?;
         }
+        self.lease.finish().await?;
         if self.terminated.load(Ordering::Acquire) {
             Ok(())
         } else {
@@ -140,67 +148,32 @@ pub(crate) async fn pipes(
     executable: PathBuf,
     filesystem: Policy,
     network: Network,
-    proxy: Option<Arc<maka_network::proxy::Proxy>>,
+    mut proxy: Option<Arc<maka_network::proxy::Proxy>>,
+    lease: Lease,
     plan: crate::command::Prepared,
 ) -> io::Result<pipe::Spawned> {
     let _directory = plan.directory();
     let cancellation = plan.startup_cancellation();
-    let endpoint = Endpoint::current_user()?;
-    let helper = helper(executable, endpoint.id()).await?;
-    let (stdin, input_peer) = crate::windows::pipe::input().await?;
-    let (stdout, output_peer) = crate::windows::pipe::output().await?;
-    let (stderr, error_peer) = crate::windows::pipe::output().await?;
-    let job = Job::new()?;
-    let (helper_process, _) = crate::windows::spawn::launch(
-        &helper,
-        &job,
-        [&input_peer, &output_peer, &error_peer],
-        crate::windows::spawn::Console::None,
-    )?;
-    let (process, pid, owner) = admit(
-        endpoint,
-        helper_process,
-        &job,
-        proxy,
-        Start {
-            plan: plan.runner_plan(),
-            filesystem,
-            network,
-        },
-        &cancellation,
-    )
-    .await?;
-    Ok(pipe::Spawned {
-        child: pipe::Child::from_mxc(process, pid, job, owner),
-        stdin,
-        stdout,
-        stderr,
-    })
-}
-
-pub(crate) async fn terminal(
-    executable: PathBuf,
-    filesystem: Policy,
-    network: Network,
-    proxy: Option<Arc<maka_network::proxy::Proxy>>,
-    plan: crate::command::Prepared,
-    size: TerminalSize,
-) -> io::Result<(pty::PtyChild, pty::PtyIo)> {
-    let _directory = plan.directory();
-    let cancellation = plan.startup_cancellation();
-    let endpoint = Endpoint::current_user()?;
-    let helper = helper(executable, endpoint.id()).await?;
-    let (output, output_peer) = crate::windows::pipe::pty_output().await?;
-    let (input, input_peer) = crate::windows::pipe::pty_input().await?;
-    let job = Job::new()?;
-    let console = pty::windows::console::Console::new(size, &input_peer, &output_peer)?;
-    let launch = async {
-        let (helper_process, _) = pty::windows::spawn::launch(&helper, &console, &job)?;
-        admit(
+    let mut lease = Some(lease);
+    let result = async {
+        let endpoint = Endpoint::current_user()?;
+        let helper = helper(executable, endpoint.id()).await?;
+        let (stdin, input_peer) = crate::windows::pipe::input().await?;
+        let (stdout, output_peer) = crate::windows::pipe::output().await?;
+        let (stderr, error_peer) = crate::windows::pipe::output().await?;
+        let job = lease.as_ref().expect("preparation owns lease").job()?;
+        let (helper_process, _) = crate::windows::spawn::launch(
+            &helper,
+            &job,
+            [&input_peer, &output_peer, &error_peer],
+            crate::windows::spawn::Console::None,
+        )?;
+        let (process, pid, owner) = admit(
             endpoint,
             helper_process,
             &job,
-            proxy,
+            proxy.take(),
+            lease.take().expect("admission takes lease"),
             Start {
                 plan: plan.runner_plan(),
                 filesystem,
@@ -208,21 +181,85 @@ pub(crate) async fn terminal(
             },
             &cancellation,
         )
-        .await
+        .await?;
+        Ok(pipe::Spawned {
+            child: pipe::Child::from_mxc(process, pid, job, owner),
+            stdin,
+            stdout,
+            stderr,
+        })
     }
     .await;
-    match launch {
-        Ok((process, pid, owner)) => Ok(pty::windows::from_mxc(
-            process, pid, job, input, output, console, owner,
-        )),
-        Err(error) => {
-            let _ = output.disconnect();
-            tokio::task::spawn_blocking(move || drop(console))
-                .await
-                .map_err(io::Error::other)?;
-            Err(error)
+    if let Some(mut lease) = lease {
+        close_proxy(proxy).await.map_err(cleanup_error)?;
+        lease.finish().await.map_err(cleanup_error)?;
+    }
+    result
+}
+
+pub(crate) async fn terminal(
+    executable: PathBuf,
+    filesystem: Policy,
+    network: Network,
+    mut proxy: Option<Arc<maka_network::proxy::Proxy>>,
+    lease: Lease,
+    plan: crate::command::Prepared,
+    size: TerminalSize,
+) -> io::Result<(pty::PtyChild, pty::PtyIo)> {
+    let _directory = plan.directory();
+    let cancellation = plan.startup_cancellation();
+    let mut lease = Some(lease);
+    let result = async {
+        let endpoint = Endpoint::current_user()?;
+        let helper = helper(executable, endpoint.id()).await?;
+        let (output, output_peer) = crate::windows::pipe::pty_output().await?;
+        let (input, input_peer) = crate::windows::pipe::pty_input().await?;
+        let job = lease.as_ref().expect("preparation owns lease").job()?;
+        let console = pty::windows::console::Console::new(size, &input_peer, &output_peer)?;
+        let launch = async {
+            let (helper_process, _) = pty::windows::spawn::launch(&helper, &console, &job)?;
+            admit(
+                endpoint,
+                helper_process,
+                &job,
+                proxy.take(),
+                lease.take().expect("admission takes lease"),
+                Start {
+                    plan: plan.runner_plan(),
+                    filesystem,
+                    network,
+                },
+                &cancellation,
+            )
+            .await
+        }
+        .await;
+        match launch {
+            Ok((process, pid, owner)) => Ok(pty::windows::from_mxc(
+                process, pid, job, input, output, console, owner,
+            )),
+            Err(error) => {
+                let _ = output.disconnect();
+                tokio::task::spawn_blocking(move || drop(console))
+                    .await
+                    .map_err(io::Error::other)?;
+                Err(error)
+            }
         }
     }
+    .await;
+    if let Some(mut lease) = lease {
+        close_proxy(proxy).await.map_err(cleanup_error)?;
+        lease.finish().await.map_err(cleanup_error)?;
+    }
+    result
+}
+
+async fn close_proxy(proxy: Option<Arc<maka_network::proxy::Proxy>>) -> io::Result<()> {
+    if let Some(mut proxy) = proxy.and_then(Arc::into_inner) {
+        proxy.close().await?;
+    }
+    Ok(())
 }
 
 async fn helper(executable: PathBuf, endpoint: uuid::Uuid) -> io::Result<crate::command::Prepared> {
@@ -247,6 +284,7 @@ async fn admit(
     helper: OwnedHandle,
     job: &Job,
     proxy: Option<Arc<maka_network::proxy::Proxy>>,
+    mut lease: Lease,
     request: Start,
     cancellation: &tokio_util::sync::CancellationToken,
 ) -> io::Result<(OwnedHandle, u32, Owner)> {
@@ -291,6 +329,7 @@ async fn admit(
                     job: execution_job,
                     channel,
                     proxy,
+                    lease,
                     terminated: terminated.clone(),
                 }),
                 terminated,
@@ -300,12 +339,14 @@ async fn admit(
         Err(error) => {
             let cleanup = async {
                 job.terminate(130)?;
+                // Termination is asynchronous; ActiveProcesses == 0 alone must
+                // not publish admission cancellation while this helper is alive.
+                wait_process(&helper).await?;
                 while !job.is_empty()? {
                     tokio::time::sleep(Duration::from_millis(10)).await;
                 }
-                if let Some(mut proxy) = proxy.take().and_then(Arc::into_inner) {
-                    proxy.close().await?;
-                }
+                close_proxy(proxy.take()).await?;
+                lease.finish().await?;
                 Ok::<_, io::Error>(())
             }
             .await;
diff --git a/platform/process/src/bootstrap/mxc/lease.rs b/platform/process/src/bootstrap/mxc/lease.rs
new file mode 100644
index 0000000000..302c98ac70
--- /dev/null
+++ b/platform/process/src/bootstrap/mxc/lease.rs
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+//! Recoverable Host filesystem authority attached to one native command tree.
+use crate::{command::Cleanup, windows::job::Job};
+use maka_sandbox::windows::ExecutionJob;
+use std::{io, os::windows::io::AsHandle};
+
+/// The Host binds recoverable guards to this exact named execution Job before
+/// launch. The process owner retains the lease through native and proxy drain.
+pub struct Lease {
+    job: ExecutionJob,
+    cleanup: Cleanup,
+}
+impl Lease {
+    pub fn new(
+        job: ExecutionJob,
+        cleanup: impl FnOnce() -> io::Result<()> + Send + Sync + 'static,
+    ) -> Self {
+        Self {
+            job,
+            cleanup: Cleanup::new(cleanup),
+        }
+    }
+
+    pub(super) fn job(&self) -> io::Result<Job> {
+        Ok(Job(self.job.as_handle().try_clone_to_owned()?))
+    }
+
+    /// Roll back a preparation that has not launched, or finish after the native
+    /// owner has observed every process fence and drained its proxy. The private
+    /// Job handle cannot be used to launch through this lease after transfer.
+    pub async fn finish(&mut self) -> io::Result<()> {
+        if !self.job()?.is_empty()? {
+            return Err(io::Error::new(
+                io::ErrorKind::WouldBlock,
+                "MXC execution is still running",
+            ));
+        }
+        self.cleanup.finish().await
+    }
+}
diff --git a/platform/sandbox/README.md b/platform/sandbox/README.md
index 1e35dd18b4..b1b181f8ee 100644
--- a/platform/sandbox/README.md
+++ b/platform/sandbox/README.md
@@ -25,13 +25,17 @@ Prepares authorized commands for operating-system isolation. The Host decides pe
 
 macOS uses Seatbelt; Linux uses bubblewrap and network namespaces. Linux requires bubblewrap 0.11 or newer at `/usr/bin/bwrap`. Unsupported policies fail before execution.
 
-Windows prefers [Microsoft MXC](https://github.com/microsoft/mxc), using a native process security environment for each command tree. Maka probes actual creation and startup support, then checks the filesystem policy's requirements. Supported commands need no UAC prompt, dedicated account, host ACL mutation or `sandbox setup`. The execution owner retains isolation after the root process exits, until its background processes are settled. Read-only, workspace-write and disabled sandbox cho [...]
+Windows prefers [Microsoft MXC](https://github.com/microsoft/mxc), using a native process security environment for each command tree. Maka probes actual creation and startup support, then checks the filesystem policy's requirements. Supported commands need no UAC prompt, dedicated account, changes to existing objects' ACLs or `sandbox setup`. The execution owner retains isolation after the root process exits, until its background processes are settled. Read-only, workspace-write and disa [...]
+
+For read-only exceptions inside writable trees, the Host reuses recoverable filesystem guards: it creates temporary placeholders for missing paths and pins boundaries and ancestors against renaming. One protected registry per Windows account owns their staging and recovery records. Publishing a placeholder requires a same-volume atomic move that cannot replace an existing object; an unavailable capability is rejected before execution. After the command tree settles, cleanup removes only  [...]
 
 Only unsupported capabilities or policies select the existing low-privilege-account backend, which requires explicit `maka sandbox setup --root PATH` and uses WFP rules and Jobs. A failed MXC launch or cleanup is reported; it never retries the command with another backend or without isolation. The Host remains unelevated.
 
 Destination-restricted traffic passes through an execution-owned gateway. Routing and credentials stay with the Host, and redirects need their own destination permission. Linux and Windows expand deny globs before launch; future matching paths are covered on a subsequent launch, not by that existing snapshot.
 
-MXC networking follows Codex: `denied` blocks network access including IPv4/IPv6 loopback. `restricted` allows local clients and servers on `127.0.0.0/8` and `::1`; remote destinations must pass through the execution-owned proxy, which enforces the granted destination host and port. Thus the restricted policy permits access to local services beyond the proxy itself. `allowed` permits normal networking.
+Filesystem permissions apply to paths. MXC tightens conflicting object aliases listed in the policy; it does not scan writable trees for pre-existing, unlisted hard links.
+
+MXC networking follows Codex: `denied` blocks network access including IPv4/IPv6 loopback. `restricted` permits Windows host-loopback access, including `127.0.0.0/8`, `::1` and the host's other local interface addresses. Remote destinations must pass through the execution-owned proxy, which enforces the granted destination host and port. Thus the restricted policy permits access to local services beyond the proxy itself. `allowed` permits normal networking.
 
 To exercise a policy with the production backend:
 
@@ -53,7 +57,7 @@ The native Windows acceptance fixture builds the real Host backend and process t
 cargo build -p maka-host-runtime --example windows_mxc --target x86_64-pc-windows-msvc
 ```
 
-Run the resulting `windows_mxc.exe` from a Windows local path as a normal user. It creates and removes its own `LOCALAPPDATA` temporary directory and verifies filesystem restrictions, arguments and environment, pipe and ConPTY I/O, descendant lifetime, cancellation, and networking. Unavailable MXC support is a failure, not a skipped check.
+Run the resulting `windows_mxc.exe` from a Windows local path as a normal user. It creates and removes its own `LOCALAPPDATA` temporary directory and verifies filesystem restrictions, arguments and environment, pipe and ConPTY I/O, descendant lifetime, cancellation, and networking. Its network checks require the unsandboxed host to reach `example.com:443` over TCP as a positive control; no HTTP request is sent. Unavailable MXC support is a failure, not a skipped check.
 
 ## Source provenance
 `src/seatbelt/{base,network}.sbpl` are copied unchanged from Codex's
diff --git a/platform/sandbox/README.zh-CN.md b/platform/sandbox/README.zh-CN.md
index bc5b92998a..022de47b3b 100644
--- a/platform/sandbox/README.zh-CN.md
+++ b/platform/sandbox/README.zh-CN.md
@@ -25,13 +25,17 @@
 
 macOS 使用 Seatbelt,Linux 使用 bubblewrap 和网络命名空间。Linux 需要 `/usr/bin/bwrap` 提供 bubblewrap 0.11 或更新版本。不支持的策略在执行前失败。
 
-Windows 优先使用 [Microsoft MXC](https://github.com/microsoft/mxc),为每个命令树创建原生进程安全环境。Maka 实际探测创建和启动能力,再检查文件系统策略的具体要求。支持的命令不需要 UAC 提权、专用账户、修改宿主 ACL 或执行 `sandbox setup`。根进程退出后,执行所有者继续持有隔离,直到后台后代进程完成清理。只读、工作区可写及关闭沙箱三种选择保持现有含义。
+Windows 优先使用 [Microsoft MXC](https://github.com/microsoft/mxc),为每个命令树创建原生进程安全环境。Maka 实际探测创建和启动能力,再检查文件系统策略的具体要求。支持的命令不需要 UAC 提权、专用账户、修改已有对象的 ACL 或执行 `sandbox setup`。根进程退出后,执行所有者继续持有隔离,直到后台后代进程完成清理。只读、工作区可写及关闭沙箱三种选择保持现有含义。
+
+对于可写目录树中的只读例外,Host 复用可恢复的文件保护机制:为缺失路径创建临时占位对象,并固定权限边界及其祖先,防止重命名。每个 Windows 账户使用唯一的受保护注册目录,保存暂存对象和恢复记录。发布占位对象需要同卷原子移动,且不能替换已有对象;不具备该能力时,在执行前拒绝策略。命令树完成清理后,仅回收这些保护机制自己创建且仍为空的对象。
 
 只有能力或策略不支持时,才选择现有低权限账户后端;该后端需要显式执行 `maka sandbox setup --root PATH`,并使用 WFP 规则和 Job。MXC 启动或清理失败会直接报错,不会换后端重试命令,也不会无隔离执行。Host 保持非提权运行。
 
 限制目标的网络流量经过执行所有者持有的网关。路由和凭据留在 Host,重定向目标需要独立权限。Linux 与 Windows 在启动前展开拒绝 glob;之后新增的匹配路径由下次启动覆盖,不属于已有快照。
 
-MXC 网络语义与 Codex 对齐:`denied` 禁止网络访问,包括 IPv4/IPv6 回环;`restricted` 允许访问 `127.0.0.0/8` 与 `::1` 上的本地客户端和服务,远程目标必须经过执行所有者持有的代理,由代理检查已授权的目标主机与端口。因此,受限策略允许访问代理以外的本地服务。`allowed` 允许正常联网。
+文件系统权限按路径应用。MXC 会收紧策略中明确列出的对象别名冲突,不会扫描可写目录树中预先存在、未列入策略的硬链接。
+
+MXC 网络语义与 Codex 对齐:`denied` 禁止网络访问,包括 IPv4/IPv6 回环;`restricted` 允许 Windows 的宿主回环访问,包括 `127.0.0.0/8`、`::1` 及宿主的其他本地接口地址。远程目标必须经过执行所有者持有的代理,由代理检查已授权的目标主机与端口。因此,受限策略允许访问代理以外的本地服务。`allowed` 允许正常联网。
 
 通过实际后端测试策略:
 
@@ -53,7 +57,7 @@ Windows 的 `--root` 选择 Host 状态目录;MXC 支持的策略不要求在
 cargo build -p maka-host-runtime --example windows_mxc --target x86_64-pc-windows-msvc
 ```
 
-将生成的 `windows_mxc.exe` 放在 Windows 本地路径,以普通用户身份运行。它在 `LOCALAPPDATA` 创建并清理自己的临时目录,验证文件系统限制、参数与环境、管道及 ConPTY I/O、后代进程生命周期、取消和网络。MXC 不可用会使验证失败,不会跳过检查。
+将生成的 `windows_mxc.exe` 放在 Windows 本地路径,以普通用户身份运行。它在 `LOCALAPPDATA` 创建并清理自己的临时目录,验证文件系统限制、参数与环境、管道及 ConPTY I/O、后代进程生命周期、取消和网络。网络检查要求无沙箱的宿主能够通过 TCP 连接 `example.com:443`,作为正向对照,不发送 HTTP 请求。MXC 不可用会使验证失败,不会跳过检查。
 
 Seatbelt 策略的上游版本、版权和许可证记录见[英文版来源说明](README.md#source-provenance)。
 
diff --git a/platform/sandbox/src/filesystem.rs b/platform/sandbox/src/filesystem.rs
index ee60d87082..bd4d522a72 100644
--- a/platform/sandbox/src/filesystem.rs
+++ b/platform/sandbox/src/filesystem.rs
@@ -201,6 +201,21 @@ impl Compiled {
         }
         self.path_access(path, true)
     }
+    /// Authority inherited before this rule applies. An exact rule can override
+    /// a subtree grant at the same path; a subtree rule inherits only its parents.
+    pub fn inherited_access(&self, rule: &Rule) -> Access {
+        self.policy
+            .rules
+            .iter()
+            .filter(|ancestor| {
+                ancestor.scope == Scope::Subtree
+                    && crate::path::within(&rule.path, &ancestor.path)
+                    && (rule.scope == Scope::Exact
+                        || !crate::path::compare(&rule.path, &ancestor.path).is_eq())
+            })
+            .max_by_key(|ancestor| ancestor.path.components().count())
+            .map_or(self.policy.default, |ancestor| ancestor.access)
+    }
     /// Directory mutations affect descendants too. Unknown future glob matches
     /// cannot authorize moving a subtree; callers must keep those names in place.
     pub fn permits_subtree(&self, path: &Path, access: Access) -> bool {
@@ -243,6 +258,42 @@ pub(crate) fn compile_glob(pattern: &str) -> Result<globset::Glob, Error> {
 mod tests {
     use super::*;
 
+    #[test]
+    fn inherited_authority_uses_subtree_rules_instead_of_exact_parent_access() {
+        let directory = tempfile::tempdir().unwrap();
+        let root = directory.path();
+        let policy = Policy {
+            default: Access::Read,
+            rules: vec![
+                Rule::subtree(root, Access::Write),
+                Rule::exact(root, Access::Read),
+                Rule::subtree(root.join("metadata"), Access::Read),
+                Rule::subtree(root.join("metadata/output"), Access::Write),
+                Rule::exact(root.join("file"), Access::Read),
+            ],
+            deny_globs: Vec::new(),
+        }
+        .compile()
+        .unwrap();
+        assert_eq!(policy.access(root), Access::Read);
+        assert_eq!(policy.access(&root.join("ordinary")), Access::Write);
+        for (path, scope, inherited) in [
+            (root.to_path_buf(), Scope::Subtree, Access::Read),
+            (root.to_path_buf(), Scope::Exact, Access::Write),
+            (root.join("metadata"), Scope::Subtree, Access::Write),
+            (root.join("metadata/output"), Scope::Subtree, Access::Read),
+            (root.join("file"), Scope::Exact, Access::Write),
+        ] {
+            let rule = policy
+                .policy()
+                .rules
+                .iter()
+                .find(|rule| rule.path == path && rule.scope == scope)
+                .unwrap();
+            assert_eq!(policy.inherited_access(rule), inherited, "{path:?}");
+        }
+    }
+
     #[test]
     fn normalization_removes_noop_boundaries_without_erasing_nested_reopenings() {
         let directory = tempfile::tempdir().unwrap();
diff --git a/platform/sandbox/src/windows/acl/target.rs b/platform/sandbox/src/windows/acl/target.rs
index 54bdba43ee..3e6d310cfc 100644
--- a/platform/sandbox/src/windows/acl/target.rs
+++ b/platform/sandbox/src/windows/acl/target.rs
@@ -51,19 +51,40 @@ pub enum Removal {
 
 impl Target {
     pub fn capture(path: &Path) -> io::Result<(Self, File)> {
-        Self::capture_shared(path, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE)
+        Self::capture_shared(
+            path,
+            FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+            READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES,
+        )
     }
 
     /// Pin a protection boundary against rename/deletion until native execution
     /// settles. Independent executions may retain their own read-shared pins.
     pub fn pin(path: &Path) -> io::Result<(Self, File)> {
-        Self::capture_shared(path, FILE_SHARE_READ | FILE_SHARE_WRITE)
+        Self::capture_shared(
+            path,
+            FILE_SHARE_READ | FILE_SHARE_WRITE,
+            READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES,
+        )
     }
 
-    fn capture_shared(path: &Path, share: FILE_SHARE_MODE) -> io::Result<(Self, File)> {
+    /// Pin a native filesystem boundary without acquiring ACL mutation rights.
+    pub fn pin_read(path: &Path) -> io::Result<(Self, File)> {
+        Self::capture_shared(
+            path,
+            FILE_SHARE_READ | FILE_SHARE_WRITE,
+            READ_CONTROL | FILE_READ_ATTRIBUTES,
+        )
+    }
+
+    fn capture_shared(
+        path: &Path,
+        share: FILE_SHARE_MODE,
+        access: FILE_ACCESS_RIGHTS,
+    ) -> io::Result<(Self, File)> {
         crate::path::validate(path).map_err(io::Error::other)?;
         let file = OpenOptions::new()
-            .access_mode(READ_CONTROL | WRITE_DAC | FILE_READ_ATTRIBUTES)
+            .access_mode(access)
             .share_mode(share)
             .custom_flags(FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT)
             .open(path)?;
@@ -95,7 +116,13 @@ impl Target {
         };
         // A file-ID query alone does not prove that this filesystem supports
         // reopening it. Establish that while the original object is still live.
-        if target.reopen()?.is_none() {
+        if target
+            .reopen_shared(
+                access,
+                FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
+            )?
+            .is_none()
+        {
             return Err(io::Error::new(
                 io::ErrorKind::Unsupported,
                 "filesystem cannot recover this object's persistent identity",
diff --git a/platform/sandbox/src/windows/mxc/availability.rs b/platform/sandbox/src/windows/mxc/availability.rs
index e5d8cd2dc2..2a7e6dbc5c 100644
--- a/platform/sandbox/src/windows/mxc/availability.rs
+++ b/platform/sandbox/src/windows/mxc/availability.rs
@@ -35,22 +35,17 @@ pub fn is_available() -> bool {
     static USABLE: OnceLock<bool> = OnceLock::new();
     *USABLE.get_or_init(|| {
         let policy = ContainerPolicy::default();
-        let specification = ProcessSecurityEnvironmentT {
-            version: SchemaVersionT { major: 1, minor: 0 },
-            disallow_win32k_system_calls: policy.ui.disable,
-            ui_restrictions: u64::from(to_job_object_uilimit_mask(&resolve_ui_restrictions(
-                &policy.ui,
-                &policy.base_process_ui,
-            ))),
-            network_policy: Some(Box::new(NetworkPolicyT {
-                egress: Some(Box::new(EndpointPolicyT {
-                    default_action: FilterAction::deny,
-                    ..Default::default()
-                })),
-                ..Default::default()
-            })),
-            ..Default::default()
-        };
+        let mut egress = EndpointPolicyT::default();
+        egress.default_action = FilterAction::deny;
+        let mut network = NetworkPolicyT::default();
+        network.egress = Some(Box::new(egress));
+        let mut specification = ProcessSecurityEnvironmentT::default();
+        specification.version = SchemaVersionT { major: 1, minor: 0 };
+        specification.disallow_win32k_system_calls = policy.ui.disable;
+        specification.ui_restrictions = u64::from(to_job_object_uilimit_mask(
+            &resolve_ui_restrictions(&policy.ui, &policy.base_process_ui),
+        ));
+        specification.network_policy = Some(Box::new(network));
         let mut builder = FlatBufferBuilder::new();
         let specification = specification.pack(&mut builder);
         finish_process_security_environment_buffer(&mut builder, specification);
diff --git a/platform/sandbox/src/windows/mxc/policy.rs b/platform/sandbox/src/windows/mxc/policy.rs
index 4770e94b5c..88d4b63915 100644
--- a/platform/sandbox/src/windows/mxc/policy.rs
+++ b/platform/sandbox/src/windows/mxc/policy.rs
@@ -88,9 +88,9 @@ fn filesystem_policy(
         }
     }
     for rule in &filesystem.rules {
-        if rule.scope == Scope::Exact {
+        if rule.scope == Scope::Exact || rule.access != Access::Deny {
             match fs::metadata(&rule.path) {
-                Ok(metadata) if metadata.is_dir() => {
+                Ok(metadata) if rule.scope == Scope::Exact && metadata.is_dir() => {
                     return Err(Error::Unsupported(
                         "native MXC cannot express an exact directory permission".into(),
                     ));
@@ -101,9 +101,9 @@ fn filesystem_policy(
                             "MXC policy contains a dangling filesystem link".into(),
                         ));
                     }
-                    if rule.access == Access::Write {
+                    if rule.access != Access::Deny {
                         return Err(Error::Unsupported(
-                            "native MXC cannot grant an exact missing file path".into(),
+                            "native MXC ignores missing filesystem grants; prepare existing boundaries before launch".into(),
                         ));
                     }
                 }
diff --git a/plugins/workspace/workspace-files/engine/src/workspace/permissions.rs b/plugins/workspace/workspace-files/engine/src/workspace/permissions.rs
index 42168414a7..f4ca25eb28 100644
--- a/plugins/workspace/workspace-files/engine/src/workspace/permissions.rs
+++ b/plugins/workspace/workspace-files/engine/src/workspace/permissions.rs
@@ -32,9 +32,11 @@ pub fn resolve(mode: Mode, cwd: &Path) -> Result<(Sandbox, Sandbox), Error> {
     let mut filesystem = Policy::uniform(Access::Read);
     if mode == Mode::WorkspaceWrite {
         filesystem.rules.push(Rule::subtree(cwd, Access::Write));
-        let mut temporary = vec![std::env::temp_dir()];
-        #[cfg(unix)]
-        temporary.push(PathBuf::from("/tmp"));
+        let temporary = [
+            std::env::temp_dir(),
+            #[cfg(unix)]
+            PathBuf::from("/tmp"),
+        ];
         for path in temporary {
             let path = path.canonicalize()?;
             let path = PathBuf::from(


---------------------------------------------------------------------
To unsubscribe, e-mail: commits-unsubscribe@maka.apache.org
For additional commands, e-mail: commits-help@maka.apache.org