Drivers
Mentat uses five runtime drivers in a zero trust model. Each workload runs in the isolation level that fits its risk profile. No single driver fits all workloads — the platform picks the right one.
When To Use Each Driver
| Firecracker | Dedicated kernel per service (KVM). For Raft clusters, Rust unikernels, and high-security workloads. ~8ms boot, ~256MB |
| Sandbox | Linux namespace isolation (PID + mount + UTS + IPC) with cgroups v2. For Next.js, Node.js, web apps. No Docker daemon. ~2MB overhead |
| Docker | OCI containers for existing services. Mars Mode auto-adopts running containers on restart |
| Exec | Bare binaries for trusted internal agents. No isolation — host-level access |
| Hull | In-house daemonless container runtime. ~3 MB Zig binary, 7-layer isolation (ns + cgroups + seccomp + landlock + pivot_root), optional veth bridge. Runs BEAM / .NET / Node workloads |
FirecrackerDriver
Use Firecracker when isolation matters most. Each workload gets its own kernel boundary and dedicated runtime environment, which is a better fit for sensitive services, internal platforms, and private serverless-style workloads.
Typical use: StreamForge and OxideStore.
What the Agent Does
- Prepares a root filesystem with the service binary
- Configures tap networking and VM resources
- Starts Firecracker and attaches volumes
- Boots the workload and waits for health
Example: StreamForge (CLI args)
StreamForge receives runtime arguments directly through the service definition.
name: streamforge
driver: firecracker
count: 3
config:
memory_mb: 256
vcpus: 2
args:
- "--wal"
- "--data-dir"
- "/data/streamforge"
- "--raft-port"
- "8081"
- "--http-port"
- "8080"
volumes:
- source: /mnt/unikernel-volumes/streamforge
target: /dataExample: OxideStore (env vars)
OxideStore reads configuration from environment variables and persistent volumes.
name: oxidestore
driver: firecracker
count: 1
config:
memory_mb: 256
vcpus: 2
env:
OXIDESTORE_DATA_DIR: "/data/oxidestore"
OXIDESTORE_BIND: "0.0.0.0:9000"
OXIDESTORE_REGION: "us-east-1"
volumes:
- source: /mnt/unikernel-volumes/oxidestore
target: /dataBoot Flow
1. mt run service.yaml
2. Server schedules the allocation
3. Agent prepares runtime assets
4. Firecracker receives machine config, boot source, and volumes
5. Workload starts and reports healthyDockerDriver
Use Docker when the service already exists as a container and the main goal is operational consistency, not runtime conversion. This is the migration path for existing .NET, Node.js, and third-party services.
Typical use: API Jarvis and other existing containerized services.
config:
driver: docker
image: "registry.example/api-service:latest"
env:
- - ASPNETCORE_ENVIRONMENT
- Production
ports:
- "8080:80"
volumes:
- source: /data/api-service
target: /app/data
read_only: trueSandboxDriver
Use Sandbox for web applications, APIs, and services that need isolation without Docker. Linux namespace isolation (PID + mount + UTS + IPC) with cgroups v2 resource limits. A 15KB C binary acts as PID 1 inside the sandbox, with pivot_root and all capabilities dropped.
Typical use: Next.js SSR, React apps, Python FastAPI, Java Spring Boot, static sites.
Pre-built Bases (overlayfs)
No need to build images or copy runtime dependencies. Mentat provides pre-built base rootfs that are merged with your application code via overlayfs. The base is created once and reused across all deploys.
| node22 | Node.js 22 + npm + SSL certs | Next.js SSR, Express, Fastify |
| python312 | Python 3.12 + stdlib + pip | FastAPI, Django, Flask |
| java21 | OpenJDK 21 JRE + SSL | Spring Boot, Quarkus, Micronaut |
| static | BusyBox httpd only | React, Vue, Angular static builds |
name: my-nextjs-app
driver: sandbox
base: node22 # pre-built, reused via overlayfs
rootfs: ./.next/standalone # just your build output
command: /bin/sh
args: ["/app/start.sh"]
env:
PORT: "3000"
NODE_ENV: production
resources:
memory: 256m
cpu: 50%
scale:
min: 1
max: 5
metric: cpu
scale_up_at: 70
scale_down_at: 20name: my-api
driver: sandbox
base: python312
rootfs: ./app
command: /usr/bin/python3
args: ["-m", "uvicorn", "main:app", "--host", "0.0.0.0"]
env:
PORT: "8000"
resources:
memory: 128mSecurity Model
- PID namespace — process cannot see or signal host processes
- Mount namespace — pivot_root (not chroot) into isolated rootfs
- ALL capabilities dropped — no privilege escalation possible
- NO_NEW_PRIVS — set via prctl, inherited by all children
- cgroups v2 — memory, CPU, and PID limits enforced by kernel
- Minimal /dev — only null, zero, random, urandom
ExecDriver
Use exec for host-level processes that should start fast and run without container or VM overhead. This is useful for platform-side agents, collectors, and operational helpers.
Typical use: Node Agent
config:
driver: exec
command: /opt/bin/node-agent
args:
- "--port"
- "9090"
- "--workers"
- "4"
env:
- - RUST_LOG
- infoHullDriver
Use Hull when you want Docker-style container semantics without a daemon, a container registry, or an OCI layer tree. Hull is a single ~3 MB static-musl Zig binary that starts the workload inside a fresh bundle of Linux namespaces, applies cgroups v2 limits, installs a seccomp-bpf syscall allowlist per workload profile, locks down the filesystem via Landlock, and pivot_roots into the container’s rootfs — all in one call.
Typical use: Titan ESB (Elixir/Phoenix/BEAM), .NET AOT services, Node.js services that don’t fit the sandbox base model.
Isolation layers (outer → inner)
- cgroups v2 — CPU / memory / pids hard limits
- PID + NET + MNT + UTS + IPC namespaces (+ optional USER in
--rootlessmode) mount --make-rprivate+ fresh/procinside the mount namespace- seccomp-bpf allowlist (
KILL_PROCESSon violation, curated per workload profile) - Landlock LSM filesystem allowlist (graceful fallback on kernels < 5.13)
pivot_rootinto a dedicated rootfs (extracted and cached under/var/lib/hull/rootfs/<name>)
Bridge networking
Set network: bridge in the manifest and Hull creates a veth pair, attaches one end to the hull0 bridge (10.88.0.0/24 by default), leases the next free IP via atomic O_EXCL lock files, and wires the container side from the host via nsenter. nftables masquerade andiptables -I FORWARD 1 -i hull0 -j ACCEPTare installed idempotently so containers can reach the internet through the host’s default route.
Rootless mode
Pass --rootless and Hull runs the NEWUSER dance even when invoked as root: the parent forks a userns_setup child, writes/proc/<pid>/{setgroups,gid_map,uid_map}, and the workload sees itself as uid 0 inside a brand new user namespace. Defense in depth — there’s no root credential for the workload to escape to.
name: titan
replicas: 1
config:
driver: hull
manifest: /etc/hull/titan.json
binary: /usr/local/bin/hull-sudo
port: 4500
resources:
cpu_mhz: 2000
memory_mb: 1024
disk_mb: 256
port: 4500
ingress:
host: www.titan-bus.com
aliases:
- titan.getmentat.run
path: /
tls: true
security:
profile: hull{
"name": "titan",
"rootfs": "/var/lib/hull/rootfs/titan",
"argv": ["/opt/titan/bin/titan", "start"],
"env": ["PHX_SERVER=true", "PORT=4500", "PHX_HOST=www.titan-bus.com"],
"profile": "beam",
"network": "bridge",
"bridge": { "subnet": "10.88.0.0/24" },
"hostname": "titan",
"cwd": "/opt/titan",
"limits": { "memory_mb": 1024, "cpu": 2.0, "pids": 4096 }
}Inspecting a running container
hull inspect <name> reads~/.hull/state/<name>.json, then prints the live cgroup numbers, all 7 namespace inums (viareadlink /proc/<pid>/ns/*), and the first 12 mount points from mountinfo. No daemon to query —ps, stop, kill, logs, and inspect all go through the same state directory.
Persistent Volumes
All drivers support persistent storage. Firecracker uses attached block-backed volumes. Docker maps host paths as bind mounts. The same platform model can therefore support data-heavy services and lightweight stateless workloads together.
volumes:
- source: /mnt/unikernel-volumes/vector-store
target: /data
read_only: false