Architecture

Vrtmv is a client–server system with three parts.

The client — vrtmv

A single binary that runs where the customer's disk images live. It probes a source, extracts an inventory locally, and performs every translation lookup over the network against the authenticated Vrtmv API. It never connects to the translation database directly, and it never sends workload data off-host — only package identifiers.

The client is written in Rust: it parses untrusted disk images, so memory safety is a security property, not a convenience.

The API — vrtmv-api

The only thing that connects to the Translation Index. It is the authentication and account boundary: licensing, metering, and quota all live here. Clients authenticate per account with a bearer token; each lookup is metered. See The Vrtmv API.

The Translation Index

A curated PostgreSQL database mapping packages, services, config paths, service accounts, and MAC policy across distributions, with provenance on every row. It is the product's moat, and it is served, not shipped — never embedded in the client. See The Translation Index.

The pipeline

A migration flows through five stages:

  1. Probe / source — detect the image format, or open a mounted root or SSH source.
  2. Inventory — read the distro (os-release) and installed packages (dpkg / rpm) locally.
  3. Resolve — map native package names to canonical slugs via the API.
  4. Translate — fetch the target-distro packages, config-path relocations, and conditionals for those canonicals.
  5. Render — evaluate conditionals against the local inventory and emit an Ansible role plus a JSON attestation.

Stages 1–2 are entirely local. Stages 3–4 send only identifiers to the API. Stage 5 is local again — which is what makes the attestation specific to this host.

Where your data goes

DataLeaves the host?
Image contents, /etc, accounts, keysNo
Extracted inventory (package list)No
Package identifiers (names, canonical slugs, release pair)Yes — to the API, to look up translations
Depersonalized VM fingerprint (a one-way hash)Yes — for metering only
Host hint (a one-way hash of the machine's hardware id, see below)Yes — with usage events, to count the hosts behind one fingerprint
The hardware id itself (DMI product_uuid, NIC MAC addresses)No

The host hint exists because clones of one image share a machine-id, so several real hosts can report under one fingerprint. With each usage event the engine sends a SHA-256 of the host's DMI product_uuid, or, when the machine has none, of the sorted MAC addresses of its physical network interfaces. A placeholder product_uuid (malformed, one repeated digit, or a known firmware default) counts as none. The hash is keyed with the VM's raw identity (the stamp /etc/vrtmv/instance-id, else its machine-id), which never leaves the host, so a hint means something only beside its own fingerprint and cannot be traced back to the hardware id. It is read only from a running host (the local root, or over --ssh); a cold image sends none, and so does a run that cannot read the product_uuid (it is readable by root only). A local run reads it only as root, and only when / is the running system's own root, so a run inside a chroot of an image sends none. A network interface whose address is not its permanent one (a random or assigned MAC) is left out, and a machine with neither a product_uuid nor such an interface sends none. verify sends none, because it reads the target. A run inside a container is not told apart from a run on the machine the container runs on, and sends that machine's hint. Two virtual machines defined with one UUID (a libvirt definition copied to a second hypervisor with its <uuid>) report one hint. It is never a billing key: seats are counted by fingerprint.

Until 2 October 2026 an opt-in beta channel also reported diagnostics; the channel is retired and no build from 2.2.0 on contains the reporting code. See The beta channel (retired).