Commit Graph
26 Commits
Author SHA1 Message Date
pmb 1a3e112946 Fix ipapi.is response parsing: handle flat string format
docker-build-push / build-push (push) Successful in 25s
ipapi.is free tier returns company/asn as flat strings ("Google LLC",
"AS15169 Google LLC") not nested dicts. The old code assumed nested
objects and crashed with AttributeError when trying to call .get() on
strings. This broke IP-based signup scrutiny for every new signup.

Now handles both formats (string and dict) for backward compat. Route
field handling also simplified since free tier doesn't nest asn data.

Fixes signup accounts falling through without IP classification.
2026-09-05 23:24:25 -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 fc965e3f17 Suspend immediately when both IP and email signals flag a signup
docker-build-push / build-push (push) Successful in 21s
The hourly OR-based sweep (SUSPICIOUS_GRACE_HOURS) still handles a
single-signal flag as before. This adds an additional check in
process_signup(): a signup flagged by BOTH IP-scrutiny and
email-domain scrutiny at once is a stronger signal, so it's suspended
right away instead of waiting out the grace period, skipping the
hold/welcome path and the suspicious_watch entry entirely.

Gated behind new SUSPICIOUS_COMBINED_* env vars, dry-run first per the
usual rollout convention (unlike SUSPICIOUS_DRY_RUN, which shipped
live by design). Falls back to the normal held-welcome path if the
suspend API call fails, and respects ABUSE_ALLOWLIST.
2026-07-15 10:07:19 -07:00
pmb a7208a84d1 Cache ipapi.is results by CIDR range instead of exact IP
docker-build-push / build-push (push) Successful in 5s
Uses asn.route from ipapi.is to bucket the classification cache by
network block (IPv4 and IPv6), so signups from the same datacenter/ASN
block no longer each burn a separate lookup against the 1000/day quota.
Falls back to a single-address range when route is missing or doesn't
actually contain the queried IP.
2026-07-13 08:35:55 -07:00
pmb 319f0bd0f9 fixin a spelling error
docker-build-push / build-push (push) Successful in 5s
2026-07-07 15:52:24 -07:00
pmb 25bc4a1a70 Fix suspicious-watch baseline snapshot to use the admin accounts API
docker-build-push / build-push (push) Successful in 6s
fetch_account_counts() was hitting the public /api/v1/accounts/:id
endpoint, which 404s for any account that hasn't confirmed its email
yet. Most flagged signups haven't confirmed at the moment
account.created/approved fires, so the baseline snapshot silently
failed and the account never entered suspicious_watch -- 6 of 8
IP-flagged signups to date missed the sweep entirely. Switch to the
admin endpoint suspicious_sweep.py already uses successfully.
2026-07-06 18:16:55 -07:00
pmb 9b523f1ce3 Update CLAUDE.md: admin.yttrx.com is a plain directory now, not a git checkout
docker-build-push / build-push (push) Successful in 5s
Deploy target was cleaned up to just docker-compose.yml + .env (+ .env.bak-*
snapshots) after switching to registry-pulled images. Fixes references to
git pull, chown waffles, and app/main.py.bak rollback that no longer apply.
2026-07-06 13:59:27 -07:00
pmb 5dad3f99dd Drop the dead dockerd/vfs step — the job container already shares the host's Docker socket
docker-build-push / build-push (push) Successful in 5s
Verified from the actual job container logs: act_runner bind-mounts
/var/run/docker.sock into every job container unconditionally, even before
privileged mode was enabled. The previous step's nested dockerd never
actually got used.
2026-07-06 13:50:37 -07:00
pmb 4917c101b3 Build image in-runner instead of delegating to the admin host over SSH
docker-build-push / build-push (push) Successful in 5s
act_runner now runs privileged, and the job image ships docker+dockerd, so
the build can happen directly in the job container. Needs vfs as the
storage driver since overlay2 doesn't nest inside overlay2 on this host.
2026-07-06 13:46:57 -07:00
pmb 3eb855222a Pull the image from the Gitea registry instead of building locally on admin.yttrx.com
docker-build-push / build-push (push) Successful in 5s
2026-07-06 13:42:39 -07:00
pmb 25c4e2f72f Add CI: build+push image to the Gitea container registry on push to main
docker-build-push / build-push (push) Successful in 20s
2026-07-06 13:39:12 -07:00
pmb 51233868ca Act immediately on flagged signups Mastodon already auto-approved
account.approved never fires for a signup that went through open
registration without ever being queued (no prior block on that
address). process_signup was waiting on that event forever, so the
first flagged signup on any new bad IP/email domain never got welcomed
or watched by the suspicious-sweep. Now checks Admin::Account.approved
from the account.created payload and acts immediately if it's already
true.
2026-07-06 13:27:15 -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 e62e3330e4 Only log suspicious-sweep actions, not routine no-op skips/clears
With the sweep now running every 10 minutes, the per-run 'N due' summary
and per-account skip/clear log lines (already-limited, staff, cleared by
activity, not found) were mostly noise drowning out the actual suspend/
silence actions. Those states are still recorded in suspicious_watch.status
for anyone querying the DB directly; only the taken action, dry-run
simulation, and error paths still log.
2026-07-06 07:11:23 -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
pmb 6ad9a93f38 Document /root/bin as the real install location (symlinked from /usr/local/bin) 2026-07-02 16:57:03 -07:00
pmb 0fd6f1c142 Fix welcomebot-signups: heredoc instead of -c string to avoid bash/python quote clash 2026-07-02 16:56:21 -07:00
pmb 00dec0266c Fix f-string quote escaping bug in welcomebot-signups 2026-07-02 16:54:14 -07:00
pmb 37577fbb56 Add welcomebot-signups CLI to inspect signup-IP classification history 2026-07-02 16:53:36 -07:00
pmb 0a196c3788 Add --ip filter to welcomebot-logs for signup-scrutiny decisions 2026-07-02 16:51:24 -07:00
pmb 0c51b186ac Note pre-git backup removal 2026-07-02 16:42:25 -07:00
pmb 78d4f7961f Document git-checkout deploy workflow (replaces rsync) 2026-07-02 16:35:43 -07:00
pmb c0e5df3cc4 Initial commit: welcome-bot, abuse-bot, dormant-sweep, IP signup scrutiny 2026-07-02 16:22:30 -07:00