diff --git a/src/client/cmdTemplate/templateEdit.go b/src/client/cmdTemplate/templateEdit.go
index 1996d0ccf7195f4bb7ad632b0be10e906f9221e5..075bba874842e172cb18dc39fe48e1ccd8962beb 100644
--- a/src/client/cmdTemplate/templateEdit.go
+++ b/src/client/cmdTemplate/templateEdit.go
@@ -44,7 +44,7 @@ func (cmd *Edit) Run(args []string) int {
 
 	p, err := cmd.parseArgs(args[1:])
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
diff --git a/src/client/cmdUser/userDel.go b/src/client/cmdUser/userDel.go
index ec41cb4e85acafd04bf7583c084cb29b714eec91..3d1159e48a6f5b8c01d577c1d88695ec3ef8a9df 100644
--- a/src/client/cmdUser/userDel.go
+++ b/src/client/cmdUser/userDel.go
@@ -103,7 +103,7 @@ func (cmd *Del) Run(args []string) int {
 		for i := range args {
 			email := args[i]
 			if err := libclient.UserDelete(email); err != nil {
-				u.PrintlnErr(err.Error())
+				u.PrintlnErr(err)
 				statusCode = 1
 			} else {
 				u.Println("Successfully deleted user " + email)
diff --git a/src/client/cmdUser/userResetPwd.go b/src/client/cmdUser/userResetPwd.go
index 73758b6ea53e7dd0f572b3980842898247ad54e0..e981a1725072940665eb5a21ac01e984556fb1d6 100644
--- a/src/client/cmdUser/userResetPwd.go
+++ b/src/client/cmdUser/userResetPwd.go
@@ -40,7 +40,7 @@ func (cmd *ResetPwd) Run(args []string) int {
 		email := args[i]
 		newpwd, err := libclient.UserResetPwd(email)
 		if err != nil {
-			u.PrintlnErr(err.Error())
+			u.PrintlnErr(err)
 			return 1
 		} else {
 			u.Println("New password for user " + email + ": " + newpwd)
diff --git a/src/client/cmdUser/userSetCaps.go b/src/client/cmdUser/userSetCaps.go
index 387fbdf3dd669184c29d23ef61c6ec9bb932c49f..44133e72c9ed55dd15c933a4a0dda051df017de8 100644
--- a/src/client/cmdUser/userSetCaps.go
+++ b/src/client/cmdUser/userSetCaps.go
@@ -123,7 +123,7 @@ func (cmd *SetCaps) Run(args []string) int {
 			}
 
 			if err := libclient.UserSetCaps(email, userCaps); err != nil {
-				u.PrintlnErr(err.Error())
+				u.PrintlnErr(err)
 				statusCode = 1
 			} else {
 				u.Println("Successfully set capabilities for " + email)
@@ -139,7 +139,7 @@ func (cmd *SetCaps) Run(args []string) int {
 		}
 
 		if err := libclient.UserSetCaps(email, userCaps); err != nil {
-			u.PrintlnErr(err.Error())
+			u.PrintlnErr(err)
 			statusCode = 1
 		} else {
 			u.Println("Successfully set capabilities for " + email)
diff --git a/src/client/cmdUser/userUnlock.go b/src/client/cmdUser/userUnlock.go
index 50e7c243f9fbfbb3429f32108a237cdd701192a3..9db711139efa38227528106ad6a1313a9344753d 100644
--- a/src/client/cmdUser/userUnlock.go
+++ b/src/client/cmdUser/userUnlock.go
@@ -39,7 +39,7 @@ func (cmd *Unlock) Run(args []string) int {
 	for i := range args {
 		email := args[i]
 		if err := libclient.UserUnlock(email); err != nil {
-			u.PrintlnErr(err.Error())
+			u.PrintlnErr(err)
 			return 1
 		} else {
 			u.Println("Successfully unlocked user " + email)
diff --git a/src/client/cmdUser/userUpdatePwd.go b/src/client/cmdUser/userUpdatePwd.go
index be9669ec62e74e2ef13a8a40c7e4e048e6e6fe66..1bd34d0e2c1f0ff3ba75e5d783b254d8fab8d8ce 100644
--- a/src/client/cmdUser/userUpdatePwd.go
+++ b/src/client/cmdUser/userUpdatePwd.go
@@ -60,7 +60,7 @@ func (cmd *UpdatePwd) Run(args []string) int {
 
 	err = libclient.UserUpdatePwd(string(newPwd))
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
diff --git a/src/client/cmdUser/userWhoami.go b/src/client/cmdUser/userWhoami.go
index 915d302e38bc6197d69260dc93e5d28d95b39826..56bc5065adc970facad77430dd149416c9d95772 100644
--- a/src/client/cmdUser/userWhoami.go
+++ b/src/client/cmdUser/userWhoami.go
@@ -35,7 +35,7 @@ func (cmd *Whoami) Run(args []string) int {
 
 	user, err := libclient.UserWhoAmI()
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
diff --git a/src/client/cmdVM/vmAddAccess.go b/src/client/cmdVM/vmAddAccess.go
index f5a9f13b5c1187093bba3dc11616fbd2f065c2ef..dba53d535ab682bdf23b90b7d9bb7c2790a30cbe 100644
--- a/src/client/cmdVM/vmAddAccess.go
+++ b/src/client/cmdVM/vmAddAccess.go
@@ -123,8 +123,8 @@ func (cmd *AddAccess) Run(args []string) int {
 				}
 			}
 
-			if err := cmd.runRequest(vmID, vmID, email, vmAccessCaps); err != nil {
-				u.PrintlnErr(err.Error())
+			if err := libclient.VMAddAccess(vmID, vmID, email, vmAccessCaps); err != nil {
+				u.PrintlnErr(err)
 				statusCode = 1
 			} else {
 				u.Println("Successfully set VM access for " + email + " in VM \"" + vmID + "\"")
@@ -135,13 +135,13 @@ func (cmd *AddAccess) Run(args []string) int {
 
 		email, capabilities, patterns, err := cmd.parseArgs(args)
 		if err != nil {
-			u.PrintlnErr(err.Error())
+			u.PrintlnErr(err)
 			return 1
 		}
 
 		vms, err := getFilteredVMs(libclient.GetEditAccessVMs, patterns)
 		if err != nil {
-			u.PrintlnErr(err.Error())
+			u.PrintlnErr(err)
 			return 1
 		}
 
@@ -156,10 +156,8 @@ func (cmd *AddAccess) Run(args []string) int {
 		}
 
 		for _, vm := range vms {
-			vmID := vm.ID.String()
-			vmName := vm.Name
-			if err := cmd.runRequest(vmID, vmName, email, vmAccessCaps); err != nil {
-				u.PrintlnErr(err.Error())
+			if err := libclient.VMAddAccess(vm.ID.String(), vm.Name, email, vmAccessCaps); err != nil {
+				u.PrintlnErr(err)
 				statusCode = 1
 			} else {
 				u.Println("Successfully set VM access for " + email + " in VM \"" + vm.Name + "\"")
@@ -170,22 +168,6 @@ func (cmd *AddAccess) Run(args []string) int {
 	return statusCode
 }
 
-func (cmd *AddAccess) runRequest(vmID, vmName, email string, vmAccessCaps *params.VMAddAccess) error {
-	client := g.GetInstance().Client
-	host := g.GetInstance().Host
-
-	resp, err := client.R().SetBody(vmAccessCaps).Put(host + "/vms/" + vmID + "/access/" + email)
-	if err != nil {
-		return errors.New("Failed setting VM access for " + email + " in VM \"" + vmName + "\": " + err.Error())
-	}
-
-	if resp.IsSuccess() {
-		return nil
-	} else {
-		return errors.New("Failed setting VM access for " + email + " in VM \"" + vmName + "\": " + resp.Status() + ": " + resp.String())
-	}
-}
-
 func (cmd *AddAccess) parseArgs(args []string) (string, []string, []string, error) {
 	var patterns []string
 	var capabilities []string
diff --git a/src/client/cmdVM/vmAttachAsync.go b/src/client/cmdVM/vmAttachAsync.go
index da5183514054dd26bd190bbc4e35782995d584bc..d147716b78bb3e0a0b835fbdeaed84325f5b99db 100644
--- a/src/client/cmdVM/vmAttachAsync.go
+++ b/src/client/cmdVM/vmAttachAsync.go
@@ -26,7 +26,7 @@ func (cmd *AttachAsync) PrintUsage() {
 
 func (cmd *AttachAsync) Run(args []string) int {
 	if err := exec.CheckRemoteViewer(); err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
@@ -38,7 +38,7 @@ func (cmd *AttachAsync) Run(args []string) int {
 
 	creds, err := getFilteredVMCredentials("/vms/attach", args)
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
diff --git a/src/client/cmdVM/vmAttachAsyncSingle.go b/src/client/cmdVM/vmAttachAsyncSingle.go
index 0f2a5f242ff962b637a455dbfa80bb808efe9a38..6fce7a941879a75e30f5af7b090c1fb7fb173882 100644
--- a/src/client/cmdVM/vmAttachAsyncSingle.go
+++ b/src/client/cmdVM/vmAttachAsyncSingle.go
@@ -32,7 +32,7 @@ func (cmd *AttachAsyncSingle) PrintUsage() {
 
 func (cmd *AttachAsyncSingle) Run(args []string) int {
 	if err := exec.CheckRemoteViewer(); err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
diff --git a/src/client/cmdVM/vmAttachFromPwd.go b/src/client/cmdVM/vmAttachFromPwd.go
index d3e1fb895a769d59ddc3dcafcbdd726278bb9b0a..48d75f33eeb91a3520c9acb31fcc21753e01eaf2 100644
--- a/src/client/cmdVM/vmAttachFromPwd.go
+++ b/src/client/cmdVM/vmAttachFromPwd.go
@@ -37,7 +37,7 @@ func (cmd *AttachFromPwd) PrintUsage() {
 
 func (cmd *AttachFromPwd) Run(args []string) int {
 	if err := exec.CheckRemoteViewer(); err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
diff --git a/src/client/cmdVM/vmAttachSync.go b/src/client/cmdVM/vmAttachSync.go
deleted file mode 100644
index 28cda3b1fa06cbe7b7e25d617823846878c09492..0000000000000000000000000000000000000000
--- a/src/client/cmdVM/vmAttachSync.go
+++ /dev/null
@@ -1,56 +0,0 @@
-package cmdVM
-
-import (
-	"nexus-client/exec"
-	u "nexus-client/utils"
-)
-
-type AttachSync struct {
-	Name string
-}
-
-func (cmd *AttachSync) GetName() string {
-	return cmd.Name
-}
-
-func (cmd *AttachSync) GetDesc() []string {
-	return []string{
-		"Attaches to one or more VMs in order to use their desktop environment.",
-		"If not the VM's owner: requires VM_ATTACH VM access capability or VM_ATTACH_ANY user capability."}
-}
-
-func (cmd *AttachSync) PrintUsage() {
-	printRegexUsage(cmd)
-	printRegexUsageDetails()
-}
-
-func (cmd *AttachSync) Run(args []string) int {
-	if err := exec.CheckRemoteViewer(); err != nil {
-		u.PrintlnErr(err.Error())
-		return 1
-	}
-
-	argc := len(args)
-	if argc < 1 {
-		cmd.PrintUsage()
-		return 1
-	}
-
-	creds, err := getFilteredVMCredentials("/vms/attach", args)
-	if err != nil {
-		u.PrintlnErr(err.Error())
-		return 1
-	}
-
-	if len(creds) == 0 {
-		u.PrintlnErr("Error: VM(s) not found.")
-		return 1
-	}
-
-	statusCode, err := AttachToVMs(creds, true)
-	if err != nil {
-		u.PrintlnErr(err)
-	}
-
-	return statusCode
-}
diff --git a/src/client/cmdVM/vmCreate.go b/src/client/cmdVM/vmCreate.go
index 073a453f45b92e3f5892fe19f313276555459320..57f8994b1a86e09d4950c48e7062bdcab0aee834 100644
--- a/src/client/cmdVM/vmCreate.go
+++ b/src/client/cmdVM/vmCreate.go
@@ -84,7 +84,7 @@ func (cmd *Create) Run(args []string) int {
 			csvFile := args[6]
 			csvEntries, err = u.ReadCSVColumn(csvFile, 0)
 			if err != nil {
-				u.PrintlnErr(err.Error())
+				u.PrintlnErr(err)
 				return 1
 			}
 			count = len(csvEntries)
diff --git a/src/client/cmdVM/vmDel.go b/src/client/cmdVM/vmDel.go
index 68caed12f4ffd4684484392e492eeadf5c7c54fb..68a392f26b1825839efd523e928babbebd654305 100644
--- a/src/client/cmdVM/vmDel.go
+++ b/src/client/cmdVM/vmDel.go
@@ -33,7 +33,7 @@ func (cmd *Del) Run(args []string) int {
 
 	vms, err := getFilteredVMs(libclient.GetDelVMs, args)
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
@@ -42,15 +42,17 @@ func (cmd *Del) Run(args []string) int {
 		return 1
 	}
 
-	for _, vm := range vms {
+	statusCode := 0
 
+	for _, vm := range vms {
 		err := libclient.VMDel(vm.ID.String())
 		if err != nil {
 			u.PrintlnErr(err)
-			return 1
+			statusCode = 1
+		} else {
+			u.Println("Deleted VM \"" + vm.Name + "\"")
 		}
-		u.Println("Deleted VM \"" + vm.Name + "\"")
 	}
 
-	return 0
+	return statusCode
 }
diff --git a/src/client/cmdVM/vmDelAccess.go b/src/client/cmdVM/vmDelAccess.go
index ee61f45a0e25f836742a7714aeaa991563fe13be..4bd7ef9fdd6d755669492df16637082abbda04b4 100644
--- a/src/client/cmdVM/vmDelAccess.go
+++ b/src/client/cmdVM/vmDelAccess.go
@@ -91,8 +91,8 @@ func (cmd *DelAccess) Run(args []string) int {
 				continue
 			}
 
-			if err := cmd.runRequest(vmID, vmID, email); err != nil {
-				u.PrintlnErr(err.Error())
+			if err := libclient.VMDelAccess(vmID, vmID, email); err != nil {
+				u.PrintlnErr(err)
 				statusCode = 1
 			} else {
 				u.Println("Successfully removed VM access for " + email + " in VM \"" + vmID + "\"")
@@ -103,13 +103,13 @@ func (cmd *DelAccess) Run(args []string) int {
 
 		email, patterns, err := cmd.parseArgs(args)
 		if err != nil {
-			u.PrintlnErr(err.Error())
+			u.PrintlnErr(err)
 			return 1
 		}
 
 		vms, err := getFilteredVMs(libclient.GetEditAccessVMs, patterns)
 		if err != nil {
-			u.PrintlnErr(err.Error())
+			u.PrintlnErr(err)
 			return 1
 		}
 
@@ -119,10 +119,8 @@ func (cmd *DelAccess) Run(args []string) int {
 		}
 
 		for _, vm := range vms {
-			vmID := vm.ID.String()
-			vmName := vm.Name
-			if err := cmd.runRequest(vmID, vmName, email); err != nil {
-				u.PrintlnErr(err.Error())
+			if err := libclient.VMDelAccess(vm.ID.String(), vm.Name, email); err != nil {
+				u.PrintlnErr(err)
 				statusCode = 1
 			} else {
 				u.Println("Successfully removed VM access for " + email + " in VM \"" + vm.Name + "\"")
@@ -133,22 +131,6 @@ func (cmd *DelAccess) Run(args []string) int {
 	return statusCode
 }
 
-func (cmd *DelAccess) runRequest(vmID, vmName, email string) error {
-	client := g.GetInstance().Client
-	host := g.GetInstance().Host
-
-	resp, err := client.R().Delete(host + "/vms/" + vmID + "/access/" + email)
-	if err != nil {
-		return errors.New("Failed removing VM access for " + email + " in VM \"" + vmName + "\": " + err.Error())
-	}
-
-	if resp.IsSuccess() {
-		return nil
-	} else {
-		return errors.New("Failed removing VM access for " + email + " in VM \"" + vmName + "\": " + resp.Status() + ": " + resp.String())
-	}
-}
-
 func (cmd *DelAccess) parseArgs(args []string) (string, []string, error) {
 	var patterns []string
 	emailFound := false
diff --git a/src/client/cmdVM/vmEdit.go b/src/client/cmdVM/vmEdit.go
index 13055fbcbcc7dfb014d5b69ab61f17e4bc8a9d20..eced17e4bbeb562a11a2ac9e314d9c0b664324e6 100644
--- a/src/client/cmdVM/vmEdit.go
+++ b/src/client/cmdVM/vmEdit.go
@@ -5,7 +5,6 @@ import (
 	u "nexus-client/utils"
 	"nexus-common/params"
 	"nexus-common/vm"
-	g "nexus-libclient/globals"
 	libclient "nexus-libclient/vm"
 	"strconv"
 	"strings"
@@ -46,9 +45,6 @@ usb     List of USB devices exposed in the VM; either "none" or a list of comma
 }
 
 func (cmd *Edit) Run(args []string) int {
-	client := g.GetInstance().Client
-	host := g.GetInstance().Host
-
 	argc := len(args)
 	if argc < 1 {
 		cmd.PrintUsage()
@@ -57,7 +53,7 @@ func (cmd *Edit) Run(args []string) int {
 
 	vmParams, patterns, err := cmd.parseArgs(args)
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
@@ -68,7 +64,7 @@ func (cmd *Edit) Run(args []string) int {
 
 	vms, err := getFilteredVMs(libclient.GetEditVMs, patterns)
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
@@ -80,19 +76,14 @@ func (cmd *Edit) Run(args []string) int {
 	statusCode := 0
 
 	for _, vm := range vms {
-		uuid := vm.ID.String()
-		resp, err := client.R().SetBody(vmParams).Put(host + "/vms/" + uuid)
+		vmID := vm.ID.String()
+		vmEdited, err := libclient.VMEdit(vmID, *vmParams)
 		if err != nil {
-			u.PrintlnErr("Failed editing VM \"" + vm.Name + "\": " + err.Error())
+			u.PrintlnErr(err)
 			statusCode = 1
 		} else {
-			if resp.IsSuccess() {
-				u.Println("Successfully edited VM \"" + vm.ID.String() + "\":")
-				u.Println(resp.String())
-			} else {
-				u.PrintlnErr("Failed editing VM \"" + vm.Name + "\": " + resp.Status() + ": " + resp.String())
-				statusCode = 1
-			}
+			u.Println("Successfully edited VM \"" + vmID + "\":")
+			u.Println(vmEdited.String())
 		}
 	}
 
diff --git a/src/client/cmdVM/vmExportDir.go b/src/client/cmdVM/vmExportDir.go
index cb3f4562be37b8261138ea29bd3d0d3e37b4005c..e17420100d3b4ff63739ff08e9e82d5cc52103fc 100644
--- a/src/client/cmdVM/vmExportDir.go
+++ b/src/client/cmdVM/vmExportDir.go
@@ -2,8 +2,6 @@ package cmdVM
 
 import (
 	u "nexus-client/utils"
-	"nexus-common/params"
-	g "nexus-libclient/globals"
 	libclient "nexus-libclient/vm"
 )
 
@@ -36,9 +34,6 @@ Remark: one archive, named after the VM's name, is created per VM.`
 }
 
 func (cmd *ExportDir) Run(args []string) int {
-	client := g.GetInstance().Client
-	host := g.GetInstance().Host
-
 	argc := len(args)
 	if argc < 2 {
 		cmd.PrintUsage()
@@ -49,7 +44,7 @@ func (cmd *ExportDir) Run(args []string) int {
 
 	vms, err := getFilteredVMs(libclient.GetExportDirVMs, args[:argc-1])
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
@@ -58,27 +53,15 @@ func (cmd *ExportDir) Run(args []string) int {
 		return 1
 	}
 
-	params := &params.VMExportDir{Dir: dir}
-
 	statusCode := 0
 
-	client.SetAllowGetMethodPayload(true)
-
 	for _, vm := range vms {
-		uuid := vm.ID.String()
-		outputFile := vm.Name + "_" + vm.ID.String() + ".tar.gz"
-		resp, err := client.R().SetOutput(outputFile).SetBody(params).Get(host + "/vms/" + uuid + "/exportdir")
+		outputFile, err := libclient.VMExportDir(vm.Name, vm.ID.String(), dir)
 		if err != nil {
-			u.PrintlnErr("Failed exporting " + dir + " from VM \"" + vm.Name + "\": " + err.Error())
+			u.PrintlnErr(err)
 			statusCode = 1
 		} else {
-			if resp.IsSuccess() {
-				u.Println("Exported " + dir + " from VM \"" + vm.Name + "\" into " + outputFile)
-			} else {
-				errorMsg, _ := u.FileToString(outputFile)
-				u.PrintlnErr("Failed exporting " + dir + " from VM \"" + vm.Name + "\": " + resp.Status() + ": " + errorMsg)
-				statusCode = 1
-			}
+			u.Println("Exported " + dir + " from VM \"" + vm.Name + "\" into " + outputFile)
 		}
 	}
 
diff --git a/src/client/cmdVM/vmImportDir.go b/src/client/cmdVM/vmImportDir.go
index 659fa4f09c10109de9691d51d702dd6ea8a335b0..60e3fe861814d537c563476e0c3472a33311e153 100644
--- a/src/client/cmdVM/vmImportDir.go
+++ b/src/client/cmdVM/vmImportDir.go
@@ -52,7 +52,7 @@ func (cmd *ImportDir) Run(args []string) int {
 
 	vms, err := getFilteredVMs(libclient.GetImportFileVMs, args[:argc-2])
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
@@ -65,13 +65,13 @@ func (cmd *ImportDir) Run(args []string) int {
 
 	tmpTarGzFile, err := u.GetRandomTempFilename()
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 	tmpTarGzFile += ".tar.gz"
 	defer os.Remove(tmpTarGzFile)
 	if err := u.TarGzDir(localDir, tmpTarGzFile); err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
diff --git a/src/client/cmdVM/vmReboot.go b/src/client/cmdVM/vmReboot.go
index e62c57d1626db11b402bda7b0f0a31ceab52d413..ff8ed2a8d8247eaadd774b11fe7bd7744be14dbd 100644
--- a/src/client/cmdVM/vmReboot.go
+++ b/src/client/cmdVM/vmReboot.go
@@ -34,7 +34,7 @@ func (cmd *Reboot) Run(args []string) int {
 
 	vms, err := getFilteredVMs(libclient.GetRebootVMs, args)
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
@@ -43,15 +43,17 @@ func (cmd *Reboot) Run(args []string) int {
 		return 1
 	}
 
+	statusCode := 0
+
 	for _, vm := range vms {
 		err := libclient.VMReboot(vm.ID.String())
 		if err != nil {
 			u.PrintlnErr(err)
-			return 1
+			statusCode = 1
+		} else {
+			u.Println("Sent reboot message to VM \"" + vm.Name + "\"")
 		}
-		u.Println("Sent reboot message to VM \"" + vm.Name + "\"")
-
 	}
 
-	return 0
+	return statusCode
 }
diff --git a/src/client/cmdVM/vmShutdown.go b/src/client/cmdVM/vmShutdown.go
index da07af9f1b57084d3ad853482c4837c46be9b6c2..4c513215a4a9cd01feebac868018fe112b130c57 100644
--- a/src/client/cmdVM/vmShutdown.go
+++ b/src/client/cmdVM/vmShutdown.go
@@ -2,7 +2,6 @@ package cmdVM
 
 import (
 	u "nexus-client/utils"
-	g "nexus-libclient/globals"
 	libclient "nexus-libclient/vm"
 )
 
@@ -26,9 +25,6 @@ func (cmd *Shutdown) PrintUsage() {
 }
 
 func (cmd *Shutdown) Run(args []string) int {
-	client := g.GetInstance().Client
-	host := g.GetInstance().Host
-
 	argc := len(args)
 	if argc < 1 {
 		cmd.PrintUsage()
@@ -37,7 +33,7 @@ func (cmd *Shutdown) Run(args []string) int {
 
 	vms, err := getFilteredVMs(libclient.GetStopVMs, args)
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
@@ -49,20 +45,13 @@ func (cmd *Shutdown) Run(args []string) int {
 	statusCode := 0
 
 	for _, vm := range vms {
-		uuid := vm.ID.String()
-		resp, err := client.R().Put(host + "/vms/" + uuid + "/shutdown")
+		err := libclient.VMShutdown(vm.ID.String())
 		if err != nil {
-			u.PrintlnErr("Shutdown failed for VM \"" + vm.Name + "\": " + err.Error())
+			u.PrintlnErr(err)
 			statusCode = 1
 		} else {
-			if resp.IsSuccess() {
-				u.Println("Sent shutdown message to VM \"" + vm.Name + "\"")
-			} else {
-				u.PrintlnErr("Shutdown failed for VM \"" + vm.Name + "\": " + resp.Status() + ": " + resp.String())
-				statusCode = 1
-			}
+			u.Println("Sent shutdown message to VM \"" + vm.Name + "\"")
 		}
-
 	}
 
 	return statusCode
diff --git a/src/client/cmdVM/vmStart.go b/src/client/cmdVM/vmStart.go
index bf11948bef514a364404e4c94da705e6a2dcba48..5e4e31e1e41dc0c6b0050a7064e9e6ac520fc64c 100644
--- a/src/client/cmdVM/vmStart.go
+++ b/src/client/cmdVM/vmStart.go
@@ -2,7 +2,6 @@ package cmdVM
 
 import (
 	u "nexus-client/utils"
-	g "nexus-libclient/globals"
 	libclient "nexus-libclient/vm"
 )
 
@@ -26,9 +25,6 @@ func (cmd *Start) PrintUsage() {
 }
 
 func (cmd *Start) Run(args []string) int {
-	client := g.GetInstance().Client
-	host := g.GetInstance().Host
-
 	argc := len(args)
 	if argc < 1 {
 		cmd.PrintUsage()
@@ -37,7 +33,7 @@ func (cmd *Start) Run(args []string) int {
 
 	vms, err := getFilteredVMs(libclient.GetStartVMs, args)
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
@@ -49,20 +45,13 @@ func (cmd *Start) Run(args []string) int {
 	statusCode := 0
 
 	for _, vm := range vms {
-		uuid := vm.ID.String()
-		resp, err := client.R().Put(host + "/vms/" + uuid + "/start")
+		err := libclient.VMStart(vm.ID.String())
 		if err != nil {
-			u.PrintlnErr("Failed starting VM \"" + vm.Name + "\": " + err.Error())
+			u.PrintlnErr(err)
 			statusCode = 1
 		} else {
-			if resp.IsSuccess() {
-				u.Println("Started VM \"" + vm.Name + "\"")
-			} else {
-				u.PrintlnErr("Failed starting VM \"" + vm.Name + "\": " + resp.Status() + ": " + resp.String())
-				statusCode = 1
-			}
+			u.Println("Started VM \"" + vm.Name + "\"")
 		}
-
 	}
 
 	return statusCode
diff --git a/src/client/cmdVM/vmStartAttach.go b/src/client/cmdVM/vmStartAttach.go
index 49b9a8790bc3c3719696f8af234de7363b6af366..4cca859438ff75e810d36043b3f4a1dd7b1a4351 100644
--- a/src/client/cmdVM/vmStartAttach.go
+++ b/src/client/cmdVM/vmStartAttach.go
@@ -38,13 +38,13 @@ func (cmd *StartAttach) Run(args []string) int {
 	}
 
 	if err := exec.CheckRemoteViewer(); err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
 	vms, err := getFilteredVMs(libclient.GetStartVMs, args)
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
@@ -75,7 +75,7 @@ func (cmd *StartAttach) Run(args []string) int {
 	// at this point, the returned filtered credentials only works for VMs that started successfully
 	creds, err := getFilteredVMCredentials("/vms/attach", args)
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
diff --git a/src/client/cmdVM/vmStartWithCreds.go b/src/client/cmdVM/vmStartWithCreds.go
index 67569d89a7ed54e7ed24eecf2fbfe6cf3700158d..30663e57c5654882b64d93ccad313e43d9419d37 100644
--- a/src/client/cmdVM/vmStartWithCreds.go
+++ b/src/client/cmdVM/vmStartWithCreds.go
@@ -4,7 +4,7 @@ import (
 	"errors"
 	u "nexus-client/utils"
 	"nexus-common/params"
-	g "nexus-libclient/globals"
+	libclient "nexus-libclient/vm"
 )
 
 type StartWithCreds struct {
@@ -52,9 +52,6 @@ func (cmd *StartWithCreds) parseCSVFile(csvFile string) ([]string, []string, err
 }
 
 func (cmd *StartWithCreds) Run(args []string) int {
-	client := g.GetInstance().Client
-	host := g.GetInstance().Host
-
 	argc := len(args)
 	if argc != 1 {
 		cmd.PrintUsage()
@@ -63,27 +60,22 @@ func (cmd *StartWithCreds) Run(args []string) int {
 
 	vmIDs, pwds, err := cmd.parseCSVFile(args[0])
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
 	statusCode := 0
 
-	vmArgs := &params.VMStartWithCreds{}
+	vmArgs := params.VMStartWithCreds{}
 
 	for i, vmID := range vmIDs {
 		vmArgs.Pwd = pwds[i]
-		resp, err := client.R().SetBody(vmArgs).Put(host + "/vms/" + vmID + "/startwithcreds")
+		err := libclient.VMStartWithCreds(vmID, vmArgs)
 		if err != nil {
-			u.PrintlnErr("Failed starting VM \"" + vmID + "\": " + err.Error())
+			u.PrintlnErr(err)
 			statusCode = 1
 		} else {
-			if resp.IsSuccess() {
-				u.Println("Started VM \"" + vmID + "\"")
-			} else {
-				u.PrintlnErr("Failed starting VM \"" + vmID + "\": " + resp.Status() + ": " + resp.String())
-				statusCode = 1
-			}
+			u.Println("Started VM \"" + vmID + "\"")
 		}
 	}
 
diff --git a/src/client/cmdVM/vmStop.go b/src/client/cmdVM/vmStop.go
index 22f86d7b5b2f88e14240e250c99c9ce42a951273..eae718143ace33afda2ef51f09d749ee60186e64 100644
--- a/src/client/cmdVM/vmStop.go
+++ b/src/client/cmdVM/vmStop.go
@@ -33,7 +33,7 @@ func (cmd *Stop) Run(args []string) int {
 
 	vms, err := getFilteredVMs(libclient.GetStopVMs, args)
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 
@@ -42,14 +42,17 @@ func (cmd *Stop) Run(args []string) int {
 		return 1
 	}
 
+	statusCode := 0
+
 	for _, vm := range vms {
 		err := libclient.VMStop(vm.ID.String())
 		if err != nil {
 			u.PrintlnErr(err)
-			return 1
+			statusCode = 1
+		} else {
+			u.Println("Stopped VM \"" + vm.Name + "\"")
 		}
-		u.Println("Stopped VM \"" + vm.Name + "\"")
 	}
 
-	return 0
+	return statusCode
 }
diff --git a/src/client/nexush/nexush.go b/src/client/nexush/nexush.go
index 5488e9f5aa235b202c7f33d50e4fc5caec6e7d59..3a12c03e4d9cf4b1799f1047c3a7727880f06b46 100644
--- a/src/client/nexush/nexush.go
+++ b/src/client/nexush/nexush.go
@@ -165,7 +165,7 @@ func run() int {
 	fmt.Printf(email + "'s password: ")
 	bytePwd, err := term.ReadPassword(int(syscall.Stdin))
 	if err != nil {
-		u.PrintlnErr(err.Error())
+		u.PrintlnErr(err)
 		return 1
 	}
 	pwd := string(bytePwd)
diff --git a/src/libclient/go.sum b/src/libclient/go.sum
new file mode 100644
index 0000000000000000000000000000000000000000..7790d7c3e03900e267f0aade3acce649895b2246
--- /dev/null
+++ b/src/libclient/go.sum
@@ -0,0 +1,2 @@
+github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
+github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
diff --git a/src/libclient/vm/vmAddAccess.go b/src/libclient/vm/vmAddAccess.go
new file mode 100644
index 0000000000000000000000000000000000000000..b03612f4ea5fb1bb56a1298ef654eb73bd18618f
--- /dev/null
+++ b/src/libclient/vm/vmAddAccess.go
@@ -0,0 +1,23 @@
+package vm
+
+import (
+	"nexus-common/params"
+	g "nexus-libclient/globals"
+	"nexus-libclient/response"
+)
+
+func VMAddAccess(vmID, vmName, email string, vmAccessCaps *params.VMAddAccess) error {
+	client := g.GetInstance().Client
+	host := g.GetInstance().Host
+
+	resp, err := client.R().SetBody(vmAccessCaps).Put(host + "/vms/" + vmID + "/access/" + email)
+	if err != nil {
+		return err
+	}
+
+	if resp.IsSuccess() {
+		return nil
+	} else {
+		return response.ErrorToMsg(resp)
+	}
+}
diff --git a/src/libclient/vm/vmDelAccess.go b/src/libclient/vm/vmDelAccess.go
new file mode 100644
index 0000000000000000000000000000000000000000..4d69b604c7c2f420982a899fbb6d37867d8128a5
--- /dev/null
+++ b/src/libclient/vm/vmDelAccess.go
@@ -0,0 +1,22 @@
+package vm
+
+import (
+	g "nexus-libclient/globals"
+	"nexus-libclient/response"
+)
+
+func VMDelAccess(vmID, vmName, email string) error {
+	client := g.GetInstance().Client
+	host := g.GetInstance().Host
+
+	resp, err := client.R().Delete(host + "/vms/" + vmID + "/access/" + email)
+	if err != nil {
+		return err
+	}
+
+	if resp.IsSuccess() {
+		return nil
+	} else {
+		return response.ErrorToMsg(resp)
+	}
+}
diff --git a/src/libclient/vm/vmEdit.go b/src/libclient/vm/vmEdit.go
new file mode 100644
index 0000000000000000000000000000000000000000..191f510a11aa065f582c02384aec4ed6cfb1401e
--- /dev/null
+++ b/src/libclient/vm/vmEdit.go
@@ -0,0 +1,28 @@
+package vm
+
+import (
+	"encoding/json"
+	"nexus-common/params"
+	"nexus-common/vm"
+	g "nexus-libclient/globals"
+	"nexus-libclient/response"
+)
+
+func VMEdit(vmID string, p params.VMEdit) (*vm.VMNetworkSerialized, error) {
+	client := g.GetInstance().Client
+	host := g.GetInstance().Host
+
+	resp, err := client.R().SetBody(p).Put(host + "/vms/" + vmID)
+	if err != nil {
+		return nil, err
+	}
+	if resp.IsSuccess() {
+		var vm vm.VMNetworkSerialized
+		if err := json.Unmarshal(resp.Body(), &vm); err != nil {
+			return nil, err
+		}
+		return &vm, nil
+	} else {
+		return nil, response.ErrorToMsg(resp)
+	}
+}
diff --git a/src/libclient/vm/vmExportDir.go b/src/libclient/vm/vmExportDir.go
new file mode 100644
index 0000000000000000000000000000000000000000..adf02367987b920a419725d3ca93d4c3716366e6
--- /dev/null
+++ b/src/libclient/vm/vmExportDir.go
@@ -0,0 +1,30 @@
+package vm
+
+import (
+	"errors"
+	u "nexus-client/utils"
+	"nexus-common/params"
+	g "nexus-libclient/globals"
+)
+
+func VMExportDir(vmName, vmID, dirToExport string) (string, error) {
+	client := g.GetInstance().Client
+	host := g.GetInstance().Host
+
+	p := &params.VMExportDir{Dir: dirToExport}
+
+	client.SetAllowGetMethodPayload(true)
+
+	outputFile := vmName + "_" + vmID + ".tar.gz"
+	resp, err := client.R().SetOutput(outputFile).SetBody(p).Get(host + "/vms/" + vmID + "/exportdir")
+	if err != nil {
+		// u.PrintlnErr("Failed exporting " + dir + " from VM \"" + vm.Name + "\": " + err.Error())
+		return "", err
+	}
+	if resp.IsSuccess() {
+		return outputFile, nil
+	} else {
+		errorMsg, _ := u.FileToString(outputFile)
+		return "", errors.New("Failed exporting " + dirToExport + " from VM \"" + vmName + "\": " + resp.Status() + ": " + errorMsg)
+	}
+}
diff --git a/src/libclient/vm/vmShutdown.go b/src/libclient/vm/vmShutdown.go
new file mode 100644
index 0000000000000000000000000000000000000000..19dc4e6e5b85296c20bdb24a9d537bced513d39c
--- /dev/null
+++ b/src/libclient/vm/vmShutdown.go
@@ -0,0 +1,21 @@
+package vm
+
+import (
+	g "nexus-libclient/globals"
+	"nexus-libclient/response"
+)
+
+func VMShutdown(vmID string) error {
+	client := g.GetInstance().Client
+	host := g.GetInstance().Host
+
+	resp, err := client.R().Put(host + "/vms/" + vmID + "/shutdown")
+	if err != nil {
+		return err
+	}
+	if resp.IsSuccess() {
+		return nil
+	} else {
+		return response.ErrorToMsg(resp)
+	}
+}
diff --git a/src/libclient/vm/vmStart.go b/src/libclient/vm/vmStart.go
new file mode 100644
index 0000000000000000000000000000000000000000..a46f068f320702f652af0c2e841c092d7f58881b
--- /dev/null
+++ b/src/libclient/vm/vmStart.go
@@ -0,0 +1,21 @@
+package vm
+
+import (
+	g "nexus-libclient/globals"
+	"nexus-libclient/response"
+)
+
+func VMStart(vmID string) error {
+	client := g.GetInstance().Client
+	host := g.GetInstance().Host
+
+	resp, err := client.R().Put(host + "/vms/" + vmID + "/start")
+	if err != nil {
+		return err
+	}
+	if resp.IsSuccess() {
+		return nil
+	} else {
+		return response.ErrorToMsg(resp)
+	}
+}
diff --git a/src/libclient/vm/vmStartWithCreds.go b/src/libclient/vm/vmStartWithCreds.go
new file mode 100644
index 0000000000000000000000000000000000000000..4cef89eeca33f1d41f74682119f10758a7993b8f
--- /dev/null
+++ b/src/libclient/vm/vmStartWithCreds.go
@@ -0,0 +1,22 @@
+package vm
+
+import (
+	"nexus-common/params"
+	g "nexus-libclient/globals"
+	"nexus-libclient/response"
+)
+
+func VMStartWithCreds(vmID string, p params.VMStartWithCreds) error {
+	client := g.GetInstance().Client
+	host := g.GetInstance().Host
+
+	resp, err := client.R().SetBody(p).Put(host + "/vms/" + vmID + "/startwithcreds")
+	if err != nil {
+		return err
+	}
+	if resp.IsSuccess() {
+		return nil
+	} else {
+		return response.ErrorToMsg(resp)
+	}
+}