Drivers

Mentat usa cinco drivers de runtime en un modelo zero trust. Cada workload corre en el nivel de aislamiento que se ajusta a su perfil de riesgo. Ningun driver sirve para todo — la plataforma elige el correcto.

Cuando Usar Cada Driver

FirecrackerKernel dedicado por servicio (KVM). Para clusters Raft, unikernels Rust y workloads de alta seguridad. ~8ms boot, ~256MB
SandboxAislamiento con namespaces Linux (PID + mount + UTS + IPC) con cgroups v2. Para Next.js, Node.js, web apps. Sin daemon Docker. ~2MB overhead
DockerContenedores OCI para servicios existentes. Mars Mode auto-adopta contenedores en ejecucion al reiniciar
ExecBinarios directos para agentes internos de confianza. Sin aislamiento — acceso a nivel host
HullRuntime de contenedores daemonless propio. Binario Zig de ~3 MB, aislamiento de 7 capas (ns + cgroups + seccomp + landlock + pivot_root), bridge veth opcional. Corre workloads BEAM / .NET / Node

FirecrackerDriver

Usa Firecracker cuando el aislamiento importa mas. Cada workload recibe su propia frontera de kernel y un entorno de ejecucion dedicado, lo que calza mejor para servicios sensibles, plataformas internas y workloads tipo serverless privado.

Uso tipico: StreamForge y OxideStore.

Que Hace el Agent

  1. Prepara un root filesystem con el binario del servicio
  2. Configura networking tap y recursos de VM
  3. Inicia Firecracker y adjunta volumenes
  4. Levanta el workload y espera health

Ejemplo: StreamForge (args CLI)

StreamForge recibe argumentos de runtime directamente a traves de la definicion del servicio.

streamforge service.yaml
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: /data

Ejemplo: OxideStore (env vars)

OxideStore lee configuracion desde variables de entorno y volumenes persistentes.

oxidestore service.yaml
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: /data

Flujo de Boot

1. mt run service.yaml
2. Server agenda la asignacion
3. Agent prepara los assets del runtime
4. Firecracker recibe config de maquina, boot source y volumenes
5. El workload inicia y reporta healthy

DockerDriver

Usa Docker cuando el servicio ya existe como contenedor y la prioridad es consistencia operativa, no conversion de runtime. Es la ruta de migracion para servicios .NET, Node.js existentes y servicios de terceros.

Uso tipico: API Jarvis y otros servicios ya containerizados.

docker config
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: true

ExecDriver

Usa exec para procesos a nivel host que deben iniciar rapido y correr sin overhead de contenedor o VM. Es util para agentes de plataforma, recolectores y utilidades operativas.

Uso tipico: Node Agent

exec config
config:
  driver: exec
  command: /opt/bin/node-agent
  args:
    - "--port"
    - "9090"
    - "--workers"
    - "4"
  env:
    - - RUST_LOG
      - info

HullDriver

Usa Hull cuando quieras semantica de contenedor tipo Docker sin un daemon, sin registry, y sin arbol de capas OCI. Hull es un solo binario Zig static-musl de ~3 MB que inicia el workload dentro de un bundle fresco de namespaces Linux, aplica limites cgroups v2, instala una allowlist seccomp-bpf por perfil de workload, cierra el filesystem via Landlock y hace pivot_root al rootfs del contenedor — todo en una sola llamada.

Uso tipico: Titan ESB (Elixir/Phoenix/BEAM), servicios .NET AOT, servicios Node.js que no encajan en el modelo de sandbox base.

Capas de aislamiento (afuera → adentro)

  1. cgroups v2 — limites duros de CPU / memoria / pids
  2. Namespaces PID + NET + MNT + UTS + IPC (+ USER opcional en modo --rootless)
  3. mount --make-rprivate + /proc nuevo dentro del mount namespace
  4. Allowlist seccomp-bpf (KILL_PROCESS en violacion, curada por perfil de workload)
  5. Allowlist de filesystem Landlock LSM (fallback gracioso en kernels < 5.13)
  6. pivot_root a un rootfs dedicado (extraido y cacheado en /var/lib/hull/rootfs/<name>)

Networking via bridge

Pon network: bridge en el manifest y Hull crea un par veth, conecta un extremo al bridge hull0(10.88.0.0/24 por defecto), asigna la siguiente IP libre via lockfiles atomicos O_EXCL, y configura el lado del contenedor desde el host via nsenter. Las reglas masquerade de nftables yiptables -I FORWARD 1 -i hull0 -j ACCEPT se instalan idempotentemente, asi los contenedores alcanzan internet a traves de la ruta default del host.

Modo rootless

Pasa --rootless y Hull ejecuta el dance de NEWUSER incluso siendo invocado como root: el parent forkea un child userns_setup, escribe/proc/<pid>/{setgroups,gid_map,uid_map}, y el workload se ve como uid 0 dentro de un user namespace completamente nuevo. Defense in depth — no hay credenciales root a las que el workload pueda escapar.

Titan ESB en hull
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
manifest hull (/etc/hull/titan.json)
{
  "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 }
}

Inspeccionar un contenedor vivo

hull inspect <name> lee~/.hull/state/<name>.json, luego imprime los numeros del cgroup en vivo, los 7 inums de namespace (viareadlink /proc/<pid>/ns/*) y los primeros 12 mount points de mountinfo. Sin daemon al que consultar —ps, stop, kill, logse inspect van todos por el mismo directorio de estado.

Volumenes Persistentes

Todos los drivers soportan almacenamiento persistente. Firecracker usa volumenes de bloque adjuntos. Docker mapea paths del host como bind mounts. Asi el mismo modelo de plataforma puede soportar servicios con datos pesados y workloads livianos stateless juntos.

volumes:
  - source: /mnt/unikernel-volumes/vector-store
    target: /data
    read_only: false