Add a reputation query API for pre-signup blocking
docker-build-push / build-push (push) Successful in 6s
docker-build-push / build-push (push) Successful in 6s
Everything the bot does today is reactive: Mastodon creates the account, the account.created webhook arrives, we classify the signup IP and email domain, and then we block the range and suspend what already exists. POST /api/reputation is the pre-emptive half. A caller asks about an IP and/or email domain; we classify it, record the answer, register the matching Admin::IpBlock / EmailDomainBlock, and only then return. A caller that runs its own block lookup immediately afterwards therefore finds a block that did not exist when its request started. The intended caller is Mastodon's signup path, which does exactly that. It writes, so unlike /api/blocks it is authenticated and refuses to serve without a key (REPUTATION_API_KEY): an unauthenticated endpoint that installs IP blocks is a denial-of-service lever, so there is no open mode. Notes on the pieces that aren't obvious: - REPUTATION_API_MAX_LOOKUPS_PER_HOUR caps *fresh* provider lookups. The caller hits this on every view of the signup form, not just on submission, so without a ceiling a crawler walking /auth/sign_up from a new range each time would set our ipapi.is bill. Over budget we answer "unknown" and install nothing -- never "flagged". Cache hits are free and uncounted, and the webhook path never spends from this budget. - Blocks installed here are recorded in their own table, keyed by the CIDR/domain rather than by account (there is no account yet), and are folded into /api/blocks and the reconcile pass so neither one under-reports as blocking shifts to the pre-signup path. The published entry shape is unchanged; a range blocked before anyone signed up from it simply reads "signups": 0. - A repeat query for an already-installed block skips the Mastodon round trip. This runs on a live signup request, so that is saved latency.
This commit is contained in:
1 parent
8490048d2d
commit
57234ca548
5 files changed
+758
-4
No files matched your search
@@ -258,3 +258,59 @@ BLOCKS_API_PUBLIC_URL=https://hooks.yttrx.com
|
||||
# changes when a signup is flagged, so a few minutes of client/intermediate
|
||||
# caching costs nothing.
|
||||
BLOCKS_API_CACHE_SECONDS=300
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Reputation query API (pre-signup pre-blocking)
|
||||
# ---------------------------------------------------------------------------
|
||||
# POST /api/reputation turns the bot from reactive into pre-emptive. Mastodon
|
||||
# calls it during signup, immediately BEFORE running its own IpBlock /
|
||||
# EmailDomainBlock lookup:
|
||||
#
|
||||
# POST /api/reputation {"ip": "...", "email": "...", "source": "..."}
|
||||
# X-Reputation-Key: <REPUTATION_API_KEY>
|
||||
#
|
||||
# The handler classifies the IP (ipapi.is) and/or the email's domain
|
||||
# (check-mail.org), records the answer, registers the matching Admin::IpBlock
|
||||
# / EmailDomainBlock, and only THEN returns — so the lookup Mastodon makes
|
||||
# microseconds later finds the block this call just installed and rejects the
|
||||
# signup outright, instead of the bot cleaning up an account that already
|
||||
# exists. Both request fields are optional (the IP choke point has no address
|
||||
# to offer); at least one is required.
|
||||
#
|
||||
# The caller is the `call-welcomebot-reputation-api` patch on yttrx's Mastodon
|
||||
# fork — see yttrx-documentation/mastodon-fork-patches.md. It fails open: if
|
||||
# this endpoint is slow, down or disabled, Mastodon proceeds with its own
|
||||
# lookup exactly as before.
|
||||
#
|
||||
# Unlike the blocklist API above this one WRITES (it installs blocks), so it
|
||||
# is authenticated and refuses to serve without a key.
|
||||
REPUTATION_API_ENABLED=false
|
||||
|
||||
# Shared secret the caller presents as the X-Reputation-Key header; must match
|
||||
# WELCOMEBOT_REPUTATION_KEY on the Mastodon side. Generate with
|
||||
# `openssl rand -hex 32`. Empty = the route 404s even with ENABLED=true: an
|
||||
# unauthenticated endpoint that installs IP blocks is a denial-of-service
|
||||
# lever, so there is deliberately no "open" mode.
|
||||
REPUTATION_API_KEY=
|
||||
|
||||
# Whether a flagged lookup actually registers the block, or only classifies,
|
||||
# records and answers. false is the dry-run equivalent for this path — the way
|
||||
# to watch what it *would* pre-block before letting it act. The per-signal
|
||||
# IP_SCRUTINY_DRY_RUN / CHECK_MAIL_DRY_RUN and IP_SCRUTINY_AUTO_IPBLOCK /
|
||||
# CHECK_MAIL_AUTO_DOMAIN_BLOCK switches still apply on top of this.
|
||||
REPUTATION_API_INSTALL_BLOCKS=true
|
||||
|
||||
# Ceiling on *fresh* (cache-missing) provider lookups per rolling hour.
|
||||
# Mastodon calls this on every view of the signup form, not just on
|
||||
# submission, so without a ceiling a crawler walking /auth/sign_up from a new
|
||||
# range each time would set the ipapi.is / check-mail.org bill. Over budget an
|
||||
# uncached value answers "unknown" and installs nothing — never "flagged".
|
||||
# Cache hits are free and are never counted, and the webhook path never spends
|
||||
# from this budget (account.created is authoritative and must not be starved).
|
||||
# 0 = no ceiling.
|
||||
REPUTATION_API_MAX_LOOKUPS_PER_HOUR=500
|
||||
|
||||
# DM MOD_ALERT_ACCT when this endpoint installs a block it has never installed
|
||||
# before. Bounded by how many genuinely new bad ranges/domains appear, not by
|
||||
# request volume: repeat hits on an already-installed block are silent.
|
||||
REPUTATION_API_DM_MODERATOR=true
|
||||
Reference in new issue
Block a user