Auto-assign sequential VM IDs instead of requiring manual specification

VM IDs (used for vsock CIDs and TAP name truncation) are now
automatically assigned starting from 3 based on list position,
removing the need for users to specify them. Also removes the
constraints that IDs must be odd numbers in range 3-255.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Davíð Steinn Geirsson 2026-02-16 20:10:06 +00:00
parent b0b0809577
commit c80a8c807a
8 changed files with 20 additions and 56 deletions

View file

@ -331,7 +331,6 @@ Two filter tiers (child is a strict subset of main):
};
nixosVms = [{
id = 3;
name = "banking";
color = "#2ecc71"; # Window decoration color (default: "red")
waylandProxy = "wayland-proxy-virtwl"; # or "sommelier" (default: "wayland-proxy-virtwl")

View file

@ -54,7 +54,6 @@ Import the module and configure VMs in your NixOS configuration:
nixosVms = [
{
id = 3;
name = "banking";
memory = 4096;
cpus = 4;
@ -71,7 +70,6 @@ Import the module and configure VMs in your NixOS configuration:
guestPrograms = with pkgs; [ firefox konsole ];
}
{
id = 5;
name = "shopping";
memory = 2048;
cpus = 2;
@ -88,7 +86,6 @@ Import the module and configure VMs in your NixOS configuration:
guestPrograms = with pkgs; [ firefox konsole ];
}
{
id = 7;
name = "personal";
memory = 4096;
cpus = 4;
@ -123,7 +120,6 @@ Import the module and configure VMs in your NixOS configuration:
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `id` | int | required | VM ID (odd number 3-255). Used for vsock CID |
| `name` | string | required | VM name for scripts |
| `memory` | int | `1024` | Memory allocation in MB |
| `cpus` | int | `2` | Number of virtual CPUs |
@ -263,9 +259,9 @@ Each VM's `color` option controls its KDE window decoration color, providing a v
```nix
nixosVms = [
{ id = 3; name = "banking"; color = "#2ecc71"; ... } # Green
{ id = 5; name = "shopping"; color = "#3498db"; ... } # Blue
{ id = 7; name = "untrusted"; color = "red"; ... } # Red (default)
{ name = "banking"; color = "#2ecc71"; ... } # Green
{ name = "shopping"; color = "#3498db"; ... } # Blue
{ name = "untrusted"; color = "red"; ... } # Red (default)
];
```
@ -356,15 +352,6 @@ Interface names are user-specified via `network.interfaces` attrset keys. Names
PCI slots are assigned alphabetically by interface name (sorted position + 22). For example, with interfaces `{ internal = ...; wan = ...; }`, `internal` gets PCI slot 22 (`00:16.0`) and `wan` gets slot 23 (`00:17.0`).
### ID Requirements
VM IDs must be:
- Odd numbers (3, 5, 7, 9, ...)
- In range 3-255
- Unique across all VMs
IDs are used for vsock CIDs (not for IP addressing).
### NAT
When `hostNetworking.nat.enable = true`, the module configures:
@ -381,7 +368,6 @@ To use `vm-shell --ssh`, configure SSH keys in per-VM `guestConfig`:
```nix
programs.vmsilo = {
nixosVms = [{
id = 3;
name = "dev";
guestConfig = {
users.users.user.openssh.authorizedKeys.keys = [ "ssh-ed25519 AAAA..." ];
@ -399,7 +385,6 @@ Since interface names are the attrset keys, you can reference them directly:
```nix
{
id = 3;
name = "banking";
network.interfaces.wan = {
type = "tap";
@ -442,7 +427,6 @@ in {
programs.vmsilo = {
nixosVms = [
{
id = 3;
name = "dev";
network = mkTapNetwork "10.0.0.254/24" "10.0.0.1/24";
guestPrograms = commonGuestPrograms;
@ -452,7 +436,6 @@ in {
additionalDisks = [{ path = "/dev/mapper/main-dev-home"; ro = false; }];
}
{
id = 5;
name = "banking";
network = mkTapNetwork "10.0.1.254/24" "10.0.1.1/24";
guestPrograms = commonGuestPrograms;
@ -469,7 +452,6 @@ VMs are offline by default (no `network.interfaces`). For sensitive data that sh
```nix
{
id = 13;
name = "vault";
memory = 2048;
# No network.interfaces = completely offline
@ -484,7 +466,6 @@ VMs can communicate with each other via `network.interfaces` with `type = "vm-sw
```nix
nixosVms = [
{
id = 3;
name = "router";
network.interfaces = {
# TAP for internet access
@ -503,7 +484,6 @@ nixosVms = [
};
}
{
id = 5;
name = "banking";
network.interfaces.internal = {
type = "vm-switch";
@ -513,7 +493,6 @@ nixosVms = [
};
}
{
id = 7;
name = "shopping";
network.interfaces.internal = {
type = "vm-switch";
@ -539,7 +518,6 @@ VMs that auto-shutdown when idle to save memory:
```nix
{
id = 9;
name = "untrusted";
memory = 4096;
autoShutdown = { enable = true; after = 60; }; # Shutdown 60 seconds after last command exits
@ -579,13 +557,11 @@ programs.vmsilo = {
isolatedPciDevices = [ "01:00.0" "02:00.0" ];
nixosVms = [{
id = 3;
name = "sys-usb";
memory = 1024;
pciDevices = [{ path = "01:00.0"; }]; # USB controller
}
{
id = 5;
name = "sys-net";
memory = 1024;
pciDevices = [{ path = "02:00.0"; }]; # Network card

View file

@ -64,7 +64,6 @@
nixosVms = [
{
id = 3;
name = "testvm";
memory = 4096;
cpus = 4;
@ -93,7 +92,6 @@
guestPrograms = commonGuestPrograms;
}
{
id = 5;
name = "netvm";
autoStart = true;
memory = 512;

View file

@ -27,7 +27,6 @@ in
config = lib.mkIf cfg.enable {
assertions =
let
vmIds = map (vm: vm.id) cfg.nixosVms;
vmNames = map (vm: vm.name) cfg.nixosVms;
in
[
@ -35,19 +34,11 @@ in
assertion = cfg.hostNetworking.nat.enable -> cfg.hostNetworking.nat.interface != "";
message = "programs.vmsilo.hostNetworking.nat.interface must be set when hostNetworking.nat.enable is true";
}
{
assertion = lib.length vmIds == lib.length (lib.unique vmIds);
message = "VM IDs must be unique";
}
{
assertion = lib.length vmNames == lib.length (lib.unique vmNames);
message = "VM names must be unique";
}
]
++ map (vm: {
assertion = vm.id >= 3 && vm.id <= 255 && lib.mod vm.id 2 == 1;
message = "VM '${vm.name}' has invalid id ${toString vm.id}. Must be odd number 3-255.";
}) cfg.nixosVms
# PCI passthrough assertions
++ lib.concatMap (
vm:

View file

@ -173,6 +173,9 @@
in
if lib.stringLength fullName <= maxLen then fullName else "${truncatedName}-${ifIndexStr}";
# Auto-assign sequential VM IDs starting from 3 based on list position
assignVmIds = vms: lib.imap0 (idx: vm: vm // { id = idx + 3; }) vms;
# Sanitize VM name for use in device IDs (alphanumeric + underscore only)
sanitizeName =
name:

View file

@ -16,8 +16,11 @@ let
generateMac
sortedInterfaceList
makeTapName
assignVmIds
;
vms = assignVmIds cfg.nixosVms;
# Get effective MAC for an interface (uses user-specified interface name)
getEffectiveIfaceMac =
vm: ifName: iface:
@ -30,7 +33,7 @@ let
lib.concatMap (iface: lib.optional (isVmSwitchIface iface) iface.vmNetwork.name) (
lib.attrValues vm.network.interfaces
)
) cfg.nixosVms
) vms
);
# Get VMs that are part of a specific vm-switch network
@ -41,7 +44,7 @@ let
lib.any (iface: isVmSwitchIface iface && iface.vmNetwork.name == netName) (
lib.attrValues vm.network.interfaces
)
) cfg.nixosVms;
) vms;
# Find the interface name for a VM on a specific network
getVmNetworkIfaceName =
@ -79,7 +82,7 @@ let
makeTapName vm.name vm.id (idxToIfIndex idx);
}
) (sortedInterfaceList vm.network.interfaces)
) cfg.nixosVms
) vms
);
in
{

View file

@ -168,12 +168,6 @@ let
vmSubmodule = lib.types.submodule {
options = {
id = lib.mkOption {
type = lib.types.int;
description = "VM ID (odd number 3-255). Used for IP address and vsock CID.";
example = 3;
};
name = lib.mkOption {
type = lib.types.str;
description = "VM name. Used for TAP interface naming and scripts.";
@ -595,7 +589,6 @@ in
description = "List of NixOS-based VMs to create.";
example = lib.literalExpression ''
[{
id = 3;
name = "banking";
memory = 4096;
cpus = 4;

View file

@ -25,8 +25,11 @@ let
sortedInterfaceList
sanitizeName
makeTapName
assignVmIds
;
vms = assignVmIds cfg.nixosVms;
# User UID/GID and runtime directory for explicit paths (system services need these)
userUid = config.users.users.${cfg.user}.uid;
userGid = config.users.groups.${config.users.users.${cfg.user}.group}.gid;
@ -399,10 +402,10 @@ let
# Generate shell case statement for VM dispatch
mkVmCase = makeCase: ''
case "$VM_NAME" in
${lib.concatMapStringsSep "\n " makeCase cfg.nixosVms}
${lib.concatMapStringsSep "\n " makeCase vms}
*)
echo "Unknown VM: $VM_NAME" >&2
echo "Available VMs: ${lib.concatMapStringsSep ", " (vm: vm.name) cfg.nixosVms}" >&2
echo "Available VMs: ${lib.concatMapStringsSep ", " (vm: vm.name) vms}" >&2
exit 1
;;
esac
@ -422,7 +425,7 @@ let
if [ ! -S "$SOCKET" ]; then
echo "Unknown VM or socket not active: $VM_NAME" >&2
echo "Available VMs: ${lib.concatMapStringsSep ", " (vm: vm.name) cfg.nixosVms}" >&2
echo "Available VMs: ${lib.concatMapStringsSep ", " (vm: vm.name) vms}" >&2
exit 1
fi
@ -543,11 +546,9 @@ in
config = lib.mkIf cfg.enable {
# Set internal options for other modules to consume
programs.vmsilo._internal = {
vmScripts = lib.listToAttrs (map (vm: lib.nameValuePair vm.name (mkVmScript vm)) cfg.nixosVms);
vmScripts = lib.listToAttrs (map (vm: lib.nameValuePair vm.name (mkVmScript vm)) vms);
proxyScripts = lib.listToAttrs (
map (vm: lib.nameValuePair vm.name (mkProxyScript vm)) cfg.nixosVms
);
proxyScripts = lib.listToAttrs (map (vm: lib.nameValuePair vm.name (mkProxyScript vm)) vms);
userScripts = {
vm-run = vmRunScript;