diff --git a/CLAUDE.md b/CLAUDE.md index a63f70e..61e33dc 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -99,11 +99,31 @@ admin.yttrx.com (source: `bin/welcomebot-logs` in this repo). It wraps ssh admin.yttrx.com welcomebot-logs # last 200 lines ssh admin.yttrx.com welcomebot-logs -f --abuse # follow abuse activity only ssh admin.yttrx.com welcomebot-logs -n 1000 --since 24h +ssh admin.yttrx.com welcomebot-logs --ip --since 24h # signup-IP scrutiny decisions ssh admin.yttrx.com 'welcomebot-logs --help' ``` -To reinstall after editing `bin/welcomebot-logs`: -`scp bin/welcomebot-logs admin.yttrx.com:/tmp/ && ssh admin.yttrx.com 'install -m755 /tmp/welcomebot-logs /usr/local/bin/'` +### Inspecting signup-IP classification history + +`welcomebot-signups` (also `/usr/local/bin/welcomebot-signups`, source +`bin/welcomebot-signups`) reads the persistent `signup_ip` sqlite table +directly — a permanent record of every classification decision, independent +of log retention: + +```bash +ssh admin.yttrx.com welcomebot-signups # last 20 decisions +ssh admin.yttrx.com welcomebot-signups --flagged # only datacenter-flagged signups +ssh admin.yttrx.com welcomebot-signups -a someuser +ssh admin.yttrx.com welcomebot-signups -n all +``` + +Both CLIs are just files tracked in this repo now — after editing either one, +reinstall with: + +```bash +ssh admin.yttrx.com 'cd /root/yttrx-welcomebot && git pull origin main && \ + install -m755 bin/welcomebot-logs bin/welcomebot-signups /usr/local/bin/' +``` ### Rollback diff --git a/README.md b/README.md index d2cfde6..68b2d32 100644 --- a/README.md +++ b/README.md @@ -231,6 +231,7 @@ test toot. ```bash welcomebot-logs -f --abuse # convenience CLI on admin (see bin/welcomebot-logs) +welcomebot-signups --flagged # signup-IP classification history (see bin/welcomebot-signups) docker compose logs -f welcomebot # watch deliveries docker compose restart welcomebot # after editing .env docker compose down && docker compose up -d --build # redeploy after code change diff --git a/bin/welcomebot-signups b/bin/welcomebot-signups new file mode 100755 index 0000000..292c6de --- /dev/null +++ b/bin/welcomebot-signups @@ -0,0 +1,87 @@ +#!/usr/bin/env bash +# welcomebot-signups — inspect the signup-IP classification history for yttrx-welcomebot. +# +# Installed at /usr/local/bin/welcomebot-signups on admin.yttrx.com. +# Source of truth: ~/yttrx-welcomebot/bin/welcomebot-signups (workstation). +# Reads the persistent `signup_ip` sqlite table (survives log rotation) — +# see app/main.py's record_signup_ip / IP-based signup scrutiny. +set -euo pipefail + +CONTAINER="${WELCOMEBOT_CONTAINER:-yttrx-welcomebot}" +limit=20 +flagged_only=0 +acct_filter="" + +usage() { + cat <&2; usage >&2; exit 2 ;; + esac + shift +done + +if ! command -v docker >/dev/null 2>&1; then + echo "welcomebot-signups: docker not found in PATH" >&2; exit 1 +fi +if ! docker inspect "$CONTAINER" >/dev/null 2>&1; then + echo "welcomebot-signups: container '$CONTAINER' not found (is it deployed/running?)" >&2; exit 1 +fi + +docker exec \ + -e SIG_LIMIT="$limit" \ + -e SIG_FLAGGED_ONLY="$flagged_only" \ + -e SIG_ACCT="$acct_filter" \ + "$CONTAINER" python3 -c ' +import os +import sqlite3 + +conn = sqlite3.connect("/data/welcomed.db") +where, params = [], [] +if os.environ.get("SIG_FLAGGED_ONLY") == "1": + where.append("flagged = 1") +if os.environ.get("SIG_ACCT"): + where.append("acct = ?") + params.append(os.environ["SIG_ACCT"]) + +sql = "SELECT acct, ip, classification, org, flagged, ipblock_registered, classified_at FROM signup_ip" +if where: + sql += " WHERE " + " AND ".join(where) +sql += " ORDER BY classified_at DESC" + +limit = os.environ.get("SIG_LIMIT", "20") +if limit != "all": + sql += f" LIMIT {int(limit)}" + +rows = conn.execute(sql, params).fetchall() +if not rows: + print("(no matching signup records)") +for acct, ip, classification, org, flagged, ipblock, ts in rows: + flag = "FLAGGED" if flagged else "-" + ipb = "ipblock=yes" if ipblock else "ipblock=no" + print(f"{ts} {acct or \"\":20s} {ip or \"\":16s} {classification or \"\":12s} {flag:8s} {ipb:12s} {org or \"\"}") +'