package model import ( "errors" "fmt" ) // MaxVMNameLen is the upper bound on a user-provided VM name. DNS // labels (RFC 1123) allow up to 63 octets; the name ends up as the // first label of `.vm` records served by banger's vmdns, and // also as the guest's /etc/hostname — so fitting both invariants in // a single ceiling keeps the model simple. const MaxVMNameLen = 63 // ValidateVMName rejects names that aren't safe to use as a DNS // label, a Linux hostname, a kernel-command-line token, or a // file-path component. Concretely: lowercase ASCII letters, digits, // and '-', 1..MaxVMNameLen chars, no leading or trailing hyphen. // // No normalization (trimming, case folding) — the VM name becomes // the user-visible identifier (store lookup key, `ssh .vm`, // `vm show `), and a silent rewrite would hand the user back // a different name than they typed. Reject early with an explicit // message instead. func ValidateVMName(name string) error { if name == "" { return errors.New("vm name is required") } if len(name) > MaxVMNameLen { return fmt.Errorf("vm name %q is %d characters; max is %d (DNS label limit)", name, len(name), MaxVMNameLen) } if name[0] == '-' || name[len(name)-1] == '-' { return fmt.Errorf("vm name %q cannot start or end with '-'", name) } for i, r := range name { switch { case r >= 'a' && r <= 'z': case r >= '0' && r <= '9': case r == '-': default: return fmt.Errorf("vm name %q has invalid character %q at position %d (allowed: lowercase a-z, 0-9, '-')", name, r, i) } } return nil }