From 663e8384a3bed7db57ec83fe8e2360295d9b4591 Mon Sep 17 00:00:00 2001
From: bravechamp <radular@protonmail.com>
Date: Sun, 13 Mar 2022 21:03:20 +0000
Subject: [PATCH 01/37] Nickname support

---
 cmd/headscale/cli/nodes.go                    |  53 +++
 gen/go/headscale/v1/headscale.pb.go           | 215 +++++------
 gen/go/headscale/v1/headscale.pb.gw.go        | 119 ++++++
 gen/go/headscale/v1/headscale_grpc.pb.go      |  36 ++
 gen/go/headscale/v1/machine.pb.go             | 339 +++++++++++++-----
 .../headscale/v1/headscale.swagger.json       |  48 +++
 grpcv1.go                                     |  22 ++
 machine.go                                    |  17 +-
 proto/headscale/v1/headscale.proto            |   6 +
 proto/headscale/v1/machine.proto              |  11 +
 10 files changed, 669 insertions(+), 197 deletions(-)

diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go
index 0abe87b..f0c4ede 100644
--- a/cmd/headscale/cli/nodes.go
+++ b/cmd/headscale/cli/nodes.go
@@ -40,6 +40,13 @@ func init() {
 	}
 	nodeCmd.AddCommand(expireNodeCmd)
 
+	renameNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
+	err = renameNodeCmd.MarkFlagRequired("identifier")
+	if err != nil {
+		log.Fatalf(err.Error())
+	}
+	nodeCmd.AddCommand(renameNodeCmd)
+
 	deleteNodeCmd.Flags().Uint64P("identifier", "i", 0, "Node identifier (ID)")
 	err = deleteNodeCmd.MarkFlagRequired("identifier")
 	if err != nil {
@@ -207,6 +214,50 @@ var expireNodeCmd = &cobra.Command{
 	},
 }
 
+var renameNodeCmd = &cobra.Command{
+	Use:     "rename NEW_NAME",
+	Short:   "Renames a machine in your network",
+	Run: func(cmd *cobra.Command, args []string) {
+		output, _ := cmd.Flags().GetString("output")
+
+		identifier, err := cmd.Flags().GetUint64("identifier")
+		if err != nil {
+			ErrorOutput(
+				err,
+				fmt.Sprintf("Error converting ID to integer: %s", err),
+				output,
+			)
+
+			return
+		}
+
+		ctx, client, conn, cancel := getHeadscaleCLIClient()
+		defer cancel()
+		defer conn.Close()
+
+		request := &v1.RenameMachineRequest{
+			MachineId: identifier,
+			NewName: args[0],
+		}
+
+		response, err := client.RenameMachine(ctx, request)
+		if err != nil {
+			ErrorOutput(
+				err,
+				fmt.Sprintf(
+					"Cannot expire machine: %s\n",
+					status.Convert(err).Message(),
+				),
+				output,
+			)
+
+			return
+		}
+
+		SuccessOutput(response.Machine, "Machine renamed", output)
+	},
+}
+
 var deleteNodeCmd = &cobra.Command{
 	Use:     "delete",
 	Short:   "Delete a node",
@@ -304,6 +355,7 @@ func nodesToPtables(
 		{
 			"ID",
 			"Name",
+			"Nickname",
 			"NodeKey",
 			"Namespace",
 			"IP addresses",
@@ -368,6 +420,7 @@ func nodesToPtables(
 			[]string{
 				strconv.FormatUint(machine.Id, headscale.Base10),
 				machine.Name,
+				machine.Nickname,
 				nodeKey.ShortString(),
 				namespace,
 				strings.Join(machine.IpAddresses, ", "),
diff --git a/gen/go/headscale/v1/headscale.pb.go b/gen/go/headscale/v1/headscale.pb.go
index 5c70011..6643b16 100644
--- a/gen/go/headscale/v1/headscale.pb.go
+++ b/gen/go/headscale/v1/headscale.pb.go
@@ -36,7 +36,7 @@ var file_headscale_v1_headscale_proto_rawDesc = []byte{
 	0x6f, 0x74, 0x6f, 0x1a, 0x19, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76,
 	0x31, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x19,
 	0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69,
-	0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xa3, 0x13, 0x0a, 0x10, 0x48, 0x65,
+	0x6b, 0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x32, 0xb6, 0x14, 0x0a, 0x10, 0x48, 0x65,
 	0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x77,
 	0x0a, 0x0c, 0x47, 0x65, 0x74, 0x4e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x21,
 	0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65,
@@ -144,57 +144,66 @@ var file_headscale_v1_headscale_proto_rawDesc = []byte{
 	0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x22, 0x23, 0x2f, 0x61, 0x70,
 	0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b, 0x6d, 0x61,
 	0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65,
-	0x12, 0x6e, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73,
-	0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e,
-	0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e,
-	0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x11, 0x12,
-	0x0f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
-	0x12, 0x8b, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52,
-	0x6f, 0x75, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65,
-	0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f,
-	0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e, 0x68, 0x65, 0x61,
-	0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63,
-	0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
-	0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f,
-	0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68,
-	0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x97,
-	0x01, 0x0a, 0x13, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
-	0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x28, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61,
-	0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x63, 0x68,
-	0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
-	0x1a, 0x29, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e,
-	0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75,
-	0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4,
-	0x93, 0x02, 0x25, 0x22, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63,
+	0x12, 0x90, 0x01, 0x0a, 0x0d, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69,
+	0x6e, 0x65, 0x12, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76,
+	0x31, 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x23, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61,
+	0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x4d, 0x61, 0x63, 0x68,
+	0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x36, 0x82, 0xd3, 0xe4,
+	0x93, 0x02, 0x30, 0x22, 0x2e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63,
 	0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64,
-	0x7d, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x70, 0x0a, 0x0c, 0x43, 0x72, 0x65, 0x61,
-	0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73,
-	0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70,
-	0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65,
+	0x7d, 0x2f, 0x72, 0x65, 0x6e, 0x61, 0x6d, 0x65, 0x2f, 0x7b, 0x6e, 0x65, 0x77, 0x5f, 0x6e, 0x61,
+	0x6d, 0x65, 0x7d, 0x12, 0x6e, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69,
+	0x6e, 0x65, 0x73, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e,
+	0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52,
+	0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61,
+	0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
+	0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x17, 0x82, 0xd3, 0xe4, 0x93,
+	0x02, 0x11, 0x12, 0x0f, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68,
+	0x69, 0x6e, 0x65, 0x12, 0x8b, 0x01, 0x0a, 0x0f, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69,
+	0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x12, 0x24, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63,
+	0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
+	0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x25, 0x2e,
+	0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x47, 0x65, 0x74,
+	0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x12, 0x23, 0x2f, 0x61,
+	0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b, 0x6d,
+	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65,
+	0x73, 0x12, 0x97, 0x01, 0x0a, 0x13, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x63, 0x68,
+	0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x28, 0x2e, 0x68, 0x65, 0x61, 0x64,
+	0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d,
+	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75,
+	0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e,
+	0x76, 0x31, 0x2e, 0x45, 0x6e, 0x61, 0x62, 0x6c, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
+	0x52, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x2b,
+	0x82, 0xd3, 0xe4, 0x93, 0x02, 0x25, 0x22, 0x23, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f,
+	0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2f, 0x7b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
+	0x5f, 0x69, 0x64, 0x7d, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x12, 0x70, 0x0a, 0x0c, 0x43,
+	0x72, 0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x68, 0x65,
 	0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74,
-	0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22,
-	0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x22, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31,
-	0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a, 0x0c, 0x45, 0x78,
-	0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e, 0x68, 0x65, 0x61,
-	0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65,
-	0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22, 0x2e,
+	0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x22,
+	0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x43, 0x72,
+	0x65, 0x61, 0x74, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x22, 0x19, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x13, 0x22, 0x0e, 0x2f, 0x61, 0x70, 0x69,
+	0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x3a, 0x01, 0x2a, 0x12, 0x77, 0x0a,
+	0x0c, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x12, 0x21, 0x2e,
 	0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x45, 0x78, 0x70,
-	0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
-	0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x61, 0x70, 0x69, 0x2f,
-	0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x2f, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65,
-	0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65,
-	0x79, 0x73, 0x12, 0x20, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76,
-	0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x52, 0x65, 0x71,
-	0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65,
-	0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x10, 0x12,
-	0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x42,
-	0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75,
-	0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65,
-	0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x33,
+	0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x1a, 0x22, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e,
+	0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x22, 0x20, 0x82, 0xd3, 0xe4, 0x93, 0x02, 0x1a, 0x22, 0x15, 0x2f, 0x61,
+	0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b, 0x65, 0x79, 0x2f, 0x65, 0x78, 0x70,
+	0x69, 0x72, 0x65, 0x3a, 0x01, 0x2a, 0x12, 0x6a, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70,
+	0x69, 0x4b, 0x65, 0x79, 0x73, 0x12, 0x20, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c,
+	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65, 0x79, 0x73,
+	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x21, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63,
+	0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x41, 0x70, 0x69, 0x4b, 0x65,
+	0x79, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x16, 0x82, 0xd3, 0xe4, 0x93,
+	0x02, 0x10, 0x12, 0x0e, 0x2f, 0x61, 0x70, 0x69, 0x2f, 0x76, 0x31, 0x2f, 0x61, 0x70, 0x69, 0x6b,
+	0x65, 0x79, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
+	0x2f, 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63,
+	0x61, 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var file_headscale_v1_headscale_proto_goTypes = []interface{}{
@@ -211,31 +220,33 @@ var file_headscale_v1_headscale_proto_goTypes = []interface{}{
 	(*RegisterMachineRequest)(nil),      // 10: headscale.v1.RegisterMachineRequest
 	(*DeleteMachineRequest)(nil),        // 11: headscale.v1.DeleteMachineRequest
 	(*ExpireMachineRequest)(nil),        // 12: headscale.v1.ExpireMachineRequest
-	(*ListMachinesRequest)(nil),         // 13: headscale.v1.ListMachinesRequest
-	(*GetMachineRouteRequest)(nil),      // 14: headscale.v1.GetMachineRouteRequest
-	(*EnableMachineRoutesRequest)(nil),  // 15: headscale.v1.EnableMachineRoutesRequest
-	(*CreateApiKeyRequest)(nil),         // 16: headscale.v1.CreateApiKeyRequest
-	(*ExpireApiKeyRequest)(nil),         // 17: headscale.v1.ExpireApiKeyRequest
-	(*ListApiKeysRequest)(nil),          // 18: headscale.v1.ListApiKeysRequest
-	(*GetNamespaceResponse)(nil),        // 19: headscale.v1.GetNamespaceResponse
-	(*CreateNamespaceResponse)(nil),     // 20: headscale.v1.CreateNamespaceResponse
-	(*RenameNamespaceResponse)(nil),     // 21: headscale.v1.RenameNamespaceResponse
-	(*DeleteNamespaceResponse)(nil),     // 22: headscale.v1.DeleteNamespaceResponse
-	(*ListNamespacesResponse)(nil),      // 23: headscale.v1.ListNamespacesResponse
-	(*CreatePreAuthKeyResponse)(nil),    // 24: headscale.v1.CreatePreAuthKeyResponse
-	(*ExpirePreAuthKeyResponse)(nil),    // 25: headscale.v1.ExpirePreAuthKeyResponse
-	(*ListPreAuthKeysResponse)(nil),     // 26: headscale.v1.ListPreAuthKeysResponse
-	(*DebugCreateMachineResponse)(nil),  // 27: headscale.v1.DebugCreateMachineResponse
-	(*GetMachineResponse)(nil),          // 28: headscale.v1.GetMachineResponse
-	(*RegisterMachineResponse)(nil),     // 29: headscale.v1.RegisterMachineResponse
-	(*DeleteMachineResponse)(nil),       // 30: headscale.v1.DeleteMachineResponse
-	(*ExpireMachineResponse)(nil),       // 31: headscale.v1.ExpireMachineResponse
-	(*ListMachinesResponse)(nil),        // 32: headscale.v1.ListMachinesResponse
-	(*GetMachineRouteResponse)(nil),     // 33: headscale.v1.GetMachineRouteResponse
-	(*EnableMachineRoutesResponse)(nil), // 34: headscale.v1.EnableMachineRoutesResponse
-	(*CreateApiKeyResponse)(nil),        // 35: headscale.v1.CreateApiKeyResponse
-	(*ExpireApiKeyResponse)(nil),        // 36: headscale.v1.ExpireApiKeyResponse
-	(*ListApiKeysResponse)(nil),         // 37: headscale.v1.ListApiKeysResponse
+	(*RenameMachineRequest)(nil),        // 13: headscale.v1.RenameMachineRequest
+	(*ListMachinesRequest)(nil),         // 14: headscale.v1.ListMachinesRequest
+	(*GetMachineRouteRequest)(nil),      // 15: headscale.v1.GetMachineRouteRequest
+	(*EnableMachineRoutesRequest)(nil),  // 16: headscale.v1.EnableMachineRoutesRequest
+	(*CreateApiKeyRequest)(nil),         // 17: headscale.v1.CreateApiKeyRequest
+	(*ExpireApiKeyRequest)(nil),         // 18: headscale.v1.ExpireApiKeyRequest
+	(*ListApiKeysRequest)(nil),          // 19: headscale.v1.ListApiKeysRequest
+	(*GetNamespaceResponse)(nil),        // 20: headscale.v1.GetNamespaceResponse
+	(*CreateNamespaceResponse)(nil),     // 21: headscale.v1.CreateNamespaceResponse
+	(*RenameNamespaceResponse)(nil),     // 22: headscale.v1.RenameNamespaceResponse
+	(*DeleteNamespaceResponse)(nil),     // 23: headscale.v1.DeleteNamespaceResponse
+	(*ListNamespacesResponse)(nil),      // 24: headscale.v1.ListNamespacesResponse
+	(*CreatePreAuthKeyResponse)(nil),    // 25: headscale.v1.CreatePreAuthKeyResponse
+	(*ExpirePreAuthKeyResponse)(nil),    // 26: headscale.v1.ExpirePreAuthKeyResponse
+	(*ListPreAuthKeysResponse)(nil),     // 27: headscale.v1.ListPreAuthKeysResponse
+	(*DebugCreateMachineResponse)(nil),  // 28: headscale.v1.DebugCreateMachineResponse
+	(*GetMachineResponse)(nil),          // 29: headscale.v1.GetMachineResponse
+	(*RegisterMachineResponse)(nil),     // 30: headscale.v1.RegisterMachineResponse
+	(*DeleteMachineResponse)(nil),       // 31: headscale.v1.DeleteMachineResponse
+	(*ExpireMachineResponse)(nil),       // 32: headscale.v1.ExpireMachineResponse
+	(*RenameMachineResponse)(nil),       // 33: headscale.v1.RenameMachineResponse
+	(*ListMachinesResponse)(nil),        // 34: headscale.v1.ListMachinesResponse
+	(*GetMachineRouteResponse)(nil),     // 35: headscale.v1.GetMachineRouteResponse
+	(*EnableMachineRoutesResponse)(nil), // 36: headscale.v1.EnableMachineRoutesResponse
+	(*CreateApiKeyResponse)(nil),        // 37: headscale.v1.CreateApiKeyResponse
+	(*ExpireApiKeyResponse)(nil),        // 38: headscale.v1.ExpireApiKeyResponse
+	(*ListApiKeysResponse)(nil),         // 39: headscale.v1.ListApiKeysResponse
 }
 var file_headscale_v1_headscale_proto_depIdxs = []int32{
 	0,  // 0: headscale.v1.HeadscaleService.GetNamespace:input_type -> headscale.v1.GetNamespaceRequest
@@ -251,33 +262,35 @@ var file_headscale_v1_headscale_proto_depIdxs = []int32{
 	10, // 10: headscale.v1.HeadscaleService.RegisterMachine:input_type -> headscale.v1.RegisterMachineRequest
 	11, // 11: headscale.v1.HeadscaleService.DeleteMachine:input_type -> headscale.v1.DeleteMachineRequest
 	12, // 12: headscale.v1.HeadscaleService.ExpireMachine:input_type -> headscale.v1.ExpireMachineRequest
-	13, // 13: headscale.v1.HeadscaleService.ListMachines:input_type -> headscale.v1.ListMachinesRequest
-	14, // 14: headscale.v1.HeadscaleService.GetMachineRoute:input_type -> headscale.v1.GetMachineRouteRequest
-	15, // 15: headscale.v1.HeadscaleService.EnableMachineRoutes:input_type -> headscale.v1.EnableMachineRoutesRequest
-	16, // 16: headscale.v1.HeadscaleService.CreateApiKey:input_type -> headscale.v1.CreateApiKeyRequest
-	17, // 17: headscale.v1.HeadscaleService.ExpireApiKey:input_type -> headscale.v1.ExpireApiKeyRequest
-	18, // 18: headscale.v1.HeadscaleService.ListApiKeys:input_type -> headscale.v1.ListApiKeysRequest
-	19, // 19: headscale.v1.HeadscaleService.GetNamespace:output_type -> headscale.v1.GetNamespaceResponse
-	20, // 20: headscale.v1.HeadscaleService.CreateNamespace:output_type -> headscale.v1.CreateNamespaceResponse
-	21, // 21: headscale.v1.HeadscaleService.RenameNamespace:output_type -> headscale.v1.RenameNamespaceResponse
-	22, // 22: headscale.v1.HeadscaleService.DeleteNamespace:output_type -> headscale.v1.DeleteNamespaceResponse
-	23, // 23: headscale.v1.HeadscaleService.ListNamespaces:output_type -> headscale.v1.ListNamespacesResponse
-	24, // 24: headscale.v1.HeadscaleService.CreatePreAuthKey:output_type -> headscale.v1.CreatePreAuthKeyResponse
-	25, // 25: headscale.v1.HeadscaleService.ExpirePreAuthKey:output_type -> headscale.v1.ExpirePreAuthKeyResponse
-	26, // 26: headscale.v1.HeadscaleService.ListPreAuthKeys:output_type -> headscale.v1.ListPreAuthKeysResponse
-	27, // 27: headscale.v1.HeadscaleService.DebugCreateMachine:output_type -> headscale.v1.DebugCreateMachineResponse
-	28, // 28: headscale.v1.HeadscaleService.GetMachine:output_type -> headscale.v1.GetMachineResponse
-	29, // 29: headscale.v1.HeadscaleService.RegisterMachine:output_type -> headscale.v1.RegisterMachineResponse
-	30, // 30: headscale.v1.HeadscaleService.DeleteMachine:output_type -> headscale.v1.DeleteMachineResponse
-	31, // 31: headscale.v1.HeadscaleService.ExpireMachine:output_type -> headscale.v1.ExpireMachineResponse
-	32, // 32: headscale.v1.HeadscaleService.ListMachines:output_type -> headscale.v1.ListMachinesResponse
-	33, // 33: headscale.v1.HeadscaleService.GetMachineRoute:output_type -> headscale.v1.GetMachineRouteResponse
-	34, // 34: headscale.v1.HeadscaleService.EnableMachineRoutes:output_type -> headscale.v1.EnableMachineRoutesResponse
-	35, // 35: headscale.v1.HeadscaleService.CreateApiKey:output_type -> headscale.v1.CreateApiKeyResponse
-	36, // 36: headscale.v1.HeadscaleService.ExpireApiKey:output_type -> headscale.v1.ExpireApiKeyResponse
-	37, // 37: headscale.v1.HeadscaleService.ListApiKeys:output_type -> headscale.v1.ListApiKeysResponse
-	19, // [19:38] is the sub-list for method output_type
-	0,  // [0:19] is the sub-list for method input_type
+	13, // 13: headscale.v1.HeadscaleService.RenameMachine:input_type -> headscale.v1.RenameMachineRequest
+	14, // 14: headscale.v1.HeadscaleService.ListMachines:input_type -> headscale.v1.ListMachinesRequest
+	15, // 15: headscale.v1.HeadscaleService.GetMachineRoute:input_type -> headscale.v1.GetMachineRouteRequest
+	16, // 16: headscale.v1.HeadscaleService.EnableMachineRoutes:input_type -> headscale.v1.EnableMachineRoutesRequest
+	17, // 17: headscale.v1.HeadscaleService.CreateApiKey:input_type -> headscale.v1.CreateApiKeyRequest
+	18, // 18: headscale.v1.HeadscaleService.ExpireApiKey:input_type -> headscale.v1.ExpireApiKeyRequest
+	19, // 19: headscale.v1.HeadscaleService.ListApiKeys:input_type -> headscale.v1.ListApiKeysRequest
+	20, // 20: headscale.v1.HeadscaleService.GetNamespace:output_type -> headscale.v1.GetNamespaceResponse
+	21, // 21: headscale.v1.HeadscaleService.CreateNamespace:output_type -> headscale.v1.CreateNamespaceResponse
+	22, // 22: headscale.v1.HeadscaleService.RenameNamespace:output_type -> headscale.v1.RenameNamespaceResponse
+	23, // 23: headscale.v1.HeadscaleService.DeleteNamespace:output_type -> headscale.v1.DeleteNamespaceResponse
+	24, // 24: headscale.v1.HeadscaleService.ListNamespaces:output_type -> headscale.v1.ListNamespacesResponse
+	25, // 25: headscale.v1.HeadscaleService.CreatePreAuthKey:output_type -> headscale.v1.CreatePreAuthKeyResponse
+	26, // 26: headscale.v1.HeadscaleService.ExpirePreAuthKey:output_type -> headscale.v1.ExpirePreAuthKeyResponse
+	27, // 27: headscale.v1.HeadscaleService.ListPreAuthKeys:output_type -> headscale.v1.ListPreAuthKeysResponse
+	28, // 28: headscale.v1.HeadscaleService.DebugCreateMachine:output_type -> headscale.v1.DebugCreateMachineResponse
+	29, // 29: headscale.v1.HeadscaleService.GetMachine:output_type -> headscale.v1.GetMachineResponse
+	30, // 30: headscale.v1.HeadscaleService.RegisterMachine:output_type -> headscale.v1.RegisterMachineResponse
+	31, // 31: headscale.v1.HeadscaleService.DeleteMachine:output_type -> headscale.v1.DeleteMachineResponse
+	32, // 32: headscale.v1.HeadscaleService.ExpireMachine:output_type -> headscale.v1.ExpireMachineResponse
+	33, // 33: headscale.v1.HeadscaleService.RenameMachine:output_type -> headscale.v1.RenameMachineResponse
+	34, // 34: headscale.v1.HeadscaleService.ListMachines:output_type -> headscale.v1.ListMachinesResponse
+	35, // 35: headscale.v1.HeadscaleService.GetMachineRoute:output_type -> headscale.v1.GetMachineRouteResponse
+	36, // 36: headscale.v1.HeadscaleService.EnableMachineRoutes:output_type -> headscale.v1.EnableMachineRoutesResponse
+	37, // 37: headscale.v1.HeadscaleService.CreateApiKey:output_type -> headscale.v1.CreateApiKeyResponse
+	38, // 38: headscale.v1.HeadscaleService.ExpireApiKey:output_type -> headscale.v1.ExpireApiKeyResponse
+	39, // 39: headscale.v1.HeadscaleService.ListApiKeys:output_type -> headscale.v1.ListApiKeysResponse
+	20, // [20:40] is the sub-list for method output_type
+	0,  // [0:20] is the sub-list for method input_type
 	0,  // [0:0] is the sub-list for extension type_name
 	0,  // [0:0] is the sub-list for extension extendee
 	0,  // [0:0] is the sub-list for field type_name
diff --git a/gen/go/headscale/v1/headscale.pb.gw.go b/gen/go/headscale/v1/headscale.pb.gw.go
index 0938e14..80317d6 100644
--- a/gen/go/headscale/v1/headscale.pb.gw.go
+++ b/gen/go/headscale/v1/headscale.pb.gw.go
@@ -589,6 +589,78 @@ func local_request_HeadscaleService_ExpireMachine_0(ctx context.Context, marshal
 
 }
 
+func request_HeadscaleService_RenameMachine_0(ctx context.Context, marshaler runtime.Marshaler, client HeadscaleServiceClient, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq RenameMachineRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["machine_id"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "machine_id")
+	}
+
+	protoReq.MachineId, err = runtime.Uint64(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "machine_id", err)
+	}
+
+	val, ok = pathParams["new_name"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "new_name")
+	}
+
+	protoReq.NewName, err = runtime.String(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "new_name", err)
+	}
+
+	msg, err := client.RenameMachine(ctx, &protoReq, grpc.Header(&metadata.HeaderMD), grpc.Trailer(&metadata.TrailerMD))
+	return msg, metadata, err
+
+}
+
+func local_request_HeadscaleService_RenameMachine_0(ctx context.Context, marshaler runtime.Marshaler, server HeadscaleServiceServer, req *http.Request, pathParams map[string]string) (proto.Message, runtime.ServerMetadata, error) {
+	var protoReq RenameMachineRequest
+	var metadata runtime.ServerMetadata
+
+	var (
+		val string
+		ok  bool
+		err error
+		_   = err
+	)
+
+	val, ok = pathParams["machine_id"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "machine_id")
+	}
+
+	protoReq.MachineId, err = runtime.Uint64(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "machine_id", err)
+	}
+
+	val, ok = pathParams["new_name"]
+	if !ok {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "missing parameter %s", "new_name")
+	}
+
+	protoReq.NewName, err = runtime.String(val)
+	if err != nil {
+		return nil, metadata, status.Errorf(codes.InvalidArgument, "type mismatch, parameter: %s, error: %v", "new_name", err)
+	}
+
+	msg, err := server.RenameMachine(ctx, &protoReq)
+	return msg, metadata, err
+
+}
+
 var (
 	filter_HeadscaleService_ListMachines_0 = &utilities.DoubleArray{Encoding: map[string]int{}, Base: []int(nil), Check: []int(nil)}
 )
@@ -1138,6 +1210,29 @@ func RegisterHeadscaleServiceHandlerServer(ctx context.Context, mux *runtime.Ser
 
 	})
 
+	mux.Handle("POST", pattern_HeadscaleService_RenameMachine_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		var stream runtime.ServerTransportStream
+		ctx = grpc.NewContextWithServerTransportStream(ctx, &stream)
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateIncomingContext(ctx, mux, req, "/headscale.v1.HeadscaleService/RenameMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}/rename/{new_name}"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := local_request_HeadscaleService_RenameMachine_0(rctx, inboundMarshaler, server, req, pathParams)
+		md.HeaderMD, md.TrailerMD = metadata.Join(md.HeaderMD, stream.Header()), metadata.Join(md.TrailerMD, stream.Trailer())
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_HeadscaleService_RenameMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	mux.Handle("GET", pattern_HeadscaleService_ListMachines_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 		ctx, cancel := context.WithCancel(req.Context())
 		defer cancel()
@@ -1577,6 +1672,26 @@ func RegisterHeadscaleServiceHandlerClient(ctx context.Context, mux *runtime.Ser
 
 	})
 
+	mux.Handle("POST", pattern_HeadscaleService_RenameMachine_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
+		ctx, cancel := context.WithCancel(req.Context())
+		defer cancel()
+		inboundMarshaler, outboundMarshaler := runtime.MarshalerForRequest(mux, req)
+		rctx, err := runtime.AnnotateContext(ctx, mux, req, "/headscale.v1.HeadscaleService/RenameMachine", runtime.WithHTTPPathPattern("/api/v1/machine/{machine_id}/rename/{new_name}"))
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+		resp, md, err := request_HeadscaleService_RenameMachine_0(rctx, inboundMarshaler, client, req, pathParams)
+		ctx = runtime.NewServerMetadataContext(ctx, md)
+		if err != nil {
+			runtime.HTTPError(ctx, mux, outboundMarshaler, w, req, err)
+			return
+		}
+
+		forward_HeadscaleService_RenameMachine_0(ctx, mux, outboundMarshaler, w, req, resp, mux.GetForwardResponseOptions()...)
+
+	})
+
 	mux.Handle("GET", pattern_HeadscaleService_ListMachines_0, func(w http.ResponseWriter, req *http.Request, pathParams map[string]string) {
 		ctx, cancel := context.WithCancel(req.Context())
 		defer cancel()
@@ -1727,6 +1842,8 @@ var (
 
 	pattern_HeadscaleService_ExpireMachine_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "machine", "machine_id", "expire"}, ""))
 
+	pattern_HeadscaleService_RenameMachine_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4, 1, 0, 4, 1, 5, 5}, []string{"api", "v1", "machine", "machine_id", "rename", "new_name"}, ""))
+
 	pattern_HeadscaleService_ListMachines_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2}, []string{"api", "v1", "machine"}, ""))
 
 	pattern_HeadscaleService_GetMachineRoute_0 = runtime.MustPattern(runtime.NewPattern(1, []int{2, 0, 2, 1, 2, 2, 1, 0, 4, 1, 5, 3, 2, 4}, []string{"api", "v1", "machine", "machine_id", "routes"}, ""))
@@ -1767,6 +1884,8 @@ var (
 
 	forward_HeadscaleService_ExpireMachine_0 = runtime.ForwardResponseMessage
 
+	forward_HeadscaleService_RenameMachine_0 = runtime.ForwardResponseMessage
+
 	forward_HeadscaleService_ListMachines_0 = runtime.ForwardResponseMessage
 
 	forward_HeadscaleService_GetMachineRoute_0 = runtime.ForwardResponseMessage
diff --git a/gen/go/headscale/v1/headscale_grpc.pb.go b/gen/go/headscale/v1/headscale_grpc.pb.go
index e4edf3f..fed52e5 100644
--- a/gen/go/headscale/v1/headscale_grpc.pb.go
+++ b/gen/go/headscale/v1/headscale_grpc.pb.go
@@ -38,6 +38,7 @@ type HeadscaleServiceClient interface {
 	RegisterMachine(ctx context.Context, in *RegisterMachineRequest, opts ...grpc.CallOption) (*RegisterMachineResponse, error)
 	DeleteMachine(ctx context.Context, in *DeleteMachineRequest, opts ...grpc.CallOption) (*DeleteMachineResponse, error)
 	ExpireMachine(ctx context.Context, in *ExpireMachineRequest, opts ...grpc.CallOption) (*ExpireMachineResponse, error)
+	RenameMachine(ctx context.Context, in *RenameMachineRequest, opts ...grpc.CallOption) (*RenameMachineResponse, error)
 	ListMachines(ctx context.Context, in *ListMachinesRequest, opts ...grpc.CallOption) (*ListMachinesResponse, error)
 	// --- Route start ---
 	GetMachineRoute(ctx context.Context, in *GetMachineRouteRequest, opts ...grpc.CallOption) (*GetMachineRouteResponse, error)
@@ -173,6 +174,15 @@ func (c *headscaleServiceClient) ExpireMachine(ctx context.Context, in *ExpireMa
 	return out, nil
 }
 
+func (c *headscaleServiceClient) RenameMachine(ctx context.Context, in *RenameMachineRequest, opts ...grpc.CallOption) (*RenameMachineResponse, error) {
+	out := new(RenameMachineResponse)
+	err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/RenameMachine", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 func (c *headscaleServiceClient) ListMachines(ctx context.Context, in *ListMachinesRequest, opts ...grpc.CallOption) (*ListMachinesResponse, error) {
 	out := new(ListMachinesResponse)
 	err := c.cc.Invoke(ctx, "/headscale.v1.HeadscaleService/ListMachines", in, out, opts...)
@@ -247,6 +257,7 @@ type HeadscaleServiceServer interface {
 	RegisterMachine(context.Context, *RegisterMachineRequest) (*RegisterMachineResponse, error)
 	DeleteMachine(context.Context, *DeleteMachineRequest) (*DeleteMachineResponse, error)
 	ExpireMachine(context.Context, *ExpireMachineRequest) (*ExpireMachineResponse, error)
+	RenameMachine(context.Context, *RenameMachineRequest) (*RenameMachineResponse, error)
 	ListMachines(context.Context, *ListMachinesRequest) (*ListMachinesResponse, error)
 	// --- Route start ---
 	GetMachineRoute(context.Context, *GetMachineRouteRequest) (*GetMachineRouteResponse, error)
@@ -301,6 +312,9 @@ func (UnimplementedHeadscaleServiceServer) DeleteMachine(context.Context, *Delet
 func (UnimplementedHeadscaleServiceServer) ExpireMachine(context.Context, *ExpireMachineRequest) (*ExpireMachineResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method ExpireMachine not implemented")
 }
+func (UnimplementedHeadscaleServiceServer) RenameMachine(context.Context, *RenameMachineRequest) (*RenameMachineResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method RenameMachine not implemented")
+}
 func (UnimplementedHeadscaleServiceServer) ListMachines(context.Context, *ListMachinesRequest) (*ListMachinesResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method ListMachines not implemented")
 }
@@ -566,6 +580,24 @@ func _HeadscaleService_ExpireMachine_Handler(srv interface{}, ctx context.Contex
 	return interceptor(ctx, in, info, handler)
 }
 
+func _HeadscaleService_RenameMachine_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(RenameMachineRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(HeadscaleServiceServer).RenameMachine(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/headscale.v1.HeadscaleService/RenameMachine",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(HeadscaleServiceServer).RenameMachine(ctx, req.(*RenameMachineRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 func _HeadscaleService_ListMachines_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(ListMachinesRequest)
 	if err := dec(in); err != nil {
@@ -733,6 +765,10 @@ var HeadscaleService_ServiceDesc = grpc.ServiceDesc{
 			MethodName: "ExpireMachine",
 			Handler:    _HeadscaleService_ExpireMachine_Handler,
 		},
+		{
+			MethodName: "RenameMachine",
+			Handler:    _HeadscaleService_RenameMachine_Handler,
+		},
 		{
 			MethodName: "ListMachines",
 			Handler:    _HeadscaleService_ListMachines_Handler,
diff --git a/gen/go/headscale/v1/machine.pb.go b/gen/go/headscale/v1/machine.pb.go
index b25db29..862b710 100644
--- a/gen/go/headscale/v1/machine.pb.go
+++ b/gen/go/headscale/v1/machine.pb.go
@@ -91,6 +91,7 @@ type Machine struct {
 	PreAuthKey           *PreAuthKey            `protobuf:"bytes,11,opt,name=pre_auth_key,json=preAuthKey,proto3" json:"pre_auth_key,omitempty"`
 	CreatedAt            *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
 	RegisterMethod       RegisterMethod         `protobuf:"varint,13,opt,name=register_method,json=registerMethod,proto3,enum=headscale.v1.RegisterMethod" json:"register_method,omitempty"`
+	Nickname             string                 `protobuf:"bytes,18,opt,name=nickname,proto3" json:"nickname,omitempty"`
 }
 
 func (x *Machine) Reset() {
@@ -216,6 +217,13 @@ func (x *Machine) GetRegisterMethod() RegisterMethod {
 	return RegisterMethod_REGISTER_METHOD_UNSPECIFIED
 }
 
+func (x *Machine) GetNickname() string {
+	if x != nil {
+		return x.Nickname
+	}
+	return ""
+}
+
 type RegisterMachineRequest struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -591,6 +599,108 @@ func (x *ExpireMachineResponse) GetMachine() *Machine {
 	return nil
 }
 
+type RenameMachineRequest struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	MachineId uint64 `protobuf:"varint,1,opt,name=machine_id,json=machineId,proto3" json:"machine_id,omitempty"`
+	NewName   string `protobuf:"bytes,2,opt,name=new_name,json=newName,proto3" json:"new_name,omitempty"`
+}
+
+func (x *RenameMachineRequest) Reset() {
+	*x = RenameMachineRequest{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_headscale_v1_machine_proto_msgTypes[9]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *RenameMachineRequest) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RenameMachineRequest) ProtoMessage() {}
+
+func (x *RenameMachineRequest) ProtoReflect() protoreflect.Message {
+	mi := &file_headscale_v1_machine_proto_msgTypes[9]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use RenameMachineRequest.ProtoReflect.Descriptor instead.
+func (*RenameMachineRequest) Descriptor() ([]byte, []int) {
+	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{9}
+}
+
+func (x *RenameMachineRequest) GetMachineId() uint64 {
+	if x != nil {
+		return x.MachineId
+	}
+	return 0
+}
+
+func (x *RenameMachineRequest) GetNewName() string {
+	if x != nil {
+		return x.NewName
+	}
+	return ""
+}
+
+type RenameMachineResponse struct {
+	state         protoimpl.MessageState
+	sizeCache     protoimpl.SizeCache
+	unknownFields protoimpl.UnknownFields
+
+	Machine *Machine `protobuf:"bytes,1,opt,name=machine,proto3" json:"machine,omitempty"`
+}
+
+func (x *RenameMachineResponse) Reset() {
+	*x = RenameMachineResponse{}
+	if protoimpl.UnsafeEnabled {
+		mi := &file_headscale_v1_machine_proto_msgTypes[10]
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		ms.StoreMessageInfo(mi)
+	}
+}
+
+func (x *RenameMachineResponse) String() string {
+	return protoimpl.X.MessageStringOf(x)
+}
+
+func (*RenameMachineResponse) ProtoMessage() {}
+
+func (x *RenameMachineResponse) ProtoReflect() protoreflect.Message {
+	mi := &file_headscale_v1_machine_proto_msgTypes[10]
+	if protoimpl.UnsafeEnabled && x != nil {
+		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
+		if ms.LoadMessageInfo() == nil {
+			ms.StoreMessageInfo(mi)
+		}
+		return ms
+	}
+	return mi.MessageOf(x)
+}
+
+// Deprecated: Use RenameMachineResponse.ProtoReflect.Descriptor instead.
+func (*RenameMachineResponse) Descriptor() ([]byte, []int) {
+	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{10}
+}
+
+func (x *RenameMachineResponse) GetMachine() *Machine {
+	if x != nil {
+		return x.Machine
+	}
+	return nil
+}
+
 type ListMachinesRequest struct {
 	state         protoimpl.MessageState
 	sizeCache     protoimpl.SizeCache
@@ -602,7 +712,7 @@ type ListMachinesRequest struct {
 func (x *ListMachinesRequest) Reset() {
 	*x = ListMachinesRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_headscale_v1_machine_proto_msgTypes[9]
+		mi := &file_headscale_v1_machine_proto_msgTypes[11]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -615,7 +725,7 @@ func (x *ListMachinesRequest) String() string {
 func (*ListMachinesRequest) ProtoMessage() {}
 
 func (x *ListMachinesRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_headscale_v1_machine_proto_msgTypes[9]
+	mi := &file_headscale_v1_machine_proto_msgTypes[11]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -628,7 +738,7 @@ func (x *ListMachinesRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListMachinesRequest.ProtoReflect.Descriptor instead.
 func (*ListMachinesRequest) Descriptor() ([]byte, []int) {
-	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{9}
+	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{11}
 }
 
 func (x *ListMachinesRequest) GetNamespace() string {
@@ -649,7 +759,7 @@ type ListMachinesResponse struct {
 func (x *ListMachinesResponse) Reset() {
 	*x = ListMachinesResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_headscale_v1_machine_proto_msgTypes[10]
+		mi := &file_headscale_v1_machine_proto_msgTypes[12]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -662,7 +772,7 @@ func (x *ListMachinesResponse) String() string {
 func (*ListMachinesResponse) ProtoMessage() {}
 
 func (x *ListMachinesResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_headscale_v1_machine_proto_msgTypes[10]
+	mi := &file_headscale_v1_machine_proto_msgTypes[12]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -675,7 +785,7 @@ func (x *ListMachinesResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use ListMachinesResponse.ProtoReflect.Descriptor instead.
 func (*ListMachinesResponse) Descriptor() ([]byte, []int) {
-	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{10}
+	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{12}
 }
 
 func (x *ListMachinesResponse) GetMachines() []*Machine {
@@ -699,7 +809,7 @@ type DebugCreateMachineRequest struct {
 func (x *DebugCreateMachineRequest) Reset() {
 	*x = DebugCreateMachineRequest{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_headscale_v1_machine_proto_msgTypes[11]
+		mi := &file_headscale_v1_machine_proto_msgTypes[13]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -712,7 +822,7 @@ func (x *DebugCreateMachineRequest) String() string {
 func (*DebugCreateMachineRequest) ProtoMessage() {}
 
 func (x *DebugCreateMachineRequest) ProtoReflect() protoreflect.Message {
-	mi := &file_headscale_v1_machine_proto_msgTypes[11]
+	mi := &file_headscale_v1_machine_proto_msgTypes[13]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -725,7 +835,7 @@ func (x *DebugCreateMachineRequest) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use DebugCreateMachineRequest.ProtoReflect.Descriptor instead.
 func (*DebugCreateMachineRequest) Descriptor() ([]byte, []int) {
-	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{11}
+	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{13}
 }
 
 func (x *DebugCreateMachineRequest) GetNamespace() string {
@@ -767,7 +877,7 @@ type DebugCreateMachineResponse struct {
 func (x *DebugCreateMachineResponse) Reset() {
 	*x = DebugCreateMachineResponse{}
 	if protoimpl.UnsafeEnabled {
-		mi := &file_headscale_v1_machine_proto_msgTypes[12]
+		mi := &file_headscale_v1_machine_proto_msgTypes[14]
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		ms.StoreMessageInfo(mi)
 	}
@@ -780,7 +890,7 @@ func (x *DebugCreateMachineResponse) String() string {
 func (*DebugCreateMachineResponse) ProtoMessage() {}
 
 func (x *DebugCreateMachineResponse) ProtoReflect() protoreflect.Message {
-	mi := &file_headscale_v1_machine_proto_msgTypes[12]
+	mi := &file_headscale_v1_machine_proto_msgTypes[14]
 	if protoimpl.UnsafeEnabled && x != nil {
 		ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))
 		if ms.LoadMessageInfo() == nil {
@@ -793,7 +903,7 @@ func (x *DebugCreateMachineResponse) ProtoReflect() protoreflect.Message {
 
 // Deprecated: Use DebugCreateMachineResponse.ProtoReflect.Descriptor instead.
 func (*DebugCreateMachineResponse) Descriptor() ([]byte, []int) {
-	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{12}
+	return file_headscale_v1_machine_proto_rawDescGZIP(), []int{14}
 }
 
 func (x *DebugCreateMachineResponse) GetMachine() *Machine {
@@ -814,7 +924,7 @@ var file_headscale_v1_machine_proto_rawDesc = []byte{
 	0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70,
 	0x61, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1d, 0x68, 0x65, 0x61, 0x64, 0x73,
 	0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x65, 0x61, 0x75, 0x74, 0x68, 0x6b,
-	0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xdd, 0x04, 0x0a, 0x07, 0x4d, 0x61, 0x63,
+	0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf9, 0x04, 0x0a, 0x07, 0x4d, 0x61, 0x63,
 	0x68, 0x69, 0x6e, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
 	0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f,
 	0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69,
@@ -852,69 +962,81 @@ var file_headscale_v1_machine_proto_rawDesc = []byte{
 	0x74, 0x68, 0x6f, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x68, 0x65, 0x61,
 	0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74,
 	0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74,
-	0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x22, 0x48, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69,
-	0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
-	0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
-	0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
-	0x65, 0x79, 0x22, 0x4a, 0x0a, 0x17, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61,
-	0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a,
-	0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15,
-	0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61,
-	0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x32,
-	0x0a, 0x11, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75,
-	0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69,
-	0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
-	0x49, 0x64, 0x22, 0x45, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
-	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68,
-	0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64,
-	0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
-	0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x35, 0x0a, 0x14, 0x44, 0x65, 0x6c,
-	0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64,
-	0x22, 0x17, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
-	0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x0a, 0x14, 0x45, 0x78, 0x70,
-	0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18,
-	0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64,
-	0x22, 0x48, 0x0a, 0x15, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
+	0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x63, 0x6b,
+	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, 0x63, 0x6b,
+	0x6e, 0x61, 0x6d, 0x65, 0x22, 0x48, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72,
+	0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c,
+	0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03,
+	0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x4a,
+	0x0a, 0x17, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
 	0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63,
 	0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61,
 	0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
-	0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x33, 0x0a, 0x13, 0x4c, 0x69,
-	0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
-	0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01,
-	0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22,
-	0x49, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52,
-	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69,
-	0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64,
-	0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
-	0x52, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x77, 0x0a, 0x19, 0x44, 0x65,
-	0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
-	0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73,
-	0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65,
-	0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18,
-	0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72,
-	0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75,
-	0x74, 0x65, 0x73, 0x22, 0x4d, 0x0a, 0x1a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61,
-	0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
+	0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x32, 0x0a, 0x11, 0x47, 0x65,
+	0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
+	0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22, 0x45,
+	0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70,
+	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c,
+	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x07, 0x6d, 0x61,
+	0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x35, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d,
+	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a,
+	0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22, 0x17, 0x0a, 0x15,
+	0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x0a, 0x14, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4d,
+	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a,
+	0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
+	0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22, 0x48, 0x0a, 0x15,
+	0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73,
+	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61,
+	0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x07, 0x6d,
+	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x50, 0x0a, 0x14, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65,
+	0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d,
+	0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x12, 0x19, 0x0a,
+	0x08, 0x6e, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x07, 0x6e, 0x65, 0x77, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x48, 0x0a, 0x15, 0x52, 0x65, 0x6e, 0x61,
+	0x6d, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
 	0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01,
 	0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76,
 	0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69,
-	0x6e, 0x65, 0x2a, 0x82, 0x01, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d,
-	0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45,
-	0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49,
-	0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54,
-	0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x5f, 0x4b,
-	0x45, 0x59, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52,
-	0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x43, 0x4c, 0x49, 0x10, 0x02, 0x12, 0x18, 0x0a,
-	0x14, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44,
-	0x5f, 0x4f, 0x49, 0x44, 0x43, 0x10, 0x03, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75,
-	0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68,
-	0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f,
-	0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x6e, 0x65, 0x22, 0x33, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
+	0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d,
+	0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61,
+	0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x49, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4d,
+	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
+	0x31, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
+	0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31,
+	0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e,
+	0x65, 0x73, 0x22, 0x77, 0x0a, 0x19, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, 0x74,
+	0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
+	0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a,
+	0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
+	0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
+	0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20,
+	0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x22, 0x4d, 0x0a, 0x1a, 0x44,
+	0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
+	0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63,
+	0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61,
+	0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
+	0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2a, 0x82, 0x01, 0x0a, 0x0e, 0x52,
+	0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1f, 0x0a,
+	0x1b, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44,
+	0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c,
+	0x0a, 0x18, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f,
+	0x44, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13,
+	0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f,
+	0x43, 0x4c, 0x49, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45,
+	0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x4f, 0x49, 0x44, 0x43, 0x10, 0x03, 0x42,
+	0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75,
+	0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65,
+	0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
+	0x6f, 0x33,
 }
 
 var (
@@ -930,7 +1052,7 @@ func file_headscale_v1_machine_proto_rawDescGZIP() []byte {
 }
 
 var file_headscale_v1_machine_proto_enumTypes = make([]protoimpl.EnumInfo, 1)
-var file_headscale_v1_machine_proto_msgTypes = make([]protoimpl.MessageInfo, 13)
+var file_headscale_v1_machine_proto_msgTypes = make([]protoimpl.MessageInfo, 15)
 var file_headscale_v1_machine_proto_goTypes = []interface{}{
 	(RegisterMethod)(0),                // 0: headscale.v1.RegisterMethod
 	(*Machine)(nil),                    // 1: headscale.v1.Machine
@@ -942,32 +1064,35 @@ var file_headscale_v1_machine_proto_goTypes = []interface{}{
 	(*DeleteMachineResponse)(nil),      // 7: headscale.v1.DeleteMachineResponse
 	(*ExpireMachineRequest)(nil),       // 8: headscale.v1.ExpireMachineRequest
 	(*ExpireMachineResponse)(nil),      // 9: headscale.v1.ExpireMachineResponse
-	(*ListMachinesRequest)(nil),        // 10: headscale.v1.ListMachinesRequest
-	(*ListMachinesResponse)(nil),       // 11: headscale.v1.ListMachinesResponse
-	(*DebugCreateMachineRequest)(nil),  // 12: headscale.v1.DebugCreateMachineRequest
-	(*DebugCreateMachineResponse)(nil), // 13: headscale.v1.DebugCreateMachineResponse
-	(*Namespace)(nil),                  // 14: headscale.v1.Namespace
-	(*timestamppb.Timestamp)(nil),      // 15: google.protobuf.Timestamp
-	(*PreAuthKey)(nil),                 // 16: headscale.v1.PreAuthKey
+	(*RenameMachineRequest)(nil),       // 10: headscale.v1.RenameMachineRequest
+	(*RenameMachineResponse)(nil),      // 11: headscale.v1.RenameMachineResponse
+	(*ListMachinesRequest)(nil),        // 12: headscale.v1.ListMachinesRequest
+	(*ListMachinesResponse)(nil),       // 13: headscale.v1.ListMachinesResponse
+	(*DebugCreateMachineRequest)(nil),  // 14: headscale.v1.DebugCreateMachineRequest
+	(*DebugCreateMachineResponse)(nil), // 15: headscale.v1.DebugCreateMachineResponse
+	(*Namespace)(nil),                  // 16: headscale.v1.Namespace
+	(*timestamppb.Timestamp)(nil),      // 17: google.protobuf.Timestamp
+	(*PreAuthKey)(nil),                 // 18: headscale.v1.PreAuthKey
 }
 var file_headscale_v1_machine_proto_depIdxs = []int32{
-	14, // 0: headscale.v1.Machine.namespace:type_name -> headscale.v1.Namespace
-	15, // 1: headscale.v1.Machine.last_seen:type_name -> google.protobuf.Timestamp
-	15, // 2: headscale.v1.Machine.last_successful_update:type_name -> google.protobuf.Timestamp
-	15, // 3: headscale.v1.Machine.expiry:type_name -> google.protobuf.Timestamp
-	16, // 4: headscale.v1.Machine.pre_auth_key:type_name -> headscale.v1.PreAuthKey
-	15, // 5: headscale.v1.Machine.created_at:type_name -> google.protobuf.Timestamp
+	16, // 0: headscale.v1.Machine.namespace:type_name -> headscale.v1.Namespace
+	17, // 1: headscale.v1.Machine.last_seen:type_name -> google.protobuf.Timestamp
+	17, // 2: headscale.v1.Machine.last_successful_update:type_name -> google.protobuf.Timestamp
+	17, // 3: headscale.v1.Machine.expiry:type_name -> google.protobuf.Timestamp
+	18, // 4: headscale.v1.Machine.pre_auth_key:type_name -> headscale.v1.PreAuthKey
+	17, // 5: headscale.v1.Machine.created_at:type_name -> google.protobuf.Timestamp
 	0,  // 6: headscale.v1.Machine.register_method:type_name -> headscale.v1.RegisterMethod
 	1,  // 7: headscale.v1.RegisterMachineResponse.machine:type_name -> headscale.v1.Machine
 	1,  // 8: headscale.v1.GetMachineResponse.machine:type_name -> headscale.v1.Machine
 	1,  // 9: headscale.v1.ExpireMachineResponse.machine:type_name -> headscale.v1.Machine
-	1,  // 10: headscale.v1.ListMachinesResponse.machines:type_name -> headscale.v1.Machine
-	1,  // 11: headscale.v1.DebugCreateMachineResponse.machine:type_name -> headscale.v1.Machine
-	12, // [12:12] is the sub-list for method output_type
-	12, // [12:12] is the sub-list for method input_type
-	12, // [12:12] is the sub-list for extension type_name
-	12, // [12:12] is the sub-list for extension extendee
-	0,  // [0:12] is the sub-list for field type_name
+	1,  // 10: headscale.v1.RenameMachineResponse.machine:type_name -> headscale.v1.Machine
+	1,  // 11: headscale.v1.ListMachinesResponse.machines:type_name -> headscale.v1.Machine
+	1,  // 12: headscale.v1.DebugCreateMachineResponse.machine:type_name -> headscale.v1.Machine
+	13, // [13:13] is the sub-list for method output_type
+	13, // [13:13] is the sub-list for method input_type
+	13, // [13:13] is the sub-list for extension type_name
+	13, // [13:13] is the sub-list for extension extendee
+	0,  // [0:13] is the sub-list for field type_name
 }
 
 func init() { file_headscale_v1_machine_proto_init() }
@@ -1087,7 +1212,7 @@ func file_headscale_v1_machine_proto_init() {
 			}
 		}
 		file_headscale_v1_machine_proto_msgTypes[9].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ListMachinesRequest); i {
+			switch v := v.(*RenameMachineRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1099,7 +1224,7 @@ func file_headscale_v1_machine_proto_init() {
 			}
 		}
 		file_headscale_v1_machine_proto_msgTypes[10].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*ListMachinesResponse); i {
+			switch v := v.(*RenameMachineResponse); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1111,7 +1236,7 @@ func file_headscale_v1_machine_proto_init() {
 			}
 		}
 		file_headscale_v1_machine_proto_msgTypes[11].Exporter = func(v interface{}, i int) interface{} {
-			switch v := v.(*DebugCreateMachineRequest); i {
+			switch v := v.(*ListMachinesRequest); i {
 			case 0:
 				return &v.state
 			case 1:
@@ -1123,6 +1248,30 @@ func file_headscale_v1_machine_proto_init() {
 			}
 		}
 		file_headscale_v1_machine_proto_msgTypes[12].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*ListMachinesResponse); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_headscale_v1_machine_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} {
+			switch v := v.(*DebugCreateMachineRequest); i {
+			case 0:
+				return &v.state
+			case 1:
+				return &v.sizeCache
+			case 2:
+				return &v.unknownFields
+			default:
+				return nil
+			}
+		}
+		file_headscale_v1_machine_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} {
 			switch v := v.(*DebugCreateMachineResponse); i {
 			case 0:
 				return &v.state
@@ -1141,7 +1290,7 @@ func file_headscale_v1_machine_proto_init() {
 			GoPackagePath: reflect.TypeOf(x{}).PkgPath(),
 			RawDescriptor: file_headscale_v1_machine_proto_rawDesc,
 			NumEnums:      1,
-			NumMessages:   13,
+			NumMessages:   15,
 			NumExtensions: 0,
 			NumServices:   0,
 		},
diff --git a/gen/openapiv2/headscale/v1/headscale.swagger.json b/gen/openapiv2/headscale/v1/headscale.swagger.json
index 8d808f9..0ee446f 100644
--- a/gen/openapiv2/headscale/v1/headscale.swagger.json
+++ b/gen/openapiv2/headscale/v1/headscale.swagger.json
@@ -291,6 +291,43 @@
         ]
       }
     },
+    "/api/v1/machine/{machineId}/rename/{newName}": {
+      "post": {
+        "operationId": "HeadscaleService_RenameMachine",
+        "responses": {
+          "200": {
+            "description": "A successful response.",
+            "schema": {
+              "$ref": "#/definitions/v1RenameMachineResponse"
+            }
+          },
+          "default": {
+            "description": "An unexpected error response.",
+            "schema": {
+              "$ref": "#/definitions/rpcStatus"
+            }
+          }
+        },
+        "parameters": [
+          {
+            "name": "machineId",
+            "in": "path",
+            "required": true,
+            "type": "string",
+            "format": "uint64"
+          },
+          {
+            "name": "newName",
+            "in": "path",
+            "required": true,
+            "type": "string"
+          }
+        ],
+        "tags": [
+          "HeadscaleService"
+        ]
+      }
+    },
     "/api/v1/machine/{machineId}/routes": {
       "get": {
         "summary": "--- Route start ---",
@@ -906,6 +943,9 @@
         },
         "registerMethod": {
           "$ref": "#/definitions/v1RegisterMethod"
+        },
+        "nickname": {
+          "type": "string"
         }
       }
     },
@@ -973,6 +1013,14 @@
       ],
       "default": "REGISTER_METHOD_UNSPECIFIED"
     },
+    "v1RenameMachineResponse": {
+      "type": "object",
+      "properties": {
+        "machine": {
+          "$ref": "#/definitions/v1Machine"
+        }
+      }
+    },
     "v1RenameNamespaceResponse": {
       "type": "object",
       "properties": {
diff --git a/grpcv1.go b/grpcv1.go
index 647e599..68303be 100644
--- a/grpcv1.go
+++ b/grpcv1.go
@@ -222,6 +222,28 @@ func (api headscaleV1APIServer) ExpireMachine(
 	return &v1.ExpireMachineResponse{Machine: machine.toProto()}, nil
 }
 
+func (api headscaleV1APIServer) RenameMachine(
+	ctx context.Context,
+	request *v1.RenameMachineRequest,
+) (*v1.RenameMachineResponse, error) {
+	machine, err := api.h.GetMachineByID(request.GetMachineId())
+	if err != nil {
+		return nil, err
+	}
+
+	api.h.RenameMachine(
+		machine,
+		request.GetNewName(),
+	)
+
+	log.Trace().
+		Str("machine", machine.Name).
+		Time("expiry", *machine.Expiry).
+		Msg("machine expired")
+
+	return &v1.RenameMachineResponse{Machine: machine.toProto()}, nil
+}
+
 func (api headscaleV1APIServer) ListMachines(
 	ctx context.Context,
 	request *v1.ListMachinesRequest,
diff --git a/machine.go b/machine.go
index a637f54..b2581ab 100644
--- a/machine.go
+++ b/machine.go
@@ -40,6 +40,7 @@ type Machine struct {
 	DiscoKey    string
 	IPAddresses MachineAddresses
 	Name        string
+	Nickname    string
 	NamespaceID uint
 	Namespace   Namespace `gorm:"foreignKey:NamespaceID"`
 
@@ -367,6 +368,15 @@ func (h *Headscale) ExpireMachine(machine *Machine) {
 	h.db.Save(machine)
 }
 
+// RenameMachine takes a Machine struct and sets the expire field to now.
+func (h *Headscale) RenameMachine(machine *Machine, newName string) {
+	machine.Nickname = newName
+
+	h.setLastStateChangeToNow(machine.Namespace.Name)
+
+	h.db.Save(machine)
+}
+
 // RefreshMachine takes a Machine struct and sets the expire field to now.
 func (h *Headscale) RefreshMachine(machine *Machine, expiry time.Time) {
 	now := time.Now()
@@ -554,11 +564,15 @@ func (machine Machine) toNode(
 		keyExpiry = time.Time{}
 	}
 
+	name := machine.Name
+	if len(machine.Nickname) > 0 {
+		name = machine.Nickname
+	}
 	var hostname string
 	if dnsConfig != nil && dnsConfig.Proxied { // MagicDNS
 		hostname = fmt.Sprintf(
 			"%s.%s.%s",
-			machine.Name,
+			name,
 			machine.Namespace.Name,
 			baseDomain,
 		)
@@ -612,6 +626,7 @@ func (machine *Machine) toProto() *v1.Machine {
 		DiscoKey:    machine.DiscoKey,
 		IpAddresses: machine.IPAddresses.ToStringSlice(),
 		Name:        machine.Name,
+		Nickname:    machine.Nickname,
 		Namespace:   machine.Namespace.toProto(),
 
 		// TODO(kradalby): Implement register method enum converter
diff --git a/proto/headscale/v1/headscale.proto b/proto/headscale/v1/headscale.proto
index d6571b0..fa74249 100644
--- a/proto/headscale/v1/headscale.proto
+++ b/proto/headscale/v1/headscale.proto
@@ -99,6 +99,12 @@ service HeadscaleService {
         };
     }
 
+    rpc RenameMachine(RenameMachineRequest) returns (RenameMachineResponse) {
+        option (google.api.http) = {
+            post: "/api/v1/machine/{machine_id}/rename/{new_name}"
+        };
+    }
+
     rpc ListMachines(ListMachinesRequest) returns (ListMachinesResponse) {
         option (google.api.http) = {
             get: "/api/v1/machine"
diff --git a/proto/headscale/v1/machine.proto b/proto/headscale/v1/machine.proto
index 75552d7..42fab53 100644
--- a/proto/headscale/v1/machine.proto
+++ b/proto/headscale/v1/machine.proto
@@ -38,6 +38,8 @@ message Machine {
     // bytes host_info      = 15;
     // bytes endpoints      = 16;
     // bytes enabled_routes = 17;
+
+    string          nickname     = 18;
 }
 
 message RegisterMachineRequest {
@@ -72,6 +74,15 @@ message ExpireMachineResponse {
     Machine machine = 1;
 }
 
+message RenameMachineRequest {
+    uint64 machine_id = 1;
+    string new_name = 2;
+}
+
+message RenameMachineResponse {
+    Machine machine = 1;
+}
+
 message ListMachinesRequest {
     string namespace = 1;
 }

From 9901d6b2e74f563fb3edaf86249f756c148588fd Mon Sep 17 00:00:00 2001
From: bravechamp <radular@protonmail.com>
Date: Sun, 13 Mar 2022 21:10:41 +0000
Subject: [PATCH 02/37] Ability to clear nickname

---
 cmd/headscale/cli/nodes.go | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go
index f0c4ede..868062e 100644
--- a/cmd/headscale/cli/nodes.go
+++ b/cmd/headscale/cli/nodes.go
@@ -235,9 +235,13 @@ var renameNodeCmd = &cobra.Command{
 		defer cancel()
 		defer conn.Close()
 
+		newName := ""
+		if len(args) > 0 {
+			newName = args[0]
+		}
 		request := &v1.RenameMachineRequest{
 			MachineId: identifier,
-			NewName: args[0],
+			NewName: newName,
 		}
 
 		response, err := client.RenameMachine(ctx, request)

From 60ee04674d4252bf28015e5091458956b5253081 Mon Sep 17 00:00:00 2001
From: bravechamp <radular@protonmail.com>
Date: Sun, 13 Mar 2022 21:55:36 +0000
Subject: [PATCH 03/37] Normalize nickname before saving to database

---
 machine.go | 16 +++++++++++++++-
 1 file changed, 15 insertions(+), 1 deletion(-)

diff --git a/machine.go b/machine.go
index b2581ab..e0b9130 100644
--- a/machine.go
+++ b/machine.go
@@ -370,7 +370,21 @@ func (h *Headscale) ExpireMachine(machine *Machine) {
 
 // RenameMachine takes a Machine struct and sets the expire field to now.
 func (h *Headscale) RenameMachine(machine *Machine, newName string) {
-	machine.Nickname = newName
+	newNickname, err := NormalizeToFQDNRules(
+		newName,
+		h.cfg.OIDC.StripEmaildomain,
+	)
+	if err != nil {
+		log.Error().
+			Caller().
+			Str("func", "RenameMachine").
+			Str("machine", machine.Name).
+			Str("newName", newName).
+			Err(err)
+
+		return
+	}
+	machine.Nickname = newNickname
 
 	h.setLastStateChangeToNow(machine.Namespace.Name)
 

From 6e2768097a620fe7d43049e49f6d0fd6bc40fc8d Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Sun, 24 Apr 2022 20:54:38 +0100
Subject: [PATCH 04/37] Rename name -> hostname, nickname -> givenname

---
 acls_test.go         | 14 ++++-----
 api.go               | 17 ++++++-----
 app.go               |  4 +--
 dns_test.go          | 28 +++++++++---------
 machine.go           | 69 ++++++++++++++++++++++++--------------------
 machine_test.go      | 36 +++++++++++------------
 namespaces_test.go   | 16 +++++-----
 preauth_keys_test.go |  6 ++--
 routes_test.go       |  4 +--
 utils_test.go        |  6 ++--
 10 files changed, 104 insertions(+), 96 deletions(-)

diff --git a/acls_test.go b/acls_test.go
index 9dcc40b..4c63ca7 100644
--- a/acls_test.go
+++ b/acls_test.go
@@ -118,7 +118,7 @@ func (s *Suite) TestValidExpandTagOwnersInUsers(c *check.C) {
 		MachineKey:     "foo",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "testmachine",
+		Hostname:       "testmachine",
 		IPAddresses:    MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
@@ -164,7 +164,7 @@ func (s *Suite) TestValidExpandTagOwnersInPorts(c *check.C) {
 		MachineKey:     "12345",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "testmachine",
+		Hostname:       "testmachine",
 		IPAddresses:    MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
@@ -210,7 +210,7 @@ func (s *Suite) TestInvalidTagValidNamespace(c *check.C) {
 		MachineKey:     "12345",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "testmachine",
+		Hostname:       "testmachine",
 		IPAddresses:    MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
@@ -255,7 +255,7 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) {
 		MachineKey:     "12345",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "webserver",
+		Hostname:       "webserver",
 		IPAddresses:    MachineAddresses{netaddr.MustParseIP("100.64.0.1")},
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
@@ -274,7 +274,7 @@ func (s *Suite) TestValidTagInvalidNamespace(c *check.C) {
 		MachineKey:     "56789",
 		NodeKey:        "bar2",
 		DiscoKey:       "faab",
-		Name:           "user",
+		Hostname:       "user",
 		IPAddresses:    MachineAddresses{netaddr.MustParseIP("100.64.0.2")},
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
@@ -368,7 +368,7 @@ func (s *Suite) TestPortNamespace(c *check.C) {
 		MachineKey:     "12345",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "testmachine",
+		Hostname:       "testmachine",
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
 		IPAddresses:    ips,
@@ -410,7 +410,7 @@ func (s *Suite) TestPortGroup(c *check.C) {
 		MachineKey:     "foo",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "testmachine",
+		Hostname:       "testmachine",
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
 		IPAddresses:    ips,
diff --git a/api.go b/api.go
index 61ec1b5..dcdf49a 100644
--- a/api.go
+++ b/api.go
@@ -133,7 +133,7 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) {
 
 			return
 		}
-		hname, err := NormalizeToFQDNRules(
+		normalizedHostname, err := NormalizeToFQDNRules(
 			req.Hostinfo.Hostname,
 			h.cfg.OIDC.StripEmaildomain,
 		)
@@ -153,7 +153,8 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) {
 		// happens
 		newMachine := Machine{
 			MachineKey: machineKeyStr,
-			Name:       hname,
+			Hostname:   req.Hostinfo.Hostname,
+			GivenName:  normalizedHostname,
 			NodeKey:    NodePublicKeyStripPrefix(req.NodeKey),
 			LastSeen:   &now,
 			Expiry:     &time.Time{},
@@ -361,7 +362,7 @@ func (h *Headscale) handleMachineLogOut(
 	resp := tailcfg.RegisterResponse{}
 
 	log.Info().
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Msg("Client requested logout")
 
 	h.ExpireMachine(&machine)
@@ -391,7 +392,7 @@ func (h *Headscale) handleMachineValidRegistration(
 
 	// The machine registration is valid, respond with redirect to /map
 	log.Debug().
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Msg("Client is registered and we have the current NodeKey. All clear to /map")
 
 	resp.AuthURL = ""
@@ -426,7 +427,7 @@ func (h *Headscale) handleMachineExpired(
 
 	// The client has registered before, but has expired
 	log.Debug().
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Msg("Machine registration has expired. Sending a authurl to register")
 
 	if registerRequest.Auth.AuthKey != "" {
@@ -469,7 +470,7 @@ func (h *Headscale) handleMachineRefreshKey(
 	resp := tailcfg.RegisterResponse{}
 
 	log.Debug().
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Msg("We have the OldNodeKey in the database. This is a key refresh")
 	machine.NodeKey = NodePublicKeyStripPrefix(registerRequest.NodeKey)
 	h.db.Save(&machine)
@@ -594,7 +595,7 @@ func (h *Headscale) handleAuthKey(
 	if machine != nil {
 		log.Trace().
 			Caller().
-			Str("machine", machine.Name).
+			Str("machine", machine.Hostname).
 			Msg("machine already registered, refreshing with new auth key")
 
 		machine.NodeKey = nodeKey
@@ -603,7 +604,7 @@ func (h *Headscale) handleAuthKey(
 	} else {
 		now := time.Now().UTC()
 		machineToRegister := Machine{
-			Name:           registerRequest.Hostinfo.Hostname,
+			Hostname:       registerRequest.Hostinfo.Hostname,
 			NamespaceID:    pak.Namespace.ID,
 			MachineKey:     machineKeyStr,
 			RegisterMethod: RegisterMethodAuthKey,
diff --git a/app.go b/app.go
index b87fb33..162ba98 100644
--- a/app.go
+++ b/app.go
@@ -298,14 +298,14 @@ func (h *Headscale) expireEphemeralNodesWorker() {
 				time.Now().
 					After(machine.LastSeen.Add(h.cfg.EphemeralNodeInactivityTimeout)) {
 				log.Info().
-					Str("machine", machine.Name).
+					Str("machine", machine.Hostname).
 					Msg("Ephemeral client removed from database")
 
 				err = h.db.Unscoped().Delete(machine).Error
 				if err != nil {
 					log.Error().
 						Err(err).
-						Str("machine", machine.Name).
+						Str("machine", machine.Hostname).
 						Msg("🤮 Cannot delete ephemeral machine from the database")
 				}
 			}
diff --git a/dns_test.go b/dns_test.go
index 5fd30d3..9df75b1 100644
--- a/dns_test.go
+++ b/dns_test.go
@@ -161,7 +161,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
 		MachineKey:     "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
 		NodeKey:        "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
 		DiscoKey:       "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
-		Name:           "test_get_shared_nodes_1",
+		Hostname:           "test_get_shared_nodes_1",
 		NamespaceID:    namespaceShared1.ID,
 		Namespace:      *namespaceShared1,
 		RegisterMethod: RegisterMethodAuthKey,
@@ -170,7 +170,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
 	}
 	app.db.Save(machineInShared1)
 
-	_, err = app.GetMachine(namespaceShared1.Name, machineInShared1.Name)
+	_, err = app.GetMachine(namespaceShared1.Name, machineInShared1.Hostname)
 	c.Assert(err, check.IsNil)
 
 	machineInShared2 := &Machine{
@@ -178,7 +178,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
 		MachineKey:     "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
-		Name:           "test_get_shared_nodes_2",
+		Hostname:           "test_get_shared_nodes_2",
 		NamespaceID:    namespaceShared2.ID,
 		Namespace:      *namespaceShared2,
 		RegisterMethod: RegisterMethodAuthKey,
@@ -187,7 +187,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
 	}
 	app.db.Save(machineInShared2)
 
-	_, err = app.GetMachine(namespaceShared2.Name, machineInShared2.Name)
+	_, err = app.GetMachine(namespaceShared2.Name, machineInShared2.Hostname)
 	c.Assert(err, check.IsNil)
 
 	machineInShared3 := &Machine{
@@ -195,7 +195,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
 		MachineKey:     "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
-		Name:           "test_get_shared_nodes_3",
+		Hostname:           "test_get_shared_nodes_3",
 		NamespaceID:    namespaceShared3.ID,
 		Namespace:      *namespaceShared3,
 		RegisterMethod: RegisterMethodAuthKey,
@@ -204,7 +204,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
 	}
 	app.db.Save(machineInShared3)
 
-	_, err = app.GetMachine(namespaceShared3.Name, machineInShared3.Name)
+	_, err = app.GetMachine(namespaceShared3.Name, machineInShared3.Hostname)
 	c.Assert(err, check.IsNil)
 
 	machine2InShared1 := &Machine{
@@ -212,7 +212,7 @@ func (s *Suite) TestDNSConfigMapResponseWithMagicDNS(c *check.C) {
 		MachineKey:     "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
-		Name:           "test_get_shared_nodes_4",
+		Hostname:           "test_get_shared_nodes_4",
 		NamespaceID:    namespaceShared1.ID,
 		Namespace:      *namespaceShared1,
 		RegisterMethod: RegisterMethodAuthKey,
@@ -304,7 +304,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
 		MachineKey:     "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
 		NodeKey:        "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
 		DiscoKey:       "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
-		Name:           "test_get_shared_nodes_1",
+		Hostname:           "test_get_shared_nodes_1",
 		NamespaceID:    namespaceShared1.ID,
 		Namespace:      *namespaceShared1,
 		RegisterMethod: RegisterMethodAuthKey,
@@ -313,7 +313,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
 	}
 	app.db.Save(machineInShared1)
 
-	_, err = app.GetMachine(namespaceShared1.Name, machineInShared1.Name)
+	_, err = app.GetMachine(namespaceShared1.Name, machineInShared1.Hostname)
 	c.Assert(err, check.IsNil)
 
 	machineInShared2 := &Machine{
@@ -321,7 +321,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
 		MachineKey:     "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
-		Name:           "test_get_shared_nodes_2",
+		Hostname:           "test_get_shared_nodes_2",
 		NamespaceID:    namespaceShared2.ID,
 		Namespace:      *namespaceShared2,
 		RegisterMethod: RegisterMethodAuthKey,
@@ -330,7 +330,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
 	}
 	app.db.Save(machineInShared2)
 
-	_, err = app.GetMachine(namespaceShared2.Name, machineInShared2.Name)
+	_, err = app.GetMachine(namespaceShared2.Name, machineInShared2.Hostname)
 	c.Assert(err, check.IsNil)
 
 	machineInShared3 := &Machine{
@@ -338,7 +338,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
 		MachineKey:     "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
-		Name:           "test_get_shared_nodes_3",
+		Hostname:           "test_get_shared_nodes_3",
 		NamespaceID:    namespaceShared3.ID,
 		Namespace:      *namespaceShared3,
 		RegisterMethod: RegisterMethodAuthKey,
@@ -347,7 +347,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
 	}
 	app.db.Save(machineInShared3)
 
-	_, err = app.GetMachine(namespaceShared3.Name, machineInShared3.Name)
+	_, err = app.GetMachine(namespaceShared3.Name, machineInShared3.Hostname)
 	c.Assert(err, check.IsNil)
 
 	machine2InShared1 := &Machine{
@@ -355,7 +355,7 @@ func (s *Suite) TestDNSConfigMapResponseWithoutMagicDNS(c *check.C) {
 		MachineKey:     "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
-		Name:           "test_get_shared_nodes_4",
+		Hostname:           "test_get_shared_nodes_4",
 		NamespaceID:    namespaceShared1.ID,
 		Namespace:      *namespaceShared1,
 		RegisterMethod: RegisterMethodAuthKey,
diff --git a/machine.go b/machine.go
index e0b9130..b1f03f0 100644
--- a/machine.go
+++ b/machine.go
@@ -39,8 +39,18 @@ type Machine struct {
 	NodeKey     string
 	DiscoKey    string
 	IPAddresses MachineAddresses
-	Name        string
-	Nickname    string
+
+	// Hostname represents the name given by the Tailscale
+	// client during registration
+	Hostname string
+
+	// Givenname represents either:
+	// a DNS normalized version of Hostname
+	// a valid name set by the User
+	//
+	// GivenName is the name used in all DNS related
+	// parts of headscale.
+	GivenName   string
 	NamespaceID uint
 	Namespace   Namespace `gorm:"foreignKey:NamespaceID"`
 
@@ -150,7 +160,7 @@ func getFilteredByACLPeers(
 ) Machines {
 	log.Trace().
 		Caller().
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Msg("Finding peers filtered by ACLs")
 
 	peers := make(map[uint64]Machine)
@@ -217,7 +227,7 @@ func getFilteredByACLPeers(
 
 	log.Trace().
 		Caller().
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Msgf("Found some machines: %v", machines)
 
 	return authorizedPeers
@@ -226,7 +236,7 @@ func getFilteredByACLPeers(
 func (h *Headscale) ListPeers(machine *Machine) (Machines, error) {
 	log.Trace().
 		Caller().
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Msg("Finding direct peers")
 
 	machines := Machines{}
@@ -241,7 +251,7 @@ func (h *Headscale) ListPeers(machine *Machine) (Machines, error) {
 
 	log.Trace().
 		Caller().
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Msgf("Found peers: %s", machines.String())
 
 	return machines, nil
@@ -278,7 +288,7 @@ func (h *Headscale) getPeers(machine *Machine) (Machines, error) {
 
 	log.Trace().
 		Caller().
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Msgf("Found total peers: %s", peers.String())
 
 	return peers, nil
@@ -318,7 +328,7 @@ func (h *Headscale) GetMachine(namespace string, name string) (*Machine, error)
 	}
 
 	for _, m := range machines {
-		if m.Name == name {
+		if m.Hostname == name {
 			return &m, nil
 		}
 	}
@@ -369,26 +379,27 @@ func (h *Headscale) ExpireMachine(machine *Machine) {
 }
 
 // RenameMachine takes a Machine struct and sets the expire field to now.
-func (h *Headscale) RenameMachine(machine *Machine, newName string) {
-	newNickname, err := NormalizeToFQDNRules(
+func (h *Headscale) RenameMachine(machine *Machine, newName string) error {
+	err := CheckForFQDNRules(
 		newName,
-		h.cfg.OIDC.StripEmaildomain,
 	)
 	if err != nil {
 		log.Error().
 			Caller().
 			Str("func", "RenameMachine").
-			Str("machine", machine.Name).
+			Str("machine", machine.Hostname).
 			Str("newName", newName).
 			Err(err)
 
-		return
+		return err
 	}
-	machine.Nickname = newNickname
+	machine.GivenName = newName
 
 	h.setLastStateChangeToNow(machine.Namespace.Name)
 
 	h.db.Save(machine)
+
+	return nil
 }
 
 // RefreshMachine takes a Machine struct and sets the expire field to now.
@@ -458,23 +469,23 @@ func (h *Headscale) isOutdated(machine *Machine) bool {
 	}
 	log.Trace().
 		Caller().
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Time("last_successful_update", lastChange).
 		Time("last_state_change", lastUpdate).
-		Msgf("Checking if %s is missing updates", machine.Name)
+		Msgf("Checking if %s is missing updates", machine.Hostname)
 
 	return lastUpdate.Before(lastChange)
 }
 
 func (machine Machine) String() string {
-	return machine.Name
+	return machine.Hostname
 }
 
 func (machines Machines) String() string {
 	temp := make([]string, len(machines))
 
 	for index, machine := range machines {
-		temp[index] = machine.Name
+		temp[index] = machine.Hostname
 	}
 
 	return fmt.Sprintf("[ %s ](%d)", strings.Join(temp, ", "), len(temp))
@@ -485,7 +496,7 @@ func (machines MachinesP) String() string {
 	temp := make([]string, len(machines))
 
 	for index, machine := range machines {
-		temp[index] = machine.Name
+		temp[index] = machine.Hostname
 	}
 
 	return fmt.Sprintf("[ %s ](%d)", strings.Join(temp, ", "), len(temp))
@@ -578,15 +589,11 @@ func (machine Machine) toNode(
 		keyExpiry = time.Time{}
 	}
 
-	name := machine.Name
-	if len(machine.Nickname) > 0 {
-		name = machine.Nickname
-	}
 	var hostname string
 	if dnsConfig != nil && dnsConfig.Proxied { // MagicDNS
 		hostname = fmt.Sprintf(
 			"%s.%s.%s",
-			name,
+			machine.GivenName,
 			machine.Namespace.Name,
 			baseDomain,
 		)
@@ -598,7 +605,7 @@ func (machine Machine) toNode(
 			)
 		}
 	} else {
-		hostname = machine.Name
+		hostname = machine.GivenName
 	}
 
 	hostInfo := machine.GetHostInfo()
@@ -639,8 +646,8 @@ func (machine *Machine) toProto() *v1.Machine {
 		NodeKey:     machine.NodeKey,
 		DiscoKey:    machine.DiscoKey,
 		IpAddresses: machine.IPAddresses.ToStringSlice(),
-		Name:        machine.Name,
-		Nickname:    machine.Nickname,
+		Name:        machine.Hostname,
+		GivenName:   machine.GivenName,
 		Namespace:   machine.Namespace.toProto(),
 
 		// TODO(kradalby): Implement register method enum converter
@@ -711,7 +718,7 @@ func (h *Headscale) RegisterMachine(machine Machine,
 
 	log.Trace().
 		Caller().
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Msg("Attempting to register machine")
 
 	h.ipAllocationMutex.Lock()
@@ -722,7 +729,7 @@ func (h *Headscale) RegisterMachine(machine Machine,
 		log.Error().
 			Caller().
 			Err(err).
-			Str("machine", machine.Name).
+			Str("machine", machine.Hostname).
 			Msg("Could not find IP for the new machine")
 
 		return nil, err
@@ -734,7 +741,7 @@ func (h *Headscale) RegisterMachine(machine Machine,
 
 	log.Trace().
 		Caller().
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Str("ip", strings.Join(ips.ToStringSlice(), ",")).
 		Msg("Machine registered with the database")
 
@@ -783,7 +790,7 @@ func (h *Headscale) EnableRoutes(machine *Machine, routeStrs ...string) error {
 		if !containsIPPrefix(machine.GetAdvertisedRoutes(), newRoute) {
 			return fmt.Errorf(
 				"route (%s) is not available on node %s: %w",
-				machine.Name,
+				machine.Hostname,
 				newRoute, errMachineRouteIsNotAvailable,
 			)
 		}
diff --git a/machine_test.go b/machine_test.go
index a455a0b..02f2d0d 100644
--- a/machine_test.go
+++ b/machine_test.go
@@ -27,7 +27,7 @@ func (s *Suite) TestGetMachine(c *check.C) {
 		MachineKey:     "foo",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "testmachine",
+		Hostname:       "testmachine",
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
 		AuthKeyID:      uint(pak.ID),
@@ -53,7 +53,7 @@ func (s *Suite) TestGetMachineByID(c *check.C) {
 		MachineKey:     "foo",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "testmachine",
+		Hostname:       "testmachine",
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
 		AuthKeyID:      uint(pak.ID),
@@ -72,7 +72,7 @@ func (s *Suite) TestDeleteMachine(c *check.C) {
 		MachineKey:     "foo",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "testmachine",
+		Hostname:       "testmachine",
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
 		AuthKeyID:      uint(1),
@@ -94,7 +94,7 @@ func (s *Suite) TestHardDeleteMachine(c *check.C) {
 		MachineKey:     "foo",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "testmachine3",
+		Hostname:       "testmachine3",
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
 		AuthKeyID:      uint(1),
@@ -124,7 +124,7 @@ func (s *Suite) TestListPeers(c *check.C) {
 			MachineKey:     "foo" + strconv.Itoa(index),
 			NodeKey:        "bar" + strconv.Itoa(index),
 			DiscoKey:       "faa" + strconv.Itoa(index),
-			Name:           "testmachine" + strconv.Itoa(index),
+			Hostname:       "testmachine" + strconv.Itoa(index),
 			NamespaceID:    namespace.ID,
 			RegisterMethod: RegisterMethodAuthKey,
 			AuthKeyID:      uint(pak.ID),
@@ -139,9 +139,9 @@ func (s *Suite) TestListPeers(c *check.C) {
 	c.Assert(err, check.IsNil)
 
 	c.Assert(len(peersOfMachine0), check.Equals, 9)
-	c.Assert(peersOfMachine0[0].Name, check.Equals, "testmachine2")
-	c.Assert(peersOfMachine0[5].Name, check.Equals, "testmachine7")
-	c.Assert(peersOfMachine0[8].Name, check.Equals, "testmachine10")
+	c.Assert(peersOfMachine0[0].Hostname, check.Equals, "testmachine2")
+	c.Assert(peersOfMachine0[5].Hostname, check.Equals, "testmachine7")
+	c.Assert(peersOfMachine0[8].Hostname, check.Equals, "testmachine10")
 }
 
 func (s *Suite) TestGetACLFilteredPeers(c *check.C) {
@@ -172,7 +172,7 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) {
 			IPAddresses: MachineAddresses{
 				netaddr.MustParseIP(fmt.Sprintf("100.64.0.%v", strconv.Itoa(index+1))),
 			},
-			Name:           "testmachine" + strconv.Itoa(index),
+			Hostname:       "testmachine" + strconv.Itoa(index),
 			NamespaceID:    stor[index%2].namespace.ID,
 			RegisterMethod: RegisterMethodAuthKey,
 			AuthKeyID:      uint(stor[index%2].key.ID),
@@ -197,11 +197,11 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) {
 	c.Assert(err, check.IsNil)
 
 	adminMachine, err := app.GetMachineByID(1)
-	c.Logf("Machine(%v), namespace: %v", adminMachine.Name, adminMachine.Namespace)
+	c.Logf("Machine(%v), namespace: %v", adminMachine.Hostname, adminMachine.Namespace)
 	c.Assert(err, check.IsNil)
 
 	testMachine, err := app.GetMachineByID(2)
-	c.Logf("Machine(%v), namespace: %v", testMachine.Name, testMachine.Namespace)
+	c.Logf("Machine(%v), namespace: %v", testMachine.Hostname, testMachine.Namespace)
 	c.Assert(err, check.IsNil)
 
 	machines, err := app.ListMachines()
@@ -212,15 +212,15 @@ func (s *Suite) TestGetACLFilteredPeers(c *check.C) {
 
 	c.Log(peersOfTestMachine)
 	c.Assert(len(peersOfTestMachine), check.Equals, 4)
-	c.Assert(peersOfTestMachine[0].Name, check.Equals, "testmachine4")
-	c.Assert(peersOfTestMachine[1].Name, check.Equals, "testmachine6")
-	c.Assert(peersOfTestMachine[3].Name, check.Equals, "testmachine10")
+	c.Assert(peersOfTestMachine[0].Hostname, check.Equals, "testmachine4")
+	c.Assert(peersOfTestMachine[1].Hostname, check.Equals, "testmachine6")
+	c.Assert(peersOfTestMachine[3].Hostname, check.Equals, "testmachine10")
 
 	c.Log(peersOfAdminMachine)
 	c.Assert(len(peersOfAdminMachine), check.Equals, 9)
-	c.Assert(peersOfAdminMachine[0].Name, check.Equals, "testmachine2")
-	c.Assert(peersOfAdminMachine[2].Name, check.Equals, "testmachine4")
-	c.Assert(peersOfAdminMachine[5].Name, check.Equals, "testmachine7")
+	c.Assert(peersOfAdminMachine[0].Hostname, check.Equals, "testmachine2")
+	c.Assert(peersOfAdminMachine[2].Hostname, check.Equals, "testmachine4")
+	c.Assert(peersOfAdminMachine[5].Hostname, check.Equals, "testmachine7")
 }
 
 func (s *Suite) TestExpireMachine(c *check.C) {
@@ -238,7 +238,7 @@ func (s *Suite) TestExpireMachine(c *check.C) {
 		MachineKey:     "foo",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "testmachine",
+		Hostname:       "testmachine",
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
 		AuthKeyID:      uint(pak.ID),
diff --git a/namespaces_test.go b/namespaces_test.go
index 5d873bd..5368037 100644
--- a/namespaces_test.go
+++ b/namespaces_test.go
@@ -52,7 +52,7 @@ func (s *Suite) TestDestroyNamespaceErrors(c *check.C) {
 		MachineKey:     "foo",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "testmachine",
+		Hostname:           "testmachine",
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
 		AuthKeyID:      uint(pak.ID),
@@ -142,7 +142,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
 		MachineKey:     "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
 		NodeKey:        "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
 		DiscoKey:       "686824e749f3b7f2a5927ee6c1e422aee5292592d9179a271ed7b3e659b44a66",
-		Name:           "test_get_shared_nodes_1",
+		Hostname:           "test_get_shared_nodes_1",
 		NamespaceID:    namespaceShared1.ID,
 		Namespace:      *namespaceShared1,
 		RegisterMethod: RegisterMethodAuthKey,
@@ -151,7 +151,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
 	}
 	app.db.Save(machineInShared1)
 
-	_, err = app.GetMachine(namespaceShared1.Name, machineInShared1.Name)
+	_, err = app.GetMachine(namespaceShared1.Name, machineInShared1.Hostname)
 	c.Assert(err, check.IsNil)
 
 	machineInShared2 := &Machine{
@@ -159,7 +159,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
 		MachineKey:     "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
-		Name:           "test_get_shared_nodes_2",
+		Hostname:           "test_get_shared_nodes_2",
 		NamespaceID:    namespaceShared2.ID,
 		Namespace:      *namespaceShared2,
 		RegisterMethod: RegisterMethodAuthKey,
@@ -168,7 +168,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
 	}
 	app.db.Save(machineInShared2)
 
-	_, err = app.GetMachine(namespaceShared2.Name, machineInShared2.Name)
+	_, err = app.GetMachine(namespaceShared2.Name, machineInShared2.Hostname)
 	c.Assert(err, check.IsNil)
 
 	machineInShared3 := &Machine{
@@ -176,7 +176,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
 		MachineKey:     "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
-		Name:           "test_get_shared_nodes_3",
+		Hostname:           "test_get_shared_nodes_3",
 		NamespaceID:    namespaceShared3.ID,
 		Namespace:      *namespaceShared3,
 		RegisterMethod: RegisterMethodAuthKey,
@@ -185,7 +185,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
 	}
 	app.db.Save(machineInShared3)
 
-	_, err = app.GetMachine(namespaceShared3.Name, machineInShared3.Name)
+	_, err = app.GetMachine(namespaceShared3.Name, machineInShared3.Hostname)
 	c.Assert(err, check.IsNil)
 
 	machine2InShared1 := &Machine{
@@ -193,7 +193,7 @@ func (s *Suite) TestGetMapResponseUserProfiles(c *check.C) {
 		MachineKey:     "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		NodeKey:        "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
 		DiscoKey:       "dec46ef9dc45c7d2f03bfcd5a640d9e24e3cc68ce3d9da223867c9bc6d5e9863",
-		Name:           "test_get_shared_nodes_4",
+		Hostname:           "test_get_shared_nodes_4",
 		NamespaceID:    namespaceShared1.ID,
 		Namespace:      *namespaceShared1,
 		RegisterMethod: RegisterMethodAuthKey,
diff --git a/preauth_keys_test.go b/preauth_keys_test.go
index 6f233a9..c54c1bf 100644
--- a/preauth_keys_test.go
+++ b/preauth_keys_test.go
@@ -78,7 +78,7 @@ func (*Suite) TestAlreadyUsedKey(c *check.C) {
 		MachineKey:     "foo",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "testest",
+		Hostname:       "testest",
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
 		AuthKeyID:      uint(pak.ID),
@@ -102,7 +102,7 @@ func (*Suite) TestReusableBeingUsedKey(c *check.C) {
 		MachineKey:     "foo",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "testest",
+		Hostname:       "testest",
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
 		AuthKeyID:      uint(pak.ID),
@@ -139,7 +139,7 @@ func (*Suite) TestEphemeralKey(c *check.C) {
 		MachineKey:     "foo",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "testest",
+		Hostname:       "testest",
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
 		LastSeen:       &now,
diff --git a/routes_test.go b/routes_test.go
index 31b8d44..0108d88 100644
--- a/routes_test.go
+++ b/routes_test.go
@@ -28,7 +28,7 @@ func (s *Suite) TestGetRoutes(c *check.C) {
 		MachineKey:     "foo",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "test_get_route_machine",
+		Hostname:           "test_get_route_machine",
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
 		AuthKeyID:      uint(pak.ID),
@@ -79,7 +79,7 @@ func (s *Suite) TestGetEnableRoutes(c *check.C) {
 		MachineKey:     "foo",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "test_enable_route_machine",
+		Hostname:           "test_enable_route_machine",
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
 		AuthKeyID:      uint(pak.ID),
diff --git a/utils_test.go b/utils_test.go
index 7c72c09..ff85ac8 100644
--- a/utils_test.go
+++ b/utils_test.go
@@ -34,7 +34,7 @@ func (s *Suite) TestGetUsedIps(c *check.C) {
 		MachineKey:     "foo",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "testmachine",
+		Hostname:           "testmachine",
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
 		AuthKeyID:      uint(pak.ID),
@@ -82,7 +82,7 @@ func (s *Suite) TestGetMultiIp(c *check.C) {
 			MachineKey:     "foo",
 			NodeKey:        "bar",
 			DiscoKey:       "faa",
-			Name:           "testmachine",
+			Hostname:           "testmachine",
 			NamespaceID:    namespace.ID,
 			RegisterMethod: RegisterMethodAuthKey,
 			AuthKeyID:      uint(pak.ID),
@@ -172,7 +172,7 @@ func (s *Suite) TestGetAvailableIpMachineWithoutIP(c *check.C) {
 		MachineKey:     "foo",
 		NodeKey:        "bar",
 		DiscoKey:       "faa",
-		Name:           "testmachine",
+		Hostname:           "testmachine",
 		NamespaceID:    namespace.ID,
 		RegisterMethod: RegisterMethodAuthKey,
 		AuthKeyID:      uint(pak.ID),

From caf79f6910dc8caee29b3e608931eb7273e472a4 Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Sun, 24 Apr 2022 20:55:11 +0100
Subject: [PATCH 05/37] Change nickname to givenname in proto

---
 proto/headscale/v1/machine.proto | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/proto/headscale/v1/machine.proto b/proto/headscale/v1/machine.proto
index 42fab53..1309e64 100644
--- a/proto/headscale/v1/machine.proto
+++ b/proto/headscale/v1/machine.proto
@@ -39,7 +39,7 @@ message Machine {
     // bytes endpoints      = 16;
     // bytes enabled_routes = 17;
 
-    string          nickname     = 18;
+    string          given_name     = 18;
 }
 
 message RegisterMachineRequest {

From 6b79679cb419835854ee87e137c406ca6cf6350f Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Sun, 24 Apr 2022 20:55:20 +0100
Subject: [PATCH 06/37] Generate from proto

---
 gen/go/headscale/v1/machine.pb.go             | 152 +++++++++---------
 .../headscale/v1/headscale.swagger.json       |   2 +-
 2 files changed, 77 insertions(+), 77 deletions(-)

diff --git a/gen/go/headscale/v1/machine.pb.go b/gen/go/headscale/v1/machine.pb.go
index 862b710..c377625 100644
--- a/gen/go/headscale/v1/machine.pb.go
+++ b/gen/go/headscale/v1/machine.pb.go
@@ -91,7 +91,7 @@ type Machine struct {
 	PreAuthKey           *PreAuthKey            `protobuf:"bytes,11,opt,name=pre_auth_key,json=preAuthKey,proto3" json:"pre_auth_key,omitempty"`
 	CreatedAt            *timestamppb.Timestamp `protobuf:"bytes,12,opt,name=created_at,json=createdAt,proto3" json:"created_at,omitempty"`
 	RegisterMethod       RegisterMethod         `protobuf:"varint,13,opt,name=register_method,json=registerMethod,proto3,enum=headscale.v1.RegisterMethod" json:"register_method,omitempty"`
-	Nickname             string                 `protobuf:"bytes,18,opt,name=nickname,proto3" json:"nickname,omitempty"`
+	GivenName            string                 `protobuf:"bytes,18,opt,name=given_name,json=givenName,proto3" json:"given_name,omitempty"`
 }
 
 func (x *Machine) Reset() {
@@ -217,9 +217,9 @@ func (x *Machine) GetRegisterMethod() RegisterMethod {
 	return RegisterMethod_REGISTER_METHOD_UNSPECIFIED
 }
 
-func (x *Machine) GetNickname() string {
+func (x *Machine) GetGivenName() string {
 	if x != nil {
-		return x.Nickname
+		return x.GivenName
 	}
 	return ""
 }
@@ -924,7 +924,7 @@ var file_headscale_v1_machine_proto_rawDesc = []byte{
 	0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70,
 	0x61, 0x63, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1d, 0x68, 0x65, 0x61, 0x64, 0x73,
 	0x63, 0x61, 0x6c, 0x65, 0x2f, 0x76, 0x31, 0x2f, 0x70, 0x72, 0x65, 0x61, 0x75, 0x74, 0x68, 0x6b,
-	0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xf9, 0x04, 0x0a, 0x07, 0x4d, 0x61, 0x63,
+	0x65, 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xfc, 0x04, 0x0a, 0x07, 0x4d, 0x61, 0x63,
 	0x68, 0x69, 0x6e, 0x65, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x04,
 	0x52, 0x02, 0x69, 0x64, 0x12, 0x1f, 0x0a, 0x0b, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f,
 	0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69,
@@ -962,81 +962,81 @@ var file_headscale_v1_machine_proto_rawDesc = []byte{
 	0x74, 0x68, 0x6f, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0e, 0x32, 0x1c, 0x2e, 0x68, 0x65, 0x61,
 	0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74,
 	0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x52, 0x0e, 0x72, 0x65, 0x67, 0x69, 0x73, 0x74,
-	0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1a, 0x0a, 0x08, 0x6e, 0x69, 0x63, 0x6b,
-	0x6e, 0x61, 0x6d, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6e, 0x69, 0x63, 0x6b,
-	0x6e, 0x61, 0x6d, 0x65, 0x22, 0x48, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72,
-	0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c,
-	0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a, 0x03,
-	0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x22, 0x4a,
-	0x0a, 0x17, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
-	0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63,
-	0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61,
-	0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
-	0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x32, 0x0a, 0x11, 0x47, 0x65,
-	0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
-	0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20,
-	0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22, 0x45,
-	0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70,
+	0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x69, 0x76, 0x65,
+	0x6e, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x12, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x69,
+	0x76, 0x65, 0x6e, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x48, 0x0a, 0x16, 0x52, 0x65, 0x67, 0x69, 0x73,
+	0x74, 0x65, 0x72, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12,
+	0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65,
+	0x79, 0x22, 0x4a, 0x0a, 0x17, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x61, 0x63,
+	0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07,
+	0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e,
+	0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63,
+	0x68, 0x69, 0x6e, 0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x32, 0x0a,
+	0x11, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64,
+	0x18, 0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49,
+	0x64, 0x22, 0x45, 0x0a, 0x12, 0x47, 0x65, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52,
+	0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69,
+	0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73,
+	0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52,
+	0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x35, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65,
+	0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22,
+	0x17, 0x0a, 0x15, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x0a, 0x14, 0x45, 0x78, 0x70, 0x69,
+	0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74,
+	0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22,
+	0x48, 0x0a, 0x15, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
+	0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68,
+	0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64,
+	0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
+	0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x50, 0x0a, 0x14, 0x52, 0x65, 0x6e,
+	0x61, 0x6d, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73,
+	0x74, 0x12, 0x1d, 0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64,
+	0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01,
+	0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x77, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x48, 0x0a, 0x15, 0x52,
+	0x65, 0x6e, 0x61, 0x6d, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70,
 	0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18,
 	0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c,
 	0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x07, 0x6d, 0x61,
-	0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x35, 0x0a, 0x14, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d,
-	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a,
-	0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22, 0x17, 0x0a, 0x15,
-	0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73,
-	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x35, 0x0a, 0x14, 0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4d,
-	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d, 0x0a,
-	0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28,
-	0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x22, 0x48, 0x0a, 0x15,
-	0x45, 0x78, 0x70, 0x69, 0x72, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73,
-	0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65,
-	0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61,
-	0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x07, 0x6d,
-	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x50, 0x0a, 0x14, 0x52, 0x65, 0x6e, 0x61, 0x6d, 0x65,
-	0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1d,
-	0x0a, 0x0a, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x04, 0x52, 0x09, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x49, 0x64, 0x12, 0x19, 0x0a,
-	0x08, 0x6e, 0x65, 0x77, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x07, 0x6e, 0x65, 0x77, 0x4e, 0x61, 0x6d, 0x65, 0x22, 0x48, 0x0a, 0x15, 0x52, 0x65, 0x6e, 0x61,
-	0x6d, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73,
-	0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76,
-	0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69,
-	0x6e, 0x65, 0x22, 0x33, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
-	0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d,
-	0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61,
-	0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x49, 0x0a, 0x14, 0x4c, 0x69, 0x73, 0x74, 0x4d,
-	0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12,
-	0x31, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28,
-	0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31,
-	0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e,
-	0x65, 0x73, 0x22, 0x77, 0x0a, 0x19, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, 0x74,
-	0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12,
-	0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01,
-	0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x10, 0x0a,
-	0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12,
-	0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e,
-	0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20,
-	0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x22, 0x4d, 0x0a, 0x1a, 0x44,
-	0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
-	0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07, 0x6d, 0x61, 0x63,
-	0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61,
-	0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e,
-	0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2a, 0x82, 0x01, 0x0a, 0x0e, 0x52,
-	0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x12, 0x1f, 0x0a,
-	0x1b, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44,
-	0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10, 0x00, 0x12, 0x1c,
-	0x0a, 0x18, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f,
-	0x44, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12, 0x17, 0x0a, 0x13,
-	0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f,
-	0x43, 0x4c, 0x49, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45,
-	0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x4f, 0x49, 0x44, 0x43, 0x10, 0x03, 0x42,
-	0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6a, 0x75,
-	0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65,
-	0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
-	0x6f, 0x33,
+	0x63, 0x68, 0x69, 0x6e, 0x65, 0x22, 0x33, 0x0a, 0x13, 0x4c, 0x69, 0x73, 0x74, 0x4d, 0x61, 0x63,
+	0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09,
+	0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x22, 0x49, 0x0a, 0x14, 0x4c, 0x69,
+	0x73, 0x74, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e,
+	0x73, 0x65, 0x12, 0x31, 0x0a, 0x08, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x73, 0x18, 0x01,
+	0x20, 0x03, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65,
+	0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x08, 0x6d, 0x61, 0x63,
+	0x68, 0x69, 0x6e, 0x65, 0x73, 0x22, 0x77, 0x0a, 0x19, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72,
+	0x65, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65,
+	0x73, 0x74, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18,
+	0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65,
+	0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b,
+	0x65, 0x79, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09,
+	0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73,
+	0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x73, 0x22, 0x4d,
+	0x0a, 0x1a, 0x44, 0x65, 0x62, 0x75, 0x67, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x63,
+	0x68, 0x69, 0x6e, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2f, 0x0a, 0x07,
+	0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e,
+	0x68, 0x65, 0x61, 0x64, 0x73, 0x63, 0x61, 0x6c, 0x65, 0x2e, 0x76, 0x31, 0x2e, 0x4d, 0x61, 0x63,
+	0x68, 0x69, 0x6e, 0x65, 0x52, 0x07, 0x6d, 0x61, 0x63, 0x68, 0x69, 0x6e, 0x65, 0x2a, 0x82, 0x01,
+	0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x73, 0x74, 0x65, 0x72, 0x4d, 0x65, 0x74, 0x68, 0x6f, 0x64,
+	0x12, 0x1f, 0x0a, 0x1b, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54,
+	0x48, 0x4f, 0x44, 0x5f, 0x55, 0x4e, 0x53, 0x50, 0x45, 0x43, 0x49, 0x46, 0x49, 0x45, 0x44, 0x10,
+	0x00, 0x12, 0x1c, 0x0a, 0x18, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45,
+	0x54, 0x48, 0x4f, 0x44, 0x5f, 0x41, 0x55, 0x54, 0x48, 0x5f, 0x4b, 0x45, 0x59, 0x10, 0x01, 0x12,
+	0x17, 0x0a, 0x13, 0x52, 0x45, 0x47, 0x49, 0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48,
+	0x4f, 0x44, 0x5f, 0x43, 0x4c, 0x49, 0x10, 0x02, 0x12, 0x18, 0x0a, 0x14, 0x52, 0x45, 0x47, 0x49,
+	0x53, 0x54, 0x45, 0x52, 0x5f, 0x4d, 0x45, 0x54, 0x48, 0x4f, 0x44, 0x5f, 0x4f, 0x49, 0x44, 0x43,
+	0x10, 0x03, 0x42, 0x29, 0x5a, 0x27, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d,
+	0x2f, 0x6a, 0x75, 0x61, 0x6e, 0x66, 0x6f, 0x6e, 0x74, 0x2f, 0x68, 0x65, 0x61, 0x64, 0x73, 0x63,
+	0x61, 0x6c, 0x65, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x62, 0x06, 0x70,
+	0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/gen/openapiv2/headscale/v1/headscale.swagger.json b/gen/openapiv2/headscale/v1/headscale.swagger.json
index 0ee446f..ceaf558 100644
--- a/gen/openapiv2/headscale/v1/headscale.swagger.json
+++ b/gen/openapiv2/headscale/v1/headscale.swagger.json
@@ -944,7 +944,7 @@
         "registerMethod": {
           "$ref": "#/definitions/v1RegisterMethod"
         },
-        "nickname": {
+        "givenName": {
           "type": "string"
         }
       }

From 14994cb6cca3d2b02e4b5ac24f66552f9b6fcce6 Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Sun, 24 Apr 2022 20:55:54 +0100
Subject: [PATCH 07/37] Use new logic and fields for dns

---
 namespaces.go |   1 +
 oidc.go       |   2 +-
 poll.go       | 115 +++++++++++++++++++++++---------------------------
 3 files changed, 54 insertions(+), 64 deletions(-)

diff --git a/namespaces.go b/namespaces.go
index bb32795..9f7ba26 100644
--- a/namespaces.go
+++ b/namespaces.go
@@ -237,6 +237,7 @@ func (n *Namespace) toProto() *v1.Namespace {
 	}
 }
 
+// TODO(kradalby): We need these fields to be unique, we need to add a hash or something at the end.
 // NormalizeToFQDNRules will replace forbidden chars in namespace
 // it can also return an error if the namespace doesn't respect RFC 952 and 1123.
 func NormalizeToFQDNRules(name string, stripEmailDomain bool) (string, error) {
diff --git a/oidc.go b/oidc.go
index 598a208..e87183a 100644
--- a/oidc.go
+++ b/oidc.go
@@ -231,7 +231,7 @@ func (h *Headscale) OIDCCallback(ctx *gin.Context) {
 	if machine != nil {
 		log.Trace().
 			Caller().
-			Str("machine", machine.Name).
+			Str("machine", machine.Hostname).
 			Msg("machine already registered, reauthenticating")
 
 		h.RefreshMachine(machine, time.Time{})
diff --git a/poll.go b/poll.go
index 3bad0b8..d6c686d 100644
--- a/poll.go
+++ b/poll.go
@@ -80,21 +80,10 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) {
 	log.Trace().
 		Str("handler", "PollNetMap").
 		Str("id", ctx.Param("id")).
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Msg("Found machine in database")
 
-	hname, err := NormalizeToFQDNRules(
-		req.Hostinfo.Hostname,
-		h.cfg.OIDC.StripEmaildomain,
-	)
-	if err != nil {
-		log.Error().
-			Caller().
-			Str("func", "handleAuthKey").
-			Str("hostinfo.name", req.Hostinfo.Hostname).
-			Err(err)
-	}
-	machine.Name = hname
+	machine.Hostname = req.Hostinfo.Hostname
 	machine.HostInfo = HostInfo(*req.Hostinfo)
 	machine.DiscoKey = DiscoPublicKeyStripPrefix(req.DiscoKey)
 	now := time.Now().UTC()
@@ -106,7 +95,7 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) {
 			log.Error().
 				Caller().
 				Str("func", "handleAuthKey").
-				Str("machine", machine.Name).
+				Str("machine", machine.Hostname).
 				Err(err)
 		}
 	}
@@ -129,7 +118,7 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) {
 		log.Error().
 			Str("handler", "PollNetMap").
 			Str("id", ctx.Param("id")).
-			Str("machine", machine.Name).
+			Str("machine", machine.Hostname).
 			Err(err).
 			Msg("Failed to get Map response")
 		ctx.String(http.StatusInternalServerError, ":(")
@@ -145,7 +134,7 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) {
 	log.Debug().
 		Str("handler", "PollNetMap").
 		Str("id", ctx.Param("id")).
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Bool("readOnly", req.ReadOnly).
 		Bool("omitPeers", req.OmitPeers).
 		Bool("stream", req.Stream).
@@ -154,7 +143,7 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) {
 	if req.ReadOnly {
 		log.Info().
 			Str("handler", "PollNetMap").
-			Str("machine", machine.Name).
+			Str("machine", machine.Hostname).
 			Msg("Client is starting up. Probably interested in a DERP map")
 		ctx.Data(http.StatusOK, "application/json; charset=utf-8", data)
 
@@ -172,27 +161,27 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) {
 	log.Trace().
 		Str("handler", "PollNetMap").
 		Str("id", ctx.Param("id")).
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Msg("Loading or creating update channel")
 
 	const chanSize = 8
 	updateChan := make(chan struct{}, chanSize)
 
 	pollDataChan := make(chan []byte, chanSize)
-	defer closeChanWithLog(pollDataChan, machine.Name, "pollDataChan")
+	defer closeChanWithLog(pollDataChan, machine.Hostname, "pollDataChan")
 
 	keepAliveChan := make(chan []byte)
 
 	if req.OmitPeers && !req.Stream {
 		log.Info().
 			Str("handler", "PollNetMap").
-			Str("machine", machine.Name).
+			Str("machine", machine.Hostname).
 			Msg("Client sent endpoint update and is ok with a response without peer list")
 		ctx.Data(http.StatusOK, "application/json; charset=utf-8", data)
 
 		// It sounds like we should update the nodes when we have received a endpoint update
 		// even tho the comments in the tailscale code dont explicitly say so.
-		updateRequestsFromNode.WithLabelValues(machine.Namespace.Name, machine.Name, "endpoint-update").
+		updateRequestsFromNode.WithLabelValues(machine.Namespace.Name, machine.Hostname, "endpoint-update").
 			Inc()
 		updateChan <- struct{}{}
 
@@ -200,7 +189,7 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) {
 	} else if req.OmitPeers && req.Stream {
 		log.Warn().
 			Str("handler", "PollNetMap").
-			Str("machine", machine.Name).
+			Str("machine", machine.Hostname).
 			Msg("Ignoring request, don't know how to handle it")
 		ctx.String(http.StatusBadRequest, "")
 
@@ -209,19 +198,19 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) {
 
 	log.Info().
 		Str("handler", "PollNetMap").
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Msg("Client is ready to access the tailnet")
 	log.Info().
 		Str("handler", "PollNetMap").
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Msg("Sending initial map")
 	pollDataChan <- data
 
 	log.Info().
 		Str("handler", "PollNetMap").
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Msg("Notifying peers")
-	updateRequestsFromNode.WithLabelValues(machine.Namespace.Name, machine.Name, "full-update").
+	updateRequestsFromNode.WithLabelValues(machine.Namespace.Name, machine.Hostname, "full-update").
 		Inc()
 	updateChan <- struct{}{}
 
@@ -237,7 +226,7 @@ func (h *Headscale) PollNetMapHandler(ctx *gin.Context) {
 	log.Trace().
 		Str("handler", "PollNetMap").
 		Str("id", ctx.Param("id")).
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Msg("Finished stream, closing PollNetMap session")
 }
 
@@ -272,7 +261,7 @@ func (h *Headscale) PollNetMapStream(
 			return
 		}
 
-		ctx := context.WithValue(ctx.Request.Context(), "machineName", machine.Name)
+		ctx := context.WithValue(ctx.Request.Context(), "machineName", machine.Hostname)
 
 		ctx, cancel := context.WithCancel(ctx)
 		defer cancel()
@@ -290,19 +279,19 @@ func (h *Headscale) PollNetMapStream(
 	ctx.Stream(func(writer io.Writer) bool {
 		log.Trace().
 			Str("handler", "PollNetMapStream").
-			Str("machine", machine.Name).
+			Str("machine", machine.Hostname).
 			Msg("Waiting for data to stream...")
 
 		log.Trace().
 			Str("handler", "PollNetMapStream").
-			Str("machine", machine.Name).
+			Str("machine", machine.Hostname).
 			Msgf("pollData is %#v, keepAliveChan is %#v, updateChan is %#v", pollDataChan, keepAliveChan, updateChan)
 
 		select {
 		case data := <-pollDataChan:
 			log.Trace().
 				Str("handler", "PollNetMapStream").
-				Str("machine", machine.Name).
+				Str("machine", machine.Hostname).
 				Str("channel", "pollData").
 				Int("bytes", len(data)).
 				Msg("Sending data received via pollData channel")
@@ -310,7 +299,7 @@ func (h *Headscale) PollNetMapStream(
 			if err != nil {
 				log.Error().
 					Str("handler", "PollNetMapStream").
-					Str("machine", machine.Name).
+					Str("machine", machine.Hostname).
 					Str("channel", "pollData").
 					Err(err).
 					Msg("Cannot write data")
@@ -319,7 +308,7 @@ func (h *Headscale) PollNetMapStream(
 			}
 			log.Trace().
 				Str("handler", "PollNetMapStream").
-				Str("machine", machine.Name).
+				Str("machine", machine.Hostname).
 				Str("channel", "pollData").
 				Int("bytes", len(data)).
 				Msg("Data from pollData channel written successfully")
@@ -330,7 +319,7 @@ func (h *Headscale) PollNetMapStream(
 			if err != nil {
 				log.Error().
 					Str("handler", "PollNetMapStream").
-					Str("machine", machine.Name).
+					Str("machine", machine.Hostname).
 					Str("channel", "pollData").
 					Err(err).
 					Msg("Cannot update machine from database")
@@ -342,7 +331,7 @@ func (h *Headscale) PollNetMapStream(
 			now := time.Now().UTC()
 			machine.LastSeen = &now
 
-			lastStateUpdate.WithLabelValues(machine.Namespace.Name, machine.Name).
+			lastStateUpdate.WithLabelValues(machine.Namespace.Name, machine.Hostname).
 				Set(float64(now.Unix()))
 			machine.LastSuccessfulUpdate = &now
 
@@ -350,14 +339,14 @@ func (h *Headscale) PollNetMapStream(
 			if err != nil {
 				log.Error().
 					Str("handler", "PollNetMapStream").
-					Str("machine", machine.Name).
+					Str("machine", machine.Hostname).
 					Str("channel", "pollData").
 					Err(err).
 					Msg("Cannot update machine LastSuccessfulUpdate")
 			} else {
 				log.Trace().
 					Str("handler", "PollNetMapStream").
-					Str("machine", machine.Name).
+					Str("machine", machine.Hostname).
 					Str("channel", "pollData").
 					Int("bytes", len(data)).
 					Msg("Machine entry in database updated successfully after sending pollData")
@@ -368,7 +357,7 @@ func (h *Headscale) PollNetMapStream(
 		case data := <-keepAliveChan:
 			log.Trace().
 				Str("handler", "PollNetMapStream").
-				Str("machine", machine.Name).
+				Str("machine", machine.Hostname).
 				Str("channel", "keepAlive").
 				Int("bytes", len(data)).
 				Msg("Sending keep alive message")
@@ -376,7 +365,7 @@ func (h *Headscale) PollNetMapStream(
 			if err != nil {
 				log.Error().
 					Str("handler", "PollNetMapStream").
-					Str("machine", machine.Name).
+					Str("machine", machine.Hostname).
 					Str("channel", "keepAlive").
 					Err(err).
 					Msg("Cannot write keep alive message")
@@ -385,7 +374,7 @@ func (h *Headscale) PollNetMapStream(
 			}
 			log.Trace().
 				Str("handler", "PollNetMapStream").
-				Str("machine", machine.Name).
+				Str("machine", machine.Hostname).
 				Str("channel", "keepAlive").
 				Int("bytes", len(data)).
 				Msg("Keep alive sent successfully")
@@ -396,7 +385,7 @@ func (h *Headscale) PollNetMapStream(
 			if err != nil {
 				log.Error().
 					Str("handler", "PollNetMapStream").
-					Str("machine", machine.Name).
+					Str("machine", machine.Hostname).
 					Str("channel", "keepAlive").
 					Err(err).
 					Msg("Cannot update machine from database")
@@ -411,14 +400,14 @@ func (h *Headscale) PollNetMapStream(
 			if err != nil {
 				log.Error().
 					Str("handler", "PollNetMapStream").
-					Str("machine", machine.Name).
+					Str("machine", machine.Hostname).
 					Str("channel", "keepAlive").
 					Err(err).
 					Msg("Cannot update machine LastSeen")
 			} else {
 				log.Trace().
 					Str("handler", "PollNetMapStream").
-					Str("machine", machine.Name).
+					Str("machine", machine.Hostname).
 					Str("channel", "keepAlive").
 					Int("bytes", len(data)).
 					Msg("Machine updated successfully after sending keep alive")
@@ -429,10 +418,10 @@ func (h *Headscale) PollNetMapStream(
 		case <-updateChan:
 			log.Trace().
 				Str("handler", "PollNetMapStream").
-				Str("machine", machine.Name).
+				Str("machine", machine.Hostname).
 				Str("channel", "update").
 				Msg("Received a request for update")
-			updateRequestsReceivedOnChannel.WithLabelValues(machine.Namespace.Name, machine.Name).
+			updateRequestsReceivedOnChannel.WithLabelValues(machine.Namespace.Name, machine.Hostname).
 				Inc()
 			if h.isOutdated(machine) {
 				var lastUpdate time.Time
@@ -441,15 +430,15 @@ func (h *Headscale) PollNetMapStream(
 				}
 				log.Debug().
 					Str("handler", "PollNetMapStream").
-					Str("machine", machine.Name).
+					Str("machine", machine.Hostname).
 					Time("last_successful_update", lastUpdate).
 					Time("last_state_change", h.getLastStateChange(machine.Namespace.Name)).
-					Msgf("There has been updates since the last successful update to %s", machine.Name)
+					Msgf("There has been updates since the last successful update to %s", machine.Hostname)
 				data, err := h.getMapResponse(machineKey, mapRequest, machine)
 				if err != nil {
 					log.Error().
 						Str("handler", "PollNetMapStream").
-						Str("machine", machine.Name).
+						Str("machine", machine.Hostname).
 						Str("channel", "update").
 						Err(err).
 						Msg("Could not get the map update")
@@ -458,21 +447,21 @@ func (h *Headscale) PollNetMapStream(
 				if err != nil {
 					log.Error().
 						Str("handler", "PollNetMapStream").
-						Str("machine", machine.Name).
+						Str("machine", machine.Hostname).
 						Str("channel", "update").
 						Err(err).
 						Msg("Could not write the map response")
-					updateRequestsSentToNode.WithLabelValues(machine.Namespace.Name, machine.Name, "failed").
+					updateRequestsSentToNode.WithLabelValues(machine.Namespace.Name, machine.Hostname, "failed").
 						Inc()
 
 					return false
 				}
 				log.Trace().
 					Str("handler", "PollNetMapStream").
-					Str("machine", machine.Name).
+					Str("machine", machine.Hostname).
 					Str("channel", "update").
 					Msg("Updated Map has been sent")
-				updateRequestsSentToNode.WithLabelValues(machine.Namespace.Name, machine.Name, "success").
+				updateRequestsSentToNode.WithLabelValues(machine.Namespace.Name, machine.Hostname, "success").
 					Inc()
 
 				// Keep track of the last successful update,
@@ -486,7 +475,7 @@ func (h *Headscale) PollNetMapStream(
 				if err != nil {
 					log.Error().
 						Str("handler", "PollNetMapStream").
-						Str("machine", machine.Name).
+						Str("machine", machine.Hostname).
 						Str("channel", "update").
 						Err(err).
 						Msg("Cannot update machine from database")
@@ -497,7 +486,7 @@ func (h *Headscale) PollNetMapStream(
 				}
 				now := time.Now().UTC()
 
-				lastStateUpdate.WithLabelValues(machine.Namespace.Name, machine.Name).
+				lastStateUpdate.WithLabelValues(machine.Namespace.Name, machine.Hostname).
 					Set(float64(now.Unix()))
 				machine.LastSuccessfulUpdate = &now
 
@@ -505,7 +494,7 @@ func (h *Headscale) PollNetMapStream(
 				if err != nil {
 					log.Error().
 						Str("handler", "PollNetMapStream").
-						Str("machine", machine.Name).
+						Str("machine", machine.Hostname).
 						Str("channel", "update").
 						Err(err).
 						Msg("Cannot update machine LastSuccessfulUpdate")
@@ -517,10 +506,10 @@ func (h *Headscale) PollNetMapStream(
 				}
 				log.Trace().
 					Str("handler", "PollNetMapStream").
-					Str("machine", machine.Name).
+					Str("machine", machine.Hostname).
 					Time("last_successful_update", lastUpdate).
 					Time("last_state_change", h.getLastStateChange(machine.Namespace.Name)).
-					Msgf("%s is up to date", machine.Name)
+					Msgf("%s is up to date", machine.Hostname)
 			}
 
 			return true
@@ -528,7 +517,7 @@ func (h *Headscale) PollNetMapStream(
 		case <-ctx.Request.Context().Done():
 			log.Info().
 				Str("handler", "PollNetMapStream").
-				Str("machine", machine.Name).
+				Str("machine", machine.Hostname).
 				Msg("The client has closed the connection")
 				// TODO: Abstract away all the database calls, this can cause race conditions
 				// when an outdated machine object is kept alive, e.g. db is update from
@@ -537,7 +526,7 @@ func (h *Headscale) PollNetMapStream(
 			if err != nil {
 				log.Error().
 					Str("handler", "PollNetMapStream").
-					Str("machine", machine.Name).
+					Str("machine", machine.Hostname).
 					Str("channel", "Done").
 					Err(err).
 					Msg("Cannot update machine from database")
@@ -552,7 +541,7 @@ func (h *Headscale) PollNetMapStream(
 			if err != nil {
 				log.Error().
 					Str("handler", "PollNetMapStream").
-					Str("machine", machine.Name).
+					Str("machine", machine.Hostname).
 					Str("channel", "Done").
 					Err(err).
 					Msg("Cannot update machine LastSeen")
@@ -603,16 +592,16 @@ func (h *Headscale) scheduledPollWorker(
 
 			log.Debug().
 				Str("func", "keepAlive").
-				Str("machine", machine.Name).
+				Str("machine", machine.Hostname).
 				Msg("Sending keepalive")
 			keepAliveChan <- data
 
 		case <-updateCheckerTicker.C:
 			log.Debug().
 				Str("func", "scheduledPollWorker").
-				Str("machine", machine.Name).
+				Str("machine", machine.Hostname).
 				Msg("Sending update request")
-			updateRequestsFromNode.WithLabelValues(machine.Namespace.Name, machine.Name, "scheduled-update").
+			updateRequestsFromNode.WithLabelValues(machine.Namespace.Name, machine.Hostname, "scheduled-update").
 				Inc()
 			updateChan <- struct{}{}
 		}

From 62808cbd86599008acbf66101a46c2c7c11febfa Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Sun, 24 Apr 2022 20:56:28 +0100
Subject: [PATCH 08/37] Bubble error up to user for rename

---
 grpcv1.go | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/grpcv1.go b/grpcv1.go
index 68303be..36c9ad1 100644
--- a/grpcv1.go
+++ b/grpcv1.go
@@ -215,7 +215,7 @@ func (api headscaleV1APIServer) ExpireMachine(
 	)
 
 	log.Trace().
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Time("expiry", *machine.Expiry).
 		Msg("machine expired")
 
@@ -231,13 +231,16 @@ func (api headscaleV1APIServer) RenameMachine(
 		return nil, err
 	}
 
-	api.h.RenameMachine(
+	err = api.h.RenameMachine(
 		machine,
 		request.GetNewName(),
 	)
+	if err != nil {
+		return nil, err
+	}
 
 	log.Trace().
-		Str("machine", machine.Name).
+		Str("machine", machine.Hostname).
 		Time("expiry", *machine.Expiry).
 		Msg("machine expired")
 
@@ -393,7 +396,7 @@ func (api headscaleV1APIServer) DebugCreateMachine(
 
 	newMachine := Machine{
 		MachineKey: request.GetKey(),
-		Name:       request.GetName(),
+		Hostname:   request.GetName(),
 		Namespace:  *namespace,
 
 		Expiry:               &time.Time{},

From 06c928bc525e3a79d985030658bf6dd17767151a Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Sun, 24 Apr 2022 20:56:42 +0100
Subject: [PATCH 09/37] Migrate name and nickname fields

---
 db.go | 42 ++++++++++++++++++++++++++++++++++++++++--
 1 file changed, 40 insertions(+), 2 deletions(-)

diff --git a/db.go b/db.go
index 9130d90..6eb0683 100644
--- a/db.go
+++ b/db.go
@@ -39,6 +39,44 @@ func (h *Headscale) initDB() error {
 	}
 
 	_ = db.Migrator().RenameColumn(&Machine{}, "ip_address", "ip_addresses")
+	_ = db.Migrator().RenameColumn(&Machine{}, "name", "hostname")
+
+	// GivenName is used as the primary source of DNS names, make sure
+	// the field is populated and normalized if it was not when the
+	// machine was registered.
+	_ = db.Migrator().RenameColumn(&Machine{}, "nickname", "given_name")
+	if db.Migrator().HasColumn(&Machine{}, "given_name") {
+		machines := Machines{}
+		if err := h.db.Find(&machines).Error; err != nil {
+			log.Error().Err(err).Msg("Error accessing db")
+		}
+
+		for _, machine := range machines {
+			if machine.GivenName == "" {
+				normalizedHostname, err := NormalizeToFQDNRules(
+					machine.Hostname,
+					h.cfg.OIDC.StripEmaildomain,
+				)
+				if err != nil {
+					log.Error().
+						Caller().
+						Str("hostname", machine.Hostname).
+						Err(err).
+						Msg("Failed to normalize machine hostname in DB migration")
+				}
+
+				err = h.RenameMachine(&machine, normalizedHostname)
+				if err != nil {
+					log.Error().
+						Caller().
+						Str("hostname", machine.Hostname).
+						Err(err).
+						Msg("Failed to save normalized machine name in DB migration")
+				}
+
+			}
+		}
+	}
 
 	// If the Machine table has a column for registered,
 	// find all occourences of "false" and drop them. Then
@@ -54,13 +92,13 @@ func (h *Headscale) initDB() error {
 
 		for _, machine := range machines {
 			log.Info().
-				Str("machine", machine.Name).
+				Str("machine", machine.Hostname).
 				Str("machine_key", machine.MachineKey).
 				Msg("Deleting unregistered machine")
 			if err := h.db.Delete(&Machine{}, machine.ID).Error; err != nil {
 				log.Error().
 					Err(err).
-					Str("machine", machine.Name).
+					Str("machine", machine.Hostname).
 					Str("machine_key", machine.MachineKey).
 					Msg("Error deleting unregistered machine")
 			}

From 79704dc9b02a847ef09519ed8222e3ace80385d9 Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Sun, 24 Apr 2022 20:57:15 +0100
Subject: [PATCH 10/37] Update command with new fields

---
 cmd/headscale/cli/nodes.go | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go
index 868062e..d339a95 100644
--- a/cmd/headscale/cli/nodes.go
+++ b/cmd/headscale/cli/nodes.go
@@ -215,8 +215,8 @@ var expireNodeCmd = &cobra.Command{
 }
 
 var renameNodeCmd = &cobra.Command{
-	Use:     "rename NEW_NAME",
-	Short:   "Renames a machine in your network",
+	Use:   "rename NEW_NAME",
+	Short: "Renames a machine in your network",
 	Run: func(cmd *cobra.Command, args []string) {
 		output, _ := cmd.Flags().GetString("output")
 
@@ -241,7 +241,7 @@ var renameNodeCmd = &cobra.Command{
 		}
 		request := &v1.RenameMachineRequest{
 			MachineId: identifier,
-			NewName: newName,
+			NewName:   newName,
 		}
 
 		response, err := client.RenameMachine(ctx, request)
@@ -358,8 +358,8 @@ func nodesToPtables(
 	tableData := pterm.TableData{
 		{
 			"ID",
-			"Name",
-			"Nickname",
+			"Hostname",
+			"Friendly name",
 			"NodeKey",
 			"Namespace",
 			"IP addresses",
@@ -424,7 +424,7 @@ func nodesToPtables(
 			[]string{
 				strconv.FormatUint(machine.Id, headscale.Base10),
 				machine.Name,
-				machine.Nickname,
+				machine.GivenName,
 				nodeKey.ShortString(),
 				namespace,
 				strings.Join(machine.IpAddresses, ", "),

From 7ef8cd881c2bcdf624c05d039399095873f32df8 Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Sun, 24 Apr 2022 21:10:50 +0100
Subject: [PATCH 11/37] Fix comment

---
 machine.go | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/machine.go b/machine.go
index b1f03f0..0b08d3f 100644
--- a/machine.go
+++ b/machine.go
@@ -378,7 +378,8 @@ func (h *Headscale) ExpireMachine(machine *Machine) {
 	h.db.Save(machine)
 }
 
-// RenameMachine takes a Machine struct and sets the expire field to now.
+// RenameMachine takes a Machine struct and a new GivenName for the machines
+// and renames it.
 func (h *Headscale) RenameMachine(machine *Machine, newName string) error {
 	err := CheckForFQDNRules(
 		newName,

From 8504d0d8badf922ed8314d57ed81933587a0ecbc Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Sun, 24 Apr 2022 21:12:45 +0100
Subject: [PATCH 12/37] Move todo to correct file

---
 api.go        | 2 ++
 namespaces.go | 1 -
 2 files changed, 2 insertions(+), 1 deletion(-)

diff --git a/api.go b/api.go
index dcdf49a..3d85d1e 100644
--- a/api.go
+++ b/api.go
@@ -133,6 +133,8 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) {
 
 			return
 		}
+
+		// TODO(kradalby): We need these fields to be unique, we need to add a hash or something at the end.
 		normalizedHostname, err := NormalizeToFQDNRules(
 			req.Hostinfo.Hostname,
 			h.cfg.OIDC.StripEmaildomain,
diff --git a/namespaces.go b/namespaces.go
index 9f7ba26..bb32795 100644
--- a/namespaces.go
+++ b/namespaces.go
@@ -237,7 +237,6 @@ func (n *Namespace) toProto() *v1.Namespace {
 	}
 }
 
-// TODO(kradalby): We need these fields to be unique, we need to add a hash or something at the end.
 // NormalizeToFQDNRules will replace forbidden chars in namespace
 // it can also return an error if the namespace doesn't respect RFC 952 and 1123.
 func NormalizeToFQDNRules(name string, stripEmailDomain bool) (string, error) {

From f4873d93878b22b251623cf6be59d28624cf2aaf Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 16 May 2022 20:29:31 +0200
Subject: [PATCH 13/37] Fix rename cli error

---
 cmd/headscale/cli/nodes.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/cmd/headscale/cli/nodes.go b/cmd/headscale/cli/nodes.go
index d339a95..5ed57d0 100644
--- a/cmd/headscale/cli/nodes.go
+++ b/cmd/headscale/cli/nodes.go
@@ -249,7 +249,7 @@ var renameNodeCmd = &cobra.Command{
 			ErrorOutput(
 				err,
 				fmt.Sprintf(
-					"Cannot expire machine: %s\n",
+					"Cannot rename machine: %s\n",
 					status.Convert(err).Message(),
 				),
 				output,

From 177c21b294c1ada788054d170b4f29f20f76d3b0 Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 16 May 2022 20:30:43 +0200
Subject: [PATCH 14/37] Add helper function to create a unique givenname

---
 machine.go      |  30 +++++++++++
 machine_test.go | 134 ++++++++++++++++++++++++++++++++++++++++++++++++
 utils.go        |  13 +++++
 3 files changed, 177 insertions(+)

diff --git a/machine.go b/machine.go
index 0b08d3f..5b8a3ae 100644
--- a/machine.go
+++ b/machine.go
@@ -26,6 +26,7 @@ const (
 	)
 	errCouldNotConvertMachineInterface = Error("failed to convert machine interface")
 	errHostnameTooLong                 = Error("Hostname too long")
+	MachineGivenNameHashLength         = 8
 )
 
 const (
@@ -813,3 +814,32 @@ func (machine *Machine) RoutesToProto() *v1.Routes {
 		EnabledRoutes:    ipPrefixToString(enabledRoutes),
 	}
 }
+
+func (h *Headscale) GenerateGivenName(suppliedName string) (string, error) {
+	// If a hostname is or will be longer than 63 chars after adding the hash,
+	// it needs to be trimmed.
+	trimmedHostnameLength := labelHostnameLength - MachineGivenNameHashLength - 2
+
+	normalizedHostname, err := NormalizeToFQDNRules(
+		suppliedName,
+		h.cfg.OIDC.StripEmaildomain,
+	)
+	if err != nil {
+		return "", err
+	}
+
+	postfix, err := GenerateRandomStringDNSSafe(MachineGivenNameHashLength)
+	if err != nil {
+		return "", err
+	}
+
+	// Verify that that the new unique name is shorter than the maximum allowed
+	// DNS segment.
+	if len(normalizedHostname) <= trimmedHostnameLength {
+		normalizedHostname = fmt.Sprintf("%s-%s", normalizedHostname, postfix)
+	} else {
+		normalizedHostname = fmt.Sprintf("%s-%s", normalizedHostname[:trimmedHostnameLength], postfix)
+	}
+
+	return normalizedHostname, nil
+}
diff --git a/machine_test.go b/machine_test.go
index 02f2d0d..e2d1f39 100644
--- a/machine_test.go
+++ b/machine_test.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"reflect"
 	"strconv"
+	"strings"
 	"testing"
 	"time"
 
@@ -654,3 +655,136 @@ func Test_getFilteredByACLPeers(t *testing.T) {
 		})
 	}
 }
+
+func TestHeadscale_GenerateGivenName(t *testing.T) {
+	type args struct {
+		suppliedName string
+	}
+	tests := []struct {
+		name    string
+		h       *Headscale
+		args    args
+		want    string
+		wantErr bool
+	}{
+		{
+			name: "simple machine name generation",
+			h: &Headscale{
+				cfg: Config{
+					OIDC: OIDCConfig{
+						StripEmaildomain: true,
+					},
+				},
+			},
+			args: args{
+				suppliedName: "testmachine",
+			},
+			want:    "testmachine",
+			wantErr: false,
+		},
+		{
+			name: "machine name with 53 chars",
+			h: &Headscale{
+				cfg: Config{
+					OIDC: OIDCConfig{
+						StripEmaildomain: true,
+					},
+				},
+			},
+			args: args{
+				suppliedName: "testmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaachine",
+			},
+			want:    "testmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaachine",
+			wantErr: false,
+		},
+		{
+			name: "machine name with 60 chars",
+			h: &Headscale{
+				cfg: Config{
+					OIDC: OIDCConfig{
+						StripEmaildomain: true,
+					},
+				},
+			},
+			args: args{
+				suppliedName: "testmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaachine1234567",
+			},
+			want:    "testmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaachine",
+			wantErr: false,
+		},
+		{
+			name: "machine name with 63 chars",
+			h: &Headscale{
+				cfg: Config{
+					OIDC: OIDCConfig{
+						StripEmaildomain: true,
+					},
+				},
+			},
+			args: args{
+				suppliedName: "testmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaachine1234567890",
+			},
+			want:    "testmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+			wantErr: false,
+		},
+		{
+			name: "machine name with 64 chars",
+			h: &Headscale{
+				cfg: Config{
+					OIDC: OIDCConfig{
+						StripEmaildomain: true,
+					},
+				},
+			},
+			args: args{
+				suppliedName: "testmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaachine1234567891",
+			},
+			want:    "testmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
+			wantErr: false,
+		},
+		{
+			name: "machine name with 73 chars",
+			h: &Headscale{
+				cfg: Config{
+					OIDC: OIDCConfig{
+						StripEmaildomain: true,
+					},
+				},
+			},
+			args: args{
+				suppliedName: "testmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaachine12345678901234567890",
+			},
+			want:    "",
+			wantErr: true,
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			got, err := tt.h.GenerateGivenName(tt.args.suppliedName)
+			if (err != nil) != tt.wantErr {
+				t.Errorf(
+					"Headscale.GenerateGivenName() error = %v, wantErr %v",
+					err,
+					tt.wantErr,
+				)
+				return
+			}
+
+			if tt.want != "" && strings.Contains(tt.want, got) {
+				t.Errorf(
+					"Headscale.GenerateGivenName() = %v, is not a substring of %v",
+					tt.want,
+					got,
+				)
+			}
+
+			if len(got) > labelHostnameLength {
+				t.Errorf(
+					"Headscale.GenerateGivenName() = %v is larger than allowed DNS segment %d",
+					got,
+					labelHostnameLength,
+				)
+			}
+		})
+	}
+}
diff --git a/utils.go b/utils.go
index af267eb..615ca46 100644
--- a/utils.go
+++ b/utils.go
@@ -317,3 +317,16 @@ func GenerateRandomStringURLSafe(n int) (string, error) {
 
 	return base64.RawURLEncoding.EncodeToString(b), err
 }
+
+// GenerateRandomStringDNSSafe returns a DNS-safe
+// securely generated random string.
+// It will return an error if the system's secure random
+// number generator fails to function correctly, in which
+// case the caller should not continue.
+func GenerateRandomStringDNSSafe(n int) (string, error) {
+	str, err := GenerateRandomStringURLSafe(n)
+
+	str = strings.ReplaceAll(str, "_", "-")
+
+	return str[:n], err
+}

From 03cccd60a641d51c78b4fcc06776c96bc8c43125 Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 16 May 2022 20:31:32 +0200
Subject: [PATCH 15/37] Reword FQDN normalize errors to not _only_ cover
 namespaces

---
 namespaces.go | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/namespaces.go b/namespaces.go
index bb32795..0f92977 100644
--- a/namespaces.go
+++ b/namespaces.go
@@ -266,21 +266,21 @@ func NormalizeToFQDNRules(name string, stripEmailDomain bool) (string, error) {
 func CheckForFQDNRules(name string) error {
 	if len(name) > labelHostnameLength {
 		return fmt.Errorf(
-			"Namespace must not be over 63 chars. %v doesn't comply with this rule: %w",
+			"DNS segment must not be over 63 chars. %v doesn't comply with this rule: %w",
 			name,
 			errInvalidNamespaceName,
 		)
 	}
 	if strings.ToLower(name) != name {
 		return fmt.Errorf(
-			"Namespace name should be lowercase. %v doesn't comply with this rule: %w",
+			"DNS segment should be lowercase. %v doesn't comply with this rule: %w",
 			name,
 			errInvalidNamespaceName,
 		)
 	}
 	if invalidCharsInNamespaceRegex.MatchString(name) {
 		return fmt.Errorf(
-			"Namespace name should only be composed of lowercase ASCII letters numbers, hyphen and dots. %v doesn't comply with theses rules: %w",
+			"DNS segment should only be composed of lowercase ASCII letters numbers, hyphen and dots. %v doesn't comply with theses rules: %w",
 			name,
 			errInvalidNamespaceName,
 		)

From 5fa3016703ef51cfff6868e11dab660d1ea43b6d Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 16 May 2022 20:32:37 +0200
Subject: [PATCH 16/37] Generate unique givennames for hosts joining (and debug
 added)

---
 api.go    | 8 ++------
 grpcv1.go | 6 ++++++
 2 files changed, 8 insertions(+), 6 deletions(-)

diff --git a/api.go b/api.go
index 3d85d1e..b4e3a7d 100644
--- a/api.go
+++ b/api.go
@@ -134,11 +134,7 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) {
 			return
 		}
 
-		// TODO(kradalby): We need these fields to be unique, we need to add a hash or something at the end.
-		normalizedHostname, err := NormalizeToFQDNRules(
-			req.Hostinfo.Hostname,
-			h.cfg.OIDC.StripEmaildomain,
-		)
+		givenName, err := h.GenerateGivenName(req.Hostinfo.Hostname)
 		if err != nil {
 			log.Error().
 				Caller().
@@ -156,7 +152,7 @@ func (h *Headscale) RegistrationHandler(ctx *gin.Context) {
 		newMachine := Machine{
 			MachineKey: machineKeyStr,
 			Hostname:   req.Hostinfo.Hostname,
-			GivenName:  normalizedHostname,
+			GivenName:  givenName,
 			NodeKey:    NodePublicKeyStripPrefix(req.NodeKey),
 			LastSeen:   &now,
 			Expiry:     &time.Time{},
diff --git a/grpcv1.go b/grpcv1.go
index 36c9ad1..9c59389 100644
--- a/grpcv1.go
+++ b/grpcv1.go
@@ -394,9 +394,15 @@ func (api headscaleV1APIServer) DebugCreateMachine(
 		Hostname:    "DebugTestMachine",
 	}
 
+	givenName, err := api.h.GenerateGivenName(request.GetName())
+	if err != nil {
+		return nil, err
+	}
+
 	newMachine := Machine{
 		MachineKey: request.GetKey(),
 		Hostname:   request.GetName(),
+		GivenName:  givenName,
 		Namespace:  *namespace,
 
 		Expiry:               &time.Time{},

From 9b393eb86105a23dcb35f6c368dca30b00238147 Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 16 May 2022 20:32:56 +0200
Subject: [PATCH 17/37] Add integration cli tests for rename command

---
 integration_cli_test.go | 172 ++++++++++++++++++++++++++++++++++++++++
 1 file changed, 172 insertions(+)

diff --git a/integration_cli_test.go b/integration_cli_test.go
index 9644037..700179a 100644
--- a/integration_cli_test.go
+++ b/integration_cli_test.go
@@ -914,6 +914,178 @@ func (s *IntegrationCLITestSuite) TestNodeExpireCommand() {
 	assert.True(s.T(), listAllAfterExpiry[4].Expiry.AsTime().IsZero())
 }
 
+func (s *IntegrationCLITestSuite) TestNodeRenameCommand() {
+	namespace, err := s.createNamespace("machine-rename-command")
+	assert.Nil(s.T(), err)
+
+	// Randomly generated machine keys
+	machineKeys := []string{
+		"cf7b0fd05da556fdc3bab365787b506fd82d64a70745db70e00e86c1b1c03084",
+		"8bc13285cee598acf76b1824a6f4490f7f2e3751b201e28aeb3b07fe81d5b4a1",
+		"f08305b4ee4250b95a70f3b7504d048d75d899993c624a26d422c67af0422507",
+		"6abd00bb5fdda622db51387088c68e97e71ce58e7056aa54f592b6a8219d524c",
+		"9b2ffa7e08cc421a3d2cca9012280f6a236fd0de0b4ce005b30a98ad930306fe",
+	}
+	machines := make([]*v1.Machine, len(machineKeys))
+	assert.Nil(s.T(), err)
+
+	for index, machineKey := range machineKeys {
+		_, err := ExecuteCommand(
+			&s.headscale,
+			[]string{
+				"headscale",
+				"debug",
+				"create-node",
+				"--name",
+				fmt.Sprintf("machine-%d", index+1),
+				"--namespace",
+				namespace.Name,
+				"--key",
+				machineKey,
+				"--output",
+				"json",
+			},
+			[]string{},
+		)
+		assert.Nil(s.T(), err)
+
+		machineResult, err := ExecuteCommand(
+			&s.headscale,
+			[]string{
+				"headscale",
+				"nodes",
+				"--namespace",
+				namespace.Name,
+				"register",
+				"--key",
+				machineKey,
+				"--output",
+				"json",
+			},
+			[]string{},
+		)
+		assert.Nil(s.T(), err)
+
+		var machine v1.Machine
+		err = json.Unmarshal([]byte(machineResult), &machine)
+		assert.Nil(s.T(), err)
+
+		machines[index] = &machine
+	}
+
+	assert.Len(s.T(), machines, len(machineKeys))
+
+	listAllResult, err := ExecuteCommand(
+		&s.headscale,
+		[]string{
+			"headscale",
+			"nodes",
+			"list",
+			"--output",
+			"json",
+		},
+		[]string{},
+	)
+	assert.Nil(s.T(), err)
+
+	var listAll []v1.Machine
+	err = json.Unmarshal([]byte(listAllResult), &listAll)
+	assert.Nil(s.T(), err)
+
+	assert.Len(s.T(), listAll, 5)
+
+	assert.Contains(s.T(), listAll[0].GetGivenName(), "machine-1")
+	assert.Contains(s.T(), listAll[1].GetGivenName(), "machine-2")
+	assert.Contains(s.T(), listAll[2].GetGivenName(), "machine-3")
+	assert.Contains(s.T(), listAll[3].GetGivenName(), "machine-4")
+	assert.Contains(s.T(), listAll[4].GetGivenName(), "machine-5")
+
+	for i := 0; i < 3; i++ {
+		_, err := ExecuteCommand(
+			&s.headscale,
+			[]string{
+				"headscale",
+				"nodes",
+				"rename",
+				"--identifier",
+				fmt.Sprintf("%d", listAll[i].Id),
+				fmt.Sprintf("newmachine-%d", i+1),
+			},
+			[]string{},
+		)
+		assert.Nil(s.T(), err)
+	}
+
+	listAllAfterRenameResult, err := ExecuteCommand(
+		&s.headscale,
+		[]string{
+			"headscale",
+			"nodes",
+			"list",
+			"--output",
+			"json",
+		},
+		[]string{},
+	)
+	assert.Nil(s.T(), err)
+
+	var listAllAfterRename []v1.Machine
+	err = json.Unmarshal([]byte(listAllAfterRenameResult), &listAllAfterRename)
+	assert.Nil(s.T(), err)
+
+	assert.Len(s.T(), listAllAfterRename, 5)
+
+	assert.Equal(s.T(), "newmachine-1", listAllAfterRename[0].GetGivenName())
+	assert.Equal(s.T(), "newmachine-2", listAllAfterRename[1].GetGivenName())
+	assert.Equal(s.T(), "newmachine-3", listAllAfterRename[2].GetGivenName())
+	assert.Contains(s.T(), listAllAfterRename[3].GetGivenName(), "machine-4")
+	assert.Contains(s.T(), listAllAfterRename[4].GetGivenName(), "machine-5")
+
+	// Test failure for too long names
+	result, err := ExecuteCommand(
+		&s.headscale,
+		[]string{
+			"headscale",
+			"nodes",
+			"rename",
+			"--identifier",
+			fmt.Sprintf("%d", listAll[4].Id),
+			"testmaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaachine12345678901234567890",
+		},
+		[]string{},
+	)
+	assert.Nil(s.T(), err)
+	assert.Contains(s.T(), result, "not be over 63 chars")
+
+	listAllAfterRenameAttemptResult, err := ExecuteCommand(
+		&s.headscale,
+		[]string{
+			"headscale",
+			"nodes",
+			"list",
+			"--output",
+			"json",
+		},
+		[]string{},
+	)
+	assert.Nil(s.T(), err)
+
+	var listAllAfterRenameAttempt []v1.Machine
+	err = json.Unmarshal(
+		[]byte(listAllAfterRenameAttemptResult),
+		&listAllAfterRenameAttempt,
+	)
+	assert.Nil(s.T(), err)
+
+	assert.Len(s.T(), listAllAfterRenameAttempt, 5)
+
+	assert.Equal(s.T(), "newmachine-1", listAllAfterRenameAttempt[0].GetGivenName())
+	assert.Equal(s.T(), "newmachine-2", listAllAfterRenameAttempt[1].GetGivenName())
+	assert.Equal(s.T(), "newmachine-3", listAllAfterRenameAttempt[2].GetGivenName())
+	assert.Contains(s.T(), listAllAfterRenameAttempt[3].GetGivenName(), "machine-4")
+	assert.Contains(s.T(), listAllAfterRenameAttempt[4].GetGivenName(), "machine-5")
+}
+
 func (s *IntegrationCLITestSuite) TestRouteCommand() {
 	namespace, err := s.createNamespace("routes-namespace")
 	assert.Nil(s.T(), err)

From 4aae917f74f08eacb26271c1072a0c23d754e8ac Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 16 May 2022 20:33:47 +0200
Subject: [PATCH 18/37] Require GivenName to be unique

---
 machine.go | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/machine.go b/machine.go
index 5b8a3ae..0193e8e 100644
--- a/machine.go
+++ b/machine.go
@@ -51,7 +51,7 @@ type Machine struct {
 	//
 	// GivenName is the name used in all DNS related
 	// parts of headscale.
-	GivenName   string
+	GivenName   string `gorm:"type:varchar(63);unique_index"`
 	NamespaceID uint
 	Namespace   Namespace `gorm:"foreignKey:NamespaceID"`
 

From 163e5c29e4cce5e6bf0e366a47fd3791a16a99ee Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 16 May 2022 20:35:35 +0200
Subject: [PATCH 19/37] fix trace log message

---
 grpcv1.go | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/grpcv1.go b/grpcv1.go
index 9c59389..11eaad5 100644
--- a/grpcv1.go
+++ b/grpcv1.go
@@ -241,8 +241,8 @@ func (api headscaleV1APIServer) RenameMachine(
 
 	log.Trace().
 		Str("machine", machine.Hostname).
-		Time("expiry", *machine.Expiry).
-		Msg("machine expired")
+		Str("new_name", request.GetNewName()).
+		Msg("machine renamed")
 
 	return &v1.RenameMachineResponse{Machine: machine.toProto()}, nil
 }

From 9ebeb3d7e4612d967a6df44c5c4d1486fd462a59 Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Tue, 17 May 2022 22:11:51 +0200
Subject: [PATCH 20/37] Retreive hostnames from headscale, now that they are
 random

---
 integration_common_test.go        | 36 +++++++++++++++++++++++++++++++
 integration_embedded_derp_test.go |  7 +++---
 2 files changed, 40 insertions(+), 3 deletions(-)

diff --git a/integration_common_test.go b/integration_common_test.go
index d5ff7e8..a7b948b 100644
--- a/integration_common_test.go
+++ b/integration_common_test.go
@@ -5,10 +5,12 @@ package headscale
 
 import (
 	"bytes"
+	"encoding/json"
 	"fmt"
 	"strings"
 	"time"
 
+	v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
 	"github.com/ory/dockertest/v3"
 	"github.com/ory/dockertest/v3/docker"
 	"inet.af/netaddr"
@@ -212,3 +214,37 @@ func getIPs(
 
 	return ips, nil
 }
+
+func getDNSNames(
+	headscale *dockertest.Resource,
+) ([]string, error) {
+
+	listAllResult, err := ExecuteCommand(
+		headscale,
+		[]string{
+			"headscale",
+			"nodes",
+			"list",
+			"--output",
+			"json",
+		},
+		[]string{},
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	var listAll []v1.Machine
+	err = json.Unmarshal([]byte(listAllResult), &listAll)
+	if err != nil {
+		return nil, err
+	}
+
+	hostnames := make([]string, len(listAll))
+
+	for index := range listAll {
+		hostnames[index] = listAll[index].GetGivenName()
+	}
+
+	return hostnames, nil
+}
diff --git a/integration_embedded_derp_test.go b/integration_embedded_derp_test.go
index 5e42fd7..69b427f 100644
--- a/integration_embedded_derp_test.go
+++ b/integration_embedded_derp_test.go
@@ -353,11 +353,12 @@ func (s *IntegrationDERPTestSuite) saveLog(
 }
 
 func (s *IntegrationDERPTestSuite) TestPingAllPeersByHostname() {
-	ips, err := getIPs(s.tailscales)
+	hostnames, err := getDNSNames(&s.headscale)
 	assert.Nil(s.T(), err)
+
 	for hostname, tailscale := range s.tailscales {
-		for peername := range ips {
-			if peername == hostname {
+		for _, peername := range hostnames {
+			if strings.Contains(peername, hostname) {
 				continue
 			}
 			s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) {

From 802eb931d1ffcddad29bf3393b1240a4b734adde Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Tue, 17 May 2022 22:02:18 +0200
Subject: [PATCH 21/37] Make sure givenname is set for preauthkeys

---
 api.go                            | 13 +++++++++++++
 integration_embedded_derp_test.go |  2 ++
 2 files changed, 15 insertions(+)

diff --git a/api.go b/api.go
index b4e3a7d..a47857d 100644
--- a/api.go
+++ b/api.go
@@ -601,8 +601,21 @@ func (h *Headscale) handleAuthKey(
 		h.RefreshMachine(machine, registerRequest.Expiry)
 	} else {
 		now := time.Now().UTC()
+
+		givenName, err := h.GenerateGivenName(registerRequest.Hostinfo.Hostname)
+		if err != nil {
+			log.Error().
+				Caller().
+				Str("func", "RegistrationHandler").
+				Str("hostinfo.name", registerRequest.Hostinfo.Hostname).
+				Err(err)
+
+			return
+		}
+
 		machineToRegister := Machine{
 			Hostname:       registerRequest.Hostinfo.Hostname,
+			GivenName:      givenName,
 			NamespaceID:    pak.Namespace.ID,
 			MachineKey:     machineKeyStr,
 			RegisterMethod: RegisterMethodAuthKey,
diff --git a/integration_embedded_derp_test.go b/integration_embedded_derp_test.go
index 69b427f..5f38869 100644
--- a/integration_embedded_derp_test.go
+++ b/integration_embedded_derp_test.go
@@ -356,6 +356,8 @@ func (s *IntegrationDERPTestSuite) TestPingAllPeersByHostname() {
 	hostnames, err := getDNSNames(&s.headscale)
 	assert.Nil(s.T(), err)
 
+	log.Printf("Hostnames: %#v\n", hostnames)
+
 	for hostname, tailscale := range s.tailscales {
 		for _, peername := range hostnames {
 			if strings.Contains(peername, hostname) {

From 4a9d3bedf95f199d64d9dc5775dc23e4c145db8b Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Wed, 18 May 2022 20:14:18 +0200
Subject: [PATCH 22/37] Use new names to resolve magic dns

---
 integration_test.go | 12 +++++++++---
 1 file changed, 9 insertions(+), 3 deletions(-)

diff --git a/integration_test.go b/integration_test.go
index da3bc8a..d1fb887 100644
--- a/integration_test.go
+++ b/integration_test.go
@@ -639,9 +639,13 @@ func (s *IntegrationTestSuite) TestMagicDNS() {
 	for namespace, scales := range s.namespaces {
 		ips, err := getIPs(scales.tailscales)
 		assert.Nil(s.T(), err)
+
+		hostnames, err := getDNSNames(&s.headscale)
+		assert.Nil(s.T(), err)
+
 		for hostname, tailscale := range scales.tailscales {
-			for peername, ips := range ips {
-				if peername == hostname {
+			for _, peername := range hostnames {
+				if strings.Contains(peername, hostname) {
 					continue
 				}
 				s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) {
@@ -663,7 +667,9 @@ func (s *IntegrationTestSuite) TestMagicDNS() {
 					assert.Nil(t, err)
 					log.Printf("Result for %s: %s\n", hostname, result)
 
-					for _, ip := range ips {
+					peerBaseName := peername[:len(peername)-MachineGivenNameHashLength-1]
+					expectedAddresses := ips[peerBaseName]
+					for _, ip := range expectedAddresses {
 						assert.Contains(t, result, ip.String())
 					}
 				})

From 77ceeaf5fd68ccfbaf02c4b1c85108dea51d6047 Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Wed, 18 May 2022 21:18:03 +0200
Subject: [PATCH 23/37] Test magic dns with the correct urls

---
 integration_common_test.go | 34 ++++++++++++++++++++++++++++++++++
 integration_test.go        | 14 ++++++++------
 2 files changed, 42 insertions(+), 6 deletions(-)

diff --git a/integration_common_test.go b/integration_common_test.go
index a7b948b..fa848ae 100644
--- a/integration_common_test.go
+++ b/integration_common_test.go
@@ -248,3 +248,37 @@ func getDNSNames(
 
 	return hostnames, nil
 }
+
+func getMagicFQDN(
+	headscale *dockertest.Resource,
+) ([]string, error) {
+
+	listAllResult, err := ExecuteCommand(
+		headscale,
+		[]string{
+			"headscale",
+			"nodes",
+			"list",
+			"--output",
+			"json",
+		},
+		[]string{},
+	)
+	if err != nil {
+		return nil, err
+	}
+
+	var listAll []v1.Machine
+	err = json.Unmarshal([]byte(listAllResult), &listAll)
+	if err != nil {
+		return nil, err
+	}
+
+	hostnames := make([]string, len(listAll))
+
+	for index := range listAll {
+		hostnames[index] = fmt.Sprintf("%s.%s.headscale.net", listAll[index].GetGivenName(), listAll[index].GetNamespace().GetName())
+	}
+
+	return hostnames, nil
+}
diff --git a/integration_test.go b/integration_test.go
index d1fb887..124b458 100644
--- a/integration_test.go
+++ b/integration_test.go
@@ -636,11 +636,13 @@ func (s *IntegrationTestSuite) TestPingAllPeersByHostname() {
 }
 
 func (s *IntegrationTestSuite) TestMagicDNS() {
-	for namespace, scales := range s.namespaces {
-		ips, err := getIPs(scales.tailscales)
-		assert.Nil(s.T(), err)
+	hostnames, err := getMagicFQDN(&s.headscale)
+	assert.Nil(s.T(), err)
 
-		hostnames, err := getDNSNames(&s.headscale)
+	log.Printf("Resolved hostnames: %#v", hostnames)
+
+	for _, scales := range s.namespaces {
+		ips, err := getIPs(scales.tailscales)
 		assert.Nil(s.T(), err)
 
 		for hostname, tailscale := range scales.tailscales {
@@ -648,10 +650,10 @@ func (s *IntegrationTestSuite) TestMagicDNS() {
 				if strings.Contains(peername, hostname) {
 					continue
 				}
+
 				s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) {
 					command := []string{
-						"tailscale", "ip",
-						fmt.Sprintf("%s.%s.headscale.net", peername, namespace),
+						"tailscale", "ip", peername,
 					}
 
 					log.Printf(

From 5fa987519d1a113de4f1d108e94751e25ec5b453 Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 23 May 2022 17:33:07 +0100
Subject: [PATCH 24/37] move populate to after when given_name exist

---
 db.go | 65 ++++++++++++++++++++++++++++++-----------------------------
 1 file changed, 33 insertions(+), 32 deletions(-)

diff --git a/db.go b/db.go
index 6eb0683..a24e46b 100644
--- a/db.go
+++ b/db.go
@@ -45,38 +45,6 @@ func (h *Headscale) initDB() error {
 	// the field is populated and normalized if it was not when the
 	// machine was registered.
 	_ = db.Migrator().RenameColumn(&Machine{}, "nickname", "given_name")
-	if db.Migrator().HasColumn(&Machine{}, "given_name") {
-		machines := Machines{}
-		if err := h.db.Find(&machines).Error; err != nil {
-			log.Error().Err(err).Msg("Error accessing db")
-		}
-
-		for _, machine := range machines {
-			if machine.GivenName == "" {
-				normalizedHostname, err := NormalizeToFQDNRules(
-					machine.Hostname,
-					h.cfg.OIDC.StripEmaildomain,
-				)
-				if err != nil {
-					log.Error().
-						Caller().
-						Str("hostname", machine.Hostname).
-						Err(err).
-						Msg("Failed to normalize machine hostname in DB migration")
-				}
-
-				err = h.RenameMachine(&machine, normalizedHostname)
-				if err != nil {
-					log.Error().
-						Caller().
-						Str("hostname", machine.Hostname).
-						Err(err).
-						Msg("Failed to save normalized machine name in DB migration")
-				}
-
-			}
-		}
-	}
 
 	// If the Machine table has a column for registered,
 	// find all occourences of "false" and drop them. Then
@@ -115,6 +83,39 @@ func (h *Headscale) initDB() error {
 		return err
 	}
 
+	if db.Migrator().HasColumn(&Machine{}, "given_name") {
+		machines := Machines{}
+		if err := h.db.Find(&machines).Error; err != nil {
+			log.Error().Err(err).Msg("Error accessing db")
+		}
+
+		for _, machine := range machines {
+			if machine.GivenName == "" {
+				normalizedHostname, err := NormalizeToFQDNRules(
+					machine.Hostname,
+					h.cfg.OIDC.StripEmaildomain,
+				)
+				if err != nil {
+					log.Error().
+						Caller().
+						Str("hostname", machine.Hostname).
+						Err(err).
+						Msg("Failed to normalize machine hostname in DB migration")
+				}
+
+				err = h.RenameMachine(&machine, normalizedHostname)
+				if err != nil {
+					log.Error().
+						Caller().
+						Str("hostname", machine.Hostname).
+						Err(err).
+						Msg("Failed to save normalized machine name in DB migration")
+				}
+
+			}
+		}
+	}
+
 	err = db.AutoMigrate(&KV{})
 	if err != nil {
 		return err

From 4f3f0542d43197ad3c1561b3668ba28072cc285a Mon Sep 17 00:00:00 2001
From: Juan Font Alonso <juanfontalonso@gmail.com>
Date: Sat, 28 May 2022 12:54:57 +0200
Subject: [PATCH 25/37] Fix some issues in testing with new hostname handling

---
 go.mod              | 15 ---------------
 go.sum              | 46 ---------------------------------------------
 integration_test.go | 20 +++++++++++---------
 utils.go            |  2 +-
 4 files changed, 12 insertions(+), 71 deletions(-)

diff --git a/go.mod b/go.mod
index aeafb7d..4d2f9b1 100644
--- a/go.mod
+++ b/go.mod
@@ -49,11 +49,9 @@ require (
 	github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74 // indirect
 	github.com/atomicgo/cursor v0.0.1 // indirect
 	github.com/beorn7/perks v1.0.1 // indirect
-	github.com/bufbuild/buf v1.4.0 // indirect
 	github.com/cenkalti/backoff/v4 v4.1.2 // indirect
 	github.com/cespare/xxhash/v2 v2.1.2 // indirect
 	github.com/containerd/continuity v0.0.0-20190827140505-75bee3e2ccb6 // indirect
-	github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
 	github.com/davecgh/go-spew v1.1.1 // indirect
 	github.com/docker/cli v20.10.11+incompatible // indirect
 	github.com/docker/docker v20.10.7+incompatible // indirect
@@ -65,9 +63,7 @@ require (
 	github.com/go-playground/locales v0.13.0 // indirect
 	github.com/go-playground/universal-translator v0.17.0 // indirect
 	github.com/go-playground/validator/v10 v10.4.1 // indirect
-	github.com/gofrs/flock v0.8.1 // indirect
 	github.com/gogo/protobuf v1.3.2 // indirect
-	github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
 	github.com/golang/protobuf v1.5.2 // indirect
 	github.com/google/go-cmp v0.5.7 // indirect
 	github.com/google/go-github v17.0.0+incompatible // indirect
@@ -87,16 +83,12 @@ require (
 	github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b // indirect
 	github.com/jackc/pgtype v1.11.0 // indirect
 	github.com/jackc/pgx/v4 v4.16.0 // indirect
-	github.com/jdxcode/netrc v0.0.0-20210204082910-926c7f70242a // indirect
-	github.com/jhump/protocompile v0.0.0-20220216033700-d705409f108f // indirect
-	github.com/jhump/protoreflect v1.12.1-0.20220417024638-438db461d753 // indirect
 	github.com/jinzhu/inflection v1.0.0 // indirect
 	github.com/jinzhu/now v1.1.4 // indirect
 	github.com/josharian/native v1.0.0 // indirect
 	github.com/jsimonetti/rtnetlink v1.1.2-0.20220408201609-d380b505068b // indirect
 	github.com/json-iterator/go v1.1.12 // indirect
 	github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect
-	github.com/klauspost/pgzip v1.2.5 // indirect
 	github.com/kr/pretty v0.3.0 // indirect
 	github.com/kr/text v0.2.0 // indirect
 	github.com/leodido/go-urn v1.2.0 // indirect
@@ -118,9 +110,7 @@ require (
 	github.com/opencontainers/runc v1.0.2 // indirect
 	github.com/pelletier/go-toml v1.9.4 // indirect
 	github.com/pelletier/go-toml/v2 v2.0.0-beta.8 // indirect
-	github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
 	github.com/pkg/errors v0.9.1 // indirect
-	github.com/pkg/profile v1.6.0 // indirect
 	github.com/pmezard/go-difflib v1.0.0 // indirect
 	github.com/prometheus/client_model v0.2.0 // indirect
 	github.com/prometheus/common v0.32.1 // indirect
@@ -128,7 +118,6 @@ require (
 	github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
 	github.com/rivo/uniseg v0.2.0 // indirect
 	github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 // indirect
-	github.com/russross/blackfriday/v2 v2.1.0 // indirect
 	github.com/sirupsen/logrus v1.8.1 // indirect
 	github.com/spf13/afero v1.8.2 // indirect
 	github.com/spf13/cast v1.4.1 // indirect
@@ -140,10 +129,6 @@ require (
 	github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
 	github.com/xeipuuv/gojsonschema v1.2.0 // indirect
 	github.com/xo/terminfo v0.0.0-20210125001918-ca9a967f8778 // indirect
-	go.opencensus.io v0.23.0 // indirect
-	go.uber.org/atomic v1.9.0 // indirect
-	go.uber.org/multierr v1.8.0 // indirect
-	go.uber.org/zap v1.21.0 // indirect
 	go4.org/intern v0.0.0-20211027215823-ae77deb06f29 // indirect
 	go4.org/mem v0.0.0-20210711025021-927187094b94 // indirect
 	go4.org/unsafe/assume-no-moving-gc v0.0.0-20211027215541-db492cf91b37 // indirect
diff --git a/go.sum b/go.sum
index 0f575e2..7ebdcd6 100644
--- a/go.sum
+++ b/go.sum
@@ -71,14 +71,11 @@ github.com/alexbrainman/sspi v0.0.0-20210105120005-909beea2cc74/go.mod h1:cEWa1L
 github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
 github.com/atomicgo/cursor v0.0.1 h1:xdogsqa6YYlLfM+GyClC/Lchf7aiMerFiZQn7soTOoU=
 github.com/atomicgo/cursor v0.0.1/go.mod h1:cBON2QmmrysudxNBFthvMtN32r3jxVRIvzkUiF/RuIk=
-github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
 github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
 github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
 github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
 github.com/bits-and-blooms/bitset v1.2.0/go.mod h1:gIdJ4wp64HaoK2YrL1Q5/N7Y16edYb8uY+O0FJTyyDA=
-github.com/bufbuild/buf v1.4.0 h1:GqE3a8CMmcFvWPzuY3Mahf9Kf3S9XgZ/ORpfYFzO+90=
-github.com/bufbuild/buf v1.4.0/go.mod h1:mwHG7klTHnX+rM/ym8LXGl7vYpVmnwT96xWoRB4H5QI=
 github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029 h1:POmUHfxXdeyM8Aomg4tKDcwATCFuW+cYLkj6pwsw9pc=
 github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029/go.mod h1:Rpr5n9cGHYdM3S3IK8ROSUUUYjQOu+MSUCZDcJbYWi8=
 github.com/cenkalti/backoff/v4 v4.1.2 h1:6Yo7N8UP2K6LWZnW94DLVSSrbobcWdVzAYOisuDPIFo=
@@ -114,7 +111,6 @@ github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7
 github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
-github.com/cpuguy83/go-md2man/v2 v2.0.1 h1:r/myEWzV9lfsM1tFLgDyu0atFtJ1fXn261LKYj/3DxU=
 github.com/cpuguy83/go-md2man/v2 v2.0.1/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
 github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY=
 github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
@@ -142,7 +138,6 @@ github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.m
 github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
 github.com/envoyproxy/go-control-plane v0.9.7/go.mod h1:cwu0lG7PUMfa9snN8LXBig5ynNVH9qI8YYLbd1fK2po=
 github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
-github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
 github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
 github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
@@ -181,8 +176,6 @@ github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7a
 github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
 github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
 github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
-github.com/gofrs/flock v0.8.1 h1:+gYjHKf32LDeiEEFhQaotPbLuUXjY5ZqxKgXy7n59aw=
-github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14jxHU=
 github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
 github.com/gofrs/uuid v4.2.0+incompatible h1:yyYWMnhkhrKwwr8gAOcOCYxOOscHgDS9yZgBrnJfGa0=
 github.com/gofrs/uuid v4.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
@@ -194,8 +187,6 @@ github.com/golang/glog v1.0.0 h1:nfP3RFugxnNRyKgeWd4oI1nYvXpxrx8ck8ZrcizshdQ=
 github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE=
-github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
 github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
 github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
@@ -332,16 +323,6 @@ github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0f
 github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
 github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
 github.com/jackc/puddle v1.2.1/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk=
-github.com/jdxcode/netrc v0.0.0-20210204082910-926c7f70242a h1:d4+I1YEKVmWZrgkt6jpXBnLgV2ZjO0YxEtLDdfIZfH4=
-github.com/jdxcode/netrc v0.0.0-20210204082910-926c7f70242a/go.mod h1:Zi/ZFkEqFHTm7qkjyNJjaWH4LQA9LQhGJyF0lTYGpxw=
-github.com/jhump/gopoet v0.0.0-20190322174617-17282ff210b3/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI=
-github.com/jhump/gopoet v0.1.0/go.mod h1:me9yfT6IJSlOL3FCfrg+L6yzUEZ+5jW6WHt4Sk+UPUI=
-github.com/jhump/goprotoc v0.5.0/go.mod h1:VrbvcYrQOrTi3i0Vf+m+oqQWk9l72mjkJCYo7UvLHRQ=
-github.com/jhump/protocompile v0.0.0-20220216033700-d705409f108f h1:BNuUg9k2EiJmlMwjoef3e8vZLHplbVw6DrjGFjLL+Yo=
-github.com/jhump/protocompile v0.0.0-20220216033700-d705409f108f/go.mod h1:qr2b5kx4HbFS7/g4uYO5qv9ei8303JMsC7ESbYiqr2Q=
-github.com/jhump/protoreflect v1.11.0/go.mod h1:U7aMIjN0NWq9swDP7xDdoMfRHb35uiuTd3Z9nFXJf5E=
-github.com/jhump/protoreflect v1.12.1-0.20220417024638-438db461d753 h1:uFlcJKZPLQd7rmOY/RrvBuUaYmAFnlFHKLivhO6cOy8=
-github.com/jhump/protoreflect v1.12.1-0.20220417024638-438db461d753/go.mod h1:JytZfP5d0r8pVNLZvai7U/MCuTWITgrI4tTg7puQFKI=
 github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
 github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
 github.com/jinzhu/now v1.1.4 h1:tHnRBy1i5F2Dh8BAFxqFzxKqqvezXrL2OW1TnX+Mlas=
@@ -371,8 +352,6 @@ github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa02
 github.com/klauspost/cpuid/v2 v2.0.10/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
 github.com/klauspost/cpuid/v2 v2.0.12 h1:p9dKCg8i4gmOxtv35DvrYoWqYzQrvEVdjQ762Y0OqZE=
 github.com/klauspost/cpuid/v2 v2.0.12/go.mod h1:g2LTdtYhdyuGPqyWyv7qRAmj1WBqxuObKfj5c0PQa7c=
-github.com/klauspost/pgzip v1.2.5 h1:qnWYvvKqedOF2ulHpMG72XQol4ILEJ8k2wwRl/Km8oE=
-github.com/klauspost/pgzip v1.2.5/go.mod h1:Ch1tH69qFZu15pkjo5kYi6mth2Zzwzt50oCQKQE9RUs=
 github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
@@ -441,7 +420,6 @@ github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjY
 github.com/mrunalp/fileutils v0.5.0/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ=
 github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
 github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
-github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
 github.com/opencontainers/go-digest v1.0.0-rc1 h1:WzifXhOVOEOuFYOJAW6aQqW0TooG2iki3E3Ii+WN7gQ=
 github.com/opencontainers/go-digest v1.0.0-rc1/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s=
 github.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=
@@ -461,15 +439,11 @@ github.com/pelletier/go-toml/v2 v2.0.0-beta.8 h1:dy81yyLYJDwMTifq24Oi/IslOslRrDS
 github.com/pelletier/go-toml/v2 v2.0.0-beta.8/go.mod h1:r9LEWfGN8R5k0VXJ+0BkIe7MYkRdwZOjgMj2KwnJFUo=
 github.com/philip-bui/grpc-zerolog v1.0.1 h1:EMacvLRUd2O1K0eWod27ZP5CY1iTNkhBDLSN+Q4JEvA=
 github.com/philip-bui/grpc-zerolog v1.0.1/go.mod h1:qXbiq/2X4ZUMMshsqlWyTHOcw7ns+GZmlqZZN05ZHcQ=
-github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 h1:KoWmjvw+nsYOo29YJK9vDA65RGE3NrOnUtO7a+RF9HU=
-github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8/go.mod h1:HKlIX3XHQyzLZPlr7++PzdhaXEj94dEiJgZDTsxEqUI=
 github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA=
 github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
-github.com/pkg/profile v1.6.0 h1:hUDfIISABYI59DyeB3OTay/HxSRwTQ8rB/H83k6r5dM=
-github.com/pkg/profile v1.6.0/go.mod h1:qBsxPvzyUincmltOk6iyRVxHYg4adc0OFOv72ZdLa18=
 github.com/pkg/sftp v1.13.1/go.mod h1:3HaPG6Dq1ILlpPZRO0HVMrsydcdLt6HRDccSgb87qRg=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
@@ -520,7 +494,6 @@ github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThC
 github.com/rs/zerolog v1.26.1 h1:/ihwxqH+4z8UxyI70wM1z9yCvkWcfz/a3mj48k/Zngc=
 github.com/rs/zerolog v1.26.1/go.mod h1:/wSSJWX7lVrsOwlbyTRSOJvqRlc+WjWlfes+CiJ+tmc=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
-github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
 github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
@@ -587,7 +560,6 @@ github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
 github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
 github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
-github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q=
 github.com/zsais/go-gin-prometheus v0.1.0 h1:bkLv1XCdzqVgQ36ScgRi09MA2UC1t3tAB6nsfErsGO4=
@@ -598,29 +570,18 @@ go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
 go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk=
-go.opencensus.io v0.23.0 h1:gqCw0LfLxScz8irSi8exQc7fyQ0fKQU/qnC/X8+V/1M=
-go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E=
 go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
 go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
 go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
 go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
-go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/atomic v1.9.0 h1:ECmE8Bn/WFTYwEW/bpKD3M8VtR/zQVbavAoalC1PYyE=
-go.uber.org/atomic v1.9.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
-go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
 go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
 go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
 go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
-go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
-go.uber.org/multierr v1.8.0 h1:dg6GjLku4EH+249NNmoIciG9N/jURbDG+pFlTkhzIC8=
-go.uber.org/multierr v1.8.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
 go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
 go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
 go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
-go.uber.org/zap v1.21.0 h1:WefMeulhovoZ2sYXz7st6K0sLj7bBhpiFaud4r4zST8=
-go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw=
 go4.org/intern v0.0.0-20211027215823-ae77deb06f29 h1:UXLjNohABv4S58tHmeuIZDO6e3mHpW2Dx33gaNt03LE=
 go4.org/intern v0.0.0-20211027215823-ae77deb06f29/go.mod h1:cS2ma+47FKrLPdXFpr7CuxiTW3eyJbWew4qx0qtQWDA=
 go4.org/mem v0.0.0-20210711025021-927187094b94 h1:OAAkygi2Js191AJP1Ds42MhJRgeofeKGjuoUqNp1QC4=
@@ -712,7 +673,6 @@ golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81R
 golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201031054903-ff519b6c9102/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
-golang.org/x/net v0.0.0-20201110031124-69a78807bb2b/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20201209123823-ac852fbbde11/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20201224014010-6772e930b67b/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
@@ -808,7 +768,6 @@ golang.org/x/sys v0.0.0-20210426230700-d19ff857e887/go.mod h1:h1NjWce9XRLGQEsW7w
 golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20210616045830-e2b7044e8c71/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20210902050250-f475640dd07b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
@@ -828,7 +787,6 @@ golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9sn
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
 golang.org/x/term v0.0.0-20210615171337-6886f2dfbf5b/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/term v0.0.0-20220411215600-e5f449aeb171 h1:EH1Deb8WZJ0xc0WK//leUHXcX9aLE5SymusoTmMZye8=
 golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@@ -903,7 +861,6 @@ golang.org/x/tools v0.0.0-20210105154028-b0ab187a4818/go.mod h1:emZCQorbCU4vsT4f
 golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.0.0-20210108195828-e2f9c7f1fc8e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
 golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
-golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
@@ -1000,7 +957,6 @@ google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv
 google.golang.org/grpc v1.34.0/go.mod h1:WotjhfgOW/POjDeRt8vscBtXq+2VjORFy659qA51WJ8=
 google.golang.org/grpc v1.35.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
 google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
-google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQdJfM=
 google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ=
 google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8=
 google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk=
@@ -1016,7 +972,6 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj
 google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
 google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
 google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
-google.golang.org/protobuf v1.27.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
 google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw=
 google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
@@ -1024,7 +979,6 @@ gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLks
 gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
-gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
 gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
 gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
diff --git a/integration_test.go b/integration_test.go
index 124b458..18f28b2 100644
--- a/integration_test.go
+++ b/integration_test.go
@@ -501,7 +501,6 @@ func (s *IntegrationTestSuite) TestTailDrop() {
 	for _, scales := range s.namespaces {
 		ips, err := getIPs(scales.tailscales)
 		assert.Nil(s.T(), err)
-		assert.Nil(s.T(), err)
 
 		retry := func(times int, sleepInverval time.Duration, doWork func() error) (err error) {
 			for attempts := 0; attempts < times; attempts++ {
@@ -531,7 +530,7 @@ func (s *IntegrationTestSuite) TestTailDrop() {
 					command := []string{
 						"tailscale", "file", "cp",
 						fmt.Sprintf("/tmp/file_from_%s", hostname),
-						fmt.Sprintf("%s:", peername),
+						fmt.Sprintf("%s:", ips[peername][1]),
 					}
 					retry(10, 1*time.Second, func() error {
 						log.Printf(
@@ -576,7 +575,7 @@ func (s *IntegrationTestSuite) TestTailDrop() {
 					log.Printf(
 						"Checking file in %s (%s) from %s (%s)\n",
 						hostname,
-						ips[hostname],
+						ips[hostname][1],
 						peername,
 						ip,
 					)
@@ -599,21 +598,24 @@ func (s *IntegrationTestSuite) TestTailDrop() {
 }
 
 func (s *IntegrationTestSuite) TestPingAllPeersByHostname() {
-	for namespace, scales := range s.namespaces {
-		ips, err := getIPs(scales.tailscales)
-		assert.Nil(s.T(), err)
+	hostnames, err := getMagicFQDN(&s.headscale)
+	assert.Nil(s.T(), err)
+
+	log.Printf("Resolved hostnames: %#v", hostnames)
+	for _, scales := range s.namespaces {
 		for hostname, tailscale := range scales.tailscales {
-			for peername := range ips {
-				if peername == hostname {
+			for _, peername := range hostnames {
+				if strings.Contains(peername, hostname) {
 					continue
 				}
+
 				s.T().Run(fmt.Sprintf("%s-%s", hostname, peername), func(t *testing.T) {
 					command := []string{
 						"tailscale", "ping",
 						"--timeout=10s",
 						"--c=20",
 						"--until-direct=true",
-						fmt.Sprintf("%s.%s.headscale.net", peername, namespace),
+						peername,
 					}
 
 					log.Printf(
diff --git a/utils.go b/utils.go
index 38e9466..e7a7a73 100644
--- a/utils.go
+++ b/utils.go
@@ -320,7 +320,7 @@ func GenerateRandomStringURLSafe(n int) (string, error) {
 func GenerateRandomStringDNSSafe(n int) (string, error) {
 	str, err := GenerateRandomStringURLSafe(n)
 
-	str = strings.ReplaceAll(str, "_", "-")
+	str = strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(str, "_", ""), "-", ""))
 
 	return str[:n], err
 }

From fc502e1e792b526b731cc6a0d887ec12e8c5c33c Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 30 May 2022 11:13:31 +0200
Subject: [PATCH 26/37] Update golines and fix go mod checksum

---
 flake.nix | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/flake.nix b/flake.nix
index b0856be..4818ba0 100644
--- a/flake.nix
+++ b/flake.nix
@@ -22,7 +22,7 @@
           golines =
             pkgs.buildGoModule rec {
               pname = "golines";
-              version = "0.9.0";
+              version = "0.10.0";
 
               src = pkgs.fetchFromGitHub {
                 owner = "segmentio";
@@ -63,7 +63,7 @@
 
               # When updating go.mod or go.sum, a new sha will need to be calculated,
               # update this if you have a mismatch after doing a change to thos files.
-              vendorSha256 = "sha256-bYEN0Rz7D1oJIIUjAHxdPB0CkVlb91f1lIQbucLnirg=";
+              vendorSha256 = "sha256-hZXNbbPy1XcCd3t2jBRMcvrMnCDcUpROK+NZYCXx6XY=";
 
               ldflags = [ "-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}" ];
             };

From 59a1a85a2b57c674074926738893d11fe444be88 Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 30 May 2022 11:49:35 +0200
Subject: [PATCH 27/37] Change to a go generics set implementation, no more
 casting :tada:

---
 dns.go     | 12 +++---------
 flake.nix  |  2 +-
 go.mod     |  2 +-
 go.sum     |  4 ++--
 machine.go | 13 +++----------
 5 files changed, 10 insertions(+), 23 deletions(-)

diff --git a/dns.go b/dns.go
index 9a91cff..be7c87c 100644
--- a/dns.go
+++ b/dns.go
@@ -4,7 +4,7 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/fatih/set"
+	mapset "github.com/deckarep/golang-set/v2"
 	"inet.af/netaddr"
 	"tailscale.com/tailcfg"
 	"tailscale.com/util/dnsname"
@@ -170,18 +170,12 @@ func getMapResponseDNSConfig(
 			),
 		)
 
-		namespaceSet := set.New(set.ThreadSafe)
+		namespaceSet := mapset.NewSet[Namespace]()
 		namespaceSet.Add(machine.Namespace)
 		for _, p := range peers {
 			namespaceSet.Add(p.Namespace)
 		}
-		for _, ns := range namespaceSet.List() {
-			namespace, ok := ns.(Namespace)
-			if !ok {
-				dnsConfig = dnsConfigOrig
-
-				continue
-			}
+		for _, namespace := range namespaceSet.ToSlice() {
 			dnsRoute := fmt.Sprintf("%v.%v", namespace.Name, baseDomain)
 			dnsConfig.Routes[dnsRoute] = nil
 		}
diff --git a/flake.nix b/flake.nix
index 4818ba0..5d45535 100644
--- a/flake.nix
+++ b/flake.nix
@@ -63,7 +63,7 @@
 
               # When updating go.mod or go.sum, a new sha will need to be calculated,
               # update this if you have a mismatch after doing a change to thos files.
-              vendorSha256 = "sha256-hZXNbbPy1XcCd3t2jBRMcvrMnCDcUpROK+NZYCXx6XY=";
+              vendorSha256 = "sha256-1yFvqQvsJLmjQn2RLDjwvyiZVDXv0dg/V+UUDvwk6Hg=";
 
               ldflags = [ "-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}" ];
             };
diff --git a/go.mod b/go.mod
index 4d2f9b1..69510a5 100644
--- a/go.mod
+++ b/go.mod
@@ -6,8 +6,8 @@ require (
 	github.com/AlecAivazis/survey/v2 v2.3.4
 	github.com/ccding/go-stun/stun v0.0.0-20200514191101-4dc67bcdb029
 	github.com/coreos/go-oidc/v3 v3.1.0
+	github.com/deckarep/golang-set/v2 v2.1.0
 	github.com/efekarakus/termcolor v1.0.1
-	github.com/fatih/set v0.2.1
 	github.com/gin-gonic/gin v1.7.7
 	github.com/glebarez/sqlite v1.4.3
 	github.com/gofrs/uuid v4.2.0+incompatible
diff --git a/go.sum b/go.sum
index 7ebdcd6..85c6034 100644
--- a/go.sum
+++ b/go.sum
@@ -121,6 +121,8 @@ github.com/cyphar/filepath-securejoin v0.2.2/go.mod h1:FpkQEhXnPnOthhzymB7CGsFk2
 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/deckarep/golang-set/v2 v2.1.0 h1:g47V4Or+DUdzbs8FxCCmgb6VYd+ptPAngjM6dtGktsI=
+github.com/deckarep/golang-set/v2 v2.1.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4=
 github.com/docker/cli v20.10.11+incompatible h1:tXU1ezXcruZQRrMP8RN2z9N91h+6egZTS1gsPsKantc=
 github.com/docker/cli v20.10.11+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=
 github.com/docker/docker v20.10.7+incompatible h1:Z6O9Nhsjv+ayUEeI1IojKbYcsGdgYSNqxe1s2MYzUhQ=
@@ -141,8 +143,6 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m
 github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
 github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
 github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
-github.com/fatih/set v0.2.1 h1:nn2CaJyknWE/6txyUDGwysr3G5QC6xWB/PtVjPBbeaA=
-github.com/fatih/set v0.2.1/go.mod h1:+RKtMCH+favT2+3YecHGxcc0b4KyVWA1QWWJUs4E0CI=
 github.com/frankban/quicktest v1.11.3/go.mod h1:wRf/ReqHper53s+kmmSZizM8NamnL3IM0I9ntUbOk+k=
 github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss=
 github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
diff --git a/machine.go b/machine.go
index 28a49a3..3f37909 100644
--- a/machine.go
+++ b/machine.go
@@ -9,7 +9,7 @@ import (
 	"strings"
 	"time"
 
-	"github.com/fatih/set"
+	mapset "github.com/deckarep/golang-set/v2"
 	v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
 	"github.com/rs/zerolog/log"
 	"google.golang.org/protobuf/types/known/timestamppb"
@@ -469,17 +469,10 @@ func (h *Headscale) isOutdated(machine *Machine) bool {
 		return true
 	}
 
-	namespaceSet := set.New(set.ThreadSafe)
+	namespaceSet := mapset.NewSet[string]()
 	namespaceSet.Add(machine.Namespace.Name)
 
-	namespaces := make([]string, namespaceSet.Size())
-	for index, namespace := range namespaceSet.List() {
-		if name, ok := namespace.(string); ok {
-			namespaces[index] = name
-		}
-	}
-
-	lastChange := h.getLastStateChange(namespaces...)
+	lastChange := h.getLastStateChange(namespaceSet.ToSlice()...)
 	lastUpdate := machine.CreatedAt
 	if machine.LastSuccessfulUpdate != nil {
 		lastUpdate = *machine.LastSuccessfulUpdate

From 5316dd9c27bfa623b2936e764876e40c8eb8fe95 Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 30 May 2022 11:59:22 +0200
Subject: [PATCH 28/37] Use new nix stable (22.05)

---
 flake.lock | 13 +++++++------
 flake.nix  |  5 +----
 2 files changed, 8 insertions(+), 10 deletions(-)

diff --git a/flake.lock b/flake.lock
index 7368f5f..85ff470 100644
--- a/flake.lock
+++ b/flake.lock
@@ -17,17 +17,18 @@
     },
     "nixpkgs": {
       "locked": {
-        "lastModified": 1647536224,
-        "narHash": "sha256-SUIiz4DhMXgM7i+hvFWmLnhywr1WeRGIz+EIbwQQguM=",
+        "lastModified": 1653733789,
+        "narHash": "sha256-VIYazYCWNvcFNns2XQkHx/mVmCZ3oebZv8W2LS1gLQE=",
         "owner": "NixOS",
         "repo": "nixpkgs",
-        "rev": "dd8cebebbf0f9352501f251ac37b851d947f92dc",
+        "rev": "d1086907f56c5a6c33c0c2e8dc9f42ef6988294f",
         "type": "github"
       },
       "original": {
-        "id": "nixpkgs",
-        "ref": "master",
-        "type": "indirect"
+        "owner": "NixOS",
+        "ref": "nixos-22.05",
+        "repo": "nixpkgs",
+        "type": "github"
       }
     },
     "root": {
diff --git a/flake.nix b/flake.nix
index 5d45535..43efacb 100644
--- a/flake.nix
+++ b/flake.nix
@@ -2,10 +2,7 @@
   description = "headscale - Open Source Tailscale Control server";
 
   inputs = {
-    # TODO: Use unstable when Go 1.18 has made it in
-    # https://nixpk.gs/pr-tracker.html?pr=164292
-    # nixpkgs.url = "nixpkgs/nixpkgs-unstable";
-    nixpkgs.url = "nixpkgs/master";
+    nixpkgs.url = "github:NixOS/nixpkgs/nixos-22.05";
     flake-utils.url = "github:numtide/flake-utils";
   };
 

From 3a3fc0a4bee14c63bcbd29ce22d9adbc2db3b82e Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 30 May 2022 12:03:16 +0200
Subject: [PATCH 29/37] Update headscale checksum

---
 flake.nix | 26 +++++++++++++-------------
 1 file changed, 13 insertions(+), 13 deletions(-)

diff --git a/flake.nix b/flake.nix
index 43efacb..f9e97e6 100644
--- a/flake.nix
+++ b/flake.nix
@@ -16,6 +16,19 @@
           pkgs = nixpkgs.legacyPackages.${prev.system};
         in
         rec {
+          headscale =
+            pkgs.buildGo118Module rec {
+              pname = "headscale";
+              version = headscaleVersion;
+              src = pkgs.lib.cleanSource self;
+
+              # When updating go.mod or go.sum, a new sha will need to be calculated,
+              # update this if you have a mismatch after doing a change to thos files.
+              vendorSha256 = "sha256-e3s10NsadWTEk7H7SbEvePhihJ1x2dS9UfIqPNIbJqI=";
+
+              ldflags = [ "-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}" ];
+            };
+
           golines =
             pkgs.buildGoModule rec {
               pname = "golines";
@@ -51,19 +64,6 @@
 
               subPackages = [ "protoc-gen-grpc-gateway" "protoc-gen-openapiv2" ];
             };
-
-          headscale =
-            pkgs.buildGo118Module rec {
-              pname = "headscale";
-              version = headscaleVersion;
-              src = pkgs.lib.cleanSource self;
-
-              # When updating go.mod or go.sum, a new sha will need to be calculated,
-              # update this if you have a mismatch after doing a change to thos files.
-              vendorSha256 = "sha256-1yFvqQvsJLmjQn2RLDjwvyiZVDXv0dg/V+UUDvwk6Hg=";
-
-              ldflags = [ "-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}" ];
-            };
         };
     } // flake-utils.lib.eachDefaultSystem
       (system:

From 9993f51b5eb34ac79b5b5e922d176045e5f33929 Mon Sep 17 00:00:00 2001
From: "github-actions[bot]"
 <41898282+github-actions[bot]@users.noreply.github.com>
Date: Sun, 29 May 2022 14:49:25 +0000
Subject: [PATCH 30/37] docs(README): update contributors

---
 README.md | 25 ++++++++++++++++---------
 1 file changed, 16 insertions(+), 9 deletions(-)

diff --git a/README.md b/README.md
index 57cd122..a00b41a 100644
--- a/README.md
+++ b/README.md
@@ -320,6 +320,13 @@ make build
             <sub style="font-size:14px"><b>Casey Marshall</b></sub>
         </a>
     </td>
+    <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
+        <a href=https://github.com/pvinis>
+            <img src=https://avatars.githubusercontent.com/u/100233?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Pavlos Vinieratos/>
+            <br />
+            <sub style="font-size:14px"><b>Pavlos Vinieratos</b></sub>
+        </a>
+    </td>
     <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
         <a href=https://github.com/SilverBut>
             <img src=https://avatars.githubusercontent.com/u/6560655?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Silver Bullet/>
@@ -327,6 +334,8 @@ make build
             <sub style="font-size:14px"><b>Silver Bullet</b></sub>
         </a>
     </td>
+</tr>
+<tr>
     <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
         <a href=https://github.com/majst01>
             <img src=https://avatars.githubusercontent.com/u/410110?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Stefan Majer/>
@@ -334,8 +343,6 @@ make build
             <sub style="font-size:14px"><b>Stefan Majer</b></sub>
         </a>
     </td>
-</tr>
-<tr>
     <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
         <a href=https://github.com/lachy2849>
             <img src=https://avatars.githubusercontent.com/u/98844035?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=lachy2849/>
@@ -371,13 +378,6 @@ make build
             <sub style="font-size:14px"><b>Aofei Sheng</b></sub>
         </a>
     </td>
-    <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
-        <a href=https://github.com/aofei>
-            <img src=https://avatars.githubusercontent.com/u/5037285?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=Aofei Sheng/>
-            <br />
-            <sub style="font-size:14px"><b>Aofei Sheng</b></sub>
-        </a>
-    </td>
 </tr>
 <tr>
     <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
@@ -584,5 +584,12 @@ make build
             <sub style="font-size:14px"><b>Wakeful-Cloud</b></sub>
         </a>
     </td>
+    <td align="center" style="word-wrap: break-word; width: 150.0; height: 150.0">
+        <a href=https://github.com/xpzouying>
+            <img src=https://avatars.githubusercontent.com/u/3946563?v=4 width="100;"  style="border-radius:50%;align-items:center;justify-content:center;overflow:hidden;padding-top:10px" alt=zy/>
+            <br />
+            <sub style="font-size:14px"><b>zy</b></sub>
+        </a>
+    </td>
 </tr>
 </table>

From dbc1d981c9830db605270594a314ca22da20b47b Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 30 May 2022 12:07:00 +0200
Subject: [PATCH 31/37] Revert golines

---
 flake.nix | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/flake.nix b/flake.nix
index f9e97e6..cbdeaf7 100644
--- a/flake.nix
+++ b/flake.nix
@@ -32,7 +32,7 @@
           golines =
             pkgs.buildGoModule rec {
               pname = "golines";
-              version = "0.10.0";
+              version = "0.9.0";
 
               src = pkgs.fetchFromGitHub {
                 owner = "segmentio";

From a992840c9bea0105dd5ea45f730968a569b1e9a8 Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 30 May 2022 12:18:23 +0200
Subject: [PATCH 32/37] Give UpdateMachine a more meaningful name

---
 machine.go | 6 +++---
 poll.go    | 8 ++++----
 2 files changed, 7 insertions(+), 7 deletions(-)

diff --git a/machine.go b/machine.go
index 3f37909..d1ef246 100644
--- a/machine.go
+++ b/machine.go
@@ -362,9 +362,9 @@ func (h *Headscale) GetMachineByMachineKey(
 	return &m, nil
 }
 
-// UpdateMachine takes a Machine struct pointer (typically already loaded from database
+// UpdateMachineFromDatabase takes a Machine struct pointer (typically already loaded from database
 // and updates it with the latest data from the database.
-func (h *Headscale) UpdateMachine(machine *Machine) error {
+func (h *Headscale) UpdateMachineFromDatabase(machine *Machine) error {
 	if result := h.db.Find(machine).First(&machine); result.Error != nil {
 		return result.Error
 	}
@@ -463,7 +463,7 @@ func (machine *Machine) GetHostInfo() tailcfg.Hostinfo {
 }
 
 func (h *Headscale) isOutdated(machine *Machine) bool {
-	if err := h.UpdateMachine(machine); err != nil {
+	if err := h.UpdateMachineFromDatabase(machine); err != nil {
 		// It does not seem meaningful to propagate this error as the end result
 		// will have to be that the machine has to be considered outdated.
 		return true
diff --git a/poll.go b/poll.go
index dcadadf..a74d857 100644
--- a/poll.go
+++ b/poll.go
@@ -319,7 +319,7 @@ func (h *Headscale) PollNetMapStream(
 				// TODO(kradalby): Abstract away all the database calls, this can cause race conditions
 				// when an outdated machine object is kept alive, e.g. db is update from
 				// command line, but then overwritten.
-			err = h.UpdateMachine(machine)
+			err = h.UpdateMachineFromDatabase(machine)
 			if err != nil {
 				log.Error().
 					Str("handler", "PollNetMapStream").
@@ -385,7 +385,7 @@ func (h *Headscale) PollNetMapStream(
 				// TODO(kradalby): Abstract away all the database calls, this can cause race conditions
 				// when an outdated machine object is kept alive, e.g. db is update from
 				// command line, but then overwritten.
-			err = h.UpdateMachine(machine)
+			err = h.UpdateMachineFromDatabase(machine)
 			if err != nil {
 				log.Error().
 					Str("handler", "PollNetMapStream").
@@ -475,7 +475,7 @@ func (h *Headscale) PollNetMapStream(
 				// TODO(kradalby): Abstract away all the database calls, this can cause race conditions
 				// when an outdated machine object is kept alive, e.g. db is update from
 				// command line, but then overwritten.
-				err = h.UpdateMachine(machine)
+				err = h.UpdateMachineFromDatabase(machine)
 				if err != nil {
 					log.Error().
 						Str("handler", "PollNetMapStream").
@@ -526,7 +526,7 @@ func (h *Headscale) PollNetMapStream(
 				// TODO: Abstract away all the database calls, this can cause race conditions
 				// when an outdated machine object is kept alive, e.g. db is update from
 				// command line, but then overwritten.
-			err := h.UpdateMachine(machine)
+			err := h.UpdateMachineFromDatabase(machine)
 			if err != nil {
 				log.Error().
 					Str("handler", "PollNetMapStream").

From a443255b3e5e0b14f50de11a1476ff9cca98dc06 Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 30 May 2022 12:39:41 +0200
Subject: [PATCH 33/37] Validate isOutdated against all namespaces

This commit makes isOutdated validate a nodes necessity to update
against all namespaces, and not just the nodes own namespace (which made
more sense before).

getLastStateChange is now uses the passed namespaces as a filter,
meaning that not requesting any namespace will give you the total last
updated state.

In addition, the sync.Map is exchanged for a variant that uses generics
which allows us to remove some casting logic.
---
 app.go     | 24 ++++++++++++++++++------
 flake.nix  |  2 +-
 go.mod     |  1 +
 go.sum     |  2 ++
 machine.go | 11 ++++++-----
 5 files changed, 28 insertions(+), 12 deletions(-)

diff --git a/app.go b/app.go
index ebe8eb6..c0f18de 100644
--- a/app.go
+++ b/app.go
@@ -25,6 +25,7 @@ import (
 	v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
 	"github.com/patrickmn/go-cache"
 	zerolog "github.com/philip-bui/grpc-zerolog"
+	"github.com/puzpuzpuz/xsync"
 	zl "github.com/rs/zerolog"
 	"github.com/rs/zerolog/log"
 	ginprometheus "github.com/zsais/go-gin-prometheus"
@@ -160,7 +161,7 @@ type Headscale struct {
 	aclPolicy *ACLPolicy
 	aclRules  []tailcfg.FilterRule
 
-	lastStateChange sync.Map
+	lastStateChange *xsync.MapOf[time.Time]
 
 	oidcProvider *oidc.Provider
 	oauth2Config *oauth2.Config
@@ -793,18 +794,29 @@ func (h *Headscale) getTLSSettings() (*tls.Config, error) {
 func (h *Headscale) setLastStateChangeToNow(namespace string) {
 	now := time.Now().UTC()
 	lastStateUpdate.WithLabelValues("", "headscale").Set(float64(now.Unix()))
+	if h.lastStateChange == nil {
+		h.lastStateChange = xsync.NewMapOf[time.Time]()
+	}
 	h.lastStateChange.Store(namespace, now)
 }
 
 func (h *Headscale) getLastStateChange(namespaces ...string) time.Time {
 	times := []time.Time{}
 
-	for _, namespace := range namespaces {
-		if wrapped, ok := h.lastStateChange.Load(namespace); ok {
-			lastChange, _ := wrapped.(time.Time)
-
-			times = append(times, lastChange)
+	// getLastStateChange takes a list of namespaces as a "filter", if no namespaces
+	// are past, then use the entier list of namespaces and look for the last update
+	if len(namespaces) > 0 {
+		for _, namespace := range namespaces {
+			if lastChange, ok := h.lastStateChange.Load(namespace); ok {
+				times = append(times, lastChange)
+			}
 		}
+	} else {
+		h.lastStateChange.Range(func(key string, value time.Time) bool {
+			times = append(times, value)
+
+			return true
+		})
 	}
 
 	sort.Slice(times, func(i, j int) bool {
diff --git a/flake.nix b/flake.nix
index cbdeaf7..74aa665 100644
--- a/flake.nix
+++ b/flake.nix
@@ -24,7 +24,7 @@
 
               # When updating go.mod or go.sum, a new sha will need to be calculated,
               # update this if you have a mismatch after doing a change to thos files.
-              vendorSha256 = "sha256-e3s10NsadWTEk7H7SbEvePhihJ1x2dS9UfIqPNIbJqI=";
+              vendorSha256 = "sha256-b6qPOO/NmcXsAsSRWZlYXZKyRAF++DsL4TEZzRhQhME=";
 
               ldflags = [ "-s" "-w" "-X github.com/juanfont/headscale/cmd/headscale/cli.Version=v${version}" ];
             };
diff --git a/go.mod b/go.mod
index 69510a5..2d22403 100644
--- a/go.mod
+++ b/go.mod
@@ -115,6 +115,7 @@ require (
 	github.com/prometheus/client_model v0.2.0 // indirect
 	github.com/prometheus/common v0.32.1 // indirect
 	github.com/prometheus/procfs v0.7.3 // indirect
+	github.com/puzpuzpuz/xsync v1.2.1 // indirect
 	github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 // indirect
 	github.com/rivo/uniseg v0.2.0 // indirect
 	github.com/rogpeppe/go-internal v1.8.1-0.20211023094830-115ce09fd6b4 // indirect
diff --git a/go.sum b/go.sum
index 85c6034..eaf15b8 100644
--- a/go.sum
+++ b/go.sum
@@ -478,6 +478,8 @@ github.com/pterm/pterm v0.12.36/go.mod h1:NjiL09hFhT/vWjQHSj1athJpx6H8cjpHXNAK5b
 github.com/pterm/pterm v0.12.40/go.mod h1:ffwPLwlbXxP+rxT0GsgDTzS3y3rmpAO1NMjUkGTYf8s=
 github.com/pterm/pterm v0.12.41 h1:e2BRfFo1H9nL8GY0S3ImbZqfZ/YimOk9XtkhoobKJVs=
 github.com/pterm/pterm v0.12.41/go.mod h1:LW/G4J2A42XlTaPTAGRPvbBfF4UXvHWhC6SN7ueU4jU=
+github.com/puzpuzpuz/xsync v1.2.1 h1:faRb6HT9XN3IAhnE7IP0TnPpokPK42qFKXkhQVkWNwM=
+github.com/puzpuzpuz/xsync v1.2.1/go.mod h1:K98BYhX3k1dQ2M63t1YNVDanbwUPmBCAhNmVrrxfiGg=
 github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0 h1:OdAsTTz6OkFY5QxjkYwrChwuRruF69c169dPK26NUlk=
 github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
 github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
diff --git a/machine.go b/machine.go
index d1ef246..db2c63f 100644
--- a/machine.go
+++ b/machine.go
@@ -9,7 +9,6 @@ import (
 	"strings"
 	"time"
 
-	mapset "github.com/deckarep/golang-set/v2"
 	v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
 	"github.com/rs/zerolog/log"
 	"google.golang.org/protobuf/types/known/timestamppb"
@@ -469,10 +468,12 @@ func (h *Headscale) isOutdated(machine *Machine) bool {
 		return true
 	}
 
-	namespaceSet := mapset.NewSet[string]()
-	namespaceSet.Add(machine.Namespace.Name)
-
-	lastChange := h.getLastStateChange(namespaceSet.ToSlice()...)
+	// Get the last update from all headscale namespaces to compare with our nodes
+	// last update.
+	// TODO(kradalby): Only request updates from namespaces where we can talk to nodes
+	// This would mostly be for a bit of performance, and can be calculated based on
+	// ACLs.
+	lastChange := h.getLastStateChange()
 	lastUpdate := machine.CreatedAt
 	if machine.LastSuccessfulUpdate != nil {
 		lastUpdate = *machine.LastSuccessfulUpdate

From 4ffd3eacb040c86e71077c4b3e9393aaabbc62a2 Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 30 May 2022 13:05:09 +0200
Subject: [PATCH 34/37] Override golangci-lint to use go 1.17

---
 flake.nix | 23 +++++++++++++++++++++++
 1 file changed, 23 insertions(+)

diff --git a/flake.nix b/flake.nix
index 74aa665..75bf676 100644
--- a/flake.nix
+++ b/flake.nix
@@ -46,6 +46,29 @@
               nativeBuildInputs = [ pkgs.installShellFiles ];
             };
 
+          golangci-lint = prev.golangci-lint.override {
+            # Override https://github.com/NixOS/nixpkgs/pull/166801 which changed this
+            # to buildGo118Module because it does not build on Darwin.
+            inherit (prev) buildGoModule;
+          };
+
+          # golangci-lint =
+          #   pkgs.buildGo117Module rec {
+          #     pname = "golangci-lint";
+          #     version = "1.46.2";
+          #
+          #     src = pkgs.fetchFromGitHub {
+          #       owner = "golangci";
+          #       repo = "golangci-lint";
+          #       rev = "v${version}";
+          #       sha256 = "sha256-7sDAwWz+qoB/ngeH35tsJ5FZUfAQvQsU6kU9rUHIHMk=";
+          #     };
+          #
+          #     vendorSha256 = "sha256-w38OKN6HPoz37utG/2QSPMai55IRDXCIIymeMe6ogIU=";
+          #
+          #     nativeBuildInputs = [ pkgs.installShellFiles ];
+          #   };
+
           protoc-gen-grpc-gateway =
             pkgs.buildGoModule rec {
               pname = "grpc-gateway";

From 266aac9e6197a11e4ab97a40a924d187931d2dda Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 30 May 2022 13:27:57 +0200
Subject: [PATCH 35/37] Update CHANGELOG

---
 CHANGELOG.md | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index ad78da0..4b2ea44 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -13,6 +13,10 @@
 - Add command to set tags on a node [#525](https://github.com/juanfont/headscale/issues/525)
 - Add command to view tags of nodes [#356](https://github.com/juanfont/headscale/issues/356)
 - Add --all (-a) flag to enable routes command [#360](https://github.com/juanfont/headscale/issues/360)
+- Fix issue where nodes was not updated across namespaces [#560](https://github.com/juanfont/headscale/pull/560)
+- Add the ability to rename a nodes name [#560](https://github.com/juanfont/headscale/pull/560)
+  - Node DNS names are now unique, a random suffix will be added when a node joins
+  - This change contains database changes, remember to **backup** your database before upgrading
 
 ## 0.15.0 (2022-03-20)
 

From d860270733e5441b47d773c2d111b5d9d5b52f3f Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Mon, 30 May 2022 16:10:39 +0200
Subject: [PATCH 36/37] Use Prometheus duration parser (support days and weeks)

---
 cmd/headscale/cli/api_key.go     | 23 ++++++++++++++++++-----
 cmd/headscale/cli/preauthkeys.go | 23 ++++++++++++++++++-----
 2 files changed, 36 insertions(+), 10 deletions(-)

diff --git a/cmd/headscale/cli/api_key.go b/cmd/headscale/cli/api_key.go
index aa056c5..d97cefa 100644
--- a/cmd/headscale/cli/api_key.go
+++ b/cmd/headscale/cli/api_key.go
@@ -7,6 +7,7 @@ import (
 
 	"github.com/juanfont/headscale"
 	v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
+	"github.com/prometheus/common/model"
 	"github.com/pterm/pterm"
 	"github.com/rs/zerolog/log"
 	"github.com/spf13/cobra"
@@ -15,7 +16,7 @@ import (
 
 const (
 	// 90 days.
-	DefaultAPIKeyExpiry = 90 * 24 * time.Hour
+	DefaultAPIKeyExpiry = "90d"
 )
 
 func init() {
@@ -23,7 +24,7 @@ func init() {
 	apiKeysCmd.AddCommand(listAPIKeys)
 
 	createAPIKeyCmd.Flags().
-		DurationP("expiration", "e", DefaultAPIKeyExpiry, "Human-readable expiration of the key (e.g. 30m, 24h)")
+		StringP("expiration", "e", DefaultAPIKeyExpiry, "Human-readable expiration of the key (e.g. 30m, 24h)")
 
 	apiKeysCmd.AddCommand(createAPIKeyCmd)
 
@@ -118,10 +119,22 @@ If you loose a key, create a new one and revoke (expire) the old one.`,
 
 		request := &v1.CreateApiKeyRequest{}
 
-		duration, _ := cmd.Flags().GetDuration("expiration")
-		expiration := time.Now().UTC().Add(duration)
+		durationStr, _ := cmd.Flags().GetString("expiration")
 
-		log.Trace().Dur("expiration", duration).Msg("expiration has been set")
+		duration, err := model.ParseDuration(durationStr)
+		if err != nil {
+			ErrorOutput(
+				err,
+				fmt.Sprintf("Could not parse duration: %s\n", err),
+				output,
+			)
+
+			return
+		}
+
+		expiration := time.Now().UTC().Add(time.Duration(duration))
+
+		log.Trace().Dur("expiration", time.Duration(duration)).Msg("expiration has been set")
 
 		request.Expiration = timestamppb.New(expiration)
 
diff --git a/cmd/headscale/cli/preauthkeys.go b/cmd/headscale/cli/preauthkeys.go
index 7efb72f..ffa1a81 100644
--- a/cmd/headscale/cli/preauthkeys.go
+++ b/cmd/headscale/cli/preauthkeys.go
@@ -6,6 +6,7 @@ import (
 	"time"
 
 	v1 "github.com/juanfont/headscale/gen/go/headscale/v1"
+	"github.com/prometheus/common/model"
 	"github.com/pterm/pterm"
 	"github.com/rs/zerolog/log"
 	"github.com/spf13/cobra"
@@ -13,7 +14,7 @@ import (
 )
 
 const (
-	DefaultPreAuthKeyExpiry = 1 * time.Hour
+	DefaultPreAuthKeyExpiry = "1h"
 )
 
 func init() {
@@ -31,7 +32,7 @@ func init() {
 	createPreAuthKeyCmd.PersistentFlags().
 		Bool("ephemeral", false, "Preauthkey for ephemeral nodes")
 	createPreAuthKeyCmd.Flags().
-		DurationP("expiration", "e", DefaultPreAuthKeyExpiry, "Human-readable expiration of the key (e.g. 30m, 24h)")
+		StringP("expiration", "e", DefaultPreAuthKeyExpiry, "Human-readable expiration of the key (e.g. 30m, 24h)")
 }
 
 var preauthkeysCmd = &cobra.Command{
@@ -148,10 +149,22 @@ var createPreAuthKeyCmd = &cobra.Command{
 			Ephemeral: ephemeral,
 		}
 
-		duration, _ := cmd.Flags().GetDuration("expiration")
-		expiration := time.Now().UTC().Add(duration)
+		durationStr, _ := cmd.Flags().GetString("expiration")
 
-		log.Trace().Dur("expiration", duration).Msg("expiration has been set")
+		duration, err := model.ParseDuration(durationStr)
+		if err != nil {
+			ErrorOutput(
+				err,
+				fmt.Sprintf("Could not parse duration: %s\n", err),
+				output,
+			)
+
+			return
+		}
+
+		expiration := time.Now().UTC().Add(time.Duration(duration))
+
+		log.Trace().Dur("expiration", time.Duration(duration)).Msg("expiration has been set")
 
 		request.Expiration = timestamppb.New(expiration)
 

From 3d93cf9e2d9d5934a229dc522e47a00c004f15e2 Mon Sep 17 00:00:00 2001
From: Kristoffer Dalby <kradalby@kradalby.no>
Date: Tue, 31 May 2022 09:42:50 +0200
Subject: [PATCH 37/37] Update changelog

---
 CHANGELOG.md | 1 +
 1 file changed, 1 insertion(+)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 67a576e..77bcaeb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -15,6 +15,7 @@
 - Add --all (-a) flag to enable routes command [#360](https://github.com/juanfont/headscale/issues/360)
 - Add option to enable/disable logtail (Tailscale's logging infrastructure) [#596](https://github.com/juanfont/headscale/pull/596)
   - This change disables the logs by default
+- Use [Prometheus]'s duration parser, supporting days (`d`), weeks (`w`) and years (`y`) [#598](https://github.com/juanfont/headscale/pull/598)
 
 ## 0.15.0 (2022-03-20)