about summary refs log tree commit diff
path: root/nix/modules/libvirtnix
diff options
context:
space:
mode:
Diffstat (limited to 'nix/modules/libvirtnix')
-rw-r--r--nix/modules/libvirtnix/a.xml259
-rw-r--r--nix/modules/libvirtnix/b.xml183
-rw-r--r--nix/modules/libvirtnix/config.nix583
-rw-r--r--nix/modules/libvirtnix/domain.nix2056
-rw-r--r--nix/modules/libvirtnix/os.nix119
-rw-r--r--nix/modules/libvirtnix/secret.nix174
-rw-r--r--nix/modules/libvirtnix/test.nix10
-rw-r--r--nix/modules/libvirtnix/xml.nix58
8 files changed, 2785 insertions, 657 deletions
diff --git a/nix/modules/libvirtnix/a.xml b/nix/modules/libvirtnix/a.xml
new file mode 100644
index 0000000..1255ad4
--- /dev/null
+++ b/nix/modules/libvirtnix/a.xml
@@ -0,0 +1,259 @@
+<domain type='kvm' id='5'>
+  <name>fileserver2</name>
+  <uuid>d62e0276-d474-452b-80f4-c951c39bcf74</uuid>
+  <metadata>
+    <libosinfo:libosinfo xmlns:libosinfo='http://libosinfo.org/xmlns/libvirt/domain/1.0'>
+      <libosinfo:os id='http://nixos.org/nixos/unknown'/>
+    </libosinfo:libosinfo>
+  </metadata>
+  <memory unit='KiB'>2097152</memory>
+  <currentMemory unit='KiB'>2097152</currentMemory>
+  <vcpu placement='static'>3</vcpu>
+  <resource>
+    <partition>/machine</partition>
+  </resource>
+  <os>
+    <type arch='x86_64' machine='pc-q35-3.1'>hvm</type>
+    <loader readonly='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
+    <nvram>/var/lib/libvirt/qemu/nvram/fileserver2_VARS.fd</nvram>
+    <boot dev='hd'/>
+  </os>
+  <features>
+    <acpi/>
+    <apic/>
+    <vmport state='off'/>
+  </features>
+  <cpu mode='host-passthrough' check='none' migratable='on'/>
+  <clock offset='utc'>
+    <timer name='rtc' tickpolicy='catchup'/>
+    <timer name='pit' tickpolicy='delay'/>
+    <timer name='hpet' present='no'/>
+  </clock>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <pm>
+    <suspend-to-mem enabled='no'/>
+    <suspend-to-disk enabled='no'/>
+  </pm>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <disk type='block' device='disk'>
+      <driver name='qemu' type='raw' cache='none' io='native' discard='unmap'/>
+      <source dev='/dev/vmstorage/fileserver2' index='3'/>
+      <backingStore/>
+      <target dev='vda' bus='virtio'/>
+      <alias name='virtio-disk0'/>
+      <address type='pci' domain='0x0000' bus='0x04' slot='0x00' function='0x0'/>
+    </disk>
+    <disk type='block' device='disk'>
+      <driver name='qemu' type='raw'/>
+      <source dev='/dev/mapper/storage1' index='2'/>
+      <backingStore/>
+      <target dev='vdb' bus='virtio'/>
+      <alias name='virtio-disk1'/>
+      <address type='pci' domain='0x0000' bus='0x08' slot='0x00' function='0x0'/>
+    </disk>
+    <disk type='block' device='disk'>
+      <driver name='qemu' type='raw'/>
+      <source dev='/dev/mapper/storage2' index='1'/>
+      <backingStore/>
+      <target dev='vdc' bus='virtio'/>
+      <alias name='virtio-disk2'/>
+      <address type='pci' domain='0x0000' bus='0x09' slot='0x00' function='0x0'/>
+    </disk>
+    <controller type='usb' index='0' model='qemu-xhci' ports='15'>
+      <alias name='usb'/>
+      <address type='pci' domain='0x0000' bus='0x02' slot='0x00' function='0x0'/>
+    </controller>
+    <controller type='pci' index='0' model='pcie-root'>
+      <alias name='pcie.0'/>
+    </controller>
+    <controller type='pci' index='1' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='1' port='0x10'/>
+      <alias name='pci.1'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x0' multifunction='on'/>
+    </controller>
+    <controller type='pci' index='2' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='2' port='0x11'/>
+      <alias name='pci.2'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x1'/>
+    </controller>
+    <controller type='pci' index='3' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='3' port='0x12'/>
+      <alias name='pci.3'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x2'/>
+    </controller>
+    <controller type='pci' index='4' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='4' port='0x13'/>
+      <alias name='pci.4'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x3'/>
+    </controller>
+    <controller type='pci' index='5' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='5' port='0x14'/>
+      <alias name='pci.5'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x4'/>
+    </controller>
+    <controller type='pci' index='6' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='6' port='0x15'/>
+      <alias name='pci.6'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x5'/>
+    </controller>
+    <controller type='pci' index='7' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='7' port='0x16'/>
+      <alias name='pci.7'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x6'/>
+    </controller>
+    <controller type='pci' index='8' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='8' port='0x17'/>
+      <alias name='pci.8'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x02' function='0x7'/>
+    </controller>
+    <controller type='pci' index='9' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='9' port='0x18'/>
+      <alias name='pci.9'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x0' multifunction='on'/>
+    </controller>
+    <controller type='pci' index='10' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='10' port='0x19'/>
+      <alias name='pci.10'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x1'/>
+    </controller>
+    <controller type='pci' index='11' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='11' port='0x1a'/>
+      <alias name='pci.11'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x2'/>
+    </controller>
+    <controller type='pci' index='12' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='12' port='0x1b'/>
+      <alias name='pci.12'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x3'/>
+    </controller>
+    <controller type='pci' index='13' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='13' port='0x1c'/>
+      <alias name='pci.13'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x4'/>
+    </controller>
+    <controller type='pci' index='14' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='14' port='0x1d'/>
+      <alias name='pci.14'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x5'/>
+    </controller>
+    <controller type='pci' index='15' model='pcie-root-port'>
+      <model name='pcie-root-port'/>
+      <target chassis='15' port='0x1e'/>
+      <alias name='pci.15'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x03' function='0x6'/>
+    </controller>
+    <controller type='pci' index='16' model='pcie-to-pci-bridge'>
+      <model name='pcie-pci-bridge'/>
+      <alias name='pci.16'/>
+      <address type='pci' domain='0x0000' bus='0x07' slot='0x00' function='0x0'/>
+    </controller>
+    <controller type='sata' index='0'>
+      <alias name='ide'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x1f' function='0x2'/>
+    </controller>
+    <controller type='virtio-serial' index='0'>
+      <alias name='virtio-serial0'/>
+      <address type='pci' domain='0x0000' bus='0x03' slot='0x00' function='0x0'/>
+    </controller>
+    <controller type='scsi' index='0' model='lsilogic'>
+      <alias name='scsi0'/>
+      <address type='pci' domain='0x0000' bus='0x10' slot='0x01' function='0x0'/>
+    </controller>
+    <interface type='bridge'>
+      <mac address='52:54:00:bf:ba:88'/>
+      <source network='services' portid='6c3a3512-d823-4521-9043-3a91a65682f5' bridge='br7'/>
+      <target dev='vnet2'/>
+      <model type='virtio'/>
+      <alias name='net0'/>
+      <address type='pci' domain='0x0000' bus='0x01' slot='0x00' function='0x0'/>
+    </interface>
+    <serial type='pty'>
+      <source path='/dev/pts/3'/>
+      <target type='isa-serial' port='0'>
+        <model name='isa-serial'/>
+      </target>
+      <alias name='serial0'/>
+    </serial>
+    <console type='pty' tty='/dev/pts/3'>
+      <source path='/dev/pts/3'/>
+      <target type='serial' port='0'/>
+      <alias name='serial0'/>
+    </console>
+    <channel type='unix'>
+      <source mode='bind' path='/var/lib/libvirt/qemu/channel/target/domain-5-fileserver2/org.qemu.guest_agent.0'/>
+      <target type='virtio' name='org.qemu.guest_agent.0' state='disconnected'/>
+      <alias name='channel0'/>
+      <address type='virtio-serial' controller='0' bus='0' port='1'/>
+    </channel>
+    <channel type='spicevmc'>
+      <target type='virtio' name='com.redhat.spice.0' state='disconnected'/>
+      <alias name='channel1'/>
+      <address type='virtio-serial' controller='0' bus='0' port='2'/>
+    </channel>
+    <input type='tablet' bus='usb'>
+      <alias name='input0'/>
+      <address type='usb' bus='0' port='1'/>
+    </input>
+    <input type='mouse' bus='ps2'>
+      <alias name='input1'/>
+    </input>
+    <input type='keyboard' bus='ps2'>
+      <alias name='input2'/>
+    </input>
+    <graphics type='spice' port='5901' autoport='yes' listen='127.0.0.1'>
+      <listen type='address' address='127.0.0.1'/>
+    </graphics>
+    <sound model='ich9'>
+      <alias name='sound0'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x1b' function='0x0'/>
+    </sound>
+    <audio id='1' type='spice'/>
+    <video>
+      <model type='qxl' ram='65536' vram='65536' vgamem='16384' heads='1' primary='yes'/>
+      <alias name='video0'/>
+      <address type='pci' domain='0x0000' bus='0x00' slot='0x01' function='0x0'/>
+    </video>
+    <redirdev bus='usb' type='spicevmc'>
+      <alias name='redir0'/>
+      <address type='usb' bus='0' port='2'/>
+    </redirdev>
+    <redirdev bus='usb' type='spicevmc'>
+      <alias name='redir1'/>
+      <address type='usb' bus='0' port='3'/>
+    </redirdev>
+    <memballoon model='virtio'>
+      <alias name='balloon0'/>
+      <address type='pci' domain='0x0000' bus='0x05' slot='0x00' function='0x0'/>
+    </memballoon>
+    <rng model='virtio'>
+      <backend model='random'>/dev/urandom</backend>
+      <alias name='rng0'/>
+      <address type='pci' domain='0x0000' bus='0x06' slot='0x00' function='0x0'/>
+    </rng>
+  </devices>
+  <seclabel type='dynamic' model='apparmor' relabel='yes'>
+    <label>libvirt-d62e0276-d474-452b-80f4-c951c39bcf74</label>
+    <imagelabel>libvirt-d62e0276-d474-452b-80f4-c951c39bcf74</imagelabel>
+  </seclabel>
+  <seclabel type='dynamic' model='dac' relabel='yes'>
+    <label>+109:+115</label>
+    <imagelabel>+109:+115</imagelabel>
+  </seclabel>
+</domain>
diff --git a/nix/modules/libvirtnix/b.xml b/nix/modules/libvirtnix/b.xml
new file mode 100644
index 0000000..311e7d5
--- /dev/null
+++ b/nix/modules/libvirtnix/b.xml
@@ -0,0 +1,183 @@
+<domain type="kvm" id="1337">
+  <name>blub</name>
+  <uuid>cafebabe-d474-452b-80f4-c951c39bcf74</uuid>
+  <metadata>
+    <libosinfo:libosinfo xmlns:libosinfo="https://libosinfo.org/xmlns/libvirt/domain/1.0">
+      <libosinfo:os id="https://nixos.org/nixos/unknown"/>
+    </libosinfo:libosinfo>
+  </metadata>
+  <memory unit="KiB">2097152</memory>
+  <currentMemory unit="KiB">2097152</currentMemory>
+  <vcpu placement="static">3</vcpu>
+  <resource>
+    <partition>/machine</partition>
+  </resource>
+  <os>
+    <type arch="x86_64" machine="pc-q35-3.1">hvm</type>
+    <loader readonly="yes" pflash="pflash">/usr/share/OVMF/OVMF_CODE.fd</loader>
+    <nvram>/var/lib/libvirt/qemu/nvram/fileserver2_VARS.fd</nvram>
+    <type dev="hd"/>
+  </os>
+  <features>
+    <acpi/>
+    <apic/>
+    <vmport state="off"/>
+  </features>
+  <cpu mode="host-passthrough" check="none" migratable="on"/>
+  <clock offset="utc">
+    <timer name="rtc" tickpolicy="catchup"/>
+    <timer name="pit" tickpolicy="delay"/>
+    <timer name="hpet" present="no"/>
+  </clock>
+  <on_poweroff>destroy</on_poweroff>
+  <on_reboot>restart</on_reboot>
+  <on_crash>destroy</on_crash>
+  <pm>
+    <suspend-to-mem enabled="no"/>
+    <suspend-to-disk enabled="no"/>
+  </pm>
+  <devices>
+    <emulator>/usr/bin/qemu-system-x86_64</emulator>
+    <disk type="block" device="disk">block
+      <driver name="qemu" type="raw" cache="none" io="native" discard="unmap"/>
+      <source dev="/dev/vmstorage/fileserver2" index="3"/>
+      <backingStore/>
+      <target dev="vda" bus="virtio"/>
+      <alias name="virtio-disk0"/>
+      <address type="pci" domain="0x0000" bus="0x04" slot="0x00" function="0x0"/>
+    </disk>
+    <disk type="block" device="disk">block
+      <driver name="qemu" type="raw"/>
+      <source dev="/dev/mapper/storage1" index="2"/>
+      <backingStore/>
+      <target dev="vdb" bus="virtio"/>
+      <alias name="virtio-disk1"/>
+      <address type="pci" domain="0x0000" bus="0x08" slot="0x00" function="0x0"/>
+    </disk>
+    <disk type="block" device="disk">block
+      <driver name="qemu" type="raw"/>
+      <source dev="/dev/maper/storage2" index="1"/>
+      <backingStore/>
+      <target dev="vdc" bus="virtio"/>
+      <alias name="virtio-disk2"/>
+      <address type="pci" domain="0x0000" bus="0x09" slot="0x00" function="0x0"/>
+    </disk>
+    <controller type="usb" index="0" model="qemu-xhci" ports="15">
+      <model name="qemu-xhci"/>
+      <alias name="usb"/>
+      <address type="pci" domain="0x0000" bus="0x02" slot="0x00" function="0x0"/>
+    </controller>
+    <controller type="pci" index="0" model="pci-root">
+      <model name="pci-root"/>
+      <alias name="pcie.0"/>
+    </controller>
+    <controller type="pci" index="1" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="1" port="0x10"/>
+      <alias name="pci.1"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x0"/>
+    </controller>
+    <controller type="pci" index="2" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="2" port="0x11"/>
+      <alias name="pci.2"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x0"/>
+    </controller>
+    <controller type="pci" index="3" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="3" port="0x12"/>
+      <alias name="pci.3"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x2"/>
+    </controller>
+    <controller type="pci" index="4" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="4" port="0x13"/>
+      <alias name="pci.4"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x3"/>
+    </controller>
+    <controller type="pci" index="5" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="5" port="0x14"/>
+      <alias name="pci.5"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x4"/>
+    </controller>
+    <controller type="pci" index="6" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="6" port="0x15"/>
+      <alias name="pci.6"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x5"/>
+    </controller>
+    <controller type="pci" index="7" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="7" port="0x16"/>
+      <alias name="pci.7"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x6"/>
+    </controller>
+    <controller type="pci" index="8" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="8" port="0x17"/>
+      <alias name="pci.8"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x02" function="0x7"/>
+    </controller>
+    <controller type="pci" index="9" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="9" port="0x18"/>
+      <alias name="pci.9"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x0"/>
+    </controller>
+    <controller type="pci" index="10" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="10" port="0x19"/>
+      <alias name="pci.10"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x1"/>
+    </controller>
+    <controller type="pci" index="11" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="11" port="0x1a"/>
+      <alias name="pci.11"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x2"/>
+    </controller>
+    <controller type="pci" index="12" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="12" port="0x1b"/>
+      <alias name="pci.12"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x3"/>
+    </controller>
+    <controller type="pci" index="13" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="13" port="0x1c"/>
+      <alias name="pci.13"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x4"/>
+    </controller>
+    <controller type="pci" index="14" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="14" port="0x1d"/>
+      <alias name="pci.14"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x5"/>
+    </controller>
+    <controller type="pci" index="15" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <target chassis="15" port="0x1e"/>
+      <alias name="pci.15"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x03" function="0x6"/>
+    </controller>
+    <controller type="pci" index="16" model="pcie-root-port">
+      <model name="pcie-root-port"/>
+      <alias name="pci.16"/>
+      <address type="pci" domain="0x0000" bus="0x07" slot="0x00" function="0x0"/>
+    </controller>
+    <controller type="sata" index="0">
+      <alias name="ide"/>
+      <address type="pci" domain="0x0000" bus="0x00" slot="0x1f" function="0x02"/>
+    </controller>
+    <controller type="virtio-serial" index="0">
+      <alias name="virtio-serial0"/>
+      <address type="pci" domain="0x0000" bus="0x03" slot="0x00" function="0x0"/>
+    </controller>
+    <controller type="scsi" index="0" model="lsilogic">
+      <model name="lsilogic"/>
+      <alias name="scsi0"/>
+      <address type="pci" domain="0x0000" bus="0x10" slot="0x01" function="0x0"/>
+    </controller>
+  </devices>
+</domain>
diff --git a/nix/modules/libvirtnix/config.nix b/nix/modules/libvirtnix/config.nix
new file mode 100644
index 0000000..5272ce2
--- /dev/null
+++ b/nix/modules/libvirtnix/config.nix
@@ -0,0 +1,583 @@
+{
+  services.emile.libvirtnix = {
+    enable = true;
+    vm = {
+      "alan" = {
+        vm_type = "kvm";
+        id = "1337";
+        name = "blub";
+        uuid = "cafebabe-d474-452b-80f4-c951c39bcf74";
+
+        metadata.libosinfo = "https://libosinfo.org/xmlns/libvirt/domain/1.0";
+        metadata.libosinfo_os = "https://nixos.org/nixos/unknown";
+
+        memory.unit = "KiB";
+        memory.value = 2097152;
+
+        currentMemory.unit = "KiB";
+        currentMemory.value = 2097152;
+
+        vcpu.placement = "static";
+        vcpu.count = 3;
+
+        resource.partition = "/machine";
+
+        os = {
+          type = {
+            arch = "x86_64";
+            machine = "pc-q35-3.1";
+            value = "hvm";
+          };
+
+          loader = {
+            readonly = "yes";
+            type = "pflash";
+            value = "/usr/share/OVMF/OVMF_CODE.fd";
+          };
+
+          nvram.value = "/var/lib/libvirt/qemu/nvram/fileserver2_VARS.fd";
+
+          boot.dev = "hd";
+        };
+
+        features = {
+          acpi = true;
+          apic = true;
+          vmport = {
+            state = "off";
+          };
+        };
+
+        cpu = {
+          mode = "host-passthrough";
+          check = "none";
+          migratable = "on";
+        };
+
+        clock = {
+          offset = "utc";
+          timer = [
+            {
+              name = "rtc";
+              tickpolicy = "catchup";
+            }
+            {
+              name = "pit";
+              tickpolicy = "delay";
+            }
+            {
+              name = "hpet";
+              present = "no";
+            }
+          ];
+        };
+
+        on_poweroff = "destroy";
+        on_reboot = "restart";
+        on_crash = "destroy";
+
+        pm = {
+          suspend-to-mem = "no";
+          suspend-to-disk = "no";
+        };
+
+        devices = {
+          emulators = [
+            {
+              value = "/usr/bin/qemu-system-x86_64";
+            }
+          ];
+
+          disks = [
+            {
+              type = "block";
+              device = "disk";
+              driver = {
+                name = "qemu";
+                type = "raw";
+                cache = "none";
+                io = "native";
+                discard = "unmap";
+              };
+              source = {
+                dev = "/dev/vmstorage/fileserver2";
+                index = 3;
+              };
+              backingStore = true;
+              target = {
+                dev = "vda";
+                bus = "virtio";
+              };
+              alias = {
+                name = "virtio-disk0";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x04";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+            {
+              type = "block";
+              device = "disk";
+              driver = {
+                name = "qemu";
+                type = "raw";
+              };
+              source = {
+                dev = "/dev/mapper/storage1";
+                index = 2;
+              };
+              backingStore = true;
+              target = {
+                dev = "vdb";
+                bus = "virtio";
+              };
+              alias = {
+                name = "virtio-disk1";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x08";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+            {
+              type = "block";
+              device = "disk";
+              driver = {
+                name = "qemu";
+                type = "raw";
+              };
+              source = {
+                dev = "/dev/maper/storage2";
+                index = 1;
+              };
+              backingStore = true;
+              target = {
+                dev = "vdc";
+                bus = "virtio";
+              };
+              alias = {
+                name = "virtio-disk2";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x09";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+          ];
+
+          controllers = [
+            {
+              type = "usb";
+              index = 0;
+              model = "qemu-xhci";
+              ports = 15;
+              alias = {
+                name = "usb";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x02";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+            {
+              type = "pci";
+              index = 0;
+              model = "pci-root";
+              alias = {
+                name = "pcie.0";
+              };
+            }
+            {
+              type = "pci";
+              index = 1;
+              model = "pcie-root-port";
+              target = {
+                chassis = 1;
+                port = "0x10";
+              };
+              alias = {
+                name = "pci.1";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x0";
+                multifunction = "on";
+              };
+            }
+            {
+              type = "pci";
+              index = 2;
+              model = "pcie-root-port";
+              target = {
+                chassis = 2;
+                port = "0x11";
+              };
+              alias = {
+                name = "pci.2";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x0";
+                multifunction = "on";
+              };
+            }
+            {
+              type = "pci";
+              index = 3;
+              model = "pcie-root-port";
+              target = {
+                chassis = 3;
+                port = "0x12";
+              };
+              alias = {
+                name = "pci.3";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x2";
+              };
+            }
+            {
+              type = "pci";
+              index = 4;
+              model = "pcie-root-port";
+              target = {
+                chassis = 4;
+                port = "0x13";
+              };
+              alias = {
+                name = "pci.4";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x3";
+              };
+            }
+            {
+              type = "pci";
+              index = 5;
+              model = "pcie-root-port";
+              target = {
+                chassis = 5;
+                port = "0x14";
+              };
+              alias = {
+                name = "pci.5";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x4";
+              };
+            }
+            {
+              type = "pci";
+              index = 6;
+              model = "pcie-root-port";
+              target = {
+                chassis = 6;
+                port = "0x15";
+              };
+              alias = {
+                name = "pci.6";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x5";
+              };
+            }
+            {
+              type = "pci";
+              index = 7;
+              model = "pcie-root-port";
+              target = {
+                chassis = 7;
+                port = "0x16";
+              };
+              alias = {
+                name = "pci.7";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x6";
+              };
+            }
+            {
+              type = "pci";
+              index = 8;
+              model = "pcie-root-port";
+              target = {
+                chassis = 8;
+                port = "0x17";
+              };
+              alias = {
+                name = "pci.8";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x02";
+                function = "0x7";
+              };
+            }
+            {
+              type = "pci";
+              index = 9;
+              model = "pcie-root-port";
+              target = {
+                chassis = 9;
+                port = "0x18";
+              };
+              alias = {
+                name = "pci.9";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x0";
+                multifunction = "on";
+              };
+            }
+            {
+              type = "pci";
+              index = 10;
+              model = "pcie-root-port";
+              target = {
+                chassis = 10;
+                port = "0x19";
+              };
+              alias = {
+                name = "pci.10";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x1";
+              };
+            }
+            {
+              type = "pci";
+              index = 11;
+              model = "pcie-root-port";
+              target = {
+                chassis = 11;
+                port = "0x1a";
+              };
+              alias = {
+                name = "pci.11";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x2";
+              };
+            }
+            {
+              type = "pci";
+              index = 12;
+              model = "pcie-root-port";
+              target = {
+                chassis = 12;
+                port = "0x1b";
+              };
+              alias = {
+                name = "pci.12";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x3";
+              };
+            }
+            {
+              type = "pci";
+              index = 13;
+              model = "pcie-root-port";
+              target = {
+                chassis = 13;
+                port = "0x1c";
+              };
+              alias = {
+                name = "pci.13";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x4";
+              };
+            }
+            {
+              type = "pci";
+              index = 14;
+              model = "pcie-root-port";
+              target = {
+                chassis = 14;
+                port = "0x1d";
+              };
+              alias = {
+                name = "pci.14";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x5";
+              };
+            }
+            {
+              type = "pci";
+              index = 15;
+              model = "pcie-root-port";
+              target = {
+                chassis = 15;
+                port = "0x1e";
+              };
+              alias = {
+                name = "pci.15";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x03";
+                function = "0x6";
+              };
+            }
+            {
+              type = "pci";
+              index = 16;
+              model = "pcie-root-port";
+              alias = {
+                name = "pci.16";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x07";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+            {
+              type = "sata";
+              index = 0;
+              alias = {
+                name = "ide";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x00";
+                slot = "0x1f";
+                function = "0x02";
+              };
+            }
+            {
+              type = "virtio-serial";
+              index = 0;
+              alias = {
+                name = "virtio-serial0";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x03";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+            {
+              type = "scsi";
+              index = 0;
+              model = "lsilogic";
+              alias = {
+                name = "scsi0";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x10";
+                slot = "0x01";
+                function = "0x0";
+              };
+            }
+          ];
+          interfaces = [
+            {
+              type = "bridge";
+              mac = {
+                address = "52:54:00:bf:ba:88";
+              };
+              source = {
+                network = "services";
+                portid = "6c3a3512-d823-4521-9043-3a91a65682f5";
+                bridge = "br7";
+              };
+              target = {
+                dev = "vnet2";
+              };
+              model = {
+                type = "virtio";
+              };
+              alias = {
+                name = "net0";
+              };
+              address = {
+                type = "pci";
+                domain = "0x0000";
+                bus = "0x01";
+                slot = "0x00";
+                function = "0x0";
+              };
+            }
+          ];
+        };
+      };
+    };
+  };
+}
diff --git a/nix/modules/libvirtnix/domain.nix b/nix/modules/libvirtnix/domain.nix
index a028c7d..d1a5b9c 100644
--- a/nix/modules/libvirtnix/domain.nix
+++ b/nix/modules/libvirtnix/domain.nix
@@ -1,701 +1,1443 @@
-{ pkgs, lib, ... }:
-
-{
-  options = with lib; {
-
-    # meta, this allows defining the package used for each vm individually, defaults should be sane
-    packages = mkOption {
-      type = types.submodule {
-        options = {
-          libvirt = mkPackageOption pkgs "libvirt" { };
-          qemu = mkPackageOption pkgs "qemu" { };
-        };
-      };
+{ config, lib, ... }:
+
+let
+  mkTag = import ./xml.nix { inherit lib; };
+  pkgs = import <nixpkgs> { };
+
+  stringOption =
+    {
+      default ? "",
+    }:
+    lib.mkOption {
+      type = lib.types.str;
+      default = "${default}";
     };
 
-    # attributs for the root `<domain ...>` node
-    type = mkOption {
-      type = types.enum [
-        "xen"
-        "hvm"
-        "kvm"
-        "qemu"
-        "lxc"
-      ];
-      default = "kvm";
-      example = "qemu";
-      description = ''
-        				hypervisor used for running the domain
-
-        				allowed values are driver specific, if missing, plz send PR
-
-        				hvm since since 8.1.0 and QEMU 2.12
-        			'';
-    };
-
-    id = mkOption {
-      type = types.int;
-      default = 0;
-      example = 42;
-    };
-
-    # General metadata
-    name = mkOption {
-      type = types.str;
-      example = "MyGuest";
-    };
-
-    uuid = mkOption {
-      type = types.str;
-      example = "3e3fce45-4f53-4fa7-bb32-11f34168b82b";
-    };
-
-    genid = mkOption {
-      type = types.str;
-      example = "43dc0cf8-809b-4adb-9bea-a9abb5f3d90e";
-    };
-
-    title = mkOption {
-      type = types.str;
-      example = "A short description - title - of the domain";
+  # shorthand for an option with an enum, no default value
+  enumOpt =
+    options:
+    lib.mkOption {
+      type = lib.types.nullOr (lib.types.enum options);
+      default = null;
     };
+in
+{
+  options = {
+    services.emile.libvirtnix =
+      let
+
+        address = lib.mkOption {
+          default = null;
+          type = lib.types.nullOr (
+            lib.types.submodule {
+              options = {
+                type = enumOpt [ "pci" ];
+                domain = lib.mkOption {
+                  type = lib.types.str;
+                  example = "0x0000";
+                };
+                bus = lib.mkOption {
+                  type = lib.types.str;
+                  example = "0x4";
+                };
+                slot = lib.mkOption {
+                  type = lib.types.str;
+                  example = "0x00";
+                };
+                function = lib.mkOption {
+                  type = lib.types.str;
+                  example = "0x00";
+                };
+                multifunction = lib.mkOption {
+                  type = lib.types.enum [
+                    "on"
+                    "off"
+                  ];
+                  example = "on";
+                  default = "off";
+                };
+              };
+            }
+          );
+        };
 
-    description = mkOption {
-      type = types.str;
-      example = "Some human readable description";
-    };
+        interface = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              type = enumOpt [ "bridge" ];
+              mac = lib.mkOption {
+                type = lib.types.submodule {
+                  options = {
+                    address = lib.types.mkOption {
+                      type = lib.types.str;
+                    };
+                  };
+                };
+              };
+              source = lib.mkOption {
+                type = lib.types.submodule {
+                  options = {
+                    network = lib.types.mkOption {
+                      type = lib.types.str;
+                    };
+                    portid = lib.types.mkOption {
+                      type = lib.types.str;
+                    };
+                    bridge = lib.types.mkOption {
+                      type = lib.types.str;
+                    };
+                  };
+                };
+              };
+              target = lib.mkOption {
+                type = lib.types.submodule {
+                  options = {
+                    dev = lib.types.mkOption {
+                      type = lib.types.str;
+                    };
+                  };
+                };
+              };
+              model = lib.mkOption {
+                type = lib.types.submodule {
+                  options = {
+                    type = lib.types.mkOption {
+                      type = enumOpt [ "virtio" ];
+                    };
+                  };
+                };
+              };
+              alias = lib.mkOption {
+                type = lib.types.submodule {
+                  options = {
+                    dev = lib.types.mkOption {
+                      name = lib.types.str;
+                    };
+                  };
+                };
+              };
+              inherit address;
+            };
+          };
+        };
 
-    metadata = mkOption {
-      type = types.str;
-      default = "";
-      example = ''
-        			  <metadata>
-        			    <app1:foo xmlns:app1="http://app1.org/app1/">..</app1:foo>
-        			    <app2:bar xmlns:app2="http://app1.org/app2/">..</app2:bar>
-        			  </metadata>
-        			'';
-    };
 
-    os = mkOption {
-      type = types.submodule {
-        options = {
-          firmware = mkOption {
-            type = types.enum [
-              "bios"
-              "efi"
+        controller = lib.types.submodule {
+          options = {
+            type = enumOpt [
+              "usb"
+              "pci"
+              "sata"
+              "virtio-serial"
+              "scsi"
             ];
-            default = "efi";
-            example = "bios";
+            index = lib.mkOption { type = lib.types.int; };
+            model = lib.mkOption {
+              type = lib.types.nullOr lib.types.str;
+              default = null;
+            };
+            ports = lib.mkOption {
+              type = lib.types.nullOr lib.types.int;
+              default = null;
+            };
+            alias = lib.mkOption {
+              type = lib.types.submodule {
+                options = {
+                  name = lib.mkOption { type = lib.types.str; };
+                };
+              };
+            };
+            target = lib.mkOption {
+              type = lib.types.nullOr (
+                lib.types.submodule {
+                  options = {
+                    chassis = lib.mkOption { type = lib.types.int; };
+                    port = lib.mkOption { type = lib.types.str; };
+                  };
+                }
+              );
+              default = null;
+            };
+            inherit address;
           };
-          type = mkOption {
-            type = types.enum [
-              "hvm"
-              "linux"
-            ];
-            default = "hvm";
-            example = "linux";
-          };
-          arch = mkOption { type = types.str; };
-          machine = mkOption { type = types.str; };
-
-          # TODO(emile): features
-          # Search for the following...
-          # > When using firmware auto-selection there are different features enabled in the firmwares
-          # ... here: https://libvirt.org/formatdomain.html
-          # can't bother to implement this now
-
-          # features = mkOption {
-          # 	type = types.submodule {
-          # 		options = {
-          # 			enabled = {
-          # 				type = types.enum [ "yes" "no" ];
-          # 			};
-          # 			name = {};
-          # 		};
-          # 	};
-          # };
-
-          # such as:
-          # <loader readonly='yes' secure='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
-          loader = mkOption {
-            type = types.submodule {
-              options = {
-                readonly = mkOption {
-                  type = types.enum [
-                    "yes"
-                    "no"
-                  ];
+        };
+
+        emulator = lib.types.submodule {
+          options = {
+            value = lib.mkOption { type = lib.types.str; };
+          };
+        };
+
+        disk = lib.types.submodule {
+          options = {
+            type = enumOpt [ "block" ];
+            device = enumOpt [ "disk" ];
+            driver = lib.mkOption {
+              type = lib.types.submodule {
+                options = {
+                  name = lib.mkOption { type = lib.types.str; };
+                  type = enumOpt [ "raw" ];
+                  cache = enumOpt [ "none" ];
+                  io = enumOpt [ "native" ];
+                  discard = enumOpt [ "unmap" ];
                 };
-                type = mkOption {
-                  type = types.enum [
-                    "rom"
-                    "pflash"
-                  ];
+              };
+            };
+            source = lib.mkOption {
+              type = lib.types.submodule {
+                options = {
+                  dev = lib.mkOption { type = lib.types.str; };
+                  index = lib.mkOption { type = lib.types.int; };
+                };
+              };
+            };
+            backingStore = lib.mkOption { type = lib.types.bool; };
+            target = lib.mkOption {
+              type = lib.types.submodule {
+                options = {
+                  dev = lib.mkOption { type = lib.types.str; };
+                  bus = enumOpt [ "virtio" ];
+                };
+              };
+            };
+            alias = lib.mkOption {
+              type = lib.types.submodule {
+                options = {
+                  name = lib.mkOption { type = lib.types.str; };
                 };
-                secure = mkOption {
-                  type = types.enum [
+              };
+            };
+            inherit address;
+          };
+        };
+
+        devices = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              emulators = lib.mkOption {
+                type = lib.types.listOf emulator;
+              };
+              disks = lib.mkOption {
+                type = lib.types.listOf disk;
+              };
+              controllers = lib.mkOption {
+                type = lib.types.listOf controller;
+              };
+              interfaces = lib.mkOption {
+                type = lib.types.listOf interface;
+              };
+            };
+          };
+        };
+
+        pm = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              suspend-to-mem = lib.mkOption {
+                type = lib.types.nullOr (
+                  lib.types.enum [
                     "yes"
                     "no"
-                  ];
-                };
-                stateless = mkOption {
-                  type = types.enum [
+                  ]
+                );
+                default = null;
+                example = "yes";
+              };
+              suspend-to-disk = lib.mkOption {
+                type = lib.types.nullOr (
+                  lib.types.enum [
                     "yes"
                     "no"
-                  ];
-                };
-                format = mkOption {
-                  type = types.enum [
-                    "raw"
-                    "qcow2"
-                  ];
-                };
-                value = mkOption {
-                  type = types.str;
-                  example = "/usr/share/OVMF/OVMF_CODE.fd";
-                };
+                  ]
+                );
+                default = null;
+                example = "yes";
               };
             };
           };
+        };
 
-          # <nvram type='network'>
-          #   <source protocol='iscsi' name='iqn.2013-07.com.example:iscsi-nopool/0'>
-          #     <host name='example.com' port='6000'/>
-          #     <auth username='myname'>
-          #       <secret type='iscsi' usage='mycluster_myname'/>
-          #     </auth>
-          #   </source>
-          # </nvram>
-
-          # nvram = {
-          # 	type = "network";
-          # 	source = {
-          # 		protocol = "iscsi";
-          # 		name = "iqn.2013-07.com.example:iscsi-nopool/0";
-          # 	};
-          # };
-
-          nvram = mkOption {
-            type = types.submodule {
-              options = {
-                type = mkOption {
-                  type = types.enum [
-                    "file"
-                    "block"
-                    "dir"
-                    "network"
-                    "volume"
-                    "nvme"
-                    "vhostuser"
-                    "vhostvdpa"
-                  ];
-                  default = "";
-                };
+        on_handle = lib.mkOption {
+          type = lib.types.enum [
+            "destroy"
+            "restart"
+            "preserve"
+            "rename-restart"
+          ];
+        };
 
-                source = mkOption {
-                  type = types.submodule {
+        on_poweroff = on_handle;
+        on_reboot = on_handle;
+        on_crash = on_handle;
+
+        clock = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              offset = lib.mkOption {
+                type = lib.types.str;
+                example = "utc";
+                default = "utc";
+              };
+              timer = lib.mkOption {
+                type = lib.types.listOf (
+                  lib.types.submodule {
                     options = {
-                      # TODO(emile): figure out how to conditionally allow setting the options below
-                      #              if the type defined above has been set
-
-                      # if type == file
-                      file = mkOption { type = types.str; };
-                      fdgroup = mkOption { type = types.str; };
-
-                      # if type == block
-                      dev = mkOption { type = types.str; };
-
-                      # if type == dir
-                      dir = mkOption { type = types.str; };
-
-                      # if type == network
-                      protocol = mkOption {
-                        type = types.enum [
-                          "nbd"
-                          "iscsi"
-                          "rbd"
-                          "sheepdog"
-                          "gluster"
-                          "vxhs"
-                          "nfs"
-                          "http"
-                          "https"
-                          "ftp"
-                          "ftps"
-                          "tftp"
-                          "ssh"
-                        ];
-                      };
-                      name = mkOption { type = types.str; };
-                      tls = mkOption {
-                        type = types.enum [
-                          "yes"
-                          "no"
+                      name = lib.mkOption {
+                        type = lib.types.enum [
+                          # "platform" (currently unsupported)
+                          "hpet" # xen, qemu, lxc
+                          "kvmclock" # qemu
+                          "pit" # qemu
+                          "rtc" # qemu, lxc
+                          "tsc" # xen, qemu - since 3.2.0
+
+                          # The hypervclock timer adds support for the reference time counter and the reference page for iTSC feature for guests running the Microsoft Windows operating system.
+                          "hypervclock" # qemu - since 1.2.2
+
+                          "armvtimer" # qemu - since 6.1.0
                         ];
+                        default = "";
+                        example = "";
                       };
-                      tlsHostname = mkOption { type = types.str; };
-                      query = mkOption { type = types.str; };
-
-                      # if type == volume
-                      pool = mkOption { type = types.str; };
-                      volume = mkOption { type = types.str; };
-                      mode = mkOption {
-                        type = types.enum [
-                          "direct"
-                          "host"
+                      track = lib.mkOption {
+                        # TODO(emile): Only valid for name="rtc" or name="platform".
+                        type = lib.types.enum [
+                          "boot"
+                          "guest"
+                          "wall"
+                          "realtime"
                         ];
-                        default = "host";
-                      };
-
-                      # if type == nvme
-                      type = mkOption {
-                        type = types.enum [ "pci" ];
-                        default = "pci";
-                        description = ''
-                          													When the type is `nvme`, only `pci` is supported
-                          													When the type is `vhostuser`, only `unix` is supported
-
-                          													(I've got not clue how to model this is nix, it's essentially an attribute that is overloaded based on another value)
-                          												'';
+                        default = "";
+                        example = "";
                       };
-                      managed = mkOption {
-                        type = types.enum [
-                          "yes"
-                          "no"
-                        ];
+                      tickpolicy = lib.mkOption {
+                        type = lib.types.nullOr (
+                          lib.types.enum [
+                            "catchup"
+                            "delay"
+                            "merge"
+                            "discard"
+                            "catchup"
+                          ]
+                        );
+                        default = null;
+                        example = "";
                       };
-                      namespace = mkOption {
-                        type = types.int;
-                        default = 0;
+                      present = lib.mkOption {
+                        type = lib.types.nullOr (
+                          lib.types.enum [
+                            "yes"
+                            "no"
+                          ]
+                        );
+                        default = null;
+                        example = "";
                       };
+                    };
+                  }
+                );
+                example = [
+                  {
+                    name = "rtc";
+                    tickpolicy = "catchup";
+                  }
+                  {
+                    name = "pit";
+                    tickpolicy = "delay";
+                  }
+                  {
+                    name = "hpet";
+                    present = "no";
+                  }
+                ];
+                default = [
+                  {
+                    name = "rtc";
+                    tickpolicy = "catchup";
+                  }
+                  {
+                    name = "pit";
+                    tickpolicy = "delay";
+                  }
+                  {
+                    name = "hpet";
+                    present = "no";
+                  }
+                ];
+              };
+            };
+          };
+        };
 
-                      # if type == vhostuser
-                      # type = see the type in the nvme section above
-                      path = mkOption { type = types.str; };
+        cpu = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              mode = lib.mkOption {
+                type = lib.types.str;
+                example = "host-passthrough";
+                default = "";
+              };
+              check = lib.mkOption {
+                type = lib.types.str;
+                example = "none";
+                default = "";
+              };
+              migratable = lib.mkOption {
+                type = lib.types.str;
+                example = "on";
+                default = "";
+              };
+            };
+          };
+        };
 
-                      # if type == vhostvdpa
-                      # dev = (defined above in the "type == block" section)
+        features = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              acpi = lib.mkEnableOption "enable acpi";
+              apic = lib.mkEnableOption "enable apic";
+
+              vmport = lib.mkOption {
+                type = lib.types.submodule {
+                  options = {
+                    state = lib.mkOption {
+                      type = lib.types.str;
+                      example = "off";
+                      default = "";
+                    };
+                  };
+                };
+              };
+            };
+          };
+        };
 
-                      # if type == file
-                      # if type == block
-                      # if type == volume
-                      seclabel = mkOption { type = types.str; };
+        os_boot = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              dev = lib.mkOption {
+                type = lib.types.str;
+                example = "hd";
+                default = "";
+              };
+            };
+          };
+        };
 
-                      index = mkOption {
-                        type = types.int;
-                        default = 0;
-                      };
+        os_nvram = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              value = lib.mkOption {
+                type = lib.types.str;
+                example = "/var/lib/libvirt/qemu/nvram/fileserver2_VARS.fd";
+                default = "";
+              };
+            };
+          };
+        };
 
-                      # TODO(emile): implement checks here
-                      # start here and scroll down a bit to the table
-                      # https://libvirt.org/formatdomain.html#hard-drives-floppy-disks-cdroms
-                      # if type == network
-                      host = mkOption {
-                        type = types.submodule {
-                          options = {
-                            name = mkOption { type = types.str; };
-                            port = mkOption {
-                              type = types.int;
-                              default = 0;
-                            };
-                            transport = mkOption { type = types.str; };
-                            socket = mkOption { type = types.str; };
-
-                          };
-                        };
-                      };
+        os_loader = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              readonly = lib.mkOption {
+                type = lib.types.str;
+                example = "yes";
+                default = "";
+              };
+              type = lib.mkOption {
+                type = lib.types.str;
+                example = "pflash";
+                default = "";
+              };
+              value = lib.mkOption {
+                type = lib.types.str;
+                example = "/usr/share/OVMF/OVMF_CODE.fd";
+                default = "";
+              };
+            };
+          };
+        };
 
-                      snapshot = mkOption {
-                        type = types.submodule {
-                          options = {
-                            name = mkOption { type = types.str; };
-                          };
-                        };
-                      };
+        os_type = lib.mkOption {
+          type = lib.types.submodule {
+            options = {
+              arch = lib.mkOption {
+                type = lib.types.str;
+                example = "x86_64";
+                default = "";
+              };
+              machine = lib.mkOption {
+                type = lib.types.str;
+                example = "pc-q35-3.1";
+                default = "";
+              };
+              value = lib.mkOption {
+                type = lib.types.str;
+                example = "hvm";
+                default = "";
+              };
+            };
+          };
+        };
 
-                      config = mkOption {
-                        type = types.submodule {
-                          options = {
-                            file = mkOption { type = types.str; };
-                          };
-                        };
-                      };
+        os = lib.mkOption {
+          description = "os";
+          type = lib.types.submodule {
+            options = {
+              type = os_type;
+              loader = os_loader;
+              nvram = os_nvram;
+              boot = os_boot;
+            };
+          };
+        };
 
-                      # Since 3.9.0, the auth element is supported for a disk type "network" that is using a source element with the protocol attributes "rbd", "iscsi", or "ssh".
-                      auth = mkOption {
-                        type = types.submodule {
-                          options = {
-                            type = mkOption {
-                              # type = types.enum [ "chap" ];
-                              # freeform, yet "chap" is one of the allowed options, I don't know others (yet)
-                              type = types.str;
-                            };
-                            username = mkOption { type = types.str; };
-
-                            # https://libvirt.org/formatsecret.html
-                            secret = mkOption {
-                              type = types.submodule {
-                                options = {
-                                  ephemeral = mkOption {
-                                    type = types.enum [
-                                      "yes"
-                                      "no"
-                                    ];
-                                    default = "no";
-                                  };
-                                  private = mkOption {
-                                    type = types.enum [
-                                      "yes"
-                                      "no"
-                                    ];
-                                    default = "no";
-                                  };
-
-                                  uuid = types.submodule {
-                                    options = {
-                                      value = mkOption {
-                                        type = types.str;
-                                        default = "";
-                                      };
-                                    };
-                                  };
-                                  description = types.submodule {
-                                    options = {
-                                      value = mkOption {
-                                        type = types.str;
-                                        default = "";
-                                      };
-                                    };
-                                  };
-
-                                  usage = types.submodule {
-                                    options = {
-                                      type = mkOption {
-                                        type = types.enum [
-                                          "volume"
-                                          "ceph"
-                                          "iscsi"
-                                          "tls"
-                                          "vtpm"
-                                        ];
-                                        default = "";
-                                      };
-                                      value = mkOption {
-                                        type = types.str;
-                                        default = "";
-                                      };
-
-                                      name = mkOption {
-                                        type = types.submodule {
-                                          options = {
-                                            value = mkOption { type = types.str; };
-                                          };
-                                        };
-                                      };
-
-                                      volume = mkOption {
-                                        type = types.submodule {
-                                          options = {
-                                            value = mkOption { type = types.str; };
-                                          };
-                                        };
-                                      };
-
-                                      # when using the "iscsi" type
-                                      target = mkOption {
-                                        type = types.submodule {
-                                          options = {
-                                            value = mkOption { type = types.str; };
-                                          };
-                                        };
-                                      };
-
-                                    };
-                                  };
-
-                                };
-                              };
-                            }; # end of secret
-
-                          };
-                        };
-                      }; # end of auth
-
-                      # https://libvirt.org/formatstorageencryption.html
-                      encryption = mkOption {
-                        type = types.submodule {
-                          options = {
-                            type = mkOption { type = types.str; };
-
-                            # mandatory
-                            format = mkOption {
-                              type = types.enum [
-                                "default"
-                                "qcow"
-                                "luks"
-                                "luks2"
-                                "luks-any"
-                              ];
-                            };
-
-                            engine = mkOption {
-                              type = types.enum [
-                                "qemu"
-                                "librbd"
-                              ];
-                            };
-
-                            secrets = mkOption {
-                              type = types.listOf (mkOption {
-                                type = types.submodule {
-                                  options = {
-
-                                    # mandatory
-                                    type = mkOption { type = types.enum [ "volume" ]; };
-
-                                    # uuid or usage
-                                    uuid = mkOption { type = types.str; };
-                                    usage = mkOption { type = types.str; };
-
-                                  };
-                                };
-                              });
-                            }; # end of secrets
-
-                            cipher = mkOption {
-                              type = types.submodule {
-                                options = {
-                                  name = mkOption {
-                                    type = types.str;
-                                    example = "'aes', 'des', 'cast5', 'serpent', 'twofish', etc.";
-                                  };
-                                  size = mkOption {
-                                    type = types.str;
-                                    example = "'256', '192', '128', etc.";
-                                  };
-                                  mode = mkOption {
-                                    type = types.str;
-                                    example = "'cbc', 'xts', 'ecb', etc.";
-                                  };
-                                  hash = mkOption {
-                                    type = types.str;
-                                    example = "'md5', 'sha1', 'sha256', etc.";
-                                  };
-                                };
-                              };
-                            }; # end of cipher
-
-                            ivgen = mkOption {
-                              type = types.submodule {
-                                options = {
-                                  name = mkOption {
-                                    type = types.str;
-                                    example = "'plain', 'plain64', 'essiv', etc.";
-                                  };
-                                  hash = mkOption {
-                                    type = types.str;
-                                    example = "'md5', 'sha1', 'sha256'";
-                                  };
-                                };
-                              };
-                            }; # end of ivygen
-                          };
-                        };
-                      }; # end of encryption
-
-                      # TODO(emile): reservations
-                      # Looking at the following, it seems like this can use the source element recursively
-                      # Haven't looked into how to define recursive nix options yet...
-                      # https://github.com/virt-manager/virt-manager/blob/5ddd3456a0ca9836a98fc6ca4f0b2eaab268bf47/tests/data/cli/compare/virt-install-many-devices.xml#L398-L400
-
-                      # TODO(emile): initiator
-
-                      # Based on this here:
-                      # https://github.com/virt-manager/virt-manager/blob/5ddd3456a0ca9836a98fc6ca4f0b2eaab268bf47/tests/data/cli/compare/virt-install-many-devices.xml#L440
-                      address = mkOption {
-                        type = types.submodule {
-                          options = {
-                            domain = mkOption { type = types.int; };
-                            bus = mkOption { type = types.int; };
-                            slot = mkOption { type = types.int; };
-                            function = mkOption { type = types.int; };
-                          };
-                        };
-                      }; # end of address
-
-                      # TODO(emile): slices
-                      # Didn't find any usage of it, why is there documentation for it then?
-
-                      ssl = mkOption {
-                        type = types.submodule {
-                          options = {
-                            verify = mkOption {
-                              type = types.enum [
-                                "yes"
-                                "no"
-                              ];
-                            };
-                          };
-                        };
-                      }; # end of ssl
-
-                      # TODO(emile): cookies for http and https
-
-                      readahead = mkOption {
-                        type = types.submodule {
-                          options = {
-                            size = mkOption { type = types.int; };
-                          };
-                        };
-                      }; # end of readahead
-
-                      timeout = mkOption {
-                        type = types.submodule {
-                          options = {
-                            seconds = mkOption { type = types.int; };
-                          };
-                        };
-                      }; # end of timeout
-
-                      identity = mkOption {
-                        type = types.submodule {
-                          options = {
-                            user = mkOption { type = types.str; };
-                            group = mkOption { type = types.str; };
-
-                            # if ssh
-                            # required
-                            username = mkOption { type = types.str; };
-
-                            # one of these:
-                            agentsock = mkOption { type = types.str; };
-                            keyfile = mkOption { type = types.str; };
-                          };
-                        };
-                      }; # end of identity
-
-                      # disk type "vhostuser"
-                      reconnect = mkOption {
-                        type = types.submodule {
-                          options = {
-                            # mandatory
-                            enabled = mkOption {
-                              type = types.enum [
-                                "yes"
-                                "no"
-                              ];
-                            };
-                            timeout = mkOption {
-                              type = types.int;
-                              description = "seconds";
-                            };
-
-                            # optional for disk type network and protocol nbd
-                            delay = mkOption {
-                              type = types.int;
-                              default = 0;
-                              description = "seconds";
-                            };
-                          };
-                        };
-                      }; # end of reconnect
-
-                      # disk type "ssh"
-                      knownHosts = mkOption {
-                        type = types.submodule {
-                          options = {
-                            path = mkOption { type = types.str; };
-                          };
-                        };
-                      }; # end of knownHosts
-
-                      dataStore = mkOption {
-                        type = types.submodule {
-                          options = {
-                            # TODO(emile): can accept the same types as a `source` element
-                            type = mkOption { type = types.str; };
-
-                            # TODO(emile): can accept format and source subelements
-                            # this is (once again) recursive for the source, stil need to figure
-                            # out how to handle this, just not now
-                          };
-                        };
-                      }; # end of dataStore
-
-                      startupPolicy = mkOption {
-                        type = types.enum [
-                          "mandatory"
-                          "requisite"
-                          "optional"
-                        ];
-                        default = "mandatory";
-                      }; # end of startupPolicy
-
-                      backingStore = mkOption {
-                        type = types.submodule {
-                          options = {
-                            type = mkOption {
-                              # TODO(emile): can accept the same types as a `source` element
-                              type = types.str;
-                            };
-
-                            index = mkOption {
-                              # TODO(emile): figure out if this can just be an int
-                              type = types.str;
-                            };
-
-                            # TODO(emile): can use the following sub elements:
-                            # - format
-                            # - source
-                            # - backingStore
-                          };
-                        };
-                      }; # end of backingStore
-
-                      mirror = mkOption {
-                        type = types.submodule {
-                          options = {
-                            api = mkOption {
-                              type = types.enum [
-                                "copy"
-                                "active-commit"
-                              ];
-                            };
-                            ready = mkOption {
-                              type = types.enum [
-                                "yes"
-                                "abort"
-                                "pivot"
-                              ];
-                            };
-                            # TODO(emile): can use the following sub elements:
-                            # - type (disk types)
-                            # - format
-                            # - source
-                            # - file
-                          };
-                        };
-                      }; # end of mirror
+        resource = lib.mkOption {
+          description = "resource";
+          type = lib.types.submodule {
+            options = {
+              partition = lib.mkOption {
+                type = lib.types.str;
+                example = "/machine";
+                default = "";
+              };
+            };
+          };
+        };
 
-                    };
-                  };
-                }; # end of source
+        vcpu = lib.mkOption {
+          description = "vcpu";
+          type = lib.types.submodule {
+            options = {
+              placement = lib.mkOption {
+                # TODO(emile): fill up
+                type = lib.types.enum [ "static" ];
+                example = "static";
+                default = "static";
+              };
+              count = lib.mkOption {
+                type = lib.types.int;
+                example = 4;
+                default = 1;
+              };
+            };
+          };
+        };
+
+        mem = lib.mkOption {
+          description = "memory";
+          type = lib.types.submodule {
+            options = {
+              unit = lib.mkOption {
+                # TODO(emile): fill up
+                type = lib.types.enum [
+                  "KiB"
+                  "MiB"
+                  "GiB"
+                  "TiB"
+                ];
+                example = "KiB";
+                default = "KiB";
+              };
+              value = lib.mkOption {
+                type = lib.types.int;
+                example = 2097152;
+                default = 1000;
+              };
+            };
+          };
+        };
 
+        memory = mem;
+        currentMemory = mem;
+
+        metadata = lib.mkOption {
+          description = "metadata submodule";
+          type = lib.types.submodule {
+            options = {
+              libosinfo = lib.mkOption {
+                type = lib.types.str;
+                example = "https://libosinfo.org/xmlns/libvirt/domain/1.0";
+                default = "";
+              };
+              libosinfo_os = lib.mkOption {
+                type = lib.types.str;
+                example = "https://nixos.org/nixos/unknown";
+                default = "";
               };
             };
-          }; # end of nvram
+          };
+        };
 
+        vm = lib.types.submodule {
+          options = {
+            vm_type = stringOption { default = "kvm"; };
+            id = stringOption { };
+            name = stringOption { };
+            uuid = stringOption { };
+
+            inherit
+              metadata
+              memory
+              currentMemory
+              vcpu
+              resource
+              os
+              features
+              cpu
+              clock
+              on_poweroff
+              on_reboot
+              on_crash
+              pm
+              devices
+              ;
+          };
+        };
+      in
+      {
+        enable = lib.mkEnableOption "Enable r2wars-web";
+
+        # Temporary output we write the domain to, in order to inspect the correctness of
+        # the generated xml
+        output.domain = lib.mkOption { type = lib.types.path; };
+
+        # An attrset of VMs
+        vm = lib.mkOption {
+          description = "VMs to define";
+          type = lib.types.attrsOf vm;
         };
       };
-    };
+  };
 
-    memory = lib.mkOption {
-      type = lib.types.int;
-      default = 1024;
-      example = 2048;
-      description = ''
-        The amount of memory to provide to the VM
-      '';
-    };
+  config = lib.mkIf config.services.emile.libvirtnix.enable {
+    services.emile.libvirtnix =
+      let
+        vm = config.services.emile.libvirtnix.vm;
+
+        # try this in a nix repl
+        # nix-repl> mkTag = import ./xml.nix { lib = (import <nixpkgs> {}).lib; }
+        # nix-repl> :p let func = (x: mkTag {name=x.variant;}); case = {"emulator" = func; "disk" = func;}; in map (x: case."${x.variant}" x) [ {variant = "emulator"; value = "asd";} {variant = "disk"; type = "block"; } ]
+
+        emulator =
+          x:
+          mkTag {
+            name = "emulator";
+            value = x.value;
+          };
+
+        interface =
+          x:
+          mkTag {
+            name = "interface";
+            args = [
+              (
+                if x.type != null then
+                  {
+                    key = "type";
+                    val = x.type;
+                  }
+                else
+                  ""
+              )
+            ];
+            children = [
+              # (
+              #   if x.mac != null then
+              #     (mkTag {
+              #       name = "mac";
+              #       args = [
+              #         {
+              #           key = "address";
+              #           val = x.mac.address;
+              #         }
+              #       ];
+              #       closing = false;
+              #     })
+              #   else
+              #     ""
+              # )
+              #(
+              #  if x.source != null then
+              #    (mkTag {
+              #      name = "source";
+              #      args = [
+              #        {
+              #          key = "network";
+              #          val = x.source.network;
+              #        }
+              #        {
+              #          key = "portid";
+              #          val = x.source.portid;
+              #        }
+              #        {
+              #          key = "bridge";
+              #          val = x.source.bridge;
+              #        }
+              #      ];
+              #      closing = false;
+              #    })
+              #  else
+              #    ""
+              #)
+              #(
+              #  if x.target != null then
+              #    (mkTag {
+              #      name = "target";
+              #      args = [
+              #        {
+              #          key = "dev";
+              #          val = x.target.dev;
+              #        }
+              #      ];
+              #    })
+              #  else
+              #    ""
+              #)
+              #(
+              #  if x.model != null then
+              #    (mkTag {
+              #      name = "model";
+              #      args = [
+              #        {
+              #          key = "type";
+              #          val = x.model.type;
+              #        }
+              #      ];
+              #    })
+              #  else
+              #    ""
+              #)
+              #(
+              #  if x.alias!= null then
+              #    (mkTag {
+              #      name = "alias";
+              #      args = [
+              #        {
+              #          key = "name";
+              #          val = x.alias.name;
+              #        }
+              #      ];
+              #    })
+              #  else
+              #    ""
+              #)
+              #(
+              #  if x.address != null then
+              #    mkTag {
+              #      name = "address";
+              #      args = [
+              #        (
+              #          if x.address.type != null then
+              #            {
+              #              key = "type";
+              #              val = x.address.type;
+              #            }
+              #          else
+              #            ""
+              #        )
+              #        {
+              #          key = "domain";
+              #          val = x.address.domain;
+              #        }
+              #        {
+              #          key = "bus";
+              #          val = x.address.bus;
+              #        }
+              #        {
+              #          key = "slot";
+              #          val = x.address.slot;
+              #        }
+              #        {
+              #          key = "function";
+              #          val = x.address.function;
+              #        }
+              #      ];
+              #      closing = false;
+              #    }
+              #  else
+              #    ""
+              #)
+            ];
+          };
+
+        controller =
+          x:
+          mkTag {
+            name = "controller";
+            args = [
+              (
+                if x.type != null then
+                  {
+                    key = "type";
+                    val = x.type;
+                  }
+                else
+                  ""
+              )
+              (
+                if x.index != null then
+                  {
+                    key = "index";
+                    val = "${toString x.index}";
+                  }
+                else
+                  ""
+              )
+              (
+                if x.model != null then
+                  {
+                    key = "model";
+                    val = x.model;
+                  }
+                else
+                  ""
+              )
+              (
+                if x.ports != null then
+                  {
+                    key = "ports";
+                    val = "${toString x.ports}";
+                  }
+                else
+                  ""
+              )
+            ];
+            children = [
+              # TODO(emile): figure out if the arg in controller is really the same as the
+              #              one given in model. The configs I've got let it seem so
+              # <controller type="pci" index="1" model="pcie-root-port">
+              #   <model name="pcie-root-port"/>
+              (
+                if x.model != null then
+                  (mkTag {
+                    name = "model";
+                    args = [
+                      {
+                        key = "name";
+                        val = x.model;
+                      }
+                    ];
+                    closing = false;
+                  })
+                else
+                  ""
+              )
+              (
+                if x.target != null then
+                  (mkTag {
+                    name = "target";
+                    args = [
+                      {
+                        key = "chassis";
+                        val = "${toString x.target.chassis}";
+                      }
+                      {
+                        key = "port";
+                        val = x.target.port;
+                      }
+                    ];
+                  })
+                else
+                  ""
+              )
+              (mkTag {
+                name = "alias";
+                args = [
+                  (
+                    if x.alias.name != null then
+                      {
+                        key = "name";
+                        val = x.alias.name;
+                      }
+                    else
+                      ""
+                  )
+                ];
+                closing = false;
+              })
+
+              (
+                if x.address != null then
+                  mkTag {
+                    name = "address";
+                    args = [
+                      (
+                        if x.address.type != null then
+                          {
+                            key = "type";
+                            val = x.address.type;
+                          }
+                        else
+                          ""
+                      )
+                      {
+                        key = "domain";
+                        val = x.address.domain;
+                      }
+                      {
+                        key = "bus";
+                        val = x.address.bus;
+                      }
+                      {
+                        key = "slot";
+                        val = x.address.slot;
+                      }
+                      {
+                        key = "function";
+                        val = x.address.function;
+                      }
+                    ];
+                    closing = false;
+                  }
+                else
+                  ""
+              )
+            ];
+          };
+
+        disk =
+          x:
+          mkTag {
+            name = "disk";
+            args = [
+              {
+                key = "type";
+                val = x.type;
+              }
+              {
+                key = "device";
+                val = x.device;
+              }
+            ];
+            children = [
+              (mkTag {
+                name = "driver";
+                args = [
+                  (
+                    if x.driver.name != null then
+                      {
+                        key = "name";
+                        val = x.driver.name;
+                      }
+                    else
+                      ""
+                  )
+                  (
+                    if x.driver.type != null then
+                      {
+                        key = "type";
+                        val = x.driver.type;
+                      }
+                    else
+                      ""
+                  )
+                  (
+                    if x.driver.cache != null then
+                      {
+                        key = "cache";
+                        val = x.driver.cache;
+                      }
+                    else
+                      ""
+                  )
+                  (
+                    if x.driver.io != null then
+                      {
+                        key = "io";
+                        val = x.driver.io;
+                      }
+                    else
+                      ""
+                  )
+                  (
+                    if x.driver.discard != null then
+                      {
+                        key = "discard";
+                        val = x.driver.discard;
+                      }
+                    else
+                      ""
+                  )
+                ];
+                closing = false;
+              })
+              (mkTag {
+                name = "source";
+                args = [
+                  {
+                    key = "dev";
+                    val = x.source.dev;
+                  }
+                  {
+                    key = "index";
+                    val = "${toString x.source.index}";
+                  }
+                ];
+                closing = false;
+              })
+              (
+                if x.backingStore == true then
+                  mkTag {
+                    name = "backingStore";
+                    closing = false;
+                  }
+                else
+                  ""
+              )
+              (mkTag {
+                name = "target";
+                args = [
+                  {
+                    key = "dev";
+                    val = x.target.dev;
+                  }
+                  {
+                    key = "bus";
+                    val = x.target.bus;
+                  }
+                ];
+                closing = false;
+              })
+              (mkTag {
+                name = "alias";
+                args = [
+                  {
+                    key = "name";
+                    val = x.alias.name;
+                  }
+                ];
+                closing = false;
+              })
+              (mkTag {
+                name = "address";
+                args = [
+                  {
+                    key = "type";
+                    val = x.address.type;
+                  }
+                  {
+                    key = "domain";
+                    val = x.address.domain;
+                  }
+                  {
+                    key = "bus";
+                    val = x.address.bus;
+                  }
+                  {
+                    key = "slot";
+                    val = x.address.slot;
+                  }
+                  {
+                    key = "function";
+                    val = x.address.function;
+                  }
+                ];
+                closing = false;
+              })
+            ];
+            value = x.type;
+          };
+
+        devices =
+          vm_name:
+          mkTag {
+            name = "devices";
+            children =
+              map (x: emulator x) vm.${vm_name}.devices.emulators
+              ++ map (x: disk x) vm.${vm_name}.devices.disks
+              ++ map (x: controller x) vm.${vm_name}.devices.controllers
+              ++ map (x: interface x) vm.${vm_name}.devices.interfaces;
+          };
+
+        pm =
+          vm_name:
+          mkTag {
+            name = "pm";
+            children =
+              let
+                suspend-to-mem = mkTag {
+                  name = "suspend-to-mem";
+                  args = [
+                    {
+                      key = "enabled";
+                      val = vm.${vm_name}.pm.suspend-to-mem;
+                    }
+                  ];
+                  closing = false;
+                };
+
+                suspend-to-disk = mkTag {
+                  name = "suspend-to-disk";
+                  args = [
+                    {
+                      key = "enabled";
+                      val = vm.${vm_name}.pm.suspend-to-disk;
+                    }
+                  ];
+                  closing = false;
+                };
+              in
+              [
+                suspend-to-mem
+                suspend-to-disk
+              ];
+          };
+
+        on_handler =
+          { vm_name, tag }:
+          mkTag {
+            name = "${tag}";
+            value = vm.${vm_name}.${tag};
+          };
+
+        on_poweroff =
+          vm_name:
+          on_handler {
+            inherit vm_name;
+            tag = "on_poweroff";
+          };
+        on_reboot =
+          vm_name:
+          on_handler {
+            inherit vm_name;
+            tag = "on_reboot";
+          };
+        on_crash =
+          vm_name:
+          on_handler {
+            inherit vm_name;
+            tag = "on_crash";
+          };
+
+        clock =
+          vm_name:
+          mkTag {
+            name = "clock";
+            args = [
+              {
+                key = "offset";
+                val = vm.${vm_name}.clock.offset;
+              }
+            ];
+            children = map (
+              x:
+              mkTag {
+                name = "timer";
+                args =
+                  let
+                    tickpolicy =
+                      if x.tickpolicy != null then
+                        {
+                          key = "tickpolicy";
+                          val = x.tickpolicy;
+                        }
+                      else
+                        { };
+
+                    present =
+                      if x.present != null then
+                        {
+                          key = "present";
+                          val = x.present;
+                        }
+                      else
+                        { };
+                  in
+                  [
+                    {
+                      key = "name";
+                      val = x.name;
+                    }
+                    tickpolicy
+                    present
+                  ];
+              }
+            ) vm.${vm_name}.clock.timer;
+          };
+
+        cpu =
+          vm_name:
+          mkTag {
+            name = "cpu";
+            args =
+              let
+                mode = vm.${vm_name}.cpu.mode;
+                check = vm.${vm_name}.cpu.check;
+                migratable = vm.${vm_name}.cpu.migratable;
+              in
+              [
+                (
+                  if (mode != null) then
+                    {
+                      key = "mode";
+                      val = mode;
+                    }
+                  else
+                    ""
+                )
+                (
+                  if (check != null) then
+                    {
+                      key = "check";
+                      val = check;
+                    }
+                  else
+                    ""
+                )
+                (
+                  if (migratable != null) then
+                    {
+                      key = "migratable";
+                      val = migratable;
+                    }
+                  else
+                    ""
+                )
+              ];
+            closing = false;
+          };
+
+        features =
+          vm_name:
+          mkTag {
+            name = "features";
+            children =
+              let
+                acpi =
+                  vm_name:
+                  mkTag {
+                    name = "acpi";
+                    closing = false;
+                  };
+                apic =
+                  vm_name:
+                  mkTag {
+                    name = "apic";
+                    closing = false;
+                  };
+                vmport =
+                  vm_name:
+                  mkTag {
+                    name = "vmport";
+                    args = [
+                      {
+                        key = "state";
+                        val = vm.${vm_name}.features.vmport.state;
+                      }
+                    ];
+                    closing = false;
+                  };
+              in
+              [
+                (if (vm.${vm_name}.features.acpi != false) then (acpi vm_name) else "")
+                (if (vm.${vm_name}.features.apic != false) then (apic vm_name) else "")
+                (if (vm.${vm_name}.features.vmport != null) then (vmport vm_name) else "")
+              ];
+          };
+
+        os =
+          vm_name:
+          mkTag {
+            name = "os";
+            children =
+              let
+                os_type =
+                  vm_name:
+                  mkTag {
+                    name = "type";
+                    args = [
+                      {
+                        key = "arch";
+                        val = vm.${vm_name}.os.type.arch;
+                      }
+                      {
+                        key = "machine";
+                        val = vm.${vm_name}.os.type.machine;
+                      }
+                    ];
+                    value = vm.${vm_name}.os.type.value;
+                  };
+                os_loader =
+                  vm_name:
+                  mkTag {
+                    name = "loader";
+                    args = [
+                      {
+                        key = "readonly";
+                        val = vm.${vm_name}.os.loader.readonly;
+                      }
+                      {
+                        key = "pflash";
+                        val = vm.${vm_name}.os.loader.type;
+                      }
+                    ];
+                    value = vm.${vm_name}.os.loader.value;
+                  };
+                os_nvram =
+                  vm_name:
+                  mkTag {
+                    name = "nvram";
+                    value = vm.${vm_name}.os.nvram.value;
+                  };
+                os_boot =
+                  vm_name:
+                  mkTag {
+                    name = "type";
+                    args = [
+                      {
+                        key = "dev";
+                        val = vm.${vm_name}.os.boot.dev;
+                      }
+                    ];
+                    closing = false;
+                  };
+              in
+              [
+                (os_type vm_name)
+                (os_loader vm_name)
+                (os_nvram vm_name)
+                (os_boot vm_name)
+              ];
+          };
+
+        resource =
+          vm_name:
+          mkTag {
+            name = "resource";
+            children = [
+              (mkTag {
+                name = "partition";
+                value = "${toString vm.${vm_name}.resource.partition}";
+              })
+            ];
+          };
+
+        vcpu =
+          vm_name:
+          mkTag {
+            name = "vcpu";
+            args = [
+              {
+                key = "placement";
+                val = vm.${vm_name}.vcpu.placement;
+              }
+            ];
+            value = "${toString vm.${vm_name}.vcpu.count}";
+          };
+
+        currentMemory =
+          vm_name:
+          mkTag {
+            name = "currentMemory";
+            args = [
+              {
+                key = "unit";
+                val = vm.${vm_name}.currentMemory.unit;
+              }
+            ];
+            value = "${toString vm.${vm_name}.currentMemory.value}";
+          };
+        memory =
+          vm_name:
+          mkTag {
+            name = "memory";
+            args = [
+              {
+                key = "unit";
+                val = vm.${vm_name}.memory.unit;
+              }
+            ];
+            value = "${toString vm.${vm_name}.memory.value}";
+          };
+
+        libosinfo_os =
+          vm_name:
+          mkTag {
+            name = "libosinfo:os";
+            args = [
+              {
+                key = "id";
+                val = vm.${vm_name}.metadata.libosinfo_os;
+              }
+            ];
+            closing = false;
+          };
+
+        libosinfo_libosinfo =
+          vm_name:
+          mkTag {
+            name = "libosinfo:libosinfo";
+            args = [
+              {
+                key = "xmlns:libosinfo";
+                val = vm.${vm_name}.metadata.libosinfo;
+              }
+            ];
+            children = [
+              (libosinfo_os vm_name)
+            ];
+          };
+
+        metadata =
+          vm_name:
+          mkTag {
+            name = "metadata";
+            children = [ (libosinfo_libosinfo vm_name) ];
+          };
+
+        uuid =
+          vm_name:
+          mkTag {
+            name = "uuid";
+            value = vm.${vm_name}.uuid;
+          };
+
+        name =
+          vm_name:
+          mkTag {
+            name = "name";
+            value = vm.${vm_name}.name;
+          };
+
+        # the vm_name has to be passed in, as we want to accesses the value for the given vm when
+        # generating the config
+        domain =
+          vm_name:
+          mkTag {
+            name = "domain";
+            args = [
+              {
+                key = "type";
+                val = vm.${vm_name}.vm_type;
+              }
+              {
+                key = "id";
+                val = vm.${vm_name}.id;
+              }
+            ];
+            children = [
+              (name vm_name)
+              (uuid vm_name)
+              (metadata vm_name)
+              (memory vm_name)
+              (currentMemory vm_name)
+              (vcpu vm_name)
+              (resource vm_name)
+              (os vm_name)
+              (features vm_name)
+              (cpu vm_name)
+              (clock vm_name)
+              (on_poweroff vm_name)
+              (on_reboot vm_name)
+              (on_crash vm_name)
+              (pm vm_name)
+              (devices vm_name)
+            ];
+          };
+      in
+      {
+        output.domain = pkgs.writeText "libvirt-domain-config.xml" (domain "alan");
+      };
   };
 }
diff --git a/nix/modules/libvirtnix/os.nix b/nix/modules/libvirtnix/os.nix
new file mode 100644
index 0000000..41f36f3
--- /dev/null
+++ b/nix/modules/libvirtnix/os.nix
@@ -0,0 +1,119 @@
+{ lib, ... }:
+
+let
+  mkOption = lib.mkOption;
+  submodule = lib.types.submodule;
+  types = lib.types;
+
+  os = {
+    firmware = mkOption {
+      type = types.enum [
+        "bios"
+        "efi"
+      ];
+      default = "efi";
+      example = "bios";
+    };
+
+    type = mkOption {
+      type = types.enum [
+        "hvm"
+        "linux"
+      ];
+      default = "hvm";
+      example = "linux";
+    };
+
+    arch = mkOption { type = types.str; };
+    machine = mkOption { type = types.str; };
+
+    # TODO(emile): features
+    # Search for the following...
+    # > When using firmware auto-selection there are different features enabled in the firmwares
+    # ... here: https://libvirt.org/formatdomain.html
+    # can't bother to implement this now
+
+    # features = mkOption {
+    # 	type = types.submodule {
+    # 		options = {
+    # 			enabled = {
+    # 				type = types.enum [ "yes" "no" ];
+    # 			};
+    # 			name = {};
+    # 		};
+    # 	};
+    # };
+
+    # such as:
+    # <loader readonly='yes' secure='yes' type='pflash'>/usr/share/OVMF/OVMF_CODE.fd</loader>
+    loader = mkOption {
+      type = types.submodule {
+        options = {
+          readonly = mkOption {
+            type = types.enum [
+              "yes"
+              "no"
+            ];
+          };
+          type = mkOption {
+            type = types.enum [
+              "rom"
+              "pflash"
+            ];
+          };
+          secure = mkOption {
+            type = types.enum [
+              "yes"
+              "no"
+            ];
+          };
+          stateless = mkOption {
+            type = types.enum [
+              "yes"
+              "no"
+            ];
+          };
+          format = mkOption {
+            type = types.enum [
+              "raw"
+              "qcow2"
+            ];
+          };
+          value = mkOption {
+            type = types.str;
+            example = "/usr/share/OVMF/OVMF_CODE.fd";
+          };
+        };
+      };
+    };
+
+    # <nvram type='network'>
+    #   <source protocol='iscsi' name='iqn.2013-07.com.example:iscsi-nopool/0'>
+    #     <host name='example.com' port='6000'/>
+    #     <auth username='myname'>
+    #       <secret type='iscsi' usage='mycluster_myname'/>
+    #     </auth>
+    #   </source>
+    # </nvram>
+
+    # nvram = {
+    # 	type = "network";
+    # 	source = {
+    # 		protocol = "iscsi";
+    # 		name = "iqn.2013-07.com.example:iscsi-nopool/0";
+    # 	};
+    # };
+    
+  };
+in
+{
+  options = {
+    os = mkOption {
+      type = submodule {
+        options = {
+          inherit (os) firmware type arch machine loader;
+        };
+      };
+    };
+  };
+}
diff --git a/nix/modules/libvirtnix/secret.nix b/nix/modules/libvirtnix/secret.nix
new file mode 100644
index 0000000..b597174
--- /dev/null
+++ b/nix/modules/libvirtnix/secret.nix
@@ -0,0 +1,174 @@
+{ config, lib, ... }:
+
+# https://libvirt.org/formatsecret.html
+
+let
+  pkgs = import <nixpkgs> { };
+
+  mkOption = lib.mkOption;
+  submodule = lib.types.submodule;
+  types = lib.types;
+  enum = types.enum;
+  str = types.str;
+
+  yesNoOption = mkOption {
+    type = enum [
+      "yes"
+      "no"
+    ];
+    default = "no";
+  };
+  # YesnoOption = mkOption { type = enum [ "yes" "no" ]; default = "yes"; };
+
+  # takes a few args and creats a valid xml tag pair out of it
+  #
+  # testTag = mkTag {
+  #   name = "name";
+  #   args = [
+  #     {
+  #       key = "arg1";
+  #       val = "arg1val";
+  #     }
+  #     {
+  #       key = "arg2";
+  #       val = "arg2val";
+  #     }
+  #   ];
+  #   value = "qwe";
+  #   children = [
+  #     (mkTag { name = "nested"; args = []; value = "qwe"; children = [];})
+  #   ];
+  # };
+  #
+  # <name arg1=arg1val arg2=arg2val>
+  #   value
+  #   {children}
+  # </name>
+  mkTag =
+    {
+      name, # name of the tag to be used, such as `secret`, `description`, ...
+      args ? [ ], # args, [ { key="a"; val="b"; } { key="c"; val="d"; } ]
+      value ? "", # the value to place in the middle
+      children ? [ ], # the child elements
+    }:
+    let
+      args_str =
+        " " + lib.strings.concatStrings (lib.strings.intersperse " " (map (x: "${x.key}='${x.val}'") args));
+      child_evaled = lib.strings.concatStrings children;
+    in
+    "<${name}${lib.optionalString (args != [ ]) args_str}>${value}${child_evaled}</${name}>";
+
+  strOption =
+    {
+      default ? "",
+    }:
+    mkOption {
+      type = str;
+      default = "${default}";
+    };
+
+  usage = mkOption {
+    type = submodule {
+      options = {
+        type = mkOption {
+          type = enum [
+            "volume"
+            "ceph"
+            "iscsi"
+            "tls"
+            "vtpm"
+          ];
+          default = "";
+        };
+
+        value = strOption { };
+
+        name = strOption { };
+        volume = strOption { };
+        target = strOption { };
+      };
+    };
+  };
+
+  secret = {
+    inherit usage;
+
+    ephemeral = yesNoOption;
+    private = yesNoOption;
+
+    uuid = strOption { };
+    description = strOption { };
+  };
+
+in
+{
+  options = {
+    services.emile.libvirtnix = {
+      enable = lib.mkEnableOption "Enable r2wars-web";
+
+      secret = mkOption {
+        type = submodule {
+          options = {
+            inherit (secret)
+              ephemeral
+              private
+              uuid
+              description
+              usage
+              ;
+          };
+        };
+      };
+
+      # output = mkOption { type = types.path; };
+    };
+  };
+
+  config = lib.mkIf config.services.emile.libvirtnix.enable {
+    services.emile.libvirtnix =
+      let
+        secret = mkTag {
+          name = "secret";
+          args = [
+            {
+              key = "ephemeral";
+              val = config.services.emile.libvirtnix.secret.ephemeral;
+            }
+            {
+              key = "private";
+              val = config.services.emile.libvirtnix.secret.private;
+            }
+          ];
+          children = [
+            (mkTag {
+              name = "description";
+              value = "Super secret description";
+            })
+            (mkTag {
+              name = "uuid";
+              value = "0a81f5b2-8403-7b23-c8d6-21ccc2f80d6f";
+            })
+            (mkTag {
+              name = "usage";
+              args = [
+                {
+                  key = "type";
+                  val = "volume";
+                }
+              ];
+              children = [
+                (mkTag {
+                  name = "volume";
+                  value = "/var/lib/libvirt/images/kernel.img";
+                })
+              ];
+            })
+          ];
+        };
+
+      in
+      {
+        # output = pkgs.writeText "libvirt-secret-config.xml" secret;
+      };
+  };
+}
diff --git a/nix/modules/libvirtnix/test.nix b/nix/modules/libvirtnix/test.nix
new file mode 100644
index 0000000..8542b7e
--- /dev/null
+++ b/nix/modules/libvirtnix/test.nix
@@ -0,0 +1,10 @@
+let
+	pkgs = import <nixpkgs> {};
+in
+	pkgs.lib.evalModules {
+		modules = [
+			# ./secret.nix
+			./domain.nix
+			./config.nix
+		];
+	}
diff --git a/nix/modules/libvirtnix/xml.nix b/nix/modules/libvirtnix/xml.nix
new file mode 100644
index 0000000..10eefa3
--- /dev/null
+++ b/nix/modules/libvirtnix/xml.nix
@@ -0,0 +1,58 @@
+{ lib, ... }:
+
+# takes a few args and creats a valid xml tag pair out of it
+#
+# testTag = mkTag {
+#   name = "name";
+#   args = [
+#     {
+#       key = "arg1";
+#       val = "arg1val";
+#     }
+#     {
+#       key = "arg2";
+#       val = "arg2val";
+#     }
+#   ];
+#   value = "qwe";
+#   children = [
+#     (mkTag { name = "nested"; args = []; value = "qwe"; children = [];})
+#   ];
+# };
+#
+# <name arg1=arg1val arg2=arg2val>
+#   value
+#   {children}
+# </name>
+{
+  name, # name of the tag to be used, such as `secret`, `description`, ...
+  args ? [ ], # args, [ { key="a"; val="b"; } { key="c"; val="d"; } ]
+  value ? "", # the value to place in the middle
+  children ? [ ], # the child elements
+  closing ? true, # add a closing tag
+}:
+let
+
+  # filter out all values missing a key
+  # this allows passing empty args such as `{}` which then get discarded
+  filteredArgs = (builtins.filter (x: x ? key) args);
+
+  # convert the list of attr sets into a list of strings
+  mappedArgs = (map (x: "${x.key}='${x.val}'") filteredArgs);
+
+  # [ "A" "B" "C" ] => [ "A" " " "B" " " "C" ]
+  spacedArgs = lib.strings.intersperse "" mappedArgs;
+
+  # [ "A" " " "B" " " "C" ] => "A B C"
+  joinedArgs = lib.strings.concatStrings spacedArgs;
+
+  # prefix the args with a space when inserting
+  args_str = lib.optionalString (filteredArgs != [ ]) (" " + joinedArgs);
+
+  child_evaled = lib.strings.concatStrings children;
+
+  cond = condition: value: if condition then value else "";
+
+  closingTag = if closing == true then "</${name}>" else "";
+in
+"<${name}${args_str}${cond (closing == false) "/"}>${value}${child_evaled}${lib.optionalString (closing) closingTag}"