Commit Graph
11 Commits
Author SHA1 Message Date
pmb af49aab79c Backfill block_cidr so /api/blocks reports real ranges
docker-build-push / build-push (push) Successful in 3s
Every row blocked before the block_cidr column existed had nothing to
report, so the endpoint fell back to the signup IP's /32 — the address
that signed up rather than the range that was blocked, which is the
opposite of what the list is for.

The range is recoverable from the bot's own ipapi range cache, which
stores the CIDR each classification was cached under — the same value
that was handed to register_ip_block. Run once on admin.yttrx.com:
of 154 blocked rows, 65 gained their real range, 79 genuinely were
single-address blocks (no asn.route from ipapi.is), and 10 with no cache
entry stay as a /32, since under-reporting beats claiming a range is
blocked when it isn't. Rows classified before 2026-07-15 are skipped —
range-blocking didn't exist yet, so only the /32 was ever registered.

Live output went from 150 entries (all /32) to 145, of which 59 are
real networks.
2026-09-13 00:48:20 -07:00
pmb eb6d7cad46 Add read-only blocklist API (off by default)
docker-build-push / build-push (push) Successful in 5s
Publishes the IP ranges and email domains the bot has auto-blocked as
JSON: GET /api/blocks, /api/blocks/ip-ranges, /api/blocks/email-domains,
each taking an optional ?limit=N.

The endpoints are unauthenticated, so BLOCKS_API_ENABLED gates them and
defaults to false — with it unset the paths 404 as if they didn't exist.
Publishing which ranges are already blocked also tells a spam signup
which ones to avoid, so opting in is a per-deployment call; yttrx.com
sets it true in the box's .env.

Responses assert only that a block exists: the blocked range/domain, a
signups count, and first/last seen. No account identifiers, and none of
the ipapi.is or check-mail.org classification that drove the decision —
republishing a paid reputation feed's verdicts on an open endpoint would
be redistributing their data rather than ours.

Supporting changes:

- signup_ip gains a block_cidr column (migrated in place on open) so the
  API reports the network actually blocked rather than the signup IP.
  Rows predating it fall back to that IP's /32 or /128, which is exactly
  what was registered before range-blocking started 2026-07-15.
- register_ip_block / register_email_domain_block now return whether the
  block landed, and the caller only marks it registered on success, so a
  failed POST is no longer published as a block that exists.
2026-09-13 00:41:48 -07:00
pmb 65c9bd5a9e Suspend immediately on strong IP-reputation signals (not vpn alone)
docker-build-push / build-push (push) Failing after 15s
datacenter/proxy/tor/abuser is a much cleaner bulk/bot-signup indicator
than vpn, which also flags plenty of privacy-conscious real users. A
strong-flagged signup now suspends immediately regardless of the email
signal; vpn-only still gets the normal held-welcome/ipblock/lowered-
threshold treatment, falling through to the existing combined-signal
suspend only if also paired with a flagged email domain.
2026-07-21 22:14:39 -07:00
pmb 0b0842dc9d Switch IP-scrutiny auto-blocks to hard reject (sign_up_block)
docker-build-push / build-push (push) Successful in 5s
2026-07-15 17:39:48 -07:00
pmb b39528ac30 Block the whole network for a flagged signup, not just its /32
docker-build-push / build-push (push) Successful in 4s
register_ip_block always blocked the exact signup IP as a /32 (or /128
for v6), which is close to pointless for the datacenter/VPN/proxy space
these flags fire on: a repeat bad actor from the same provider almost
never reuses the exact same address, but very often reuses a different
one in the same block.

classify_signup_ip now returns a fourth value, block_cidr — ipapi.is's
asn.route CIDR when it's a valid network that actually contains the
signup IP (already being tracked in ipapi_range_cache purely for lookup
caching), falling back to the address's own /32 or /128 when no usable
route exists. register_ip_block blocks that instead of always deriving
a /32 itself.

Added range_cache_tests() covering the cache/fallback logic directly
(pure sqlite + ipaddress, no network), and updated the existing
ip_scrutiny_tests()/combined_signal_tests() mocks for the new 4-tuple
classify_signup_ip return and register_ip_block arity.
2026-07-15 10:25:14 -07:00
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
pmb 8b0e1eb5ab Lower suspicious-sweep grace window from 24h to 1h, tighten sweep cadence to 10min
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.
2026-07-06 07:07:43 -07:00
pmb 9924e06b5a Add suspicious-signup sweep: suspend flagged signups with no activity
Watches every account flagged by IP-scrutiny or email-domain scrutiny at
signup and, once it goes live, records a baseline post/follow count. A
scheduled sweep (app/suspicious_sweep.py, run via cron on admin.yttrx.com)
suspends any watch past SUSPICIOUS_GRACE_HOURS with zero new posts and
zero new follows since that baseline; any activity clears the watch.

Works whether yttrx is open-registration or requires moderator approval,
since the watch starts at whichever event actually makes the account live
(account.created vs account.approved), same dual handling the welcome flow
already uses. Needs ABUSE_BOT_TOKEN re-minted with admin:read:accounts.
2026-07-05 10:22:39 -07:00
waffle2k 656eec09c0 Add disposable/high-risk email signup scrutiny via check-mail.org
Classifies each signup's email domain (domain-only, GDPR-friendly) alongside
the existing IP scrutiny signal, with matching held-welcome and auto
email_domain_block behavior. A report against an account with a flagged
domain suspends immediately (no reporter-count threshold) and blocks the
domain, classified live from the report payload rather than any signup-time
record so it also covers pre-existing accounts. Ships CHECK_MAIL_DRY_RUN=true
by default, independent of ABUSE_DRY_RUN, so the new report-triggered suspend
path stays inert until watched.
2026-07-03 09:08:30 -07:00
pmb 37577fbb56 Add welcomebot-signups CLI to inspect signup-IP classification history 2026-07-02 16:53:36 -07:00
pmb c0e5df3cc4 Initial commit: welcome-bot, abuse-bot, dormant-sweep, IP signup scrutiny 2026-07-02 16:22:30 -07:00