Ghaf Architecture for Developers
Ghaf Architecture for Developers
Section titled “Ghaf Architecture for Developers”This section covers the internal architecture of the Ghaf framework from a developer’s perspective. Understanding these concepts is essential for:
- Contributing new modules or features to Ghaf
- Creating new VM types
- Building downstream projects on top of Ghaf
- Debugging configuration issues
Compartmentalization Model
Section titled “Compartmentalization Model”Ghaf is built on the principle of security through compartmentalization. Instead of running all applications in a single operating system, Ghaf isolates different security domains into separate virtual machines (VMs).
Why VMs Instead of Containers?
Section titled “Why VMs Instead of Containers?”| Aspect | VMs | Containers |
|---|---|---|
| Isolation | Hardware-level via hypervisor | Kernel-level via namespaces |
| Attack surface | Minimal (hypervisor only) | Larger (shared kernel) |
| Kernel exploits | Contained to single VM | Can escape to host |
| Resource overhead | Higher | Lower |
For security-critical applications, the stronger isolation of VMs outweighs the performance cost.
The Minimal Host Principle
Section titled “The Minimal Host Principle”The Ghaf host runs only what’s necessary to orchestrate VMs:
┌─────────────────────────────────────────────────────────────┐│ GHAF HOST ││ ┌─────────────┐ ┌─────────────┐ ┌─────────────────────┐ ││ │ microvm │ │ GIVC │ │ Hardware Drivers │ ││ │ daemon │ │ (IPC) │ │ (passthrough) │ ││ └─────────────┘ └─────────────┘ └─────────────────────┘ │└─────────────────────────────────────────────────────────────┘ │ │ │ ▼ ▼ ▼┌───────────┐ ┌───────────┐ ┌───────────┐ ┌───────────┐│ GUI VM │ │ Net VM │ │ Audio VM │ │ App VMs ││ (display) │ │ (network) │ │ (sound) │ │ (apps) │└───────────┘ └───────────┘ └───────────┘ └───────────┘Each VM has a specific security domain:
- GUI VM: Display, input devices, desktop environment
- Net VM: All network interfaces and traffic
- Audio VM: Sound hardware and processing
- App VMs: Individual applications (browser, messaging, etc.)
Configuration Hierarchy
Section titled “Configuration Hierarchy”Ghaf uses a layered configuration system that flows from the host to each VM.
The Three Configuration Contexts
Section titled “The Three Configuration Contexts”┌─────────────────────────────────────────────────────────────┐│ HOST NIXOS CONFIGURATION ││ ││ • Defines which VMs exist ││ • Sets global configuration (ghaf.global-config) ││ • Configures hardware passthrough ││ • Orchestrates VM lifecycle │└─────────────────────────────────────────────────────────────┘ │ ┌───────────────┼───────────────┐ │ │ │ ▼ ▼ ▼ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │ globalConfig│ │ hostConfig │ │ VM Config │ │ │ │ │ │ │ │ Same for │ │ Different │ │ VM's own │ │ all VMs │ │ per VM │ │ settings │ └─────────────┘ └─────────────┘ └─────────────┘1. Host NixOS Configuration
Section titled “1. Host NixOS Configuration”The top-level configuration defined in targets/ or downstream projects. This is where you:
- Select which VMs to enable
- Configure
ghaf.global-configwith desired profile - Set hardware-specific options
- Define target-specific customizations
{ ghaf.global-config = lib.ghaf.profiles.debug;
ghaf.virtualization.microvm = { guivm.enable = true; netvm.enable = true; audiovm.enable = true; };}2. globalConfig
Section titled “2. globalConfig”Settings that should be identical across all VMs. These propagate automatically via specialArgs.
Use globalConfig for:
- Debug mode
- SSH access
- Logging configuration
- GIVC settings
- Feature flags (fingerprint, yubikey, etc.)
# Accessed in VM modules via specialArgs{ globalConfig, ... }:{ config = { ghaf.profiles.debug.enable = globalConfig.debug.enable; };}3. hostConfig
Section titled “3. hostConfig”Settings that are different for each VM but derived from the host. These also propagate via specialArgs.
Use hostConfig for:
- VM name and type
- Network configuration (IP, MAC)
- User configuration
- Passthrough device assignments
# Accessed in VM modules via specialArgs{ hostConfig, ... }:{ config = { networking.hostName = hostConfig.vmName; networking.interfaces.ethint0.ipv4.addresses = [{ address = hostConfig.networking.thisVm.ipv4; prefixLength = 24; }]; };}How Configuration Flows
Section titled “How Configuration Flows”The following diagram shows how settings flow from host to VM:
┌─────────────────────────────────────────────────────────────────┐│ TARGET CONFIGURATION (targets/laptop/flake-module.nix) ││ ││ ghaf.global-config = lib.ghaf.profiles.debug; ││ ││ ghaf.virtualization.microvm.guivm = { ││ enable = true; ││ evaluatedConfig = ...extendModules { modules = [...] }; ││ }; │└─────────────────────────────────────────────────────────────────┘ │ │ 1. Profile creates base config ▼┌─────────────────────────────────────────────────────────────────┐│ PROFILE (modules/profiles/laptop-x86.nix) ││ ││ guivmBase = lib.nixosSystem { ││ specialArgs = lib.ghaf.vm.mkSpecialArgs { ││ inherit lib inputs; ││ globalConfig = config.ghaf.global-config; ◄── From host ││ hostConfig = lib.ghaf.vm.mkHostConfig { ... }; ││ }; ││ modules = [ inputs.self.nixosModules.guivm-base ]; ││ }; │└─────────────────────────────────────────────────────────────────┘ │ │ 2. Base module receives via specialArgs ▼┌─────────────────────────────────────────────────────────────────┐│ VM BASE MODULE (modules/microvm/sysvms/guivm-base.nix) ││ ││ { config, lib, pkgs, globalConfig, hostConfig, ... }: ││ { ││ # Use globalConfig for inherited settings ││ ghaf.profiles.debug.enable = globalConfig.debug.enable; ││ ││ # Use hostConfig for VM-specific settings ││ networking.hostName = hostConfig.vmName; ││ } │└─────────────────────────────────────────────────────────────────┘ │ │ 3. VM module consumes evaluatedConfig ▼┌─────────────────────────────────────────────────────────────────┐│ VM ORCHESTRATION MODULE (modules/microvm/sysvms/guivm.nix) ││ ││ microvm.vms."gui-vm" = { ││ inherit (cfg) evaluatedConfig; ◄── Fully evaluated config ││ }; │└─────────────────────────────────────────────────────────────────┘Key Concepts
Section titled “Key Concepts”specialArgs
Section titled “specialArgs”The specialArgs parameter to evalModules provides values that are available to all modules without needing to be passed explicitly. This avoids the anti-pattern of threading values through module parameters.
# specialArgs makes these available everywhere in the module treespecialArgs = { inherit lib inputs; globalConfig = config.ghaf.global-config; hostConfig = lib.ghaf.vm.mkHostConfig { ... };};extendModules
Section titled “extendModules”The extendModules function allows composing configurations lazily. This is crucial for Ghaf because:
- Lazy evaluation: VM configs aren’t evaluated until needed
- Composition: Downstream can extend without copying
- No circular deps: Host can reference VM config safely
# Base configuration from profilebaseConfig = profileConfig.guivmBase;
# Extend with target-specific settingsevaluatedConfig = baseConfig.extendModules { modules = [ { environment.systemPackages = [ pkgs.myApp ]; } ];};evaluatedConfig
Section titled “evaluatedConfig”Each VM module has an evaluatedConfig option that receives the fully-evaluated NixOS configuration for that VM. This is what microvm.vms consumes to create the actual VM.
options.ghaf.virtualization.microvm.guivm = { enable = lib.mkEnableOption "GUI VM";
evaluatedConfig = lib.mkOption { type = lib.types.nullOr lib.types.unspecified; default = null; description = "Pre-evaluated NixOS configuration for GUI VM"; };};Further Reading
Section titled “Further Reading”- VM Composition with extendModules - Deep dive into the composition pattern
- Configuration Propagation - globalConfig and hostConfig in detail
- Module Conventions - Standards for writing Ghaf modules
- Anti-Patterns - Common mistakes to avoid