Skip to main content
Nash achieves total sandboxing through structural enforcement, not just policy. The runtime is architecturally incapable of executing system commands or accessing the host filesystem without explicit permission.

No System Command Execution

Nash never spawns real processes or calls the operating system shell. The codebase completely avoids std::process::Command.
grep -r "std::process\|Command::new\|bash -c" src/
# (no output)
All 28 built-in commands are implemented in Rust and executed in-memory. There is no way to shell out to the host OS.

Virtual Filesystem Isolation

Every file operation goes through the Vfs API, which maintains an in-memory directory tree. Host paths are completely unreachable unless explicitly mounted.
VFS structure
// src/vfs/mod.rs
pub struct Vfs {
    nodes: HashMap<String, FsNode>,  // In-memory file tree
    mounts: Vec<MountPoint>,         // Explicit host bindings
}
File operations are dispatched to the VFS:
File I/O through VFS
pub fn read(&self, path: &str) -> Result<Vec<u8>> {
    let p = VfsPath::normalize(path);
    
    // Try in-memory nodes first
    if let Some(node) = self.nodes.get(&p) {
        match node {
            FsNode::File(data) => return Ok(data.clone()),
            FsNode::Directory(_) => bail!("is a directory: {}", path),
        }
    }
    
    // Fall back to host mount only if configured
    if let Some(mp) = self.find_mount(&p) {
        let host_path = /* map VFS path to host */;
        return std::fs::read(&host_path);
    }
    
    bail!("no such file: {}", path)
}
Without a mount binding, Nash commands cannot read, write, or even detect the existence of host files.

Read-Only Mount Protection

When directories are mounted with --bind-ro, writes are rejected at the VFS layer—before any command logic runs.
Write protection
fn check_write_allowed(&self, vfs_path: &str) -> Result<()> {
    if let Some(mp) = self.find_mount(vfs_path) {
        if mp.opts.read_only {
            bail!("filesystem is read-only: {}", vfs_path);
        }
    }
    Ok(())
}

pub fn write(&mut self, path: &str, data: Vec<u8>) -> Result<()> {
    let p = VfsPath::normalize(path);
    self.check_write_allowed(&p)?;  // Enforced here
    // ... proceed with write
}

Example

Read-only enforcement
nash --bind-ro ./config:/config

user@nash:/home/user$ echo "hack" > /config/settings.toml
Error: filesystem is read-only: /config/settings.toml

user@nash:/home/user$ cat /config/settings.toml
# Original file content is readable

Subshell Context Isolation

Subshells ( ) run on a cloned context. Environment variable changes and directory changes inside the subshell do not escape.
Subshell isolation
Expr::Subshell { expr } => {
    // Save current state
    let saved_cwd = self.ctx.cwd.clone();
    let saved_env = self.ctx.env.clone();
    
    // Execute subshell
    let result = self.eval(expr, stdin);
    
    // Restore state (env changes don't propagate)
    self.ctx.cwd = saved_cwd;
    self.ctx.env = saved_env;
    
    result
}

Example

Environment isolation
user@nash:/home/user$ export TESTVAR=before
user@nash:/home/user$ echo $TESTVAR
before

user@nash:/home/user$ (export TESTVAR=inside; echo $TESTVAR)
inside

user@nash:/home/user$ echo $TESTVAR
before
Directory changes inside a subshell also do not persist:
(cd /tmp && pwd)  # prints /tmp
pwd               # still prints /home/user

Security Verification

You can audit Nash’s sandboxing guarantees by inspecting the source:
Check for system calls
# Should return no results
grep -r "std::process" src/
grep -r "Command::new" src/
grep -r "std::process::Command" src/
Check VFS enforcement
# All file operations go through vfs/mod.rs
grep -rn "std::fs::read\|std::fs::write" src/ | grep -v "src/vfs/"
# Only vfs/mod.rs touches std::fs (for mounts)
Verify read-only checks
grep -n "check_write_allowed" src/vfs/mod.rs
# Line 127, 159, 241, 259, 348 — all write paths protected

Architecture Diagram

┌─────────────────────────────────────────┐
│          User Commands                  │
└────────────┬────────────────────────────┘


┌─────────────────────────────────────────┐
│    Executor (runtime/executor.rs)       │
│    • No std::process::Command           │
│    • All commands = Rust builtins       │
└────────────┬────────────────────────────┘


┌─────────────────────────────────────────┐
│       VFS API (vfs/mod.rs)              │
│    • In-memory file tree                │
│    • Mount point enforcement            │
│    • Read-only checks                   │
└────────────┬────────────────────────────┘


┌─────────────────────────────────────────┐
│    Host Filesystem (if mounted)         │
│    • Only accessible via --bind         │
│    • Enforced read-only if --bind-ro    │
└─────────────────────────────────────────┘

Summary

Security LayerEnforcement Mechanism
No system commandsstd::process::Command never imported
Filesystem isolationAll I/O through Vfs API
Read-only mountsWrite rejection at VFS layer
Subshell isolationContext cloning before execution
Mount requirementHost paths unreachable without --bind

Build docs developers (and LLMs) love