diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 0000000000000000000000000000000000000000..b458b995e10b004df89e7e4b0af934d183d66266
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+    "rust-analyzer.diagnostics.disabled": [
+        "unresolved-macro-call"
+    ]
+}
\ No newline at end of file
diff --git a/Cargo.lock b/Cargo.lock
index 612f5003cd1b598b483658fbed9a5afc021dec0d..20bc4ec6f00cb262e9f73c79c4aed4d2d40bbe8a 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2475,7 +2475,7 @@ dependencies = [
 
 [[package]]
 name = "revolt"
-version = "0.4.1-alpha.1"
+version = "0.4.1-alpha.2"
 dependencies = [
  "async-std",
  "async-tungstenite",
diff --git a/Cargo.toml b/Cargo.toml
index 0422bb740a5facded0e17dc48d6d713b6a08c908..ba3470967f097d4e02efcf2458a633e5fb4cd3bb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "revolt"
-version = "0.4.1-alpha.1"
+version = "0.4.1-alpha.2"
 authors = ["Paul Makles <paulmakles@gmail.com>"]
 edition = "2018"
 
diff --git a/src/database/entities/user.rs b/src/database/entities/user.rs
index e7c0e614b60de2afe1e3d587fdc0fffae3957119..165e9a163314ab718334285a853dbf42aa5085d5 100644
--- a/src/database/entities/user.rs
+++ b/src/database/entities/user.rs
@@ -82,6 +82,8 @@ pub struct User {
 impl User {
     /// Mutate the user object to include relationship as seen by user.
     pub fn from(mut self, user: &User) -> User {
+        self.relationship = Some(RelationshipStatus::None);
+
         if self.id == user.id {
             self.relationship = Some(RelationshipStatus::User);
             return self;
@@ -102,12 +104,26 @@ impl User {
     pub fn with(mut self, permissions: UserPermissions<[u32; 1]>) -> User {
         if permissions.get_view_profile() {
             self.online = Some(is_online(&self.id));
+        } else {
+            self.status = None;
         }
 
         self.profile = None;
         self
     }
 
+    /// Mutate the user object to appear as seen by user.
+    /// Also overrides the relationship status.
+    pub async fn from_override(mut self, user: &User, relationship: RelationshipStatus) -> Result<User> {
+        let permissions = PermissionCalculator::new(&user)
+            .with_relationship(&relationship)
+            .for_user(&self.id).await?;
+
+        self.relations = None;
+        self.relationship = Some(relationship);
+        Ok(self.with(permissions))
+    }
+
     /// Utility function for checking claimed usernames.
     pub async fn is_username_taken(username: &str) -> Result<bool> {
         if username.to_lowercase() == "revolt" && username.to_lowercase() == "admin" {
diff --git a/src/database/permissions/mod.rs b/src/database/permissions/mod.rs
index 7fc9b26eb89eda009c677d5c83885cfac40df000..61644789d3f032a09b5c041b1375cce557d65dbe 100644
--- a/src/database/permissions/mod.rs
+++ b/src/database/permissions/mod.rs
@@ -9,6 +9,7 @@ pub struct PermissionCalculator<'a> {
     perspective: &'a User,
 
     user: Option<&'a User>,
+    relationship: Option<&'a RelationshipStatus>,
     channel: Option<&'a Channel>,
 
     has_mutual_connection: bool,
@@ -20,6 +21,7 @@ impl<'a> PermissionCalculator<'a> {
             perspective,
 
             user: None,
+            relationship: None,
             channel: None,
 
             has_mutual_connection: false,
@@ -33,6 +35,13 @@ impl<'a> PermissionCalculator<'a> {
         }
     }
 
+    pub fn with_relationship(self, relationship: &'a RelationshipStatus) -> PermissionCalculator {
+        PermissionCalculator {
+            relationship: Some(&relationship),
+            ..self
+        }
+    }
+
     pub fn with_channel(self, channel: &'a Channel) -> PermissionCalculator {
         PermissionCalculator {
             channel: Some(&channel),
diff --git a/src/database/permissions/user.rs b/src/database/permissions/user.rs
index 7d516eef7d038f560a8f127e35f8d21c3064ed39..f4d7c581bbc02dc459e9d67acd8f94f128c2e711 100644
--- a/src/database/permissions/user.rs
+++ b/src/database/permissions/user.rs
@@ -49,7 +49,7 @@ impl<'a> PermissionCalculator<'a> {
         }
 
         let mut permissions: u32 = 0;
-        match get_relationship(&self.perspective, &target) {
+        match self.relationship.clone().map(|v| v.to_owned()).unwrap_or_else(|| get_relationship(&self.perspective, &target)) {
             RelationshipStatus::Friend => return Ok(u32::MAX),
             RelationshipStatus::Blocked | RelationshipStatus::BlockedOther => {
                 return Ok(UserPermission::Access as u32)
diff --git a/src/notifications/events.rs b/src/notifications/events.rs
index 4e92bdb9113f6fc8e196bdffdcecc94a94f5ddd3..a71f0ae77bd7dcaa96a557cfacf243e432e6c05c 100644
--- a/src/notifications/events.rs
+++ b/src/notifications/events.rs
@@ -80,8 +80,8 @@ pub enum ClientboundNotification {
     },
     UserRelationship {
         id: String,
-        user: String,
-        status: RelationshipStatus,
+        user: User,
+        status: RelationshipStatus
     },
     UserPresence {
         id: String,
@@ -116,7 +116,7 @@ pub fn prehandle_hook(notification: &ClientboundNotification) {
         }
         ClientboundNotification::UserRelationship { id, user, status } => {
             if status != &RelationshipStatus::None {
-                subscribe_if_exists(id.clone(), user.clone()).ok();
+                subscribe_if_exists(id.clone(), user.id.clone()).ok();
             }
         }
         _ => {}
@@ -132,7 +132,7 @@ pub fn posthandle_hook(notification: &ClientboundNotification) {
             if status == &RelationshipStatus::None {
                 get_hive()
                     .hive
-                    .unsubscribe(&id.to_string(), &user.to_string())
+                    .unsubscribe(&id.to_string(), &user.id.to_string())
                     .ok();
             }
         }
diff --git a/src/routes/root.rs b/src/routes/root.rs
index 1f2be02f919f61f41a0324162e072da38abab98c..669b08c27307b6e3f7729b04c094b798e22097e0 100644
--- a/src/routes/root.rs
+++ b/src/routes/root.rs
@@ -9,7 +9,7 @@ use rocket_contrib::json::JsonValue;
 #[get("/")]
 pub async fn root() -> JsonValue {
     json!({
-        "revolt": "0.4.1-alpha.1",
+        "revolt": "0.4.1-alpha.2",
         "features": {
             "registration": !*DISABLE_REGISTRATION,
             "captcha": {
diff --git a/src/routes/users/add_friend.rs b/src/routes/users/add_friend.rs
index f918fc8e340038d9e5cdae3132ec03dc4c808ebc..8d5cbef761268f82505ae1892e7c9f007ebddd42 100644
--- a/src/routes/users/add_friend.rs
+++ b/src/routes/users/add_friend.rs
@@ -31,6 +31,8 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> {
         with: "user",
     })?;
 
+    let target_user = Ref::from(target_id.to_string())?.fetch_user().await?;
+
     match get_relationship(&user, &target_id) {
         RelationshipStatus::User => return Err(Error::NoEffect),
         RelationshipStatus::Friend => return Err(Error::AlreadyFriends),
@@ -65,16 +67,19 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> {
                 )
             ) {
                 Ok(_) => {
+                    let target_user = target_user.from_override(&user, RelationshipStatus::Friend).await?;
+                    let user = user.from_override(&target_user, RelationshipStatus::Friend).await?;
+
                     try_join!(
                         ClientboundNotification::UserRelationship {
                             id: user.id.clone(),
-                            user: target_id.to_string(),
+                            user: target_user,
                             status: RelationshipStatus::Friend
                         }
                         .publish(user.id.clone()),
                         ClientboundNotification::UserRelationship {
                             id: target_id.to_string(),
-                            user: user.id.clone(),
+                            user,
                             status: RelationshipStatus::Friend
                         }
                         .publish(target_id.to_string())
@@ -121,16 +126,18 @@ pub async fn req(user: User, username: String) -> Result<JsonValue> {
                 )
             ) {
                 Ok(_) => {
+                    let target_user = target_user.from_override(&user, RelationshipStatus::Outgoing).await?;
+                    let user = user.from_override(&target_user, RelationshipStatus::Incoming).await?;
                     try_join!(
                         ClientboundNotification::UserRelationship {
                             id: user.id.clone(),
-                            user: target_id.to_string(),
+                            user: target_user,
                             status: RelationshipStatus::Outgoing
                         }
                         .publish(user.id.clone()),
                         ClientboundNotification::UserRelationship {
                             id: target_id.to_string(),
-                            user: user.id.clone(),
+                            user,
                             status: RelationshipStatus::Incoming
                         }
                         .publish(target_id.to_string())
diff --git a/src/routes/users/block_user.rs b/src/routes/users/block_user.rs
index 1fb51831da77965a9e0e7f254ba15d5e67dbf3c8..a554c289059363b6f7148c1ba1ef933166cb6ad4 100644
--- a/src/routes/users/block_user.rs
+++ b/src/routes/users/block_user.rs
@@ -10,6 +10,8 @@ use rocket_contrib::json::JsonValue;
 pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
     let col = get_collection("users");
 
+    let target = target.fetch_user().await?;
+
     match get_relationship(&user, &target.id) {
         RelationshipStatus::User | RelationshipStatus::Blocked => Err(Error::NoEffect),
         RelationshipStatus::BlockedOther => {
@@ -33,7 +35,7 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
 
             ClientboundNotification::UserRelationship {
                 id: user.id.clone(),
-                user: target.id.clone(),
+                user: target,
                 status: RelationshipStatus::Blocked,
             }
             .publish(user.id.clone())
@@ -74,19 +76,23 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                 )
             ) {
                 Ok(_) => {
+                    let target = target.from_override(&user, RelationshipStatus::Friend).await?;
+                    let user = user.from_override(&target, RelationshipStatus::Friend).await?;
+                    let target_id = target.id.clone();
+
                     try_join!(
                         ClientboundNotification::UserRelationship {
                             id: user.id.clone(),
-                            user: target.id.clone(),
+                            user: target,
                             status: RelationshipStatus::Blocked
                         }
                         .publish(user.id.clone()),
                         ClientboundNotification::UserRelationship {
-                            id: target.id.clone(),
-                            user: user.id.clone(),
+                            id: target_id.clone(),
+                            user,
                             status: RelationshipStatus::BlockedOther
                         }
-                        .publish(target.id.clone())
+                        .publish(target_id)
                     )
                     .ok();
 
@@ -128,19 +134,23 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                 )
             ) {
                 Ok(_) => {
+                    let target = target.from_override(&user, RelationshipStatus::Blocked).await?;
+                    let user = user.from_override(&target, RelationshipStatus::BlockedOther).await?;
+                    let target_id = target.id.clone();
+
                     try_join!(
                         ClientboundNotification::UserRelationship {
                             id: user.id.clone(),
-                            user: target.id.clone(),
+                            user: target,
                             status: RelationshipStatus::Blocked
                         }
                         .publish(user.id.clone()),
                         ClientboundNotification::UserRelationship {
-                            id: target.id.clone(),
-                            user: user.id.clone(),
+                            id: target_id.clone(),
+                            user,
                             status: RelationshipStatus::BlockedOther
                         }
-                        .publish(target.id.clone())
+                        .publish(target_id)
                     )
                     .ok();
 
diff --git a/src/routes/users/remove_friend.rs b/src/routes/users/remove_friend.rs
index 37e831a174ff6ff57e47f0d62e9eedab04bd45d5..882594d870c37548331e6459d31bea97a9a4c313 100644
--- a/src/routes/users/remove_friend.rs
+++ b/src/routes/users/remove_friend.rs
@@ -10,6 +10,8 @@ use rocket_contrib::json::JsonValue;
 pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
     let col = get_collection("users");
 
+    let target = target.fetch_user().await?;
+
     match get_relationship(&user, &target.id) {
         RelationshipStatus::Friend
         | RelationshipStatus::Outgoing
@@ -43,19 +45,23 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                 )
             ) {
                 Ok(_) => {
+                    let target = target.from_override(&user, RelationshipStatus::None).await?;
+                    let user = user.from_override(&target, RelationshipStatus::None).await?;
+                    let target_id = target.id.clone();
+
                     try_join!(
                         ClientboundNotification::UserRelationship {
                             id: user.id.clone(),
-                            user: target.id.clone(),
+                            user: target,
                             status: RelationshipStatus::None
                         }
                         .publish(user.id.clone()),
                         ClientboundNotification::UserRelationship {
-                            id: target.id.clone(),
-                            user: user.id.clone(),
+                            id: target_id.clone(),
+                            user,
                             status: RelationshipStatus::None
                         }
-                        .publish(target.id.clone())
+                        .publish(target_id)
                     )
                     .ok();
 
diff --git a/src/routes/users/unblock_user.rs b/src/routes/users/unblock_user.rs
index ec575c4bda7c404eca4597497186849a6de4a2ec..8be43035b1d914090691aff150ebc342313285bb 100644
--- a/src/routes/users/unblock_user.rs
+++ b/src/routes/users/unblock_user.rs
@@ -9,10 +9,11 @@ use rocket_contrib::json::JsonValue;
 #[delete("/<target>/block")]
 pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
     let col = get_collection("users");
+    let target = target.fetch_user().await?;
 
     match get_relationship(&user, &target.id) {
         RelationshipStatus::Blocked => {
-            match get_relationship(&target.fetch_user().await?, &user.id) {
+            match get_relationship(&target, &user.id) {
                 RelationshipStatus::Blocked => {
                     col.update_one(
                         doc! {
@@ -32,9 +33,10 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                         with: "user",
                     })?;
 
+                    let target = target.from_override(&user, RelationshipStatus::BlockedOther).await?;
                     ClientboundNotification::UserRelationship {
                         id: user.id.clone(),
-                        user: target.id.clone(),
+                        user: target,
                         status: RelationshipStatus::BlockedOther,
                     }
                     .publish(user.id.clone())
@@ -73,19 +75,23 @@ pub async fn req(user: User, target: Ref) -> Result<JsonValue> {
                         )
                     ) {
                         Ok(_) => {
+                            let target = target.from_override(&user, RelationshipStatus::None).await?;
+                            let user = user.from_override(&target, RelationshipStatus::None).await?;
+                            let target_id = target.id.clone();
+
                             try_join!(
                                 ClientboundNotification::UserRelationship {
                                     id: user.id.clone(),
-                                    user: target.id.clone(),
+                                    user: target,
                                     status: RelationshipStatus::None
                                 }
                                 .publish(user.id.clone()),
                                 ClientboundNotification::UserRelationship {
-                                    id: target.id.clone(),
-                                    user: user.id.clone(),
+                                    id: target_id.clone(),
+                                    user: user,
                                     status: RelationshipStatus::None
                                 }
-                                .publish(target.id.clone())
+                                .publish(target_id)
                             )
                             .ok();