Skip to main content

Account Moderators

Account owners can delegate scoped permissions to trusted users ("moderators") so those moderators can post, make clips, go live, create chats, and send messages on behalf of the owner's account.

Granting or changing these permissions requires the owner to have two-factor authentication enabled — a hijacked account can't be used to silently hand full access to an attacker. Revoking a moderator is always allowed (no 2FA needed), so an owner who lost their authenticator can still cut off access.

  • Add a moderator (search a user, grant permissions)
  • Per-permission toggles: Post, Clips, Go live, Post stories, Create chats, Send messages, Comment, React, Manage comments
  • 2FA gate on add/update (blocked with a CTA to enable 2FA when it's off)
  • Step-up re-auth: adding/updating moderators also asks the owner to re-enter their password if they haven't authenticated recently
  • Remove a moderator (no 2FA required)
  • "Accounts I moderate" list for the moderator
  • Server-side enforcement of each permission when acting on behalf of an owner
  • "Publicar como" selector in the post composer for moderators
  • "Comentar como" selector in the comment box for moderators
  • "Acting as" UI on the live / new-chat / message composers (GoLiveModal, ConversationList, ChatView all now offer the same selector the post composer and comment box already had; this page previously said only posts/clips and comments were wired — corrected)

Permission model

PermissionGated actionEnforced in
canPostCreate a regular postcreatePost
canClipCreate a clip (single video)createPost (detected as a clip)
canLiveStart a livecreateLiveStream
canStoryPost a storycreateStory
canCreateChatsCreate a conversationcreateConversation
canSendMessagesSend a messagesendMessage
canCommentComment / reply on postscreateComment
canReactReact / like a postlikePost
canManageCommentsModerate (delete) comments on the owner's postsdeleteComment

What is intentionally never delegated (owner-only, no matter what): password, email, phone, 2FA, adding/editing other moderators, withdrawals / spending coins, privacy settings, blocking users, and deleting/deactivating the account. Delegation covers content and engagement, not account control or money.

How "acting as" works

Each of the create/send mutations — plus likePost and deleteComment — accepts an optional actAsUserId. The resolver calls accountModeratorManager.resolveActingUser(callerId, actAsUserId, action):

  • If actAsUserId is unset or equals the caller → the caller acts as themselves.
  • If set and the caller is a permitted moderator of that owner → the owner becomes the effective author (post userId, live userId, conversation creator_id, message senderId).
  • Otherwise → the mutation throws moderators.not_permitted.

For messages, the access check ("is a participant") is run against the effective sender (the owner), so the owner must be in the conversation.

Data model

account_moderator table:

ColumnMeaning
owner_idThe account being moderated
moderator_user_idThe delegated user
can_post, can_clip, can_live, can_story, can_create_chats, can_send_messages, can_comment, can_react, can_manage_commentsPermission flags

Unique on (owner_id, moderator_user_id).

Audit trail (who did what)

Every action a moderator performs on the owner's behalf is recorded in the account_moderator_action table (owner_id, moderator_user_id, action, acted_as, target_type, target_id, metadata, created_at). The owner reads their own feed via the moderatorActivity(limit, offset, action, moderatorUserId) query (ModeratorAction type), shown newest-first in Settings → Moderadores as "Actividad de moderadores". Actions performed as the moderator's own identity are not logged as owner-actions. Logging is best-effort: a logging failure never blocks the underlying action.

Message attribution (who really sent it)

When a moderator sends a message as the owner, the real sender is stamped into the message's metadata.viaModeratorId. The Message.viaModerator: User field resolves that back to the moderator, but only for the owner (the effective sender) — other participants just see the message as coming from the owner. This lets the owner always trace which moderator sent any given message under their name.

Commenting as yourself or as the owner

PostCommentCreateInput gains actAsUserId. A moderator with canComment can comment either under their own name (default) or as the owner (actAsUserId = ownerId). In the web UI (PostModal), a moderator of the post's author sees a "Comentar como: [Yo] / [@owner]" selector above the comment box. Commenting as the owner is recorded in the audit trail.

GraphQL

  • Queries: myModerators, accountsIModerate, moderatorActivity (owner audit feed).
  • Mutations: addModerator(userId, permissions), updateModeratorPermissions(userId, permissions) (both 2FA-gated), removeModerator(userId), verifyPasswordForModerators(password) (step-up re-auth for the two 2FA-gated mutations above).
  • The create/send inputs gain actAsUserId; likePost and deleteComment take it as a plain argument instead.

Key files

  • Backend: database/models/AccountModerator.js, data-access-services/moderator/account-moderator.access-service.js, managers/user-managers/account-moderator.manager.js, graphql/types/account-moderator.type.js, graphql/resolvers/account-moderator.resolver.js; actAsUserId wiring in post.resolver.js (createPost, createStory), post-interaction.resolver.js (likePost), live-stream.resolver.js, conversation.resolver.js, message.resolver.js, post-comment.resolver.js (createComment, deleteComment).
  • Frontend: page-components/settings/ModeratorsPage.tsx (expanded permission toggles + activity feed), "Publicar como" in components/CreatePostModal.tsx, "Comentar como" selector in components/PostModal.tsx, "acting as" selector in components/live/GoLiveModal.tsx, components/chat/ConversationList.tsx, and components/chat/ChatView.tsx.
  • Audit/attribution: database/models/AccountModeratorAction.js, data-access-services/moderator/account-moderator-action.access-service.js; logAction/getModeratorActivity in account-moderator.manager.js; Message.viaModerator in message.type.js/message.resolver.js.

Configuration

Run the 20260721130000-create-account-moderator and 20260721150000-expand-account-moderator migrations and restart the backend. No new environment variables. The owner must enable 2FA (Settings → Security) before adding moderators; sensitive moderator changes also require a recent password re-entry (step-up auth).