Skip to main content

Moderation and auto-moderation

Node admins run the node from its administration page; flood detection, reports and content rules help rooms and the node against spam and abuse.

The first account registered on a node becomes node admin (plus anyone listed in TAVRN_ADMIN_USERS). Node admins get a “Node administration” page in the client: overview and effective configuration, users (disable, promote, room-creation grants, delete all content, reset a locked-out user’s second factor), account invites for TAVRN_REGISTRATION=invite, node-wide bans, other nodes with their conformance results (block, unblock, check), entitlements, legal pages and notices, and the node audit log. Room moderation (kicks, bans, timeouts, roles and permissions) is done by each room’s owners and moderators.

Room permissions are layered (protocol permissions.md): roles give the base, a category’s overrides apply to all of its channels, and a channel’s own overrides win over the category’s. Overrides only hold channel-scoped permissions; at start the node removes room-wide permissions from overrides created by older versions and records each change in the room’s audit log (role.override_restricted). Nothing needs to be done by the operator.

A channel that a member may not view (View channel after roles and overrides) does not exist for that member, and the node enforces this everywhere it sends data, not only in the client (protocol: PROTOCOL.md §6). Such a member does not get:

  • the channel itself: GET /api/v1/rooms/{id} leaves it and its overrides out, and a category without a visible channel is left out too (unless the member may manage channels);
  • its realtime events: messages, edits, reactions, pins, typing, threads, voice participants (voice.state, also in GET /users/me/voice), whiteboard sessions and the channel’s own changes;
  • notifications about it: no unread or mention badges, no web push, and no notify hint to the member’s home node when the member is a user of another node;
  • its history, search results, files and encryption group.

When a change of roles, overrides or membership shows or hides a channel, the member’s clients get the channel (or its removal, marked as “hidden”, not deleted) with the next event, so nothing has to reload. A channel above a member’s age gate stays listed but locked (Age gates); a moderator’s move into a voice channel shows it for the time of the move (Moved members). Room moderators still see reports about hidden channels (Reports). With several instances each instance applies changes as their events arrive over the bus (Audit of the in-memory state).

TAVRN_AUTOMOD_BURST_LIMIT (default 10): more than this many messages of one user in one room channel within 5 seconds count as a flood. The node deletes the burst (with its attachments), gives the user a room timeout of TAVRN_AUTOMOD_TIMEOUT_MINUTES (default 10), alerts the room’s moderators and writes automod.spam to the room’s audit log. Room owners, moderators (permission to moderate members) and node admins are exempt. 0 switches the detection off. It works on message metadata only, so it applies to encrypted channels too.

What the node does is specified in MODERATION.md. Members report messages, members and rooms from the context menus. Reports about room content land in the room’s “Reports” settings tab (moderators with moderate_members); reports about direct messages, about users without a room, about rooms and about a room’s owner land in Admin → Reports, where node admins also see every room report. Reporters are visible to moderators; the reported user is never told. For end-to-end encrypted messages the reporter can release the plaintext of the reported message and up to ten messages before it: the inbox shows it labelled “released by the reporter”, the node cannot verify it. Plaintext messages are linked and snapshotted instead. The inbox offers shortcuts (delete the message, time out, kick, ban, delete the user’s content) that run the normal moderation actions and note the report in the audit log. Every moderator of a room sees the room’s reports, including those about channels hidden from them: moderation needs the context, and the reporter addressed the room’s moderators as a whole (MODERATION.md §1.1). Give a private area that needs its own moderators its own room.

TAVRN_REPORTS_RATE_PER_HOUR (default 10) limits reports per user, TAVRN_REPORTS_RETENTION_DAYS (default 90) deletes resolved and dismissed reports with their evidence; admins can delete a report at any time. Reports are announced in the client (bell and toast) and, to moderators and admins who are not connected, by a web push that names only the report’s kind and room (each user can turn it off: “Reports” in the notification settings).

Moderation bots can read a room’s reports: a bot whose token has the scope moderation and that holds “Moderate members” in the room (give it a role with that permission) lists the room’s reports and receives new ones and changes, for example to post a summary in a moderators’ channel or to escalate after several reports about one user. Bots see metadata only: kind, category, the reported user, status, counts, times and the id of the reported message, never the reporter’s text, the snapshot, released plaintext, your notes or actions, and the reporter only as a pseudonym (the same for the same reporter in that room, never the address). Bots never see reports about themselves, their owner, their owner’s other bots or the room’s owner, and they cannot report, change, act on, forward or delete reports: acting stays with people (MODERATION.md §1.4a). Take the permission away to close the inbox for the bot.

A report about a user of another node (a member from elsewhere in one of your rooms, or the author of a direct message hosted elsewhere) lands with you, but only the admins of that user’s home node can act on the account. Node admins can therefore forward such a report from Admin → Reports (“Forward to the user’s home node”, after a confirmation); the home node must announce reports.v2 (current tavrn nodes with federation on do). Forwarding is a federation message (report.forward, retried for three days like every federation message) and happens once per report; the report stays in your inbox with “Forwarded to …” and you keep handling it as before.

What is forwarded: the category, the reporter’s text, the time, the message id, the snapshot of a plaintext message or the plaintext the reporter released for an encrypted one. What is not: the reporter’s address (the home node sees a pseudonym that stays the same for the same reporter, but cannot be linked across nodes), the addresses and devices of other people in the released messages (pseudonyms too), room names and ids, your notes and actions. The reporter and the reported user are told nothing. The text itself goes as it is: read it before you forward, since the other node’s admins, possibly the reported user themselves on a one-person node, will read it.

Forwarded reports from other nodes appear in Admin → Reports marked “Forwarded from …” with the pseudonym of the reporter; they are node reports about your user and offer the account actions (node ban, delete the user’s content), not the message actions (the content lives on the other node). Each original report arrives once; other nodes may forward TAVRN_REPORTS_INBOUND_PER_HOUR (default 20) reports per hour per sending domain, five times as many from all nodes together. Retention is the same as for every report (TAVRN_REPORTS_RETENTION_DAYS after closing; open reports are kept), on each node independently: deleting a report on one node does not delete the other node’s copy, and an erased account’s forwarded reports lose their target but keep the origin. Both directions are in the audit log (report.forward, report.forwarded_in).

Content rules only work where the node can read: unencrypted channels (never ciphertext). Rooms define their own rules (room settings → Auto-moderation: word lists with * wildcards, link allow and block lists, exempt roles and channels; actions block, delete or timeout), checked on send and edit, including attachment file names and bots’ interaction replies. Delete and timeout rules act only on members ranked below whoever last saved the rules (as that member’s own moderation would), and a timeout is shown to the room like a moderator’s. Matching runs off the request threads with a fixed amount of work per message; a message that would need more is refused. As operator you can add node-wide lists that block in every unencrypted conversation: TAVRN_AUTOMOD_WORDS_FILE=/data/automod/words.txt (one term per line, whole words, # comments) and TAVRN_AUTOMOD_LINKS=/data/automod/domains.txt (one domain per line, subdomains included; off by default). Both files are read at start (at most 1 MiB and 10000 entries; check-config names problems) and read again, without a restart, by the hourly housekeeping and when the node gets SIGHUP (docker compose kill -s HUP tavrn; with several instances every instance reads its own copy, so signal each one or wait for the hour). A reload validates both files like check-config: when one of them is invalid or unreadable, the node keeps both previous lists and logs a warning naming the problem; otherwise it logs how many terms and domains were added and removed (“Auto-moderation node lists reloaded”). Point the variables at a file, not at a path that does not exist yet: the paths themselves are read from the configuration at start. Matches are audited with the matched term, never the message text, and show up in the moderators’ inbox.

The welcome channel is the room’s security log: its entries cannot be deleted or purged by anyone. It never names a channel that @everyone cannot view: when a bot joins or leaves the encryption group of such a channel, the entry is written into that channel as a security notice instead (visible to the channel’s members only, never counted as unread, protected like the welcome channel’s entries). Room ownership moves with room settings → Overview → “Transfer ownership” (the owner confirms with their password or passkey); accounts that own rooms must transfer or delete them before they can be deleted.

Every room has an audit log (room settings → Audit log, for members with View audit log), and the node has one for its admins (Node administration → Audit log, GET /api/v1/admin/audit-log). Entries name the action, who did it, the target, a reason where one was given and details; they never contain message text, credentials or a date of birth. Some of the actions they record:

Audit logExamples
Roommember.kick, member.ban, member.unban, member.timeout, moderation.purge, voice.move, voice.mute, room.update, room.legal, room.transfer, room.age_gate, channel.age_gate, invite.direct, automod.spam, automod.content, files.delete, files.backend, history.restore_requested, history.restore_approved, history.restore_denied, role.override_restricted, role.everyone_restricted
Nodeuser.register, user.second_factor_enroll, user.recovery_code_used, admin.user_disable, admin.user_delete, admin.second_factor_reset, admin.date_of_birth, admin.ban, admin.federation_block, federation.block_auto, federation.user_deleted, app.create, app.token_create, webhook.create, entitlements.grant_create, legal.*, retention.messages, directory.reports_notice, report.forward, report.forwarded_in

Moderation shortcuts of the reports inbox add the report they act on (report_id). The node keeps audit entries as long as the room (room log) or the node (node log) exists; deleted accounts stay in them pseudonymised (LEGAL.md).

A user who resets their security key (lost every device and the recovery key) keeps their rooms but not their encrypted history: the node stops serving the encrypted messages from before the reset to them, and other members’ apps no longer hand those keys over automatically (protocol: ENCRYPTION.md §7b). The member can ask the room for the history; the owner, administrators and roles with Restore history (restore_history, never @everyone, never a bot) approve or deny it in room settings → History requests. The approving member’s app sends the keys from its own device, so an approval restores only what that device can read. Requests, approvals and denials are in the room’s audit log (history.restore_requested, history.restore_approved, history.restore_denied with the reason) and the decision is an entry in the welcome channel. Rooms switch requests off in room settings → Overview (“History after a key reset”); remote members’ resets are learned through federation like the key change itself. There is nothing to configure and nothing an operator can restore: the node never holds the keys. Decided requests are deleted after 30 days by the housekeeping; an approval stays valid for key shares for 30 days. Tell moderators to verify the person’s identity on another channel before approving: a key reset is also what someone who took over the account would do.


This page is generated from tavrn-node/docs/HANDBOOK.md (AGPL-3.0-only) when the website is built; its links were adapted for this site.