Skip to content

Add dynamic hostname generation for hardware-based device identification#1512

Merged
brianmcgillion merged 1 commit intotiiuae:mainfrom
vadika:hw-hostname-2
Nov 14, 2025
Merged

Add dynamic hostname generation for hardware-based device identification#1512
brianmcgillion merged 1 commit intotiiuae:mainfrom
vadika:hw-hostname-2

Conversation

@vadika
Copy link
Copy Markdown
Contributor

@vadika vadika commented Oct 29, 2025

Implement runtime-generated, human-readable hostnames derived from
permanent hardware properties (DMI serial/UUID, MAC address, or
machine-id). The hostname format is 'ghaf-NNNNNNNNNN' where N is a
10-digit number deterministically generated from hardware properties.

Key features:

  • Host generates and shares hostname via /persist/common/ghaf/hostname
  • NetVM sets its actual hostname for DHCP client identification
  • All VMs have access to the hostname via environment variable
  • NetworkManager configured to preserve the dynamic hostname
  • 10 billion possible hostnames for excellent uniqueness
  • Replaces random device-id with deterministic hardware-based ID
  • Consolidates device-id and machine-id generation into single script
  • All machine-ids are deterministic and hardware-based (not random UUIDs)

Implementation:

  • dynamic-hostname.nix: Single script generates hostname, device-id, and all VM machine-ids
  • vm-hostname-export.nix: Exports hostname as environment variable
  • vm-hostname-setter.nix: Sets actual VM hostname from shared file
  • Virtiofs share (/persist/common -> /etc/common) propagates to VMs
  • NetworkManager hostname-mode set to 'none' to prevent override
  • Replaces generate-device-id service from microvm-host.nix with single atomic operation

Technical details:

  • Hostname: ghaf-NNNNNNNNNN (10-digit CRC32-based hardware ID)
  • Device-id: Hardware ID formatted as hex string (e.g., 00-01-23-45-67)
  • Machine-ids: MD5 hash of hardware key + VM name (deterministic, not random)
  • Service runs before network-online.target for DHCP client identification
  • All identity files generated atomically in single service execution
  • Same hardware always produces same hostname, device-id, and machine-ids
  • Each VM gets unique but deterministic machine-id

Use cases:

  • DHCP client identification with unique device names
  • Network troubleshooting and device tracking
  • Logging and monitoring across host and VMs
  • User visibility of hardware instance identity
  • Deterministic device identification across reboots
  • Reproducible VM identities for testing and automation

Documentation added at docs/src/content/docs/ghaf/dev/ref/dynamic-hostname.mdx

Description of Changes

Type of Change

  • New Feature
  • Bug Fix
  • Improvement / Refactor

Related Issues / Tickets

Checklist

  • Clear summary in PR description
  • Detailed and meaningful commit message(s)
  • Commits are logically organized and squashed if appropriate
  • Contribution guidelines followed
  • Ghaf documentation updated with the commit - https://tiiuae.github.io/ghaf/
  • Author has run make-checks and it passes
  • All automatic GitHub Action checks pass - see actions
  • Author has added reviewers and removed PR draft status

Testing Instructions

Applicable Targets

  • Orin AGX aarch64
  • Orin NX aarch64
  • Lenovo X1 x86_64
  • Dell Latitude x86_64
  • System 76 x86_64

Installation Method

  • Requires full re-installation
  • Can be updated with nixos-rebuild ... switch
  • Other:

Test Steps To Verify:

Testing Instructions: Dynamic Hostname Generation

Overview

This commit adds hardware-based dynamic hostname generation for Ghaf hosts and VMs. Test the following scenarios to ensure proper functionality.


Test 1: Basic Hostname Generation (Hardware Mode)

Setup

Build and deploy a Ghaf system with default configuration.

Steps

  1. Boot the system
  2. On the host, check hostname generation:
    cat /run/ghaf-hostname
    cat /var/lib/ghaf/identity/hostname
    cat /var/lib/ghaf/identity/id
    echo $GHAF_HOSTNAME
  3. Verify format: ghaf-NNNNNNNNNN (10 digits)
  4. Check host's actual hostname remains unchanged:
    hostname  # Should show "ghaf-host"

Expected Results

  • /run/ghaf-hostname contains hostname like ghaf-1234567890
  • $GHAF_HOSTNAME environment variable is set
  • Host's actual hostname is still ghaf-host (not changed)
  • Files are created in /persist/common/ghaf/

Test 2: VM Hostname Propagation

Steps

  1. Access NetVM:
    microvm-console net-vm
  2. Check hostname in NetVM:
    cat /etc/common/ghaf/hostname
    echo $GHAF_HOSTNAME
    hostname  # Should match the dynamic hostname
  3. Access GUIVM and repeat:
    cat /etc/common/ghaf/hostname
    echo $GHAF_HOSTNAME
    hostname  # Should show "gui-vm" (static)

Expected Results

  • NetVM: actual hostname matches dynamic hostname (for DHCP)
  • GUIVM: $GHAF_HOSTNAME set, but actual hostname remains gui-vm
  • Both VMs can read /etc/common/ghaf/hostname

Test 3: Deterministic Behavior

Steps

  1. Note the generated hostname: cat /run/ghaf-hostname
  2. Reboot the system
  3. After reboot, verify hostname is identical:
    cat /run/ghaf-hostname

Expected Results

  • Same hardware produces identical hostname across reboots
  • Hostname is deterministic, not random

Test 4: Device ID Generation

Steps

  1. Check device-id file:
    cat /persist/common/device-id
  2. Verify format is hex with dashes: XX-XX-XX-XX-XX

Expected Results

  • Device ID exists and has correct format
  • Device ID is derived from same hardware key as hostname

Test 5: VM Machine IDs

Steps

  1. Check machine-ids for each VM:
    cat /persist/storagevm/net-vm/etc/machine-id
    cat /persist/storagevm/gui-vm/etc/machine-id
  2. Verify they are unique but deterministic
  3. Reboot and verify machine-ids remain the same

Expected Results

  • Each VM has a unique machine-id
  • Machine-ids are deterministic (not random UUIDs)
  • Same across reboots

Test 6: DHCP Client Identification

Steps

  1. Access NetVM
  2. Check NetworkManager configuration:
    nmcli general hostname
  3. Request DHCP lease and verify hostname is sent:
    journalctl -u NetworkManager | grep -i hostname
  4. On DHCP server, verify client hostname appears as ghaf-NNNNNNNNNN

Expected Results

  • NetVM uses dynamic hostname for DHCP identification
  • DHCP server sees the hardware-based hostname
  • NetworkManager does not override the hostname (hostname-mode = "none")

Test 7: Static Mode Configuration

Setup

Configure system with static hardware ID:

ghaf.identity.dynamicHostName = {
  enable = true;
  source = "static";
  staticValue = "my-test-device";
};

Steps

  1. Rebuild and deploy
  2. Check generated hostname:
    cat /run/ghaf-hostname

Expected Results

  • Hostname is deterministically derived from "my-test-device"
  • Format is still ghaf-NNNNNNNNNN
  • Same static value produces same hostname

Test 8: Random Mode Configuration

Setup

Configure with random ID generation:

ghaf.identity.dynamicHostName = {
  enable = true;
  source = "random";
};

Steps

  1. Rebuild and deploy
  2. Note the generated hostname
  3. Reboot and verify hostname persists
  4. Check random seed file:
    cat /var/lib/ghaf/identity/random-seed

Expected Results

  • Random hostname generated on first boot
  • Hostname persists across reboots (not regenerated)
  • Random seed file exists and doesn't change

Test 9: Custom Prefix and Digits

Setup

Configure with custom parameters:

ghaf.identity.dynamicHostName = {
  enable = true;
  prefix = "test";
  digits = 6;
};

Steps

  1. Rebuild and check hostname format

Expected Results

  • Hostname format: test-NNNNNN (6 digits)
  • All functionality works with custom parameters

Test 10: Hardware Detection Priority

Steps

  1. Check which hardware source is being used:
    cat /sys/class/dmi/id/product_serial  # Priority 1
    cat /sys/class/dmi/id/product_uuid    # Priority 2
    ls /dev/disk/by-uuid/                 # Priority 3
    cat /sys/class/net/*/address          # Priority 4 (non-loopback)
    cat /etc/machine-id                   # Priority 5 (fallback)
  2. Test on different hardware (if available)

Expected Results

  • System uses highest-priority available hardware identifier
  • Different hardware produces different hostnames
  • Fallback chain works correctly

Test 11: Cross-VM Communication

Steps

  1. From host, log all VMs' environment variables:
    # In each VM
    echo $GHAF_HOSTNAME
  2. Verify all VMs see the same hostname

Expected Results

  • All VMs share the same $GHAF_HOSTNAME value
  • Hostname is consistent across the entire system

@brianmcgillion
Copy link
Copy Markdown
Collaborator

generate-device-id = {
enable = true;
description = "Generate device and machine ids";
wantedBy = [ "local-fs.target" ];
after = [ "local-fs.target" ];
unitConfig.ConditionPathExists = "!/persist/common/device-id";
serviceConfig = {
Type = "oneshot";
ExecStart = [
# Generate a unique device id
"${pkgs.writeShellScript "generate-device-id" ''
echo -n "$(od -txC -An -N6 /dev/urandom | tr ' ' - | cut -c 2-)" > /persist/common/device-id
''}"
]
++ (map (
vm:
# Generate unique machine ids
"${pkgs.writeShellScript "generate-machine-id" ''
${pkgs.util-linux}/bin/uuidgen | tr -d '-' > /persist/storagevm/${vm}/etc/machine-id
''}"
) activeMicrovms);
RemainAfterExit = true;
Restart = "on-failure";
RestartSec = "1";
};
};

can we consolidate your new change into this. so that we can have a unified and hopefully persistent name for the device that we can search in graphaha?

@vadika
Copy link
Copy Markdown
Contributor Author

vadika commented Nov 11, 2025

found the problem and finally fixed it

jenkins-pre-merge is failing for this PR due to the net-vm name change. I ran the automated tests with test-automation patch and found one issue. There is an extra newline in the device-id.
This can also be seen in Grafana image
Other than that the automated test results looked good on both Darter Pro and Orin NX.
The review comments should be addressed and this PR should be approved by someone before we proceed with manual testing.

fixed

Extra newline is still there, the issue was not fixed. I re-ran pre merge tests, this time in dev with the test-automation changes. Everything else is passing, but the Grafana logs are not found.

@vadika vadika added the Needs Testing CI Team to pre-verify label Nov 11, 2025
@milva-unikie
Copy link
Copy Markdown

Tested on Darter Pro (new image)

Issues

Test 6: DHCP Client Identification

  • nmcli shows static hostname instead of a dynamic hostname.
  • On the DHCP server the hostname is net-vm, not the dynamic hostname.
[ghaf@ghaf-1552147931:~]$ nmcli general hostname
net-vm
image

Test 11: Cross-VM Communication

  • Admin-vm, audio-vm, business-vm, chrome-vm, comms-vm and zathura-vm do not have the environmental variable $GHAF_HOSTNAME.
  • Only ghaf-host, gui-vm and net-vm have it.
[ghaf@gui-vm:~]$ echo $GHAF_HOSTNAME
ghaf-1552147931

[ghaf@chrome-vm:~]$ echo $GHAF_HOSTNAME

Working

  • Devices get a unique hostname that persists between boots and new installations.
  • Hostname has the correct format ghaf-NNNNNNNNNN.
  • Devices get a unique device ID that persists between boots and new installations.
  • Device ID has the correct format XX-XX-XX-XX-XX.
  • All VMs except net-vm show the old static hostname.
  • All automated pre-merge tests pass on all tested target devices.

Notes

  • Machine IDs for VMs have been removed (test 5).
  • Skipped tests 7-9. We can try setting static/random/custom hardware IDs later. For now I believe the default values working is enough.
  • Also skipped test 10. I could not figure out how my device was calculating the ID. However it stays the same so I am happy with that.

@milva-unikie milva-unikie removed the Needs Testing CI Team to pre-verify label Nov 12, 2025
@maseabunikie
Copy link
Copy Markdown

@vadika
If the user wants to connect via SSH would the device be discovered with the dynamic hostname?
In practice: ssh ghaf@ghaf-NNNNNNNNNN

@vadika
Copy link
Copy Markdown
Contributor Author

vadika commented Nov 12, 2025

@vadika If the user wants to connect via SSH would the device be discovered with the dynamic hostname? In practice: ssh ghaf@ghaf-NNNNNNNNNN
@maseabunikie
it's not rhe target of this PR, but I think is doable if you add avahi or similar dynamic name resolution daemon to netvm.

@maseabunikie
Copy link
Copy Markdown

@vadika If the user wants to connect via SSH would the device be discovered with the dynamic hostname? In practice: ssh ghaf@ghaf-NNNNNNNNNN
@maseabunikie
it's not rhe target of this PR, but I think is doable if you add avahi or similar dynamic name resolution daemon to netvm.

Thanks for the quick reply! It is clear, and thanks for the hint/proposal.

…tion

    Implement runtime-generated, human-readable hostnames derived from
    permanent hardware properties (DMI serial/UUID, MAC address, or
    machine-id). The hostname format is 'ghaf-NNNNNNNNNN' where N is a
    10-digit number deterministically generated from hardware properties.

    Key features:
    - Host generates and shares hostname via /persist/common/ghaf/hostname
    - NetVM sets its actual hostname for DHCP client identification
    - All VMs have access to the hostname via environment variable
    - NetworkManager configured to preserve the dynamic hostname
    - 10 billion possible hostnames for excellent uniqueness
    - Replaces random device-id with deterministic hardware-based ID
    - Consolidates device-id and machine-id generation into single script
    - All machine-ids are deterministic and hardware-based (not random UUIDs)
    - Configurable ID source: hardware (default), static, or random

    Implementation:
    - dynamic-hostname.nix: Single script generates hostname, device-id, and all VM machine-ids
    - vm-hostname-export.nix: Exports hostname as environment variable
    - vm-hostname-setter.nix: Sets actual VM hostname from shared file
    - Virtiofs share (/persist/common -> /etc/common) propagates to VMs
    - NetworkManager hostname-mode set to 'none' to prevent override
    - dynamic-hostname.nix: Single script generates hostname, device-id, and all VM machine-i
ds
    - vm-hostname-export.nix: Exports hostname as environment variable
    - vm-hostname-setter.nix: Sets actual VM hostname from shared file
    - Virtiofs share (/persist/common -> /etc/common) propagates to VMs
    - NetworkManager hostname-mode set to 'none' to prevent override
    - Replaces generate-device-id service from microvm-host.nix with single atomic operation
    - Uses writeShellApplication for better error handling and shellcheck validation
    - Code refactored with inherit (lib) for cleaner imports
    - Uses sysfs directly instead of iproute2 for MAC address reading

    Configuration options:
    - source = "hardware": Best-effort detection (DMI <E2><86><92> disk UUID <E2><86><92> MAC <E2><86><92> machine-id)
    - source = "static": User-provided value via staticValue option
    - source = "random": Random value generated once and persisted

    Technical details:
    - Hostname: ghaf-NNNNNNNNNN (10-digit CRC32-based hardware ID)
    - Device-id: Hardware ID formatted as hex string (e.g., 00-01-23-45-67)
    - Machine-ids: MD5 hash of hardware key + VM name (deterministic, not random)
    - Service runs before network-online.target for DHCP client identification
    - All identity files generated atomically in single service execution
    - Same hardware always produces same hostname, device-id, and machine-ids
    - Each VM gets unique but deterministic machine-id

    Use cases:
    - DHCP client identification with unique device names
    - Network troubleshooting and device tracking
    - Logging and monitoring across host and VMs
    - User visibility of hardware instance identity
    - Deterministic device identification across reboots
    - Reproducible VM identities for testing and automation
    - Privacy-focused deployments with random mode
    - Testing environments with static mode

    Documentation added at docs/src/content/docs/ghaf/dev/ref/dynamic-hostname.mdx

Signed-off-by: Vadim Likholetov <vadikas@gmail.com>
@vadika
Copy link
Copy Markdown
Contributor Author

vadika commented Nov 13, 2025

Tested on Darter Pro (new image)

Issues

Test 6: DHCP Client Identification

  • nmcli shows static hostname instead of a dynamic hostname.
  • On the DHCP server the hostname is net-vm, not the dynamic hostname.
[ghaf@ghaf-1552147931:~]$ nmcli general hostname
net-vm
image **Test 11: Cross-VM Communication**
  • Admin-vm, audio-vm, business-vm, chrome-vm, comms-vm and zathura-vm do not have the environmental variable $GHAF_HOSTNAME.
  • Only ghaf-host, gui-vm and net-vm have it.
[ghaf@gui-vm:~]$ echo $GHAF_HOSTNAME
ghaf-1552147931

[ghaf@chrome-vm:~]$ echo $GHAF_HOSTNAME

Working

  • Devices get a unique hostname that persists between boots and new installations.
  • Hostname has the correct format ghaf-NNNNNNNNNN.
  • Devices get a unique device ID that persists between boots and new installations.
  • Device ID has the correct format XX-XX-XX-XX-XX.
  • All VMs except net-vm show the old static hostname.
  • All automated pre-merge tests pass on all tested target devices.

Notes

  • Machine IDs for VMs have been removed (test 5).
  • Skipped tests 7-9. We can try setting static/random/custom hardware IDs later. For now I believe the default values working is enough.
  • Also skipped test 10. I could not figure out how my device was calculating the ID. However it stays the same so I am happy with that.

fixed the issues, lets make another round

@milva-unikie
Copy link
Copy Markdown

Tested on Darter Pro (new image)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

9 participants