From: Dessalines <tyhou13@gmx.com>
Date: Wed, 10 Mar 2021 22:33:55 +0000 (-0500)
Subject: ~80% done
X-Git-Url: http://these/git/%22https:/image.com/static/README.zh.hant.md?a=commitdiff_plain;h=ddf4a667b1ba15222287b30455a202bd3a336547;p=lemmy.git

~80% done
---

ddf4a667b1ba15222287b30455a202bd3a336547
diff --cc crates/api/src/comment.rs
index bd4f33f5,cb8b2d51..711e8e72
--- a/crates/api/src/comment.rs
+++ b/crates/api/src/comment.rs
@@@ -3,8 -3,8 +3,8 @@@ use crate::
    check_downvotes_enabled,
    collect_moderated_communities,
    get_post,
--  get_user_from_jwt,
--  get_user_from_jwt_opt,
++  get_local_user_view_from_jwt,
++  get_local_user_view_from_jwt_opt,
    is_mod_or_admin,
    Perform,
  };
@@@ -48,7 -48,7 +48,7 @@@ impl Perform for CreateComment 
      websocket_id: Option<ConnectionId>,
    ) -> Result<CommentResponse, LemmyError> {
      let data: &CreateComment = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let content_slurs_removed = remove_slurs(&data.content.to_owned());
  
@@@ -56,7 -56,7 +56,7 @@@
      let post_id = data.post_id;
      let post = get_post(post_id, context.pool()).await?;
  
--    check_community_ban(user.id, post.community_id, context.pool()).await?;
++    check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
  
      // Check if post is locked, no new comments
      if post.locked {
@@@ -80,7 -80,7 +80,7 @@@
        content: content_slurs_removed,
        parent_id: data.parent_id.to_owned(),
        post_id: data.post_id,
--      creator_id: user.id,
++      creator_id: local_user_view.person.id,
        removed: None,
        deleted: None,
        read: None,
@@@ -115,7 -115,7 +115,7 @@@
          Err(_e) => return Err(ApiError::err("couldnt_create_comment").into()),
        };
  
--    updated_comment.send_create(&user, context).await?;
++    updated_comment.send_create(&local_user_view.person, context).await?;
  
      // Scan the comment for user mentions, add those rows
      let post_id = post.id;
@@@ -123,7 -123,7 +123,7 @@@
      let recipient_ids = send_local_notifs(
        mentions,
        updated_comment.clone(),
--      &user,
++      local_user_view.person.clone(),
        post,
        context.pool(),
        true,
@@@ -134,7 -134,7 +134,7 @@@
      let like_form = CommentLikeForm {
        comment_id: inserted_comment.id,
        post_id,
-       person_id: user.id,
 -      user_id: user.id,
++      person_id: local_user_view.person.id,
        score: 1,
      };
  
@@@ -143,17 -143,17 +143,17 @@@
        return Err(ApiError::err("couldnt_like_comment").into());
      }
  
--    updated_comment.send_like(&user, context).await?;
++    updated_comment.send_like(&local_user_view.person, context).await?;
  
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let mut comment_view = blocking(context.pool(), move |conn| {
--      CommentView::read(&conn, inserted_comment.id, Some(user_id))
++      CommentView::read(&conn, inserted_comment.id, Some(person_id))
      })
      .await??;
  
      // If its a comment to yourself, mark it as read
      let comment_id = comment_view.comment.id;
--    if user.id == comment_view.get_recipient_id() {
++    if local_user_view.person.id == comment_view.get_recipient_id() {
        match blocking(context.pool(), move |conn| {
          Comment::update_read(conn, comment_id, true)
        })
@@@ -193,7 -193,7 +193,7 @@@ impl Perform for EditComment 
      websocket_id: Option<ConnectionId>,
    ) -> Result<CommentResponse, LemmyError> {
      let data: &EditComment = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let comment_id = data.comment_id;
      let orig_comment = blocking(context.pool(), move |conn| {
@@@ -201,10 -201,10 +201,10 @@@
      })
      .await??;
  
--    check_community_ban(user.id, orig_comment.community.id, context.pool()).await?;
++    check_community_ban(local_user_view.person.id, orig_comment.community.id, context.pool()).await?;
  
      // Verify that only the creator can edit
--    if user.id != orig_comment.creator.id {
++    if local_user_view.person.id != orig_comment.creator.id {
        return Err(ApiError::err("no_comment_edit_allowed").into());
      }
  
@@@ -221,7 -221,7 +221,7 @@@
      };
  
      // Send the apub update
--    updated_comment.send_update(&user, context).await?;
++    updated_comment.send_update(&local_user_view.person, context).await?;
  
      // Do the mentions / recipients
      let updated_comment_content = updated_comment.content.to_owned();
@@@ -229,7 -229,7 +229,7 @@@
      let recipient_ids = send_local_notifs(
        mentions,
        updated_comment,
--      &user,
++      local_user_view.person.clone(),
        orig_comment.post,
        context.pool(),
        false,
@@@ -237,9 -237,9 +237,9 @@@
      .await?;
  
      let comment_id = data.comment_id;
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let comment_view = blocking(context.pool(), move |conn| {
--      CommentView::read(conn, comment_id, Some(user_id))
++      CommentView::read(conn, comment_id, Some(person_id))
      })
      .await??;
  
@@@ -269,7 -269,7 +269,7 @@@ impl Perform for DeleteComment 
      websocket_id: Option<ConnectionId>,
    ) -> Result<CommentResponse, LemmyError> {
      let data: &DeleteComment = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let comment_id = data.comment_id;
      let orig_comment = blocking(context.pool(), move |conn| {
@@@ -277,10 -277,10 +277,10 @@@
      })
      .await??;
  
--    check_community_ban(user.id, orig_comment.community.id, context.pool()).await?;
++    check_community_ban(local_user_view.person.id, orig_comment.community.id, context.pool()).await?;
  
      // Verify that only the creator can delete
--    if user.id != orig_comment.creator.id {
++    if local_user_view.person.id != orig_comment.creator.id {
        return Err(ApiError::err("no_comment_edit_allowed").into());
      }
  
@@@ -297,16 -297,16 +297,16 @@@
  
      // Send the apub message
      if deleted {
--      updated_comment.send_delete(&user, context).await?;
++      updated_comment.send_delete(&local_user_view.person, context).await?;
      } else {
--      updated_comment.send_undo_delete(&user, context).await?;
++      updated_comment.send_undo_delete(&local_user_view.person, context).await?;
      }
  
      // Refetch it
      let comment_id = data.comment_id;
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let comment_view = blocking(context.pool(), move |conn| {
--      CommentView::read(conn, comment_id, Some(user_id))
++      CommentView::read(conn, comment_id, Some(person_id))
      })
      .await??;
  
@@@ -316,7 -316,7 +316,7 @@@
      let recipient_ids = send_local_notifs(
        mentions,
        updated_comment,
--      &user,
++      local_user_view.person.clone(),
        comment_view_2.post,
        context.pool(),
        false,
@@@ -349,7 -349,7 +349,7 @@@ impl Perform for RemoveComment 
      websocket_id: Option<ConnectionId>,
    ) -> Result<CommentResponse, LemmyError> {
      let data: &RemoveComment = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let comment_id = data.comment_id;
      let orig_comment = blocking(context.pool(), move |conn| {
@@@ -357,10 -357,10 +357,10 @@@
      })
      .await??;
  
--    check_community_ban(user.id, orig_comment.community.id, context.pool()).await?;
++    check_community_ban(local_user_view.person.id, orig_comment.community.id, context.pool()).await?;
  
      // Verify that only a mod or admin can remove
--    is_mod_or_admin(context.pool(), user.id, orig_comment.community.id).await?;
++    is_mod_or_admin(context.pool(), local_user_view.person.id, orig_comment.community.id).await?;
  
      // Do the remove
      let removed = data.removed;
@@@ -375,7 -375,7 +375,7 @@@
  
      // Mod tables
      let form = ModRemoveCommentForm {
-       mod_person_id: user.id,
 -      mod_user_id: user.id,
++      mod_person_id: local_user_view.person.id,
        comment_id: data.comment_id,
        removed: Some(removed),
        reason: data.reason.to_owned(),
@@@ -387,16 -387,16 +387,16 @@@
  
      // Send the apub message
      if removed {
--      updated_comment.send_remove(&user, context).await?;
++      updated_comment.send_remove(&local_user_view.person, context).await?;
      } else {
--      updated_comment.send_undo_remove(&user, context).await?;
++      updated_comment.send_undo_remove(&local_user_view.person, context).await?;
      }
  
      // Refetch it
      let comment_id = data.comment_id;
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let comment_view = blocking(context.pool(), move |conn| {
--      CommentView::read(conn, comment_id, Some(user_id))
++      CommentView::read(conn, comment_id, Some(person_id))
      })
      .await??;
  
@@@ -407,7 -407,7 +407,7 @@@
      let recipient_ids = send_local_notifs(
        mentions,
        updated_comment,
--      &user,
++      local_user_view.person.clone(),
        comment_view_2.post,
        context.pool(),
        false,
@@@ -440,7 -440,7 +440,7 @@@ impl Perform for MarkCommentAsRead 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<CommentResponse, LemmyError> {
      let data: &MarkCommentAsRead = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let comment_id = data.comment_id;
      let orig_comment = blocking(context.pool(), move |conn| {
@@@ -448,10 -448,10 +448,10 @@@
      })
      .await??;
  
--    check_community_ban(user.id, orig_comment.community.id, context.pool()).await?;
++    check_community_ban(local_user_view.person.id, orig_comment.community.id, context.pool()).await?;
  
      // Verify that only the recipient can mark as read
--    if user.id != orig_comment.get_recipient_id() {
++    if local_user_view.person.id != orig_comment.get_recipient_id() {
        return Err(ApiError::err("no_comment_edit_allowed").into());
      }
  
@@@ -468,9 -468,9 +468,9 @@@
  
      // Refetch it
      let comment_id = data.comment_id;
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let comment_view = blocking(context.pool(), move |conn| {
--      CommentView::read(conn, comment_id, Some(user_id))
++      CommentView::read(conn, comment_id, Some(person_id))
      })
      .await??;
  
@@@ -494,11 -494,11 +494,11 @@@ impl Perform for SaveComment 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<CommentResponse, LemmyError> {
      let data: &SaveComment = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let comment_saved_form = CommentSavedForm {
        comment_id: data.comment_id,
-       person_id: user.id,
 -      user_id: user.id,
++      person_id: local_user_view.person.id,
      };
  
      if data.save {
@@@ -514,9 -514,9 +514,9 @@@
      }
  
      let comment_id = data.comment_id;
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let comment_view = blocking(context.pool(), move |conn| {
--      CommentView::read(conn, comment_id, Some(user_id))
++      CommentView::read(conn, comment_id, Some(person_id))
      })
      .await??;
  
@@@ -538,7 -538,7 +538,7 @@@ impl Perform for CreateCommentLike 
      websocket_id: Option<ConnectionId>,
    ) -> Result<CommentResponse, LemmyError> {
      let data: &CreateCommentLike = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let mut recipient_ids = Vec::new();
  
@@@ -551,7 -551,7 +551,7 @@@
      })
      .await??;
  
--    check_community_ban(user.id, orig_comment.community.id, context.pool()).await?;
++    check_community_ban(local_user_view.person.id, orig_comment.community.id, context.pool()).await?;
  
      // Add parent user to recipients
      recipient_ids.push(orig_comment.get_recipient_id());
@@@ -559,14 -559,14 +559,14 @@@
      let like_form = CommentLikeForm {
        comment_id: data.comment_id,
        post_id: orig_comment.post.id,
-       person_id: user.id,
 -      user_id: user.id,
++      person_id: local_user_view.person.id,
        score: data.score,
      };
  
      // Remove any likes first
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      blocking(context.pool(), move |conn| {
--      CommentLike::remove(conn, user_id, comment_id)
++      CommentLike::remove(conn, person_id, comment_id)
      })
      .await??;
  
@@@ -581,19 -581,19 +581,19 @@@
        }
  
        if like_form.score == 1 {
--        comment.send_like(&user, context).await?;
++        comment.send_like(&local_user_view.person, context).await?;
        } else if like_form.score == -1 {
--        comment.send_dislike(&user, context).await?;
++        comment.send_dislike(&local_user_view.person, context).await?;
        }
      } else {
--      comment.send_undo_like(&user, context).await?;
++      comment.send_undo_like(&local_user_view.person, context).await?;
      }
  
      // Have to refetch the comment to get the current state
      let comment_id = data.comment_id;
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let liked_comment = blocking(context.pool(), move |conn| {
--      CommentView::read(conn, comment_id, Some(user_id))
++      CommentView::read(conn, comment_id, Some(person_id))
      })
      .await??;
  
@@@ -623,8 -623,8 +623,8 @@@ impl Perform for GetComments 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<GetCommentsResponse, LemmyError> {
      let data: &GetComments = &self;
--    let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?;
--    let user_id = user.map(|u| u.id);
++    let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
++    let person_id = local_user_view.map(|u| u.person.id);
  
      let type_ = ListingType::from_str(&data.type_)?;
      let sort = SortType::from_str(&data.sort)?;
@@@ -639,7 -639,7 +639,7 @@@
          .sort(&sort)
          .community_id(community_id)
          .community_name(community_name)
--        .my_user_id(user_id)
++        .my_person_id(person_id)
          .page(page)
          .limit(limit)
          .list()
@@@ -665,7 -665,7 +665,7 @@@ impl Perform for CreateCommentReport 
      websocket_id: Option<ConnectionId>,
    ) -> Result<CreateCommentReportResponse, LemmyError> {
      let data: &CreateCommentReport = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      // check size of report and check for whitespace
      let reason = data.reason.trim();
@@@ -676,17 -676,17 +676,17 @@@
        return Err(ApiError::err("report_too_long").into());
      }
  
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let comment_id = data.comment_id;
      let comment_view = blocking(context.pool(), move |conn| {
        CommentView::read(&conn, comment_id, None)
      })
      .await??;
  
--    check_community_ban(user_id, comment_view.community.id, context.pool()).await?;
++    check_community_ban(person_id, comment_view.community.id, context.pool()).await?;
  
      let report_form = CommentReportForm {
--      creator_id: user_id,
++      creator_id: person_id,
        comment_id,
        original_comment_text: comment_view.comment.content,
        reason: data.reason.to_owned(),
@@@ -706,7 -706,7 +706,7 @@@
      context.chat_server().do_send(SendUserRoomMessage {
        op: UserOperation::CreateCommentReport,
        response: res.clone(),
--      recipient_id: user.id,
++      recipient_id: local_user_view.person.id,
        websocket_id,
      });
  
@@@ -732,7 -732,7 +732,7 @@@ impl Perform for ResolveCommentReport 
      websocket_id: Option<ConnectionId>,
    ) -> Result<ResolveCommentReportResponse, LemmyError> {
      let data: &ResolveCommentReport = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let report_id = data.report_id;
      let report = blocking(context.pool(), move |conn| {
@@@ -740,15 -740,15 +740,15 @@@
      })
      .await??;
  
--    let user_id = user.id;
--    is_mod_or_admin(context.pool(), user_id, report.community.id).await?;
++    let person_id = local_user_view.person.id;
++    is_mod_or_admin(context.pool(), person_id, report.community.id).await?;
  
      let resolved = data.resolved;
      let resolve_fun = move |conn: &'_ _| {
        if resolved {
--        CommentReport::resolve(conn, report_id, user_id)
++        CommentReport::resolve(conn, report_id, person_id)
        } else {
--        CommentReport::unresolve(conn, report_id, user_id)
++        CommentReport::unresolve(conn, report_id, person_id)
        }
      };
  
@@@ -785,12 -785,12 +785,12 @@@ impl Perform for ListCommentReports 
      websocket_id: Option<ConnectionId>,
    ) -> Result<ListCommentReportsResponse, LemmyError> {
      let data: &ListCommentReports = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let community_id = data.community;
      let community_ids =
--      collect_moderated_communities(user_id, community_id, context.pool()).await?;
++      collect_moderated_communities(person_id, community_id, context.pool()).await?;
  
      let page = data.page;
      let limit = data.limit;
@@@ -808,7 -808,7 +808,7 @@@
      context.chat_server().do_send(SendUserRoomMessage {
        op: UserOperation::ListCommentReports,
        response: res.clone(),
--      recipient_id: user.id,
++      recipient_id: local_user_view.person.id,
        websocket_id,
      });
  
diff --cc crates/api/src/community.rs
index 40dc345e,cee5d371..6949a4a9
--- a/crates/api/src/community.rs
+++ b/crates/api/src/community.rs
@@@ -1,8 -1,7 +1,7 @@@
  use crate::{
    check_community_ban,
-   check_optional_url,
--  get_user_from_jwt,
--  get_user_from_jwt_opt,
++  get_local_user_view_from_jwt,
++  get_local_user_view_from_jwt_opt,
    is_admin,
    is_mod_or_admin,
    Perform,
@@@ -41,9 -41,8 +41,8 @@@ use lemmy_db_views_actor::
    community_follower_view::CommunityFollowerView,
    community_moderator_view::CommunityModeratorView,
    community_view::{CommunityQueryBuilder, CommunityView},
--  user_view::UserViewSafe,
++  person_view::PersonViewSafe,
  };
- use lemmy_structs::{blocking, community::*};
  use lemmy_utils::{
    apub::generate_actor_keypair,
    location_info,
@@@ -69,8 -68,8 +68,8 @@@ impl Perform for GetCommunity 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<GetCommunityResponse, LemmyError> {
      let data: &GetCommunity = &self;
--    let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?;
--    let user_id = user.map(|u| u.id);
++    let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
++    let person_id = local_user_view.map(|u| u.person.id);
  
      let community_id = match data.id {
        Some(id) => id,
@@@ -89,7 -88,7 +88,7 @@@
      };
  
      let community_view = match blocking(context.pool(), move |conn| {
--      CommunityView::read(conn, community_id, user_id)
++      CommunityView::read(conn, community_id, person_id)
      })
      .await?
      {
@@@ -133,7 -132,7 +132,7 @@@ impl Perform for CreateCommunity 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<CommunityResponse, LemmyError> {
      let data: &CreateCommunity = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      check_slurs(&data.name)?;
      check_slurs(&data.title)?;
@@@ -170,7 -166,7 +166,7 @@@
        description: data.description.to_owned(),
        icon,
        banner,
--      creator_id: user.id,
++      creator_id: local_user_view.person.id,
        removed: None,
        deleted: None,
        nsfw: data.nsfw,
@@@ -198,7 -194,7 +194,7 @@@
      // The community creator becomes a moderator
      let community_moderator_form = CommunityModeratorForm {
        community_id: inserted_community.id,
-       person_id: user.id,
 -      user_id: user.id,
++      person_id: local_user_view.person.id,
      };
  
      let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
@@@ -209,7 -205,7 +205,7 @@@
      // Follow your own community
      let community_follower_form = CommunityFollowerForm {
        community_id: inserted_community.id,
-       person_id: user.id,
 -      user_id: user.id,
++      person_id: local_user_view.person.id,
        pending: false,
      };
  
@@@ -218,9 -214,9 +214,9 @@@
        return Err(ApiError::err("community_follower_already_exists").into());
      }
  
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let community_view = blocking(context.pool(), move |conn| {
--      CommunityView::read(conn, inserted_community.id, Some(user_id))
++      CommunityView::read(conn, inserted_community.id, Some(person_id))
      })
      .await??;
  
@@@ -238,7 -234,7 +234,7 @@@ impl Perform for EditCommunity 
      websocket_id: Option<ConnectionId>,
    ) -> Result<CommunityResponse, LemmyError> {
      let data: &EditCommunity = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      check_slurs(&data.title)?;
      check_slurs_opt(&data.description)?;
@@@ -250,7 -246,7 +246,7 @@@
          .map(|v| v.into_iter().map(|m| m.moderator.id).collect())
      })
      .await??;
--    if !mods.contains(&user.id) {
++    if !mods.contains(&local_user_view.person.id) {
        return Err(ApiError::err("not_a_moderator").into());
      }
  
@@@ -302,9 -295,9 +295,9 @@@
      // process for communities and users
  
      let community_id = data.community_id;
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let community_view = blocking(context.pool(), move |conn| {
--      CommunityView::read(conn, community_id, Some(user_id))
++      CommunityView::read(conn, community_id, Some(person_id))
      })
      .await??;
  
@@@ -326,7 -319,7 +319,7 @@@ impl Perform for DeleteCommunity 
      websocket_id: Option<ConnectionId>,
    ) -> Result<CommunityResponse, LemmyError> {
      let data: &DeleteCommunity = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      // Verify its the creator (only a creator can delete the community)
      let community_id = data.community_id;
@@@ -334,7 -327,7 +327,7 @@@
        Community::read(conn, community_id)
      })
      .await??;
--    if read_community.creator_id != user.id {
++    if read_community.creator_id != local_user_view.person.id {
        return Err(ApiError::err("no_community_edit_allowed").into());
      }
  
@@@ -358,9 -351,9 +351,9 @@@
      }
  
      let community_id = data.community_id;
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let community_view = blocking(context.pool(), move |conn| {
--      CommunityView::read(conn, community_id, Some(user_id))
++      CommunityView::read(conn, community_id, Some(person_id))
      })
      .await??;
  
@@@ -382,10 -375,10 +375,10 @@@ impl Perform for RemoveCommunity 
      websocket_id: Option<ConnectionId>,
    ) -> Result<CommunityResponse, LemmyError> {
      let data: &RemoveCommunity = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      // Verify its an admin (only an admin can remove a community)
--    is_admin(context.pool(), user.id).await?;
++    is_admin(&local_user_view)?;
  
      // Do the remove
      let community_id = data.community_id;
@@@ -405,7 -398,7 +398,7 @@@
        None => None,
      };
      let form = ModRemoveCommunityForm {
-       mod_person_id: user.id,
 -      mod_user_id: user.id,
++      mod_person_id: local_user_view.person.id,
        community_id: data.community_id,
        removed: Some(removed),
        reason: data.reason.to_owned(),
@@@ -424,9 -417,9 +417,9 @@@
      }
  
      let community_id = data.community_id;
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let community_view = blocking(context.pool(), move |conn| {
--      CommunityView::read(conn, community_id, Some(user_id))
++      CommunityView::read(conn, community_id, Some(person_id))
      })
      .await??;
  
@@@ -448,15 -441,15 +441,16 @@@ impl Perform for ListCommunities 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<ListCommunitiesResponse, LemmyError> {
      let data: &ListCommunities = &self;
--    let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
  
--    let user_id = match &user {
--      Some(user) => Some(user.id),
++    let person_id = match &local_user_view {
++      Some(uv) => Some(uv.person.id),
        None => None,
      };
  
--    let show_nsfw = match &user {
--      Some(user) => user.show_nsfw,
++    // Don't show NSFW by default
++    let show_nsfw = match &local_user_view {
++      Some(uv) => uv.local_user.show_nsfw,
        None => false,
      };
  
@@@ -470,7 -463,7 +464,7 @@@
          .listing_type(&type_)
          .sort(&sort)
          .show_nsfw(show_nsfw)
--        .my_user_id(user_id)
++        .my_person_id(person_id)
          .page(page)
          .limit(limit)
          .list()
@@@ -492,7 -485,7 +486,7 @@@ impl Perform for FollowCommunity 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<CommunityResponse, LemmyError> {
      let data: &FollowCommunity = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let community_id = data.community_id;
      let community = blocking(context.pool(), move |conn| {
@@@ -501,13 -494,13 +495,13 @@@
      .await??;
      let community_follower_form = CommunityFollowerForm {
        community_id: data.community_id,
-       person_id: user.id,
 -      user_id: user.id,
++      person_id: local_user_view.person.id,
        pending: false,
      };
  
      if community.local {
        if data.follow {
--        check_community_ban(user.id, community_id, context.pool()).await?;
++        check_community_ban(local_user_view.person.id, community_id, context.pool()).await?;
  
          let follow = move |conn: &'_ _| CommunityFollower::follow(conn, &community_follower_form);
          if blocking(context.pool(), follow).await?.is_err() {
@@@ -523,9 -516,9 +517,9 @@@
      } else if data.follow {
        // Dont actually add to the community followers here, because you need
        // to wait for the accept
--      user.send_follow(&community.actor_id(), context).await?;
++      local_user_view.person.send_follow(&community.actor_id(), context).await?;
      } else {
--      user.send_unfollow(&community.actor_id(), context).await?;
++      local_user_view.person.send_unfollow(&community.actor_id(), context).await?;
        let unfollow = move |conn: &'_ _| CommunityFollower::unfollow(conn, &community_follower_form);
        if blocking(context.pool(), unfollow).await?.is_err() {
          return Err(ApiError::err("community_follower_already_exists").into());
@@@ -533,9 -526,9 +527,9 @@@
      }
  
      let community_id = data.community_id;
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let mut community_view = blocking(context.pool(), move |conn| {
--      CommunityView::read(conn, community_id, Some(user_id))
++      CommunityView::read(conn, community_id, Some(person_id))
      })
      .await??;
  
@@@ -560,11 -553,11 +554,11 @@@ impl Perform for GetFollowedCommunitie
      _websocket_id: Option<ConnectionId>,
    ) -> Result<GetFollowedCommunitiesResponse, LemmyError> {
      let data: &GetFollowedCommunities = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let communities = match blocking(context.pool(), move |conn| {
--      CommunityFollowerView::for_user(conn, user_id)
++      CommunityFollowerView::for_person(conn, person_id)
      })
      .await?
      {
@@@ -587,17 -580,17 +581,17 @@@ impl Perform for BanFromCommunity 
      websocket_id: Option<ConnectionId>,
    ) -> Result<BanFromCommunityResponse, LemmyError> {
      let data: &BanFromCommunity = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let community_id = data.community_id;
--    let banned_user_id = data.user_id;
++    let banned_person_id = data.person_id;
  
      // Verify that only mods or admins can ban
--    is_mod_or_admin(context.pool(), user.id, community_id).await?;
++    is_mod_or_admin(context.pool(), local_user_view.person.id, community_id).await?;
  
 -    let community_user_ban_form = CommunityUserBanForm {
 +    let community_user_ban_form = CommunityPersonBanForm {
        community_id: data.community_id,
-       person_id: data.user_id,
 -      user_id: data.user_id,
++      person_id: data.person_id,
      };
  
      if data.ban {
@@@ -609,7 -602,7 +603,7 @@@
        // Also unsubscribe them from the community, if they are subscribed
        let community_follower_form = CommunityFollowerForm {
          community_id: data.community_id,
-         person_id: banned_user_id,
 -        user_id: banned_user_id,
++        person_id: banned_person_id,
          pending: false,
        };
        blocking(context.pool(), move |conn: &'_ _| {
@@@ -628,7 -621,7 +622,7 @@@
      if data.remove_data {
        // Posts
        blocking(context.pool(), move |conn: &'_ _| {
--        Post::update_removed_for_creator(conn, banned_user_id, Some(community_id), true)
++        Post::update_removed_for_creator(conn, banned_person_id, Some(community_id), true)
        })
        .await??;
  
@@@ -636,7 -629,7 +630,7 @@@
        // TODO Diesel doesn't allow updates with joins, so this has to be a loop
        let comments = blocking(context.pool(), move |conn| {
          CommentQueryBuilder::create(conn)
--          .creator_id(banned_user_id)
++          .creator_id(banned_person_id)
            .community_id(community_id)
            .limit(std::i64::MAX)
            .list()
@@@ -660,8 -653,8 +654,8 @@@
      };
  
      let form = ModBanFromCommunityForm {
-       mod_person_id: user.id,
-       other_person_id: data.user_id,
 -      mod_user_id: user.id,
 -      other_user_id: data.user_id,
++      mod_person_id: local_user_view.person.id,
++      other_person_id: data.person_id,
        community_id: data.community_id,
        reason: data.reason.to_owned(),
        banned: Some(data.ban),
@@@ -672,14 -665,14 +666,14 @@@
      })
      .await??;
  
--    let user_id = data.user_id;
--    let user_view = blocking(context.pool(), move |conn| {
--      UserViewSafe::read(conn, user_id)
++    let person_id = data.person_id;
++    let person_view = blocking(context.pool(), move |conn| {
++      PersonViewSafe::read(conn, person_id)
      })
      .await??;
  
      let res = BanFromCommunityResponse {
--      user_view,
++      person_view,
        banned: data.ban,
      };
  
@@@ -704,17 -697,17 +698,17 @@@ impl Perform for AddModToCommunity 
      websocket_id: Option<ConnectionId>,
    ) -> Result<AddModToCommunityResponse, LemmyError> {
      let data: &AddModToCommunity = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let community_moderator_form = CommunityModeratorForm {
        community_id: data.community_id,
-       person_id: data.user_id,
 -      user_id: data.user_id,
++      person_id: data.person_id,
      };
  
      let community_id = data.community_id;
  
      // Verify that only mods or admins can add mod
--    is_mod_or_admin(context.pool(), user.id, community_id).await?;
++    is_mod_or_admin(context.pool(), local_user_view.person.id, community_id).await?;
  
      if data.added {
        let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
@@@ -730,8 -723,8 +724,8 @@@
  
      // Mod tables
      let form = ModAddCommunityForm {
-       mod_person_id: user.id,
-       other_person_id: data.user_id,
 -      mod_user_id: user.id,
 -      other_user_id: data.user_id,
++      mod_person_id: local_user_view.person.id,
++      other_person_id: data.person_id,
        community_id: data.community_id,
        removed: Some(!data.added),
      };
@@@ -769,7 -762,7 +763,7 @@@ impl Perform for TransferCommunity 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<GetCommunityResponse, LemmyError> {
      let data: &TransferCommunity = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let community_id = data.community_id;
      let read_community = blocking(context.pool(), move |conn| {
@@@ -782,25 -775,25 +776,25 @@@
      })
      .await??;
  
--    let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??;
++    let mut admins = blocking(context.pool(), move |conn| PersonViewSafe::admins(conn)).await??;
  
      // Making sure the creator, if an admin, is at the top
      let creator_index = admins
        .iter()
--      .position(|r| r.user.id == site_creator_id)
++      .position(|r| r.person.id == site_creator_id)
        .context(location_info!())?;
--    let creator_user = admins.remove(creator_index);
--    admins.insert(0, creator_user);
++    let creator_person = admins.remove(creator_index);
++    admins.insert(0, creator_person);
  
      // Make sure user is the creator, or an admin
--    if user.id != read_community.creator_id
--      && !admins.iter().map(|a| a.user.id).any(|x| x == user.id)
++    if local_user_view.person.id != read_community.creator_id
++      && !admins.iter().map(|a| a.person.id).any(|x| x == local_user_view.person.id)
      {
        return Err(ApiError::err("not_an_admin").into());
      }
  
      let community_id = data.community_id;
--    let new_creator = data.user_id;
++    let new_creator = data.person_id;
      let update = move |conn: &'_ _| Community::update_creator(conn, community_id, new_creator);
      if blocking(context.pool(), update).await?.is_err() {
        return Err(ApiError::err("couldnt_update_community").into());
@@@ -814,10 -807,10 +808,10 @@@
      .await??;
      let creator_index = community_mods
        .iter()
--      .position(|r| r.moderator.id == data.user_id)
++      .position(|r| r.moderator.id == data.person_id)
        .context(location_info!())?;
--    let creator_user = community_mods.remove(creator_index);
--    community_mods.insert(0, creator_user);
++    let creator_person = community_mods.remove(creator_index);
++    community_mods.insert(0, creator_person);
  
      let community_id = data.community_id;
      blocking(context.pool(), move |conn| {
@@@ -840,8 -833,8 +834,8 @@@
  
      // Mod tables
      let form = ModAddCommunityForm {
-       mod_person_id: user.id,
-       other_person_id: data.user_id,
 -      mod_user_id: user.id,
 -      other_user_id: data.user_id,
++      mod_person_id: local_user_view.person.id,
++      other_person_id: data.person_id,
        community_id: data.community_id,
        removed: Some(false),
      };
@@@ -851,9 -844,9 +845,9 @@@
      .await??;
  
      let community_id = data.community_id;
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let community_view = match blocking(context.pool(), move |conn| {
--      CommunityView::read(conn, community_id, Some(user_id))
++      CommunityView::read(conn, community_id, Some(person_id))
      })
      .await?
      {
@@@ -886,7 -879,7 +880,7 @@@ fn send_community_websocket
    websocket_id: Option<ConnectionId>,
    op: UserOperation,
  ) {
--  // Strip out the user id and subscribed when sending to others
++  // Strip out the person id and subscribed when sending to others
    let mut res_sent = res.clone();
    res_sent.community_view.subscribed = false;
  
diff --cc crates/api/src/lib.rs
index ab0bc8ac,d87375ca..3fe3bc7a
--- a/crates/api/src/lib.rs
+++ b/crates/api/src/lib.rs
@@@ -1,9 -1,18 +1,19 @@@
  use actix_web::{web, web::Data};
+ use lemmy_api_structs::{
+   blocking,
+   comment::*,
+   community::*,
+   post::*,
+   site::*,
 -  user::*,
++  person::*,
+   websocket::*,
+ };
  use lemmy_db_queries::{
    source::{
      community::{CommunityModerator_, Community_},
      site::Site_,
--    user::UserSafeSettings_,
++    local_user::LocalUserSettings_,
++    local_user::LocalUser_,
    },
    Crud,
    DbPool,
@@@ -12,14 -21,19 +22,22 @@@ use lemmy_db_schema::source::
    community::{Community, CommunityModerator},
    post::Post,
    site::Site,
--  user::{UserSafeSettings, User_},
++  person::{Person, PersonSafe},
++  local_user::LocalUserSettings,
++  local_user::LocalUser,
  };
  use lemmy_db_views_actor::{
--  community_user_ban_view::CommunityUserBanView,
++  community_person_ban_view::CommunityPersonBanView,
    community_view::CommunityView,
  };
- use lemmy_structs::{blocking, comment::*, community::*, post::*, site::*, user::*, websocket::*};
- use lemmy_utils::{claims::Claims, settings::Settings, ApiError, ConnectionId, LemmyError};
++use lemmy_db_views::local_user_view::{LocalUserView, LocalUserSettingsView};
+ use lemmy_utils::{
+   claims::Claims,
+   settings::structs::Settings,
+   ApiError,
+   ConnectionId,
+   LemmyError,
+ };
  use lemmy_websocket::{serialize_websocket_message, LemmyContext, UserOperation};
  use serde::Deserialize;
  use std::process::Command;
@@@ -46,11 -60,11 +64,11 @@@ pub trait Perform 
  
  pub(crate) async fn is_mod_or_admin(
    pool: &DbPool,
--  user_id: i32,
++  person_id: i32,
    community_id: i32,
  ) -> Result<(), LemmyError> {
    let is_mod_or_admin = blocking(pool, move |conn| {
--    CommunityView::is_mod_or_admin(conn, user_id, community_id)
++    CommunityView::is_mod_or_admin(conn, person_id, community_id)
    })
    .await?;
    if !is_mod_or_admin {
@@@ -58,9 -72,9 +76,18 @@@
    }
    Ok(())
  }
--pub async fn is_admin(pool: &DbPool, user_id: i32) -> Result<(), LemmyError> {
--  let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
--  if !user.admin {
++
++// TODO this probably isn't necessary anymore
++// pub async fn is_admin(pool: &DbPool, person_id: i32) -> Result<(), LemmyError> {
++//   let user = blocking(pool, move |conn| LocalUser::read(conn, person_id)).await??;
++//   if !user.admin {
++//     return Err(ApiError::err("not_an_admin").into());
++//   }
++//   Ok(())
++// }
++
++pub fn is_admin(local_user_view: &LocalUserView) -> Result<(), LemmyError> {
++  if !local_user_view.local_user.admin {
      return Err(ApiError::err("not_an_admin").into());
    }
    Ok(())
@@@ -73,63 -87,63 +100,60 @@@ pub(crate) async fn get_post(post_id: i
    }
  }
  
--pub(crate) async fn get_user_from_jwt(jwt: &str, pool: &DbPool) -> Result<User_, LemmyError> {
++pub(crate) async fn get_local_user_view_from_jwt(jwt: &str, pool: &DbPool) -> Result<LocalUserView, LemmyError> {
    let claims = match Claims::decode(&jwt) {
      Ok(claims) => claims.claims,
      Err(_e) => return Err(ApiError::err("not_logged_in").into()),
    };
--  let user_id = claims.id;
--  let user = blocking(pool, move |conn| User_::read(conn, user_id)).await??;
++  let person_id = claims.id;
++  let local_user_view = blocking(pool, move |conn| LocalUserView::read(conn, person_id)).await??;
    // Check for a site ban
--  if user.banned {
++  if local_user_view.person.banned {
      return Err(ApiError::err("site_ban").into());
    }
--  Ok(user)
++  Ok(local_user_view)
  }
  
--pub(crate) async fn get_user_from_jwt_opt(
++pub(crate) async fn get_local_user_view_from_jwt_opt(
    jwt: &Option<String>,
    pool: &DbPool,
--) -> Result<Option<User_>, LemmyError> {
++) -> Result<Option<LocalUserView>, LemmyError> {
    match jwt {
--    Some(jwt) => Ok(Some(get_user_from_jwt(jwt, pool).await?)),
++    Some(jwt) => Ok(Some(get_local_user_view_from_jwt(jwt, pool).await?)),
      None => Ok(None),
    }
  }
  
--pub(crate) async fn get_user_safe_settings_from_jwt(
--  jwt: &str,
--  pool: &DbPool,
--) -> Result<UserSafeSettings, LemmyError> {
++pub(crate) async fn get_local_user_settings_view_from_jwt(jwt: &str, pool: &DbPool) -> Result<LocalUserSettingsView, LemmyError> {
    let claims = match Claims::decode(&jwt) {
      Ok(claims) => claims.claims,
      Err(_e) => return Err(ApiError::err("not_logged_in").into()),
    };
--  let user_id = claims.id;
--  let user = blocking(pool, move |conn| UserSafeSettings::read(conn, user_id)).await??;
++  let person_id = claims.id;
++  let local_user_view = blocking(pool, move |conn| LocalUserSettingsView::read(conn, person_id)).await??;
    // Check for a site ban
--  if user.banned {
++  if local_user_view.person.banned {
      return Err(ApiError::err("site_ban").into());
    }
--  Ok(user)
++  Ok(local_user_view)
  }
  
--pub(crate) async fn get_user_safe_settings_from_jwt_opt(
++pub(crate) async fn get_local_user_settings_view_from_jwt_opt(
    jwt: &Option<String>,
    pool: &DbPool,
--) -> Result<Option<UserSafeSettings>, LemmyError> {
++) -> Result<Option<LocalUserSettingsView>, LemmyError> {
    match jwt {
--    Some(jwt) => Ok(Some(get_user_safe_settings_from_jwt(jwt, pool).await?)),
++    Some(jwt) => Ok(Some(get_local_user_settings_view_from_jwt(jwt, pool).await?)),
      None => Ok(None),
    }
  }
  
  pub(crate) async fn check_community_ban(
--  user_id: i32,
++  person_id: i32,
    community_id: i32,
    pool: &DbPool,
  ) -> Result<(), LemmyError> {
--  let is_banned = move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
++  let is_banned = move |conn: &'_ _| CommunityPersonBanView::get(conn, person_id, community_id).is_ok();
    if blocking(pool, is_banned).await? {
      Err(ApiError::err("community_ban").into())
    } else {
@@@ -226,17 -236,17 +246,17 @@@ pub async fn match_websocket_operation
      UserOperation::Login => do_websocket_operation::<Login>(context, id, op, data).await,
      UserOperation::Register => do_websocket_operation::<Register>(context, id, op, data).await,
      UserOperation::GetCaptcha => do_websocket_operation::<GetCaptcha>(context, id, op, data).await,
--    UserOperation::GetUserDetails => {
--      do_websocket_operation::<GetUserDetails>(context, id, op, data).await
++    UserOperation::GetPersonDetails => {
++      do_websocket_operation::<GetPersonDetails>(context, id, op, data).await
      }
      UserOperation::GetReplies => do_websocket_operation::<GetReplies>(context, id, op, data).await,
      UserOperation::AddAdmin => do_websocket_operation::<AddAdmin>(context, id, op, data).await,
--    UserOperation::BanUser => do_websocket_operation::<BanUser>(context, id, op, data).await,
--    UserOperation::GetUserMentions => {
--      do_websocket_operation::<GetUserMentions>(context, id, op, data).await
++    UserOperation::BanPerson => do_websocket_operation::<BanPerson>(context, id, op, data).await,
++    UserOperation::GetPersonMentions => {
++      do_websocket_operation::<GetPersonMentions>(context, id, op, data).await
      }
--    UserOperation::MarkUserMentionAsRead => {
--      do_websocket_operation::<MarkUserMentionAsRead>(context, id, op, data).await
++    UserOperation::MarkPersonMentionAsRead => {
++      do_websocket_operation::<MarkPersonMentionAsRead>(context, id, op, data).await
      }
      UserOperation::MarkAllAsRead => {
        do_websocket_operation::<MarkAllAsRead>(context, id, op, data).await
diff --cc crates/api/src/post.rs
index 33983a4e,202ea30b..92226171
--- a/crates/api/src/post.rs
+++ b/crates/api/src/post.rs
@@@ -1,10 -1,9 +1,9 @@@
  use crate::{
    check_community_ban,
    check_downvotes_enabled,
-   check_optional_url,
    collect_moderated_communities,
--  get_user_from_jwt,
--  get_user_from_jwt_opt,
++  get_local_user_view_from_jwt,
++  get_local_user_view_from_jwt_opt,
    is_mod_or_admin,
    Perform,
  };
@@@ -61,7 -60,7 +60,7 @@@ impl Perform for CreatePost 
      websocket_id: Option<ConnectionId>,
    ) -> Result<PostResponse, LemmyError> {
      let data: &CreatePost = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      check_slurs(&data.name)?;
      check_slurs_opt(&data.body)?;
@@@ -70,20 -69,19 +69,19 @@@
        return Err(ApiError::err("invalid_post_title").into());
      }
  
--    check_community_ban(user.id, data.community_id, context.pool()).await?;
- 
-     check_optional_url(&Some(data.url.to_owned()))?;
++    check_community_ban(local_user_view.person.id, data.community_id, context.pool()).await?;
  
      // Fetch Iframely and pictrs cached image
+     let data_url = data.url.as_ref();
      let (iframely_title, iframely_description, iframely_html, pictrs_thumbnail) =
-       fetch_iframely_and_pictrs_data(context.client(), data.url.to_owned()).await;
+       fetch_iframely_and_pictrs_data(context.client(), data_url).await;
  
      let post_form = PostForm {
        name: data.name.trim().to_owned(),
-       url: data.url.to_owned(),
+       url: data_url.map(|u| u.to_owned().into()),
        body: data.body.to_owned(),
        community_id: data.community_id,
--      creator_id: user.id,
++      creator_id: local_user_view.person.id,
        removed: None,
        deleted: None,
        nsfw: data.nsfw,
@@@ -124,12 -122,12 +122,12 @@@
        Err(_e) => return Err(ApiError::err("couldnt_create_post").into()),
      };
  
--    updated_post.send_create(&user, context).await?;
++    updated_post.send_create(&local_user_view.person, context).await?;
  
      // They like their own post by default
      let like_form = PostLikeForm {
        post_id: inserted_post.id,
-       person_id: user.id,
 -      user_id: user.id,
++      person_id: local_user_view.person.id,
        score: 1,
      };
  
@@@ -138,12 -136,12 +136,12 @@@
        return Err(ApiError::err("couldnt_like_post").into());
      }
  
--    updated_post.send_like(&user, context).await?;
++    updated_post.send_like(&local_user_view.person, context).await?;
  
      // Refetch the view
      let inserted_post_id = inserted_post.id;
      let post_view = match blocking(context.pool(), move |conn| {
--      PostView::read(conn, inserted_post_id, Some(user.id))
++      PostView::read(conn, inserted_post_id, Some(local_user_view.person.id))
      })
      .await?
      {
@@@ -173,12 -171,12 +171,12 @@@ impl Perform for GetPost 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<GetPostResponse, LemmyError> {
      let data: &GetPost = &self;
--    let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?;
--    let user_id = user.map(|u| u.id);
++    let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
++    let person_id = local_user_view.map(|u| u.person.id);
  
      let id = data.id;
      let post_view = match blocking(context.pool(), move |conn| {
--      PostView::read(conn, id, user_id)
++      PostView::read(conn, id, person_id)
      })
      .await?
      {
@@@ -189,7 -187,7 +187,7 @@@
      let id = data.id;
      let comments = blocking(context.pool(), move |conn| {
        CommentQueryBuilder::create(conn)
--        .my_user_id(user_id)
++        .my_person_id(person_id)
          .post_id(id)
          .limit(9999)
          .list()
@@@ -204,7 -202,7 +202,7 @@@
  
      // Necessary for the sidebar
      let community_view = match blocking(context.pool(), move |conn| {
--      CommunityView::read(conn, community_id, user_id)
++      CommunityView::read(conn, community_id, person_id)
      })
      .await?
      {
@@@ -239,15 -237,15 +237,15 @@@ impl Perform for GetPosts 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<GetPostsResponse, LemmyError> {
      let data: &GetPosts = &self;
--    let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
  
--    let user_id = match &user {
--      Some(user) => Some(user.id),
++    let person_id = match &local_user_view {
++      Some(uv) => Some(uv.person.id),
        None => None,
      };
  
--    let show_nsfw = match &user {
--      Some(user) => user.show_nsfw,
++    let show_nsfw = match &local_user_view {
++      Some(uv) => uv.local_user.show_nsfw,
        None => false,
      };
  
@@@ -265,7 -263,7 +263,7 @@@
          .show_nsfw(show_nsfw)
          .community_id(community_id)
          .community_name(community_name)
--        .my_user_id(user_id)
++        .my_person_id(person_id)
          .page(page)
          .limit(limit)
          .list()
@@@ -290,7 -288,7 +288,7 @@@ impl Perform for CreatePostLike 
      websocket_id: Option<ConnectionId>,
    ) -> Result<PostResponse, LemmyError> {
      let data: &CreatePostLike = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      // Don't do a downvote if site has downvotes disabled
      check_downvotes_enabled(data.score, context.pool()).await?;
@@@ -299,18 -297,18 +297,18 @@@
      let post_id = data.post_id;
      let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
  
--    check_community_ban(user.id, post.community_id, context.pool()).await?;
++    check_community_ban(local_user_view.person.id, post.community_id, context.pool()).await?;
  
      let like_form = PostLikeForm {
        post_id: data.post_id,
-       person_id: user.id,
 -      user_id: user.id,
++      person_id: local_user_view.person.id,
        score: data.score,
      };
  
      // Remove any likes first
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      blocking(context.pool(), move |conn| {
--      PostLike::remove(conn, user_id, post_id)
++      PostLike::remove(conn, person_id, post_id)
      })
      .await??;
  
@@@ -324,18 -322,18 +322,18 @@@
        }
  
        if like_form.score == 1 {
--        post.send_like(&user, context).await?;
++        post.send_like(&local_user_view.person, context).await?;
        } else if like_form.score == -1 {
--        post.send_dislike(&user, context).await?;
++        post.send_dislike(&local_user_view.person, context).await?;
        }
      } else {
--      post.send_undo_like(&user, context).await?;
++      post.send_undo_like(&local_user_view.person, context).await?;
      }
  
      let post_id = data.post_id;
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let post_view = match blocking(context.pool(), move |conn| {
--      PostView::read(conn, post_id, Some(user_id))
++      PostView::read(conn, post_id, Some(person_id))
      })
      .await?
      {
@@@ -365,7 -363,7 +363,7 @@@ impl Perform for EditPost 
      websocket_id: Option<ConnectionId>,
    ) -> Result<PostResponse, LemmyError> {
      let data: &EditPost = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      check_slurs(&data.name)?;
      check_slurs_opt(&data.body)?;
@@@ -377,10 -375,10 +375,10 @@@
      let post_id = data.post_id;
      let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
  
--    check_community_ban(user.id, orig_post.community_id, context.pool()).await?;
++    check_community_ban(local_user_view.person.id, orig_post.community_id, context.pool()).await?;
  
      // Verify that only the creator can edit
--    if !Post::is_post_creator(user.id, orig_post.creator_id) {
++    if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
        return Err(ApiError::err("no_post_edit_allowed").into());
      }
  
@@@ -428,11 -427,11 +427,11 @@@
      };
  
      // Send apub update
--    updated_post.send_update(&user, context).await?;
++    updated_post.send_update(&local_user_view.person, context).await?;
  
      let post_id = data.post_id;
      let post_view = blocking(context.pool(), move |conn| {
--      PostView::read(conn, post_id, Some(user.id))
++      PostView::read(conn, post_id, Some(local_user_view.person.id))
      })
      .await??;
  
@@@ -458,15 -457,15 +457,15 @@@ impl Perform for DeletePost 
      websocket_id: Option<ConnectionId>,
    ) -> Result<PostResponse, LemmyError> {
      let data: &DeletePost = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let post_id = data.post_id;
      let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
  
--    check_community_ban(user.id, orig_post.community_id, context.pool()).await?;
++    check_community_ban(local_user_view.person.id, orig_post.community_id, context.pool()).await?;
  
      // Verify that only the creator can delete
--    if !Post::is_post_creator(user.id, orig_post.creator_id) {
++    if !Post::is_post_creator(local_user_view.person.id, orig_post.creator_id) {
        return Err(ApiError::err("no_post_edit_allowed").into());
      }
  
@@@ -480,15 -479,15 +479,15 @@@
  
      // apub updates
      if deleted {
--      updated_post.send_delete(&user, context).await?;
++      updated_post.send_delete(&local_user_view.person, context).await?;
      } else {
--      updated_post.send_undo_delete(&user, context).await?;
++      updated_post.send_undo_delete(&local_user_view.person, context).await?;
      }
  
      // Refetch the post
      let post_id = data.post_id;
      let post_view = blocking(context.pool(), move |conn| {
--      PostView::read(conn, post_id, Some(user.id))
++      PostView::read(conn, post_id, Some(local_user_view.person.id))
      })
      .await??;
  
@@@ -514,15 -513,15 +513,15 @@@ impl Perform for RemovePost 
      websocket_id: Option<ConnectionId>,
    ) -> Result<PostResponse, LemmyError> {
      let data: &RemovePost = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let post_id = data.post_id;
      let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
  
--    check_community_ban(user.id, orig_post.community_id, context.pool()).await?;
++    check_community_ban(local_user_view.person.id, orig_post.community_id, context.pool()).await?;
  
      // Verify that only the mods can remove
--    is_mod_or_admin(context.pool(), user.id, orig_post.community_id).await?;
++    is_mod_or_admin(context.pool(), local_user_view.person.id, orig_post.community_id).await?;
  
      // Update the post
      let post_id = data.post_id;
@@@ -534,7 -533,7 +533,7 @@@
  
      // Mod tables
      let form = ModRemovePostForm {
-       mod_person_id: user.id,
 -      mod_user_id: user.id,
++      mod_person_id: local_user_view.person.id,
        post_id: data.post_id,
        removed: Some(removed),
        reason: data.reason.to_owned(),
@@@ -546,16 -545,16 +545,16 @@@
  
      // apub updates
      if removed {
--      updated_post.send_remove(&user, context).await?;
++      updated_post.send_remove(&local_user_view.person, context).await?;
      } else {
--      updated_post.send_undo_remove(&user, context).await?;
++      updated_post.send_undo_remove(&local_user_view.person, context).await?;
      }
  
      // Refetch the post
      let post_id = data.post_id;
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let post_view = blocking(context.pool(), move |conn| {
--      PostView::read(conn, post_id, Some(user_id))
++      PostView::read(conn, post_id, Some(person_id))
      })
      .await??;
  
@@@ -581,15 -580,15 +580,15 @@@ impl Perform for LockPost 
      websocket_id: Option<ConnectionId>,
    ) -> Result<PostResponse, LemmyError> {
      let data: &LockPost = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let post_id = data.post_id;
      let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
  
--    check_community_ban(user.id, orig_post.community_id, context.pool()).await?;
++    check_community_ban(local_user_view.person.id, orig_post.community_id, context.pool()).await?;
  
      // Verify that only the mods can lock
--    is_mod_or_admin(context.pool(), user.id, orig_post.community_id).await?;
++    is_mod_or_admin(context.pool(), local_user_view.person.id, orig_post.community_id).await?;
  
      // Update the post
      let post_id = data.post_id;
@@@ -601,19 -600,19 +600,19 @@@
  
      // Mod tables
      let form = ModLockPostForm {
-       mod_person_id: user.id,
 -      mod_user_id: user.id,
++      mod_person_id: local_user_view.person.id,
        post_id: data.post_id,
        locked: Some(locked),
      };
      blocking(context.pool(), move |conn| ModLockPost::create(conn, &form)).await??;
  
      // apub updates
--    updated_post.send_update(&user, context).await?;
++    updated_post.send_update(&local_user_view.person, context).await?;
  
      // Refetch the post
      let post_id = data.post_id;
      let post_view = blocking(context.pool(), move |conn| {
--      PostView::read(conn, post_id, Some(user.id))
++      PostView::read(conn, post_id, Some(local_user_view.person.id))
      })
      .await??;
  
@@@ -639,15 -638,15 +638,15 @@@ impl Perform for StickyPost 
      websocket_id: Option<ConnectionId>,
    ) -> Result<PostResponse, LemmyError> {
      let data: &StickyPost = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let post_id = data.post_id;
      let orig_post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
  
--    check_community_ban(user.id, orig_post.community_id, context.pool()).await?;
++    check_community_ban(local_user_view.person.id, orig_post.community_id, context.pool()).await?;
  
      // Verify that only the mods can sticky
--    is_mod_or_admin(context.pool(), user.id, orig_post.community_id).await?;
++    is_mod_or_admin(context.pool(), local_user_view.person.id, orig_post.community_id).await?;
  
      // Update the post
      let post_id = data.post_id;
@@@ -659,7 -658,7 +658,7 @@@
  
      // Mod tables
      let form = ModStickyPostForm {
-       mod_person_id: user.id,
 -      mod_user_id: user.id,
++      mod_person_id: local_user_view.person.id,
        post_id: data.post_id,
        stickied: Some(stickied),
      };
@@@ -670,12 -669,12 +669,12 @@@
  
      // Apub updates
      // TODO stickied should pry work like locked for ease of use
--    updated_post.send_update(&user, context).await?;
++    updated_post.send_update(&local_user_view.person, context).await?;
  
      // Refetch the post
      let post_id = data.post_id;
      let post_view = blocking(context.pool(), move |conn| {
--      PostView::read(conn, post_id, Some(user.id))
++      PostView::read(conn, post_id, Some(local_user_view.person.id))
      })
      .await??;
  
@@@ -701,11 -700,11 +700,11 @@@ impl Perform for SavePost 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<PostResponse, LemmyError> {
      let data: &SavePost = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let post_saved_form = PostSavedForm {
        post_id: data.post_id,
-       person_id: user.id,
 -      user_id: user.id,
++      person_id: local_user_view.person.id,
      };
  
      if data.save {
@@@ -721,9 -720,9 +720,9 @@@
      }
  
      let post_id = data.post_id;
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let post_view = blocking(context.pool(), move |conn| {
--      PostView::read(conn, post_id, Some(user_id))
++      PostView::read(conn, post_id, Some(person_id))
      })
      .await??;
  
@@@ -742,7 -741,7 +741,7 @@@ impl Perform for CreatePostReport 
      websocket_id: Option<ConnectionId>,
    ) -> Result<CreatePostReportResponse, LemmyError> {
      let data: &CreatePostReport = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      // check size of report and check for whitespace
      let reason = data.reason.trim();
@@@ -753,17 -752,17 +752,17 @@@
        return Err(ApiError::err("report_too_long").into());
      }
  
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let post_id = data.post_id;
      let post_view = blocking(context.pool(), move |conn| {
        PostView::read(&conn, post_id, None)
      })
      .await??;
  
--    check_community_ban(user_id, post_view.community.id, context.pool()).await?;
++    check_community_ban(person_id, post_view.community.id, context.pool()).await?;
  
      let report_form = PostReportForm {
--      creator_id: user_id,
++      creator_id: person_id,
        post_id,
        original_post_name: post_view.post.name,
        original_post_url: post_view.post.url,
@@@ -785,7 -784,7 +784,7 @@@
      context.chat_server().do_send(SendUserRoomMessage {
        op: UserOperation::CreatePostReport,
        response: res.clone(),
--      recipient_id: user.id,
++      recipient_id: local_user_view.person.id,
        websocket_id,
      });
  
@@@ -811,7 -810,7 +810,7 @@@ impl Perform for ResolvePostReport 
      websocket_id: Option<ConnectionId>,
    ) -> Result<ResolvePostReportResponse, LemmyError> {
      let data: &ResolvePostReport = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let report_id = data.report_id;
      let report = blocking(context.pool(), move |conn| {
@@@ -819,15 -818,15 +818,15 @@@
      })
      .await??;
  
--    let user_id = user.id;
--    is_mod_or_admin(context.pool(), user_id, report.community.id).await?;
++    let person_id = local_user_view.person.id;
++    is_mod_or_admin(context.pool(), person_id, report.community.id).await?;
  
      let resolved = data.resolved;
      let resolve_fun = move |conn: &'_ _| {
        if resolved {
--        PostReport::resolve(conn, report_id, user_id)
++        PostReport::resolve(conn, report_id, person_id)
        } else {
--        PostReport::unresolve(conn, report_id, user_id)
++        PostReport::unresolve(conn, report_id, person_id)
        }
      };
  
@@@ -863,12 -862,12 +862,12 @@@ impl Perform for ListPostReports 
      websocket_id: Option<ConnectionId>,
    ) -> Result<ListPostReportsResponse, LemmyError> {
      let data: &ListPostReports = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
--    let user_id = user.id;
++    let person_id = local_user_view.person.id;
      let community_id = data.community;
      let community_ids =
--      collect_moderated_communities(user_id, community_id, context.pool()).await?;
++      collect_moderated_communities(person_id, community_id, context.pool()).await?;
  
      let page = data.page;
      let limit = data.limit;
@@@ -886,7 -885,7 +885,7 @@@
      context.chat_server().do_send(SendUserRoomMessage {
        op: UserOperation::ListPostReports,
        response: res.clone(),
--      recipient_id: user.id,
++      recipient_id: local_user_view.person.id,
        websocket_id,
      });
  
diff --cc crates/api/src/routes.rs
index 7ca609f1,a64e0bff..0c70dc0d
--- a/crates/api/src/routes.rs
+++ b/crates/api/src/routes.rs
@@@ -1,6 -1,6 +1,6 @@@
  use crate::Perform;
  use actix_web::{error::ErrorBadRequest, *};
- use lemmy_structs::{comment::*, community::*, post::*, site::*, user::*, websocket::*};
 -use lemmy_api_structs::{comment::*, community::*, post::*, site::*, user::*, websocket::*};
++use lemmy_api_structs::{comment::*, community::*, post::*, site::*, person::*, websocket::*};
  use lemmy_utils::rate_limit::RateLimit;
  use lemmy_websocket::{routes::chat_route, LemmyContext};
  use serde::Deserialize;
@@@ -137,11 -137,11 +137,11 @@@ pub fn config(cfg: &mut web::ServiceCon
        .service(
          web::scope("/user")
            .wrap(rate_limit.message())
--          .route("", web::get().to(route_get::<GetUserDetails>))
--          .route("/mention", web::get().to(route_get::<GetUserMentions>))
++          .route("", web::get().to(route_get::<GetPersonDetails>))
++          .route("/mention", web::get().to(route_get::<GetPersonMentions>))
            .route(
              "/mention/mark_as_read",
--            web::post().to(route_post::<MarkUserMentionAsRead>),
++            web::post().to(route_post::<MarkPersonMentionAsRead>),
            )
            .route("/replies", web::get().to(route_get::<GetReplies>))
            .route(
@@@ -150,7 -150,7 +150,7 @@@
            )
            .route("/join", web::post().to(route_post::<UserJoin>))
            // Admin action. I don't like that it's in /user
--          .route("/ban", web::post().to(route_post::<BanUser>))
++          .route("/ban", web::post().to(route_post::<BanPerson>))
            // Account actions. I don't like that they're in /user maybe /accounts
            .route("/login", web::post().to(route_post::<Login>))
            .route("/get_captcha", web::get().to(route_get::<GetCaptcha>))
diff --cc crates/api/src/site.rs
index 56f14dea,af9d22c4..5acd099c
--- a/crates/api/src/site.rs
+++ b/crates/api/src/site.rs
@@@ -1,16 -1,23 +1,22 @@@
  use crate::{
    build_federated_instances,
--  get_user_from_jwt,
--  get_user_from_jwt_opt,
--  get_user_safe_settings_from_jwt,
--  get_user_safe_settings_from_jwt_opt,
++  get_local_user_view_from_jwt,
++  get_local_user_view_from_jwt_opt,
++  get_local_user_settings_view_from_jwt_opt,
    is_admin,
    Perform,
  };
  use actix_web::web::Data;
  use anyhow::Context;
 -use lemmy_api_structs::{blocking, site::*, user::Register};
++use lemmy_api_structs::{blocking, site::*, person::Register};
  use lemmy_apub::fetcher::search::search_by_apub_id;
- use lemmy_db_queries::{diesel_option_overwrite, source::site::Site_, Crud, SearchType, SortType};
+ use lemmy_db_queries::{
+   diesel_option_overwrite_to_url,
+   source::site::Site_,
+   Crud,
+   SearchType,
+   SortType,
+ };
  use lemmy_db_schema::{
    naive_now,
    source::{
@@@ -25,7 -32,7 +31,7 @@@ use lemmy_db_views::
  };
  use lemmy_db_views_actor::{
    community_view::CommunityQueryBuilder,
--  user_view::{UserQueryBuilder, UserViewSafe},
++  person_view::{PersonQueryBuilder, PersonViewSafe},
  };
  use lemmy_db_views_moderator::{
    mod_add_community_view::ModAddCommunityView,
@@@ -68,36 -74,36 +73,36 @@@ impl Perform for GetModlog 
      let data: &GetModlog = &self;
  
      let community_id = data.community_id;
--    let mod_user_id = data.mod_user_id;
++    let mod_person_id = data.mod_person_id;
      let page = data.page;
      let limit = data.limit;
      let removed_posts = blocking(context.pool(), move |conn| {
--      ModRemovePostView::list(conn, community_id, mod_user_id, page, limit)
++      ModRemovePostView::list(conn, community_id, mod_person_id, page, limit)
      })
      .await??;
  
      let locked_posts = blocking(context.pool(), move |conn| {
--      ModLockPostView::list(conn, community_id, mod_user_id, page, limit)
++      ModLockPostView::list(conn, community_id, mod_person_id, page, limit)
      })
      .await??;
  
      let stickied_posts = blocking(context.pool(), move |conn| {
--      ModStickyPostView::list(conn, community_id, mod_user_id, page, limit)
++      ModStickyPostView::list(conn, community_id, mod_person_id, page, limit)
      })
      .await??;
  
      let removed_comments = blocking(context.pool(), move |conn| {
--      ModRemoveCommentView::list(conn, community_id, mod_user_id, page, limit)
++      ModRemoveCommentView::list(conn, community_id, mod_person_id, page, limit)
      })
      .await??;
  
      let banned_from_community = blocking(context.pool(), move |conn| {
--      ModBanFromCommunityView::list(conn, community_id, mod_user_id, page, limit)
++      ModBanFromCommunityView::list(conn, community_id, mod_person_id, page, limit)
      })
      .await??;
  
      let added_to_community = blocking(context.pool(), move |conn| {
--      ModAddCommunityView::list(conn, community_id, mod_user_id, page, limit)
++      ModAddCommunityView::list(conn, community_id, mod_person_id, page, limit)
      })
      .await??;
  
@@@ -105,9 -111,9 +110,9 @@@
      let (removed_communities, banned, added) = if data.community_id.is_none() {
        blocking(context.pool(), move |conn| {
          Ok((
--          ModRemoveCommunityView::list(conn, mod_user_id, page, limit)?,
--          ModBanView::list(conn, mod_user_id, page, limit)?,
--          ModAddView::list(conn, mod_user_id, page, limit)?,
++          ModRemoveCommunityView::list(conn, mod_person_id, page, limit)?,
++          ModBanView::list(conn, mod_person_id, page, limit)?,
++          ModAddView::list(conn, mod_person_id, page, limit)?,
          )) as Result<_, LemmyError>
        })
        .await??
@@@ -146,20 -152,20 +151,20 @@@ impl Perform for CreateSite 
        return Err(ApiError::err("site_already_exists").into());
      };
  
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      check_slurs(&data.name)?;
      check_slurs_opt(&data.description)?;
  
      // Make sure user is an admin
--    is_admin(context.pool(), user.id).await?;
++    is_admin(&local_user_view)?;
  
      let site_form = SiteForm {
        name: data.name.to_owned(),
        description: data.description.to_owned(),
-       icon: Some(data.icon.to_owned()),
-       banner: Some(data.banner.to_owned()),
-       creator_id: user.id,
+       icon: Some(data.icon.to_owned().map(|url| url.into())),
+       banner: Some(data.banner.to_owned().map(|url| url.into())),
 -      creator_id: user.id,
++      creator_id: local_user_view.person.id,
        enable_downvotes: data.enable_downvotes,
        open_registration: data.open_registration,
        enable_nsfw: data.enable_nsfw,
@@@ -186,13 -192,13 +191,13 @@@ impl Perform for EditSite 
      websocket_id: Option<ConnectionId>,
    ) -> Result<SiteResponse, LemmyError> {
      let data: &EditSite = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      check_slurs(&data.name)?;
      check_slurs_opt(&data.description)?;
  
      // Make sure user is an admin
--    is_admin(context.pool(), user.id).await?;
++    is_admin(&local_user_view)?;
  
      let found_site = blocking(context.pool(), move |conn| Site::read_simple(conn)).await??;
  
@@@ -277,20 -283,20 +282,20 @@@ impl Perform for GetSite 
        }
      };
  
--    let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??;
++    let mut admins = blocking(context.pool(), move |conn| PersonViewSafe::admins(conn)).await??;
  
      // Make sure the site creator is the top admin
      if let Some(site_view) = site_view.to_owned() {
        let site_creator_id = site_view.creator.id;
        // TODO investigate why this is sometimes coming back null
        // Maybe user_.admin isn't being set to true?
--      if let Some(creator_index) = admins.iter().position(|r| r.user.id == site_creator_id) {
--        let creator_user = admins.remove(creator_index);
--        admins.insert(0, creator_user);
++      if let Some(creator_index) = admins.iter().position(|r| r.person.id == site_creator_id) {
++        let creator_person = admins.remove(creator_index);
++        admins.insert(0, creator_person);
        }
      }
  
--    let banned = blocking(context.pool(), move |conn| UserViewSafe::banned(conn)).await??;
++    let banned = blocking(context.pool(), move |conn| PersonViewSafe::banned(conn)).await??;
  
      let online = context
        .chat_server()
@@@ -298,7 -304,7 +303,7 @@@
        .await
        .unwrap_or(1);
  
--    let my_user = get_user_safe_settings_from_jwt_opt(&data.auth, context.pool()).await?;
++    let my_user = get_local_user_settings_view_from_jwt_opt(&data.auth, context.pool()).await?;
      let federated_instances = build_federated_instances(context.pool()).await?;
  
      Ok(GetSiteResponse {
@@@ -329,8 -335,8 +334,8 @@@ impl Perform for Search 
        Err(e) => debug!("Failed to resolve search query as activitypub ID: {}", e),
      }
  
--    let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?;
--    let user_id = user.map(|u| u.id);
++    let local_user_view = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
++    let person_id = local_user_view.map(|u| u.person.id);
  
      let type_ = SearchType::from_str(&data.type_)?;
  
@@@ -355,7 -361,7 +360,7 @@@
              .show_nsfw(true)
              .community_id(community_id)
              .community_name(community_name)
--            .my_user_id(user_id)
++            .my_person_id(person_id)
              .search_term(q)
              .page(page)
              .limit(limit)
@@@ -368,7 -374,7 +373,7 @@@
            CommentQueryBuilder::create(&conn)
              .sort(&sort)
              .search_term(q)
--            .my_user_id(user_id)
++            .my_person_id(person_id)
              .page(page)
              .limit(limit)
              .list()
@@@ -380,7 -386,7 +385,7 @@@
            CommunityQueryBuilder::create(conn)
              .sort(&sort)
              .search_term(q)
--            .my_user_id(user_id)
++            .my_person_id(person_id)
              .page(page)
              .limit(limit)
              .list()
@@@ -389,7 -395,7 +394,7 @@@
        }
        SearchType::Users => {
          users = blocking(context.pool(), move |conn| {
--          UserQueryBuilder::create(conn)
++          PersonQueryBuilder::create(conn)
              .sort(&sort)
              .search_term(q)
              .page(page)
@@@ -405,7 -411,7 +410,7 @@@
              .show_nsfw(true)
              .community_id(community_id)
              .community_name(community_name)
--            .my_user_id(user_id)
++            .my_person_id(person_id)
              .search_term(q)
              .page(page)
              .limit(limit)
@@@ -420,7 -426,7 +425,7 @@@
            CommentQueryBuilder::create(conn)
              .sort(&sort)
              .search_term(q)
--            .my_user_id(user_id)
++            .my_person_id(person_id)
              .page(page)
              .limit(limit)
              .list()
@@@ -434,7 -440,7 +439,7 @@@
            CommunityQueryBuilder::create(conn)
              .sort(&sort)
              .search_term(q)
--            .my_user_id(user_id)
++            .my_person_id(person_id)
              .page(page)
              .limit(limit)
              .list()
@@@ -445,7 -451,7 +450,7 @@@
          let sort = SortType::from_str(&data.sort)?;
  
          users = blocking(context.pool(), move |conn| {
--          UserQueryBuilder::create(conn)
++          PersonQueryBuilder::create(conn)
              .sort(&sort)
              .search_term(q)
              .page(page)
@@@ -459,7 -465,7 +464,7 @@@
            PostQueryBuilder::create(conn)
              .sort(&sort)
              .show_nsfw(true)
--            .my_user_id(user_id)
++            .my_person_id(person_id)
              .community_id(community_id)
              .community_name(community_name)
              .url_search(q)
@@@ -492,18 -498,18 +497,18 @@@ impl Perform for TransferSite 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<GetSiteResponse, LemmyError> {
      let data: &TransferSite = &self;
--    let user = get_user_safe_settings_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
--    is_admin(context.pool(), user.id).await?;
++    is_admin(&local_user_view)?;
  
      let read_site = blocking(context.pool(), move |conn| Site::read_simple(conn)).await??;
  
      // Make sure user is the creator
--    if read_site.creator_id != user.id {
++    if read_site.creator_id != local_user_view.person.id {
        return Err(ApiError::err("not_an_admin").into());
      }
  
--    let new_creator_id = data.user_id;
++    let new_creator_id = data.person_id;
      let transfer_site = move |conn: &'_ _| Site::transfer(conn, new_creator_id);
      if blocking(context.pool(), transfer_site).await?.is_err() {
        return Err(ApiError::err("couldnt_update_site").into());
@@@ -511,8 -517,8 +516,8 @@@
  
      // Mod tables
      let form = ModAddForm {
-       mod_person_id: user.id,
-       other_person_id: data.user_id,
 -      mod_user_id: user.id,
 -      other_user_id: data.user_id,
++      mod_person_id: local_user_view.person.id,
++      other_person_id: data.person_id,
        removed: Some(false),
      };
  
@@@ -520,15 -526,15 +525,15 @@@
  
      let site_view = blocking(context.pool(), move |conn| SiteView::read(conn)).await??;
  
--    let mut admins = blocking(context.pool(), move |conn| UserViewSafe::admins(conn)).await??;
++    let mut admins = blocking(context.pool(), move |conn| PersonViewSafe::admins(conn)).await??;
      let creator_index = admins
        .iter()
--      .position(|r| r.user.id == site_view.creator.id)
++      .position(|r| r.person.id == site_view.creator.id)
        .context(location_info!())?;
--    let creator_user = admins.remove(creator_index);
--    admins.insert(0, creator_user);
++    let creator_person = admins.remove(creator_index);
++    admins.insert(0, creator_person);
  
--    let banned = blocking(context.pool(), move |conn| UserViewSafe::banned(conn)).await??;
++    let banned = blocking(context.pool(), move |conn| PersonViewSafe::banned(conn)).await??;
      let federated_instances = build_federated_instances(context.pool()).await?;
  
      Ok(GetSiteResponse {
@@@ -537,7 -543,7 +542,7 @@@
        banned,
        online: 0,
        version: version::VERSION.to_string(),
--      my_user: Some(user),
++      my_user: Some(local_user_view),
        federated_instances,
      })
    }
@@@ -553,10 -559,10 +558,10 @@@ impl Perform for GetSiteConfig 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<GetSiteConfigResponse, LemmyError> {
      let data: &GetSiteConfig = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      // Only let admins read this
--    is_admin(context.pool(), user.id).await?;
++    is_admin(&local_user_view)?;
  
      let config_hjson = Settings::read_config_file()?;
  
@@@ -574,11 -580,11 +579,11 @@@ impl Perform for SaveSiteConfig 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<GetSiteConfigResponse, LemmyError> {
      let data: &SaveSiteConfig = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      // Only let admins read this
--    let user_id = user.id;
--    is_admin(context.pool(), user_id).await?;
++    let person_id = local_user_view.person.id;
++    is_admin(&local_user_view)?;
  
      // Make sure docker doesn't have :ro at the end of the volume, so its not a read-only filesystem
      let config_hjson = match Settings::save_config_file(&data.config_hjson) {
diff --cc crates/api/src/user.rs
index a8f1477f,93ffdfff..11281490
--- a/crates/api/src/user.rs
+++ b/crates/api/src/user.rs
@@@ -1,10 -1,10 +1,10 @@@
  use crate::{
    captcha_espeak_wav_base64,
-   check_optional_url,
    collect_moderated_communities,
--  get_user_from_jwt,
--  get_user_from_jwt_opt,
++  get_local_user_view_from_jwt,
++  get_local_user_view_from_jwt_opt,
    is_admin,
+   password_length_check,
    Perform,
  };
  use actix_web::web::Data;
@@@ -12,6 -12,7 +12,7 @@@ use anyhow::Context
  use bcrypt::verify;
  use captcha::{gen, Difficulty};
  use chrono::Duration;
 -use lemmy_api_structs::{blocking, send_email_to_user, user::*};
++use lemmy_api_structs::{blocking, send_email_to_user, person::*};
  use lemmy_apub::{
    generate_apub_endpoint,
    generate_followers_url,
@@@ -29,8 -31,8 +31,8 @@@ use lemmy_db_queries::
      post::Post_,
      private_message::PrivateMessage_,
      site::Site_,
--    user::User,
--    user_mention::UserMention_,
++    person::Person_,
++    person_mention::PersonMention_,
    },
    Crud,
    Followable,
@@@ -38,34 -40,33 +40,21 @@@
    ListingType,
    SortType,
  };
--use lemmy_db_schema::{
--  naive_now,
--  source::{
--    comment::Comment,
--    community::*,
--    moderator::*,
--    password_reset_request::*,
--    post::Post,
--    private_message::*,
--    site::*,
--    user::*,
--    user_mention::*,
--  },
--};
++use lemmy_db_schema::{naive_now, source::{comment::Comment, community::*, local_user::LocalUserForm, moderator::*, password_reset_request::*, person::*, person_mention::*, post::Post, private_message::*, site::*}};
  use lemmy_db_views::{
    comment_report_view::CommentReportView,
    comment_view::CommentQueryBuilder,
    post_report_view::PostReportView,
    post_view::PostQueryBuilder,
    private_message_view::{PrivateMessageQueryBuilder, PrivateMessageView},
++  local_user_view::LocalUserView,
  };
  use lemmy_db_views_actor::{
    community_follower_view::CommunityFollowerView,
    community_moderator_view::CommunityModeratorView,
--  user_mention_view::{UserMentionQueryBuilder, UserMentionView},
--  user_view::UserViewSafe,
++  person_mention_view::{PersonMentionQueryBuilder, PersonMentionView},
++  person_view::PersonViewSafe,
  };
- use lemmy_structs::{blocking, send_email_to_user, user::*};
  use lemmy_utils::{
    apub::generate_actor_keypair,
    claims::Claims,
@@@ -104,24 -105,24 +93,24 @@@ impl Perform for Login 
  
      // Fetch that username / email
      let username_or_email = data.username_or_email.clone();
--    let user = match blocking(context.pool(), move |conn| {
--      User_::find_by_email_or_username(conn, &username_or_email)
++    let local_user_view = match blocking(context.pool(), move |conn| {
++      LocalUserView::find_by_email_or_name(conn, &username_or_email)
      })
      .await?
      {
--      Ok(user) => user,
++      Ok(uv) => uv,
        Err(_e) => return Err(ApiError::err("couldnt_find_that_username_or_email").into()),
      };
  
      // Verify the password
--    let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false);
++    let valid: bool = verify(&data.password, &local_user_view.local_user.password_encrypted).unwrap_or(false);
      if !valid {
        return Err(ApiError::err("password_incorrect").into());
      }
  
      // Return the jwt
      Ok(LoginResponse {
-       jwt: Claims::jwt(user.id, Settings::get().hostname)?,
 -      jwt: Claims::jwt(user.id, Settings::get().hostname())?,
++      jwt: Claims::jwt(local_user_view.person.id, Settings::get().hostname())?,
      })
    }
  }
@@@ -156,7 -154,7 +142,7 @@@ impl Perform for Register 
  
      // Check if there are admins. False if admins exist
      let no_admins = blocking(context.pool(), move |conn| {
--      UserViewSafe::admins(conn).map(|a| a.is_empty())
++      PersonViewSafe::admins(conn).map(|a| a.is_empty())
      })
      .await??;
  
@@@ -182,25 -180,25 +168,24 @@@
  
      check_slurs(&data.username)?;
  
--    let user_keypair = generate_actor_keypair()?;
++    let actor_keypair = generate_actor_keypair()?;
      if !is_valid_username(&data.username) {
        return Err(ApiError::err("invalid_username").into());
      }
--    let user_actor_id = generate_apub_endpoint(EndpointType::User, &data.username)?;
++    let actor_id = generate_apub_endpoint(EndpointType::Person, &data.username)?;
  
--    // Register the new user
--    let user_form = UserForm {
++    // We have to create both a person, and local_user
++    
++    // Register the new person
++    let person_form = PersonForm {
        name: data.username.to_owned(),
--      email: Some(data.email.to_owned()),
--      matrix_user_id: None,
        avatar: None,
        banner: None,
--      password_encrypted: data.password.to_owned(),
        preferred_username: None,
        published: None,
        updated: None,
--      admin: no_admins,
--      banned: Some(false),
++      banned: None,
++      deleted: None,
        show_nsfw: data.show_nsfw,
        theme: "browser".into(),
        default_sort_type: SortType::Active as i16,
@@@ -208,35 -206,35 +193,60 @@@
        lang: "browser".into(),
        show_avatars: true,
        send_notifications_to_email: false,
--      actor_id: Some(user_actor_id.clone()),
++      actor_id: Some(actor_id.clone()),
        bio: None,
        local: true,
--      private_key: Some(user_keypair.private_key),
--      public_key: Some(user_keypair.public_key),
++      private_key: Some(actor_keypair.private_key),
++      public_key: Some(actor_keypair.public_key),
        last_refreshed_at: None,
--      inbox_url: Some(generate_inbox_url(&user_actor_id)?),
--      shared_inbox_url: Some(Some(generate_shared_inbox_url(&user_actor_id)?)),
++      inbox_url: Some(generate_inbox_url(&actor_id)?),
++      shared_inbox_url: Some(Some(generate_shared_inbox_url(&actor_id)?)),
      };
  
--    // Create the user
--    let inserted_user = match blocking(context.pool(), move |conn| {
--      User_::register(conn, &user_form)
++    // insert the person
++    let inserted_person = match blocking(context.pool(), move |conn| {
++      Person::create(conn, &person_form)
      })
      .await?
      {
        Ok(user) => user,
++      Err(e) => {
++        return Err(ApiError::err("user_already_exists").into());
++      }
++    };
++
++    // Create the local user
++    let local_user_form = LocalUserForm {
++      person_id: inserted_person.id,
++      email: Some(data.email.to_owned()),
++      matrix_user_id: None,
++      password_encrypted: data.password.to_owned(),
++      admin: no_admins,
++
++    };
++
++    let inserted_local_user = match blocking(context.pool(), move |conn| {
++      LocalUser::register(conn, &local_user_form)
++    })
++    .await?
++    {
++      Ok(lu) => lu,
        Err(e) => {
          let err_type = if e.to_string()
--          == "duplicate key value violates unique constraint \"user__email_key\""
++          == "duplicate key value violates unique constraint \"local_user_email_key\""
          {
            "email_already_exists"
          } else {
            "user_already_exists"
          };
  
++        // If the local user creation errored, then delete that person
++        blocking(context.pool(), move |conn| Person::delete(&conn, inserted_person.id)).await??;
++
          return Err(ApiError::err(err_type).into());
        }
      };
++    
  
      let main_community_keypair = generate_actor_keypair()?;
  
@@@ -252,7 -250,7 +262,7 @@@
              title: "The Default Community".to_string(),
              description: Some("The Default Community".to_string()),
              nsfw: false,
--            creator_id: inserted_user.id,
++            creator_id: inserted_person.id,
              removed: None,
              deleted: None,
              updated: None,
@@@ -278,7 -276,7 +288,7 @@@
      // Sign them up for main community no matter what
      let community_follower_form = CommunityFollowerForm {
        community_id: main_community.id,
-       person_id: inserted_user.id,
 -      user_id: inserted_user.id,
++      person_id: inserted_person.id,
        pending: false,
      };
  
@@@ -291,7 -289,7 +301,7 @@@
      if no_admins {
        let community_moderator_form = CommunityModeratorForm {
          community_id: main_community.id,
-         person_id: inserted_user.id,
 -        user_id: inserted_user.id,
++        person_id: inserted_person.id,
        };
  
        let join = move |conn: &'_ _| CommunityModerator::join(conn, &community_moderator_form);
@@@ -302,7 -300,7 +312,7 @@@
  
      // Return the jwt
      Ok(LoginResponse {
-       jwt: Claims::jwt(inserted_user.id, Settings::get().hostname)?,
 -      jwt: Claims::jwt(inserted_user.id, Settings::get().hostname())?,
++      jwt: Claims::jwt(inserted_person.id, Settings::get().hostname())?,
      })
    }
  }
@@@ -364,10 -362,10 +374,10 @@@ impl Perform for SaveUserSettings 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<LoginResponse, LemmyError> {
      let data: &SaveUserSettings = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
-     let avatar = diesel_option_overwrite(&data.avatar);
-     let banner = diesel_option_overwrite(&data.banner);
+     let avatar = diesel_option_overwrite_to_url(&data.avatar)?;
+     let banner = diesel_option_overwrite_to_url(&data.banner)?;
      let email = diesel_option_overwrite(&data.email);
      let bio = diesel_option_overwrite(&data.bio);
      let preferred_username = diesel_option_overwrite(&data.preferred_username);
@@@ -490,7 -486,7 +498,7 @@@ impl Perform for GetUserDetails 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<GetUserDetailsResponse, LemmyError> {
      let data: &GetUserDetails = &self;
--    let user = get_user_from_jwt_opt(&data.auth, context.pool()).await?;
++    let user = get_local_user_view_from_jwt_opt(&data.auth, context.pool()).await?;
  
      let show_nsfw = match &user {
        Some(user) => user.show_nsfw,
@@@ -542,7 -538,7 +550,7 @@@
          .limit(limit);
  
        let mut comments_query = CommentQueryBuilder::create(conn)
--        .my_user_id(user_id)
++        .my_person_id(user_id)
          .sort(&sort)
          .saved_only(saved_only)
          .page(page)
@@@ -572,7 -568,7 +580,7 @@@
        }
      };
      let moderates = blocking(context.pool(), move |conn| {
--      CommunityModeratorView::for_user(conn, user_details_id)
++      CommunityModeratorView::for_person(conn, user_details_id)
      })
      .await??;
  
@@@ -597,7 -593,7 +605,7 @@@ impl Perform for AddAdmin 
      websocket_id: Option<ConnectionId>,
    ) -> Result<AddAdminResponse, LemmyError> {
      let data: &AddAdmin = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      // Make sure user is an admin
      is_admin(context.pool(), user.id).await?;
@@@ -653,7 -649,7 +661,7 @@@ impl Perform for BanUser 
      websocket_id: Option<ConnectionId>,
    ) -> Result<BanUserResponse, LemmyError> {
      let data: &BanUser = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      // Make sure user is an admin
      is_admin(context.pool(), user.id).await?;
@@@ -714,7 -710,7 +722,7 @@@
      };
  
      context.chat_server().do_send(SendAllMessage {
--      op: UserOperation::BanUser,
++      op: UserOperation::BanPerson,
        response: res.clone(),
        websocket_id,
      });
@@@ -733,7 -729,7 +741,7 @@@ impl Perform for GetReplies 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<GetRepliesResponse, LemmyError> {
      let data: &GetReplies = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let sort = SortType::from_str(&data.sort)?;
  
@@@ -746,7 -742,7 +754,7 @@@
          .sort(&sort)
          .unread_only(unread_only)
          .recipient_id(user_id)
--        .my_user_id(user_id)
++        .my_person_id(user_id)
          .page(page)
          .limit(limit)
          .list()
@@@ -767,7 -763,7 +775,7 @@@ impl Perform for GetUserMentions 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<GetUserMentionsResponse, LemmyError> {
      let data: &GetUserMentions = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let sort = SortType::from_str(&data.sort)?;
  
@@@ -801,7 -797,7 +809,7 @@@ impl Perform for MarkUserMentionAsRead 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<UserMentionResponse, LemmyError> {
      let data: &MarkUserMentionAsRead = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let user_mention_id = data.user_mention_id;
      let read_user_mention = blocking(context.pool(), move |conn| {
@@@ -841,12 -837,12 +849,12 @@@ impl Perform for MarkAllAsRead 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<GetRepliesResponse, LemmyError> {
      let data: &MarkAllAsRead = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let user_id = user.id;
      let replies = blocking(context.pool(), move |conn| {
        CommentQueryBuilder::create(conn)
--        .my_user_id(user_id)
++        .my_person_id(user_id)
          .recipient_id(user_id)
          .unread_only(true)
          .page(1)
@@@ -895,7 -891,7 +903,7 @@@ impl Perform for DeleteAccount 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<LoginResponse, LemmyError> {
      let data: &DeleteAccount = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      // Verify the password
      let valid: bool = verify(&data.password, &user.password_encrypted).unwrap_or(false);
@@@ -1026,7 -1024,7 +1036,7 @@@ impl Perform for CreatePrivateMessage 
      websocket_id: Option<ConnectionId>,
    ) -> Result<PrivateMessageResponse, LemmyError> {
      let data: &CreatePrivateMessage = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let content_slurs_removed = remove_slurs(&data.content.to_owned());
  
@@@ -1119,7 -1117,7 +1129,7 @@@ impl Perform for EditPrivateMessage 
      websocket_id: Option<ConnectionId>,
    ) -> Result<PrivateMessageResponse, LemmyError> {
      let data: &EditPrivateMessage = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      // Checking permissions
      let private_message_id = data.private_message_id;
@@@ -1178,7 -1176,7 +1188,7 @@@ impl Perform for DeletePrivateMessage 
      websocket_id: Option<ConnectionId>,
    ) -> Result<PrivateMessageResponse, LemmyError> {
      let data: &DeletePrivateMessage = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      // Checking permissions
      let private_message_id = data.private_message_id;
@@@ -1243,7 -1241,7 +1253,7 @@@ impl Perform for MarkPrivateMessageAsRe
      websocket_id: Option<ConnectionId>,
    ) -> Result<PrivateMessageResponse, LemmyError> {
      let data: &MarkPrivateMessageAsRead = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      // Checking permissions
      let private_message_id = data.private_message_id;
@@@ -1301,7 -1299,7 +1311,7 @@@ impl Perform for GetPrivateMessages 
      _websocket_id: Option<ConnectionId>,
    ) -> Result<PrivateMessagesResponse, LemmyError> {
      let data: &GetPrivateMessages = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
      let user_id = user.id;
  
      let page = data.page;
@@@ -1332,7 -1330,7 +1342,7 @@@ impl Perform for GetReportCount 
      websocket_id: Option<ConnectionId>,
    ) -> Result<GetReportCountResponse, LemmyError> {
      let data: &GetReportCount = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let user = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      let user_id = user.id;
      let community_id = data.community;
diff --cc crates/api/src/websocket.rs
index 4342f15b,58933712..47e21091
--- a/crates/api/src/websocket.rs
+++ b/crates/api/src/websocket.rs
@@@ -1,6 -1,6 +1,6 @@@
--use crate::{get_user_from_jwt, Perform};
++use crate::{get_local_user_view_from_jwt, Perform};
  use actix_web::web::Data;
- use lemmy_structs::websocket::*;
+ use lemmy_api_structs::websocket::*;
  use lemmy_utils::{ConnectionId, LemmyError};
  use lemmy_websocket::{
    messages::{JoinCommunityRoom, JoinModRoom, JoinPostRoom, JoinUserRoom},
@@@ -17,11 -17,11 +17,12 @@@ impl Perform for UserJoin 
      websocket_id: Option<ConnectionId>,
    ) -> Result<UserJoinResponse, LemmyError> {
      let data: &UserJoin = &self;
--    let user = get_user_from_jwt(&data.auth, context.pool()).await?;
++    let local_user_view = get_local_user_view_from_jwt(&data.auth, context.pool()).await?;
  
      if let Some(ws_id) = websocket_id {
        context.chat_server().do_send(JoinUserRoom {
--        user_id: user.id,
++        // TODO this should probably be the local_user_id
++        user_id: local_user_view.person.id,
          id: ws_id,
        });
      }
diff --cc crates/api_structs/src/community.rs
index fbbf2e40,ee349cb0..d9313ce9
--- a/crates/api_structs/src/community.rs
+++ b/crates/api_structs/src/community.rs
@@@ -2,7 -2,7 +2,7 @@@ use lemmy_db_views_actor::
    community_follower_view::CommunityFollowerView,
    community_moderator_view::CommunityModeratorView,
    community_view::CommunityView,
--  user_view::UserViewSafe,
++  person_view::PersonViewSafe,
  };
  use serde::{Deserialize, Serialize};
  
@@@ -54,7 -53,7 +53,7 @@@ pub struct ListCommunitiesResponse 
  #[derive(Deserialize, Clone)]
  pub struct BanFromCommunity {
    pub community_id: i32,
--  pub user_id: i32,
++  pub person_id: i32,
    pub ban: bool,
    pub remove_data: bool,
    pub reason: Option<String>,
@@@ -64,14 -63,14 +63,14 @@@
  
  #[derive(Serialize, Clone)]
  pub struct BanFromCommunityResponse {
--  pub user_view: UserViewSafe,
++  pub person_view: PersonViewSafe,
    pub banned: bool,
  }
  
  #[derive(Deserialize)]
  pub struct AddModToCommunity {
    pub community_id: i32,
--  pub user_id: i32,
++  pub person_id: i32,
    pub added: bool,
    pub auth: String,
  }
@@@ -129,6 -127,6 +127,6 @@@ pub struct GetFollowedCommunitiesRespon
  #[derive(Deserialize)]
  pub struct TransferCommunity {
    pub community_id: i32,
--  pub user_id: i32,
++  pub person_id: i32,
    pub auth: String,
  }
diff --cc crates/api_structs/src/lib.rs
index 8b56fab6,800fe6c8..b28cb37f
--- a/crates/api_structs/src/lib.rs
+++ b/crates/api_structs/src/lib.rs
@@@ -2,18 -2,18 +2,19 @@@ pub mod comment
  pub mod community;
  pub mod post;
  pub mod site;
--pub mod user;
++pub mod person;
  pub mod websocket;
  
  use diesel::PgConnection;
--use lemmy_db_queries::{source::user::User, Crud, DbPool};
++use lemmy_db_queries::{Crud, DbPool};
  use lemmy_db_schema::source::{
    comment::Comment,
    post::Post,
--  user::User_,
--  user_mention::{UserMention, UserMentionForm},
++  person::Person,
++  person_mention::{PersonMention, PersonMentionForm},
  };
- use lemmy_utils::{email::send_email, settings::Settings, utils::MentionData, LemmyError};
++use lemmy_db_views::local_user_view::LocalUserView;
+ use lemmy_utils::{email::send_email, settings::structs::Settings, utils::MentionData, LemmyError};
  use log::error;
  use serde::{Deserialize, Serialize};
  use url::Url;
@@@ -54,25 -54,25 +55,25 @@@ wher
  pub async fn send_local_notifs(
    mentions: Vec<MentionData>,
    comment: Comment,
--  user: &User_,
++  person: Person,
    post: Post,
    pool: &DbPool,
    do_send_email: bool,
  ) -> Result<Vec<i32>, LemmyError> {
--  let user2 = user.clone();
    let ids = blocking(pool, move |conn| {
--    do_send_local_notifs(conn, &mentions, &comment, &user2, &post, do_send_email)
++    do_send_local_notifs(conn, &mentions, &comment, &person, &post, do_send_email)
    })
    .await?;
  
    Ok(ids)
  }
  
++// TODO should this really use person_ids as recipient ids? or local_user_ids ?
  fn do_send_local_notifs(
    conn: &PgConnection,
    mentions: &[MentionData],
    comment: &Comment,
--  user: &User_,
++  person: &Person,
    post: &Post,
    do_send_email: bool,
  ) -> Vec<i32> {
@@@ -81,31 -81,31 +82,32 @@@
    // Send the local mentions
    for mention in mentions
      .iter()
--    .filter(|m| m.is_local() && m.name.ne(&user.name))
++    .filter(|m| m.is_local() && m.name.ne(&person.name))
      .collect::<Vec<&MentionData>>()
    {
--    if let Ok(mention_user) = User_::read_from_name(&conn, &mention.name) {
++    // TODO do a local user fetch
++    if let Ok(mention_user_view) = LocalUserView::read_from_name(&conn, &mention.name) {
        // TODO
        // At some point, make it so you can't tag the parent creator either
        // This can cause two notifications, one for reply and the other for mention
--      recipient_ids.push(mention_user.id);
++      recipient_ids.push(mention_user_view.person.id);
  
--      let user_mention_form = UserMentionForm {
--        recipient_id: mention_user.id,
++      let user_mention_form = PersonMentionForm {
++        recipient_id: mention_user_view.person.id,
          comment_id: comment.id,
          read: None,
        };
  
        // Allow this to fail softly, since comment edits might re-update or replace it
        // Let the uniqueness handle this fail
--      let _ = UserMention::create(&conn, &user_mention_form);
++      PersonMention::create(&conn, &user_mention_form).ok();
  
        // Send an email to those users that have notifications on
--      if do_send_email && mention_user.send_notifications_to_email {
++      if do_send_email && mention_user_view.local_user.send_notifications_to_email {
          send_email_to_user(
--          mention_user,
++          mention_user_view,
            "Mentioned by",
--          "User Mention",
++          "Person Mention",
            &comment.content,
          )
        }
@@@ -116,12 -116,12 +118,12 @@@
    match comment.parent_id {
      Some(parent_id) => {
        if let Ok(parent_comment) = Comment::read(&conn, parent_id) {
--        if parent_comment.creator_id != user.id {
--          if let Ok(parent_user) = User_::read(&conn, parent_comment.creator_id) {
--            recipient_ids.push(parent_user.id);
++        if parent_comment.creator_id != person.id {
++          if let Ok(parent_user_view) = LocalUserView::read(&conn, parent_comment.creator_id) {
++            recipient_ids.push(parent_user_view.person.id);
  
--            if do_send_email && parent_user.send_notifications_to_email {
--              send_email_to_user(parent_user, "Reply from", "Comment Reply", &comment.content)
++            if do_send_email && parent_user_view.local_user.send_notifications_to_email {
++              send_email_to_user(parent_user_view, "Reply from", "Comment Reply", &comment.content)
              }
            }
          }
@@@ -129,12 -129,12 +131,12 @@@
      }
      // Its a post
      None => {
--      if post.creator_id != user.id {
--        if let Ok(parent_user) = User_::read(&conn, post.creator_id) {
--          recipient_ids.push(parent_user.id);
++      if post.creator_id != person.id {
++        if let Ok(parent_user_view) = LocalUserView::read(&conn, post.creator_id) {
++          recipient_ids.push(parent_user_view.person.id);
  
--          if do_send_email && parent_user.send_notifications_to_email {
--            send_email_to_user(parent_user, "Reply from", "Post Reply", &comment.content)
++          if do_send_email && parent_user_view.local_user.send_notifications_to_email {
++            send_email_to_user(parent_user_view, "Reply from", "Post Reply", &comment.content)
            }
          }
        }
@@@ -143,26 -143,26 +145,26 @@@
    recipient_ids
  }
  
--pub fn send_email_to_user(user: User_, subject_text: &str, body_text: &str, comment_content: &str) {
--  if user.banned {
++pub fn send_email_to_user(local_user_view: LocalUserView, subject_text: &str, body_text: &str, comment_content: &str) {
++  if local_user_view.person.banned {
      return;
    }
  
--  if let Some(user_email) = user.email {
++  if let Some(user_email) = local_user_view.local_user.email {
      let subject = &format!(
        "{} - {} {}",
        subject_text,
-       Settings::get().hostname,
-       user.name,
+       Settings::get().hostname(),
 -      user.name,
++      local_user_view.person.name,
      );
      let html = &format!(
        "<h1>{}</h1><br><div>{} - {}</div><br><a href={}/inbox>inbox</a>",
        body_text,
--      user.name,
++      local_user_view.person.name,
        comment_content,
        Settings::get().get_protocol_and_hostname()
      );
--    match send_email(subject, &user_email, &user.name, html) {
++    match send_email(subject, &user_email, &local_user_view.person.name, html) {
        Ok(_o) => _o,
        Err(e) => error!("{}", e),
      };
diff --cc crates/api_structs/src/person.rs
index dcc35f06,dcc35f06..ac1cca85
--- a/crates/api_structs/src/person.rs
+++ b/crates/api_structs/src/person.rs
@@@ -6,8 -6,8 +6,8 @@@ use lemmy_db_views::
  use lemmy_db_views_actor::{
    community_follower_view::CommunityFollowerView,
    community_moderator_view::CommunityModeratorView,
--  user_mention_view::UserMentionView,
--  user_view::UserViewSafe,
++  person_mention_view::PersonMentionView,
++  person_view::PersonViewSafe,
  };
  use serde::{Deserialize, Serialize};
  
@@@ -70,8 -70,8 +70,8 @@@ pub struct LoginResponse 
  }
  
  #[derive(Deserialize)]
--pub struct GetUserDetails {
--  pub user_id: Option<i32>,
++pub struct GetPersonDetails {
++  pub person_id: Option<i32>,
    pub username: Option<String>,
    pub sort: String,
    pub page: Option<i64>,
@@@ -82,8 -82,8 +82,8 @@@
  }
  
  #[derive(Serialize)]
--pub struct GetUserDetailsResponse {
--  pub user_view: UserViewSafe,
++pub struct GetPersonDetailsResponse {
++  pub person_view: PersonViewSafe,
    pub follows: Vec<CommunityFollowerView>,
    pub moderates: Vec<CommunityModeratorView>,
    pub comments: Vec<CommentView>,
@@@ -96,8 -96,8 +96,8 @@@ pub struct GetRepliesResponse 
  }
  
  #[derive(Serialize)]
--pub struct GetUserMentionsResponse {
--  pub mentions: Vec<UserMentionView>,
++pub struct GetPersonMentionsResponse {
++  pub mentions: Vec<PersonMentionView>,
  }
  
  #[derive(Deserialize)]
@@@ -107,19 -107,19 +107,19 @@@ pub struct MarkAllAsRead 
  
  #[derive(Deserialize)]
  pub struct AddAdmin {
--  pub user_id: i32,
++  pub person_id: i32,
    pub added: bool,
    pub auth: String,
  }
  
  #[derive(Serialize, Clone)]
  pub struct AddAdminResponse {
--  pub admins: Vec<UserViewSafe>,
++  pub admins: Vec<PersonViewSafe>,
  }
  
  #[derive(Deserialize)]
--pub struct BanUser {
--  pub user_id: i32,
++pub struct BanPerson {
++  pub person_id: i32,
    pub ban: bool,
    pub remove_data: bool,
    pub reason: Option<String>,
@@@ -128,8 -128,8 +128,8 @@@
  }
  
  #[derive(Serialize, Clone)]
--pub struct BanUserResponse {
--  pub user_view: UserViewSafe,
++pub struct BanPersonResponse {
++  pub person_view: PersonViewSafe,
    pub banned: bool,
  }
  
@@@ -143,7 -143,7 +143,7 @@@ pub struct GetReplies 
  }
  
  #[derive(Deserialize)]
--pub struct GetUserMentions {
++pub struct GetPersonMentions {
    pub sort: String,
    pub page: Option<i64>,
    pub limit: Option<i64>,
@@@ -152,15 -152,15 +152,15 @@@
  }
  
  #[derive(Deserialize)]
--pub struct MarkUserMentionAsRead {
--  pub user_mention_id: i32,
++pub struct MarkPersonMentionAsRead {
++  pub person_mention_id: i32,
    pub read: bool,
    pub auth: String,
  }
  
  #[derive(Serialize, Clone)]
--pub struct UserMentionResponse {
--  pub user_mention_view: UserMentionView,
++pub struct PersonMentionResponse {
++  pub person_mention_view: PersonMentionView,
  }
  
  #[derive(Deserialize)]
diff --cc crates/api_structs/src/site.rs
index ef878ba5,9f69e63b..d574a160
--- a/crates/api_structs/src/site.rs
+++ b/crates/api_structs/src/site.rs
@@@ -1,6 -1,6 +1,5 @@@
--use lemmy_db_schema::source::user::UserSafeSettings;
--use lemmy_db_views::{comment_view::CommentView, post_view::PostView, site_view::SiteView};
--use lemmy_db_views_actor::{community_view::CommunityView, user_view::UserViewSafe};
++use lemmy_db_views::{comment_view::CommentView, post_view::PostView, site_view::SiteView, local_user_view::LocalUserSettingsView};
++use lemmy_db_views_actor::{community_view::CommunityView, person_view::PersonViewSafe};
  use lemmy_db_views_moderator::{
    mod_add_community_view::ModAddCommunityView,
    mod_add_view::ModAddView,
@@@ -32,12 -33,12 +32,12 @@@ pub struct SearchResponse 
    pub comments: Vec<CommentView>,
    pub posts: Vec<PostView>,
    pub communities: Vec<CommunityView>,
--  pub users: Vec<UserViewSafe>,
++  pub users: Vec<PersonViewSafe>,
  }
  
  #[derive(Deserialize)]
  pub struct GetModlog {
--  pub mod_user_id: Option<i32>,
++  pub mod_person_id: Option<i32>,
    pub community_id: Option<i32>,
    pub page: Option<i64>,
    pub limit: Option<i64>,
@@@ -93,17 -94,17 +93,17 @@@ pub struct SiteResponse 
  #[derive(Serialize)]
  pub struct GetSiteResponse {
    pub site_view: Option<SiteView>, // Because the site might not be set up yet
--  pub admins: Vec<UserViewSafe>,
--  pub banned: Vec<UserViewSafe>,
++  pub admins: Vec<PersonViewSafe>,
++  pub banned: Vec<PersonViewSafe>,
    pub online: usize,
    pub version: String,
--  pub my_user: Option<UserSafeSettings>,
++  pub my_user: Option<LocalUserSettingsView>,
    pub federated_instances: Option<FederatedInstances>, // Federation may be disabled
  }
  
  #[derive(Deserialize)]
  pub struct TransferSite {
--  pub user_id: i32,
++  pub person_id: i32,
    pub auth: String,
  }
  
diff --cc crates/apub/src/activities/receive/comment.rs
index 6d181793,591b6f33..5e01533d
--- a/crates/apub/src/activities/receive/comment.rs
+++ b/crates/apub/src/activities/receive/comment.rs
@@@ -1,4 -1,4 +1,4 @@@
--use crate::{activities::receive::get_actor_as_user, objects::FromApub, ActorType, NoteExt};
++use crate::{activities::receive::get_actor_as_person, objects::FromApub, ActorType, NoteExt};
  use activitystreams::{
    activity::{ActorAndObjectRefExt, Create, Dislike, Like, Remove, Update},
    base::ExtendsExt,
@@@ -19,11 -19,11 +19,11 @@@ pub(crate) async fn receive_create_comm
    context: &LemmyContext,
    request_counter: &mut i32,
  ) -> Result<(), LemmyError> {
--  let user = get_actor_as_user(&create, context, request_counter).await?;
++  let person = get_actor_as_person(&create, context, request_counter).await?;
    let note = NoteExt::from_any_base(create.object().to_owned().one().context(location_info!())?)?
      .context(location_info!())?;
  
--  let comment = Comment::from_apub(&note, context, user.actor_id(), request_counter).await?;
++  let comment = Comment::from_apub(&note, context, person.actor_id(), request_counter).await?;
  
    let post_id = comment.post_id;
    let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
@@@ -34,7 -34,7 +34,7 @@@
    // anyway.
    let mentions = scrape_text_for_mentions(&comment.content);
    let recipient_ids =
--    send_local_notifs(mentions, comment.clone(), &user, post, context.pool(), true).await?;
++    send_local_notifs(mentions, comment.clone(), person, post, context.pool(), true).await?;
  
    // Refetch the view
    let comment_view = blocking(context.pool(), move |conn| {
@@@ -64,9 -64,9 +64,9 @@@ pub(crate) async fn receive_update_comm
  ) -> Result<(), LemmyError> {
    let note = NoteExt::from_any_base(update.object().to_owned().one().context(location_info!())?)?
      .context(location_info!())?;
--  let user = get_actor_as_user(&update, context, request_counter).await?;
++  let person = get_actor_as_person(&update, context, request_counter).await?;
  
--  let comment = Comment::from_apub(&note, context, user.actor_id(), request_counter).await?;
++  let comment = Comment::from_apub(&note, context, person.actor_id(), request_counter).await?;
  
    let comment_id = comment.id;
    let post_id = comment.post_id;
@@@ -74,7 -74,7 +74,7 @@@
  
    let mentions = scrape_text_for_mentions(&comment.content);
    let recipient_ids =
--    send_local_notifs(mentions, comment, &user, post, context.pool(), false).await?;
++    send_local_notifs(mentions, comment, person, post, context.pool(), false).await?;
  
    // Refetch the view
    let comment_view = blocking(context.pool(), move |conn| {
@@@ -103,18 -103,18 +103,18 @@@ pub(crate) async fn receive_like_commen
    context: &LemmyContext,
    request_counter: &mut i32,
  ) -> Result<(), LemmyError> {
--  let user = get_actor_as_user(&like, context, request_counter).await?;
++  let person = get_actor_as_person(&like, context, request_counter).await?;
  
    let comment_id = comment.id;
    let like_form = CommentLikeForm {
      comment_id,
      post_id: comment.post_id,
-     person_id: user.id,
 -    user_id: user.id,
++    person_id: person.id,
      score: 1,
    };
--  let user_id = user.id;
++  let person_id = person.id;
    blocking(context.pool(), move |conn| {
--    CommentLike::remove(conn, user_id, comment_id)?;
++    CommentLike::remove(conn, person_id, comment_id)?;
      CommentLike::like(conn, &like_form)
    })
    .await??;
@@@ -148,18 -148,18 +148,18 @@@ pub(crate) async fn receive_dislike_com
    context: &LemmyContext,
    request_counter: &mut i32,
  ) -> Result<(), LemmyError> {
--  let user = get_actor_as_user(&dislike, context, request_counter).await?;
++  let person = get_actor_as_person(&dislike, context, request_counter).await?;
  
    let comment_id = comment.id;
    let like_form = CommentLikeForm {
      comment_id,
      post_id: comment.post_id,
-     person_id: user.id,
 -    user_id: user.id,
++    person_id: person.id,
      score: -1,
    };
--  let user_id = user.id;
++  let person_id = person.id;
    blocking(context.pool(), move |conn| {
--    CommentLike::remove(conn, user_id, comment_id)?;
++    CommentLike::remove(conn, person_id, comment_id)?;
      CommentLike::like(conn, &like_form)
    })
    .await??;
diff --cc crates/apub/src/activities/receive/comment_undo.rs
index 5dc021ad,70271715..22594f33
--- a/crates/apub/src/activities/receive/comment_undo.rs
+++ b/crates/apub/src/activities/receive/comment_undo.rs
@@@ -1,5 -1,6 +1,6 @@@
--use crate::activities::receive::get_actor_as_user;
++use crate::activities::receive::get_actor_as_person;
  use activitystreams::activity::{Dislike, Like};
+ use lemmy_api_structs::{blocking, comment::CommentResponse};
  use lemmy_db_queries::{source::comment::Comment_, Likeable};
  use lemmy_db_schema::source::comment::{Comment, CommentLike};
  use lemmy_db_views::comment_view::CommentView;
@@@ -13,12 -13,12 +13,12 @@@ pub(crate) async fn receive_undo_like_c
    context: &LemmyContext,
    request_counter: &mut i32,
  ) -> Result<(), LemmyError> {
--  let user = get_actor_as_user(like, context, request_counter).await?;
++  let person = get_actor_as_person(like, context, request_counter).await?;
  
    let comment_id = comment.id;
--  let user_id = user.id;
++  let person_id = person.id;
    blocking(context.pool(), move |conn| {
--    CommentLike::remove(conn, user_id, comment_id)
++    CommentLike::remove(conn, person_id, comment_id)
    })
    .await??;
  
@@@ -51,12 -51,12 +51,12 @@@ pub(crate) async fn receive_undo_dislik
    context: &LemmyContext,
    request_counter: &mut i32,
  ) -> Result<(), LemmyError> {
--  let user = get_actor_as_user(dislike, context, request_counter).await?;
++  let person = get_actor_as_person(dislike, context, request_counter).await?;
  
    let comment_id = comment.id;
--  let user_id = user.id;
++  let person_id = person.id;
    blocking(context.pool(), move |conn| {
--    CommentLike::remove(conn, user_id, comment_id)
++    CommentLike::remove(conn, person_id, comment_id)
    })
    .await??;
  
diff --cc crates/apub/src/activities/receive/mod.rs
index a03e1ef5,a03e1ef5..1d5fcc30
--- a/crates/apub/src/activities/receive/mod.rs
+++ b/crates/apub/src/activities/receive/mod.rs
@@@ -1,11 -1,11 +1,11 @@@
--use crate::fetcher::user::get_or_fetch_and_upsert_user;
++use crate::fetcher::person::get_or_fetch_and_upsert_person;
  use activitystreams::{
    activity::{ActorAndObjectRef, ActorAndObjectRefExt},
    base::{AsBase, BaseExt},
    error::DomainError,
  };
  use anyhow::{anyhow, Context};
--use lemmy_db_schema::source::user::User_;
++use lemmy_db_schema::source::person::Person;
  use lemmy_utils::{location_info, LemmyError};
  use lemmy_websocket::LemmyContext;
  use log::debug;
@@@ -28,18 -28,18 +28,18 @@@ wher
    Err(anyhow!("Activity not supported").into())
  }
  
--/// Reads the actor field of an activity and returns the corresponding `User_`.
--pub(crate) async fn get_actor_as_user<T, A>(
++/// Reads the actor field of an activity and returns the corresponding `Person`.
++pub(crate) async fn get_actor_as_person<T, A>(
    activity: &T,
    context: &LemmyContext,
    request_counter: &mut i32,
--) -> Result<User_, LemmyError>
++) -> Result<Person, LemmyError>
  where
    T: AsBase<A> + ActorAndObjectRef,
  {
    let actor = activity.actor()?;
--  let user_uri = actor.as_single_xsd_any_uri().context(location_info!())?;
--  get_or_fetch_and_upsert_user(&user_uri, context, request_counter).await
++  let person_uri = actor.as_single_xsd_any_uri().context(location_info!())?;
++  get_or_fetch_and_upsert_person(&person_uri, context, request_counter).await
  }
  
  /// Ensure that the ID of an incoming activity comes from the same domain as the actor. Optionally
diff --cc crates/apub/src/activities/receive/post.rs
index 4f153004,0fb6c880..528b1276
--- a/crates/apub/src/activities/receive/post.rs
+++ b/crates/apub/src/activities/receive/post.rs
@@@ -1,4 -1,4 +1,4 @@@
--use crate::{activities::receive::get_actor_as_user, objects::FromApub, ActorType, PageExt};
++use crate::{activities::receive::get_actor_as_person, objects::FromApub, ActorType, PageExt};
  use activitystreams::{
    activity::{Create, Dislike, Like, Remove, Update},
    prelude::*,
@@@ -16,11 -16,11 +16,11 @@@ pub(crate) async fn receive_create_post
    context: &LemmyContext,
    request_counter: &mut i32,
  ) -> Result<(), LemmyError> {
--  let user = get_actor_as_user(&create, context, request_counter).await?;
++  let person = get_actor_as_person(&create, context, request_counter).await?;
    let page = PageExt::from_any_base(create.object().to_owned().one().context(location_info!())?)?
      .context(location_info!())?;
  
--  let post = Post::from_apub(&page, context, user.actor_id(), request_counter).await?;
++  let post = Post::from_apub(&page, context, person.actor_id(), request_counter).await?;
  
    // Refetch the view
    let post_id = post.id;
@@@ -45,11 -45,11 +45,11 @@@ pub(crate) async fn receive_update_post
    context: &LemmyContext,
    request_counter: &mut i32,
  ) -> Result<(), LemmyError> {
--  let user = get_actor_as_user(&update, context, request_counter).await?;
++  let person = get_actor_as_person(&update, context, request_counter).await?;
    let page = PageExt::from_any_base(update.object().to_owned().one().context(location_info!())?)?
      .context(location_info!())?;
  
--  let post = Post::from_apub(&page, context, user.actor_id(), request_counter).await?;
++  let post = Post::from_apub(&page, context, person.actor_id(), request_counter).await?;
  
    let post_id = post.id;
    // Refetch the view
@@@ -75,17 -75,17 +75,17 @@@ pub(crate) async fn receive_like_post
    context: &LemmyContext,
    request_counter: &mut i32,
  ) -> Result<(), LemmyError> {
--  let user = get_actor_as_user(&like, context, request_counter).await?;
++  let person = get_actor_as_person(&like, context, request_counter).await?;
  
    let post_id = post.id;
    let like_form = PostLikeForm {
      post_id,
-     person_id: user.id,
 -    user_id: user.id,
++    person_id: person.id,
      score: 1,
    };
--  let user_id = user.id;
++  let person_id = person.id;
    blocking(context.pool(), move |conn| {
--    PostLike::remove(conn, user_id, post_id)?;
++    PostLike::remove(conn, person_id, post_id)?;
      PostLike::like(conn, &like_form)
    })
    .await??;
@@@ -113,17 -113,17 +113,17 @@@ pub(crate) async fn receive_dislike_pos
    context: &LemmyContext,
    request_counter: &mut i32,
  ) -> Result<(), LemmyError> {
--  let user = get_actor_as_user(&dislike, context, request_counter).await?;
++  let person = get_actor_as_person(&dislike, context, request_counter).await?;
  
    let post_id = post.id;
    let like_form = PostLikeForm {
      post_id,
-     person_id: user.id,
 -    user_id: user.id,
++    person_id: person.id,
      score: -1,
    };
--  let user_id = user.id;
++  let person_id = person.id;
    blocking(context.pool(), move |conn| {
--    PostLike::remove(conn, user_id, post_id)?;
++    PostLike::remove(conn, person_id, post_id)?;
      PostLike::like(conn, &like_form)
    })
    .await??;
diff --cc crates/apub/src/activities/receive/post_undo.rs
index 0b9d6f4a,312010a9..67cc20df
--- a/crates/apub/src/activities/receive/post_undo.rs
+++ b/crates/apub/src/activities/receive/post_undo.rs
@@@ -1,5 -1,6 +1,6 @@@
--use crate::activities::receive::get_actor_as_user;
++use crate::activities::receive::get_actor_as_person;
  use activitystreams::activity::{Dislike, Like};
+ use lemmy_api_structs::{blocking, post::PostResponse};
  use lemmy_db_queries::{source::post::Post_, Likeable};
  use lemmy_db_schema::source::post::{Post, PostLike};
  use lemmy_db_views::post_view::PostView;
@@@ -13,12 -13,12 +13,12 @@@ pub(crate) async fn receive_undo_like_p
    context: &LemmyContext,
    request_counter: &mut i32,
  ) -> Result<(), LemmyError> {
--  let user = get_actor_as_user(like, context, request_counter).await?;
++  let person = get_actor_as_person(like, context, request_counter).await?;
  
    let post_id = post.id;
--  let user_id = user.id;
++  let person_id = person.id;
    blocking(context.pool(), move |conn| {
--    PostLike::remove(conn, user_id, post_id)
++    PostLike::remove(conn, person_id, post_id)
    })
    .await??;
  
@@@ -45,12 -45,12 +45,12 @@@ pub(crate) async fn receive_undo_dislik
    context: &LemmyContext,
    request_counter: &mut i32,
  ) -> Result<(), LemmyError> {
--  let user = get_actor_as_user(dislike, context, request_counter).await?;
++  let person = get_actor_as_person(dislike, context, request_counter).await?;
  
    let post_id = post.id;
--  let user_id = user.id;
++  let person_id = person.id;
    blocking(context.pool(), move |conn| {
--    PostLike::remove(conn, user_id, post_id)
++    PostLike::remove(conn, person_id, post_id)
    })
    .await??;
  
diff --cc crates/apub/src/activities/receive/private_message.rs
index 160b20ec,98c25a58..45d4a689
--- a/crates/apub/src/activities/receive/private_message.rs
+++ b/crates/apub/src/activities/receive/private_message.rs
@@@ -1,7 -1,7 +1,7 @@@
  use crate::{
    activities::receive::verify_activity_domains_valid,
    check_is_apub_id_valid,
--  fetcher::user::get_or_fetch_and_upsert_user,
++  fetcher::person::get_or_fetch_and_upsert_person,
    inbox::get_activity_to_and_cc,
    objects::FromApub,
    NoteExt,
@@@ -13,6 -13,7 +13,7 @@@ use activitystreams::
    public,
  };
  use anyhow::{anyhow, Context};
 -use lemmy_api_structs::{blocking, user::PrivateMessageResponse};
++use lemmy_api_structs::{blocking, person::PrivateMessageResponse};
  use lemmy_db_queries::source::private_message::PrivateMessage_;
  use lemmy_db_schema::source::private_message::PrivateMessage;
  use lemmy_db_views::private_message_view::PrivateMessageView;
@@@ -181,19 -181,19 +181,19 @@@ wher
  {
    let to_and_cc = get_activity_to_and_cc(activity);
    if to_and_cc.len() != 1 {
--    return Err(anyhow!("Private message can only be addressed to one user").into());
++    return Err(anyhow!("Private message can only be addressed to one person").into());
    }
    if to_and_cc.contains(&public()) {
      return Err(anyhow!("Private message cant be public").into());
    }
--  let user_id = activity
++  let person_id = activity
      .actor()?
      .to_owned()
      .single_xsd_any_uri()
      .context(location_info!())?;
--  check_is_apub_id_valid(&user_id)?;
--  // check that the sender is a user, not a community
--  get_or_fetch_and_upsert_user(&user_id, &context, request_counter).await?;
++  check_is_apub_id_valid(&person_id)?;
++  // check that the sender is a person, not a community
++  get_or_fetch_and_upsert_person(&person_id, &context, request_counter).await?;
  
    Ok(())
  }
diff --cc crates/apub/src/activities/send/comment.rs
index f007cda4,ac7e884a..c11e86b7
--- a/crates/apub/src/activities/send/comment.rs
+++ b/crates/apub/src/activities/send/comment.rs
@@@ -2,7 -2,7 +2,7 @@@ use crate::
    activities::send::generate_activity_id,
    activity_queue::{send_comment_mentions, send_to_community},
    extensions::context::lemmy_context,
--  fetcher::user::get_or_fetch_and_upsert_user,
++  fetcher::person::get_or_fetch_and_upsert_person,
    objects::ToApub,
    ActorType,
    ApubLikeableType,
@@@ -26,12 -26,12 +26,12 @@@ use activitystreams::
  };
  use anyhow::anyhow;
  use itertools::Itertools;
+ use lemmy_api_structs::{blocking, WebFingerResponse};
  use lemmy_db_queries::{Crud, DbPool};
--use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post, user::User_};
- use lemmy_structs::{blocking, WebFingerResponse};
++use lemmy_db_schema::source::{comment::Comment, community::Community, post::Post, person::Person};
  use lemmy_utils::{
    request::{retry, RecvError},
-   settings::Settings,
+   settings::structs::Settings,
    utils::{scrape_text_for_mentions, MentionData},
    LemmyError,
  };
@@@ -44,8 -44,8 +44,8 @@@ use url::Url
  #[async_trait::async_trait(?Send)]
  impl ApubObjectType for Comment {
    /// Send out information about a newly created comment, to the followers of the community and
--  /// mentioned users.
--  async fn send_create(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
++  /// mentioned persons.
++  async fn send_create(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
      let note = self.to_apub(context.pool()).await?;
  
      let post_id = self.post_id;
@@@ -77,8 -77,8 +77,8 @@@
    }
  
    /// Send out information about an edited post, to the followers of the community and mentioned
--  /// users.
--  async fn send_update(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
++  /// persons.
++  async fn send_update(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
      let note = self.to_apub(context.pool()).await?;
  
      let post_id = self.post_id;
@@@ -109,7 -109,7 +109,7 @@@
      Ok(())
    }
  
--  async fn send_delete(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
++  async fn send_delete(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
      let post_id = self.post_id;
      let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
  
@@@ -135,7 -135,7 +135,7 @@@
  
    async fn send_undo_delete(
      &self,
--    creator: &User_,
++    creator: &Person,
      context: &LemmyContext,
    ) -> Result<(), LemmyError> {
      let post_id = self.post_id;
@@@ -173,7 -173,7 +173,7 @@@
      Ok(())
    }
  
--  async fn send_remove(&self, mod_: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
++  async fn send_remove(&self, mod_: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
      let post_id = self.post_id;
      let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
  
@@@ -197,7 -197,7 +197,7 @@@
      Ok(())
    }
  
--  async fn send_undo_remove(&self, mod_: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
++  async fn send_undo_remove(&self, mod_: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
      let post_id = self.post_id;
      let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
  
@@@ -236,7 -236,7 +236,7 @@@
  
  #[async_trait::async_trait(?Send)]
  impl ApubLikeableType for Comment {
--  async fn send_like(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
++  async fn send_like(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
      let post_id = self.post_id;
      let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
  
@@@ -260,7 -260,7 +260,7 @@@
      Ok(())
    }
  
--  async fn send_dislike(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
++  async fn send_dislike(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
      let post_id = self.post_id;
      let post = blocking(context.pool(), move |conn| Post::read(conn, post_id)).await??;
  
@@@ -286,7 -286,7 +286,7 @@@
  
    async fn send_undo_like(
      &self,
--    creator: &User_,
++    creator: &Person,
      context: &LemmyContext,
    ) -> Result<(), LemmyError> {
      let post_id = self.post_id;
@@@ -342,7 -342,7 +342,7 @@@ impl MentionsAndAddresses 
  
  /// This takes a comment, and builds a list of to_addresses, inboxes,
  /// and mention tags, so they know where to be sent to.
--/// Addresses are the users / addresses that go in the cc field.
++/// Addresses are the persons / addresses that go in the cc field.
  async fn collect_non_local_mentions(
    comment: &Comment,
    community: &Community,
@@@ -356,7 -356,7 +356,7 @@@
    // Add the mention tag
    let mut tags = Vec::new();
  
--  // Get the user IDs for any mentions
++  // Get the person IDs for any mentions
    let mentions = scrape_text_for_mentions(&comment.content)
      .into_iter()
      // Filter only the non-local ones
@@@ -369,8 -369,8 +369,8 @@@
        debug!("mention actor_id: {}", actor_id);
        addressed_ccs.push(actor_id.to_owned().to_string().parse()?);
  
--      let mention_user = get_or_fetch_and_upsert_user(&actor_id, context, &mut 0).await?;
--      inboxes.push(mention_user.get_shared_inbox_or_inbox_url());
++      let mention_person = get_or_fetch_and_upsert_person(&actor_id, context, &mut 0).await?;
++      inboxes.push(mention_person.get_shared_inbox_or_inbox_url());
  
        let mut mention_tag = Mention::new();
        mention_tag.set_href(actor_id).set_name(mention.full_name());
@@@ -387,9 -387,9 +387,9 @@@
    })
  }
  
--/// Returns the apub ID of the user this comment is responding to. Meaning, in case this is a
++/// Returns the apub ID of the person this comment is responding to. Meaning, in case this is a
  /// top-level comment, the creator of the post, otherwise the creator of the parent comment.
--async fn get_comment_parent_creator(pool: &DbPool, comment: &Comment) -> Result<User_, LemmyError> {
++async fn get_comment_parent_creator(pool: &DbPool, comment: &Comment) -> Result<Person, LemmyError> {
    let parent_creator_id = if let Some(parent_comment_id) = comment.parent_id {
      let parent_comment =
        blocking(pool, move |conn| Comment::read(conn, parent_comment_id)).await??;
@@@ -399,10 -399,10 +399,10 @@@
      let parent_post = blocking(pool, move |conn| Post::read(conn, parent_post_id)).await??;
      parent_post.creator_id
    };
--  Ok(blocking(pool, move |conn| User_::read(conn, parent_creator_id)).await??)
++  Ok(blocking(pool, move |conn| Person::read(conn, parent_creator_id)).await??)
  }
  
--/// Turns a user id like `@name@example.com` into an apub ID, like `https://example.com/user/name`,
++/// Turns a person id like `@name@example.com` into an apub ID, like `https://example.com/user/name`,
  /// using webfinger.
  async fn fetch_webfinger_url(mention: &MentionData, client: &Client) -> Result<Url, LemmyError> {
    let fetch_url = format!(
diff --cc crates/apub/src/activities/send/community.rs
index a574c7b8,3e77248f..9cef95b6
--- a/crates/apub/src/activities/send/community.rs
+++ b/crates/apub/src/activities/send/community.rs
@@@ -3,7 -3,7 +3,7 @@@ use crate::
    activity_queue::{send_activity_single_dest, send_to_community_followers},
    check_is_apub_id_valid,
    extensions::context::lemmy_context,
--  fetcher::user::get_or_fetch_and_upsert_user,
++  fetcher::person::get_or_fetch_and_upsert_person,
    ActorType,
  };
  use activitystreams::{
@@@ -70,7 -70,7 +70,7 @@@ impl ActorType for Community 
      unimplemented!()
    }
  
--  /// As a local community, accept the follow request from a remote user.
++  /// As a local community, accept the follow request from a remote person.
    async fn send_accept_follow(
      &self,
      follow: Follow,
@@@ -80,7 -80,7 +80,7 @@@
        .actor()?
        .as_single_xsd_any_uri()
        .context(location_info!())?;
--    let user = get_or_fetch_and_upsert_user(actor_uri, context, &mut 0).await?;
++    let person = get_or_fetch_and_upsert_person(actor_uri, context, &mut 0).await?;
  
      let mut accept = Accept::new(
        self.actor_id.to_owned().into_inner(),
@@@ -89,9 -89,9 +89,9 @@@
      accept
        .set_many_contexts(lemmy_context()?)
        .set_id(generate_activity_id(AcceptType::Accept)?)
--      .set_to(user.actor_id());
++      .set_to(person.actor_id());
  
--    send_activity_single_dest(accept, self, user.inbox_url.into(), context).await?;
++    send_activity_single_dest(accept, self, person.inbox_url.into(), context).await?;
      Ok(())
    }
  
diff --cc crates/apub/src/activities/send/mod.rs
index 166855e2,2da0b48c..7f19d042
--- a/crates/apub/src/activities/send/mod.rs
+++ b/crates/apub/src/activities/send/mod.rs
@@@ -6,7 -6,7 +6,7 @@@ pub(crate) mod comment
  pub(crate) mod community;
  pub(crate) mod post;
  pub(crate) mod private_message;
--pub(crate) mod user;
++pub(crate) mod person;
  
  /// Generate a unique ID for an activity, in the format:
  /// `http(s)://example.com/receive/create/202daf0a-1489-45df-8d2e-c8a3173fed36`
diff --cc crates/apub/src/activities/send/person.rs
index 13ec7340,1dc62e0b..40cc1422
--- a/crates/apub/src/activities/send/person.rs
+++ b/crates/apub/src/activities/send/person.rs
@@@ -16,15 -17,14 +17,14 @@@ use lemmy_api_structs::blocking
  use lemmy_db_queries::{ApubObject, DbPool, Followable};
  use lemmy_db_schema::source::{
    community::{Community, CommunityFollower, CommunityFollowerForm},
--  user::User_,
++  person::Person,
  };
- use lemmy_structs::blocking;
  use lemmy_utils::LemmyError;
  use lemmy_websocket::LemmyContext;
  use url::Url;
  
  #[async_trait::async_trait(?Send)]
--impl ActorType for User_ {
++impl ActorType for Person {
    fn is_local(&self) -> bool {
      self.local
    }
@@@ -48,7 -48,7 +48,7 @@@
        .into()
    }
  
--  /// As a given local user, send out a follow request to a remote community.
++  /// As a given local person, send out a follow request to a remote community.
    async fn send_follow(
      &self,
      follow_actor_id: &Url,
diff --cc crates/apub/src/activities/send/post.rs
index ce74d20a,44f5b895..0fe3f634
--- a/crates/apub/src/activities/send/post.rs
+++ b/crates/apub/src/activities/send/post.rs
@@@ -21,16 -21,16 +21,16 @@@ use activitystreams::
    prelude::*,
    public,
  };
+ use lemmy_api_structs::blocking;
  use lemmy_db_queries::Crud;
--use lemmy_db_schema::source::{community::Community, post::Post, user::User_};
- use lemmy_structs::blocking;
++use lemmy_db_schema::source::{community::Community, post::Post, person::Person};
  use lemmy_utils::LemmyError;
  use lemmy_websocket::LemmyContext;
  
  #[async_trait::async_trait(?Send)]
  impl ApubObjectType for Post {
    /// Send out information about a newly created post, to the followers of the community.
--  async fn send_create(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
++  async fn send_create(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
      let page = self.to_apub(context.pool()).await?;
  
      let community_id = self.community_id;
@@@ -54,7 -54,7 +54,7 @@@
    }
  
    /// Send out information about an edited post, to the followers of the community.
--  async fn send_update(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
++  async fn send_update(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
      let page = self.to_apub(context.pool()).await?;
  
      let community_id = self.community_id;
@@@ -77,7 -77,7 +77,7 @@@
      Ok(())
    }
  
--  async fn send_delete(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
++  async fn send_delete(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
      let community_id = self.community_id;
      let community = blocking(context.pool(), move |conn| {
        Community::read(conn, community_id)
@@@ -100,7 -100,7 +100,7 @@@
  
    async fn send_undo_delete(
      &self,
--    creator: &User_,
++    creator: &Person,
      context: &LemmyContext,
    ) -> Result<(), LemmyError> {
      let community_id = self.community_id;
@@@ -134,7 -134,7 +134,7 @@@
      Ok(())
    }
  
--  async fn send_remove(&self, mod_: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
++  async fn send_remove(&self, mod_: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
      let community_id = self.community_id;
      let community = blocking(context.pool(), move |conn| {
        Community::read(conn, community_id)
@@@ -155,7 -155,7 +155,7 @@@
      Ok(())
    }
  
--  async fn send_undo_remove(&self, mod_: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
++  async fn send_undo_remove(&self, mod_: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
      let community_id = self.community_id;
      let community = blocking(context.pool(), move |conn| {
        Community::read(conn, community_id)
@@@ -190,7 -190,7 +190,7 @@@
  
  #[async_trait::async_trait(?Send)]
  impl ApubLikeableType for Post {
--  async fn send_like(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
++  async fn send_like(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
      let community_id = self.community_id;
      let community = blocking(context.pool(), move |conn| {
        Community::read(conn, community_id)
@@@ -211,7 -211,7 +211,7 @@@
      Ok(())
    }
  
--  async fn send_dislike(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
++  async fn send_dislike(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
      let community_id = self.community_id;
      let community = blocking(context.pool(), move |conn| {
        Community::read(conn, community_id)
@@@ -234,7 -234,7 +234,7 @@@
  
    async fn send_undo_like(
      &self,
--    creator: &User_,
++    creator: &Person,
      context: &LemmyContext,
    ) -> Result<(), LemmyError> {
      let community_id = self.community_id;
diff --cc crates/apub/src/activities/send/private_message.rs
index 31184a70,322eee5f..cf0046fa
--- a/crates/apub/src/activities/send/private_message.rs
+++ b/crates/apub/src/activities/send/private_message.rs
@@@ -16,20 -16,20 +16,20 @@@ use activitystreams::
    },
    prelude::*,
  };
+ use lemmy_api_structs::blocking;
  use lemmy_db_queries::Crud;
--use lemmy_db_schema::source::{private_message::PrivateMessage, user::User_};
- use lemmy_structs::blocking;
++use lemmy_db_schema::source::{private_message::PrivateMessage, person::Person};
  use lemmy_utils::LemmyError;
  use lemmy_websocket::LemmyContext;
  
  #[async_trait::async_trait(?Send)]
  impl ApubObjectType for PrivateMessage {
    /// Send out information about a newly created private message
--  async fn send_create(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
++  async fn send_create(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
      let note = self.to_apub(context.pool()).await?;
  
      let recipient_id = self.recipient_id;
--    let recipient = blocking(context.pool(), move |conn| User_::read(conn, recipient_id)).await??;
++    let recipient = blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
  
      let mut create = Create::new(
        creator.actor_id.to_owned().into_inner(),
@@@ -46,11 -46,11 +46,11 @@@
    }
  
    /// Send out information about an edited private message, to the followers of the community.
--  async fn send_update(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
++  async fn send_update(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
      let note = self.to_apub(context.pool()).await?;
  
      let recipient_id = self.recipient_id;
--    let recipient = blocking(context.pool(), move |conn| User_::read(conn, recipient_id)).await??;
++    let recipient = blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
  
      let mut update = Update::new(
        creator.actor_id.to_owned().into_inner(),
@@@ -65,9 -65,9 +65,9 @@@
      Ok(())
    }
  
--  async fn send_delete(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError> {
++  async fn send_delete(&self, creator: &Person, context: &LemmyContext) -> Result<(), LemmyError> {
      let recipient_id = self.recipient_id;
--    let recipient = blocking(context.pool(), move |conn| User_::read(conn, recipient_id)).await??;
++    let recipient = blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
  
      let mut delete = Delete::new(
        creator.actor_id.to_owned().into_inner(),
@@@ -84,11 -84,11 +84,11 @@@
  
    async fn send_undo_delete(
      &self,
--    creator: &User_,
++    creator: &Person,
      context: &LemmyContext,
    ) -> Result<(), LemmyError> {
      let recipient_id = self.recipient_id;
--    let recipient = blocking(context.pool(), move |conn| User_::read(conn, recipient_id)).await??;
++    let recipient = blocking(context.pool(), move |conn| Person::read(conn, recipient_id)).await??;
  
      let mut delete = Delete::new(
        creator.actor_id.to_owned().into_inner(),
@@@ -113,13 -113,13 +113,13 @@@
      Ok(())
    }
  
--  async fn send_remove(&self, _mod_: &User_, _context: &LemmyContext) -> Result<(), LemmyError> {
++  async fn send_remove(&self, _mod_: &Person, _context: &LemmyContext) -> Result<(), LemmyError> {
      unimplemented!()
    }
  
    async fn send_undo_remove(
      &self,
--    _mod_: &User_,
++    _mod_: &Person,
      _context: &LemmyContext,
    ) -> Result<(), LemmyError> {
      unimplemented!()
diff --cc crates/apub/src/activity_queue.rs
index c0c4ac46,f607dafe..ccee1b90
--- a/crates/apub/src/activity_queue.rs
+++ b/crates/apub/src/activity_queue.rs
@@@ -21,8 -21,8 +21,8 @@@ use background_jobs::
  };
  use itertools::Itertools;
  use lemmy_db_queries::DbPool;
--use lemmy_db_schema::source::{community::Community, user::User_};
- use lemmy_utils::{location_info, settings::Settings, LemmyError};
++use lemmy_db_schema::source::{community::Community, person::Person};
+ use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
  use lemmy_websocket::LemmyContext;
  use log::{debug, warn};
  use reqwest::Client;
@@@ -112,7 -112,7 +112,7 @@@ wher
    Ok(())
  }
  
--/// Sends an activity from a local user to a remote community.
++/// Sends an activity from a local person to a remote community.
  ///
  /// * `activity` the activity to send
  /// * `creator` the creator of the activity
@@@ -120,7 -120,7 +120,7 @@@
  ///
  pub(crate) async fn send_to_community<T, Kind>(
    activity: T,
--  creator: &User_,
++  creator: &Person,
    community: &Community,
    context: &LemmyContext,
  ) -> Result<(), LemmyError>
@@@ -157,13 -157,13 +157,13 @@@ wher
    Ok(())
  }
  
--/// Sends notification to any users mentioned in a comment
++/// Sends notification to any persons mentioned in a comment
  ///
--/// * `creator` user who created the comment
--/// * `mentions` list of inboxes of users which are mentioned in the comment
++/// * `creator` person who created the comment
++/// * `mentions` list of inboxes of persons which are mentioned in the comment
  /// * `activity` either a `Create/Note` or `Update/Note`
  pub(crate) async fn send_comment_mentions<T, Kind>(
--  creator: &User_,
++  creator: &Person,
    mentions: Vec<Url>,
    activity: T,
    context: &LemmyContext,
diff --cc crates/apub/src/extensions/signatures.rs
index ebbf9a0a,ebbf9a0a..a9b4cfbd
--- a/crates/apub/src/extensions/signatures.rs
+++ b/crates/apub/src/extensions/signatures.rs
@@@ -95,7 -95,7 +95,7 @@@ pub(crate) fn verify_signature
    }
  }
  
--/// Extension for actor public key, which is needed on user and community for HTTP signatures.
++/// Extension for actor public key, which is needed on person and community for HTTP signatures.
  ///
  /// Taken from: https://docs.rs/activitystreams/0.5.0-alpha.17/activitystreams/ext/index.html
  #[derive(Clone, Debug, Deserialize, Serialize)]
diff --cc crates/apub/src/fetcher/community.rs
index e1211f33,fb545ed6..12821bbe
--- a/crates/apub/src/fetcher/community.rs
+++ b/crates/apub/src/fetcher/community.rs
@@@ -1,11 -1,11 +1,11 @@@
  use crate::{
    fetcher::{
      fetch::fetch_remote_object,
--    get_or_fetch_and_upsert_user,
++    get_or_fetch_and_upsert_person,
      is_deleted,
      should_refetch_actor,
    },
--  inbox::user_inbox::receive_announce,
++  inbox::person_inbox::receive_announce,
    objects::FromApub,
    GroupExt,
  };
@@@ -92,7 -92,7 +92,7 @@@ async fn fetch_remote_community
    let mut creator_and_moderators = Vec::new();
  
    for uri in creator_and_moderator_uris {
--    let c_or_m = get_or_fetch_and_upsert_user(uri, context, recursion_counter).await?;
++    let c_or_m = get_or_fetch_and_upsert_person(uri, context, recursion_counter).await?;
  
      creator_and_moderators.push(c_or_m);
    }
diff --cc crates/apub/src/fetcher/mod.rs
index 593b163f,593b163f..2c1b6b9a
--- a/crates/apub/src/fetcher/mod.rs
+++ b/crates/apub/src/fetcher/mod.rs
@@@ -2,13 -2,13 +2,13 @@@ pub(crate) mod community
  mod fetch;
  pub(crate) mod objects;
  pub mod search;
--pub(crate) mod user;
++pub(crate) mod person;
  
  use crate::{
    fetcher::{
      community::get_or_fetch_and_upsert_community,
      fetch::FetchError,
--    user::get_or_fetch_and_upsert_user,
++    person::get_or_fetch_and_upsert_person,
    },
    ActorType,
  };
@@@ -37,8 -37,8 +37,8 @@@ wher
    false
  }
  
--/// Get a remote actor from its apub ID (either a user or a community). Thin wrapper around
--/// `get_or_fetch_and_upsert_user()` and `get_or_fetch_and_upsert_community()`.
++/// Get a remote actor from its apub ID (either a person or a community). Thin wrapper around
++/// `get_or_fetch_and_upsert_person()` and `get_or_fetch_and_upsert_community()`.
  ///
  /// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database.
  /// Otherwise it is fetched from the remote instance, stored and returned.
@@@ -50,7 -50,7 +50,7 @@@ pub(crate) async fn get_or_fetch_and_up
    let community = get_or_fetch_and_upsert_community(apub_id, context, recursion_counter).await;
    let actor: Box<dyn ActorType> = match community {
      Ok(c) => Box::new(c),
--    Err(_) => Box::new(get_or_fetch_and_upsert_user(apub_id, context, recursion_counter).await?),
++    Err(_) => Box::new(get_or_fetch_and_upsert_person(apub_id, context, recursion_counter).await?),
    };
    Ok(actor)
  }
diff --cc crates/apub/src/fetcher/person.rs
index 21cdfb34,e3ef41c7..120ff845
--- a/crates/apub/src/fetcher/person.rs
+++ b/crates/apub/src/fetcher/person.rs
@@@ -5,66 -5,66 +5,66 @@@ use crate::
  };
  use anyhow::anyhow;
  use diesel::result::Error::NotFound;
- use lemmy_db_queries::{source::user::User, ApubObject};
- use lemmy_db_schema::source::user::User_;
- use lemmy_structs::blocking;
+ use lemmy_api_structs::blocking;
 -use lemmy_db_queries::{source::user::User, ApubObject};
 -use lemmy_db_schema::source::user::User_;
++use lemmy_db_queries::{source::person::Person_, ApubObject};
++use lemmy_db_schema::source::person::Person;
  use lemmy_utils::LemmyError;
  use lemmy_websocket::LemmyContext;
  use log::debug;
  use url::Url;
  
--/// Get a user from its apub ID.
++/// Get a person from its apub ID.
  ///
  /// If it exists locally and `!should_refetch_actor()`, it is returned directly from the database.
  /// Otherwise it is fetched from the remote instance, stored and returned.
--pub(crate) async fn get_or_fetch_and_upsert_user(
++pub(crate) async fn get_or_fetch_and_upsert_person(
    apub_id: &Url,
    context: &LemmyContext,
    recursion_counter: &mut i32,
--) -> Result<User_, LemmyError> {
++) -> Result<Person, LemmyError> {
    let apub_id_owned = apub_id.to_owned();
--  let user = blocking(context.pool(), move |conn| {
--    User_::read_from_apub_id(conn, &apub_id_owned.into())
++  let person = blocking(context.pool(), move |conn| {
++    Person::read_from_apub_id(conn, &apub_id_owned.into())
    })
    .await?;
  
--  match user {
++  match person {
      // If its older than a day, re-fetch it
      Ok(u) if !u.local && should_refetch_actor(u.last_refreshed_at) => {
--      debug!("Fetching and updating from remote user: {}", apub_id);
++      debug!("Fetching and updating from remote person: {}", apub_id);
        let person =
          fetch_remote_object::<PersonExt>(context.client(), apub_id, recursion_counter).await;
  
        if is_deleted(&person) {
--        // TODO: use User_::update_deleted() once implemented
++        // TODO: use Person::update_deleted() once implemented
          blocking(context.pool(), move |conn| {
--          User_::delete_account(conn, u.id)
++          Person::delete_account(conn, u.id)
          })
          .await??;
--        return Err(anyhow!("User was deleted by remote instance").into());
++        return Err(anyhow!("Person was deleted by remote instance").into());
        } else if person.is_err() {
          return Ok(u);
        }
  
--      let user = User_::from_apub(&person?, context, apub_id.to_owned(), recursion_counter).await?;
++      let person = Person::from_apub(&person?, context, apub_id.to_owned(), recursion_counter).await?;
  
--      let user_id = user.id;
++      let person_id = person.id;
        blocking(context.pool(), move |conn| {
--        User_::mark_as_updated(conn, user_id)
++        Person::mark_as_updated(conn, person_id)
        })
        .await??;
  
--      Ok(user)
++      Ok(person)
      }
      Ok(u) => Ok(u),
      Err(NotFound {}) => {
--      debug!("Fetching and creating remote user: {}", apub_id);
++      debug!("Fetching and creating remote person: {}", apub_id);
        let person =
          fetch_remote_object::<PersonExt>(context.client(), apub_id, recursion_counter).await?;
  
--      let user = User_::from_apub(&person, context, apub_id.to_owned(), recursion_counter).await?;
++      let person = Person::from_apub(&person, context, apub_id.to_owned(), recursion_counter).await?;
  
--      Ok(user)
++      Ok(person)
      }
      Err(e) => Err(e.into()),
    }
diff --cc crates/apub/src/fetcher/search.rs
index a831ac40,acaccff2..0b2921c7
--- a/crates/apub/src/fetcher/search.rs
+++ b/crates/apub/src/fetcher/search.rs
@@@ -2,7 -2,7 +2,7 @@@ use crate::
    fetcher::{
      fetch::fetch_remote_object,
      get_or_fetch_and_upsert_community,
--    get_or_fetch_and_upsert_user,
++    get_or_fetch_and_upsert_person,
      is_deleted,
    },
    find_object_by_id,
@@@ -21,7 -22,7 +22,7 @@@ use lemmy_db_queries::
      community::Community_,
      post::Post_,
      private_message::PrivateMessage_,
--    user::User,
++    person::Person_,
    },
    SearchType,
  };
@@@ -30,12 -31,11 +31,11 @@@ use lemmy_db_schema::source::
    community::Community,
    post::Post,
    private_message::PrivateMessage,
--  user::User_,
++  person::Person,
  };
  use lemmy_db_views::{comment_view::CommentView, post_view::PostView};
--use lemmy_db_views_actor::{community_view::CommunityView, user_view::UserViewSafe};
- use lemmy_structs::{blocking, site::SearchResponse};
- use lemmy_utils::{settings::Settings, LemmyError};
++use lemmy_db_views_actor::{community_view::CommunityView, person_view::PersonViewSafe};
+ use lemmy_utils::{settings::structs::Settings, LemmyError};
  use lemmy_websocket::LemmyContext;
  use log::debug;
  use url::Url;
@@@ -66,7 -66,7 +66,7 @@@ pub async fn search_by_apub_id
      debug!("Search for {}", query);
      let split = query.split('@').collect::<Vec<&str>>();
  
--    // User type will look like ['', username, instance]
++    // Person type will look like ['', username, instance]
      // Community will look like [!community, instance]
      let (name, instance) = if split.len() == 3 {
        (format!("/u/{}", split[1]), split[2])
@@@ -122,13 -122,13 +122,13 @@@ async fn build_response
  
    match fetch_response {
      SearchAcceptedObjects::Person(p) => {
--      let user_uri = p.inner.id(domain)?.context("person has no id")?;
++      let person_uri = p.inner.id(domain)?.context("person has no id")?;
  
--      let user = get_or_fetch_and_upsert_user(&user_uri, context, recursion_counter).await?;
++      let person = get_or_fetch_and_upsert_person(&person_uri, context, recursion_counter).await?;
  
        response.users = vec![
          blocking(context.pool(), move |conn| {
--          UserViewSafe::read(conn, user.id)
++          PersonViewSafe::read(conn, person.id)
          })
          .await??,
        ];
@@@ -182,10 -182,10 +182,10 @@@ async fn delete_object_locally(query_ur
        })
        .await??;
      }
--    Object::User(u) => {
++    Object::Person(u) => {
        // TODO: implement update_deleted() for user, move it to ApubObject trait
        blocking(context.pool(), move |conn| {
--        User_::delete_account(conn, u.id)
++        Person::delete_account(conn, u.id)
        })
        .await??;
      }
diff --cc crates/apub/src/http/mod.rs
index f0ffbcb1,6bf4bbde..5a1add8d
--- a/crates/apub/src/http/mod.rs
+++ b/crates/apub/src/http/mod.rs
@@@ -11,7 -12,7 +12,7 @@@ use url::Url
  pub mod comment;
  pub mod community;
  pub mod post;
--pub mod user;
++pub mod person;
  
  /// Convert the data to json and turn it into an HTTP Response with the correct ActivityPub
  /// headers.
diff --cc crates/apub/src/http/person.rs
index 7c7653e5,77c40d85..89d678cf
--- a/crates/apub/src/http/person.rs
+++ b/crates/apub/src/http/person.rs
@@@ -9,70 -9,70 +9,70 @@@ use activitystreams::
    collection::{CollectionExt, OrderedCollection},
  };
  use actix_web::{body::Body, web, HttpResponse};
- use lemmy_db_queries::source::user::User;
- use lemmy_db_schema::source::user::User_;
- use lemmy_structs::blocking;
+ use lemmy_api_structs::blocking;
 -use lemmy_db_queries::source::user::User;
 -use lemmy_db_schema::source::user::User_;
++use lemmy_db_queries::source::person::Person_;
++use lemmy_db_schema::source::person::Person;
  use lemmy_utils::LemmyError;
  use lemmy_websocket::LemmyContext;
  use serde::Deserialize;
  use url::Url;
  
  #[derive(Deserialize)]
--pub struct UserQuery {
++pub struct PersonQuery {
    user_name: String,
  }
  
--/// Return the ActivityPub json representation of a local user over HTTP.
--pub async fn get_apub_user_http(
--  info: web::Path<UserQuery>,
++/// Return the ActivityPub json representation of a local person over HTTP.
++pub async fn get_apub_person_http(
++  info: web::Path<PersonQuery>,
    context: web::Data<LemmyContext>,
  ) -> Result<HttpResponse<Body>, LemmyError> {
    let user_name = info.into_inner().user_name;
--  // TODO: this needs to be able to read deleted users, so that it can send tombstones
--  let user = blocking(context.pool(), move |conn| {
--    User_::find_by_email_or_username(conn, &user_name)
++  // TODO: this needs to be able to read deleted persons, so that it can send tombstones
++  let person = blocking(context.pool(), move |conn| {
++    Person::find_by_name(conn, &user_name)
    })
    .await??;
  
--  if !user.deleted {
--    let apub = user.to_apub(context.pool()).await?;
++  if !person.deleted {
++    let apub = person.to_apub(context.pool()).await?;
  
      Ok(create_apub_response(&apub))
    } else {
--    Ok(create_apub_tombstone_response(&user.to_tombstone()?))
++    Ok(create_apub_tombstone_response(&person.to_tombstone()?))
    }
  }
  
--pub async fn get_apub_user_outbox(
--  info: web::Path<UserQuery>,
++pub async fn get_apub_person_outbox(
++  info: web::Path<PersonQuery>,
    context: web::Data<LemmyContext>,
  ) -> Result<HttpResponse<Body>, LemmyError> {
--  let user = blocking(context.pool(), move |conn| {
--    User_::read_from_name(&conn, &info.user_name)
++  let person = blocking(context.pool(), move |conn| {
++    Person::find_by_name(&conn, &info.user_name)
    })
    .await??;
--  // TODO: populate the user outbox
++  // TODO: populate the person outbox
    let mut collection = OrderedCollection::new();
    collection
      .set_many_items(Vec::<Url>::new())
      .set_many_contexts(lemmy_context()?)
--    .set_id(user.get_outbox_url()?)
++    .set_id(person.get_outbox_url()?)
      .set_total_items(0_u64);
    Ok(create_apub_response(&collection))
  }
  
--pub async fn get_apub_user_inbox(
--  info: web::Path<UserQuery>,
++pub async fn get_apub_person_inbox(
++  info: web::Path<PersonQuery>,
    context: web::Data<LemmyContext>,
  ) -> Result<HttpResponse<Body>, LemmyError> {
--  let user = blocking(context.pool(), move |conn| {
--    User_::read_from_name(&conn, &info.user_name)
++  let person = blocking(context.pool(), move |conn| {
++    Person::find_by_name(&conn, &info.user_name)
    })
    .await??;
  
    let mut collection = OrderedCollection::new();
    collection
--    .set_id(format!("{}/inbox", user.actor_id.into_inner()).parse()?)
++    .set_id(format!("{}/inbox", person.actor_id.into_inner()).parse()?)
      .set_many_contexts(lemmy_context()?);
    Ok(create_apub_response(&collection))
  }
diff --cc crates/apub/src/inbox/community_inbox.rs
index 6a51f9fd,f003fb16..f82b1b55
--- a/crates/apub/src/inbox/community_inbox.rs
+++ b/crates/apub/src/inbox/community_inbox.rs
@@@ -29,10 -30,9 +30,9 @@@ use lemmy_api_structs::blocking
  use lemmy_db_queries::{source::community::Community_, ApubObject, DbPool, Followable};
  use lemmy_db_schema::source::{
    community::{Community, CommunityFollower, CommunityFollowerForm},
--  user::User_,
++  person::Person,
  };
--use lemmy_db_views_actor::community_user_ban_view::CommunityUserBanView;
- use lemmy_structs::blocking;
++use lemmy_db_views_actor::community_person_ban_view::CommunityPersonBanView;
  use lemmy_utils::{location_info, LemmyError};
  use lemmy_websocket::LemmyContext;
  use log::info;
@@@ -44,8 -44,8 +44,8 @@@ use url::Url
  #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
  #[serde(rename_all = "PascalCase")]
  pub enum CommunityValidTypes {
--  Follow,  // follow request from a user
--  Undo,    // unfollow from a user
++  Follow,  // follow request from a person
++  Undo,    // unfollow from a person
    Create,  // create post or comment
    Update,  // update post or comment
    Like,    // upvote post or comment
@@@ -113,21 -113,21 +113,21 @@@ pub(crate) async fn community_receive_m
    context: &LemmyContext,
    request_counter: &mut i32,
  ) -> Result<HttpResponse, LemmyError> {
--  // Only users can send activities to the community, so we can get the actor as user
++  // Only persons can send activities to the community, so we can get the actor as person
    // unconditionally.
    let actor_id = actor.actor_id();
--  let user = blocking(&context.pool(), move |conn| {
--    User_::read_from_apub_id(&conn, &actor_id.into())
++  let person = blocking(&context.pool(), move |conn| {
++    Person::read_from_apub_id(&conn, &actor_id.into())
    })
    .await??;
--  check_community_or_site_ban(&user, to_community.id, context.pool()).await?;
++  check_community_or_site_ban(&person, to_community.id, context.pool()).await?;
  
    let any_base = activity.clone().into_any_base()?;
    let actor_url = actor.actor_id();
    let activity_kind = activity.kind().context(location_info!())?;
    let do_announce = match activity_kind {
      CommunityValidTypes::Follow => {
--      handle_follow(any_base.clone(), user, &to_community, &context).await?;
++      handle_follow(any_base.clone(), person, &to_community, &context).await?;
        false
      }
      CommunityValidTypes::Undo => {
@@@ -162,7 -162,7 +162,7 @@@
      }
      CommunityValidTypes::Remove => {
        // TODO: we dont support remote mods, so this is ignored for now
--      //receive_remove_for_community(context, any_base.clone(), &user_url).await?
++      //receive_remove_for_community(context, any_base.clone(), &person_url).await?
        false
      }
    };
@@@ -178,20 -178,20 +178,20 @@@
    Ok(HttpResponse::Ok().finish())
  }
  
--/// Handle a follow request from a remote user, adding the user as follower and returning an
++/// Handle a follow request from a remote person, adding the person as follower and returning an
  /// Accept activity.
  async fn handle_follow(
    activity: AnyBase,
--  user: User_,
++  person: Person,
    community: &Community,
    context: &LemmyContext,
  ) -> Result<HttpResponse, LemmyError> {
    let follow = Follow::from_any_base(activity)?.context(location_info!())?;
--  verify_activity_domains_valid(&follow, &user.actor_id(), false)?;
++  verify_activity_domains_valid(&follow, &person.actor_id(), false)?;
  
    let community_follower_form = CommunityFollowerForm {
      community_id: community.id,
-     person_id: user.id,
 -    user_id: user.id,
++    person_id: person.id,
      pending: false,
    };
  
@@@ -226,27 -226,27 +226,27 @@@ async fn handle_undo
    }
  }
  
--/// Handle `Undo/Follow` from a user, removing the user from followers list.
++/// Handle `Undo/Follow` from a person, removing the person from followers list.
  async fn handle_undo_follow(
    activity: AnyBase,
--  user_url: Url,
++  person_url: Url,
    community: &Community,
    context: &LemmyContext,
  ) -> Result<(), LemmyError> {
    let undo = Undo::from_any_base(activity)?.context(location_info!())?;
--  verify_activity_domains_valid(&undo, &user_url, true)?;
++  verify_activity_domains_valid(&undo, &person_url, true)?;
  
    let object = undo.object().to_owned().one().context(location_info!())?;
    let follow = Follow::from_any_base(object)?.context(location_info!())?;
--  verify_activity_domains_valid(&follow, &user_url, false)?;
++  verify_activity_domains_valid(&follow, &person_url, false)?;
  
--  let user = blocking(&context.pool(), move |conn| {
--    User_::read_from_apub_id(&conn, &user_url.into())
++  let person = blocking(&context.pool(), move |conn| {
++    Person::read_from_apub_id(&conn, &person_url.into())
    })
    .await??;
    let community_follower_form = CommunityFollowerForm {
      community_id: community.id,
-     person_id: user.id,
 -    user_id: user.id,
++    person_id: person.id,
      pending: false,
    };
  
@@@ -260,17 -260,17 +260,17 @@@
  }
  
  pub(crate) async fn check_community_or_site_ban(
--  user: &User_,
++  person: &Person,
    community_id: i32,
    pool: &DbPool,
  ) -> Result<(), LemmyError> {
--  if user.banned {
--    return Err(anyhow!("User is banned from site").into());
++  if person.banned {
++    return Err(anyhow!("Person is banned from site").into());
    }
--  let user_id = user.id;
--  let is_banned = move |conn: &'_ _| CommunityUserBanView::get(conn, user_id, community_id).is_ok();
++  let person_id = person.id;
++  let is_banned = move |conn: &'_ _| CommunityPersonBanView::get(conn, person_id, community_id).is_ok();
    if blocking(pool, is_banned).await? {
--    return Err(anyhow!("User is banned from community").into());
++    return Err(anyhow!("Person is banned from community").into());
    }
  
    Ok(())
diff --cc crates/apub/src/inbox/mod.rs
index 765d5dff,21585aa6..71d1c133
--- a/crates/apub/src/inbox/mod.rs
+++ b/crates/apub/src/inbox/mod.rs
@@@ -17,9 -18,8 +18,8 @@@ use lemmy_db_queries::
    ApubObject,
    DbPool,
  };
--use lemmy_db_schema::source::{activity::Activity, community::Community, user::User_};
- use lemmy_structs::blocking;
- use lemmy_utils::{location_info, settings::Settings, LemmyError};
++use lemmy_db_schema::source::{activity::Activity, community::Community, person::Person};
+ use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
  use lemmy_websocket::LemmyContext;
  use serde::Serialize;
  use std::fmt::Debug;
@@@ -28,7 -28,7 +28,7 @@@ use url::Url
  pub mod community_inbox;
  mod receive_for_community;
  pub mod shared_inbox;
--pub mod user_inbox;
++pub mod person_inbox;
  
  pub(crate) fn get_activity_id<T, Kind>(activity: &T, creator_uri: &Url) -> Result<Url, LemmyError>
  where
@@@ -119,17 -119,17 +119,17 @@@ wher
  }
  
  /// Returns true if `to_and_cc` contains at least one local user.
--pub(crate) async fn is_addressed_to_local_user(
++pub(crate) async fn is_addressed_to_local_person(
    to_and_cc: &[Url],
    pool: &DbPool,
  ) -> Result<bool, LemmyError> {
    for url in to_and_cc {
      let url = url.to_owned();
--    let user = blocking(&pool, move |conn| {
--      User_::read_from_apub_id(&conn, &url.into())
++    let person = blocking(&pool, move |conn| {
++      Person::read_from_apub_id(&conn, &url.into())
      })
      .await?;
--    if let Ok(u) = user {
++    if let Ok(u) = person {
        if u.local {
          return Ok(true);
        }
diff --cc crates/apub/src/inbox/person_inbox.rs
index 5657faf1,1a906d62..c83c5037
--- a/crates/apub/src/inbox/person_inbox.rs
+++ b/crates/apub/src/inbox/person_inbox.rs
@@@ -25,7 -25,7 +25,7 @@@ use crate::
      inbox_verify_http_signature,
      is_activity_already_known,
      is_addressed_to_community_followers,
--    is_addressed_to_local_user,
++    is_addressed_to_local_person,
      is_addressed_to_public,
      receive_for_community::{
        receive_create_for_community,
@@@ -48,13 -48,13 +48,13 @@@ use activitystreams::
  use actix_web::{web, HttpRequest, HttpResponse};
  use anyhow::{anyhow, Context};
  use diesel::NotFound;
- use lemmy_db_queries::{source::user::User, ApubObject, Followable};
+ use lemmy_api_structs::blocking;
 -use lemmy_db_queries::{source::user::User, ApubObject, Followable};
++use lemmy_db_queries::{source::person::Person_, ApubObject, Followable};
  use lemmy_db_schema::source::{
    community::{Community, CommunityFollower},
    private_message::PrivateMessage,
--  user::User_,
++  person::Person,
  };
- use lemmy_structs::blocking;
  use lemmy_utils::{location_info, LemmyError};
  use lemmy_websocket::LemmyContext;
  use log::debug;
@@@ -63,10 -63,10 +63,10 @@@ use std::fmt::Debug
  use strum_macros::EnumString;
  use url::Url;
  
--/// Allowed activities for user inbox.
++/// Allowed activities for person inbox.
  #[derive(Clone, Debug, Eq, Ord, PartialEq, PartialOrd, Deserialize, Serialize)]
  #[serde(rename_all = "PascalCase")]
--pub enum UserValidTypes {
++pub enum PersonValidTypes {
    Accept,   // community accepted our follow request
    Create,   // create private message
    Update,   // edit private message
@@@ -76,12 -76,12 +76,12 @@@
    Announce, // post, comment or vote in community
  }
  
--pub type UserAcceptedActivities = ActorAndObject<UserValidTypes>;
++pub type PersonAcceptedActivities = ActorAndObject<PersonValidTypes>;
  
--/// Handler for all incoming activities to user inboxes.
--pub async fn user_inbox(
++/// Handler for all incoming activities to person inboxes.
++pub async fn person_inbox(
    request: HttpRequest,
--  input: web::Json<UserAcceptedActivities>,
++  input: web::Json<PersonAcceptedActivities>,
    path: web::Path<String>,
    context: web::Data<LemmyContext>,
  ) -> Result<HttpResponse, LemmyError> {
@@@ -98,29 -98,29 +98,29 @@@
  
    // Check if the activity is actually meant for us
    let username = path.into_inner();
--  let user = blocking(&context.pool(), move |conn| {
--    User_::read_from_name(&conn, &username)
++  let person = blocking(&context.pool(), move |conn| {
++    Person::find_by_name(&conn, &username)
    })
    .await??;
    let to_and_cc = get_activity_to_and_cc(&activity);
    // TODO: we should also accept activities that are sent to community followers
--  if !to_and_cc.contains(&&user.actor_id()) {
--    return Err(anyhow!("Activity delivered to wrong user").into());
++  if !to_and_cc.contains(&&person.actor_id()) {
++    return Err(anyhow!("Activity delivered to wrong person").into());
    }
  
    assert_activity_not_local(&activity)?;
    insert_activity(&activity_id, activity.clone(), false, true, context.pool()).await?;
  
    debug!(
--    "User {} received activity {:?} from {}",
--    user.name,
++    "Person {} received activity {:?} from {}",
++    person.name,
      &activity.id_unchecked(),
      &actor.actor_id()
    );
  
--  user_receive_message(
++  person_receive_message(
      activity.clone(),
--    Some(user.clone()),
++    Some(person.clone()),
      actor.as_ref(),
      &context,
      request_counter,
@@@ -129,36 -129,43 +129,43 @@@
  }
  
  /// Receives Accept/Follow, Announce, private messages and community (undo) remove, (undo) delete
--pub(crate) async fn user_receive_message(
--  activity: UserAcceptedActivities,
--  to_user: Option<User_>,
++pub(crate) async fn person_receive_message(
++  activity: PersonAcceptedActivities,
++  to_person: Option<Person>,
    actor: &dyn ActorType,
    context: &LemmyContext,
    request_counter: &mut i32,
  ) -> Result<HttpResponse, LemmyError> {
--  is_for_user_inbox(context, &activity).await?;
++  is_for_person_inbox(context, &activity).await?;
  
    let any_base = activity.clone().into_any_base()?;
    let kind = activity.kind().context(location_info!())?;
    let actor_url = actor.actor_id();
    match kind {
--    UserValidTypes::Accept => {
-       receive_accept(&context, any_base, actor, to_user.unwrap(), request_counter).await?;
++    PersonValidTypes::Accept => {
+       receive_accept(
+         &context,
+         any_base,
+         actor,
 -        to_user.expect("user provided"),
++        to_person.expect("person provided"),
+         request_counter,
+       )
+       .await?;
      }
--    UserValidTypes::Announce => {
++    PersonValidTypes::Announce => {
        receive_announce(&context, any_base, actor, request_counter).await?
      }
--    UserValidTypes::Create => {
++    PersonValidTypes::Create => {
        receive_create(&context, any_base, actor_url, request_counter).await?
      }
--    UserValidTypes::Update => {
++    PersonValidTypes::Update => {
        receive_update(&context, any_base, actor_url, request_counter).await?
      }
--    UserValidTypes::Delete => {
++    PersonValidTypes::Delete => {
        receive_delete(context, any_base, &actor_url, request_counter).await?
      }
--    UserValidTypes::Undo => receive_undo(context, any_base, &actor_url, request_counter).await?,
--    UserValidTypes::Remove => receive_remove_community(&context, any_base, &actor_url).await?,
++    PersonValidTypes::Undo => receive_undo(context, any_base, &actor_url, request_counter).await?,
++    PersonValidTypes::Remove => receive_remove_community(&context, any_base, &actor_url).await?,
    };
  
    // TODO: would be logical to move websocket notification code here
@@@ -166,16 -173,16 +173,16 @@@
    Ok(HttpResponse::Ok().finish())
  }
  
--/// Returns true if the activity is addressed directly to one or more local users, or if it is
--/// addressed to the followers collection of a remote community, and at least one local user follows
++/// Returns true if the activity is addressed directly to one or more local persons, or if it is
++/// addressed to the followers collection of a remote community, and at least one local person follows
  /// it.
--async fn is_for_user_inbox(
++async fn is_for_person_inbox(
    context: &LemmyContext,
--  activity: &UserAcceptedActivities,
++  activity: &PersonAcceptedActivities,
  ) -> Result<(), LemmyError> {
    let to_and_cc = get_activity_to_and_cc(activity);
--  // Check if it is addressed directly to any local user
--  if is_addressed_to_local_user(&to_and_cc, context.pool()).await? {
++  // Check if it is addressed directly to any local person
++  if is_addressed_to_local_person(&to_and_cc, context.pool()).await? {
      return Ok(());
    }
  
@@@ -198,7 -205,7 +205,7 @@@
      }
    }
  
--  Err(anyhow!("Not addressed for any local user").into())
++  Err(anyhow!("Not addressed for any local person").into())
  }
  
  /// Handle accepted follows.
@@@ -206,7 -213,7 +213,7 @@@ async fn receive_accept
    context: &LemmyContext,
    activity: AnyBase,
    actor: &dyn ActorType,
--  user: User_,
++  person: Person,
    request_counter: &mut i32,
  ) -> Result<(), LemmyError> {
    let accept = Accept::from_any_base(activity)?.context(location_info!())?;
@@@ -214,7 -221,7 +221,7 @@@
  
    let object = accept.object().to_owned().one().context(location_info!())?;
    let follow = Follow::from_any_base(object)?.context(location_info!())?;
--  verify_activity_domains_valid(&follow, &user.actor_id(), false)?;
++  verify_activity_domains_valid(&follow, &person.actor_id(), false)?;
  
    let community_uri = accept
      .actor()?
@@@ -226,10 -233,10 +233,10 @@@
      get_or_fetch_and_upsert_community(&community_uri, context, request_counter).await?;
  
    let community_id = community.id;
--  let user_id = user.id;
++  let person_id = person.id;
    // This will throw an error if no follow was requested
    blocking(&context.pool(), move |conn| {
--    CommunityFollower::follow_accepted(conn, community_id, user_id)
++    CommunityFollower::follow_accepted(conn, community_id, person_id)
    })
    .await??;
  
diff --cc crates/apub/src/inbox/receive_for_community.rs
index e3704d97,a3ffbf11..3c5c2303
--- a/crates/apub/src/inbox/receive_for_community.rs
+++ b/crates/apub/src/inbox/receive_for_community.rs
@@@ -58,7 -58,7 +58,7 @@@ enum PageOrNote 
  }
  
  /// This file is for post/comment activities received by the community, and for post/comment
--///       activities announced by the community and received by the user.
++///       activities announced by the community and received by the person.
  
  /// A post or comment being created
  pub(in crate::inbox) async fn receive_create_for_community(
diff --cc crates/apub/src/inbox/shared_inbox.rs
index 93df6e72,8c197a85..dd82110a
--- a/crates/apub/src/inbox/shared_inbox.rs
+++ b/crates/apub/src/inbox/shared_inbox.rs
@@@ -7,8 -7,8 +7,8 @@@ use crate::
      inbox_verify_http_signature,
      is_activity_already_known,
      is_addressed_to_community_followers,
--    is_addressed_to_local_user,
--    user_inbox::{user_receive_message, UserAcceptedActivities},
++    is_addressed_to_local_person,
++    person_inbox::{person_receive_message, PersonAcceptedActivities},
    },
    insert_activity,
  };
@@@ -69,9 -69,9 +69,9 @@@ pub async fn shared_inbox
    let mut res: Option<HttpResponse> = None;
    let to_and_cc = get_activity_to_and_cc(&activity);
    // Handle community first, so in case the sender is banned by the community, it will error out.
--  // If we handled the user receive first, the activity would be inserted to the database before the
++  // If we handled the person receive first, the activity would be inserted to the database before the
    // community could check for bans.
--  // Note that an activity can be addressed to a community and to a user (or multiple users) at the
++  // Note that an activity can be addressed to a community and to a person (or multiple persons) at the
    // same time. In this case we still only handle it once, to avoid duplicate websocket
    // notifications.
    let community = extract_local_community_from_destinations(&to_and_cc, context.pool()).await?;
@@@ -88,13 -88,13 +88,13 @@@
        )
        .await?,
      );
--  } else if is_addressed_to_local_user(&to_and_cc, context.pool()).await? {
--    let user_activity = UserAcceptedActivities::from_any_base(activity_any_base.clone())?
++  } else if is_addressed_to_local_person(&to_and_cc, context.pool()).await? {
++    let person_activity = PersonAcceptedActivities::from_any_base(activity_any_base.clone())?
        .context(location_info!())?;
--    // `to_user` is only used for follow activities (which we dont receive here), so no need to pass
++    // `to_person` is only used for follow activities (which we dont receive here), so no need to pass
      // it in
--    user_receive_message(
--      user_activity,
++    person_receive_message(
++      person_activity,
        None,
        actor.as_ref(),
        &context,
@@@ -105,11 -105,11 +105,11 @@@
      .await?
      .is_some()
    {
--    let user_activity = UserAcceptedActivities::from_any_base(activity_any_base.clone())?
++    let person_activity = PersonAcceptedActivities::from_any_base(activity_any_base.clone())?
        .context(location_info!())?;
      res = Some(
--      user_receive_message(
--        user_activity,
++      person_receive_message(
++        person_activity,
          None,
          actor.as_ref(),
          &context,
diff --cc crates/apub/src/lib.rs
index 388d57e6,850ef503..2def37d9
--- a/crates/apub/src/lib.rs
+++ b/crates/apub/src/lib.rs
@@@ -24,17 -24,20 +24,20 @@@ use activitystreams::
  use activitystreams_ext::{Ext1, Ext2};
  use anyhow::{anyhow, Context};
  use diesel::NotFound;
+ use lemmy_api_structs::blocking;
  use lemmy_db_queries::{source::activity::Activity_, ApubObject, DbPool};
- use lemmy_db_schema::source::{
-   activity::Activity,
-   comment::Comment,
-   community::Community,
-   post::Post,
-   private_message::PrivateMessage,
-   user::User_,
+ use lemmy_db_schema::{
+   source::{
+     activity::Activity,
+     comment::Comment,
+     community::Community,
+     post::Post,
+     private_message::PrivateMessage,
 -    user::User_,
++    person::Person as DbPerson,
+   },
+   DbUrl,
  };
- use lemmy_structs::blocking;
- use lemmy_utils::{location_info, settings::Settings, LemmyError};
+ use lemmy_utils::{location_info, settings::structs::Settings, LemmyError};
  use lemmy_websocket::LemmyContext;
  use serde::Serialize;
  use std::net::IpAddr;
@@@ -42,7 -45,7 +45,7 @@@ use url::{ParseError, Url}
  
  /// Activitystreams type for community
  type GroupExt = Ext2<ApActor<ApObject<Group>>, GroupExtension, PublicKeyExtension>;
--/// Activitystreams type for user
++/// Activitystreams type for person
  type PersonExt = Ext1<ApActor<ApObject<Person>>, PublicKeyExtension>;
  /// Activitystreams type for post
  type PageExt = Ext1<ApObject<Page>, PageExtension>;
@@@ -117,27 -121,27 +121,27 @@@ fn check_is_apub_id_valid(apub_id: &Url
  /// and actors in Lemmy.
  #[async_trait::async_trait(?Send)]
  pub trait ApubObjectType {
--  async fn send_create(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError>;
--  async fn send_update(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError>;
--  async fn send_delete(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError>;
++  async fn send_create(&self, creator: &DbPerson, context: &LemmyContext) -> Result<(), LemmyError>;
++  async fn send_update(&self, creator: &DbPerson, context: &LemmyContext) -> Result<(), LemmyError>;
++  async fn send_delete(&self, creator: &DbPerson, context: &LemmyContext) -> Result<(), LemmyError>;
    async fn send_undo_delete(
      &self,
--    creator: &User_,
++    creator: &DbPerson,
      context: &LemmyContext,
    ) -> Result<(), LemmyError>;
--  async fn send_remove(&self, mod_: &User_, context: &LemmyContext) -> Result<(), LemmyError>;
--  async fn send_undo_remove(&self, mod_: &User_, context: &LemmyContext) -> Result<(), LemmyError>;
++  async fn send_remove(&self, mod_: &DbPerson, context: &LemmyContext) -> Result<(), LemmyError>;
++  async fn send_undo_remove(&self, mod_: &DbPerson, context: &LemmyContext) -> Result<(), LemmyError>;
  }
  
  #[async_trait::async_trait(?Send)]
  pub trait ApubLikeableType {
--  async fn send_like(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError>;
--  async fn send_dislike(&self, creator: &User_, context: &LemmyContext) -> Result<(), LemmyError>;
--  async fn send_undo_like(&self, creator: &User_, context: &LemmyContext)
++  async fn send_like(&self, creator: &DbPerson, context: &LemmyContext) -> Result<(), LemmyError>;
++  async fn send_dislike(&self, creator: &DbPerson, context: &LemmyContext) -> Result<(), LemmyError>;
++  async fn send_undo_like(&self, creator: &DbPerson, context: &LemmyContext)
      -> Result<(), LemmyError>;
  }
  
--/// Common methods provided by ActivityPub actors (community and user). Not all methods are
++/// Common methods provided by ActivityPub actors (community and person). Not all methods are
  /// implemented by all actors.
  #[async_trait::async_trait(?Send)]
  pub trait ActorType {
@@@ -205,7 -209,7 +209,7 @@@
  
  pub enum EndpointType {
    Community,
--  User,
++  Person,
    Post,
    Comment,
    PrivateMessage,
@@@ -215,10 -219,10 +219,10 @@@
  pub fn generate_apub_endpoint(
    endpoint_type: EndpointType,
    name: &str,
- ) -> Result<lemmy_db_schema::Url, ParseError> {
+ ) -> Result<DbUrl, ParseError> {
    let point = match endpoint_type {
      EndpointType::Community => "c",
--    EndpointType::User => "u",
++    EndpointType::Person => "u",
      EndpointType::Post => "post",
      EndpointType::Comment => "comment",
      EndpointType::PrivateMessage => "private_message",
@@@ -321,7 -319,7 +319,7 @@@ pub(crate) enum Object 
    Comment(Comment),
    Post(Post),
    Community(Community),
--  User(User_),
++  Person(DbPerson),
    PrivateMessage(PrivateMessage),
  }
  
@@@ -338,12 -336,12 +336,12 @@@ pub(crate) async fn find_object_by_id
    }
  
    let ap_id = apub_id.clone();
--  let user = blocking(context.pool(), move |conn| {
--    User_::read_from_apub_id(conn, &ap_id.into())
++  let person = blocking(context.pool(), move |conn| {
++    DbPerson::read_from_apub_id(conn, &ap_id.into())
    })
    .await?;
--  if let Ok(u) = user {
--    return Ok(Object::User(u));
++  if let Ok(u) = person {
++    return Ok(Object::Person(u));
    }
  
    let ap_id = apub_id.clone();
diff --cc crates/apub/src/objects/comment.rs
index 8452f378,6c218190..ae5537e3
--- a/crates/apub/src/objects/comment.rs
+++ b/crates/apub/src/objects/comment.rs
@@@ -6,7 -6,7 +6,7 @@@ use crate::
      check_object_for_community_or_site_ban,
      create_tombstone,
      get_object_from_apub,
--    get_or_fetch_and_upsert_user,
++    get_or_fetch_and_upsert_person,
      get_source_markdown_value,
      set_content_and_source,
      FromApub,
@@@ -25,9 -26,8 +26,8 @@@ use lemmy_db_queries::{Crud, DbPool}
  use lemmy_db_schema::source::{
    comment::{Comment, CommentForm},
    post::Post,
--  user::User_,
++  person::Person,
  };
- use lemmy_structs::blocking;
  use lemmy_utils::{
    location_info,
    utils::{convert_datetime, remove_slurs},
@@@ -44,7 -44,7 +44,7 @@@ impl ToApub for Comment 
      let mut comment = ApObject::new(Note::new());
  
      let creator_id = self.creator_id;
--    let creator = blocking(pool, move |conn| User_::read(conn, creator_id)).await??;
++    let creator = blocking(pool, move |conn| Person::read(conn, creator_id)).await??;
  
      let post_id = self.post_id;
      let post = blocking(pool, move |conn| Post::read(conn, post_id)).await??;
@@@ -135,7 -135,7 +135,7 @@@ impl FromApubToForm<NoteExt> for Commen
        .as_single_xsd_any_uri()
        .context(location_info!())?;
  
--    let creator = get_or_fetch_and_upsert_user(creator_actor_id, context, request_counter).await?;
++    let creator = get_or_fetch_and_upsert_person(creator_actor_id, context, request_counter).await?;
  
      let mut in_reply_tos = note
        .in_reply_to()
diff --cc crates/apub/src/objects/community.rs
index 827eae29,200497b7..93693813
--- a/crates/apub/src/objects/community.rs
+++ b/crates/apub/src/objects/community.rs
@@@ -1,6 -1,6 +1,6 @@@
  use crate::{
    extensions::{context::lemmy_context, group_extensions::GroupExtension},
--  fetcher::user::get_or_fetch_and_upsert_user,
++  fetcher::person::get_or_fetch_and_upsert_person,
    objects::{
      check_object_domain,
      create_tombstone,
@@@ -143,7 -143,7 +143,7 @@@ impl FromApubToForm<GroupExt> for Commu
        .as_xsd_any_uri()
        .context(location_info!())?;
  
--    let creator = get_or_fetch_and_upsert_user(creator_uri, context, request_counter).await?;
++    let creator = get_or_fetch_and_upsert_person(creator_uri, context, request_counter).await?;
      let name = group
        .inner
        .preferred_username()
diff --cc crates/apub/src/objects/mod.rs
index 79b9ad40,6b59e577..235d5223
--- a/crates/apub/src/objects/mod.rs
+++ b/crates/apub/src/objects/mod.rs
@@@ -1,6 -1,6 +1,6 @@@
  use crate::{
    check_is_apub_id_valid,
--  fetcher::{community::get_or_fetch_and_upsert_community, user::get_or_fetch_and_upsert_user},
++  fetcher::{community::get_or_fetch_and_upsert_community, person::get_or_fetch_and_upsert_person},
    inbox::community_inbox::check_community_or_site_ban,
  };
  use activitystreams::{
@@@ -23,7 -28,7 +28,7 @@@ pub(crate) mod comment
  pub(crate) mod community;
  pub(crate) mod post;
  pub(crate) mod private_message;
--pub(crate) mod user;
++pub(crate) mod person;
  
  /// Trait for converting an object or actor into the respective ActivityPub type.
  #[async_trait::async_trait(?Send)]
@@@ -214,13 -212,13 +212,13 @@@ pub(in crate::objects) async fn check_o
  where
    T: ObjectExt<Kind>,
  {
--  let user_id = object
++  let person_id = object
      .attributed_to()
      .context(location_info!())?
      .as_single_xsd_any_uri()
      .context(location_info!())?;
--  let user = get_or_fetch_and_upsert_user(user_id, context, request_counter).await?;
--  check_community_or_site_ban(&user, community_id, context.pool()).await
++  let person = get_or_fetch_and_upsert_person(person_id, context, request_counter).await?;
++  check_community_or_site_ban(&person, community_id, context.pool()).await
  }
  
  pub(in crate::objects) async fn get_to_community<T, Kind>(
diff --cc crates/apub/src/objects/person.rs
index 8a911de2,83f75e49..a632fcdb
--- a/crates/apub/src/objects/person.rs
+++ b/crates/apub/src/objects/person.rs
@@@ -21,12 -22,11 +22,11 @@@ use lemmy_api_structs::blocking
  use lemmy_db_queries::{ApubObject, DbPool};
  use lemmy_db_schema::{
    naive_now,
--  source::user::{UserForm, User_},
++  source::person::{PersonForm, Person as DbPerson},
  };
- use lemmy_structs::blocking;
  use lemmy_utils::{
    location_info,
-   settings::Settings,
+   settings::structs::Settings,
    utils::{check_slurs, check_slurs_opt, convert_datetime},
    LemmyError,
  };
@@@ -34,7 -34,7 +34,7 @@@ use lemmy_websocket::LemmyContext
  use url::Url;
  
  #[async_trait::async_trait(?Send)]
--impl ToApub for User_ {
++impl ToApub for DbPerson {
    type ApubType = PersonExt;
  
    async fn to_apub(&self, _pool: &DbPool) -> Result<PersonExt, LemmyError> {
@@@ -88,7 -85,7 +85,7 @@@
  }
  
  #[async_trait::async_trait(?Send)]
--impl FromApub for User_ {
++impl FromApub for DbPerson {
    type ApubType = PersonExt;
  
    async fn from_apub(
@@@ -96,26 -93,26 +93,26 @@@
      context: &LemmyContext,
      expected_domain: Url,
      request_counter: &mut i32,
--  ) -> Result<User_, LemmyError> {
--    let user_id = person.id_unchecked().context(location_info!())?.to_owned();
--    let domain = user_id.domain().context(location_info!())?;
-     if domain == Settings::get().hostname {
-       let user = blocking(context.pool(), move |conn| {
-         User_::read_from_apub_id(conn, &user_id.into())
++  ) -> Result<DbPerson, LemmyError> {
++    let person_id = person.id_unchecked().context(location_info!())?.to_owned();
++    let domain = person_id.domain().context(location_info!())?;
+     if domain == Settings::get().hostname() {
 -      let user = blocking(context.pool(), move |conn| {
 -        User_::read_from_apub_id(conn, &user_id.into())
++      let person = blocking(context.pool(), move |conn| {
++        DbPerson::read_from_apub_id(conn, &person_id.into())
        })
        .await??;
--      Ok(user)
++      Ok(person)
      } else {
--      let user_form =
--        UserForm::from_apub(person, context, expected_domain, request_counter).await?;
--      let user = blocking(context.pool(), move |conn| User_::upsert(conn, &user_form)).await??;
--      Ok(user)
++      let person_form =
++        PersonForm::from_apub(person, context, expected_domain, request_counter).await?;
++      let person = blocking(context.pool(), move |conn| DbPerson::upsert(conn, &person_form)).await??;
++      Ok(person)
      }
    }
  }
  
  #[async_trait::async_trait(?Send)]
--impl FromApubToForm<PersonExt> for UserForm {
++impl FromApubToForm<PersonExt> for PersonForm {
    async fn from_apub(
      person: &PersonExt,
      _context: &LemmyContext,
@@@ -170,30 -167,30 +167,20 @@@
      check_slurs_opt(&preferred_username)?;
      check_slurs_opt(&bio)?;
  
--    Ok(UserForm {
++    Ok(PersonForm {
        name,
        preferred_username: Some(preferred_username),
--      password_encrypted: "".to_string(),
--      admin: false,
        banned: None,
--      email: None,
-       avatar,
-       banner,
++      deleted: None,
+       avatar: avatar.map(|o| o.map(|i| i.into())),
+       banner: banner.map(|o| o.map(|i| i.into())),
        published: person.inner.published().map(|u| u.to_owned().naive_local()),
        updated: person.updated().map(|u| u.to_owned().naive_local()),
--      show_nsfw: false,
--      theme: "".to_string(),
--      default_sort_type: 0,
--      default_listing_type: 0,
--      lang: "".to_string(),
--      show_avatars: false,
--      send_notifications_to_email: false,
--      matrix_user_id: None,
        actor_id: Some(check_object_domain(person, expected_domain)?),
        bio: Some(bio),
--      local: false,
++      local: Some(false),
        private_key: None,
--      public_key: Some(person.ext_one.public_key.to_owned().public_key_pem),
++      public_key: Some(Some(person.ext_one.public_key.to_owned().public_key_pem)),
        last_refreshed_at: Some(naive_now()),
        inbox_url: Some(person.inner.inbox()?.to_owned().into()),
        shared_inbox_url: Some(shared_inbox),
diff --cc crates/apub/src/objects/post.rs
index 2cac67ea,b066e6f8..a189c2de
--- a/crates/apub/src/objects/post.rs
+++ b/crates/apub/src/objects/post.rs
@@@ -1,6 -1,6 +1,6 @@@
  use crate::{
    extensions::{context::lemmy_context, page_extension::PageExtension},
--  fetcher::user::get_or_fetch_and_upsert_user,
++  fetcher::person::get_or_fetch_and_upsert_person,
    objects::{
      check_object_domain,
      check_object_for_community_or_site_ban,
@@@ -22,13 -22,16 +22,16 @@@ use activitystreams::
  };
  use activitystreams_ext::Ext1;
  use anyhow::Context;
+ use lemmy_api_structs::blocking;
  use lemmy_db_queries::{Crud, DbPool};
- use lemmy_db_schema::source::{
-   community::Community,
-   post::{Post, PostForm},
-   user::User_,
+ use lemmy_db_schema::{
+   self,
+   source::{
+     community::Community,
+     post::{Post, PostForm},
 -    user::User_,
++    person::Person,
+   },
  };
- use lemmy_structs::blocking;
  use lemmy_utils::{
    location_info,
    request::fetch_iframely_and_pictrs_data,
@@@ -47,7 -50,7 +50,7 @@@ impl ToApub for Post 
      let mut page = ApObject::new(Page::new());
  
      let creator_id = self.creator_id;
--    let creator = blocking(pool, move |conn| User_::read(conn, creator_id)).await??;
++    let creator = blocking(pool, move |conn| Person::read(conn, creator_id)).await??;
  
      let community_id = self.community_id;
      let community = blocking(pool, move |conn| Community::read(conn, community_id)).await??;
@@@ -142,7 -142,7 +142,7 @@@ impl FromApubToForm<PageExt> for PostFo
        .as_single_xsd_any_uri()
        .context(location_info!())?;
  
--    let creator = get_or_fetch_and_upsert_user(creator_actor_id, context, request_counter).await?;
++    let creator = get_or_fetch_and_upsert_person(creator_actor_id, context, request_counter).await?;
  
      let community = get_to_community(page, context, request_counter).await?;
  
diff --cc crates/apub/src/objects/private_message.rs
index df91b03f,0bb753e2..09b49dd0
--- a/crates/apub/src/objects/private_message.rs
+++ b/crates/apub/src/objects/private_message.rs
@@@ -1,7 -1,7 +1,7 @@@
  use crate::{
    check_is_apub_id_valid,
    extensions::context::lemmy_context,
--  fetcher::user::get_or_fetch_and_upsert_user,
++  fetcher::person::get_or_fetch_and_upsert_person,
    objects::{
      check_object_domain,
      create_tombstone,
@@@ -22,9 -23,8 +23,8 @@@ use lemmy_api_structs::blocking
  use lemmy_db_queries::{Crud, DbPool};
  use lemmy_db_schema::source::{
    private_message::{PrivateMessage, PrivateMessageForm},
--  user::User_,
++  person::Person,
  };
- use lemmy_structs::blocking;
  use lemmy_utils::{location_info, utils::convert_datetime, LemmyError};
  use lemmy_websocket::LemmyContext;
  use url::Url;
@@@ -37,10 -37,10 +37,10 @@@ impl ToApub for PrivateMessage 
      let mut private_message = ApObject::new(Note::new());
  
      let creator_id = self.creator_id;
--    let creator = blocking(pool, move |conn| User_::read(conn, creator_id)).await??;
++    let creator = blocking(pool, move |conn| Person::read(conn, creator_id)).await??;
  
      let recipient_id = self.recipient_id;
--    let recipient = blocking(pool, move |conn| User_::read(conn, recipient_id)).await??;
++    let recipient = blocking(pool, move |conn| Person::read(conn, recipient_id)).await??;
  
      private_message
        .set_many_contexts(lemmy_context()?)
@@@ -97,7 -97,7 +97,7 @@@ impl FromApubToForm<NoteExt> for Privat
        .single_xsd_any_uri()
        .context(location_info!())?;
  
--    let creator = get_or_fetch_and_upsert_user(&creator_actor_id, context, request_counter).await?;
++    let creator = get_or_fetch_and_upsert_person(&creator_actor_id, context, request_counter).await?;
      let recipient_actor_id = note
        .to()
        .context(location_info!())?
@@@ -105,7 -105,7 +105,7 @@@
        .single_xsd_any_uri()
        .context(location_info!())?;
      let recipient =
--      get_or_fetch_and_upsert_user(&recipient_actor_id, context, request_counter).await?;
++      get_or_fetch_and_upsert_person(&recipient_actor_id, context, request_counter).await?;
      let ap_id = note.id_unchecked().context(location_info!())?.to_string();
      check_is_apub_id_valid(&Url::parse(&ap_id)?)?;
  
diff --cc crates/apub/src/routes.rs
index 3717a894,07dcc7f8..6cc88778
--- a/crates/apub/src/routes.rs
+++ b/crates/apub/src/routes.rs
@@@ -9,9 -9,9 +9,9 @@@ use crate::
      },
      get_activity,
      post::get_apub_post,
--    user::{get_apub_user_http, get_apub_user_inbox, get_apub_user_outbox},
++    person::{get_apub_person_http, get_apub_person_inbox, get_apub_person_outbox},
    },
--  inbox::{community_inbox::community_inbox, shared_inbox::shared_inbox, user_inbox::user_inbox},
++  inbox::{community_inbox::community_inbox, shared_inbox::shared_inbox, person_inbox::person_inbox},
    APUB_JSON_CONTENT_TYPE,
  };
  use actix_web::*;
@@@ -55,9 -53,9 +53,9 @@@ pub fn config(cfg: &mut web::ServiceCon
              "/c/{community_name}/inbox",
              web::get().to(get_apub_community_inbox),
            )
--          .route("/u/{user_name}", web::get().to(get_apub_user_http))
--          .route("/u/{user_name}/outbox", web::get().to(get_apub_user_outbox))
--          .route("/u/{user_name}/inbox", web::get().to(get_apub_user_inbox))
++          .route("/u/{user_name}", web::get().to(get_apub_person_http))
++          .route("/u/{user_name}/outbox", web::get().to(get_apub_person_outbox))
++          .route("/u/{user_name}/inbox", web::get().to(get_apub_person_inbox))
            .route("/post/{post_id}", web::get().to(get_apub_post))
            .route("/comment/{comment_id}", web::get().to(get_apub_comment))
            .route("/activities/{type_}/{id}", web::get().to(get_activity)),
@@@ -68,7 -66,7 +66,7 @@@
            .wrap(digest_verifier)
            .guard(header_guard_content_type)
            .route("/c/{community_name}/inbox", web::post().to(community_inbox))
--          .route("/u/{user_name}/inbox", web::post().to(user_inbox))
++          .route("/u/{user_name}/inbox", web::post().to(person_inbox))
            .route("/inbox", web::post().to(shared_inbox)),
        );
    }
diff --cc crates/db_queries/src/aggregates/comment_aggregates.rs
index 9f7d678e,0826ae0d..5d4fe5dc
--- a/crates/db_queries/src/aggregates/comment_aggregates.rs
+++ b/crates/db_queries/src/aggregates/comment_aggregates.rs
@@@ -28,17 -28,19 +28,17 @@@ mod tests 
      establish_unpooled_connection,
      Crud,
      Likeable,
--    ListingType,
--    SortType,
    };
    use lemmy_db_schema::source::{
      comment::{Comment, CommentForm, CommentLike, CommentLikeForm},
      community::{Community, CommunityForm},
      post::{Post, PostForm},
 -    user::{UserForm, User_},
 +    person::{PersonForm, Person},
    };
+   use serial_test::serial;
  
    #[test]
+   #[serial]
    fn test_crud() {
      let conn = establish_unpooled_connection();
  
diff --cc crates/db_queries/src/aggregates/community_aggregates.rs
index 159b323e,ee60da9b..23fbe8bd
--- a/crates/db_queries/src/aggregates/community_aggregates.rs
+++ b/crates/db_queries/src/aggregates/community_aggregates.rs
@@@ -32,32 -32,44 +32,32 @@@ mod tests 
      establish_unpooled_connection,
      Crud,
      Followable,
--    ListingType,
--    SortType,
    };
    use lemmy_db_schema::source::{
      comment::{Comment, CommentForm},
      community::{Community, CommunityFollower, CommunityFollowerForm, CommunityForm},
      post::{Post, PostForm},
 -    user::{UserForm, User_},
 +    person::{PersonForm, Person},
    };
+   use serial_test::serial;
  
    #[test]
+   #[serial]
    fn test_crud() {
      let conn = establish_unpooled_connection();
  
 -    let new_user = UserForm {
 +    let new_person = PersonForm {
        name: "thommy_community_agg".into(),
        preferred_username: None,
 -      password_encrypted: "nope".into(),
 -      email: None,
 -      matrix_user_id: None,
        avatar: None,
        banner: None,
 -      admin: false,
--      banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
 -      show_nsfw: false,
 -      theme: "browser".into(),
 -      default_sort_type: SortType::Hot as i16,
 -      default_listing_type: ListingType::Subscribed as i16,
 -      lang: "browser".into(),
 -      show_avatars: true,
 -      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
@@@ -65,20 -77,30 +65,20 @@@
        shared_inbox_url: None,
      };
  
 -    let inserted_user = User_::create(&conn, &new_user).unwrap();
 +    let inserted_person = Person::create(&conn, &new_person).unwrap();
  
 -    let another_user = UserForm {
 +    let another_person = PersonForm {
        name: "jerry_community_agg".into(),
        preferred_username: None,
 -      password_encrypted: "nope".into(),
 -      email: None,
 -      matrix_user_id: None,
        avatar: None,
        banner: None,
 -      admin: false,
--      banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
 -      show_nsfw: false,
 -      theme: "browser".into(),
 -      default_sort_type: SortType::Hot as i16,
 -      default_listing_type: ListingType::Subscribed as i16,
 -      lang: "browser".into(),
 -      show_avatars: true,
 -      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
diff --cc crates/db_queries/src/aggregates/person_aggregates.rs
index ccbba8db,bdd16932..cebf70b0
--- a/crates/db_queries/src/aggregates/person_aggregates.rs
+++ b/crates/db_queries/src/aggregates/person_aggregates.rs
@@@ -28,32 -28,44 +28,32 @@@ mod tests 
      establish_unpooled_connection,
      Crud,
      Likeable,
--    ListingType,
--    SortType,
    };
    use lemmy_db_schema::source::{
      comment::{Comment, CommentForm, CommentLike, CommentLikeForm},
      community::{Community, CommunityForm},
      post::{Post, PostForm, PostLike, PostLikeForm},
 -    user::{UserForm, User_},
 +    person::{PersonForm, Person},
    };
+   use serial_test::serial;
  
    #[test]
+   #[serial]
    fn test_crud() {
      let conn = establish_unpooled_connection();
  
 -    let new_user = UserForm {
 +    let new_person = PersonForm {
        name: "thommy_user_agg".into(),
        preferred_username: None,
 -      password_encrypted: "nope".into(),
 -      email: None,
 -      matrix_user_id: None,
        avatar: None,
        banner: None,
 -      admin: false,
--      banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
 -      show_nsfw: false,
 -      theme: "browser".into(),
 -      default_sort_type: SortType::Hot as i16,
 -      default_listing_type: ListingType::Subscribed as i16,
 -      lang: "browser".into(),
 -      show_avatars: true,
 -      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
@@@ -61,20 -73,30 +61,20 @@@
        shared_inbox_url: None,
      };
  
 -    let inserted_user = User_::create(&conn, &new_user).unwrap();
 +    let inserted_person = Person::create(&conn, &new_person).unwrap();
  
 -    let another_user = UserForm {
 +    let another_person = PersonForm {
        name: "jerry_user_agg".into(),
        preferred_username: None,
 -      password_encrypted: "nope".into(),
 -      email: None,
 -      matrix_user_id: None,
        avatar: None,
        banner: None,
 -      admin: false,
--      banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
 -      show_nsfw: false,
 -      theme: "browser".into(),
 -      default_sort_type: SortType::Hot as i16,
 -      default_listing_type: ListingType::Subscribed as i16,
 -      lang: "browser".into(),
 -      show_avatars: true,
 -      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
diff --cc crates/db_queries/src/aggregates/post_aggregates.rs
index f272e4f8,404257a7..be4e8dc7
--- a/crates/db_queries/src/aggregates/post_aggregates.rs
+++ b/crates/db_queries/src/aggregates/post_aggregates.rs
@@@ -31,30 -31,45 +31,33 @@@ mod tests 
      aggregates::post_aggregates::PostAggregates,
      establish_unpooled_connection,
      Crud,
+     Likeable,
 -    ListingType,
 -    SortType,
    };
    use lemmy_db_schema::source::{
      comment::{Comment, CommentForm},
      community::{Community, CommunityForm},
      post::{Post, PostForm, PostLike, PostLikeForm},
 -    user::{UserForm, User_},
 +    person::{PersonForm, Person},
    };
+   use serial_test::serial;
  
    #[test]
+   #[serial]
    fn test_crud() {
      let conn = establish_unpooled_connection();
  
 -    let new_user = UserForm {
 +    let new_person = PersonForm {
        name: "thommy_community_agg".into(),
        preferred_username: None,
 -      password_encrypted: "nope".into(),
 -      email: None,
 -      matrix_user_id: None,
        avatar: None,
        banner: None,
 -      admin: false,
--      banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
 -      show_nsfw: false,
 -      theme: "browser".into(),
 -      default_sort_type: SortType::Hot as i16,
 -      default_listing_type: ListingType::Subscribed as i16,
 -      lang: "browser".into(),
 -      show_avatars: true,
 -      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
@@@ -62,20 -77,30 +65,20 @@@
        shared_inbox_url: None,
      };
  
 -    let inserted_user = User_::create(&conn, &new_user).unwrap();
 +    let inserted_person = Person::create(&conn, &new_person).unwrap();
  
 -    let another_user = UserForm {
 +    let another_person = PersonForm {
        name: "jerry_community_agg".into(),
        preferred_username: None,
 -      password_encrypted: "nope".into(),
 -      email: None,
 -      matrix_user_id: None,
        avatar: None,
        banner: None,
 -      admin: false,
--      banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
 -      show_nsfw: false,
 -      theme: "browser".into(),
 -      default_sort_type: SortType::Hot as i16,
 -      default_listing_type: ListingType::Subscribed as i16,
 -      lang: "browser".into(),
 -      show_avatars: true,
 -      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
diff --cc crates/db_queries/src/aggregates/site_aggregates.rs
index 2f33b507,81afed5c..fe25c969
--- a/crates/db_queries/src/aggregates/site_aggregates.rs
+++ b/crates/db_queries/src/aggregates/site_aggregates.rs
@@@ -28,31 -28,46 +28,34 @@@ mod tests 
    use crate::{
      aggregates::site_aggregates::SiteAggregates,
      establish_unpooled_connection,
+     Crud,
 -    ListingType,
 -    SortType,
    };
    use lemmy_db_schema::source::{
      comment::{Comment, CommentForm},
      community::{Community, CommunityForm},
      post::{Post, PostForm},
      site::{Site, SiteForm},
 -    user::{UserForm, User_},
 +    person::{PersonForm, Person},
    };
+   use serial_test::serial;
  
    #[test]
+   #[serial]
    fn test_crud() {
      let conn = establish_unpooled_connection();
  
 -    let new_user = UserForm {
 +    let new_person = PersonForm {
        name: "thommy_site_agg".into(),
        preferred_username: None,
 -      password_encrypted: "nope".into(),
 -      email: None,
 -      matrix_user_id: None,
        avatar: None,
        banner: None,
 -      admin: false,
--      banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
 -      show_nsfw: false,
 -      theme: "browser".into(),
 -      default_sort_type: SortType::Hot as i16,
 -      default_listing_type: ListingType::Subscribed as i16,
 -      lang: "browser".into(),
 -      show_avatars: true,
 -      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
diff --cc crates/db_queries/src/source/activity.rs
index 7c00681e,56b904e8..06b9bd87
--- a/crates/db_queries/src/source/activity.rs
+++ b/crates/db_queries/src/source/activity.rs
@@@ -127,11 -128,17 +128,14 @@@ mod tests 
    };
    use lemmy_db_schema::source::{
      activity::{Activity, ActivityForm},
 -    user::{UserForm, User_},
 +    person::{PersonForm, Person},
    };
    use serde_json::Value;
+   use serial_test::serial;
+   use url::Url;
  
    #[test]
+   #[serial]
    fn test_crud() {
      let conn = establish_unpooled_connection();
  
@@@ -154,10 -171,13 +158,13 @@@
        shared_inbox_url: None,
      };
  
 -    let inserted_creator = User_::create(&conn, &creator_form).unwrap();
 +    let inserted_creator = Person::create(&conn, &creator_form).unwrap();
  
-     let ap_id =
-       "https://enterprise.lemmy.ml/activities/delete/f1b5d57c-80f8-4e03-a615-688d552e946c";
+     let ap_id: DbUrl = Url::parse(
+       "https://enterprise.lemmy.ml/activities/delete/f1b5d57c-80f8-4e03-a615-688d552e946c",
+     )
+     .unwrap()
+     .into();
      let test_json: Value = serde_json::from_str(
        r#"{
      "@context": "https://www.w3.org/ns/activitystreams",
@@@ -193,8 -213,8 +200,8 @@@
      };
  
      let read_activity = Activity::read(&conn, inserted_activity.id).unwrap();
-     let read_activity_by_apub_id = Activity::read_from_apub_id(&conn, ap_id).unwrap();
+     let read_activity_by_apub_id = Activity::read_from_apub_id(&conn, &ap_id).unwrap();
 -    User_::delete(&conn, inserted_creator.id).unwrap();
 +    Person::delete(&conn, inserted_creator.id).unwrap();
      Activity::delete(&conn, inserted_activity.id).unwrap();
  
      assert_eq!(expected_activity, read_activity);
diff --cc crates/db_queries/src/source/comment.rs
index 583a59e6,8dc9050c..e4feef80
--- a/crates/db_queries/src/source/comment.rs
+++ b/crates/db_queries/src/source/comment.rs
@@@ -187,7 -187,7 +187,7 @@@ impl Saveable<CommentSavedForm> for Com
      use lemmy_db_schema::schema::comment_saved::dsl::*;
      insert_into(comment_saved)
        .values(comment_saved_form)
--      .on_conflict((comment_id, user_id))
++      .on_conflict((comment_id, person_id))
        .do_update()
        .set(comment_saved_form)
        .get_result::<Self>(conn)
@@@ -197,7 -197,7 +197,7 @@@
      diesel::delete(
        comment_saved
          .filter(comment_id.eq(comment_saved_form.comment_id))
-         .filter(user_id.eq(comment_saved_form.person_id)),
 -        .filter(user_id.eq(comment_saved_form.user_id)),
++        .filter(person_id.eq(comment_saved_form.person_id)),
      )
      .execute(conn)
    }
@@@ -205,30 -205,42 +205,32 @@@
  
  #[cfg(test)]
  mod tests {
--  use crate::{establish_unpooled_connection, Crud, Likeable, ListingType, Saveable, SortType};
++  use crate::{establish_unpooled_connection, Crud, Likeable, Saveable};
    use lemmy_db_schema::source::{
      comment::*,
      community::{Community, CommunityForm},
      post::*,
 -    user::{UserForm, User_},
 +    person::{PersonForm, Person},
    };
+   use serial_test::serial;
  
    #[test]
+   #[serial]
    fn test_crud() {
      let conn = establish_unpooled_connection();
  
 -    let new_user = UserForm {
 +    let new_person = PersonForm {
        name: "terry".into(),
        preferred_username: None,
 -      password_encrypted: "nope".into(),
 -      email: None,
 -      matrix_user_id: None,
        avatar: None,
        banner: None,
 -      admin: false,
--      banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
 -      show_nsfw: false,
 -      theme: "browser".into(),
 -      default_sort_type: SortType::Hot as i16,
 -      default_listing_type: ListingType::Subscribed as i16,
 -      lang: "browser".into(),
 -      show_avatars: true,
 -      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
@@@ -236,13 -248,13 +238,13 @@@
        shared_inbox_url: None,
      };
  
-     let inserted_persod = Person::create(&conn, &new_person).unwrap();
 -    let inserted_user = User_::create(&conn, &new_user).unwrap();
++    let inserted_person = Person::create(&conn, &new_person).unwrap();
  
      let new_community = CommunityForm {
        name: "test community".to_string(),
        title: "nada".to_owned(),
        description: None,
-       creator_id: inserted_persod.id,
 -      creator_id: inserted_user.id,
++      creator_id: inserted_person.id,
        removed: None,
        deleted: None,
        updated: None,
@@@ -264,7 -276,7 +266,7 @@@
  
      let new_post = PostForm {
        name: "A test post".into(),
-       creator_id: inserted_persod.id,
 -      creator_id: inserted_user.id,
++      creator_id: inserted_person.id,
        url: None,
        body: None,
        community_id: inserted_community.id,
@@@ -287,7 -299,7 +289,7 @@@
  
      let comment_form = CommentForm {
        content: "A test comment".into(),
-       creator_id: inserted_persod.id,
 -      creator_id: inserted_user.id,
++      creator_id: inserted_person.id,
        post_id: inserted_post.id,
        removed: None,
        deleted: None,
@@@ -304,7 -316,7 +306,7 @@@
      let expected_comment = Comment {
        id: inserted_comment.id,
        content: "A test comment".into(),
-       creator_id: inserted_persod.id,
 -      creator_id: inserted_user.id,
++      creator_id: inserted_person.id,
        post_id: inserted_post.id,
        removed: false,
        deleted: false,
@@@ -318,7 -330,7 +320,7 @@@
  
      let child_comment_form = CommentForm {
        content: "A child comment".into(),
-       creator_id: inserted_persod.id,
 -      creator_id: inserted_user.id,
++      creator_id: inserted_person.id,
        post_id: inserted_post.id,
        parent_id: Some(inserted_comment.id),
        removed: None,
@@@ -336,7 -348,7 +338,7 @@@
      let comment_like_form = CommentLikeForm {
        comment_id: inserted_comment.id,
        post_id: inserted_post.id,
-       person_id: inserted_persod.id,
 -      user_id: inserted_user.id,
++      person_id: inserted_person.id,
        score: 1,
      };
  
@@@ -346,7 -358,7 +348,7 @@@
        id: inserted_comment_like.id,
        comment_id: inserted_comment.id,
        post_id: inserted_post.id,
-       person_id: inserted_persod.id,
 -      user_id: inserted_user.id,
++      person_id: inserted_person.id,
        published: inserted_comment_like.published,
        score: 1,
      };
@@@ -354,7 -366,7 +356,7 @@@
      // Comment Saved
      let comment_saved_form = CommentSavedForm {
        comment_id: inserted_comment.id,
-       person_id: inserted_persod.id,
 -      user_id: inserted_user.id,
++      person_id: inserted_person.id,
      };
  
      let inserted_comment_saved = CommentSaved::save(&conn, &comment_saved_form).unwrap();
@@@ -362,19 -374,19 +364,19 @@@
      let expected_comment_saved = CommentSaved {
        id: inserted_comment_saved.id,
        comment_id: inserted_comment.id,
-       person_id: inserted_persod.id,
 -      user_id: inserted_user.id,
++      person_id: inserted_person.id,
        published: inserted_comment_saved.published,
      };
  
      let read_comment = Comment::read(&conn, inserted_comment.id).unwrap();
      let updated_comment = Comment::update(&conn, inserted_comment.id, &comment_form).unwrap();
-     let like_removed = CommentLike::remove(&conn, inserted_persod.id, inserted_comment.id).unwrap();
 -    let like_removed = CommentLike::remove(&conn, inserted_user.id, inserted_comment.id).unwrap();
++    let like_removed = CommentLike::remove(&conn, inserted_person.id, inserted_comment.id).unwrap();
      let saved_removed = CommentSaved::unsave(&conn, &comment_saved_form).unwrap();
      let num_deleted = Comment::delete(&conn, inserted_comment.id).unwrap();
      Comment::delete(&conn, inserted_child_comment.id).unwrap();
      Post::delete(&conn, inserted_post.id).unwrap();
      Community::delete(&conn, inserted_community.id).unwrap();
-     Person::delete(&conn, inserted_persod.id).unwrap();
 -    User_::delete(&conn, inserted_user.id).unwrap();
++    Person::delete(&conn, inserted_person.id).unwrap();
  
      assert_eq!(expected_comment, read_comment);
      assert_eq!(expected_comment, inserted_comment);
diff --cc crates/db_queries/src/source/community.rs
index 2b61d312,03484816..9a6a0560
--- a/crates/db_queries/src/source/community.rs
+++ b/crates/db_queries/src/source/community.rs
@@@ -9,10 -9,10 +9,10 @@@ use lemmy_db_schema::
      CommunityForm,
      CommunityModerator,
      CommunityModeratorForm,
 -    CommunityUserBan,
 -    CommunityUserBanForm,
 +    CommunityPersonBan,
 +    CommunityPersonBanForm,
    },
-   Url,
+   DbUrl,
  };
  
  mod safe_type {
@@@ -287,12 -290,12 +290,12 @@@ impl Followable<CommunityFollowerForm> 
      use lemmy_db_schema::schema::community_follower::dsl::*;
      insert_into(community_follower)
        .values(community_follower_form)
--      .on_conflict((community_id, user_id))
++      .on_conflict((community_id, person_id))
        .do_update()
        .set(community_follower_form)
        .get_result::<Self>(conn)
    }
--  fn follow_accepted(conn: &PgConnection, community_id_: i32, user_id_: i32) -> Result<Self, Error>
++  fn follow_accepted(conn: &PgConnection, community_id_: i32, person_id_: i32) -> Result<Self, Error>
    where
      Self: Sized,
    {
@@@ -300,7 -303,7 +303,7 @@@
      diesel::update(
        community_follower
          .filter(community_id.eq(community_id_))
--        .filter(user_id.eq(user_id_)),
++        .filter(person_id.eq(person_id_)),
      )
      .set(pending.eq(true))
      .get_result::<Self>(conn)
@@@ -313,7 -316,7 +316,7 @@@
      diesel::delete(
        community_follower
          .filter(community_id.eq(&community_follower_form.community_id))
-         .filter(user_id.eq(&community_follower_form.person_id)),
 -        .filter(user_id.eq(&community_follower_form.user_id)),
++        .filter(person_id.eq(&community_follower_form.person_id)),
      )
      .execute(conn)
    }
@@@ -336,27 -339,39 +339,27 @@@ mod tests 
      Crud,
      Followable,
      Joinable,
--    ListingType,
--    SortType,
    };
 -  use lemmy_db_schema::source::{community::*, user::*};
 +  use lemmy_db_schema::source::{community::*, person::*};
+   use serial_test::serial;
  
    #[test]
+   #[serial]
    fn test_crud() {
      let conn = establish_unpooled_connection();
  
 -    let new_user = UserForm {
 +    let new_person = PersonForm {
        name: "bobbee".into(),
        preferred_username: None,
 -      password_encrypted: "nope".into(),
 -      email: None,
 -      matrix_user_id: None,
        avatar: None,
        banner: None,
 -      admin: false,
--      banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
 -      show_nsfw: false,
 -      theme: "browser".into(),
 -      default_sort_type: SortType::Hot as i16,
 -      default_listing_type: ListingType::Subscribed as i16,
 -      lang: "browser".into(),
 -      show_avatars: true,
 -      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
diff --cc crates/db_queries/src/source/local_user.rs
index 53743864,00000000..644c3eac
mode 100644,000000..100644
--- a/crates/db_queries/src/source/local_user.rs
+++ b/crates/db_queries/src/source/local_user.rs
@@@ -1,76 -1,0 +1,159 @@@
- use crate::{is_email_regex, ApubObject, Crud, ToSafeSettings};
++use crate::{is_email_regex, Crud, ToSafeSettings};
++use diesel::{dsl::*, result::Error, *};
++use lemmy_db_schema::source::local_user::LocalUserSettings;
++use lemmy_db_schema::schema::local_user::dsl::*;
++use lemmy_db_schema::source::local_user::{LocalUser, LocalUserForm};
++use bcrypt::{hash, DEFAULT_COST};
++
++mod safe_type {
++  use crate::ToSafe;
++  use lemmy_db_schema::{schema::local_user::columns::*, source::local_user::LocalUser};
++
++  type Columns = (
++  id,
++  person_id,
++  admin,
++  matrix_user_id,
++  );
++
++  impl ToSafe for LocalUser {
++    type SafeColumns = Columns;
++    fn safe_columns_tuple() -> Self::SafeColumns {
++      (
++        id,
++        person_id,
++        admin,
++        matrix_user_id,
++      )
++    }
++  }
++}
++
 +mod safe_settings_type {
 +  use crate::ToSafeSettings;
-   use lemmy_db_schema::{schema::user_::columns::*, source::user::User_};
++  use lemmy_db_schema::{schema::local_user::columns::*, source::local_user::LocalUser};
 +
 +  type Columns = (
 +    id,
-     name,
-     preferred_username,
++    person_id,
 +    email,
-     avatar,
 +    admin,
-     banned,
-     published,
-     updated,
 +    show_nsfw,
 +    theme,
 +    default_sort_type,
 +    default_listing_type,
 +    lang,
 +    show_avatars,
 +    send_notifications_to_email,
 +    matrix_user_id,
-     actor_id,
-     bio,
-     local,
-     last_refreshed_at,
-     banner,
-     deleted,
 +  );
 +
-   impl ToSafeSettings for User_ {
++  impl ToSafeSettings for LocalUser {
 +    type SafeSettingsColumns = Columns;
++    
++    /// Includes everything but the hashed password
 +    fn safe_settings_columns_tuple() -> Self::SafeSettingsColumns {
 +      (
 +        id,
-         name,
-         preferred_username,
++        person_id,
 +        email,
-         avatar,
 +        admin,
-         banned,
-         published,
-         updated,
 +        show_nsfw,
 +        theme,
 +        default_sort_type,
 +        default_listing_type,
 +        lang,
 +        show_avatars,
 +        send_notifications_to_email,
 +        matrix_user_id,
-         actor_id,
-         bio,
-         local,
-         last_refreshed_at,
-         banner,
-         deleted,
 +      )
 +    }
 +  }
 +}
 +
- pub trait UserSafeSettings_ {
-   fn read(conn: &PgConnection, user_id: i32) -> Result<UserSafeSettings, Error>;
++pub trait LocalUser_ {
++  fn register(conn: &PgConnection, form: &LocalUserForm) -> Result<LocalUser, Error>;
++  fn update_password(conn: &PgConnection, person_id: i32, new_password: &str)
++    -> Result<LocalUser, Error>;
++  fn add_admin(conn: &PgConnection, local_user_id: i32, added: bool) -> Result<LocalUser, Error>;
++  fn find_by_email(conn: &PgConnection, from_email: &str) -> Result<LocalUser, Error>;
++  fn find_by_person(conn: &PgConnection, from_person_id: i32) -> Result<LocalUser, Error>;
++}
++
++impl LocalUser_ for LocalUser {
++  fn register(conn: &PgConnection, form: &LocalUserForm) -> Result<Self, Error> {
++    let mut edited_user = form.clone();
++    let password_hash =
++      hash(&form.password_encrypted, DEFAULT_COST).expect("Couldn't hash password");
++    edited_user.password_encrypted = password_hash;
++
++    Self::create(&conn, &edited_user)
++  }
++
++  // TODO do more individual updates like these
++  fn update_password(conn: &PgConnection, local_user_id: i32, new_password: &str) -> Result<Self, Error> {
++    let password_hash = hash(new_password, DEFAULT_COST).expect("Couldn't hash password");
++
++    diesel::update(local_user.find(local_user_id))
++      .set((
++        password_encrypted.eq(password_hash),
++      ))
++      .get_result::<Self>(conn)
++  }
++
++  // TODO is this used?
++  fn add_admin(conn: &PgConnection, local_user_id: i32, added: bool) -> Result<Self, Error> {
++    diesel::update(local_user.find(local_user_id))
++      .set(admin.eq(added))
++      .get_result::<Self>(conn)
++  }
++
++  // TODO is this used?
++  fn find_by_email(conn: &PgConnection, from_email: &str) -> Result<LocalUser, Error> {
++    local_user
++      .filter(email.eq(from_email))
++      .first::<LocalUser>(conn)
++  }
++
++  // TODO is this used?
++  fn find_by_person(conn: &PgConnection, for_person_id: i32) -> Result<LocalUser, Error> {
++    local_user
++      .filter(person_id.eq(for_person_id))
++      .first::<LocalUser>(conn)
++  }
++
++}
++
++impl Crud<LocalUserForm> for LocalUser {
++  fn read(conn: &PgConnection, local_user_id: i32) -> Result<Self, Error> {
++    local_user
++      .find(local_user_id)
++      .first::<Self>(conn)
++  }
++  fn delete(conn: &PgConnection, local_user_id: i32) -> Result<usize, Error> {
++    diesel::delete(local_user.find(local_user_id)).execute(conn)
++  }
++  fn create(conn: &PgConnection, form: &LocalUserForm) -> Result<Self, Error> {
++    insert_into(local_user).values(form).get_result::<Self>(conn)
++  }
++  fn update(conn: &PgConnection, local_user_id: i32, form: &LocalUserForm) -> Result<Self, Error> {
++    diesel::update(local_user.find(local_user_id))
++      .set(form)
++      .get_result::<Self>(conn)
++  }
++}
++
++// TODO is this used?
++pub trait LocalUserSettings_ {
++  fn read(conn: &PgConnection, user_id: i32) -> Result<LocalUserSettings, Error>;
 +}
 +
- impl UserSafeSettings_ for UserSafeSettings {
++// TODO is this used?
++impl LocalUserSettings_ for LocalUserSettings {
 +  fn read(conn: &PgConnection, user_id: i32) -> Result<Self, Error> {
-     user_
-       .select(User_::safe_settings_columns_tuple())
-       .filter(deleted.eq(false))
++    local_user
++      .select(LocalUser::safe_settings_columns_tuple())
 +      .find(user_id)
 +      .first::<Self>(conn)
 +  }
 +}
diff --cc crates/db_queries/src/source/moderator.rs
index 36d90fdd,469ada08..4477b55e
--- a/crates/db_queries/src/source/moderator.rs
+++ b/crates/db_queries/src/source/moderator.rs
@@@ -197,8 -197,9 +197,9 @@@ impl Crud<ModAddForm> for ModAdd 
  
  #[cfg(test)]
  mod tests {
--  use crate::{establish_unpooled_connection, Crud, ListingType, SortType};
 -  use lemmy_db_schema::source::{comment::*, community::*, moderator::*, post::*, user::*};
++  use crate::{establish_unpooled_connection, Crud};
 +  use lemmy_db_schema::source::{comment::*, community::*, moderator::*, post::*, person::*};
+   use serial_test::serial;
  
    // use Crud;
    #[test]
@@@ -205,18 -207,28 +207,18 @@@
    fn test_crud() {
      let conn = establish_unpooled_connection();
  
 -    let new_mod = UserForm {
 +    let new_mod = PersonForm {
        name: "the mod".into(),
        preferred_username: None,
 -      password_encrypted: "nope".into(),
 -      email: None,
 -      matrix_user_id: None,
        avatar: None,
        banner: None,
 -      admin: false,
--      banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
 -      show_nsfw: false,
 -      theme: "browser".into(),
 -      default_sort_type: SortType::Hot as i16,
 -      default_listing_type: ListingType::Subscribed as i16,
 -      lang: "browser".into(),
 -      show_avatars: true,
 -      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
@@@ -224,20 -236,30 +226,20 @@@
        shared_inbox_url: None,
      };
  
 -    let inserted_mod = User_::create(&conn, &new_mod).unwrap();
 +    let inserted_mod = Person::create(&conn, &new_mod).unwrap();
  
 -    let new_user = UserForm {
 +    let new_person = PersonForm {
        name: "jim2".into(),
        preferred_username: None,
 -      password_encrypted: "nope".into(),
 -      email: None,
 -      matrix_user_id: None,
        avatar: None,
        banner: None,
 -      admin: false,
--      banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
 -      show_nsfw: false,
 -      theme: "browser".into(),
 -      default_sort_type: SortType::Hot as i16,
 -      default_listing_type: ListingType::Subscribed as i16,
 -      lang: "browser".into(),
 -      show_avatars: true,
 -      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
diff --cc crates/db_queries/src/source/password_reset_request.rs
index a8524cfb,600037d8..96fd0647
--- a/crates/db_queries/src/source/password_reset_request.rs
+++ b/crates/db_queries/src/source/password_reset_request.rs
@@@ -76,27 -76,39 +76,27 @@@ mod tests 
      establish_unpooled_connection,
      source::password_reset_request::PasswordResetRequest_,
      Crud,
--    ListingType,
--    SortType,
    };
 -  use lemmy_db_schema::source::{password_reset_request::PasswordResetRequest, user::*};
 +  use lemmy_db_schema::source::{password_reset_request::PasswordResetRequest, person::*};
+   use serial_test::serial;
  
    #[test]
+   #[serial]
    fn test_crud() {
      let conn = establish_unpooled_connection();
  
 -    let new_user = UserForm {
 +    let new_person = PersonForm {
        name: "thommy prw".into(),
        preferred_username: None,
 -      password_encrypted: "nope".into(),
 -      email: None,
 -      matrix_user_id: None,
        avatar: None,
        banner: None,
 -      admin: false,
--      banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
 -      show_nsfw: false,
 -      theme: "browser".into(),
 -      default_sort_type: SortType::Hot as i16,
 -      default_listing_type: ListingType::Subscribed as i16,
 -      lang: "browser".into(),
 -      show_avatars: true,
 -      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
diff --cc crates/db_queries/src/source/person.rs
index e17fcda5,00000000..86b04925
mode 100644,000000..100644
--- a/crates/db_queries/src/source/person.rs
+++ b/crates/db_queries/src/source/person.rs
@@@ -1,364 -1,0 +1,308 @@@
- use crate::{is_email_regex, ApubObject, Crud, ToSafeSettings};
- use bcrypt::{hash, DEFAULT_COST};
++use crate::{is_email_regex, ApubObject, Crud};
 +use diesel::{dsl::*, result::Error, *};
 +use lemmy_db_schema::{
 +  naive_now,
 +  schema::person::dsl::*,
 +  source::person::{PersonForm, Person},
-   Url,
++  DbUrl,
 +};
- use lemmy_utils::settings::Settings;
++use lemmy_utils::settings::structs::Settings;
 +
 +mod safe_type {
 +  use crate::ToSafe;
 +  use lemmy_db_schema::{schema::person::columns::*, source::person::Person};
 +
 +  type Columns = (
 +  id,
 +  name,
 +  preferred_username,
 +  avatar,
 +  banned,
 +  published,
 +  updated,
 +  actor_id,
 +  bio,
 +  local,
-   last_refreshed_at,
 +  banner,
 +  deleted,
 +  inbox_url,
 +  shared_inbox_url,
 +  );
 +
 +  impl ToSafe for Person {
 +    type SafeColumns = Columns;
 +    fn safe_columns_tuple() -> Self::SafeColumns {
 +      (
 +        id,
 +        name,
 +        preferred_username,
 +        avatar,
 +        banned,
 +        published,
 +        updated,
 +        actor_id,
 +        bio,
 +        local,
-         last_refreshed_at,
 +        banner,
 +        deleted,
 +        inbox_url,
 +        shared_inbox_url,
 +      )
 +    }
 +  }
 +}
 +
 +mod safe_type_alias_1 {
 +  use crate::ToSafe;
 +  use lemmy_db_schema::{schema::person_alias_1::columns::*, source::person::PersonAlias1};
 +
 +  type Columns = (
 +        id,
 +        name,
 +        preferred_username,
 +        avatar,
 +        banned,
 +        published,
 +        updated,
 +        actor_id,
 +        bio,
 +        local,
-         last_refreshed_at,
 +        banner,
 +        deleted,
 +        inbox_url,
 +        shared_inbox_url,
 +  );
 +
 +  impl ToSafe for PersonAlias1 {
 +    type SafeColumns = Columns;
 +    fn safe_columns_tuple() -> Self::SafeColumns {
 +      (
 +        id,
 +        name,
 +        preferred_username,
 +        avatar,
 +        banned,
 +        published,
 +        updated,
 +        actor_id,
 +        bio,
 +        local,
-         last_refreshed_at,
 +        banner,
 +        deleted,
 +        inbox_url,
 +        shared_inbox_url,
 +      )
 +    }
 +  }
 +}
 +
 +mod safe_type_alias_2 {
 +  use crate::ToSafe;
 +  use lemmy_db_schema::{schema::person_alias_2::columns::*, source::person::PersonAlias2};
 +
 +  type Columns = (
 +        id,
 +        name,
 +        preferred_username,
 +        avatar,
 +        banned,
 +        published,
 +        updated,
 +        actor_id,
 +        bio,
 +        local,
-         last_refreshed_at,
 +        banner,
 +        deleted,
 +        inbox_url,
 +        shared_inbox_url,
 +  );
 +
 +  impl ToSafe for PersonAlias2 {
 +    type SafeColumns = Columns;
 +    fn safe_columns_tuple() -> Self::SafeColumns {
 +      (
 +        id,
 +        name,
 +        preferred_username,
 +        avatar,
 +        banned,
 +        published,
 +        updated,
 +        actor_id,
 +        bio,
 +        local,
-         last_refreshed_at,
 +        banner,
 +        deleted,
 +        inbox_url,
 +        shared_inbox_url,
 +      )
 +    }
 +  }
 +}
 +
 +impl Crud<PersonForm> for Person {
 +  fn read(conn: &PgConnection, person_id: i32) -> Result<Self, Error> {
 +    person
 +      .filter(deleted.eq(false))
 +      .find(person_id)
 +      .first::<Self>(conn)
 +  }
 +  fn delete(conn: &PgConnection, person_id: i32) -> Result<usize, Error> {
 +    diesel::delete(person.find(person_id)).execute(conn)
 +  }
 +  fn create(conn: &PgConnection, form: &PersonForm) -> Result<Self, Error> {
 +    insert_into(person).values(form).get_result::<Self>(conn)
 +  }
 +  fn update(conn: &PgConnection, person_id: i32, form: &PersonForm) -> Result<Self, Error> {
 +    diesel::update(person.find(person_id))
 +      .set(form)
 +      .get_result::<Self>(conn)
 +  }
 +}
 +
 +impl ApubObject<PersonForm> for Person {
-   fn read_from_apub_id(conn: &PgConnection, object_id: &Url) -> Result<Self, Error> {
++  fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error> {
 +    use lemmy_db_schema::schema::person::dsl::*;
 +    person
 +      .filter(deleted.eq(false))
 +      .filter(actor_id.eq(object_id))
 +      .first::<Self>(conn)
 +  }
 +
 +  fn upsert(conn: &PgConnection, person_form: &PersonForm) -> Result<Person, Error> {
 +    insert_into(person)
 +      .values(person_form)
 +      .on_conflict(actor_id)
 +      .do_update()
 +      .set(person_form)
 +      .get_result::<Self>(conn)
 +  }
 +}
 +
 +pub trait Person_ {
-   fn register(conn: &PgConnection, form: &PersonForm) -> Result<Person, Error>;
-   fn update_password(conn: &PgConnection, person_id: i32, new_password: &str)
-     -> Result<Person, Error>;
-   fn read_from_name(conn: &PgConnection, from_name: &str) -> Result<Person, Error>;
-   fn add_admin(conn: &PgConnection, person_id: i32, added: bool) -> Result<Person, Error>;
 +  fn ban_person(conn: &PgConnection, person_id: i32, ban: bool) -> Result<Person, Error>;
-   fn find_by_email_or_name(
-     conn: &PgConnection,
-     name_or_email: &str,
-   ) -> Result<Person, Error>;
++  // TODO
++  // fn find_by_email_or_name(
++  //   conn: &PgConnection,
++  //   name_or_email: &str,
++  // ) -> Result<Person, Error>;
 +  fn find_by_name(conn: &PgConnection, name: &str) -> Result<Person, Error>;
-   fn find_by_email(conn: &PgConnection, from_email: &str) -> Result<Person, Error>;
-   fn get_profile_url(&self, hostname: &str) -> String;
 +  fn mark_as_updated(conn: &PgConnection, person_id: i32) -> Result<Person, Error>;
 +  fn delete_account(conn: &PgConnection, person_id: i32) -> Result<Person, Error>;
 +}
 +
 +impl Person_ for Person {
-   fn register(conn: &PgConnection, form: &PersonForm) -> Result<Self, Error> {
-     let mut edited_person = form.clone();
-     let password_hash =
-       hash(&form.password_encrypted, DEFAULT_COST).expect("Couldn't hash password");
-     edited_person.password_encrypted = password_hash;
- 
-     Self::create(&conn, &edited_person)
-   }
- 
-   // TODO do more individual updates like these
-   fn update_password(conn: &PgConnection, person_id: i32, new_password: &str) -> Result<Self, Error> {
-     let password_hash = hash(new_password, DEFAULT_COST).expect("Couldn't hash password");
- 
-     diesel::update(person.find(person_id))
-       .set((
-         password_encrypted.eq(password_hash),
-         updated.eq(naive_now()),
-       ))
-       .get_result::<Self>(conn)
-   }
- 
-   fn read_from_name(conn: &PgConnection, from_name: &str) -> Result<Self, Error> {
-     person
-       .filter(local.eq(true))
-       .filter(deleted.eq(false))
-       .filter(name.eq(from_name))
-       .first::<Self>(conn)
-   }
- 
-   fn add_admin(conn: &PgConnection, person_id: i32, added: bool) -> Result<Self, Error> {
-     diesel::update(person.find(person_id))
-       .set(admin.eq(added))
-       .get_result::<Self>(conn)
-   }
 +
 +  fn ban_person(conn: &PgConnection, person_id: i32, ban: bool) -> Result<Self, Error> {
 +    diesel::update(person.find(person_id))
 +      .set(banned.eq(ban))
 +      .get_result::<Self>(conn)
 +  }
 +
-   fn find_by_email_or_name(
-     conn: &PgConnection,
-     name_or_email: &str,
-   ) -> Result<Self, Error> {
-     if is_email_regex(name_or_email) {
-       Self::find_by_email(conn, name_or_email)
-     } else {
-       Self::find_by_name(conn, name_or_email)
-     }
-   }
- 
-   fn find_by_name(conn: &PgConnection, name: &str) -> Result<Person, Error> {
-     person
-       .filter(deleted.eq(false))
-       .filter(local.eq(true))
-       .filter(name.ilike(name))
-       .first::<Person>(conn)
-   }
- 
-   fn find_by_email(conn: &PgConnection, from_email: &str) -> Result<Person, Error> {
++  // TODO this needs to get moved to aggregates i think
++  // fn find_by_email_or_name(
++  //   conn: &PgConnection,
++  //   name_or_email: &str,
++  // ) -> Result<Self, Error> {
++  //   if is_email_regex(name_or_email) {
++  //     Self::find_by_email(conn, name_or_email)
++  //   } else {
++  //     Self::find_by_name(conn, name_or_email)
++  //   }
++  // }
++
++  fn find_by_name(conn: &PgConnection, from_name: &str) -> Result<Person, Error> {
 +    person
 +      .filter(deleted.eq(false))
 +      .filter(local.eq(true))
-       .filter(email.eq(from_email))
++      .filter(name.ilike(from_name))
 +      .first::<Person>(conn)
 +  }
 +
-   fn get_profile_url(&self, hostname: &str) -> String {
-     format!(
-       "{}://{}/u/{}",
-       Settings::get().get_protocol_string(),
-       hostname,
-       self.name
-     )
-   }
- 
 +  fn mark_as_updated(conn: &PgConnection, person_id: i32) -> Result<Person, Error> {
 +    diesel::update(person.find(person_id))
 +      .set((last_refreshed_at.eq(naive_now()),))
 +      .get_result::<Self>(conn)
 +  }
 +
 +  fn delete_account(conn: &PgConnection, person_id: i32) -> Result<Person, Error> {
++    use lemmy_db_schema::schema::local_user;
++
++    // Set the local user info to none
++    diesel::update(local_user::table.filter(local_user::person_id.eq(person_id)))
++      .set((
++        local_user::email.eq::<Option<String>>(None),
++        local_user::matrix_user_id.eq::<Option<String>>(None),
++      ))
++      .execute(conn)?;
++
 +    diesel::update(person.find(person_id))
 +      .set((
 +        preferred_username.eq::<Option<String>>(None),
-         email.eq::<Option<String>>(None),
-         matrix_user_id.eq::<Option<String>>(None),
 +        bio.eq::<Option<String>>(None),
 +        deleted.eq(true),
 +        updated.eq(naive_now()),
 +      ))
 +      .get_result::<Self>(conn)
 +  }
 +}
 +
 +#[cfg(test)]
 +mod tests {
-   use crate::{establish_unpooled_connection, source::person::*, ListingType, SortType};
++  use crate::{establish_unpooled_connection, source::person::*};
 +
 +  #[test]
 +  fn test_crud() {
 +    let conn = establish_unpooled_connection();
 +
 +    let new_person = PersonForm {
 +      name: "thommy".into(),
 +      preferred_username: None,
 +      avatar: None,
 +      banner: None,
-       banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
 +      published: None,
 +      updated: None,
 +      actor_id: None,
 +      bio: None,
-       local: true,
++      local: None,
 +      private_key: None,
 +      public_key: None,
 +      last_refreshed_at: None,
 +      inbox_url: None,
 +      shared_inbox_url: None,
 +    };
 +
 +    let inserted_person = Person::create(&conn, &new_person).unwrap();
 +
 +    let expected_person = Person {
 +      id: inserted_person.id,
 +      name: "thommy".into(),
 +      preferred_username: None,
 +      avatar: None,
 +      banner: None,
 +      banned: false,
 +      deleted: false,
 +      published: inserted_person.published,
 +      updated: None,
 +      actor_id: inserted_person.actor_id.to_owned(),
 +      bio: None,
 +      local: true,
 +      private_key: None,
 +      public_key: None,
 +      last_refreshed_at: inserted_person.published,
-       deleted: false,
 +      inbox_url: inserted_person.inbox_url.to_owned(),
 +      shared_inbox_url: None,
 +    };
 +
 +    let read_person = Person::read(&conn, inserted_person.id).unwrap();
 +    let updated_person = Person::update(&conn, inserted_person.id, &new_person).unwrap();
 +    let num_deleted = Person::delete(&conn, inserted_person.id).unwrap();
 +
 +    assert_eq!(expected_person, read_person);
 +    assert_eq!(expected_person, inserted_person);
 +    assert_eq!(expected_person, updated_person);
 +    assert_eq!(1, num_deleted);
 +  }
 +}
diff --cc crates/db_queries/src/source/person_mention.rs
index 73543b95,5efc95bf..ba40c17f
--- a/crates/db_queries/src/source/person_mention.rs
+++ b/crates/db_queries/src/source/person_mention.rs
@@@ -73,31 -73,43 +73,33 @@@ impl PersonMention_ for PersonMention 
  
  #[cfg(test)]
  mod tests {
--  use crate::{establish_unpooled_connection, Crud, ListingType, SortType};
++  use crate::{establish_unpooled_connection, Crud};
    use lemmy_db_schema::source::{
      comment::*,
      community::{Community, CommunityForm},
      post::*,
 -    user::*,
 -    user_mention::*,
 +    person::*,
 +    person_mention::*,
    };
+   use serial_test::serial;
  
    #[test]
+   #[serial]
    fn test_crud() {
      let conn = establish_unpooled_connection();
  
 -    let new_user = UserForm {
 +    let new_person = PersonForm {
        name: "terrylake".into(),
        preferred_username: None,
 -      password_encrypted: "nope".into(),
 -      email: None,
 -      matrix_user_id: None,
        avatar: None,
        banner: None,
 -      admin: false,
--      banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
 -      show_nsfw: false,
 -      theme: "browser".into(),
 -      default_sort_type: SortType::Hot as i16,
 -      default_listing_type: ListingType::Subscribed as i16,
 -      lang: "browser".into(),
 -      show_avatars: true,
 -      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
@@@ -105,20 -117,30 +107,20 @@@
        shared_inbox_url: None,
      };
  
 -    let inserted_user = User_::create(&conn, &new_user).unwrap();
 +    let inserted_person = Person::create(&conn, &new_person).unwrap();
  
 -    let recipient_form = UserForm {
 +    let recipient_form = PersonForm {
        name: "terrylakes recipient".into(),
        preferred_username: None,
 -      password_encrypted: "nope".into(),
 -      email: None,
 -      matrix_user_id: None,
        avatar: None,
        banner: None,
 -      admin: false,
--      banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
 -      show_nsfw: false,
 -      theme: "browser".into(),
 -      default_sort_type: SortType::Hot as i16,
 -      default_listing_type: ListingType::Subscribed as i16,
 -      lang: "browser".into(),
 -      show_avatars: true,
 -      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
diff --cc crates/db_queries/src/source/post.rs
index 6a66a79c,f105dc73..6ac5039e
--- a/crates/db_queries/src/source/post.rs
+++ b/crates/db_queries/src/source/post.rs
@@@ -226,28 -226,40 +226,30 @@@ impl Readable<PostReadForm> for PostRea
  
  #[cfg(test)]
  mod tests {
--  use crate::{establish_unpooled_connection, source::post::*, ListingType, SortType};
++  use crate::{establish_unpooled_connection, source::post::*};
    use lemmy_db_schema::source::{
      community::{Community, CommunityForm},
 -    user::*,
 +    person::*,
    };
+   use serial_test::serial;
  
    #[test]
+   #[serial]
    fn test_crud() {
      let conn = establish_unpooled_connection();
  
 -    let new_user = UserForm {
 +    let new_person = PersonForm {
        name: "jim".into(),
        preferred_username: None,
 -      password_encrypted: "nope".into(),
 -      email: None,
 -      matrix_user_id: None,
        avatar: None,
        banner: None,
 -      admin: false,
--      banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
 -      show_nsfw: false,
 -      theme: "browser".into(),
 -      default_sort_type: SortType::Hot as i16,
 -      default_listing_type: ListingType::Subscribed as i16,
 -      lang: "browser".into(),
 -      show_avatars: true,
 -      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
diff --cc crates/db_queries/src/source/private_message.rs
index e4ca3c6b,c437252e..c0f74367
--- a/crates/db_queries/src/source/private_message.rs
+++ b/crates/db_queries/src/source/private_message.rs
@@@ -143,27 -143,39 +143,27 @@@ mod tests 
      establish_unpooled_connection,
      source::private_message::PrivateMessage_,
      Crud,
--    ListingType,
--    SortType,
    };
 -  use lemmy_db_schema::source::{private_message::*, user::*};
 +  use lemmy_db_schema::source::{private_message::*, person::*};
+   use serial_test::serial;
  
    #[test]
+   #[serial]
    fn test_crud() {
      let conn = establish_unpooled_connection();
  
 -    let creator_form = UserForm {
 +    let creator_form = PersonForm {
        name: "creator_pm".into(),
        preferred_username: None,
 -      password_encrypted: "nope".into(),
 -      email: None,
 -      matrix_user_id: None,
        avatar: None,
        banner: None,
 -      admin: false,
--      banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
 -      show_nsfw: false,
 -      theme: "browser".into(),
 -      default_sort_type: SortType::Hot as i16,
 -      default_listing_type: ListingType::Subscribed as i16,
 -      lang: "browser".into(),
 -      show_avatars: true,
 -      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
@@@ -171,20 -183,30 +171,20 @@@
        shared_inbox_url: None,
      };
  
 -    let inserted_creator = User_::create(&conn, &creator_form).unwrap();
 +    let inserted_creator = Person::create(&conn, &creator_form).unwrap();
  
 -    let recipient_form = UserForm {
 +    let recipient_form = PersonForm {
        name: "recipient_pm".into(),
        preferred_username: None,
 -      password_encrypted: "nope".into(),
 -      email: None,
 -      matrix_user_id: None,
        avatar: None,
        banner: None,
 -      admin: false,
--      banned: Some(false),
-       deleted: false,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
 -      show_nsfw: false,
 -      theme: "browser".into(),
 -      default_sort_type: SortType::Hot as i16,
 -      default_listing_type: ListingType::Subscribed as i16,
 -      lang: "browser".into(),
 -      show_avatars: true,
 -      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
diff --cc crates/db_queries/src/source/user.rs
index 00000000,d0e7411a..077b3e78
mode 000000,100644..100644
--- a/crates/db_queries/src/source/user.rs
+++ b/crates/db_queries/src/source/user.rs
@@@ -1,0 -1,460 +1,459 @@@
+ use crate::{is_email_regex, ApubObject, Crud, ToSafeSettings};
+ use bcrypt::{hash, DEFAULT_COST};
+ use diesel::{dsl::*, result::Error, *};
+ use lemmy_db_schema::{
+   naive_now,
+   schema::user_::dsl::*,
+   source::user::{UserForm, UserSafeSettings, User_},
+   DbUrl,
+ };
+ use lemmy_utils::settings::structs::Settings;
+ 
+ mod safe_type {
+   use crate::ToSafe;
+   use lemmy_db_schema::{schema::user_::columns::*, source::user::User_};
+ 
+   type Columns = (
+     id,
+     name,
+     preferred_username,
+     avatar,
+     admin,
+     banned,
+     published,
+     updated,
+     matrix_user_id,
+     actor_id,
+     bio,
+     local,
+     banner,
+     deleted,
+     inbox_url,
+     shared_inbox_url,
+   );
+ 
+   impl ToSafe for User_ {
+     type SafeColumns = Columns;
+     fn safe_columns_tuple() -> Self::SafeColumns {
+       (
+         id,
+         name,
+         preferred_username,
+         avatar,
+         admin,
+         banned,
+         published,
+         updated,
+         matrix_user_id,
+         actor_id,
+         bio,
+         local,
+         banner,
+         deleted,
+         inbox_url,
+         shared_inbox_url,
+       )
+     }
+   }
+ }
+ 
+ mod safe_type_alias_1 {
+   use crate::ToSafe;
+   use lemmy_db_schema::{schema::user_alias_1::columns::*, source::user::UserAlias1};
+ 
+   type Columns = (
+     id,
+     name,
+     preferred_username,
+     avatar,
+     admin,
+     banned,
+     published,
+     updated,
+     matrix_user_id,
+     actor_id,
+     bio,
+     local,
+     banner,
+     deleted,
+   );
+ 
+   impl ToSafe for UserAlias1 {
+     type SafeColumns = Columns;
+     fn safe_columns_tuple() -> Self::SafeColumns {
+       (
+         id,
+         name,
+         preferred_username,
+         avatar,
+         admin,
+         banned,
+         published,
+         updated,
+         matrix_user_id,
+         actor_id,
+         bio,
+         local,
+         banner,
+         deleted,
+       )
+     }
+   }
+ }
+ 
+ mod safe_type_alias_2 {
+   use crate::ToSafe;
+   use lemmy_db_schema::{schema::user_alias_2::columns::*, source::user::UserAlias2};
+ 
+   type Columns = (
+     id,
+     name,
+     preferred_username,
+     avatar,
+     admin,
+     banned,
+     published,
+     updated,
+     matrix_user_id,
+     actor_id,
+     bio,
+     local,
+     banner,
+     deleted,
+   );
+ 
+   impl ToSafe for UserAlias2 {
+     type SafeColumns = Columns;
+     fn safe_columns_tuple() -> Self::SafeColumns {
+       (
+         id,
+         name,
+         preferred_username,
+         avatar,
+         admin,
+         banned,
+         published,
+         updated,
+         matrix_user_id,
+         actor_id,
+         bio,
+         local,
+         banner,
+         deleted,
+       )
+     }
+   }
+ }
+ 
+ mod safe_settings_type {
+   use crate::ToSafeSettings;
+   use lemmy_db_schema::{schema::user_::columns::*, source::user::User_};
+ 
+   type Columns = (
+     id,
+     name,
+     preferred_username,
+     email,
+     avatar,
+     admin,
+     banned,
+     published,
+     updated,
+     show_nsfw,
+     theme,
+     default_sort_type,
+     default_listing_type,
+     lang,
+     show_avatars,
+     send_notifications_to_email,
+     matrix_user_id,
+     actor_id,
+     bio,
+     local,
+     last_refreshed_at,
+     banner,
+     deleted,
+   );
+ 
+   impl ToSafeSettings for User_ {
+     type SafeSettingsColumns = Columns;
+     fn safe_settings_columns_tuple() -> Self::SafeSettingsColumns {
+       (
+         id,
+         name,
+         preferred_username,
+         email,
+         avatar,
+         admin,
+         banned,
+         published,
+         updated,
+         show_nsfw,
+         theme,
+         default_sort_type,
+         default_listing_type,
+         lang,
+         show_avatars,
+         send_notifications_to_email,
+         matrix_user_id,
+         actor_id,
+         bio,
+         local,
+         last_refreshed_at,
+         banner,
+         deleted,
+       )
+     }
+   }
+ }
+ 
+ pub trait UserSafeSettings_ {
+   fn read(conn: &PgConnection, user_id: i32) -> Result<UserSafeSettings, Error>;
+ }
+ 
+ impl UserSafeSettings_ for UserSafeSettings {
+   fn read(conn: &PgConnection, user_id: i32) -> Result<Self, Error> {
+     user_
+       .select(User_::safe_settings_columns_tuple())
+       .filter(deleted.eq(false))
+       .find(user_id)
+       .first::<Self>(conn)
+   }
+ }
+ 
+ impl Crud<UserForm> for User_ {
+   fn read(conn: &PgConnection, user_id: i32) -> Result<Self, Error> {
+     user_
+       .filter(deleted.eq(false))
+       .find(user_id)
+       .first::<Self>(conn)
+   }
+   fn delete(conn: &PgConnection, user_id: i32) -> Result<usize, Error> {
+     diesel::delete(user_.find(user_id)).execute(conn)
+   }
+   fn create(conn: &PgConnection, form: &UserForm) -> Result<Self, Error> {
+     insert_into(user_).values(form).get_result::<Self>(conn)
+   }
+   fn update(conn: &PgConnection, user_id: i32, form: &UserForm) -> Result<Self, Error> {
+     diesel::update(user_.find(user_id))
+       .set(form)
+       .get_result::<Self>(conn)
+   }
+ }
+ 
+ impl ApubObject<UserForm> for User_ {
+   fn read_from_apub_id(conn: &PgConnection, object_id: &DbUrl) -> Result<Self, Error> {
+     use lemmy_db_schema::schema::user_::dsl::*;
+     user_
+       .filter(deleted.eq(false))
+       .filter(actor_id.eq(object_id))
+       .first::<Self>(conn)
+   }
+ 
+   fn upsert(conn: &PgConnection, user_form: &UserForm) -> Result<User_, Error> {
+     insert_into(user_)
+       .values(user_form)
+       .on_conflict(actor_id)
+       .do_update()
+       .set(user_form)
+       .get_result::<Self>(conn)
+   }
+ }
+ 
+ pub trait User {
+   fn register(conn: &PgConnection, form: &UserForm) -> Result<User_, Error>;
+   fn update_password(conn: &PgConnection, user_id: i32, new_password: &str)
+     -> Result<User_, Error>;
+   fn read_from_name(conn: &PgConnection, from_user_name: &str) -> Result<User_, Error>;
+   fn add_admin(conn: &PgConnection, user_id: i32, added: bool) -> Result<User_, Error>;
+   fn ban_user(conn: &PgConnection, user_id: i32, ban: bool) -> Result<User_, Error>;
+   fn find_by_email_or_username(
+     conn: &PgConnection,
+     username_or_email: &str,
+   ) -> Result<User_, Error>;
+   fn find_by_username(conn: &PgConnection, username: &str) -> Result<User_, Error>;
 -  fn find_by_email(conn: &PgConnection, from_email: &str) -> Result<User_, Error>;
+   fn get_profile_url(&self, hostname: &str) -> String;
+   fn mark_as_updated(conn: &PgConnection, user_id: i32) -> Result<User_, Error>;
+   fn delete_account(conn: &PgConnection, user_id: i32) -> Result<User_, Error>;
+ }
+ 
+ impl User for User_ {
+   fn register(conn: &PgConnection, form: &UserForm) -> Result<Self, Error> {
+     let mut edited_user = form.clone();
+     let password_hash =
+       hash(&form.password_encrypted, DEFAULT_COST).expect("Couldn't hash password");
+     edited_user.password_encrypted = password_hash;
+ 
+     Self::create(&conn, &edited_user)
+   }
+ 
+   // TODO do more individual updates like these
+   fn update_password(conn: &PgConnection, user_id: i32, new_password: &str) -> Result<Self, Error> {
+     let password_hash = hash(new_password, DEFAULT_COST).expect("Couldn't hash password");
+ 
+     diesel::update(user_.find(user_id))
+       .set((
+         password_encrypted.eq(password_hash),
+         updated.eq(naive_now()),
+       ))
+       .get_result::<Self>(conn)
+   }
+ 
+   fn read_from_name(conn: &PgConnection, from_user_name: &str) -> Result<Self, Error> {
+     user_
+       .filter(local.eq(true))
+       .filter(deleted.eq(false))
+       .filter(name.eq(from_user_name))
+       .first::<Self>(conn)
+   }
+ 
+   fn add_admin(conn: &PgConnection, user_id: i32, added: bool) -> Result<Self, Error> {
+     diesel::update(user_.find(user_id))
+       .set(admin.eq(added))
+       .get_result::<Self>(conn)
+   }
+ 
+   fn ban_user(conn: &PgConnection, user_id: i32, ban: bool) -> Result<Self, Error> {
+     diesel::update(user_.find(user_id))
+       .set(banned.eq(ban))
+       .get_result::<Self>(conn)
+   }
+ 
+   fn find_by_email_or_username(
+     conn: &PgConnection,
+     username_or_email: &str,
+   ) -> Result<Self, Error> {
+     if is_email_regex(username_or_email) {
+       Self::find_by_email(conn, username_or_email)
+     } else {
+       Self::find_by_username(conn, username_or_email)
+     }
+   }
+ 
+   fn find_by_username(conn: &PgConnection, username: &str) -> Result<User_, Error> {
+     user_
+       .filter(deleted.eq(false))
+       .filter(local.eq(true))
+       .filter(name.ilike(username))
+       .first::<User_>(conn)
+   }
+ 
+   fn find_by_email(conn: &PgConnection, from_email: &str) -> Result<User_, Error> {
+     user_
+       .filter(deleted.eq(false))
+       .filter(local.eq(true))
+       .filter(email.eq(from_email))
+       .first::<User_>(conn)
+   }
+ 
+   fn get_profile_url(&self, hostname: &str) -> String {
+     format!(
+       "{}://{}/u/{}",
+       Settings::get().get_protocol_string(),
+       hostname,
+       self.name
+     )
+   }
+ 
+   fn mark_as_updated(conn: &PgConnection, user_id: i32) -> Result<User_, Error> {
+     diesel::update(user_.find(user_id))
+       .set((last_refreshed_at.eq(naive_now()),))
+       .get_result::<Self>(conn)
+   }
+ 
+   fn delete_account(conn: &PgConnection, user_id: i32) -> Result<User_, Error> {
+     diesel::update(user_.find(user_id))
+       .set((
+         preferred_username.eq::<Option<String>>(None),
+         email.eq::<Option<String>>(None),
+         matrix_user_id.eq::<Option<String>>(None),
+         bio.eq::<Option<String>>(None),
+         deleted.eq(true),
+         updated.eq(naive_now()),
+       ))
+       .get_result::<Self>(conn)
+   }
+ }
+ 
+ #[cfg(test)]
+ mod tests {
+   use crate::{establish_unpooled_connection, source::user::*, ListingType, SortType};
+   use serial_test::serial;
+ 
+   #[test]
+   #[serial]
+   fn test_crud() {
+     let conn = establish_unpooled_connection();
+ 
+     let new_user = UserForm {
+       name: "thommy".into(),
+       preferred_username: None,
+       password_encrypted: "nope".into(),
+       email: None,
+       matrix_user_id: None,
+       avatar: None,
+       banner: None,
+       admin: false,
+       banned: Some(false),
+       published: None,
+       updated: None,
+       show_nsfw: false,
+       theme: "browser".into(),
+       default_sort_type: SortType::Hot as i16,
+       default_listing_type: ListingType::Subscribed as i16,
+       lang: "browser".into(),
+       show_avatars: true,
+       send_notifications_to_email: false,
+       actor_id: None,
+       bio: None,
+       local: true,
+       private_key: None,
+       public_key: None,
+       last_refreshed_at: None,
+       inbox_url: None,
+       shared_inbox_url: None,
+     };
+ 
+     let inserted_user = User_::create(&conn, &new_user).unwrap();
+ 
+     let expected_user = User_ {
+       id: inserted_user.id,
+       name: "thommy".into(),
+       preferred_username: None,
+       password_encrypted: "nope".into(),
+       email: None,
+       matrix_user_id: None,
+       avatar: None,
+       banner: None,
+       admin: false,
+       banned: false,
+       published: inserted_user.published,
+       updated: None,
+       show_nsfw: false,
+       theme: "browser".into(),
+       default_sort_type: SortType::Hot as i16,
+       default_listing_type: ListingType::Subscribed as i16,
+       lang: "browser".into(),
+       show_avatars: true,
+       send_notifications_to_email: false,
+       actor_id: inserted_user.actor_id.to_owned(),
+       bio: None,
+       local: true,
+       private_key: None,
+       public_key: None,
+       last_refreshed_at: inserted_user.published,
+       deleted: false,
+       inbox_url: inserted_user.inbox_url.to_owned(),
+       shared_inbox_url: None,
+     };
+ 
+     let read_user = User_::read(&conn, inserted_user.id).unwrap();
+     let updated_user = User_::update(&conn, inserted_user.id, &new_user).unwrap();
+     let num_deleted = User_::delete(&conn, inserted_user.id).unwrap();
+ 
+     assert_eq!(expected_user, read_user);
+     assert_eq!(expected_user, inserted_user);
+     assert_eq!(expected_user, updated_user);
+     assert_eq!(1, num_deleted);
+   }
+ }
diff --cc crates/db_schema/src/schema.rs
index 0641bdee,3786e00c..c5bf7d2f
--- a/crates/db_schema/src/schema.rs
+++ b/crates/db_schema/src/schema.rs
@@@ -89,11 -89,11 +89,11 @@@ table! 
          private_key -> Nullable<Text>,
          public_key -> Nullable<Text>,
          last_refreshed_at -> Timestamp,
--        icon -> Nullable<Text>,
--        banner -> Nullable<Text>,
 -        followers_url -> Text,
 -        inbox_url -> Text,
 -        shared_inbox_url -> Nullable<Text>,
++        icon -> Nullable<Varchar>,
++        banner -> Nullable<Varchar>,
 +        followers_url -> Varchar,
 +        inbox_url -> Varchar,
 +        shared_inbox_url -> Nullable<Varchar>,
      }
  }
  
@@@ -261,60 -243,9 +261,60 @@@ table! 
  table! {
      password_reset_request (id) {
          id -> Int4,
 -        user_id -> Int4,
          token_encrypted -> Text,
          published -> Timestamp,
 +        local_user_id -> Int4,
 +    }
 +}
 +
 +table! {
 +    person (id) {
 +        id -> Int4,
 +        name -> Varchar,
 +        preferred_username -> Nullable<Varchar>,
-         avatar -> Nullable<Text>,
++        avatar -> Nullable<Varchar>,
 +        banned -> Bool,
 +        published -> Timestamp,
 +        updated -> Nullable<Timestamp>,
 +        actor_id -> Varchar,
 +        bio -> Nullable<Text>,
 +        local -> Bool,
 +        private_key -> Nullable<Text>,
 +        public_key -> Nullable<Text>,
 +        last_refreshed_at -> Timestamp,
-         banner -> Nullable<Text>,
++        banner -> Nullable<Varchar>,
 +        deleted -> Bool,
 +        inbox_url -> Varchar,
 +        shared_inbox_url -> Nullable<Varchar>,
 +    }
 +}
 +
 +table! {
 +    person_aggregates (id) {
 +        id -> Int4,
 +        person_id -> Int4,
 +        post_count -> Int8,
 +        post_score -> Int8,
 +        comment_count -> Int8,
 +        comment_score -> Int8,
 +    }
 +}
 +
 +table! {
 +    person_ban (id) {
 +        id -> Int4,
 +        person_id -> Int4,
 +        published -> Timestamp,
 +    }
 +}
 +
 +table! {
 +    person_mention (id) {
 +        id -> Int4,
 +        recipient_id -> Int4,
 +        comment_id -> Int4,
 +        read -> Bool,
 +        published -> Timestamp,
      }
  }
  
@@@ -322,7 -253,7 +322,7 @@@ table! 
      post (id) {
          id -> Int4,
          name -> Varchar,
--        url -> Nullable<Text>,
++        url -> Nullable<Varchar>,
          body -> Nullable<Text>,
          creator_id -> Int4,
          community_id -> Int4,
@@@ -427,8 -358,8 +427,8 @@@ table! 
          enable_downvotes -> Bool,
          open_registration -> Bool,
          enable_nsfw -> Bool,
--        icon -> Nullable<Text>,
--        banner -> Nullable<Text>,
++        icon -> Nullable<Varchar>,
++        banner -> Nullable<Varchar>,
      }
  }
  
@@@ -470,7 -463,10 +470,7 @@@ table! 
          id -> Int4,
          name -> Varchar,
          preferred_username -> Nullable<Varchar>,
 -        password_encrypted -> Text,
 -        email -> Nullable<Text>,
--        avatar -> Nullable<Text>,
 -        admin -> Bool,
++        avatar -> Nullable<Varchar>,
          banned -> Bool,
          published -> Timestamp,
          updated -> Nullable<Timestamp>,
@@@ -480,10 -484,8 +480,10 @@@
          private_key -> Nullable<Text>,
          public_key -> Nullable<Text>,
          last_refreshed_at -> Timestamp,
--        banner -> Nullable<Text>,
++        banner -> Nullable<Varchar>,
          deleted -> Bool,
 +        inbox_url -> Varchar,
 +        shared_inbox_url -> Nullable<Varchar>,
      }
  }
  
@@@ -492,7 -494,10 +492,7 @@@ table! 
          id -> Int4,
          name -> Varchar,
          preferred_username -> Nullable<Varchar>,
 -        password_encrypted -> Text,
 -        email -> Nullable<Text>,
--        avatar -> Nullable<Text>,
 -        admin -> Bool,
++        avatar -> Nullable<Varchar>,
          banned -> Bool,
          published -> Timestamp,
          updated -> Nullable<Timestamp>,
@@@ -502,10 -515,8 +502,10 @@@
          private_key -> Nullable<Text>,
          public_key -> Nullable<Text>,
          last_refreshed_at -> Timestamp,
--        banner -> Nullable<Text>,
++        banner -> Nullable<Varchar>,
          deleted -> Bool,
 +        inbox_url -> Varchar,
 +        shared_inbox_url -> Nullable<Varchar>,
      }
  }
  
diff --cc crates/db_schema/src/source/community.rs
index bc99a575,b9fe1249..98c302dc
--- a/crates/db_schema/src/source/community.rs
+++ b/crates/db_schema/src/source/community.rs
@@@ -1,6 -1,6 +1,6 @@@
  use crate::{
 -  schema::{community, community_follower, community_moderator, community_user_ban},
 +  schema::{community, community_follower, community_moderator, community_person_ban},
-   Url,
+   DbUrl,
  };
  use serde::Serialize;
  
diff --cc crates/db_schema/src/source/local_user.rs
index c4300dfd,00000000..68a8bdcf
mode 100644,000000..100644
--- a/crates/db_schema/src/source/local_user.rs
+++ b/crates/db_schema/src/source/local_user.rs
@@@ -1,66 -1,0 +1,56 @@@
 +use crate::schema::local_user;
 +use serde::Serialize;
 +
 +#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
 +#[table_name = "local_user"]
 +pub struct LocalUser {
 +  pub id: i32,                                  
 +  pub person_id: i32,                          
 +  pub password_encrypted: String,               
 +  pub email: Option<String>,                    
 +  pub admin: bool,                              
 +  pub show_nsfw: bool,                          
 +  pub theme: String,                            
 +  pub default_sort_type: i16,                   
 +  pub default_listing_type: i16,                
 +  pub lang: String,                             
 +  pub show_avatars: bool,                       
 +  pub send_notifications_to_email: bool,        
 +  pub matrix_user_id: Option<String>,           
 +}
 +
 +// TODO redo these, check table defaults
 +#[derive(Insertable, AsChangeset, Clone)]
 +#[table_name = "local_user"]
 +pub struct LocalUserForm {
 +  pub person_id: i32,                          
 +  pub password_encrypted: String,               
 +  pub email: Option<Option<String>>,
 +  pub admin: Option<bool>,                              
 +  pub show_nsfw: Option<bool>,                          
 +  pub theme: Option<String>,                            
 +  pub default_sort_type: Option<i16>,                   
 +  pub default_listing_type: Option<i16>,                
 +  pub lang: Option<String>,                             
 +  pub show_avatars: Option<bool>,                       
 +  pub send_notifications_to_email: Option<bool>,        
 +  pub matrix_user_id: Option<Option<String>>,
 +}
 +
- /// A safe local user view, without settings, password, or email
- #[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
- #[table_name = "local_user"]
- pub struct LocalUserSafe {
-   pub id: i32,                                  
-   pub person_id: i32,                          
-   pub admin: bool,                              
-   pub matrix_user_id: Option<String>,           
- }
- 
 +/// A local user view that removes password encrypted
 +#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
 +#[table_name = "local_user"]
 +pub struct LocalUserSettings{
 +  pub id: i32,                                  
 +  pub person_id: i32,                          
 +  pub email: Option<String>,                    
 +  pub admin: bool,                              
 +  pub show_nsfw: bool,                          
 +  pub theme: String,                            
 +  pub default_sort_type: i16,                   
 +  pub default_listing_type: i16,                
 +  pub lang: String,                             
 +  pub show_avatars: bool,                       
 +  pub send_notifications_to_email: bool,        
 +  pub matrix_user_id: Option<String>,           
 +}
diff --cc crates/db_schema/src/source/password_reset_request.rs
index ce1a423f,f81f28ef..f03bcb03
--- a/crates/db_schema/src/source/password_reset_request.rs
+++ b/crates/db_schema/src/source/password_reset_request.rs
@@@ -4,9 -4,9 +4,9 @@@ use crate::schema::password_reset_reque
  #[table_name = "password_reset_request"]
  pub struct PasswordResetRequest {
    pub id: i32,
-   pub local_user_id: i32,
 -  pub user_id: i32,
    pub token_encrypted: String,
    pub published: chrono::NaiveDateTime,
++  pub local_user_id: i32,
  }
  
  #[derive(Insertable, AsChangeset)]
diff --cc crates/db_schema/src/source/person.rs
index cd072077,00000000..5971d930
mode 100644,000000..100644
--- a/crates/db_schema/src/source/person.rs
+++ b/crates/db_schema/src/source/person.rs
@@@ -1,154 -1,0 +1,151 @@@
 +use crate::{
 +  schema::{person, person_alias_1, person_alias_2},
-   Url,
++  DbUrl,
 +};
 +use serde::Serialize;
 +
 +#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
 +#[table_name = "person"]
 +pub struct Person {
 +  pub id: i32,                                  
 +  pub name: String,                             
 +  pub preferred_username: Option<String>,       
-   pub avatar: Option<String>,                   
++  pub avatar: Option<DbUrl>,                   
 +  pub banned: bool,                             
 +  pub published: chrono::NaiveDateTime,         
 +  pub updated: Option<chrono::NaiveDateTime>,   
-   pub actor_id: Url,                            
++  pub actor_id: DbUrl,                            
 +  pub bio: Option<String>,                      
 +  pub local: bool,                              
 +  pub private_key: Option<String>,              
 +  pub public_key: Option<String>,               
 +  pub last_refreshed_at: chrono::NaiveDateTime, 
-   pub banner: Option<String>,                   
++  pub banner: Option<DbUrl>,                   
 +  pub deleted: bool,                            
-   pub inbox_url: Url,                           
-   pub shared_inbox_url: Option<Url>,            
++  pub inbox_url: DbUrl,                           
++  pub shared_inbox_url: Option<DbUrl>,            
 +}
 +
 +/// A safe representation of user, without the sensitive info
 +#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
 +#[table_name = "person"]
 +pub struct PersonSafe {
 +  pub id: i32,                                  
 +  pub name: String,                             
 +  pub preferred_username: Option<String>,       
-   pub avatar: Option<String>,                   
++  pub avatar: Option<DbUrl>,                   
 +  pub banned: bool,                             
 +  pub published: chrono::NaiveDateTime,         
 +  pub updated: Option<chrono::NaiveDateTime>,   
-   pub actor_id: Url,                            
++  pub actor_id: DbUrl,                            
 +  pub bio: Option<String>,                      
 +  pub local: bool,                              
-   pub last_refreshed_at: chrono::NaiveDateTime, 
-   pub banner: Option<String>,                   
++  pub banner: Option<DbUrl>,                   
 +  pub deleted: bool,                            
-   pub inbox_url: Url,                           
-   pub shared_inbox_url: Option<Url>,            
++  pub inbox_url: DbUrl,                           
++  pub shared_inbox_url: Option<DbUrl>,            
 +}
 +
 +
 +#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
 +#[table_name = "person_alias_1"]
 +pub struct PersonAlias1 {
 +  pub id: i32,                                  
 +  pub name: String,                             
 +  pub preferred_username: Option<String>,       
-   pub avatar: Option<String>,                   
++  pub avatar: Option<DbUrl>,                   
 +  pub banned: bool,                             
 +  pub published: chrono::NaiveDateTime,         
 +  pub updated: Option<chrono::NaiveDateTime>,   
-   pub actor_id: Url,                            
++  pub actor_id: DbUrl,                            
 +  pub bio: Option<String>,                      
 +  pub local: bool,                              
 +  pub private_key: Option<String>,              
 +  pub public_key: Option<String>,               
 +  pub last_refreshed_at: chrono::NaiveDateTime, 
-   pub banner: Option<String>,                   
++  pub banner: Option<DbUrl>,                   
 +  pub deleted: bool,                            
-   pub inbox_url: Url,                           
-   pub shared_inbox_url: Option<Url>,            
++  pub inbox_url: DbUrl,                           
++  pub shared_inbox_url: Option<DbUrl>,            
 +}
 +
 +#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
 +#[table_name = "person_alias_1"]
 +pub struct PersonSafeAlias1 {
 +  pub id: i32,                                  
 +  pub name: String,                             
 +  pub preferred_username: Option<String>,       
-   pub avatar: Option<String>,                   
++  pub avatar: Option<DbUrl>,                   
 +  pub banned: bool,                             
 +  pub published: chrono::NaiveDateTime,         
 +  pub updated: Option<chrono::NaiveDateTime>,   
-   pub actor_id: Url,                            
++  pub actor_id: DbUrl,                            
 +  pub bio: Option<String>,                      
 +  pub local: bool,                              
-   pub last_refreshed_at: chrono::NaiveDateTime, 
-   pub banner: Option<String>,                   
++  pub banner: Option<DbUrl>,                   
 +  pub deleted: bool,                            
-   pub inbox_url: Url,                           
-   pub shared_inbox_url: Option<Url>,            
++  pub inbox_url: DbUrl,                           
++  pub shared_inbox_url: Option<DbUrl>,            
 +}
 +
 +#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
 +#[table_name = "person_alias_2"]
 +pub struct PersonAlias2 {
 +  pub id: i32,                                  
 +  pub name: String,                             
 +  pub preferred_username: Option<String>,       
-   pub avatar: Option<String>,                   
++  pub avatar: Option<DbUrl>,                   
 +  pub banned: bool,                             
 +  pub published: chrono::NaiveDateTime,         
 +  pub updated: Option<chrono::NaiveDateTime>,   
-   pub actor_id: Url,                            
++  pub actor_id: DbUrl,                            
 +  pub bio: Option<String>,                      
 +  pub local: bool,                              
 +  pub private_key: Option<String>,              
 +  pub public_key: Option<String>,               
 +  pub last_refreshed_at: chrono::NaiveDateTime, 
-   pub banner: Option<String>,                   
++  pub banner: Option<DbUrl>,                   
 +  pub deleted: bool,                            
-   pub inbox_url: Url,                           
-   pub shared_inbox_url: Option<Url>,            
++  pub inbox_url: DbUrl,                           
++  pub shared_inbox_url: Option<DbUrl>,            
 +}
 +
 +#[derive(Clone, Queryable, Identifiable, PartialEq, Debug, Serialize)]
 +#[table_name = "person_alias_1"]
 +pub struct PersonSafeAlias2 {
 +  pub id: i32,                                  
 +  pub name: String,                             
 +  pub preferred_username: Option<String>,       
-   pub avatar: Option<String>,                   
++  pub avatar: Option<DbUrl>,                   
 +  pub banned: bool,                             
 +  pub published: chrono::NaiveDateTime,         
 +  pub updated: Option<chrono::NaiveDateTime>,   
-   pub actor_id: Url,                            
++  pub actor_id: DbUrl,                            
 +  pub bio: Option<String>,                      
 +  pub local: bool,                              
-   pub last_refreshed_at: chrono::NaiveDateTime, 
-   pub banner: Option<String>,                   
++  pub banner: Option<DbUrl>,                   
 +  pub deleted: bool,                            
-   pub inbox_url: Url,                           
-   pub shared_inbox_url: Option<Url>,            
++  pub inbox_url: DbUrl,                           
++  pub shared_inbox_url: Option<DbUrl>,            
 +}
 +
 +#[derive(Insertable, AsChangeset, Clone)]
 +#[table_name = "person"]
 +pub struct PersonForm {
 +  pub name: String,                             
 +  pub preferred_username: Option<Option<String>>,
-   pub avatar: Option<Option<String>>,
++  pub avatar: Option<Option<DbUrl>>,
 +  pub banned: Option<bool>,                             
 +  pub published: Option<chrono::NaiveDateTime>,
 +  pub updated: Option<chrono::NaiveDateTime>,
-   pub actor_id: Option<Url>,                            
++  pub actor_id: Option<DbUrl>,                            
 +  pub bio: Option<Option<String>>,                      
 +  pub local: Option<bool>,                              
 +  pub private_key: Option<Option<String>>,              
 +  pub public_key: Option<Option<String>>,               
 +  pub last_refreshed_at: Option<chrono::NaiveDateTime>,
-   pub banner: Option<Option<String>>,
++  pub banner: Option<Option<DbUrl>>,
 +  pub deleted: Option<bool>,                            
-   pub inbox_url: Option<Url>,                           
-   pub shared_inbox_url: Option<Option<Url>>,
++  pub inbox_url: Option<DbUrl>,                           
++  pub shared_inbox_url: Option<Option<DbUrl>>,
 +}
diff --cc crates/db_views/src/comment_report_view.rs
index 2863f625,2863f625..eb10bffb
--- a/crates/db_views/src/comment_report_view.rs
+++ b/crates/db_views/src/comment_report_view.rs
@@@ -1,13 -1,13 +1,13 @@@
  use diesel::{result::Error, *};
  use lemmy_db_queries::{limit_and_offset, MaybeOptional, ToSafe, ViewToVec};
  use lemmy_db_schema::{
--  schema::{comment, comment_report, community, post, user_, user_alias_1, user_alias_2},
++  schema::{comment, comment_report, community, post, person, person_alias_1, person_alias_2},
    source::{
      comment::Comment,
      comment_report::CommentReport,
      community::{Community, CommunitySafe},
      post::Post,
--    user::{UserAlias1, UserAlias2, UserSafe, UserSafeAlias1, UserSafeAlias2, User_},
++    person::{PersonAlias1, PersonAlias2, PersonSafe, PersonSafeAlias1, PersonSafeAlias2, Person},
    },
  };
  use serde::Serialize;
@@@ -18,9 -18,9 +18,9 @@@ pub struct CommentReportView 
    pub comment: Comment,
    pub post: Post,
    pub community: CommunitySafe,
--  pub creator: UserSafe,
--  pub comment_creator: UserSafeAlias1,
--  pub resolver: Option<UserSafeAlias2>,
++  pub creator: PersonSafe,
++  pub comment_creator: PersonSafeAlias1,
++  pub resolver: Option<PersonSafeAlias2>,
  }
  
  type CommentReportViewTuple = (
@@@ -28,9 -28,9 +28,9 @@@
    Comment,
    Post,
    CommunitySafe,
--  UserSafe,
--  UserSafeAlias1,
--  Option<UserSafeAlias2>,
++  PersonSafe,
++  PersonSafeAlias1,
++  Option<PersonSafeAlias2>,
  );
  
  impl CommentReportView {
@@@ -44,19 -44,19 +44,19 @@@
          .inner_join(comment::table)
          .inner_join(post::table.on(comment::post_id.eq(post::id)))
          .inner_join(community::table.on(post::community_id.eq(community::id)))
--        .inner_join(user_::table.on(comment_report::creator_id.eq(user_::id)))
--        .inner_join(user_alias_1::table.on(post::creator_id.eq(user_alias_1::id)))
++        .inner_join(person::table.on(comment_report::creator_id.eq(person::id)))
++        .inner_join(person_alias_1::table.on(post::creator_id.eq(person_alias_1::id)))
          .left_join(
--          user_alias_2::table.on(comment_report::resolver_id.eq(user_alias_2::id.nullable())),
++          person_alias_2::table.on(comment_report::resolver_id.eq(person_alias_2::id.nullable())),
          )
          .select((
            comment_report::all_columns,
            comment::all_columns,
            post::all_columns,
            Community::safe_columns_tuple(),
--          User_::safe_columns_tuple(),
--          UserAlias1::safe_columns_tuple(),
--          UserAlias2::safe_columns_tuple().nullable(),
++          Person::safe_columns_tuple(),
++          PersonAlias1::safe_columns_tuple(),
++          PersonAlias2::safe_columns_tuple().nullable(),
          ))
          .first::<CommentReportViewTuple>(conn)?;
  
@@@ -75,7 -75,7 +75,7 @@@
    ///
    /// * `community_ids` - a Vec<i32> of community_ids to get a count for
    /// TODO this eq_any is a bad way to do this, would be better to join to communitymoderator
--  /// for a user id
++  /// for a person id
    pub fn get_report_count(conn: &PgConnection, community_ids: &[i32]) -> Result<i64, Error> {
      use diesel::dsl::*;
      comment_report::table
@@@ -135,19 -135,19 +135,19 @@@ impl<'a> CommentReportQueryBuilder<'a> 
        .inner_join(comment::table)
        .inner_join(post::table.on(comment::post_id.eq(post::id)))
        .inner_join(community::table.on(post::community_id.eq(community::id)))
--      .inner_join(user_::table.on(comment_report::creator_id.eq(user_::id)))
--      .inner_join(user_alias_1::table.on(post::creator_id.eq(user_alias_1::id)))
++      .inner_join(person::table.on(comment_report::creator_id.eq(person::id)))
++      .inner_join(person_alias_1::table.on(post::creator_id.eq(person_alias_1::id)))
        .left_join(
--        user_alias_2::table.on(comment_report::resolver_id.eq(user_alias_2::id.nullable())),
++        person_alias_2::table.on(comment_report::resolver_id.eq(person_alias_2::id.nullable())),
        )
        .select((
          comment_report::all_columns,
          comment::all_columns,
          post::all_columns,
          Community::safe_columns_tuple(),
--        User_::safe_columns_tuple(),
--        UserAlias1::safe_columns_tuple(),
--        UserAlias2::safe_columns_tuple().nullable(),
++        Person::safe_columns_tuple(),
++        PersonAlias1::safe_columns_tuple(),
++        PersonAlias2::safe_columns_tuple().nullable(),
        ))
        .into_boxed();
  
diff --cc crates/db_views/src/comment_view.rs
index 0db27794,56b22a7f..eb95a2cc
--- a/crates/db_views/src/comment_view.rs
+++ b/crates/db_views/src/comment_view.rs
@@@ -19,16 -19,16 +19,16 @@@ use lemmy_db_schema::
      comment_saved,
      community,
      community_follower,
--    community_user_ban,
++    community_person_ban,
      post,
--    user_,
--    user_alias_1,
++    person,
++    person_alias_1,
    },
    source::{
      comment::{Comment, CommentAlias1, CommentSaved},
 -    community::{Community, CommunityFollower, CommunitySafe, CommunityUserBan},
 +    community::{Community, CommunityFollower, CommunitySafe, CommunityPersonBan},
      post::Post,
--    user::{UserAlias1, UserSafe, UserSafeAlias1, User_},
++    person::{PersonAlias1, PersonSafe, PersonSafeAlias1, Person},
    },
  };
  use serde::Serialize;
@@@ -36,12 -36,12 +36,12 @@@
  #[derive(Debug, PartialEq, Serialize, Clone)]
  pub struct CommentView {
    pub comment: Comment,
--  pub creator: UserSafe,
--  pub recipient: Option<UserSafeAlias1>, // Left joins to comment and user
++  pub creator: PersonSafe,
++  pub recipient: Option<PersonSafeAlias1>, // Left joins to comment and person
    pub post: Post,
    pub community: CommunitySafe,
    pub counts: CommentAggregates,
--  pub creator_banned_from_community: bool, // Left Join to CommunityUserBan
++  pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
    pub subscribed: bool,                    // Left join to CommunityFollower
    pub saved: bool,                         // Left join to CommentSaved
    pub my_vote: Option<i16>,                // Left join to CommentLike
@@@ -49,9 -49,9 +49,9 @@@
  
  type CommentViewTuple = (
    Comment,
--  UserSafe,
++  PersonSafe,
    Option<CommentAlias1>,
--  Option<UserSafeAlias1>,
++  Option<PersonSafeAlias1>,
    Post,
    CommunitySafe,
    CommentAggregates,
@@@ -65,10 -65,10 +65,10 @@@ impl CommentView 
    pub fn read(
      conn: &PgConnection,
      comment_id: i32,
--    my_user_id: Option<i32>,
++    my_person_id: Option<i32>,
    ) -> Result<Self, Error> {
      // The left join below will return None in this case
--    let user_id_join = my_user_id.unwrap_or(-1);
++    let person_id_join = my_person_id.unwrap_or(-1);
  
      let (
        comment,
@@@ -84,59 -84,59 +84,59 @@@
        comment_like,
      ) = comment::table
        .find(comment_id)
--      .inner_join(user_::table)
++      .inner_join(person::table)
        // recipient here
        .left_join(comment_alias_1::table.on(comment_alias_1::id.nullable().eq(comment::parent_id)))
--      .left_join(user_alias_1::table.on(user_alias_1::id.eq(comment_alias_1::creator_id)))
++      .left_join(person_alias_1::table.on(person_alias_1::id.eq(comment_alias_1::creator_id)))
        .inner_join(post::table)
        .inner_join(community::table.on(post::community_id.eq(community::id)))
        .inner_join(comment_aggregates::table)
        .left_join(
--        community_user_ban::table.on(
++        community_person_ban::table.on(
            community::id
--            .eq(community_user_ban::community_id)
--            .and(community_user_ban::user_id.eq(comment::creator_id)),
++            .eq(community_person_ban::community_id)
++            .and(community_person_ban::person_id.eq(comment::creator_id)),
          ),
        )
        .left_join(
          community_follower::table.on(
            post::community_id
              .eq(community_follower::community_id)
--            .and(community_follower::user_id.eq(user_id_join)),
++            .and(community_follower::person_id.eq(person_id_join)),
          ),
        )
        .left_join(
          comment_saved::table.on(
            comment::id
              .eq(comment_saved::comment_id)
--            .and(comment_saved::user_id.eq(user_id_join)),
++            .and(comment_saved::person_id.eq(person_id_join)),
          ),
        )
        .left_join(
          comment_like::table.on(
            comment::id
              .eq(comment_like::comment_id)
--            .and(comment_like::user_id.eq(user_id_join)),
++            .and(comment_like::person_id.eq(person_id_join)),
          ),
        )
        .select((
          comment::all_columns,
--        User_::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
          comment_alias_1::all_columns.nullable(),
--        UserAlias1::safe_columns_tuple().nullable(),
++        PersonAlias1::safe_columns_tuple().nullable(),
          post::all_columns,
          Community::safe_columns_tuple(),
          comment_aggregates::all_columns,
--        community_user_ban::all_columns.nullable(),
++        community_person_ban::all_columns.nullable(),
          community_follower::all_columns.nullable(),
          comment_saved::all_columns.nullable(),
          comment_like::score.nullable(),
        ))
        .first::<CommentViewTuple>(conn)?;
  
--    // If a user is given, then my_vote, if None, should be 0, not null
--    // Necessary to differentiate between other user's votes
--    let my_vote = if my_user_id.is_some() && comment_like.is_none() {
++    // If a person is given, then my_vote, if None, should be 0, not null
++    // Necessary to differentiate between other person's votes
++    let my_vote = if my_person_id.is_some() && comment_like.is_none() {
        Some(0)
      } else {
        comment_like
@@@ -156,7 -156,7 +156,7 @@@
      })
    }
  
--  /// Gets the recipient user id.
++  /// Gets the recipient person id.
    /// If there is no parent comment, its the post creator
    pub fn get_recipient_id(&self) -> i32 {
      match &self.recipient {
@@@ -175,7 -175,7 +175,7 @@@ pub struct CommentQueryBuilder<'a> 
    post_id: Option<i32>,
    creator_id: Option<i32>,
    recipient_id: Option<i32>,
--  my_user_id: Option<i32>,
++  my_person_id: Option<i32>,
    search_term: Option<String>,
    saved_only: bool,
    unread_only: bool,
@@@ -194,7 -194,7 +194,7 @@@ impl<'a> CommentQueryBuilder<'a> 
        post_id: None,
        creator_id: None,
        recipient_id: None,
--      my_user_id: None,
++      my_person_id: None,
        search_term: None,
        saved_only: false,
        unread_only: false,
@@@ -233,8 -233,8 +233,8 @@@
      self
    }
  
--  pub fn my_user_id<T: MaybeOptional<i32>>(mut self, my_user_id: T) -> Self {
--    self.my_user_id = my_user_id.get_optional();
++  pub fn my_person_id<T: MaybeOptional<i32>>(mut self, my_person_id: T) -> Self {
++    self.my_person_id = my_person_id.get_optional();
      self
    }
  
@@@ -272,53 -272,53 +272,53 @@@
      use diesel::dsl::*;
  
      // The left join below will return None in this case
--    let user_id_join = self.my_user_id.unwrap_or(-1);
++    let person_id_join = self.my_person_id.unwrap_or(-1);
  
      let mut query = comment::table
--      .inner_join(user_::table)
++      .inner_join(person::table)
        // recipient here
        .left_join(comment_alias_1::table.on(comment_alias_1::id.nullable().eq(comment::parent_id)))
--      .left_join(user_alias_1::table.on(user_alias_1::id.eq(comment_alias_1::creator_id)))
++      .left_join(person_alias_1::table.on(person_alias_1::id.eq(comment_alias_1::creator_id)))
        .inner_join(post::table)
        .inner_join(community::table.on(post::community_id.eq(community::id)))
        .inner_join(comment_aggregates::table)
        .left_join(
--        community_user_ban::table.on(
++        community_person_ban::table.on(
            community::id
--            .eq(community_user_ban::community_id)
--            .and(community_user_ban::user_id.eq(comment::creator_id)),
++            .eq(community_person_ban::community_id)
++            .and(community_person_ban::person_id.eq(comment::creator_id)),
          ),
        )
        .left_join(
          community_follower::table.on(
            post::community_id
              .eq(community_follower::community_id)
--            .and(community_follower::user_id.eq(user_id_join)),
++            .and(community_follower::person_id.eq(person_id_join)),
          ),
        )
        .left_join(
          comment_saved::table.on(
            comment::id
              .eq(comment_saved::comment_id)
--            .and(comment_saved::user_id.eq(user_id_join)),
++            .and(comment_saved::person_id.eq(person_id_join)),
          ),
        )
        .left_join(
          comment_like::table.on(
            comment::id
              .eq(comment_like::comment_id)
--            .and(comment_like::user_id.eq(user_id_join)),
++            .and(comment_like::person_id.eq(person_id_join)),
          ),
        )
        .select((
          comment::all_columns,
--        User_::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
          comment_alias_1::all_columns.nullable(),
--        UserAlias1::safe_columns_tuple().nullable(),
++        PersonAlias1::safe_columns_tuple().nullable(),
          post::all_columns,
          Community::safe_columns_tuple(),
          comment_aggregates::all_columns,
--        community_user_ban::all_columns.nullable(),
++        community_person_ban::all_columns.nullable(),
          community_follower::all_columns.nullable(),
          comment_saved::all_columns.nullable(),
          comment_like::score.nullable(),
@@@ -329,7 -329,7 +329,7 @@@
      if let Some(recipient_id) = self.recipient_id {
        query = query
          // TODO needs lots of testing
--        .filter(user_alias_1::id.eq(recipient_id)) // Gets the comment replies
++        .filter(person_alias_1::id.eq(recipient_id)) // Gets the comment replies
          .or_filter(
            comment::parent_id
              .is_null()
@@@ -367,7 -367,7 +367,7 @@@
  
      query = match self.listing_type {
        // ListingType::Subscribed => query.filter(community_follower::subscribed.eq(true)),
--      ListingType::Subscribed => query.filter(community_follower::user_id.is_not_null()), // TODO could be this: and(community_follower::user_id.eq(user_id_join)),
++      ListingType::Subscribed => query.filter(community_follower::person_id.is_not_null()), // TODO could be this: and(community_follower::user_id.eq(user_id_join)),
        ListingType::Local => query.filter(community::local.eq(true)),
        _ => query,
      };
@@@ -439,37 -439,39 +439,27 @@@ mod tests 
      establish_unpooled_connection,
      Crud,
      Likeable,
--    ListingType,
--    SortType,
    };
--  use lemmy_db_schema::source::{comment::*, community::*, post::*, user::*};
++  use lemmy_db_schema::source::{comment::*, community::*, post::*, person::*};
+   use serial_test::serial;
  
    #[test]
+   #[serial]
    fn test_crud() {
      let conn = establish_unpooled_connection();
  
--    let new_user = UserForm {
--      name: "timmy".into(),
++    let new_person = PersonForm {
++      name: "thommy".into(),
        preferred_username: None,
--      password_encrypted: "nope".into(),
--      email: None,
--      matrix_user_id: None,
        avatar: None,
        banner: None,
--      admin: false,
--      banned: Some(false),
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
--      show_nsfw: false,
--      theme: "browser".into(),
--      default_sort_type: SortType::Hot as i16,
--      default_listing_type: ListingType::Subscribed as i16,
--      lang: "browser".into(),
--      show_avatars: true,
--      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
@@@ -477,13 -479,13 +467,13 @@@
        shared_inbox_url: None,
      };
  
--    let inserted_user = User_::create(&conn, &new_user).unwrap();
++    let inserted_person = Person::create(&conn, &new_person).unwrap();
  
      let new_community = CommunityForm {
        name: "test community 5".to_string(),
        title: "nada".to_owned(),
        description: None,
--      creator_id: inserted_user.id,
++      creator_id: inserted_person.id,
        removed: None,
        deleted: None,
        updated: None,
@@@ -505,7 -507,7 +495,7 @@@
  
      let new_post = PostForm {
        name: "A test post 2".into(),
--      creator_id: inserted_user.id,
++      creator_id: inserted_person.id,
        url: None,
        body: None,
        community_id: inserted_community.id,
@@@ -528,7 -530,7 +518,7 @@@
  
      let comment_form = CommentForm {
        content: "A test comment 32".into(),
--      creator_id: inserted_user.id,
++      creator_id: inserted_person.id,
        post_id: inserted_post.id,
        parent_id: None,
        removed: None,
@@@ -545,7 -547,7 +535,7 @@@
      let comment_like_form = CommentLikeForm {
        comment_id: inserted_comment.id,
        post_id: inserted_post.id,
-       person_id: inserted_user.id,
 -      user_id: inserted_user.id,
++      person_id: inserted_person.id,
        score: 1,
      };
  
@@@ -553,7 -555,7 +543,7 @@@
  
      let agg = CommentAggregates::read(&conn, inserted_comment.id).unwrap();
  
--    let expected_comment_view_no_user = CommentView {
++    let expected_comment_view_no_person = CommentView {
        creator_banned_from_community: false,
        my_vote: None,
        subscribed: false,
@@@ -561,7 -563,7 +551,7 @@@
        comment: Comment {
          id: inserted_comment.id,
          content: "A test comment 32".into(),
--        creator_id: inserted_user.id,
++        creator_id: inserted_person.id,
          post_id: inserted_post.id,
          parent_id: None,
          removed: false,
@@@ -572,29 -574,29 +562,27 @@@
          updated: None,
          local: true,
        },
--      creator: UserSafe {
--        id: inserted_user.id,
++      creator: PersonSafe {
++        id: inserted_person.id,
          name: "timmy".into(),
          preferred_username: None,
--        published: inserted_user.published,
++        published: inserted_person.published,
          avatar: None,
--        actor_id: inserted_user.actor_id.to_owned(),
++        actor_id: inserted_person.actor_id.to_owned(),
          local: true,
          banned: false,
          deleted: false,
          bio: None,
          banner: None,
--        admin: false,
          updated: None,
--        matrix_user_id: None,
--        inbox_url: inserted_user.inbox_url.to_owned(),
++        inbox_url: inserted_person.inbox_url.to_owned(),
          shared_inbox_url: None,
        },
        recipient: None,
        post: Post {
          id: inserted_post.id,
          name: inserted_post.name.to_owned(),
--        creator_id: inserted_user.id,
++        creator_id: inserted_person.id,
          url: None,
          body: None,
          published: inserted_post.published,
@@@ -623,7 -625,7 +611,7 @@@
          local: true,
          title: "nada".to_owned(),
          description: None,
--        creator_id: inserted_user.id,
++        creator_id: inserted_person.id,
          updated: None,
          banner: None,
          published: inserted_community.published,
@@@ -638,30 -640,30 +626,30 @@@
        },
      };
  
--    let mut expected_comment_view_with_user = expected_comment_view_no_user.to_owned();
--    expected_comment_view_with_user.my_vote = Some(1);
++    let mut expected_comment_view_with_person = expected_comment_view_no_person.to_owned();
++    expected_comment_view_with_person.my_vote = Some(1);
  
--    let read_comment_views_no_user = CommentQueryBuilder::create(&conn)
++    let read_comment_views_no_person = CommentQueryBuilder::create(&conn)
        .post_id(inserted_post.id)
        .list()
        .unwrap();
  
--    let read_comment_views_with_user = CommentQueryBuilder::create(&conn)
++    let read_comment_views_with_person = CommentQueryBuilder::create(&conn)
        .post_id(inserted_post.id)
--      .my_user_id(inserted_user.id)
++      .my_person_id(inserted_person.id)
        .list()
        .unwrap();
  
--    let like_removed = CommentLike::remove(&conn, inserted_user.id, inserted_comment.id).unwrap();
++    let like_removed = CommentLike::remove(&conn, inserted_person.id, inserted_comment.id).unwrap();
      let num_deleted = Comment::delete(&conn, inserted_comment.id).unwrap();
      Post::delete(&conn, inserted_post.id).unwrap();
      Community::delete(&conn, inserted_community.id).unwrap();
--    User_::delete(&conn, inserted_user.id).unwrap();
++    Person::delete(&conn, inserted_person.id).unwrap();
  
--    assert_eq!(expected_comment_view_no_user, read_comment_views_no_user[0]);
++    assert_eq!(expected_comment_view_no_person, read_comment_views_no_person[0]);
      assert_eq!(
--      expected_comment_view_with_user,
--      read_comment_views_with_user[0]
++      expected_comment_view_with_person,
++      read_comment_views_with_person[0]
      );
      assert_eq!(1, num_deleted);
      assert_eq!(1, like_removed);
diff --cc crates/db_views/src/lib.rs
index b46ec5a4,a9369ed2..d37a1d3e
--- a/crates/db_views/src/lib.rs
+++ b/crates/db_views/src/lib.rs
@@@ -4,3 -7,3 +7,4 @@@ pub mod post_report_view
  pub mod post_view;
  pub mod private_message_view;
  pub mod site_view;
++pub mod local_user_view;
diff --cc crates/db_views/src/local_user_view.rs
index 00000000,00000000..384bf900
new file mode 100644
--- /dev/null
+++ b/crates/db_views/src/local_user_view.rs
@@@ -1,0 -1,0 +1,78 @@@
++use diesel::{result::Error, *};
++use lemmy_db_queries::{
++  aggregates::person_aggregates::PersonAggregates,
++  ToSafe,
++  ToSafeSettings,
++};
++use lemmy_db_schema::{
++  schema::{person, person_aggregates, local_user},
++  source::person::{PersonSafe, Person},
++  source::local_user::{LocalUser, LocalUserSettings},
++};
++use serde::Serialize;
++
++#[derive(Debug, Serialize, Clone)]
++pub struct LocalUserView {
++  pub person: Person,
++  pub counts: PersonAggregates,
++  pub local_user: LocalUser,
++}
++
++type LocalUserViewTuple = (Person, PersonAggregates, LocalUser);
++
++impl LocalUserView {
++  pub fn read(conn: &PgConnection, person_id: i32) -> Result<Self, Error> {
++    let (person, counts, local_user) = person::table
++      .find(person_id)
++      .inner_join(person_aggregates::table)
++      .inner_join(local_user::table)
++      .select((person::all_columns, person_aggregates::all_columns, local_user::all_columns))
++      .first::<LocalUserViewTuple>(conn)?;
++    Ok(Self { person, counts, local_user })
++  }
++
++  // TODO check where this is used
++  pub fn read_from_name(conn: &PgConnection, name: &str) -> Result<Self, Error> {
++    let (person, counts, local_user) = person::table
++      .filter(person::name.eq(name))
++      .inner_join(person_aggregates::table)
++      .inner_join(local_user::table)
++      .select((person::all_columns, person_aggregates::all_columns, local_user::all_columns))
++      .first::<LocalUserViewTuple>(conn)?;
++    Ok(Self { person, counts, local_user })
++  }
++
++  pub fn find_by_email_or_name(
++    conn: &PgConnection,
++    name_or_email: &str,
++  ) -> Result<Self, Error> {
++    let (person, counts, local_user) = person::table
++      .inner_join(person_aggregates::table)
++      .inner_join(local_user::table)
++      .filter(person::name.ilike(name_or_email).or(local_user::email.ilike(name_or_email)))
++      .select((person::all_columns, person_aggregates::all_columns, local_user::all_columns))
++      .first::<LocalUserViewTuple>(conn)?;
++    Ok(Self { person, counts, local_user })
++  }
++}
++
++#[derive(Debug, Serialize, Clone)]
++pub struct LocalUserSettingsView {
++  pub person: PersonSafe,
++  pub counts: PersonAggregates,
++  pub local_user: LocalUserSettings,
++}
++
++type LocalUserSettingsViewTuple = (PersonSafe, PersonAggregates, LocalUserSettings);
++
++impl LocalUserSettingsView {
++  pub fn read(conn: &PgConnection, person_id: i32) -> Result<Self, Error> {
++    let (person, counts, local_user) = person::table
++      .find(person_id)
++      .inner_join(person_aggregates::table)
++      .inner_join(local_user::table)
++      .select((Person::safe_columns_tuple(), person_aggregates::all_columns, LocalUser::safe_settings_columns_tuple()))
++      .first::<LocalUserSettingsViewTuple>(conn)?;
++    Ok(Self { person, counts, local_user })
++  }
++}
diff --cc crates/db_views/src/post_report_view.rs
index 741162e3,741162e3..192a3d7b
--- a/crates/db_views/src/post_report_view.rs
+++ b/crates/db_views/src/post_report_view.rs
@@@ -1,12 -1,12 +1,12 @@@
  use diesel::{result::Error, *};
  use lemmy_db_queries::{limit_and_offset, MaybeOptional, ToSafe, ViewToVec};
  use lemmy_db_schema::{
--  schema::{community, post, post_report, user_, user_alias_1, user_alias_2},
++  schema::{community, post, post_report, person, person_alias_1, person_alias_2},
    source::{
      community::{Community, CommunitySafe},
      post::Post,
      post_report::PostReport,
--    user::{UserAlias1, UserAlias2, UserSafe, UserSafeAlias1, UserSafeAlias2, User_},
++    person::{PersonAlias1, PersonAlias2, PersonSafe, PersonSafeAlias1, PersonSafeAlias2, Person},
    },
  };
  use serde::Serialize;
@@@ -16,18 -16,18 +16,18 @@@ pub struct PostReportView 
    pub post_report: PostReport,
    pub post: Post,
    pub community: CommunitySafe,
--  pub creator: UserSafe,
--  pub post_creator: UserSafeAlias1,
--  pub resolver: Option<UserSafeAlias2>,
++  pub creator: PersonSafe,
++  pub post_creator: PersonSafeAlias1,
++  pub resolver: Option<PersonSafeAlias2>,
  }
  
  type PostReportViewTuple = (
    PostReport,
    Post,
    CommunitySafe,
--  UserSafe,
--  UserSafeAlias1,
--  Option<UserSafeAlias2>,
++  PersonSafe,
++  PersonSafeAlias1,
++  Option<PersonSafeAlias2>,
  );
  
  impl PostReportView {
@@@ -39,16 -39,16 +39,16 @@@
        .find(report_id)
        .inner_join(post::table)
        .inner_join(community::table.on(post::community_id.eq(community::id)))
--      .inner_join(user_::table.on(post_report::creator_id.eq(user_::id)))
--      .inner_join(user_alias_1::table.on(post::creator_id.eq(user_alias_1::id)))
--      .left_join(user_alias_2::table.on(post_report::resolver_id.eq(user_alias_2::id.nullable())))
++      .inner_join(person::table.on(post_report::creator_id.eq(person::id)))
++      .inner_join(person_alias_1::table.on(post::creator_id.eq(person_alias_1::id)))
++      .left_join(person_alias_2::table.on(post_report::resolver_id.eq(person_alias_2::id.nullable())))
        .select((
          post_report::all_columns,
          post::all_columns,
          Community::safe_columns_tuple(),
--        User_::safe_columns_tuple(),
--        UserAlias1::safe_columns_tuple(),
--        UserAlias2::safe_columns_tuple().nullable(),
++        Person::safe_columns_tuple(),
++        PersonAlias1::safe_columns_tuple(),
++        PersonAlias2::safe_columns_tuple().nullable(),
        ))
        .first::<PostReportViewTuple>(conn)?;
  
@@@ -66,7 -66,7 +66,7 @@@
    ///
    /// * `community_ids` - a Vec<i32> of community_ids to get a count for
    /// TODO this eq_any is a bad way to do this, would be better to join to communitymoderator
--  /// for a user id
++  /// for a person id
    pub fn get_report_count(conn: &PgConnection, community_ids: &[i32]) -> Result<i64, Error> {
      use diesel::dsl::*;
      post_report::table
@@@ -124,16 -124,16 +124,16 @@@ impl<'a> PostReportQueryBuilder<'a> 
      let mut query = post_report::table
        .inner_join(post::table)
        .inner_join(community::table.on(post::community_id.eq(community::id)))
--      .inner_join(user_::table.on(post_report::creator_id.eq(user_::id)))
--      .inner_join(user_alias_1::table.on(post::creator_id.eq(user_alias_1::id)))
--      .left_join(user_alias_2::table.on(post_report::resolver_id.eq(user_alias_2::id.nullable())))
++      .inner_join(person::table.on(post_report::creator_id.eq(person::id)))
++      .inner_join(person_alias_1::table.on(post::creator_id.eq(person_alias_1::id)))
++      .left_join(person_alias_2::table.on(post_report::resolver_id.eq(person_alias_2::id.nullable())))
        .select((
          post_report::all_columns,
          post::all_columns,
          Community::safe_columns_tuple(),
--        User_::safe_columns_tuple(),
--        UserAlias1::safe_columns_tuple(),
--        UserAlias2::safe_columns_tuple().nullable(),
++        Person::safe_columns_tuple(),
++        PersonAlias1::safe_columns_tuple(),
++        PersonAlias2::safe_columns_tuple().nullable(),
        ))
        .into_boxed();
  
diff --cc crates/db_views/src/post_view.rs
index f56c2f9b,e7b48827..4aca3328
--- a/crates/db_views/src/post_view.rs
+++ b/crates/db_views/src/post_view.rs
@@@ -14,18 -14,18 +14,18 @@@ use lemmy_db_schema::
    schema::{
      community,
      community_follower,
--    community_user_ban,
++    community_person_ban,
      post,
      post_aggregates,
      post_like,
      post_read,
      post_saved,
--    user_,
++    person,
    },
    source::{
 -    community::{Community, CommunityFollower, CommunitySafe, CommunityUserBan},
 +    community::{Community, CommunityFollower, CommunitySafe, CommunityPersonBan},
      post::{Post, PostRead, PostSaved},
--    user::{UserSafe, User_},
++    person::{PersonSafe, Person},
    },
  };
  use log::debug;
@@@ -34,9 -34,9 +34,9 @@@ use serde::Serialize
  #[derive(Debug, PartialEq, Serialize, Clone)]
  pub struct PostView {
    pub post: Post,
--  pub creator: UserSafe,
++  pub creator: PersonSafe,
    pub community: CommunitySafe,
--  pub creator_banned_from_community: bool, // Left Join to CommunityUserBan
++  pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
    pub counts: PostAggregates,
    pub subscribed: bool,     // Left join to CommunityFollower
    pub saved: bool,          // Left join to PostSaved
@@@ -46,9 -46,9 +46,9 @@@
  
  type PostViewTuple = (
    Post,
--  UserSafe,
++  PersonSafe,
    CommunitySafe,
 -  Option<CommunityUserBan>,
 +  Option<CommunityPersonBan>,
    PostAggregates,
    Option<CommunityFollower>,
    Option<PostSaved>,
@@@ -57,9 -57,9 +57,9 @@@
  );
  
  impl PostView {
--  pub fn read(conn: &PgConnection, post_id: i32, my_user_id: Option<i32>) -> Result<Self, Error> {
++  pub fn read(conn: &PgConnection, post_id: i32, my_person_id: Option<i32>) -> Result<Self, Error> {
      // The left join below will return None in this case
--    let user_id_join = my_user_id.unwrap_or(-1);
++    let person_id_join = my_person_id.unwrap_or(-1);
  
      let (
        post,
@@@ -73,13 -73,13 +73,13 @@@
        post_like,
      ) = post::table
        .find(post_id)
--      .inner_join(user_::table)
++      .inner_join(person::table)
        .inner_join(community::table)
        .left_join(
--        community_user_ban::table.on(
++        community_person_ban::table.on(
            post::community_id
--            .eq(community_user_ban::community_id)
--            .and(community_user_ban::user_id.eq(post::creator_id)),
++            .eq(community_person_ban::community_id)
++            .and(community_person_ban::person_id.eq(post::creator_id)),
          ),
        )
        .inner_join(post_aggregates::table)
@@@ -87,35 -87,35 +87,35 @@@
          community_follower::table.on(
            post::community_id
              .eq(community_follower::community_id)
--            .and(community_follower::user_id.eq(user_id_join)),
++            .and(community_follower::person_id.eq(person_id_join)),
          ),
        )
        .left_join(
          post_saved::table.on(
            post::id
              .eq(post_saved::post_id)
--            .and(post_saved::user_id.eq(user_id_join)),
++            .and(post_saved::person_id.eq(person_id_join)),
          ),
        )
        .left_join(
          post_read::table.on(
            post::id
              .eq(post_read::post_id)
--            .and(post_read::user_id.eq(user_id_join)),
++            .and(post_read::person_id.eq(person_id_join)),
          ),
        )
        .left_join(
          post_like::table.on(
            post::id
              .eq(post_like::post_id)
--            .and(post_like::user_id.eq(user_id_join)),
++            .and(post_like::person_id.eq(person_id_join)),
          ),
        )
        .select((
          post::all_columns,
--        User_::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
          Community::safe_columns_tuple(),
--        community_user_ban::all_columns.nullable(),
++        community_person_ban::all_columns.nullable(),
          post_aggregates::all_columns,
          community_follower::all_columns.nullable(),
          post_saved::all_columns.nullable(),
@@@ -124,9 -124,9 +124,9 @@@
        ))
        .first::<PostViewTuple>(conn)?;
  
--    // If a user is given, then my_vote, if None, should be 0, not null
--    // Necessary to differentiate between other user's votes
--    let my_vote = if my_user_id.is_some() && post_like.is_none() {
++    // If a person is given, then my_vote, if None, should be 0, not null
++    // Necessary to differentiate between other person's votes
++    let my_vote = if my_person_id.is_some() && post_like.is_none() {
        Some(0)
      } else {
        post_like
@@@ -153,7 -153,7 +153,7 @@@ pub struct PostQueryBuilder<'a> 
    creator_id: Option<i32>,
    community_id: Option<i32>,
    community_name: Option<String>,
--  my_user_id: Option<i32>,
++  my_person_id: Option<i32>,
    search_term: Option<String>,
    url_search: Option<String>,
    show_nsfw: bool,
@@@ -172,7 -172,7 +172,7 @@@ impl<'a> PostQueryBuilder<'a> 
        creator_id: None,
        community_id: None,
        community_name: None,
--      my_user_id: None,
++      my_person_id: None,
        search_term: None,
        url_search: None,
        show_nsfw: true,
@@@ -198,8 -198,8 +198,8 @@@
      self
    }
  
--  pub fn my_user_id<T: MaybeOptional<i32>>(mut self, my_user_id: T) -> Self {
--    self.my_user_id = my_user_id.get_optional();
++  pub fn my_person_id<T: MaybeOptional<i32>>(mut self, my_person_id: T) -> Self {
++    self.my_person_id = my_person_id.get_optional();
      self
    }
  
@@@ -247,16 -247,16 +247,16 @@@
      use diesel::dsl::*;
  
      // The left join below will return None in this case
--    let user_id_join = self.my_user_id.unwrap_or(-1);
++    let person_id_join = self.my_person_id.unwrap_or(-1);
  
      let mut query = post::table
--      .inner_join(user_::table)
++      .inner_join(person::table)
        .inner_join(community::table)
        .left_join(
--        community_user_ban::table.on(
++        community_person_ban::table.on(
            post::community_id
--            .eq(community_user_ban::community_id)
--            .and(community_user_ban::user_id.eq(community::creator_id)),
++            .eq(community_person_ban::community_id)
++            .and(community_person_ban::person_id.eq(community::creator_id)),
          ),
        )
        .inner_join(post_aggregates::table)
@@@ -264,35 -264,35 +264,35 @@@
          community_follower::table.on(
            post::community_id
              .eq(community_follower::community_id)
--            .and(community_follower::user_id.eq(user_id_join)),
++            .and(community_follower::person_id.eq(person_id_join)),
          ),
        )
        .left_join(
          post_saved::table.on(
            post::id
              .eq(post_saved::post_id)
--            .and(post_saved::user_id.eq(user_id_join)),
++            .and(post_saved::person_id.eq(person_id_join)),
          ),
        )
        .left_join(
          post_read::table.on(
            post::id
              .eq(post_read::post_id)
--            .and(post_read::user_id.eq(user_id_join)),
++            .and(post_read::person_id.eq(person_id_join)),
          ),
        )
        .left_join(
          post_like::table.on(
            post::id
              .eq(post_like::post_id)
--            .and(post_like::user_id.eq(user_id_join)),
++            .and(post_like::person_id.eq(person_id_join)),
          ),
        )
        .select((
          post::all_columns,
--        User_::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
          Community::safe_columns_tuple(),
--        community_user_ban::all_columns.nullable(),
++        community_person_ban::all_columns.nullable(),
          post_aggregates::all_columns,
          community_follower::all_columns.nullable(),
          post_saved::all_columns.nullable(),
@@@ -302,7 -302,7 +302,7 @@@
        .into_boxed();
  
      query = match self.listing_type {
--      ListingType::Subscribed => query.filter(community_follower::user_id.is_not_null()), // TODO could be this: and(community_follower::user_id.eq(user_id_join)),
++      ListingType::Subscribed => query.filter(community_follower::person_id.is_not_null()), // TODO could be this: and(community_follower::person_id.eq(person_id_join)),
        ListingType::Local => query.filter(community::local.eq(true)),
        _ => query,
      };
@@@ -333,7 -333,7 +333,7 @@@
        );
      }
  
--    // If its for a specific user, show the removed / deleted
++    // If its for a specific person, show the removed / deleted
      if let Some(creator_id) = self.creator_id {
        query = query.filter(post::creator_id.eq(creator_id));
      }
@@@ -433,38 -433,40 +433,30 @@@ mod tests 
      ListingType,
      SortType,
    };
--  use lemmy_db_schema::source::{community::*, post::*, user::*};
++  use lemmy_db_schema::source::{community::*, post::*, person::*};
+   use serial_test::serial;
  
    #[test]
+   #[serial]
    fn test_crud() {
      let conn = establish_unpooled_connection();
  
--    let user_name = "tegan".to_string();
++    let person_name = "tegan".to_string();
      let community_name = "test_community_3".to_string();
      let post_name = "test post 3".to_string();
  
--    let new_user = UserForm {
--      name: user_name.to_owned(),
++    let new_person = PersonForm {
++      name: person_name.to_owned(),
        preferred_username: None,
--      password_encrypted: "nope".into(),
--      email: None,
--      matrix_user_id: None,
        avatar: None,
        banner: None,
++      banned: None,
++      deleted: None,
        published: None,
        updated: None,
--      admin: false,
--      banned: Some(false),
--      show_nsfw: false,
--      theme: "browser".into(),
--      default_sort_type: SortType::Hot as i16,
--      default_listing_type: ListingType::Subscribed as i16,
--      lang: "browser".into(),
--      show_avatars: true,
--      send_notifications_to_email: false,
        actor_id: None,
        bio: None,
--      local: true,
++      local: None,
        private_key: None,
        public_key: None,
        last_refreshed_at: None,
@@@ -472,13 -474,13 +464,13 @@@
        shared_inbox_url: None,
      };
  
--    let inserted_user = User_::create(&conn, &new_user).unwrap();
++    let inserted_person = Person::create(&conn, &new_person).unwrap();
  
      let new_community = CommunityForm {
        name: community_name.to_owned(),
        title: "nada".to_owned(),
        description: None,
--      creator_id: inserted_user.id,
++      creator_id: inserted_person.id,
        removed: None,
        deleted: None,
        updated: None,
@@@ -502,7 -504,7 +494,7 @@@
        name: post_name.to_owned(),
        url: None,
        body: None,
--      creator_id: inserted_user.id,
++      creator_id: inserted_person.id,
        community_id: inserted_community.id,
        removed: None,
        deleted: None,
@@@ -523,7 -525,7 +515,7 @@@
  
      let post_like_form = PostLikeForm {
        post_id: inserted_post.id,
-       person_id: inserted_user.id,
 -      user_id: inserted_user.id,
++      person_id: inserted_person.id,
        score: 1,
      };
  
@@@ -532,38 -534,38 +524,38 @@@
      let expected_post_like = PostLike {
        id: inserted_post_like.id,
        post_id: inserted_post.id,
-       person_id: inserted_user.id,
 -      user_id: inserted_user.id,
++      person_id: inserted_person.id,
        published: inserted_post_like.published,
        score: 1,
      };
  
--    let read_post_listings_with_user = PostQueryBuilder::create(&conn)
++    let read_post_listings_with_person = PostQueryBuilder::create(&conn)
        .listing_type(&ListingType::Community)
        .sort(&SortType::New)
        .community_id(inserted_community.id)
--      .my_user_id(inserted_user.id)
++      .my_person_id(inserted_person.id)
        .list()
        .unwrap();
  
--    let read_post_listings_no_user = PostQueryBuilder::create(&conn)
++    let read_post_listings_no_person = PostQueryBuilder::create(&conn)
        .listing_type(&ListingType::Community)
        .sort(&SortType::New)
        .community_id(inserted_community.id)
        .list()
        .unwrap();
  
--    let read_post_listing_no_user = PostView::read(&conn, inserted_post.id, None).unwrap();
--    let read_post_listing_with_user =
--      PostView::read(&conn, inserted_post.id, Some(inserted_user.id)).unwrap();
++    let read_post_listing_no_person = PostView::read(&conn, inserted_post.id, None).unwrap();
++    let read_post_listing_with_person =
++      PostView::read(&conn, inserted_post.id, Some(inserted_person.id)).unwrap();
  
      let agg = PostAggregates::read(&conn, inserted_post.id).unwrap();
  
--    // the non user version
--    let expected_post_listing_no_user = PostView {
++    // the non person version
++    let expected_post_listing_no_person = PostView {
        post: Post {
          id: inserted_post.id,
          name: post_name,
--        creator_id: inserted_user.id,
++        creator_id: inserted_person.id,
          url: None,
          body: None,
          published: inserted_post.published,
@@@ -582,22 -584,22 +574,20 @@@
          local: true,
        },
        my_vote: None,
--      creator: UserSafe {
--        id: inserted_user.id,
--        name: user_name,
++      creator: PersonSafe {
++        id: inserted_person.id,
++        name: person_name,
          preferred_username: None,
--        published: inserted_user.published,
++        published: inserted_person.published,
          avatar: None,
--        actor_id: inserted_user.actor_id.to_owned(),
++        actor_id: inserted_person.actor_id.to_owned(),
          local: true,
          banned: false,
          deleted: false,
          bio: None,
          banner: None,
--        admin: false,
          updated: None,
--        matrix_user_id: None,
--        inbox_url: inserted_user.inbox_url.to_owned(),
++        inbox_url: inserted_person.inbox_url.to_owned(),
          shared_inbox_url: None,
        },
        creator_banned_from_community: false,
@@@ -612,7 -614,7 +602,7 @@@
          local: true,
          title: "nada".to_owned(),
          description: None,
--        creator_id: inserted_user.id,
++        creator_id: inserted_person.id,
          updated: None,
          banner: None,
          published: inserted_community.published,
@@@ -635,26 -637,26 +625,26 @@@
      };
  
      // TODO More needs to be added here
--    let mut expected_post_listing_with_user = expected_post_listing_no_user.to_owned();
++    let mut expected_post_listing_with_user = expected_post_listing_no_person.to_owned();
      expected_post_listing_with_user.my_vote = Some(1);
  
--    let like_removed = PostLike::remove(&conn, inserted_user.id, inserted_post.id).unwrap();
++    let like_removed = PostLike::remove(&conn, inserted_person.id, inserted_post.id).unwrap();
      let num_deleted = Post::delete(&conn, inserted_post.id).unwrap();
      Community::delete(&conn, inserted_community.id).unwrap();
--    User_::delete(&conn, inserted_user.id).unwrap();
++    Person::delete(&conn, inserted_person.id).unwrap();
  
      // The with user
      assert_eq!(
        expected_post_listing_with_user,
--      read_post_listings_with_user[0]
++      read_post_listings_with_person[0]
      );
--    assert_eq!(expected_post_listing_with_user, read_post_listing_with_user);
--    assert_eq!(1, read_post_listings_with_user.len());
++    assert_eq!(expected_post_listing_with_user, read_post_listing_with_person);
++    assert_eq!(1, read_post_listings_with_person.len());
  
      // Without the user
--    assert_eq!(expected_post_listing_no_user, read_post_listings_no_user[0]);
--    assert_eq!(expected_post_listing_no_user, read_post_listing_no_user);
--    assert_eq!(1, read_post_listings_no_user.len());
++    assert_eq!(expected_post_listing_no_person, read_post_listings_no_person[0]);
++    assert_eq!(expected_post_listing_no_person, read_post_listing_no_person);
++    assert_eq!(1, read_post_listings_no_person.len());
  
      // assert_eq!(expected_post, inserted_post);
      // assert_eq!(expected_post, updated_post);
diff --cc crates/db_views/src/private_message_view.rs
index 578af80e,578af80e..29b1e464
--- a/crates/db_views/src/private_message_view.rs
+++ b/crates/db_views/src/private_message_view.rs
@@@ -1,10 -1,10 +1,10 @@@
  use diesel::{pg::Pg, result::Error, *};
  use lemmy_db_queries::{limit_and_offset, MaybeOptional, ToSafe, ViewToVec};
  use lemmy_db_schema::{
--  schema::{private_message, user_, user_alias_1},
++  schema::{private_message, person, person_alias_1},
    source::{
      private_message::PrivateMessage,
--    user::{UserAlias1, UserSafe, UserSafeAlias1, User_},
++    person::{PersonAlias1, PersonSafe, PersonSafeAlias1, Person},
    },
  };
  use log::debug;
@@@ -13,23 -13,23 +13,23 @@@ use serde::Serialize
  #[derive(Debug, PartialEq, Serialize, Clone)]
  pub struct PrivateMessageView {
    pub private_message: PrivateMessage,
--  pub creator: UserSafe,
--  pub recipient: UserSafeAlias1,
++  pub creator: PersonSafe,
++  pub recipient: PersonSafeAlias1,
  }
  
--type PrivateMessageViewTuple = (PrivateMessage, UserSafe, UserSafeAlias1);
++type PrivateMessageViewTuple = (PrivateMessage, PersonSafe, PersonSafeAlias1);
  
  impl PrivateMessageView {
    pub fn read(conn: &PgConnection, private_message_id: i32) -> Result<Self, Error> {
      let (private_message, creator, recipient) = private_message::table
        .find(private_message_id)
--      .inner_join(user_::table.on(private_message::creator_id.eq(user_::id)))
--      .inner_join(user_alias_1::table.on(private_message::recipient_id.eq(user_alias_1::id)))
++      .inner_join(person::table.on(private_message::creator_id.eq(person::id)))
++      .inner_join(person_alias_1::table.on(private_message::recipient_id.eq(person_alias_1::id)))
        .order_by(private_message::published.desc())
        .select((
          private_message::all_columns,
--        User_::safe_columns_tuple(),
--        UserAlias1::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
++        PersonAlias1::safe_columns_tuple(),
        ))
        .first::<PrivateMessageViewTuple>(conn)?;
  
@@@ -77,12 -77,12 +77,12 @@@ impl<'a> PrivateMessageQueryBuilder<'a
  
    pub fn list(self) -> Result<Vec<PrivateMessageView>, Error> {
      let mut query = private_message::table
--      .inner_join(user_::table.on(private_message::creator_id.eq(user_::id)))
--      .inner_join(user_alias_1::table.on(private_message::recipient_id.eq(user_alias_1::id)))
++      .inner_join(person::table.on(private_message::creator_id.eq(person::id)))
++      .inner_join(person_alias_1::table.on(private_message::recipient_id.eq(person_alias_1::id)))
        .select((
          private_message::all_columns,
--        User_::safe_columns_tuple(),
--        UserAlias1::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
++        PersonAlias1::safe_columns_tuple(),
        ))
        .into_boxed();
  
diff --cc crates/db_views/src/site_view.rs
index c04e85e9,c04e85e9..391cf84b
--- a/crates/db_views/src/site_view.rs
+++ b/crates/db_views/src/site_view.rs
@@@ -1,10 -1,10 +1,10 @@@
  use diesel::{result::Error, *};
  use lemmy_db_queries::{aggregates::site_aggregates::SiteAggregates, ToSafe};
  use lemmy_db_schema::{
--  schema::{site, site_aggregates, user_},
++  schema::{site, site_aggregates, person},
    source::{
      site::Site,
--    user::{UserSafe, User_},
++    person::{PersonSafe, Person},
    },
  };
  use serde::Serialize;
@@@ -12,21 -12,21 +12,21 @@@
  #[derive(Debug, Serialize, Clone)]
  pub struct SiteView {
    pub site: Site,
--  pub creator: UserSafe,
++  pub creator: PersonSafe,
    pub counts: SiteAggregates,
  }
  
  impl SiteView {
    pub fn read(conn: &PgConnection) -> Result<Self, Error> {
      let (site, creator, counts) = site::table
--      .inner_join(user_::table)
++      .inner_join(person::table)
        .inner_join(site_aggregates::table)
        .select((
          site::all_columns,
--        User_::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
          site_aggregates::all_columns,
        ))
--      .first::<(Site, UserSafe, SiteAggregates)>(conn)?;
++      .first::<(Site, PersonSafe, SiteAggregates)>(conn)?;
  
      Ok(SiteView {
        site,
diff --cc crates/db_views_actor/src/community_follower_view.rs
index a4f2b20d,a4f2b20d..6673acb5
--- a/crates/db_views_actor/src/community_follower_view.rs
+++ b/crates/db_views_actor/src/community_follower_view.rs
@@@ -1,10 -1,10 +1,10 @@@
  use diesel::{result::Error, *};
  use lemmy_db_queries::{ToSafe, ViewToVec};
  use lemmy_db_schema::{
--  schema::{community, community_follower, user_},
++  schema::{community, community_follower, person},
    source::{
      community::{Community, CommunitySafe},
--    user::{UserSafe, User_},
++    person::{PersonSafe, Person},
    },
  };
  use serde::Serialize;
@@@ -12,17 -12,17 +12,17 @@@
  #[derive(Debug, Serialize, Clone)]
  pub struct CommunityFollowerView {
    pub community: CommunitySafe,
--  pub follower: UserSafe,
++  pub follower: PersonSafe,
  }
  
--type CommunityFollowerViewTuple = (CommunitySafe, UserSafe);
++type CommunityFollowerViewTuple = (CommunitySafe, PersonSafe);
  
  impl CommunityFollowerView {
    pub fn for_community(conn: &PgConnection, community_id: i32) -> Result<Vec<Self>, Error> {
      let res = community_follower::table
        .inner_join(community::table)
--      .inner_join(user_::table)
--      .select((Community::safe_columns_tuple(), User_::safe_columns_tuple()))
++      .inner_join(person::table)
++      .select((Community::safe_columns_tuple(), Person::safe_columns_tuple()))
        .filter(community_follower::community_id.eq(community_id))
        .order_by(community_follower::published)
        .load::<CommunityFollowerViewTuple>(conn)?;
@@@ -30,12 -30,12 +30,12 @@@
      Ok(Self::from_tuple_to_vec(res))
    }
  
--  pub fn for_user(conn: &PgConnection, user_id: i32) -> Result<Vec<Self>, Error> {
++  pub fn for_person(conn: &PgConnection, person_id: i32) -> Result<Vec<Self>, Error> {
      let res = community_follower::table
        .inner_join(community::table)
--      .inner_join(user_::table)
--      .select((Community::safe_columns_tuple(), User_::safe_columns_tuple()))
--      .filter(community_follower::user_id.eq(user_id))
++      .inner_join(person::table)
++      .select((Community::safe_columns_tuple(), Person::safe_columns_tuple()))
++      .filter(community_follower::person_id.eq(person_id))
        .order_by(community_follower::published)
        .load::<CommunityFollowerViewTuple>(conn)?;
  
diff --cc crates/db_views_actor/src/community_moderator_view.rs
index fe8b3c22,fe8b3c22..7cc20847
--- a/crates/db_views_actor/src/community_moderator_view.rs
+++ b/crates/db_views_actor/src/community_moderator_view.rs
@@@ -1,10 -1,10 +1,10 @@@
  use diesel::{result::Error, *};
  use lemmy_db_queries::{ToSafe, ViewToVec};
  use lemmy_db_schema::{
--  schema::{community, community_moderator, user_},
++  schema::{community, community_moderator, person},
    source::{
      community::{Community, CommunitySafe},
--    user::{UserSafe, User_},
++    person::{PersonSafe, Person},
    },
  };
  use serde::Serialize;
@@@ -12,17 -12,17 +12,17 @@@
  #[derive(Debug, Serialize, Clone)]
  pub struct CommunityModeratorView {
    pub community: CommunitySafe,
--  pub moderator: UserSafe,
++  pub moderator: PersonSafe,
  }
  
--type CommunityModeratorViewTuple = (CommunitySafe, UserSafe);
++type CommunityModeratorViewTuple = (CommunitySafe, PersonSafe);
  
  impl CommunityModeratorView {
    pub fn for_community(conn: &PgConnection, community_id: i32) -> Result<Vec<Self>, Error> {
      let res = community_moderator::table
        .inner_join(community::table)
--      .inner_join(user_::table)
--      .select((Community::safe_columns_tuple(), User_::safe_columns_tuple()))
++      .inner_join(person::table)
++      .select((Community::safe_columns_tuple(), Person::safe_columns_tuple()))
        .filter(community_moderator::community_id.eq(community_id))
        .order_by(community_moderator::published)
        .load::<CommunityModeratorViewTuple>(conn)?;
@@@ -30,12 -30,12 +30,12 @@@
      Ok(Self::from_tuple_to_vec(res))
    }
  
--  pub fn for_user(conn: &PgConnection, user_id: i32) -> Result<Vec<Self>, Error> {
++  pub fn for_person(conn: &PgConnection, person_id: i32) -> Result<Vec<Self>, Error> {
      let res = community_moderator::table
        .inner_join(community::table)
--      .inner_join(user_::table)
--      .select((Community::safe_columns_tuple(), User_::safe_columns_tuple()))
--      .filter(community_moderator::user_id.eq(user_id))
++      .inner_join(person::table)
++      .select((Community::safe_columns_tuple(), Person::safe_columns_tuple()))
++      .filter(community_moderator::person_id.eq(person_id))
        .order_by(community_moderator::published)
        .load::<CommunityModeratorViewTuple>(conn)?;
  
diff --cc crates/db_views_actor/src/community_person_ban_view.rs
index 00000000,00000000..651fcbf8
new file mode 100644
--- /dev/null
+++ b/crates/db_views_actor/src/community_person_ban_view.rs
@@@ -1,0 -1,0 +1,35 @@@
++use diesel::{result::Error, *};
++use lemmy_db_queries::ToSafe;
++use lemmy_db_schema::{
++  schema::{community, community_person_ban, person},
++  source::{
++    community::{Community, CommunitySafe},
++    person::{PersonSafe, Person},
++  },
++};
++use serde::Serialize;
++
++#[derive(Debug, Serialize, Clone)]
++pub struct CommunityPersonBanView {
++  pub community: CommunitySafe,
++  pub person: PersonSafe,
++}
++
++impl CommunityPersonBanView {
++  pub fn get(
++    conn: &PgConnection,
++    from_person_id: i32,
++    from_community_id: i32,
++  ) -> Result<Self, Error> {
++    let (community, person) = community_person_ban::table
++      .inner_join(community::table)
++      .inner_join(person::table)
++      .select((Community::safe_columns_tuple(), Person::safe_columns_tuple()))
++      .filter(community_person_ban::community_id.eq(from_community_id))
++      .filter(community_person_ban::person_id.eq(from_person_id))
++      .order_by(community_person_ban::published)
++      .first::<(CommunitySafe, PersonSafe)>(conn)?;
++
++    Ok(CommunityPersonBanView { community, person })
++  }
++}
diff --cc crates/db_views_actor/src/community_user_ban_view.rs
index d0a92584,d0a92584..00000000
deleted file mode 100644,100644
--- a/crates/db_views_actor/src/community_user_ban_view.rs
+++ /dev/null
@@@ -1,35 -1,35 +1,0 @@@
--use diesel::{result::Error, *};
--use lemmy_db_queries::ToSafe;
--use lemmy_db_schema::{
--  schema::{community, community_user_ban, user_},
--  source::{
--    community::{Community, CommunitySafe},
--    user::{UserSafe, User_},
--  },
--};
--use serde::Serialize;
--
--#[derive(Debug, Serialize, Clone)]
--pub struct CommunityUserBanView {
--  pub community: CommunitySafe,
--  pub user: UserSafe,
--}
--
--impl CommunityUserBanView {
--  pub fn get(
--    conn: &PgConnection,
--    from_user_id: i32,
--    from_community_id: i32,
--  ) -> Result<Self, Error> {
--    let (community, user) = community_user_ban::table
--      .inner_join(community::table)
--      .inner_join(user_::table)
--      .select((Community::safe_columns_tuple(), User_::safe_columns_tuple()))
--      .filter(community_user_ban::community_id.eq(from_community_id))
--      .filter(community_user_ban::user_id.eq(from_user_id))
--      .order_by(community_user_ban::published)
--      .first::<(CommunitySafe, UserSafe)>(conn)?;
--
--    Ok(CommunityUserBanView { community, user })
--  }
--}
diff --cc crates/db_views_actor/src/community_view.rs
index 9187696d,9187696d..47d0b1e2
--- a/crates/db_views_actor/src/community_view.rs
+++ b/crates/db_views_actor/src/community_view.rs
@@@ -1,4 -1,4 +1,4 @@@
--use crate::{community_moderator_view::CommunityModeratorView, user_view::UserViewSafe};
++use crate::{community_moderator_view::CommunityModeratorView, person_view::PersonViewSafe};
  use diesel::{result::Error, *};
  use lemmy_db_queries::{
    aggregates::community_aggregates::CommunityAggregates,
@@@ -12,10 -12,10 +12,10 @@@
    ViewToVec,
  };
  use lemmy_db_schema::{
--  schema::{community, community_aggregates, community_follower, user_},
++  schema::{community, community_aggregates, community_follower, person},
    source::{
      community::{Community, CommunityFollower, CommunitySafe},
--    user::{UserSafe, User_},
++    person::{PersonSafe, Person},
    },
  };
  use serde::Serialize;
@@@ -23,14 -23,14 +23,14 @@@
  #[derive(Debug, Serialize, Clone)]
  pub struct CommunityView {
    pub community: CommunitySafe,
--  pub creator: UserSafe,
++  pub creator: PersonSafe,
    pub subscribed: bool,
    pub counts: CommunityAggregates,
  }
  
  type CommunityViewTuple = (
    CommunitySafe,
--  UserSafe,
++  PersonSafe,
    CommunityAggregates,
    Option<CommunityFollower>,
  );
@@@ -39,25 -39,25 +39,25 @@@ impl CommunityView 
    pub fn read(
      conn: &PgConnection,
      community_id: i32,
--    my_user_id: Option<i32>,
++    my_person_id: Option<i32>,
    ) -> Result<Self, Error> {
      // The left join below will return None in this case
--    let user_id_join = my_user_id.unwrap_or(-1);
++    let person_id_join = my_person_id.unwrap_or(-1);
  
      let (community, creator, counts, follower) = community::table
        .find(community_id)
--      .inner_join(user_::table)
++      .inner_join(person::table)
        .inner_join(community_aggregates::table)
        .left_join(
          community_follower::table.on(
            community::id
              .eq(community_follower::community_id)
--            .and(community_follower::user_id.eq(user_id_join)),
++            .and(community_follower::person_id.eq(person_id_join)),
          ),
        )
        .select((
          Community::safe_columns_tuple(),
--        User_::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
          community_aggregates::all_columns,
          community_follower::all_columns.nullable(),
        ))
@@@ -79,14 -79,14 +79,14 @@@
          .map(|v| v.into_iter().map(|m| m.moderator.id).collect())?,
      );
      mods_and_admins
--      .append(&mut UserViewSafe::admins(conn).map(|v| v.into_iter().map(|a| a.user.id).collect())?);
++      .append(&mut PersonViewSafe::admins(conn).map(|v| v.into_iter().map(|a| a.person.id).collect())?);
      Ok(mods_and_admins)
    }
  
--  pub fn is_mod_or_admin(conn: &PgConnection, user_id: i32, community_id: i32) -> bool {
++  pub fn is_mod_or_admin(conn: &PgConnection, person_id: i32, community_id: i32) -> bool {
      Self::community_mods_and_admins(conn, community_id)
        .unwrap_or_default()
--      .contains(&user_id)
++      .contains(&person_id)
    }
  }
  
@@@ -94,7 -94,7 +94,7 @@@ pub struct CommunityQueryBuilder<'a> 
    conn: &'a PgConnection,
    listing_type: &'a ListingType,
    sort: &'a SortType,
--  my_user_id: Option<i32>,
++  my_person_id: Option<i32>,
    show_nsfw: bool,
    search_term: Option<String>,
    page: Option<i64>,
@@@ -105,7 -105,7 +105,7 @@@ impl<'a> CommunityQueryBuilder<'a> 
    pub fn create(conn: &'a PgConnection) -> Self {
      CommunityQueryBuilder {
        conn,
--      my_user_id: None,
++      my_person_id: None,
        listing_type: &ListingType::All,
        sort: &SortType::Hot,
        show_nsfw: true,
@@@ -135,8 -135,8 +135,8 @@@
      self
    }
  
--  pub fn my_user_id<T: MaybeOptional<i32>>(mut self, my_user_id: T) -> Self {
--    self.my_user_id = my_user_id.get_optional();
++  pub fn my_person_id<T: MaybeOptional<i32>>(mut self, my_person_id: T) -> Self {
++    self.my_person_id = my_person_id.get_optional();
      self
    }
  
@@@ -152,21 -152,21 +152,21 @@@
  
    pub fn list(self) -> Result<Vec<CommunityView>, Error> {
      // The left join below will return None in this case
--    let user_id_join = self.my_user_id.unwrap_or(-1);
++    let person_id_join = self.my_person_id.unwrap_or(-1);
  
      let mut query = community::table
--      .inner_join(user_::table)
++      .inner_join(person::table)
        .inner_join(community_aggregates::table)
        .left_join(
          community_follower::table.on(
            community::id
              .eq(community_follower::community_id)
--            .and(community_follower::user_id.eq(user_id_join)),
++            .and(community_follower::person_id.eq(person_id_join)),
          ),
        )
        .select((
          Community::safe_columns_tuple(),
--        User_::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
          community_aggregates::all_columns,
          community_follower::all_columns.nullable(),
        ))
@@@ -202,7 -202,7 +202,7 @@@
      };
  
      query = match self.listing_type {
--      ListingType::Subscribed => query.filter(community_follower::user_id.is_not_null()), // TODO could be this: and(community_follower::user_id.eq(user_id_join)),
++      ListingType::Subscribed => query.filter(community_follower::person_id.is_not_null()), // TODO could be this: and(community_follower::person_id.eq(person_id_join)),
        ListingType::Local => query.filter(community::local.eq(true)),
        _ => query,
      };
diff --cc crates/db_views_actor/src/lib.rs
index a2ac3193,a2ac3193..5d5203c5
--- a/crates/db_views_actor/src/lib.rs
+++ b/crates/db_views_actor/src/lib.rs
@@@ -1,6 -1,6 +1,6 @@@
  pub mod community_follower_view;
  pub mod community_moderator_view;
--pub mod community_user_ban_view;
++pub mod community_person_ban_view;
  pub mod community_view;
--pub mod user_mention_view;
--pub mod user_view;
++pub mod person_mention_view;
++pub mod person_view;
diff --cc crates/db_views_actor/src/person_mention_view.rs
index dc37a880,ffdbe030..5d7ee983
--- a/crates/db_views_actor/src/person_mention_view.rs
+++ b/crates/db_views_actor/src/person_mention_view.rs
@@@ -16,62 -16,62 +16,62 @@@ use lemmy_db_schema::
      comment_saved,
      community,
      community_follower,
--    community_user_ban,
++    community_person_ban,
      post,
--    user_,
--    user_alias_1,
--    user_mention,
++    person,
++    person_alias_1,
++    person_mention,
    },
    source::{
      comment::{Comment, CommentSaved},
 -    community::{Community, CommunityFollower, CommunitySafe, CommunityUserBan},
 +    community::{Community, CommunityFollower, CommunitySafe, CommunityPersonBan},
      post::Post,
--    user::{UserAlias1, UserSafe, UserSafeAlias1, User_},
--    user_mention::UserMention,
++    person::{PersonAlias1, PersonSafe, PersonSafeAlias1, Person},
++    person_mention::PersonMention,
    },
  };
  use serde::Serialize;
  
  #[derive(Debug, PartialEq, Serialize, Clone)]
--pub struct UserMentionView {
--  pub user_mention: UserMention,
++pub struct PersonMentionView {
++  pub person_mention: PersonMention,
    pub comment: Comment,
--  pub creator: UserSafe,
++  pub creator: PersonSafe,
    pub post: Post,
    pub community: CommunitySafe,
--  pub recipient: UserSafeAlias1,
++  pub recipient: PersonSafeAlias1,
    pub counts: CommentAggregates,
--  pub creator_banned_from_community: bool, // Left Join to CommunityUserBan
++  pub creator_banned_from_community: bool, // Left Join to CommunityPersonBan
    pub subscribed: bool,                    // Left join to CommunityFollower
    pub saved: bool,                         // Left join to CommentSaved
    pub my_vote: Option<i16>,                // Left join to CommentLike
  }
  
--type UserMentionViewTuple = (
--  UserMention,
++type PersonMentionViewTuple = (
++  PersonMention,
    Comment,
--  UserSafe,
++  PersonSafe,
    Post,
    CommunitySafe,
--  UserSafeAlias1,
++  PersonSafeAlias1,
    CommentAggregates,
 -  Option<CommunityUserBan>,
 +  Option<CommunityPersonBan>,
    Option<CommunityFollower>,
    Option<CommentSaved>,
    Option<i16>,
  );
  
--impl UserMentionView {
++impl PersonMentionView {
    pub fn read(
      conn: &PgConnection,
--    user_mention_id: i32,
--    my_user_id: Option<i32>,
++    person_mention_id: i32,
++    my_person_id: Option<i32>,
    ) -> Result<Self, Error> {
      // The left join below will return None in this case
--    let user_id_join = my_user_id.unwrap_or(-1);
++    let person_id_join = my_person_id.unwrap_or(-1);
  
      let (
--      user_mention,
++      person_mention,
        comment,
        creator,
        post,
@@@ -82,59 -82,59 +82,59 @@@
        subscribed,
        saved,
        my_vote,
--    ) = user_mention::table
--      .find(user_mention_id)
++    ) = person_mention::table
++      .find(person_mention_id)
        .inner_join(comment::table)
--      .inner_join(user_::table.on(comment::creator_id.eq(user_::id)))
++      .inner_join(person::table.on(comment::creator_id.eq(person::id)))
        .inner_join(post::table.on(comment::post_id.eq(post::id)))
        .inner_join(community::table.on(post::community_id.eq(community::id)))
--      .inner_join(user_alias_1::table)
++      .inner_join(person_alias_1::table)
        .inner_join(comment_aggregates::table.on(comment::id.eq(comment_aggregates::comment_id)))
        .left_join(
--        community_user_ban::table.on(
++        community_person_ban::table.on(
            community::id
--            .eq(community_user_ban::community_id)
--            .and(community_user_ban::user_id.eq(comment::creator_id)),
++            .eq(community_person_ban::community_id)
++            .and(community_person_ban::person_id.eq(comment::creator_id)),
          ),
        )
        .left_join(
          community_follower::table.on(
            post::community_id
              .eq(community_follower::community_id)
--            .and(community_follower::user_id.eq(user_id_join)),
++            .and(community_follower::person_id.eq(person_id_join)),
          ),
        )
        .left_join(
          comment_saved::table.on(
            comment::id
              .eq(comment_saved::comment_id)
--            .and(comment_saved::user_id.eq(user_id_join)),
++            .and(comment_saved::person_id.eq(person_id_join)),
          ),
        )
        .left_join(
          comment_like::table.on(
            comment::id
              .eq(comment_like::comment_id)
--            .and(comment_like::user_id.eq(user_id_join)),
++            .and(comment_like::person_id.eq(person_id_join)),
          ),
        )
        .select((
--        user_mention::all_columns,
++        person_mention::all_columns,
          comment::all_columns,
--        User_::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
          post::all_columns,
          Community::safe_columns_tuple(),
--        UserAlias1::safe_columns_tuple(),
++        PersonAlias1::safe_columns_tuple(),
          comment_aggregates::all_columns,
--        community_user_ban::all_columns.nullable(),
++        community_person_ban::all_columns.nullable(),
          community_follower::all_columns.nullable(),
          comment_saved::all_columns.nullable(),
          comment_like::score.nullable(),
        ))
--      .first::<UserMentionViewTuple>(conn)?;
++      .first::<PersonMentionViewTuple>(conn)?;
  
--    Ok(UserMentionView {
--      user_mention,
++    Ok(PersonMentionView {
++      person_mention,
        comment,
        creator,
        post,
@@@ -149,9 -149,9 +149,9 @@@
    }
  }
  
--pub struct UserMentionQueryBuilder<'a> {
++pub struct PersonMentionQueryBuilder<'a> {
    conn: &'a PgConnection,
--  my_user_id: Option<i32>,
++  my_person_id: Option<i32>,
    recipient_id: Option<i32>,
    sort: &'a SortType,
    unread_only: bool,
@@@ -159,11 -159,11 +159,11 @@@
    limit: Option<i64>,
  }
  
--impl<'a> UserMentionQueryBuilder<'a> {
++impl<'a> PersonMentionQueryBuilder<'a> {
    pub fn create(conn: &'a PgConnection) -> Self {
--    UserMentionQueryBuilder {
++    PersonMentionQueryBuilder {
        conn,
--      my_user_id: None,
++      my_person_id: None,
        recipient_id: None,
        sort: &SortType::New,
        unread_only: false,
@@@ -187,8 -187,8 +187,8 @@@
      self
    }
  
--  pub fn my_user_id<T: MaybeOptional<i32>>(mut self, my_user_id: T) -> Self {
--    self.my_user_id = my_user_id.get_optional();
++  pub fn my_person_id<T: MaybeOptional<i32>>(mut self, my_person_id: T) -> Self {
++    self.my_person_id = my_person_id.get_optional();
      self
    }
  
@@@ -202,56 -202,56 +202,56 @@@
      self
    }
  
--  pub fn list(self) -> Result<Vec<UserMentionView>, Error> {
++  pub fn list(self) -> Result<Vec<PersonMentionView>, Error> {
      use diesel::dsl::*;
  
      // The left join below will return None in this case
--    let user_id_join = self.my_user_id.unwrap_or(-1);
++    let person_id_join = self.my_person_id.unwrap_or(-1);
  
--    let mut query = user_mention::table
++    let mut query = person_mention::table
        .inner_join(comment::table)
--      .inner_join(user_::table.on(comment::creator_id.eq(user_::id)))
++      .inner_join(person::table.on(comment::creator_id.eq(person::id)))
        .inner_join(post::table.on(comment::post_id.eq(post::id)))
        .inner_join(community::table.on(post::community_id.eq(community::id)))
--      .inner_join(user_alias_1::table)
++      .inner_join(person_alias_1::table)
        .inner_join(comment_aggregates::table.on(comment::id.eq(comment_aggregates::comment_id)))
        .left_join(
--        community_user_ban::table.on(
++        community_person_ban::table.on(
            community::id
--            .eq(community_user_ban::community_id)
--            .and(community_user_ban::user_id.eq(comment::creator_id)),
++            .eq(community_person_ban::community_id)
++            .and(community_person_ban::person_id.eq(comment::creator_id)),
          ),
        )
        .left_join(
          community_follower::table.on(
            post::community_id
              .eq(community_follower::community_id)
--            .and(community_follower::user_id.eq(user_id_join)),
++            .and(community_follower::person_id.eq(person_id_join)),
          ),
        )
        .left_join(
          comment_saved::table.on(
            comment::id
              .eq(comment_saved::comment_id)
--            .and(comment_saved::user_id.eq(user_id_join)),
++            .and(comment_saved::person_id.eq(person_id_join)),
          ),
        )
        .left_join(
          comment_like::table.on(
            comment::id
              .eq(comment_like::comment_id)
--            .and(comment_like::user_id.eq(user_id_join)),
++            .and(comment_like::person_id.eq(person_id_join)),
          ),
        )
        .select((
--        user_mention::all_columns,
++        person_mention::all_columns,
          comment::all_columns,
--        User_::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
          post::all_columns,
          Community::safe_columns_tuple(),
--        UserAlias1::safe_columns_tuple(),
++        PersonAlias1::safe_columns_tuple(),
          comment_aggregates::all_columns,
--        community_user_ban::all_columns.nullable(),
++        community_person_ban::all_columns.nullable(),
          community_follower::all_columns.nullable(),
          comment_saved::all_columns.nullable(),
          comment_like::score.nullable(),
@@@ -259,11 -259,11 +259,11 @@@
        .into_boxed();
  
      if let Some(recipient_id) = self.recipient_id {
--      query = query.filter(user_mention::recipient_id.eq(recipient_id));
++      query = query.filter(person_mention::recipient_id.eq(recipient_id));
      }
  
      if self.unread_only {
--      query = query.filter(user_mention::read.eq(false));
++      query = query.filter(person_mention::read.eq(false));
      }
  
      query = match self.sort {
@@@ -293,19 -293,19 +293,19 @@@
      let res = query
        .limit(limit)
        .offset(offset)
--      .load::<UserMentionViewTuple>(self.conn)?;
++      .load::<PersonMentionViewTuple>(self.conn)?;
  
--    Ok(UserMentionView::from_tuple_to_vec(res))
++    Ok(PersonMentionView::from_tuple_to_vec(res))
    }
  }
  
--impl ViewToVec for UserMentionView {
--  type DbTuple = UserMentionViewTuple;
++impl ViewToVec for PersonMentionView {
++  type DbTuple = PersonMentionViewTuple;
    fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
      items
        .iter()
        .map(|a| Self {
--        user_mention: a.0.to_owned(),
++        person_mention: a.0.to_owned(),
          comment: a.1.to_owned(),
          creator: a.2.to_owned(),
          post: a.3.to_owned(),
diff --cc crates/db_views_actor/src/person_view.rs
index 00000000,00000000..ff936d6d
new file mode 100644
--- /dev/null
+++ b/crates/db_views_actor/src/person_view.rs
@@@ -1,0 -1,0 +1,152 @@@
++use diesel::{dsl::*, result::Error, *};
++use lemmy_db_queries::{
++  aggregates::person_aggregates::PersonAggregates,
++  fuzzy_search,
++  limit_and_offset,
++  MaybeOptional,
++  SortType,
++  ToSafe,
++  ViewToVec,
++};
++use lemmy_db_schema::{
++  schema::{person, person_aggregates, local_user},
++  source::person::{PersonSafe, Person},
++};
++use serde::Serialize;
++
++#[derive(Debug, Serialize, Clone)]
++pub struct PersonViewSafe {
++  pub person: PersonSafe,
++  pub counts: PersonAggregates,
++}
++
++type PersonViewSafeTuple = (PersonSafe, PersonAggregates);
++
++impl PersonViewSafe {
++  pub fn read(conn: &PgConnection, id: i32) -> Result<Self, Error> {
++    let (person, counts) = person::table
++      .find(id)
++      .inner_join(person_aggregates::table)
++      .select((Person::safe_columns_tuple(), person_aggregates::all_columns))
++      .first::<PersonViewSafeTuple>(conn)?;
++    Ok(Self { person, counts })
++  }
++
++  pub fn admins(conn: &PgConnection) -> Result<Vec<Self>, Error> {
++    let admins = person::table
++      .inner_join(person_aggregates::table)
++      .inner_join(local_user::table)
++      .select((Person::safe_columns_tuple(), person_aggregates::all_columns))
++      .filter(local_user::admin.eq(true))
++      .order_by(person::published)
++      .load::<PersonViewSafeTuple>(conn)?;
++
++    Ok(Self::from_tuple_to_vec(admins))
++  }
++
++  pub fn banned(conn: &PgConnection) -> Result<Vec<Self>, Error> {
++    let banned = person::table
++      .inner_join(person_aggregates::table)
++      .select((Person::safe_columns_tuple(), person_aggregates::all_columns))
++      .filter(person::banned.eq(true))
++      .load::<PersonViewSafeTuple>(conn)?;
++
++    Ok(Self::from_tuple_to_vec(banned))
++  }
++}
++
++pub struct PersonQueryBuilder<'a> {
++  conn: &'a PgConnection,
++  sort: &'a SortType,
++  search_term: Option<String>,
++  page: Option<i64>,
++  limit: Option<i64>,
++}
++
++impl<'a> PersonQueryBuilder<'a> {
++  pub fn create(conn: &'a PgConnection) -> Self {
++    PersonQueryBuilder {
++      conn,
++      search_term: None,
++      sort: &SortType::Hot,
++      page: None,
++      limit: None,
++    }
++  }
++
++  pub fn sort(mut self, sort: &'a SortType) -> Self {
++    self.sort = sort;
++    self
++  }
++
++  pub fn search_term<T: MaybeOptional<String>>(mut self, search_term: T) -> Self {
++    self.search_term = search_term.get_optional();
++    self
++  }
++
++  pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
++    self.page = page.get_optional();
++    self
++  }
++
++  pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
++    self.limit = limit.get_optional();
++    self
++  }
++
++  pub fn list(self) -> Result<Vec<PersonViewSafe>, Error> {
++    let mut query = person::table
++      .inner_join(person_aggregates::table)
++      .select((Person::safe_columns_tuple(), person_aggregates::all_columns))
++      .into_boxed();
++
++    if let Some(search_term) = self.search_term {
++      query = query.filter(person::name.ilike(fuzzy_search(&search_term)));
++    }
++
++    query = match self.sort {
++      SortType::Hot => query
++        .order_by(person_aggregates::comment_score.desc())
++        .then_order_by(person::published.desc()),
++      SortType::Active => query
++        .order_by(person_aggregates::comment_score.desc())
++        .then_order_by(person::published.desc()),
++      SortType::New | SortType::MostComments | SortType::NewComments => {
++        query.order_by(person::published.desc())
++      }
++      SortType::TopAll => query.order_by(person_aggregates::comment_score.desc()),
++      SortType::TopYear => query
++        .filter(person::published.gt(now - 1.years()))
++        .order_by(person_aggregates::comment_score.desc()),
++      SortType::TopMonth => query
++        .filter(person::published.gt(now - 1.months()))
++        .order_by(person_aggregates::comment_score.desc()),
++      SortType::TopWeek => query
++        .filter(person::published.gt(now - 1.weeks()))
++        .order_by(person_aggregates::comment_score.desc()),
++      SortType::TopDay => query
++        .filter(person::published.gt(now - 1.days()))
++        .order_by(person_aggregates::comment_score.desc()),
++    };
++
++    let (limit, offset) = limit_and_offset(self.page, self.limit);
++    query = query.limit(limit).offset(offset);
++
++    let res = query.load::<PersonViewSafeTuple>(self.conn)?;
++
++    Ok(PersonViewSafe::from_tuple_to_vec(res))
++  }
++}
++
++impl ViewToVec for PersonViewSafe {
++  type DbTuple = PersonViewSafeTuple;
++  fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
++    items
++      .iter()
++      .map(|a| Self {
++        person: a.0.to_owned(),
++        counts: a.1.to_owned(),
++      })
++      .collect::<Vec<Self>>()
++  }
++}
diff --cc crates/db_views_actor/src/user_view.rs
index bbe889a2,bbe889a2..00000000
deleted file mode 100644,100644
--- a/crates/db_views_actor/src/user_view.rs
+++ /dev/null
@@@ -1,151 -1,151 +1,0 @@@
--use diesel::{dsl::*, result::Error, *};
--use lemmy_db_queries::{
--  aggregates::user_aggregates::UserAggregates,
--  fuzzy_search,
--  limit_and_offset,
--  MaybeOptional,
--  SortType,
--  ToSafe,
--  ViewToVec,
--};
--use lemmy_db_schema::{
--  schema::{user_, user_aggregates},
--  source::user::{UserSafe, User_},
--};
--use serde::Serialize;
--
--#[derive(Debug, Serialize, Clone)]
--pub struct UserViewSafe {
--  pub user: UserSafe,
--  pub counts: UserAggregates,
--}
--
--type UserViewSafeTuple = (UserSafe, UserAggregates);
--
--impl UserViewSafe {
--  pub fn read(conn: &PgConnection, id: i32) -> Result<Self, Error> {
--    let (user, counts) = user_::table
--      .find(id)
--      .inner_join(user_aggregates::table)
--      .select((User_::safe_columns_tuple(), user_aggregates::all_columns))
--      .first::<UserViewSafeTuple>(conn)?;
--    Ok(Self { user, counts })
--  }
--
--  pub fn admins(conn: &PgConnection) -> Result<Vec<Self>, Error> {
--    let admins = user_::table
--      .inner_join(user_aggregates::table)
--      .select((User_::safe_columns_tuple(), user_aggregates::all_columns))
--      .filter(user_::admin.eq(true))
--      .order_by(user_::published)
--      .load::<UserViewSafeTuple>(conn)?;
--
--    Ok(Self::from_tuple_to_vec(admins))
--  }
--
--  pub fn banned(conn: &PgConnection) -> Result<Vec<Self>, Error> {
--    let banned = user_::table
--      .inner_join(user_aggregates::table)
--      .select((User_::safe_columns_tuple(), user_aggregates::all_columns))
--      .filter(user_::banned.eq(true))
--      .load::<UserViewSafeTuple>(conn)?;
--
--    Ok(Self::from_tuple_to_vec(banned))
--  }
--}
--
--pub struct UserQueryBuilder<'a> {
--  conn: &'a PgConnection,
--  sort: &'a SortType,
--  search_term: Option<String>,
--  page: Option<i64>,
--  limit: Option<i64>,
--}
--
--impl<'a> UserQueryBuilder<'a> {
--  pub fn create(conn: &'a PgConnection) -> Self {
--    UserQueryBuilder {
--      conn,
--      search_term: None,
--      sort: &SortType::Hot,
--      page: None,
--      limit: None,
--    }
--  }
--
--  pub fn sort(mut self, sort: &'a SortType) -> Self {
--    self.sort = sort;
--    self
--  }
--
--  pub fn search_term<T: MaybeOptional<String>>(mut self, search_term: T) -> Self {
--    self.search_term = search_term.get_optional();
--    self
--  }
--
--  pub fn page<T: MaybeOptional<i64>>(mut self, page: T) -> Self {
--    self.page = page.get_optional();
--    self
--  }
--
--  pub fn limit<T: MaybeOptional<i64>>(mut self, limit: T) -> Self {
--    self.limit = limit.get_optional();
--    self
--  }
--
--  pub fn list(self) -> Result<Vec<UserViewSafe>, Error> {
--    let mut query = user_::table
--      .inner_join(user_aggregates::table)
--      .select((User_::safe_columns_tuple(), user_aggregates::all_columns))
--      .into_boxed();
--
--    if let Some(search_term) = self.search_term {
--      query = query.filter(user_::name.ilike(fuzzy_search(&search_term)));
--    }
--
--    query = match self.sort {
--      SortType::Hot => query
--        .order_by(user_aggregates::comment_score.desc())
--        .then_order_by(user_::published.desc()),
--      SortType::Active => query
--        .order_by(user_aggregates::comment_score.desc())
--        .then_order_by(user_::published.desc()),
--      SortType::New | SortType::MostComments | SortType::NewComments => {
--        query.order_by(user_::published.desc())
--      }
--      SortType::TopAll => query.order_by(user_aggregates::comment_score.desc()),
--      SortType::TopYear => query
--        .filter(user_::published.gt(now - 1.years()))
--        .order_by(user_aggregates::comment_score.desc()),
--      SortType::TopMonth => query
--        .filter(user_::published.gt(now - 1.months()))
--        .order_by(user_aggregates::comment_score.desc()),
--      SortType::TopWeek => query
--        .filter(user_::published.gt(now - 1.weeks()))
--        .order_by(user_aggregates::comment_score.desc()),
--      SortType::TopDay => query
--        .filter(user_::published.gt(now - 1.days()))
--        .order_by(user_aggregates::comment_score.desc()),
--    };
--
--    let (limit, offset) = limit_and_offset(self.page, self.limit);
--    query = query.limit(limit).offset(offset);
--
--    let res = query.load::<UserViewSafeTuple>(self.conn)?;
--
--    Ok(UserViewSafe::from_tuple_to_vec(res))
--  }
--}
--
--impl ViewToVec for UserViewSafe {
--  type DbTuple = UserViewSafeTuple;
--  fn from_tuple_to_vec(items: Vec<Self::DbTuple>) -> Vec<Self> {
--    items
--      .iter()
--      .map(|a| Self {
--        user: a.0.to_owned(),
--        counts: a.1.to_owned(),
--      })
--      .collect::<Vec<Self>>()
--  }
--}
diff --cc crates/db_views_moderator/src/mod_add_community_view.rs
index dfb7de7e,dfb7de7e..32988c19
--- a/crates/db_views_moderator/src/mod_add_community_view.rs
+++ b/crates/db_views_moderator/src/mod_add_community_view.rs
@@@ -1,11 -1,11 +1,11 @@@
  use diesel::{result::Error, *};
  use lemmy_db_queries::{limit_and_offset, ToSafe, ViewToVec};
  use lemmy_db_schema::{
--  schema::{community, mod_add_community, user_, user_alias_1},
++  schema::{community, mod_add_community, person, person_alias_1},
    source::{
      community::{Community, CommunitySafe},
      moderator::ModAddCommunity,
--    user::{UserAlias1, UserSafe, UserSafeAlias1, User_},
++    person::{PersonAlias1, PersonSafe, PersonSafeAlias1, Person},
    },
  };
  use serde::Serialize;
@@@ -13,35 -13,35 +13,35 @@@
  #[derive(Debug, Serialize, Clone)]
  pub struct ModAddCommunityView {
    pub mod_add_community: ModAddCommunity,
--  pub moderator: UserSafe,
++  pub moderator: PersonSafe,
    pub community: CommunitySafe,
--  pub modded_user: UserSafeAlias1,
++  pub modded_person: PersonSafeAlias1,
  }
  
--type ModAddCommunityViewTuple = (ModAddCommunity, UserSafe, CommunitySafe, UserSafeAlias1);
++type ModAddCommunityViewTuple = (ModAddCommunity, PersonSafe, CommunitySafe, PersonSafeAlias1);
  
  impl ModAddCommunityView {
    pub fn list(
      conn: &PgConnection,
      community_id: Option<i32>,
--    mod_user_id: Option<i32>,
++    mod_person_id: Option<i32>,
      page: Option<i64>,
      limit: Option<i64>,
    ) -> Result<Vec<Self>, Error> {
      let mut query = mod_add_community::table
--      .inner_join(user_::table.on(mod_add_community::mod_user_id.eq(user_::id)))
++      .inner_join(person::table.on(mod_add_community::mod_person_id.eq(person::id)))
        .inner_join(community::table)
--      .inner_join(user_alias_1::table.on(mod_add_community::other_user_id.eq(user_alias_1::id)))
++      .inner_join(person_alias_1::table.on(mod_add_community::other_person_id.eq(person_alias_1::id)))
        .select((
          mod_add_community::all_columns,
--        User_::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
          Community::safe_columns_tuple(),
--        UserAlias1::safe_columns_tuple(),
++        PersonAlias1::safe_columns_tuple(),
        ))
        .into_boxed();
  
--    if let Some(mod_user_id) = mod_user_id {
--      query = query.filter(mod_add_community::mod_user_id.eq(mod_user_id));
++    if let Some(mod_person_id) = mod_person_id {
++      query = query.filter(mod_add_community::mod_person_id.eq(mod_person_id));
      };
  
      if let Some(community_id) = community_id {
@@@ -69,7 -69,7 +69,7 @@@ impl ViewToVec for ModAddCommunityView 
          mod_add_community: a.0.to_owned(),
          moderator: a.1.to_owned(),
          community: a.2.to_owned(),
--        modded_user: a.3.to_owned(),
++        modded_person: a.3.to_owned(),
        })
        .collect::<Vec<Self>>()
    }
diff --cc crates/db_views_moderator/src/mod_add_view.rs
index 06c7091c,06c7091c..e4a665c5
--- a/crates/db_views_moderator/src/mod_add_view.rs
+++ b/crates/db_views_moderator/src/mod_add_view.rs
@@@ -1,10 -1,10 +1,10 @@@
  use diesel::{result::Error, *};
  use lemmy_db_queries::{limit_and_offset, ToSafe, ViewToVec};
  use lemmy_db_schema::{
--  schema::{mod_add, user_, user_alias_1},
++  schema::{mod_add, person, person_alias_1},
    source::{
      moderator::ModAdd,
--    user::{UserAlias1, UserSafe, UserSafeAlias1, User_},
++    person::{PersonAlias1, PersonSafe, PersonSafeAlias1, Person},
    },
  };
  use serde::Serialize;
@@@ -12,31 -12,31 +12,31 @@@
  #[derive(Debug, Serialize, Clone)]
  pub struct ModAddView {
    pub mod_add: ModAdd,
--  pub moderator: UserSafe,
--  pub modded_user: UserSafeAlias1,
++  pub moderator: PersonSafe,
++  pub modded_person: PersonSafeAlias1,
  }
  
--type ModAddViewTuple = (ModAdd, UserSafe, UserSafeAlias1);
++type ModAddViewTuple = (ModAdd, PersonSafe, PersonSafeAlias1);
  
  impl ModAddView {
    pub fn list(
      conn: &PgConnection,
--    mod_user_id: Option<i32>,
++    mod_person_id: Option<i32>,
      page: Option<i64>,
      limit: Option<i64>,
    ) -> Result<Vec<Self>, Error> {
      let mut query = mod_add::table
--      .inner_join(user_::table.on(mod_add::mod_user_id.eq(user_::id)))
--      .inner_join(user_alias_1::table.on(mod_add::other_user_id.eq(user_alias_1::id)))
++      .inner_join(person::table.on(mod_add::mod_person_id.eq(person::id)))
++      .inner_join(person_alias_1::table.on(mod_add::other_person_id.eq(person_alias_1::id)))
        .select((
          mod_add::all_columns,
--        User_::safe_columns_tuple(),
--        UserAlias1::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
++        PersonAlias1::safe_columns_tuple(),
        ))
        .into_boxed();
  
--    if let Some(mod_user_id) = mod_user_id {
--      query = query.filter(mod_add::mod_user_id.eq(mod_user_id));
++    if let Some(mod_person_id) = mod_person_id {
++      query = query.filter(mod_add::mod_person_id.eq(mod_person_id));
      };
  
      let (limit, offset) = limit_and_offset(page, limit);
@@@ -59,7 -59,7 +59,7 @@@ impl ViewToVec for ModAddView 
        .map(|a| Self {
          mod_add: a.0.to_owned(),
          moderator: a.1.to_owned(),
--        modded_user: a.2.to_owned(),
++        modded_person: a.2.to_owned(),
        })
        .collect::<Vec<Self>>()
    }
diff --cc crates/db_views_moderator/src/mod_ban_from_community_view.rs
index 1d9b1faf,1d9b1faf..a60ede61
--- a/crates/db_views_moderator/src/mod_ban_from_community_view.rs
+++ b/crates/db_views_moderator/src/mod_ban_from_community_view.rs
@@@ -1,11 -1,11 +1,11 @@@
  use diesel::{result::Error, *};
  use lemmy_db_queries::{limit_and_offset, ToSafe, ViewToVec};
  use lemmy_db_schema::{
--  schema::{community, mod_ban_from_community, user_, user_alias_1},
++  schema::{community, mod_ban_from_community, person, person_alias_1},
    source::{
      community::{Community, CommunitySafe},
      moderator::ModBanFromCommunity,
--    user::{UserAlias1, UserSafe, UserSafeAlias1, User_},
++    person::{PersonAlias1, PersonSafe, PersonSafeAlias1, Person},
    },
  };
  use serde::Serialize;
@@@ -13,37 -13,37 +13,37 @@@
  #[derive(Debug, Serialize, Clone)]
  pub struct ModBanFromCommunityView {
    pub mod_ban_from_community: ModBanFromCommunity,
--  pub moderator: UserSafe,
++  pub moderator: PersonSafe,
    pub community: CommunitySafe,
--  pub banned_user: UserSafeAlias1,
++  pub banned_person: PersonSafeAlias1,
  }
  
--type ModBanFromCommunityViewTuple = (ModBanFromCommunity, UserSafe, CommunitySafe, UserSafeAlias1);
++type ModBanFromCommunityViewTuple = (ModBanFromCommunity, PersonSafe, CommunitySafe, PersonSafeAlias1);
  
  impl ModBanFromCommunityView {
    pub fn list(
      conn: &PgConnection,
      community_id: Option<i32>,
--    mod_user_id: Option<i32>,
++    mod_person_id: Option<i32>,
      page: Option<i64>,
      limit: Option<i64>,
    ) -> Result<Vec<Self>, Error> {
      let mut query = mod_ban_from_community::table
--      .inner_join(user_::table.on(mod_ban_from_community::mod_user_id.eq(user_::id)))
++      .inner_join(person::table.on(mod_ban_from_community::mod_person_id.eq(person::id)))
        .inner_join(community::table)
        .inner_join(
--        user_alias_1::table.on(mod_ban_from_community::other_user_id.eq(user_alias_1::id)),
++        person_alias_1::table.on(mod_ban_from_community::other_person_id.eq(person_alias_1::id)),
        )
        .select((
          mod_ban_from_community::all_columns,
--        User_::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
          Community::safe_columns_tuple(),
--        UserAlias1::safe_columns_tuple(),
++        PersonAlias1::safe_columns_tuple(),
        ))
        .into_boxed();
  
--    if let Some(mod_user_id) = mod_user_id {
--      query = query.filter(mod_ban_from_community::mod_user_id.eq(mod_user_id));
++    if let Some(mod_person_id) = mod_person_id {
++      query = query.filter(mod_ban_from_community::mod_person_id.eq(mod_person_id));
      };
  
      if let Some(community_id) = community_id {
@@@ -71,7 -71,7 +71,7 @@@ impl ViewToVec for ModBanFromCommunityV
          mod_ban_from_community: a.0.to_owned(),
          moderator: a.1.to_owned(),
          community: a.2.to_owned(),
--        banned_user: a.3.to_owned(),
++        banned_person: a.3.to_owned(),
        })
        .collect::<Vec<Self>>()
    }
diff --cc crates/db_views_moderator/src/mod_ban_view.rs
index ff599e18,ff599e18..2c55d514
--- a/crates/db_views_moderator/src/mod_ban_view.rs
+++ b/crates/db_views_moderator/src/mod_ban_view.rs
@@@ -1,10 -1,10 +1,10 @@@
  use diesel::{result::Error, *};
  use lemmy_db_queries::{limit_and_offset, ToSafe, ViewToVec};
  use lemmy_db_schema::{
--  schema::{mod_ban, user_, user_alias_1},
++  schema::{mod_ban, person, person_alias_1},
    source::{
      moderator::ModBan,
--    user::{UserAlias1, UserSafe, UserSafeAlias1, User_},
++    person::{PersonAlias1, PersonSafe, PersonSafeAlias1, Person},
    },
  };
  use serde::Serialize;
@@@ -12,31 -12,31 +12,31 @@@
  #[derive(Debug, Serialize, Clone)]
  pub struct ModBanView {
    pub mod_ban: ModBan,
--  pub moderator: UserSafe,
--  pub banned_user: UserSafeAlias1,
++  pub moderator: PersonSafe,
++  pub banned_person: PersonSafeAlias1,
  }
  
--type ModBanViewTuple = (ModBan, UserSafe, UserSafeAlias1);
++type ModBanViewTuple = (ModBan, PersonSafe, PersonSafeAlias1);
  
  impl ModBanView {
    pub fn list(
      conn: &PgConnection,
--    mod_user_id: Option<i32>,
++    mod_person_id: Option<i32>,
      page: Option<i64>,
      limit: Option<i64>,
    ) -> Result<Vec<Self>, Error> {
      let mut query = mod_ban::table
--      .inner_join(user_::table.on(mod_ban::mod_user_id.eq(user_::id)))
--      .inner_join(user_alias_1::table.on(mod_ban::other_user_id.eq(user_alias_1::id)))
++      .inner_join(person::table.on(mod_ban::mod_person_id.eq(person::id)))
++      .inner_join(person_alias_1::table.on(mod_ban::other_person_id.eq(person_alias_1::id)))
        .select((
          mod_ban::all_columns,
--        User_::safe_columns_tuple(),
--        UserAlias1::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
++        PersonAlias1::safe_columns_tuple(),
        ))
        .into_boxed();
  
--    if let Some(mod_user_id) = mod_user_id {
--      query = query.filter(mod_ban::mod_user_id.eq(mod_user_id));
++    if let Some(mod_person_id) = mod_person_id {
++      query = query.filter(mod_ban::mod_person_id.eq(mod_person_id));
      };
  
      let (limit, offset) = limit_and_offset(page, limit);
@@@ -59,7 -59,7 +59,7 @@@ impl ViewToVec for ModBanView 
        .map(|a| Self {
          mod_ban: a.0.to_owned(),
          moderator: a.1.to_owned(),
--        banned_user: a.2.to_owned(),
++        banned_person: a.2.to_owned(),
        })
        .collect::<Vec<Self>>()
    }
diff --cc crates/db_views_moderator/src/mod_lock_post_view.rs
index e6c697af,e6c697af..9369ba99
--- a/crates/db_views_moderator/src/mod_lock_post_view.rs
+++ b/crates/db_views_moderator/src/mod_lock_post_view.rs
@@@ -1,12 -1,12 +1,12 @@@
  use diesel::{result::Error, *};
  use lemmy_db_queries::{limit_and_offset, ToSafe, ViewToVec};
  use lemmy_db_schema::{
--  schema::{community, mod_lock_post, post, user_},
++  schema::{community, mod_lock_post, post, person},
    source::{
      community::{Community, CommunitySafe},
      moderator::ModLockPost,
      post::Post,
--    user::{UserSafe, User_},
++    person::{PersonSafe, Person},
    },
  };
  use serde::Serialize;
@@@ -14,28 -14,28 +14,28 @@@
  #[derive(Debug, Serialize, Clone)]
  pub struct ModLockPostView {
    pub mod_lock_post: ModLockPost,
--  pub moderator: UserSafe,
++  pub moderator: PersonSafe,
    pub post: Post,
    pub community: CommunitySafe,
  }
  
--type ModLockPostViewTuple = (ModLockPost, UserSafe, Post, CommunitySafe);
++type ModLockPostViewTuple = (ModLockPost, PersonSafe, Post, CommunitySafe);
  
  impl ModLockPostView {
    pub fn list(
      conn: &PgConnection,
      community_id: Option<i32>,
--    mod_user_id: Option<i32>,
++    mod_person_id: Option<i32>,
      page: Option<i64>,
      limit: Option<i64>,
    ) -> Result<Vec<Self>, Error> {
      let mut query = mod_lock_post::table
--      .inner_join(user_::table)
++      .inner_join(person::table)
        .inner_join(post::table)
        .inner_join(community::table.on(post::community_id.eq(community::id)))
        .select((
          mod_lock_post::all_columns,
--        User_::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
          post::all_columns,
          Community::safe_columns_tuple(),
        ))
@@@ -45,8 -45,8 +45,8 @@@
        query = query.filter(post::community_id.eq(community_id));
      };
  
--    if let Some(mod_user_id) = mod_user_id {
--      query = query.filter(mod_lock_post::mod_user_id.eq(mod_user_id));
++    if let Some(mod_person_id) = mod_person_id {
++      query = query.filter(mod_lock_post::mod_person_id.eq(mod_person_id));
      };
  
      let (limit, offset) = limit_and_offset(page, limit);
diff --cc crates/db_views_moderator/src/mod_remove_comment_view.rs
index 08270353,08270353..9633f200
--- a/crates/db_views_moderator/src/mod_remove_comment_view.rs
+++ b/crates/db_views_moderator/src/mod_remove_comment_view.rs
@@@ -1,13 -1,13 +1,13 @@@
  use diesel::{result::Error, *};
  use lemmy_db_queries::{limit_and_offset, ToSafe, ViewToVec};
  use lemmy_db_schema::{
--  schema::{comment, community, mod_remove_comment, post, user_, user_alias_1},
++  schema::{comment, community, mod_remove_comment, post, person, person_alias_1},
    source::{
      comment::Comment,
      community::{Community, CommunitySafe},
      moderator::ModRemoveComment,
      post::Post,
--    user::{UserAlias1, UserSafe, UserSafeAlias1, User_},
++    person::{PersonAlias1, PersonSafe, PersonSafeAlias1, Person},
    },
  };
  use serde::Serialize;
@@@ -15,18 -15,18 +15,18 @@@
  #[derive(Debug, Serialize, Clone)]
  pub struct ModRemoveCommentView {
    pub mod_remove_comment: ModRemoveComment,
--  pub moderator: UserSafe,
++  pub moderator: PersonSafe,
    pub comment: Comment,
--  pub commenter: UserSafeAlias1,
++  pub commenter: PersonSafeAlias1,
    pub post: Post,
    pub community: CommunitySafe,
  }
  
  type ModRemoveCommentViewTuple = (
    ModRemoveComment,
--  UserSafe,
++  PersonSafe,
    Comment,
--  UserSafeAlias1,
++  PersonSafeAlias1,
    Post,
    CommunitySafe,
  );
@@@ -35,21 -35,21 +35,21 @@@ impl ModRemoveCommentView 
    pub fn list(
      conn: &PgConnection,
      community_id: Option<i32>,
--    mod_user_id: Option<i32>,
++    mod_person_id: Option<i32>,
      page: Option<i64>,
      limit: Option<i64>,
    ) -> Result<Vec<Self>, Error> {
      let mut query = mod_remove_comment::table
--      .inner_join(user_::table)
++      .inner_join(person::table)
        .inner_join(comment::table)
--      .inner_join(user_alias_1::table.on(comment::creator_id.eq(user_alias_1::id)))
++      .inner_join(person_alias_1::table.on(comment::creator_id.eq(person_alias_1::id)))
        .inner_join(post::table.on(comment::post_id.eq(post::id)))
        .inner_join(community::table.on(post::community_id.eq(community::id)))
        .select((
          mod_remove_comment::all_columns,
--        User_::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
          comment::all_columns,
--        UserAlias1::safe_columns_tuple(),
++        PersonAlias1::safe_columns_tuple(),
          post::all_columns,
          Community::safe_columns_tuple(),
        ))
@@@ -59,8 -59,8 +59,8 @@@
        query = query.filter(post::community_id.eq(community_id));
      };
  
--    if let Some(mod_user_id) = mod_user_id {
--      query = query.filter(mod_remove_comment::mod_user_id.eq(mod_user_id));
++    if let Some(mod_person_id) = mod_person_id {
++      query = query.filter(mod_remove_comment::mod_person_id.eq(mod_person_id));
      };
  
      let (limit, offset) = limit_and_offset(page, limit);
diff --cc crates/db_views_moderator/src/mod_remove_community_view.rs
index ff62ac15,ff62ac15..6d49acbe
--- a/crates/db_views_moderator/src/mod_remove_community_view.rs
+++ b/crates/db_views_moderator/src/mod_remove_community_view.rs
@@@ -1,11 -1,11 +1,11 @@@
  use diesel::{result::Error, *};
  use lemmy_db_queries::{limit_and_offset, ToSafe, ViewToVec};
  use lemmy_db_schema::{
--  schema::{community, mod_remove_community, user_},
++  schema::{community, mod_remove_community, person},
    source::{
      community::{Community, CommunitySafe},
      moderator::ModRemoveCommunity,
--    user::{UserSafe, User_},
++    person::{PersonSafe, Person},
    },
  };
  use serde::Serialize;
@@@ -13,31 -13,31 +13,31 @@@
  #[derive(Debug, Serialize, Clone)]
  pub struct ModRemoveCommunityView {
    pub mod_remove_community: ModRemoveCommunity,
--  pub moderator: UserSafe,
++  pub moderator: PersonSafe,
    pub community: CommunitySafe,
  }
  
--type ModRemoveCommunityTuple = (ModRemoveCommunity, UserSafe, CommunitySafe);
++type ModRemoveCommunityTuple = (ModRemoveCommunity, PersonSafe, CommunitySafe);
  
  impl ModRemoveCommunityView {
    pub fn list(
      conn: &PgConnection,
--    mod_user_id: Option<i32>,
++    mod_person_id: Option<i32>,
      page: Option<i64>,
      limit: Option<i64>,
    ) -> Result<Vec<Self>, Error> {
      let mut query = mod_remove_community::table
--      .inner_join(user_::table)
++      .inner_join(person::table)
        .inner_join(community::table)
        .select((
          mod_remove_community::all_columns,
--        User_::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
          Community::safe_columns_tuple(),
        ))
        .into_boxed();
  
--    if let Some(mod_user_id) = mod_user_id {
--      query = query.filter(mod_remove_community::mod_user_id.eq(mod_user_id));
++    if let Some(mod_person_id) = mod_person_id {
++      query = query.filter(mod_remove_community::mod_person_id.eq(mod_person_id));
      };
  
      let (limit, offset) = limit_and_offset(page, limit);
diff --cc crates/db_views_moderator/src/mod_remove_post_view.rs
index 530ae039,530ae039..8530656c
--- a/crates/db_views_moderator/src/mod_remove_post_view.rs
+++ b/crates/db_views_moderator/src/mod_remove_post_view.rs
@@@ -1,12 -1,12 +1,12 @@@
  use diesel::{result::Error, *};
  use lemmy_db_queries::{limit_and_offset, ToSafe, ViewToVec};
  use lemmy_db_schema::{
--  schema::{community, mod_remove_post, post, user_},
++  schema::{community, mod_remove_post, post, person},
    source::{
      community::{Community, CommunitySafe},
      moderator::ModRemovePost,
      post::Post,
--    user::{UserSafe, User_},
++    person::{PersonSafe, Person},
    },
  };
  use serde::Serialize;
@@@ -14,28 -14,28 +14,28 @@@
  #[derive(Debug, Serialize, Clone)]
  pub struct ModRemovePostView {
    pub mod_remove_post: ModRemovePost,
--  pub moderator: UserSafe,
++  pub moderator: PersonSafe,
    pub post: Post,
    pub community: CommunitySafe,
  }
  
--type ModRemovePostViewTuple = (ModRemovePost, UserSafe, Post, CommunitySafe);
++type ModRemovePostViewTuple = (ModRemovePost, PersonSafe, Post, CommunitySafe);
  
  impl ModRemovePostView {
    pub fn list(
      conn: &PgConnection,
      community_id: Option<i32>,
--    mod_user_id: Option<i32>,
++    mod_person_id: Option<i32>,
      page: Option<i64>,
      limit: Option<i64>,
    ) -> Result<Vec<Self>, Error> {
      let mut query = mod_remove_post::table
--      .inner_join(user_::table)
++      .inner_join(person::table)
        .inner_join(post::table)
        .inner_join(community::table.on(post::community_id.eq(community::id)))
        .select((
          mod_remove_post::all_columns,
--        User_::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
          post::all_columns,
          Community::safe_columns_tuple(),
        ))
@@@ -45,8 -45,8 +45,8 @@@
        query = query.filter(post::community_id.eq(community_id));
      };
  
--    if let Some(mod_user_id) = mod_user_id {
--      query = query.filter(mod_remove_post::mod_user_id.eq(mod_user_id));
++    if let Some(mod_person_id) = mod_person_id {
++      query = query.filter(mod_remove_post::mod_person_id.eq(mod_person_id));
      };
  
      let (limit, offset) = limit_and_offset(page, limit);
diff --cc crates/db_views_moderator/src/mod_sticky_post_view.rs
index 1d726ca7,1d726ca7..9f4ea4af
--- a/crates/db_views_moderator/src/mod_sticky_post_view.rs
+++ b/crates/db_views_moderator/src/mod_sticky_post_view.rs
@@@ -1,12 -1,12 +1,12 @@@
  use diesel::{result::Error, *};
  use lemmy_db_queries::{limit_and_offset, ToSafe, ViewToVec};
  use lemmy_db_schema::{
--  schema::{community, mod_sticky_post, post, user_},
++  schema::{community, mod_sticky_post, post, person},
    source::{
      community::{Community, CommunitySafe},
      moderator::ModStickyPost,
      post::Post,
--    user::{UserSafe, User_},
++    person::{PersonSafe, Person},
    },
  };
  use serde::Serialize;
@@@ -14,28 -14,28 +14,28 @@@
  #[derive(Debug, Serialize, Clone)]
  pub struct ModStickyPostView {
    pub mod_sticky_post: ModStickyPost,
--  pub moderator: UserSafe,
++  pub moderator: PersonSafe,
    pub post: Post,
    pub community: CommunitySafe,
  }
  
--type ModStickyPostViewTuple = (ModStickyPost, UserSafe, Post, CommunitySafe);
++type ModStickyPostViewTuple = (ModStickyPost, PersonSafe, Post, CommunitySafe);
  
  impl ModStickyPostView {
    pub fn list(
      conn: &PgConnection,
      community_id: Option<i32>,
--    mod_user_id: Option<i32>,
++    mod_person_id: Option<i32>,
      page: Option<i64>,
      limit: Option<i64>,
    ) -> Result<Vec<Self>, Error> {
      let mut query = mod_sticky_post::table
--      .inner_join(user_::table)
++      .inner_join(person::table)
        .inner_join(post::table)
        .inner_join(community::table.on(post::community_id.eq(community::id)))
        .select((
          mod_sticky_post::all_columns,
--        User_::safe_columns_tuple(),
++        Person::safe_columns_tuple(),
          post::all_columns,
          Community::safe_columns_tuple(),
        ))
@@@ -45,8 -45,8 +45,8 @@@
        query = query.filter(post::community_id.eq(community_id));
      };
  
--    if let Some(mod_user_id) = mod_user_id {
--      query = query.filter(mod_sticky_post::mod_user_id.eq(mod_user_id));
++    if let Some(mod_person_id) = mod_person_id {
++      query = query.filter(mod_sticky_post::mod_person_id.eq(mod_person_id));
      };
  
      let (limit, offset) = limit_and_offset(page, limit);
diff --cc crates/routes/src/feeds.rs
index 837e1489,105f9662..56810798
--- a/crates/routes/src/feeds.rs
+++ b/crates/routes/src/feeds.rs
@@@ -2,20 -2,25 +2,25 @@@ use actix_web::{error::ErrorBadRequest
  use anyhow::anyhow;
  use chrono::{DateTime, NaiveDateTime, Utc};
  use diesel::PgConnection;
+ use lemmy_api_structs::blocking;
  use lemmy_db_queries::{
--  source::{community::Community_, user::User},
++  source::{community::Community_, person::Person_},
    ListingType,
    SortType,
  };
--use lemmy_db_schema::source::{community::Community, user::User_};
++use lemmy_db_schema::source::{community::Community, person::Person};
  use lemmy_db_views::{
    comment_view::{CommentQueryBuilder, CommentView},
    post_view::{PostQueryBuilder, PostView},
    site_view::SiteView,
  };
--use lemmy_db_views_actor::user_mention_view::{UserMentionQueryBuilder, UserMentionView};
- use lemmy_structs::blocking;
- use lemmy_utils::{claims::Claims, settings::Settings, utils::markdown_to_html, LemmyError};
++use lemmy_db_views_actor::person_mention_view::{PersonMentionQueryBuilder, PersonMentionView};
+ use lemmy_utils::{
+   claims::Claims,
+   settings::structs::Settings,
+   utils::markdown_to_html,
+   LemmyError,
+ };
  use lemmy_websocket::LemmyContext;
  use rss::{
    extension::dublincore::DublinCoreExtensionBuilder,
@@@ -162,13 -167,13 +167,12 @@@ fn get_feed_user
    user_name: String,
  ) -> Result<ChannelBuilder, LemmyError> {
    let site_view = SiteView::read(&conn)?;
--  let user = User_::find_by_username(&conn, &user_name)?;
-   let user_url = user.get_profile_url(&Settings::get().hostname);
 -  let user_url = user.get_profile_url(&Settings::get().hostname());
++  let person = Person::find_by_name(&conn, &user_name)?;
  
    let posts = PostQueryBuilder::create(&conn)
      .listing_type(&ListingType::All)
      .sort(sort_type)
--    .creator_id(user.id)
++    .creator_id(person.id)
      .list()?;
  
    let items = create_post_items(posts)?;
@@@ -176,8 -181,8 +180,8 @@@
    let mut channel_builder = ChannelBuilder::default();
    channel_builder
      .namespaces(RSS_NAMESPACE.to_owned())
--    .title(&format!("{} - {}", site_view.site.name, user.name))
--    .link(user_url)
++    .title(&format!("{} - {}", site_view.site.name, person.name))
++    .link(person.actor_id.to_string())
      .items(items);
  
    Ok(channel_builder)
@@@ -219,11 -224,11 +223,11 @@@ fn get_feed_front
    jwt: String,
  ) -> Result<ChannelBuilder, LemmyError> {
    let site_view = SiteView::read(&conn)?;
--  let user_id = Claims::decode(&jwt)?.claims.id;
++  let person_id = Claims::decode(&jwt)?.claims.id;
  
    let posts = PostQueryBuilder::create(&conn)
      .listing_type(&ListingType::Subscribed)
--    .my_user_id(user_id)
++    .my_person_id(person_id)
      .sort(sort_type)
      .list()?;
  
@@@ -245,19 -250,19 +249,19 @@@
  
  fn get_feed_inbox(conn: &PgConnection, jwt: String) -> Result<ChannelBuilder, LemmyError> {
    let site_view = SiteView::read(&conn)?;
--  let user_id = Claims::decode(&jwt)?.claims.id;
++  let person_id = Claims::decode(&jwt)?.claims.id;
  
    let sort = SortType::New;
  
    let replies = CommentQueryBuilder::create(&conn)
--    .recipient_id(user_id)
--    .my_user_id(user_id)
++    .recipient_id(person_id)
++    .my_person_id(person_id)
      .sort(&sort)
      .list()?;
  
--  let mentions = UserMentionQueryBuilder::create(&conn)
--    .recipient_id(user_id)
--    .my_user_id(user_id)
++  let mentions = PersonMentionQueryBuilder::create(&conn)
++    .recipient_id(person_id)
++    .my_person_id(person_id)
      .sort(&sort)
      .list()?;
  
@@@ -282,7 -287,7 +286,7 @@@
  
  fn create_reply_and_mention_items(
    replies: Vec<CommentView>,
--  mentions: Vec<UserMentionView>,
++  mentions: Vec<PersonMentionView>,
  ) -> Result<Vec<Item>, LemmyError> {
    let mut reply_items: Vec<Item> = replies
      .iter()
diff --cc crates/routes/src/webfinger.rs
index 4059e0f8,e0443734..c444451e
--- a/crates/routes/src/webfinger.rs
+++ b/crates/routes/src/webfinger.rs
@@@ -1,10 -1,10 +1,10 @@@
  use actix_web::{error::ErrorBadRequest, web::Query, *};
  use anyhow::anyhow;
- use lemmy_db_queries::source::{community::Community_, user::User};
- use lemmy_db_schema::source::{community::Community, user::User_};
- use lemmy_structs::{blocking, WebFingerLink, WebFingerResponse};
+ use lemmy_api_structs::{blocking, WebFingerLink, WebFingerResponse};
 -use lemmy_db_queries::source::{community::Community_, user::User};
 -use lemmy_db_schema::source::{community::Community, user::User_};
++use lemmy_db_queries::source::{community::Community_, person::Person_};
++use lemmy_db_schema::source::{community::Community, person::Person};
  use lemmy_utils::{
-   settings::Settings,
+   settings::structs::Settings,
    LemmyError,
    WEBFINGER_COMMUNITY_REGEX,
    WEBFINGER_USER_REGEX,
@@@ -55,11 -55,11 +55,11 @@@ async fn get_webfinger_response
      .await?
      .map_err(|_| ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?
      .actor_id
--  } else if let Some(user_name) = user_regex_parsed {
--    let user_name = user_name.as_str().to_owned();
++  } else if let Some(person_name) = user_regex_parsed {
++    let person_name = person_name.as_str().to_owned();
      // Make sure the requested user exists.
      blocking(context.pool(), move |conn| {
--      User_::read_from_name(conn, &user_name)
++      Person::find_by_name(conn, &person_name)
      })
      .await?
      .map_err(|_| ErrorBadRequest(LemmyError::from(anyhow!("not_found"))))?
diff --cc crates/websocket/src/lib.rs
index 4723c714,4723c714..56b18a17
--- a/crates/websocket/src/lib.rs
+++ b/crates/websocket/src/lib.rs
@@@ -117,10 -117,10 +117,10 @@@ pub enum UserOperation 
    RemoveCommunity,
    FollowCommunity,
    GetFollowedCommunities,
--  GetUserDetails,
++  GetPersonDetails,
    GetReplies,
--  GetUserMentions,
--  MarkUserMentionAsRead,
++  GetPersonMentions,
++  MarkPersonMentionAsRead,
    GetModlog,
    BanFromCommunity,
    AddModToCommunity,
@@@ -128,7 -128,7 +128,7 @@@
    EditSite,
    GetSite,
    AddAdmin,
--  BanUser,
++  BanPerson,
    Search,
    MarkAllAsRead,
    SaveUserSettings,
diff --cc migrations/2021-03-09-171136_split_user_table_2/down.sql
index e1d55a20,00000000..e1d55a20
mode 100644,000000..100644
--- a/migrations/2021-03-09-171136_split_user_table_2/down.sql
+++ b/migrations/2021-03-09-171136_split_user_table_2/down.sql
diff --cc migrations/2021-03-09-171136_split_user_table_2/up.sql
index d20bf021,00000000..db3547e5
mode 100644,000000..100644
--- a/migrations/2021-03-09-171136_split_user_table_2/up.sql
+++ b/migrations/2021-03-09-171136_split_user_table_2/up.sql
@@@ -1,391 -1,0 +1,391 @@@
 +-- Person
 +-- Drop the 2 views user_alias_1, user_alias_2
 +drop view user_alias_1, user_alias_2;
 +
 +-- rename the user_ table to person
 +alter table user_ rename to person;
 +alter sequence user__id_seq rename to person_id_seq;
 +
 +-- create a new table local_user
 +create table local_user (
 +  id serial primary key,
 +  person_id int references person on update cascade on delete cascade not null,
 +  password_encrypted text not null,
-   email text,
++  email text unique,
 +  admin boolean default false not null,
 +  show_nsfw boolean default false not null,
 +  theme character varying(20) default 'darkly'::character varying not null,
 +  default_sort_type smallint default 0 not null,
 +  default_listing_type smallint default 1 not null,
 +  lang character varying(20) default 'browser'::character varying not null,
 +  show_avatars boolean default true not null,
 +  send_notifications_to_email boolean default false not null,
 +  matrix_user_id text,
 +  unique (person_id)
 +);
 +
 +-- Copy the local users over to the new table
 +insert into local_user 
 +(
 +  person_id,
 +  password_encrypted,
 +  email,
 +  admin,
 +  show_nsfw,
 +  theme,
 +  default_sort_type,
 +  default_listing_type,
 +  lang,
 +  show_avatars,
 +  send_notifications_to_email,
 +  matrix_user_id
 +)
 +select
 +  id,
 +  password_encrypted,
 +  email,
 +  admin,
 +  show_nsfw,
 +  theme,
 +  default_sort_type,
 +  default_listing_type,
 +  lang,
 +  show_avatars,
 +  send_notifications_to_email,
 +  matrix_user_id
 +from person
 +where local = true;
 +
 +-- Drop those columns from person
 +alter table person 
 +  drop column password_encrypted,
 +  drop column email,
 +  drop column admin,
 +  drop column show_nsfw,
 +  drop column theme,
 +  drop column default_sort_type,
 +  drop column default_listing_type,
 +  drop column lang,
 +  drop column show_avatars,
 +  drop column send_notifications_to_email,
 +  drop column matrix_user_id;
 +
 +-- Rename indexes
 +alter index user__pkey rename to person__pkey;
 +alter index idx_user_actor_id rename to idx_person_actor_id;
 +alter index idx_user_inbox_url rename to idx_person_inbox_url;
 +alter index idx_user_lower_actor_id rename to idx_person_lower_actor_id;
 +alter index idx_user_published rename to idx_person_published;
 +
 +-- Rename triggers
 +alter trigger site_aggregates_user_delete on person rename to site_aggregates_person_delete;
 +alter trigger site_aggregates_user_insert on person rename to site_aggregates_person_insert;
 +
 +-- Rename the trigger functions
 +alter function site_aggregates_user_delete() rename to site_aggregates_person_delete;
 +alter function site_aggregates_user_insert() rename to site_aggregates_person_insert;
 +
 +-- Create views
 +create view person_alias_1 as select * from person;
 +create view person_alias_2 as select * from person;
 +
 +-- Redo user aggregates into person_aggregates
 +alter table user_aggregates rename to person_aggregates;
 +alter sequence user_aggregates_id_seq rename to person_aggregates_id_seq;
 +alter table person_aggregates rename column user_id to person_id;
 +
 +-- index
 +alter index user_aggregates_pkey rename to person_aggregates_pkey;
 +alter index idx_user_aggregates_comment_score rename to idx_person_aggregates_comment_score;
 +alter index user_aggregates_user_id_key rename to person_aggregates_person_id_key;
 +alter table person_aggregates rename constraint user_aggregates_user_id_fkey to person_aggregates_person_id_fkey;
 +
 +
 +-- Drop all the old triggers and functions
 +drop trigger user_aggregates_user on person;
 +drop trigger user_aggregates_post_count on post;
 +drop trigger user_aggregates_post_score on post_like;
 +drop trigger user_aggregates_comment_count on comment;
 +drop trigger user_aggregates_comment_score on comment_like;
 +drop function 
 +  user_aggregates_user, 
 +  user_aggregates_post_count,
 +  user_aggregates_post_score,
 +  user_aggregates_comment_count,
 +  user_aggregates_comment_score;
 +
 +-- initial user add
 +create function person_aggregates_person()
 +returns trigger language plpgsql
 +as $$
 +begin
 +  IF (TG_OP = 'INSERT') THEN
 +    insert into person_aggregates (person_id) values (NEW.id);
 +  ELSIF (TG_OP = 'DELETE') THEN
 +    delete from person_aggregates where person_id = OLD.id;
 +  END IF;
 +  return null;
 +end $$;
 +
 +create trigger person_aggregates_person
 +after insert or delete on person
 +for each row
 +execute procedure person_aggregates_person();
 +
 +-- post count
 +create function person_aggregates_post_count()
 +returns trigger language plpgsql
 +as $$
 +begin
 +  IF (TG_OP = 'INSERT') THEN
 +    update person_aggregates 
 +    set post_count = post_count + 1 where person_id = NEW.creator_id;
 +
 +  ELSIF (TG_OP = 'DELETE') THEN
 +    update person_aggregates 
 +    set post_count = post_count - 1 where person_id = OLD.creator_id;
 +
 +    -- If the post gets deleted, the score calculation trigger won't fire, 
 +    -- so you need to re-calculate
 +    update person_aggregates ua
 +    set post_score = pd.score
 +    from (
 +      select u.id,
 +      coalesce(0, sum(pl.score)) as score
 +      -- User join because posts could be empty
 +      from person u 
 +      left join post p on u.id = p.creator_id
 +      left join post_like pl on p.id = pl.post_id
 +      group by u.id
 +    ) pd 
 +    where ua.person_id = OLD.creator_id;
 +
 +  END IF;
 +  return null;
 +end $$;
 +
 +create trigger person_aggregates_post_count
 +after insert or delete on post
 +for each row
 +execute procedure person_aggregates_post_count();
 +
 +-- post score
 +create function person_aggregates_post_score()
 +returns trigger language plpgsql
 +as $$
 +begin
 +  IF (TG_OP = 'INSERT') THEN
 +    -- Need to get the post creator, not the voter
 +    update person_aggregates ua
 +    set post_score = post_score + NEW.score
 +    from post p
 +    where ua.person_id = p.creator_id and p.id = NEW.post_id;
 +    
 +  ELSIF (TG_OP = 'DELETE') THEN
 +    update person_aggregates ua
 +    set post_score = post_score - OLD.score
 +    from post p
 +    where ua.person_id = p.creator_id and p.id = OLD.post_id;
 +  END IF;
 +  return null;
 +end $$;
 +
 +create trigger person_aggregates_post_score
 +after insert or delete on post_like
 +for each row
 +execute procedure person_aggregates_post_score();
 +
 +-- comment count
 +create function person_aggregates_comment_count()
 +returns trigger language plpgsql
 +as $$
 +begin
 +  IF (TG_OP = 'INSERT') THEN
 +    update person_aggregates 
 +    set comment_count = comment_count + 1 where person_id = NEW.creator_id;
 +  ELSIF (TG_OP = 'DELETE') THEN
 +    update person_aggregates 
 +    set comment_count = comment_count - 1 where person_id = OLD.creator_id;
 +
 +    -- If the comment gets deleted, the score calculation trigger won't fire, 
 +    -- so you need to re-calculate
 +    update person_aggregates ua
 +    set comment_score = cd.score
 +    from (
 +      select u.id,
 +      coalesce(0, sum(cl.score)) as score
 +      -- User join because comments could be empty
 +      from person u 
 +      left join comment c on u.id = c.creator_id
 +      left join comment_like cl on c.id = cl.comment_id
 +      group by u.id
 +    ) cd 
 +    where ua.person_id = OLD.creator_id;
 +  END IF;
 +  return null;
 +end $$;
 +
 +create trigger person_aggregates_comment_count
 +after insert or delete on comment
 +for each row
 +execute procedure person_aggregates_comment_count();
 +
 +-- comment score
 +create function person_aggregates_comment_score()
 +returns trigger language plpgsql
 +as $$
 +begin
 +  IF (TG_OP = 'INSERT') THEN
 +    -- Need to get the post creator, not the voter
 +    update person_aggregates ua
 +    set comment_score = comment_score + NEW.score
 +    from comment c
 +    where ua.person_id = c.creator_id and c.id = NEW.comment_id;
 +  ELSIF (TG_OP = 'DELETE') THEN
 +    update person_aggregates ua
 +    set comment_score = comment_score - OLD.score
 +    from comment c
 +    where ua.person_id = c.creator_id and c.id = OLD.comment_id;
 +  END IF;
 +  return null;
 +end $$;
 +
 +create trigger person_aggregates_comment_score
 +after insert or delete on comment_like
 +for each row
 +execute procedure person_aggregates_comment_score();
 +
 +-- person_mention
 +alter table user_mention rename to person_mention;
 +alter sequence user_mention_id_seq rename to person_mention_id_seq;
 +alter index user_mention_pkey rename to person_mention_pkey;
 +alter index user_mention_recipient_id_comment_id_key rename to person_mention_recipient_id_comment_id_key;
 +alter table person_mention rename constraint user_mention_comment_id_fkey to person_mention_comment_id_fkey;
 +alter table person_mention rename constraint user_mention_recipient_id_fkey to person_mention_recipient_id_fkey;
 +
 +-- user_ban
 +alter table user_ban rename to person_ban;
 +alter sequence user_ban_id_seq rename to person_ban_id_seq;
 +alter index user_ban_pkey rename to person_ban_pkey;
 +alter index user_ban_user_id_key rename to person_ban_person_id_key;
 +alter table person_ban rename column user_id to person_id;
 +alter table person_ban rename constraint user_ban_user_id_fkey to person_ban_person_id_fkey;
 +
 +-- comment_like
 +alter table comment_like rename column user_id to person_id;
 +alter index idx_comment_like_user rename to idx_comment_like_person;
 +alter table comment_like rename constraint comment_like_comment_id_user_id_key to comment_like_comment_id_person_id_key;
 +alter table comment_like rename constraint comment_like_user_id_fkey to comment_like_person_id_fkey;
 +
 +-- comment_saved
 +alter table comment_saved rename column user_id to person_id;
 +alter table comment_saved rename constraint comment_saved_comment_id_user_id_key to comment_saved_comment_id_person_id_key;
 +alter table comment_saved rename constraint comment_saved_user_id_fkey to comment_saved_person_id_fkey;
 +
 +-- community_follower
 +alter table community_follower rename column user_id to person_id;
 +alter table community_follower rename constraint community_follower_community_id_user_id_key to community_follower_community_id_person_id_key;
 +alter table community_follower rename constraint community_follower_user_id_fkey to community_follower_person_id_fkey;
 +
 +-- community_moderator
 +alter table community_moderator rename column user_id to person_id;
 +alter table community_moderator rename constraint community_moderator_community_id_user_id_key to community_moderator_community_id_person_id_key;
 +alter table community_moderator rename constraint community_moderator_user_id_fkey to community_moderator_person_id_fkey;
 +
 +-- community_user_ban
 +alter table community_user_ban rename to community_person_ban;
 +alter sequence community_user_ban_id_seq rename to community_person_ban_id_seq;
 +alter table community_person_ban rename column user_id to person_id;
 +alter table community_person_ban rename constraint community_user_ban_pkey to community_person_ban_pkey; 
 +alter table community_person_ban rename constraint community_user_ban_community_id_fkey to community_person_ban_community_id_fkey;
 +alter table community_person_ban rename constraint community_user_ban_community_id_user_id_key to community_person_ban_community_id_person_id_key;
 +alter table community_person_ban rename constraint community_user_ban_user_id_fkey to community_person_ban_person_id_fkey;
 +
 +-- mod_add
 +alter table mod_add rename column mod_user_id to mod_person_id; 
 +alter table mod_add rename column other_user_id to other_person_id; 
 +alter table mod_add rename constraint mod_add_mod_user_id_fkey to mod_add_mod_person_id_fkey;
 +alter table mod_add rename constraint mod_add_other_user_id_fkey to mod_add_other_person_id_fkey;
 +
 +-- mod_add_community
 +alter table mod_add_community rename column mod_user_id to mod_person_id; 
 +alter table mod_add_community rename column other_user_id to other_person_id; 
 +alter table mod_add_community rename constraint mod_add_community_mod_user_id_fkey to mod_add_community_mod_person_id_fkey;
 +alter table mod_add_community rename constraint mod_add_community_other_user_id_fkey to mod_add_community_other_person_id_fkey;
 +
 +-- mod_ban
 +alter table mod_ban rename column mod_user_id to mod_person_id; 
 +alter table mod_ban rename column other_user_id to other_person_id; 
 +alter table mod_ban rename constraint mod_ban_mod_user_id_fkey to mod_ban_mod_person_id_fkey;
 +alter table mod_ban rename constraint mod_ban_other_user_id_fkey to mod_ban_other_person_id_fkey;
 +
 +-- mod_ban_community
 +alter table mod_ban_from_community rename column mod_user_id to mod_person_id; 
 +alter table mod_ban_from_community rename column other_user_id to other_person_id; 
 +alter table mod_ban_from_community rename constraint mod_ban_from_community_mod_user_id_fkey to mod_ban_from_community_mod_person_id_fkey;
 +alter table mod_ban_from_community rename constraint mod_ban_from_community_other_user_id_fkey to mod_ban_from_community_other_person_id_fkey;
 +
 +-- mod_lock_post
 +alter table mod_lock_post rename column mod_user_id to mod_person_id; 
 +alter table mod_lock_post rename constraint mod_lock_post_mod_user_id_fkey to mod_lock_post_mod_person_id_fkey;
 +
 +-- mod_remove_comment
 +alter table mod_remove_comment rename column mod_user_id to mod_person_id; 
 +alter table mod_remove_comment rename constraint mod_remove_comment_mod_user_id_fkey to mod_remove_comment_mod_person_id_fkey;
 +
 +-- mod_remove_community
 +alter table mod_remove_community rename column mod_user_id to mod_person_id; 
 +alter table mod_remove_community rename constraint mod_remove_community_mod_user_id_fkey to mod_remove_community_mod_person_id_fkey;
 +
 +-- mod_remove_post
 +alter table mod_remove_post rename column mod_user_id to mod_person_id; 
 +alter table mod_remove_post rename constraint mod_remove_post_mod_user_id_fkey to mod_remove_post_mod_person_id_fkey;
 +
 +-- mod_sticky_post
 +alter table mod_sticky_post rename column mod_user_id to mod_person_id; 
 +alter table mod_sticky_post rename constraint mod_sticky_post_mod_user_id_fkey to mod_sticky_post_mod_person_id_fkey;
 +
 +-- password_reset_request
 +delete from password_reset_request;
 +alter table password_reset_request drop column user_id;
 +alter table password_reset_request add column local_user_id integer not null references local_user(id) on update cascade on delete cascade;
 +
 +-- post_like
 +alter table post_like rename column user_id to person_id;
 +alter index idx_post_like_user rename to idx_post_like_person;
 +alter table post_like rename constraint post_like_post_id_user_id_key to post_like_post_id_person_id_key;
 +alter table post_like rename constraint post_like_user_id_fkey to post_like_person_id_fkey;
 +
 +-- post_read
 +alter table post_read rename column user_id to person_id;
 +alter table post_read rename constraint post_read_post_id_user_id_key to post_read_post_id_person_id_key;
 +alter table post_read rename constraint post_read_user_id_fkey to post_read_person_id_fkey;
 +
 +-- post_saved
 +alter table post_saved rename column user_id to person_id;
 +alter table post_saved rename constraint post_saved_post_id_user_id_key to post_saved_post_id_person_id_key;
 +alter table post_saved rename constraint post_saved_user_id_fkey to post_saved_person_id_fkey;
 +
 +-- redo site aggregates trigger
 +create or replace function site_aggregates_activity(i text) returns integer
 +    language plpgsql
 +    as $$
 +declare
 +   count_ integer;
 +begin
 +  select count(*)
 +  into count_
 +  from (
 +    select c.creator_id from comment c
 +    inner join person u on c.creator_id = u.id
 +    where c.published > ('now'::timestamp - i::interval) 
 +    and u.local = true
 +    union
 +    select p.creator_id from post p
 +    inner join person u on p.creator_id = u.id
 +    where p.published > ('now'::timestamp - i::interval)
 +    and u.local = true
 +  ) a;
 +  return count_;
 +end;
 +$$;
diff --cc restore_db.sh
index 00000000,00000000..318b9909
new file mode 100755
--- /dev/null
+++ b/restore_db.sh
@@@ -1,0 -1,0 +1,5 @@@
++#!/bin/bash
++
++psql -U lemmy -c "DROP SCHEMA public CASCADE; CREATE SCHEMA public;"
++cat docker/dev/lemmy_dump_2021-01-29_16_13_40.sqldump | psql -U lemmy
++psql -U lemmy -c "alter user lemmy with password 'password'"
diff --cc src/code_migrations.rs
index ea11e32d,f3bc3df5..009b7afb
--- a/src/code_migrations.rs
+++ b/src/code_migrations.rs
@@@ -72,7 -72,7 +72,7 @@@ fn user_updates_2020_04_02(conn: &PgCon
        lang: cuser.lang.to_owned(),
        show_avatars: cuser.show_avatars,
        send_notifications_to_email: cuser.send_notifications_to_email,
--      actor_id: Some(generate_apub_endpoint(EndpointType::User, &cuser.name)?),
++      actor_id: Some(generate_apub_endpoint(EndpointType::Person, &cuser.name)?),
        bio: Some(cuser.bio.to_owned()),
        local: cuser.local,
        private_key: Some(keypair.private_key),