8b0e1eb5ab
Datacenter/hosting-IP-flagged signups are a strong enough spam signal on their own that a full day of grace was mostly just delaying an inevitable suspend. Cron cadence tightened from hourly to every 10 minutes so real latency stays close to the new 1h window.
184 lines
8.8 KiB
Bash
184 lines
8.8 KiB
Bash
# Copy to .env and fill in. Do NOT commit .env.
|
|
|
|
# HMAC secret shown when you create the webhook in Mastodon
|
|
# (Administration -> Webhooks). Used to verify X-Hub-Signature.
|
|
WEBHOOK_SECRET=
|
|
|
|
# Access token for the bot account that sends the welcome DMs.
|
|
# Create a bot account on yttrx, then Preferences -> Development ->
|
|
# New application with scope: write:statuses (copy "Your access token").
|
|
BOT_ACCESS_TOKEN=
|
|
|
|
# Base URL of the Mastodon instance (no trailing slash).
|
|
MASTODON_BASE_URL=https://yttrx.com
|
|
|
|
# Welcome message template. {acct} is replaced with the new user's handle.
|
|
# The bot mentions @{acct} so a "direct" status reaches them.
|
|
WELCOME_MESSAGE=👋 Welcome to yttrx, @{acct}! Glad to have you here. If you have any questions, check out https://help.yttrx.com or just reply to this message.
|
|
|
|
# Only welcome accounts local to this instance (recommended: true).
|
|
LOCAL_ONLY=true
|
|
|
|
# Path to the sqlite dedup store inside the container (matches the volume).
|
|
DB_PATH=/data/welcomed.db
|
|
|
|
# --- Abuse-bot (report.created handling) -----------------------------------
|
|
# Access token for a DEDICATED MODERATOR bot account. That account must hold a
|
|
# role with the "Manage Users" + "Manage Reports" permissions, and the app
|
|
# token must request these scopes:
|
|
# admin:write:accounts admin:read:reports read:statuses write:statuses
|
|
# Leave blank to disable report handling entirely.
|
|
ABUSE_BOT_TOKEN=
|
|
|
|
# Master switch for report handling (also off if ABUSE_BOT_TOKEN is blank).
|
|
ABUSE_ENABLED=true
|
|
|
|
# Rollout safety: when true, log + DM what WOULD happen but take no action.
|
|
# Recommended for the first days in production; flip to false once happy.
|
|
ABUSE_DRY_RUN=true
|
|
|
|
# Moderation action: "silence" (reversible, agreed default) or "suspend".
|
|
ABUSE_ACTION=silence
|
|
|
|
# Only auto-act on accounts local to yttrx.
|
|
ABUSE_LOCAL_ONLY=true
|
|
|
|
# Account tiers (in days):
|
|
# young = oldest post newer than ABUSE_YOUNG_MAX_DAYS
|
|
# dormant = newest post older than ABUSE_DORMANT_MIN_DAYS
|
|
ABUSE_YOUNG_MAX_DAYS=30
|
|
ABUSE_DORMANT_MIN_DAYS=30
|
|
|
|
# Required number of DISTINCT reporter accounts (open reports) to auto-act:
|
|
# young / dormant / no-posts -> ABUSE_SOURCES_NEWDORMANT
|
|
# normally-active -> ABUSE_SOURCES_ACTIVE
|
|
ABUSE_SOURCES_NEWDORMANT=2
|
|
ABUSE_SOURCES_ACTIVE=3
|
|
|
|
# Safety cap on the backward status scan (40 statuses per page).
|
|
ABUSE_MAX_STATUS_PAGES=5
|
|
|
|
# Automatically never auto-act on accounts holding a staff role
|
|
# (Admin/Owner/Moderator), read from the report payload. Keep this true.
|
|
ABUSE_SKIP_PRIVILEGED=true
|
|
|
|
# Extra comma-separated handles (acct, no leading @) never to auto-act on —
|
|
# a backstop on top of ABUSE_SKIP_PRIVILEGED, and for non-staff special
|
|
# accounts. e.g. ABUSE_ALLOWLIST=waffles,tommertron,davis,bot
|
|
ABUSE_ALLOWLIST=
|
|
|
|
# Handle (acct, no @) to DM with a review summary after an auto-action.
|
|
# Leave blank to disable the DM. The DM is sent from the moderator bot.
|
|
MOD_ALERT_ACCT=
|
|
|
|
# Help/appeals page the silenced user is pointed to.
|
|
ABUSE_HELP_URL=https://welcome.yttrx.com/posts/account-limited/
|
|
|
|
# DM sent to the silenced user ({acct} + {help_url} substituted; must mention
|
|
# @{acct}). Leave blank to disable the user DM.
|
|
ABUSE_USER_DM=Hi @{acct} — your yttrx account has been temporarily limited while we review some reports. This is reversible and a moderator will take a look. Here's what happened and how to appeal: {help_url}
|
|
|
|
# --- IP-based signup scrutiny -----------------------------------------------
|
|
# Classifies each new signup's IP (Admin::Account.ip, delivered free on the
|
|
# account.created webhook) via RDAP org lookup, and treats non-residential/
|
|
# non-mobile ("datacenter"/hosting/VPN) signups with more scrutiny. Master
|
|
# switch:
|
|
IP_SCRUTINY_ENABLED=true
|
|
|
|
# Rollout safety: when true, classify + DM a moderator but take NO action
|
|
# (no held welcome, no ip_blocks write). Recommended for the first days in
|
|
# production; flip to false once you've reviewed the false-positive rate.
|
|
IP_SCRUTINY_DRY_RUN=true
|
|
|
|
# Hold the welcome DM for a flagged signup until account.approved fires
|
|
# (a human clears the existing approval-required registration gate), instead
|
|
# of welcoming immediately like every other signup.
|
|
IP_SCRUTINY_HOLD_WELCOME=true
|
|
|
|
# Distinct-reporter threshold used instead of the tier's usual
|
|
# ABUSE_SOURCES_* threshold (whichever is lower) when the reported account's
|
|
# signup IP was flagged as datacenter/hosting.
|
|
IP_SCRUTINY_ABUSE_THRESHOLD=1
|
|
|
|
# Auto-register a flagged IP into Mastodon's native Admin::IpBlock. Requires
|
|
# the ABUSE_BOT_TOKEN to carry the admin:write:ip_blocks scope (see
|
|
# CLAUDE.md's moderator-token-gotcha section) — without it this 403s and is
|
|
# logged as an error, but nothing else in the bot is affected.
|
|
IP_SCRUTINY_AUTO_IPBLOCK=true
|
|
|
|
# Severity applied to auto-registered blocks: sign_up_requires_approval (soft,
|
|
# recommended), sign_up_block (hard — no queue, cannot appeal via this bot),
|
|
# or no_access (blocks all access, not just signups).
|
|
IP_SCRUTINY_IPBLOCK_SEVERITY=sign_up_requires_approval
|
|
|
|
# Case-insensitive regexes matched against the RDAP org/ASN description.
|
|
# Anything matching neither is treated as "residential" (never flagged).
|
|
# Only IP_SCRUTINY_HOSTING_RE matches are flagged; IP_SCRUTINY_MOBILE_RE is
|
|
# informational only (mobile carriers are never scrutinized).
|
|
IP_SCRUTINY_HOSTING_RE=amazon|aws|google|microsoft|azure|digitalocean|ovh|hetzner|linode|vultr|contabo|choopa|m247|scaleway|leaseweb|hostinger|namecheap|godaddy|cloudflare|oracle|alibaba|tencent|akamai|fastly|packet|vpn|proxy|hosting|datacenter|data center|colo(?:cation)?|server
|
|
IP_SCRUTINY_MOBILE_RE=mobile|wireless|cellular|verizon|t-mobile|tmobile|at&t|att mobility|vodafone|telstra|sprint|three\b|orange mobile|deutsche telekom|o2\b
|
|
|
|
# check-mail.org disposable/high-risk email domain scrutiny (roadmap item B,
|
|
# anti-abuse.md). Domain-only query (never the full email) against
|
|
# POST https://api.check-mail.org/v2/, Authorization: Bearer <key>.
|
|
# Free tier: 1,000 req/month.
|
|
CHECK_MAIL_ENABLED=true
|
|
CHECK_MAIL_API_KEY=
|
|
|
|
# Log/DM only — no held welcome, no email_domain_block write, no
|
|
# report-triggered suspend. Keep true for rollout until you've watched the
|
|
# false-positive rate for a while (same role as IP_SCRUTINY_DRY_RUN /
|
|
# ABUSE_DRY_RUN).
|
|
CHECK_MAIL_DRY_RUN=true
|
|
|
|
# A domain is flagged if check-mail.org marks it is_disposable OR its risk
|
|
# score (0-100) is at or above this threshold.
|
|
CHECK_MAIL_RISK_THRESHOLD=80
|
|
|
|
# Hold the welcome DM for a flagged signup until account.approved fires, same
|
|
# semantics as IP_SCRUTINY_HOLD_WELCOME.
|
|
CHECK_MAIL_HOLD_WELCOME=true
|
|
|
|
# Auto-register a flagged signup's email domain into Mastodon's native
|
|
# Admin::EmailDomainBlock. Requires ABUSE_BOT_TOKEN to carry the
|
|
# admin:write:email_domain_blocks scope (see CLAUDE.md) — without it this
|
|
# 403s and is logged as an error, but nothing else in the bot is affected.
|
|
# Also gates the report-triggered suspend+block override in handle_report().
|
|
CHECK_MAIL_AUTO_DOMAIN_BLOCK=true
|
|
|
|
# --- Suspicious-signup sweep (app/suspicious_sweep.py, run on a schedule) ---
|
|
# Watches every account flagged by EITHER IP-scrutiny or email-domain
|
|
# scrutiny at signup. Once the account goes live (account.created if open/
|
|
# auto-approved, account.approved if this instance requires moderator
|
|
# approval — same dual handling the welcome flow already uses), a baseline
|
|
# post/follow count is recorded. A scheduled sweep (cron on admin.yttrx.com,
|
|
# `docker exec yttrx-welcomebot python -m app.suspicious_sweep`) then acts on
|
|
# any watch past SUSPICIOUS_GRACE_HOURS with zero new posts AND zero new
|
|
# follows since that baseline. Master switch:
|
|
SUSPICIOUS_SWEEP_ENABLED=true
|
|
|
|
# Hours of grace given to a flagged signup to show organic activity (a new
|
|
# post or a new follow) before it's considered dormant/bot-like. Lowered from
|
|
# the original 24 to 1 (2026-07-06) — the datacenter/hosting-IP signal alone
|
|
# is a strong enough spam indicator that a full day of grace was mostly just
|
|
# delaying an inevitable suspend; paired with a tighter sweep cadence (see
|
|
# CLAUDE.md) so real latency stays close to this value.
|
|
SUSPICIOUS_GRACE_HOURS=1
|
|
|
|
# Moderation action taken on a flagged signup with no activity in the grace
|
|
# window: "suspend" (agreed default — a stronger signal than the report-
|
|
# driven abuse-bot's default "silence", since this is zero organic activity,
|
|
# not just reports) or "silence".
|
|
SUSPICIOUS_ACTION=suspend
|
|
|
|
# Rollout safety switch, same role as ABUSE_DRY_RUN — ships "false" (live)
|
|
# here by design; flip to "true" to pause without redeploying.
|
|
SUSPICIOUS_DRY_RUN=false
|
|
|
|
# Requires ABUSE_BOT_TOKEN to additionally carry the admin:read:accounts
|
|
# scope (fetches current post/follow counts + role/suspended state via
|
|
# GET /api/v1/admin/accounts/:id) — see CLAUDE.md's moderator-token-gotcha
|
|
# section. Without it the sweep 403s per-account (logged as an error, that
|
|
# account is retried next sweep) but nothing else in the bot is affected.
|
|
# Reuses ABUSE_SKIP_PRIVILEGED and ABUSE_ALLOWLIST above — no separate vars.
|