diff --git a/bindings/ud.go b/bindings/ud.go
index 5fdb2cd0e0bf9a92f67c32fc74d82a780783f701..15b0e012a7ffaad5b290da54b832bcffdf39fa57 100644
--- a/bindings/ud.go
+++ b/bindings/ud.go
@@ -80,6 +80,7 @@ func (ud *UserDiscovery) ConfirmFact(confirmationID, code string) error {
 
 // Removes a previously confirmed fact.  Will fail if the passed fact string is
 // not well formed or if the fact is not associated with this client.
+// Users cannot remove username facts and must instead remove the user.
 func (ud *UserDiscovery) RemoveFact(fStr string) error {
 	f, err := fact.UnstringifyFact(fStr)
 	if err != nil {
@@ -89,6 +90,18 @@ func (ud *UserDiscovery) RemoveFact(fStr string) error {
 	return ud.ud.RemoveFact(f)
 }
 
+// RemoveUser deletes a user. The fact sent must be the username.
+// This function preserves the username forever and makes it
+// unusable.
+func (ud *UserDiscovery) RemoveUser(fStr string) error {
+	f, err := fact.UnstringifyFact(fStr)
+	if err != nil {
+		return errors.WithMessage(err, "Failed to remove due to "+
+			"malformed fact")
+	}
+	return ud.ud.RemoveUser(f)
+}
+
 // SearchCallback returns the result of a search
 type SearchCallback interface {
 	Callback(contacts *ContactList, error string)
diff --git a/cmd/ud.go b/cmd/ud.go
index 4fa083d32cdd3641f2c333fd63a2e47cf36e1af2..880a503f32ec6783591e238165a4ea11bb24eb16 100644
--- a/cmd/ud.go
+++ b/cmd/ud.go
@@ -198,6 +198,22 @@ var udCmd = &cobra.Command{
 		if err != nil {
 			jww.FATAL.Panicf("%+v", err)
 		}
+
+		userToRemove := viper.GetString("remove")
+		if userToRemove != "" {
+			f, err := fact.NewFact(fact.Username, usernameSearchStr)
+			if err != nil {
+				jww.FATAL.Panicf(
+					"Failed to create new fact: %+v", err)
+			}
+			err = userDiscoveryMgr.RemoveUser(f)
+			if err != nil {
+				jww.FATAL.Panicf(
+					"Failed to remove user %s: %+v",
+					userToRemove, err)
+			}
+		}
+
 		time.Sleep(91 * time.Second)
 		err = client.StopNetworkFollower()
 		if err != nil {
@@ -212,6 +228,10 @@ func init() {
 		"Register this user with user discovery.")
 	_ = viper.BindPFlag("register", udCmd.Flags().Lookup("register"))
 
+	udCmd.Flags().StringP("remove", "", "",
+		"Remove this user with user discovery.")
+	_ = viper.BindPFlag("remove", udCmd.Flags().Lookup("remove"))
+
 	udCmd.Flags().String("addphone", "",
 		"Add phone number to existing user registration.")
 	_ = viper.BindPFlag("addphone", udCmd.Flags().Lookup("addphone"))
diff --git a/go.mod b/go.mod
index 4374a2dd5a339fa386637cf5b8a10d5fcc367968..2e5f10caf977093d5bba142c8ee0060f88222ed6 100644
--- a/go.mod
+++ b/go.mod
@@ -17,7 +17,7 @@ require (
 	github.com/spf13/jwalterweatherman v1.1.0
 	github.com/spf13/viper v1.7.1
 	gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228
-	gitlab.com/elixxir/comms v0.0.4-0.20210823164208-0e219b367d23
+	gitlab.com/elixxir/comms v0.0.4-0.20210903140456-430cebcae4ec
 	gitlab.com/elixxir/crypto v0.0.7-0.20210803232056-ba3ff44cc618
 	gitlab.com/elixxir/ekv v0.1.5
 	gitlab.com/elixxir/primitives v0.0.3-0.20210803231939-7b924f78eaac
diff --git a/go.sum b/go.sum
index c8eb24d11ebf4db213445d279faf780c546faa42..4281cb4a024da594ef0e693b34a14d0cc8f846a1 100644
--- a/go.sum
+++ b/go.sum
@@ -253,6 +253,8 @@ gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228 h1:Gi6rj4mAlK0
 gitlab.com/elixxir/bloomfilter v0.0.0-20200930191214-10e9ac31b228/go.mod h1:H6jztdm0k+wEV2QGK/KYA+MY9nj9Zzatux/qIvDDv3k=
 gitlab.com/elixxir/comms v0.0.4-0.20210823164208-0e219b367d23 h1:ZculIcPFJjVN+Z9mCe2fdZ5JG2LW/tu0yWnceOcJ/TU=
 gitlab.com/elixxir/comms v0.0.4-0.20210823164208-0e219b367d23/go.mod h1:1fHnPjj5Sv2qfnQplu8+BKlehQy54vtgM7khp5axXMU=
+gitlab.com/elixxir/comms v0.0.4-0.20210903140456-430cebcae4ec h1:n6lL2/cNYLXExdB5fVnLJY2C+gFfqwjovjwdEsWKAQo=
+gitlab.com/elixxir/comms v0.0.4-0.20210903140456-430cebcae4ec/go.mod h1:1fHnPjj5Sv2qfnQplu8+BKlehQy54vtgM7khp5axXMU=
 gitlab.com/elixxir/crypto v0.0.0-20200804182833-984246dea2c4/go.mod h1:ucm9SFKJo+K0N2GwRRpaNr+tKXMIOVWzmyUD0SbOu2c=
 gitlab.com/elixxir/crypto v0.0.3/go.mod h1:ZNgBOblhYToR4m8tj4cMvJ9UsJAUKq+p0gCp07WQmhA=
 gitlab.com/elixxir/crypto v0.0.7-0.20210803232056-ba3ff44cc618 h1:Z1SI2a8mXP4Zf2xfhURT1Hbmtq3tufTdebNLgCdlufg=
diff --git a/ud/remove.go b/ud/remove.go
index 69514a579f4169128c455a181c34116ad7b63a61..99d9447f330cf0a5c133975abea3a29de1a71b0e 100644
--- a/ud/remove.go
+++ b/ud/remove.go
@@ -59,3 +59,50 @@ func (m *Manager) removeFact(fact fact.Fact, rFC removeFactComms) error {
 	// Return the error
 	return err
 }
+
+type removeUserComms interface {
+	SendRemoveUser(host *connect.Host, message *mixmessages.FactRemovalRequest) (*messages.Ack, error)
+}
+
+// Removes a previously confirmed fact.  Will fail if the fact is not
+// associated with this client.
+func (m *Manager) RemoveUser(fact fact.Fact) error {
+	jww.INFO.Printf("ud.RemoveUser(%s)", fact.Stringify())
+	return m.removeUser(fact, m.comms)
+}
+
+func (m *Manager) removeUser(fact fact.Fact, rFC removeUserComms) error {
+	if !m.IsRegistered() {
+		return errors.New("Failed to remove fact: " +
+			"client is not registered")
+	}
+
+	// Construct the message to send
+	// Convert our Fact to a mixmessages Fact for sending
+	mmFact := mixmessages.Fact{
+		Fact:     fact.Fact,
+		FactType: uint32(fact.T),
+	}
+
+	// Create a hash of our fact
+	fhash := factID.Fingerprint(fact)
+
+	// Sign our inFact for putting into the request
+	fsig, err := rsa.Sign(rand.Reader, m.privKey, hash.CMixHash, fhash, nil)
+	if err != nil {
+		return err
+	}
+
+	// Create our Fact Removal Request message data
+	remFactMsg := mixmessages.FactRemovalRequest{
+		UID:         m.myID.Marshal(),
+		RemovalData: &mmFact,
+		FactSig:     fsig,
+	}
+
+	// Send the message
+	_, err = rFC.SendRemoveUser(m.host, &remFactMsg)
+
+	// Return the error
+	return err
+}
diff --git a/ud/remove_test.go b/ud/remove_test.go
index 2adf4c976a0a895c30a2afc6fda98281fd8cb5c2..d65cb0e8ccd888e374e42c2585fe41dfe2305c66 100644
--- a/ud/remove_test.go
+++ b/ud/remove_test.go
@@ -51,3 +51,42 @@ func TestRemoveFact(t *testing.T) {
 		t.Fatal(err)
 	}
 }
+
+func (rFC *testRFC) SendRemoveUser(host *connect.Host, message *pb.FactRemovalRequest) (*messages.Ack, error) {
+	return &messages.Ack{}, nil
+}
+
+func TestRemoveUser(t *testing.T) {
+	h, err := connect.NewHost(&id.DummyUser, "address", nil, connect.GetDefaultHostParams())
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	rng := csprng.NewSystemRNG()
+	cpk, err := rsa.GenerateKey(rng, 2048)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	isReg := uint32(1)
+
+	m := Manager{
+		comms:      nil,
+		host:       h,
+		privKey:    cpk,
+		registered: &isReg,
+		myID:       &id.ID{},
+	}
+
+	f := fact.Fact{
+		Fact: "testing",
+		T:    2,
+	}
+
+	trfc := testRFC{}
+
+	err = m.removeUser(f, &trfc)
+	if err != nil {
+		t.Fatal(err)
+	}
+}