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,ChatViewall 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
| Permission | Gated action | Enforced in |
|---|---|---|
canPost | Create a regular post | createPost |
canClip | Create a clip (single video) | createPost (detected as a clip) |
canLive | Start a live | createLiveStream |
canStory | Post a story | createStory |
canCreateChats | Create a conversation | createConversation |
canSendMessages | Send a message | sendMessage |
canComment | Comment / reply on posts | createComment |
canReact | React / like a post | likePost |
canManageComments | Moderate (delete) comments on the owner's posts | deleteComment |
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
actAsUserIdis 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, liveuserId, conversationcreator_id, messagesenderId). - 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:
| Column | Meaning |
|---|---|
owner_id | The account being moderated |
moderator_user_id | The delegated user |
can_post, can_clip, can_live, can_story, can_create_chats, can_send_messages, can_comment, can_react, can_manage_comments | Permission 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;likePostanddeleteCommenttake 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;actAsUserIdwiring inpost.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" incomponents/CreatePostModal.tsx, "Comentar como" selector incomponents/PostModal.tsx, "acting as" selector incomponents/live/GoLiveModal.tsx,components/chat/ConversationList.tsx, andcomponents/chat/ChatView.tsx. - Audit/attribution:
database/models/AccountModeratorAction.js,data-access-services/moderator/account-moderator-action.access-service.js;logAction/getModeratorActivityinaccount-moderator.manager.js;Message.viaModeratorinmessage.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).