Files
yttrx-welcomebot/.env.example
T
pmb 0356fe7997 Replace RDAP/ipwhois signup-IP classifier with ipapi.is
Flags datacenter, vpn, proxy, tor, and independently-scored abuser
signals instead of regex-matching RDAP org names; anonymous tier
covers 1000 req/day, well above signup volume.
2026-07-06 08:57:55 -07:00

182 lines
8.3 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 ipapi.is, and flags it if ipapi.is reports it
# as any of datacenter, vpn, proxy, tor, or an independently-scored abuser.
# Master switch:
IP_SCRUTINY_ENABLED=true
# Optional ipapi.is API key for higher rate limits. Anonymous (blank) is
# capped at 1,000 req/day, which comfortably covers yttrx's signup volume;
# get a key at https://ipapi.is/ only if you expect to exceed that.
IP_SCRUTINY_IPAPI_KEY=
# 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.
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
# 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.