Self-sovereign infrastructure platform with secure, encrypted NixOS deployments
Looking for the user guide? See the Agent Sandbox User Guide for CLI usage and workflows.
This document outlines the architecture of using MicroVMs to run AI coding agents (Claude Code, Gemini CLI, Codex) in isolated, fully autonomous environments without restrictions.
AI coding agents are powerful tools for software development, but running them on a host system presents security concerns:
MicroVMs provide a lightweight, ephemeral isolation boundary that allows agents to operate with full autonomy inside a sandboxed environment while protecting the host system.
┌─────────────────────────────────────────────────────────────────────┐
│ Host System │
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ nix develop shell │ │
│ │ │ │
│ │ ┌─────────────────────────────────────────────────────────┐ │ │
│ │ │ MicroVM (QEMU direct kernel boot) │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────────────┐ ┌─────────────────────────────┐ │ │ │
│ │ │ │ AI Agent │ │ Development Tools │ │ │ │
│ │ │ │ - claude-code │ │ - git, gh │ │ │ │
│ │ │ │ - gemini-cli │ │ - language runtimes │ │ │ │
│ │ │ │ - codex │ │ - build tools │ │ │ │
│ │ │ └─────────────────┘ └─────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ │ ┌─────────────────────────────────────────────────┐ │ │ │
│ │ │ │ Shared Workspace (9p/virtiofs mount) │ │ │ │
│ │ │ │ /workspace → host project directory │ │ │ │
│ │ │ └─────────────────────────────────────────────────┘ │ │ │
│ │ │ │ │ │
│ │ └─────────────────────────────────────────────────────────┘ │ │
│ │ │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
Inside the MicroVM, agents can operate without the typical safety restrictions:
This enables truly autonomous operation for tasks like:
# tests/microvm/agent.nix
{
pkgs,
config,
lib,
...
}: {
microvm = {
hypervisor = "qemu";
# Memory allocation for agent workloads
mem = 8192; # 8GB RAM
vcpu = 4; # 4 vCPUs
# Share the project directory with the guest
shares = [{
tag = "workspace";
source = "/path/to/project";
mountPoint = "/workspace";
proto = "virtiofs"; # or "9p" for broader compatibility
}];
# Network access for API calls and package downloads
interfaces = [{
type = "user";
id = "vm-agent";
}];
};
# AI Agent packages
environment.systemPackages = with pkgs; [
# AI Agents
claude-code # Anthropic's Claude Code CLI
# gemini-cli # Google's Gemini CLI (when packaged)
# codex-cli # OpenAI Codex CLI (when packaged)
# Development tools
git
gh # GitHub CLI
jq
ripgrep
fd
# Language runtimes (customize as needed)
nodejs
python3
rustc
cargo
go
# Build tools
gnumake
cmake
gcc
];
# Configure git for the agent
programs.git = {
enable = true;
config = {
user.name = "AI Agent";
user.email = "agent@microvm.local";
safe.directory = "/workspace";
};
};
# Auto-login and start agent on boot
services.getty.autologinUser = "agent";
users.users.agent = {
isNormalUser = true;
home = "/home/agent";
extraGroups = ["wheel"];
initialPassword = "agent";
};
# Passwordless sudo for agent operations
security.sudo.wheelNeedsPassword = false;
system.stateVersion = "25.05";
}
# flake.nix
{
inputs = {
nixpkgs.url = "github:NixOS/nixpkgs/nixos-25.05";
microvm.url = "github:astro/microvm.nix";
};
outputs = { self, nixpkgs, microvm }: let
system = "x86_64-linux";
pkgs = nixpkgs.legacyPackages.${system};
in {
nixosConfigurations.agent-vm = nixpkgs.lib.nixosSystem {
inherit system;
modules = [
microvm.nixosModules.microvm
./microvm/agent.nix
];
};
devShells.${system}.agent = pkgs.mkShell {
buildInputs = [
# The microvm runner
self.nixosConfigurations.agent-vm.config.microvm.declaredRunner
];
shellHook = ''
echo "Agent MicroVM development shell"
echo "Run: microvm-run"
'';
};
};
}
# Enter the development shell
nix develop .#agent
# Start the MicroVM
microvm-run
# The agent environment is now running
# Connect via serial console or SSH
# Inside the MicroVM
cd /workspace
# Start Claude Code with full autonomy
claude --dangerously-skip-permissions
# Or run a specific task
claude "Refactor all API endpoints to use async/await"
Create a wrapper script for fully autonomous operation:
#!/usr/bin/env bash
# bin/agent-task
set -euo pipefail
TASK="$1"
# Build and run the microvm with the task
nix develop .#agent --command bash -c "
microvm-run &
MICROVM_PID=\$!
# Wait for VM to boot
sleep 10
# Execute task via SSH or console
ssh -o StrictHostKeyChecking=no agent@localhost -p 2222 \\
'cd /workspace && claude --dangerously-skip-permissions \"$TASK\"'
# Shutdown VM
kill \$MICROVM_PID
"
Usage:
./bin/agent-task "Add comprehensive test coverage to the auth module"
microvm = {
# No interfaces = no network
interfaces = [];
};
microvm = {
interfaces = [{
type = "user";
id = "vm-net";
}];
};
microvm = {
interfaces = [{
type = "bridge";
id = "vm-br";
bridge = "br0";
}];
};
Mount a secrets directory or use environment variables:
microvm = {
shares = [
{
tag = "workspace";
source = "/path/to/project";
mountPoint = "/workspace";
proto = "virtiofs";
}
{
tag = "secrets";
source = "/run/secrets/agent"; # tmpfs with API keys
mountPoint = "/run/secrets";
proto = "virtiofs";
}
];
};
Or pass via environment:
ANTHROPIC_API_KEY="sk-..." microvm-run
For git operations requiring authentication:
{
# Generate ephemeral SSH key on boot
systemd.services.setup-ssh = {
wantedBy = ["multi-user.target"];
serviceConfig.Type = "oneshot";
script = ''
mkdir -p /home/agent/.ssh
ssh-keygen -t ed25519 -f /home/agent/.ssh/id_ed25519 -N ""
chown -R agent:users /home/agent/.ssh
# Display public key for GitHub/GitLab registration
echo "Add this deploy key to your repository:"
cat /home/agent/.ssh/id_ed25519.pub
'';
};
}
Don’t persist VM state. Each run should start fresh:
# Good: Clean start
microvm-run
# Bad: Persisting state
microvm-run --persist-disk
Only share what’s needed:
# Good: Share only the project
shares = [{
source = "/home/user/projects/myapp";
mountPoint = "/workspace";
}];
# Bad: Share entire home directory
shares = [{
source = "/home/user";
mountPoint = "/home/user";
}];
If the task doesn’t require network access, disable it:
microvm.interfaces = [];
Set appropriate memory and CPU limits:
microvm = {
mem = 4096; # 4GB
vcpu = 2;
# Optional: CPU pinning for isolation
qemu.extraArgs = [
"-cpu" "host,migratable=off"
];
};
Enable comprehensive logging for post-task review:
{
# Log all commands
programs.bash.interactiveShellInit = ''
export HISTFILE=/workspace/.agent-history
export HISTTIMEFORMAT="%F %T "
shopt -s histappend
'';
# Log agent output
systemd.services.agent-logger = {
wantedBy = ["multi-user.target"];
script = ''
script -f /workspace/.agent-session.log
'';
};
}
name: Agent Task
on:
workflow_dispatch:
inputs:
task:
description: 'Task for the agent'
required: true
jobs:
agent:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v31
with:
extra_nix_config: |
experimental-features = nix-command flakes
- name: Run Agent Task
env:
ANTHROPIC_API_KEY: $
run: |
nix develop .#agent --command bash -c "
timeout 3600 ./bin/agent-task '$'
"
- name: Upload Results
uses: actions/upload-artifact@v4
with:
name: agent-output
path: |
.agent-history
.agent-session.log
# Check if KVM is available
ls -la /dev/kvm
# Verify microvm runner built correctly
nix build .#nixosConfigurations.agent-vm.config.microvm.declaredRunner
# Inside VM, check mounts
mount | grep workspace
# Verify virtiofsd is running on host
ps aux | grep virtiofsd
# Inside VM, check network
ip addr
ping -c 1 8.8.8.8
# Check DNS resolution
cat /etc/resolv.conf
# Increase resources
microvm = {
mem = 16384; # 16GB
vcpu = 8;
};
# Use virtiofs instead of 9p for better I/O
shares = [{
proto = "virtiofs";
# ...
}];