Complete guide to process, resource, and filesystem isolation in Hexe using libvoid.
Hexe uses libvoid for Linux sandboxing with three types of isolation:
- Process Isolation - Namespaces (user, PID, mount, network, UTS, IPC)
- Resource Isolation - Cgroups (CPU, memory, process limits)
- Filesystem Isolation - Chroot-style bind mounts with private /tmp
Filesystem isolation works by creating a chroot-style environment using bind mounts inside a mount namespace. Instead of the traditional chroot() syscall, libvoid selectively bind-mounts only the directories each profile needs into a fresh mount namespace — giving you a restricted root filesystem view without the security pitfalls of classic chroot.
No isolation - Normal operation with full system access.
- ❌ No namespaces
- ❌ No resource limits
- ❌ No filesystem restrictions
- ✅ Full access to all processes, files, network
Use case: Trusted code, normal shell work
Light isolation - Basic privilege separation.
Process:
- ✅ User namespace (different UID/GID mapping)
Filesystem:
- ❌ No filesystem isolation (no bind mounts, no private /tmp)
Use case: Basic privilege separation, semi-trusted tools
What you'll see:
whoami # Different UID mapping
ls / # Full filesystem (no restrictions)Process + mount isolation - Chroot-style filesystem with separate mount table.
defaultandbalancedare equivalent profiles.
Process:
- ✅ User namespace
- ✅ PID namespace (can't see other processes)
- ✅ Mount namespace (separate mount table)
Filesystem (chroot-style bind mounts):
- ✅ Private
/tmp(tmpfs) - ✅ Read-only:
/bin,/usr,/lib,/lib64,/etc - ✅ Read-only:
/nix,/pkg,/opt,/run/current-system(package managers) - ✅ Read-write: Your
/home/<user>directory - ✅ Fresh
/proc,/dev
Use case: Isolated environment for development/testing
What you'll see:
ps aux # Only shows processes in this pane!
ls /home # Only your user directory
ls / # Only: bin, usr, lib, etc, nix, pkg, opt, home/<you>, tmp
mount # Separate mount table (mounts don't leak to host)Full isolation WITH network - Secure but functional.
Process:
- ✅ User namespace
- ✅ PID namespace
- ✅ Mount namespace
- ❌ Network namespace (network allowed!)
- ✅ UTS namespace (hostname set to
hexe) - ✅ IPC namespace (isolated shared memory)
Security:
- ✅
no_new_privs(can't escalate privileges) - ✅ Capabilities dropped
Filesystem (chroot-style bind mounts):
- ✅ Private
/tmp(tmpfs) - ✅ Read-only:
/bin,/usr,/lib,/lib64,/etc - ✅ Read-only:
/nix,/pkg,/opt,/run/current-system(package managers) - ✅ Read-write: Your
/home/<user>directory - ✅ Fresh
/proc,/dev
Resources (if configured):
- ✅ Memory limit (e.g., 512M)
- ✅ CPU limit (e.g., 0.5 cores)
- ✅ Process limit (e.g., 100 PIDs)
Use case: Run untrusted code that needs internet (downloading packages, API calls)
What you'll see:
ps aux # Only this pane's processes
curl example.com # Network works!
ls / # Only: bin, usr, lib, etc, nix, pkg, opt, home/<you>, tmp
touch /tmp/test # Private tmp
hostname # "hexe" (isolated hostname)Maximum isolation - Restricted access, no network.
Process:
- ✅ User namespace
- ✅ PID namespace
- ✅ Mount namespace
- ✅ Network namespace (NO NETWORK!)
- ✅ UTS namespace (hostname set to
hexe) - ✅ IPC namespace
Security:
- ✅
no_new_privs(can't escalate privileges) - ✅ Capabilities dropped
Filesystem (chroot-style bind mounts):
- ✅ Private
/tmp(tmpfs) - ✅ Read-only:
/bin,/usr,/lib,/lib64,/etc - ❌ NO
/nix,/pkg,/opt,/run/current-system - ✅ Read-write: Your
/home/<user>directory - ✅ Fresh
/proc,/dev
Resources (if configured):
- ✅ Memory limit
- ✅ CPU limit
- ✅ Process limit
Use case: Maximum security for highly untrusted code
What you'll see:
ps aux # Only this pane's processes
ping 8.8.8.8 # Network unreachable!
ls / # Only: bin, usr, lib, lib64, etc, home/<you>, tmp
curl example.com # Fails - no network# Spawn isolated float with sandbox profile
hexe mux float --command "zsh" --isolation=sandbox
# Full isolation (no network, minimal filesystem)
hexe mux float --command "zsh" --isolation=full
# With size and title
hexe mux float --command "zsh" \
--isolation=sandbox \
--title="Isolated Shell" \
--size "80,60,0,0"
# Run untrusted script
hexe mux float --command "bash /tmp/untrusted.sh" --isolation=fullConfigure isolation for specific floats in your ~/.config/hexe/init.lua:
hx.ses.layout.define({
name = "default",
floats = {
{
key = "0",
enabled = true,
title = "sandbox",
isolation = {
profile = "sandbox", -- Isolation profile
memory = "512M", -- Memory limit
pids = 100, -- Max processes
cpu = "50000 100000", -- 0.5 CPU cores
},
},
},
})Isolation fields:
profile: Profile name (none,minimal,default/balanced,sandbox,full)memory: Memory limit (e.g., "1G", "512M", "256M") — sandbox/full onlycpu: CPU quota as "period max" (e.g., "100000 100000" = 1 core, "50000 100000" = 0.5 cores) — sandbox/full onlypids: Maximum number of processes (e.g., 100, 1000) — sandbox/full only
Control CPU, memory, and process limits:
# 512MB limit
hexe mux float --command "zsh" --isolation=sandbox # Uses config
# In Lua config:
isolation = {
profile = "sandbox",
memory = "512M", -- Killed if exceeded
}Test it:
# This will be killed when it exceeds 512M
python3 -c "a = 'x' * (1024**3)" # Try to allocate 1GB# 0.5 cores max
isolation = {
profile = "sandbox",
cpu = "50000 100000", -- 50% of one core
}Format: period max
"100000 100000"= 1 full core"50000 100000"= 0.5 cores"200000 100000"= 2 cores
# Max 100 processes
isolation = {
profile = "sandbox",
pids = 100,
}Test it:
# This will fail after 100 forks
:(){ :|:& };: # Fork bomb (safely contained!)Profiles with mount namespace (default/balanced, sandbox, full) use chroot-style bind mounts to construct a restricted root filesystem. Only explicitly listed directories are visible inside the isolated pane.
Read-only mounts (safe, can't modify):
/bin,/usr,/lib,/lib64,/etc- System binaries and configuration/nix,/pkg,/opt,/run/current-system- Package managers (not infull)
Read-write mounts (you can modify):
/home/<youruser>- Your home directory
Fresh mounts (generated per-pane):
/tmp- Private tmpfs/proc- Namespace-filtered process list/dev- Filtered device nodes
Note:
minimalhas NO filesystem isolation — it only creates a user namespace.
What's Hidden?
In chroot-style isolation, only bind-mounted paths exist. These are not visible:
/root- Root home directory/home/<otheruser>- Other users' home directories/sys- System information/var,/srv,/mnt- Other system directories/procshows only your processes (PID namespace)
Every isolated pane (except minimal) gets its own /tmp via tmpfs:
# Pane 1:
touch /tmp/secret.txt
ls /tmp # Shows secret.txt
# Pane 2 (isolated):
ls /tmp # Empty! Can't see Pane 1's fileshexe mux float --command "zsh" \
--isolation=sandbox \
--title="Dev Sandbox"
# Inside:
npm install # Network works, downloads packages
ls /tmp # Private temp directory
ps aux # Only sees npm processes# Full isolation - no network, restricted filesystem
hexe mux float --command "python3 /tmp/untrusted.py" \
--isolation=full
# The script:
# - Can't access network
# - Can't see other processes
# - Can't see /nix, /pkg, /opt
# - Limited to configured RAM (if set)
# - Can't fork bomb (PID limit)-- In init.lua
{
key = "b",
title = "builder",
command = "/usr/bin/make",
isolation = {
profile = "balanced",
memory = "2G",
cpu = "200000 100000", -- 2 cores
pids = 500,
},
}Press Ctrl+Alt+B to open isolated build environment.
✅ Process containment - Can't see/kill other processes
✅ Resource limits - Can't exhaust system memory/CPU (sandbox/full)
✅ Chroot-style filesystem - Only bind-mounted paths are visible
✅ Network isolation (full profile) - Can't make network connections
✅ Privilege restriction - no_new_privs prevents privilege escalation
❌ Kernel exploits - Still shares kernel with host ❌ Side-channel attacks - Spectre/Meltdown not mitigated ❌ Complete security - Not a VM or hardware virtualization
For maximum security: Use VMs, containers with kernel isolation, or dedicated hardware.
[pty-child] Voidbox isolation failed: error.PermissionDenied
Cause: User namespaces disabled or insufficient permissions.
Fix:
# Check if user namespaces are enabled
sysctl kernel.unprivileged_userns_clone
# Enable (requires root)
sudo sysctl -w kernel.unprivileged_userns_clone=1Issue: Files outside /home are not accessible.
Cause: Chroot-style bind mounts only expose specific directories. Paths like /var, /srv, /mnt are not mounted.
Fix: Use none or minimal profile for full filesystem access, or add custom bind mounts.
Issue: ping, curl fail with "network unreachable".
Cause: Using full profile which blocks network.
Fix: Use sandbox profile for network access with isolation.
Issue: ls, cat, etc. not found.
Cause: /bin not mounted (shouldn't happen with provided profiles).
Fix: Check profile includes /bin bind mount, or use default/sandbox profile.
For advanced users, libvoid supports custom filesystem actions via environment variables (future feature) or direct libvoid configuration.
Future enhancement: Use overlayfs for ephemeral root filesystem where changes don't persist between sessions.
Future enhancement: Custom network namespaces with virtual interfaces for controlled internet access.
| Feature | Hexe Isolation | Docker/Podman |
|---|---|---|
| Startup Time | <10ms | ~100-500ms |
| Overhead | Minimal | Container runtime |
| Filesystem | Chroot-style bind mounts | Image layers |
| Process Isolation | PID namespace | Full container |
| Network | Optional | Virtual networks |
| Use Case | Fast task isolation | Full application containers |
When to use Hexe: Quick isolation for shell tasks, scripts, development. When to use Docker: Deploying applications, reproducible environments, complex networking.
- Libvoid Documentation - Underlying sandboxing library
- Linux Namespaces - Kernel isolation primitives
- Cgroups v2 - Resource limits