__ __ __ _
/ / / /_ ______/ /____(_) __
/ /_/ / / / / __ / ___/ / |/_/
/ __ / /_/ / /_/ / / / /> <
/_/ /_/\__, /\__,_/_/ /_/_/|_|
/____/
An attempt at a somewhat secure workstation framework
Based on NixOS and MicroVMs
Heavily inspired by Qubes OS
Discl AI mer - AI was used in setting this project up, do not use unless you feel comfortable with that piece of information
Everything seen here is still under development. Once I end up with a solid prototype that has been more battle-tested and ran on different hardware, I will try to make some sort of numbered release.
UPDATE 1 - 10/07 Setup with installer should now produce a working system, after testing on a different set of laptops(all Intel-based, still needs testing with AMD hardwar[testers needed]).
Hydrix is an options-driven NixOS framework that provides complete network isolation through VM compartmentalization. Your WiFi hardware is passed directly to a router VM via VFIO, giving you granular control over network traffic while maintaining a hardened host. Qubes will always be a better setup, and from a security and segmentation standpoint, Hydrix makes(at least as of now) sacrifices such as a shared Host -> Guest shared /nix/store. Further development is neccessary to fully find ways of approximating Qubes, but expect manually reading through all of the code of this setup and tweaking things yourself to tailor settings to your security preferences. The heavy lifting here is all done with MicroVMs, huge shoutout to Astro.
For full documentation see DOCUMENTATION.md.
Things being actively worked on or not yet verified. Checked off once resolved and tested.
Blocking / in progress
- Dual-boot: single-boot installs work; dual-boot broken
- Router VM in VM installations: works on real laptop hardware; breaks when Hydrix is running inside a VM (for testing purposes)
- Desktop / USB WiFi: designed for laptops with native WiFi cards; desktops without one are untested; USB WiFi card behaviour unknown
- AMD hardware: currently Intel/ASUS-specific (VFIO, ASUS driver, ZenBook audio); needs AMD parity and hardware testing
- LUKS encryption for profile VMs: verified working for pentest and comms; the
encryption = trueflag in machine config is wired generically inflake.nix, so it extends to any profile without code changes.microvm purgedeletes the LUKS container (*.luks) but leavesencryption = truein machine config;microvm startdetects the built runner expects/dev/mapper/vm-<name>-homeand the container is missing, and fails fast with a pointer tomicrovm encrypt-setup <name>. - Encrypted VM launch via wofi:
mod+don a stopped encrypted VM now detects the LUKS volume and prompts for the passphrase viawofi --password(same masked-input pattern asvault-pick.nix), unlocking beforemicrovm startruns. No terminal needed. - Builder build progress:
microvm builder build Xnow streams live status (Building.../OK building .../DONE/ERROR) instead of going silent; errors are visible without socat'ing into the builder. Still coarse-grained: not the full per-derivation live streammicrovm build Xshows in administrative mode. - Setup script (
setup-hydrix.sh): not fully end-to-end tested - Installer post-reboot, gh auth: persistence is implemented but untested; git config is not yet declarative (requires manual
git configafter reboot) - Infra VM ephemerality: router/router-stable/files/gitsync/hostsync/usb-sandbox/vault now wipe state on every restart, matching the lurking-profile pattern;
microvm purgeis no longer required after WiFi credential changes. See DOCUMENTATION.md § Infra VM Persistence Model. - Clipboard isolation: handled by the
hypr-clip-guardHyprland plugin - hooks all Wayland clipboard protocols to enforce per-VM isolation. See DOCUMENTATION.md § Clipboard Isolation.
Polish / lower priority
- Socat terminal output: raw-mode attach/detach (
microvm console <name>) verified working. Removed thescreenfallback: it was broken sinceconsole.sockis a UNIX socket rather than a character device, so screen tried to exec the socket path as a command instead of connecting to it. Remaining known limitation: the console renders in a small, fixed geometry, inherent to qemu's serial-over-socket transport having no window-size negotiation with the guest, not fixable via socat or screen alone. - Phase out xpra: xpra, i3, and sway have been fully removed from the framework. Hyprland + waypipe is the only supported desktop stack.
- Live-switch edge cases (
microvm update): fixed two silent-failure paths: the host-side nix-store DB registration step now surfaces errors instead of swallowing them, andvm-switchno longer mislabels hard failures (e.g. exit 100, incompatible init requiring reboot) as "OK, some units failed". Other edge cases may still surface; report if found.
-
MicroVM compartmentalization - profile VMs (browsing, pentest, dev, comms, lurking) and infrastructure VMs (router, builder, gitsync, files, vault, usb-sandbox, hostsync)
-
WiFi VFIO passthrough - host has no direct internet in lockdown mode; all traffic routes through the router VM
-
wifi-sync - manage WiFi networks encrypted in
secrets/wifi.yamlvia the router VM over vsock; supports admin mode (add/pull/list/remove via router NM) and fallback mode (capture current host connection) -
Task pentest slots - pre-declared isolated VM slots (task1-3) assignable to named engagements without a host rebuild
-
Per-VM Mullvad VPN - each profile VM can exit through a different Mullvad server
-
Encrypted inter-VM file transfer - files VM with per-bridge TAP access and vsock passphrase delivery
-
Builder VM - builds host and VM closures from inside a locked-down nix environment with internet via router VM
-
Gitsync VM - push and pull git repos from lockdown mode without host internet
-
Hostsync VM - secure file inbox from VMs to host
-
Vault VM - isolated KeepassXC credential store with launcher-based picker and vsock-only access
-
USB sandbox VM - safe handling of untrusted USB storage inside an isolated VM
-
Declarative boot modes - lockdown (default), administrative, fallback as NixOS specialisations
-
Stable fallback router - immutable break-glass router VM for when the main router config breaks
Some more visual/graphical features:
- Hyprland - the only supported compositor. VM apps forwarded as native windows via waypipe over vsock. Set
hydrix.hyprland.enable = truein your machine config. - VM metrics polling - status bar pulls live CPU, RAM, disk, uptime from each running VM via vsock
- Pywal colorscheme system - three independent color layers per VM: declarative base scheme, live host wal-cache sync via virtiofs, and per-VM focus border color on the host
The host has three boot modes, each a NixOS specialisation:
| Mode | Internet | Host bridge presence | Use Case |
|---|---|---|---|
| lockdown (default) | None | No L3 addresses on any bridge | Daily secure use; nix builds via builder VM |
| administrative | Via router VM | 192.168.100.1 on br-mgmt only |
Full functionality, VM management, package installs |
| fallback | Direct WiFi, no router VM | No bridges | Emergency recovery, initial setup |
Specialisation files live in hydrix-config/specialisations/. Add extra packages per mode there:
# specialisations/administrative.nix
{ pkgs, ... }:
{
environment.systemPackages = with pkgs; [ wireshark ];
}curl -sL https://raw.githubusercontent.com/borttappat/Hydrix/main/scripts/install-hydrix.sh | sudo bashThe script partitions the disk via disko, auto-detects hardware (CPU, WiFi PCI address, ASUS features, and the live ISO's own NixOS release as this machine's system.stateVersion), prompts for username and colorscheme, generates machines/<serial>.nix + modules/user.nix + modules/common.nix, runs nixos-install, and pre-builds the router and builder VMs.
If you already have a hydrix-config on another machine, provide the repo URL when prompted. The installer enters add mode: it clones your repo and generates only machines/<serial>.nix for the new hardware. User identity, locale, and VM configs are already in the repo - no re-prompting.
curl -sL https://raw.githubusercontent.com/borttappat/Hydrix/main/scripts/setup-hydrix.sh | bashThe script detects your current system (user, locale, WiFi), creates ~/hydrix-config/, generates your machine config, and handles multi-machine setups. system.stateVersion is read from your existing /etc/nixos/configuration.nix (or prompted for manually if not found) rather than re-detected, since it must reflect this machine's original install, not its current release. Same three modes apply: fresh (new config), add (new machine to existing repo), use-existing (serial already present).
microvm start browsingmvm rebuild browsing pentest dev comms lurkingProfile VMs each have a directory in hydrix-config/profiles/ with three files:
profiles/browsing/
├── meta.nix # CID, bridge, subnet, workspace, label, focusBorder
├── default.nix # NixOS config: colorscheme, RAM, vCPUs, extra packages, hosts
└── packages/
└── default.nix # managed by vm-sync, do not edit manually
Built-in profiles and their defaults:
| VM | CID | WS | Bridge | Persistence |
|---|---|---|---|---|
| pentest | 102 | 2 | br-pentest | persistent, optionally LUKS-encrypted |
| browsing | 103 | 3 | br-browse | 10GB home |
| comms | 104 | 4 | br-comms | persistent |
| dev | 105 | 5 | br-dev | 50GB + 20GB docker |
| lurking | 106 | 6 | br-lurking | ephemeral |
Each profile is actually built as its own per-machine nixosConfiguration
(microvm-<profile>-<serial>, the same pattern the router already uses), but you never
need to know or type that: microvm <cmd> <profile> always resolves to the current
machine's real VM name via /etc/hydrix/vm-registry.json. Infra VMs (router, builder,
files, gitsync, hostsync, usb-sandbox, vault) are the exception - they stay a single
shared name across every machine, since they hold no persistent state to differentiate.
See DOCUMENTATION.md § VM Naming and Machine Identity.
Custom profiles start at CID 107+. Scaffold one with:
new-profile myvm # auto-assigns next free CID and workspace
rebuild # creates bridge, updates tap wiring and vm-registry.json
mvm rebuild router files # pick up new bridge (router + files VM)
microvm build myvm
microvm start myvmBuild a VM image (evaluates config, writes runner to nix store)
microvm build browsingStart a VM (polls readiness, then connects display tunnel)
microvm start browsingStop a VM
microvm stop browsingRestart (required for kernel, initrd, or runner changes)
microvm restart browsingLive switch (applies config changes without restart - no kernel/runner changes)
microvm update browsingCheck running vs built state
microvm switch-status browsingOperate on multiple VMs at once
mvm rebuild browsing pentest dev
mvm stop files pentest browsing router builder gitsync
mvm build files pentest browsingIn lockdown mode (no host internet), use the builder VM to fetch and build:
microvm builder build browsing # fetches deps via router VM, writes to host store
microvm builder switch # build + switch host configHyprland is the only supported compositor. Enable it in machines/<serial>.nix:
# machines/<serial>.nix
hydrix.hyprland.enable = true; # Wayland, VM apps forwarded via waypipeOn a VM workspace, pressing Super+Return launches the terminal in that VM as a native
Hyprland window via waypipe.
| Component | Program |
|---|---|
| Compositor | Hyprland |
| Status bar | waybar |
| Launcher | wofi |
| Lockscreen | hyprlock |
| VM forwarding | waypipe (vsock) |
hyprland-launch # start Hyprland session from TTY
hypr-ws-app alacritty # launch app in VM on current workspace
hypr-ws-app firefoxKeybindings live in modules/hyprland.nix.
Sway, i3, and xpra have been fully removed from the framework - there is no
hydrix.sway.enable or hydrix.i3.enable option anymore.
Three independent color layers per VM:
Layer 1 - VM internal colorscheme
hydrix.colorscheme = "hydrix"
Drives pywal inside the VM: alacritty, dunst, GTK
Layer 2 - Host wal cache via virtiofs
Host ~/.cache/wal shared read-only into VMs at boot.
Running walrgb/randomwalrgb on the host sends a REFRESH
signal to all running VMs, updating their terminals and
pywalfox in real time.
Layer 3 - Focus border (host-side)
focusBorder = "yellow" # in profiles/<name>/meta.nix
The compositor border color when a VM window is focused.
Fully independent from the VM's internal colors. Lives in meta.nix
(plain attrset) so the host flake can read it without evaluating
any VM NixOS configuration - avoids OOM on memory-constrained hosts.
walrgb /path/to/image.jpg # generate + apply colors, syncs to all running VMs
randomwalrgb # random wallpaper from configured directoryDeclarative colorschemes in profiles/<name>/default.nix:
hydrix.colorscheme = "nord"; # nord, hydrix, ... add more with `save-colorscheme xyz` User-defined colorschemes in hydrix-config/colorschemes/ (pywal JSON format) take priority over framework ones with the same name.
Add to modules/common.nix:
environment.systemPackages = with pkgs; [ ripgrep fd ];Add to machines/<serial>.nix or specialisations/administrative.nix for mode-specific installs:
environment.systemPackages = with pkgs; [ wireshark ];Add to profiles/<name>/default.nix:
environment.systemPackages = with pkgs; [ gobuster ffuf ];Or use the vm-dev workflow to build and test a package inside the VM first, then pull it to the profile:
# Inside the VM
vm-dev build https://github.com/owner/repo
vm-dev run repo
vm-sync push --name repo
# On the host
vm-sync pull repo --target pentest
microvm build pentest
microvm restart pentestSee DOCUMENTATION.md for full configuration reference, security model, secrets management, encrypted home volumes, Mullvad VPN, task pentest VMs, vsock port reference, and troubleshooting.