Skip to content

feat(abuse): send abuse-control events and errors to PostHog - #109

Merged
clbotdev merged 1 commit into
mainfrom
issue-108-abuse-telemetry
Oct 21, 2026
Merged

clbotdev merged 1 commit into
mainfrom
issue-108-abuse-telemetry

Conversation

@clbotdev

Copy link
Copy Markdown
Contributor

Closes #108 (sub-issue of #75).

What changed

  • abuse/telemetry.ts: recordAbuseEvent writes a log line (shipped to PostHog Logs) and sends a PostHog product event for each abuse-control decision:

    Event Properties Where
    signup_blocked reason (disposable_email, free_email, too_many_signups) abuse/signup.ts
    content_scanned verdict, scan_mode (heuristic or ai) abuse/content-scan.ts, only when a scan runs
    abuse_flag_raised kind, severity, organization_id abuse/flags.ts
    team_paused_for_review reason, organization_id abuse/flags.ts
    abuse_flag_resolved kind, outcome (released or confirmed), organization_id abuse/flags.ts
    organization_sending_suspended / _unsuspended organization_id abuse/flags.ts
    marketing_send_paced reason, sequence_id, organization_id automation/process-ongoing-sequence.ts, at most once per sequence per UTC hour (Redis SET NX)
  • Allowlist: PROPERTY_ALLOWLIST now lets through kind, severity, verdict, reason, outcome, scan_mode, organization_id and form_id. Before this, abuse_flag_raised lost its kind and severity, and Signup forms: block double opt-in without a sender, and show an error when the confirmation can't be sent #102's signup-form errors lost form_id. No email addresses or IPs are sent.

  • Errors: recordSignup failures and failed operator alert emails go through captureError instead of only being logged.

  • CLI flush: scripts/abuse.ts flushes telemetry before process.exit, so operator decisions aren't lost.

  • AGENTS.md: a new development tip says to always send important events and errors to PostHog through @codelitdev/observability.

Testing

  • Unit: new tests check the exact events and properties for a refused signup, a blocked email flagged, paused and confirmed, and once-per-hour pacing; one more checks that the allowlist keeps abuse codes and drops email. The full API suite passes (742 tests), as do typecheck, lint, format, check:drift and test:conformance.

  • Live E2E (cloud mode, the real PostHog project with DEPLOY_ENV=predator-agent, scratch DB):

    1. A signup with @mail.tm was refused.
    2. A broadcast with an .exe link was refused from the API and from the dashboard's Send now, which showed the "could harm recipients" message.
    3. The flag was released with the operator CLI.
    4. A clean broadcast passed the scan.
    5. A second harmful broadcast was blocked; its flag was confirmed, and the organization was unsuspended.

    The events need checking in PostHog itself before merging.

Found while testing (not changed here)

  • Deadlock when sending campaigns in cloud mode. createOutboundMessage inserts an outbound_messages row, whose foreign key takes FOR KEY SHARE on the team row. reserveSend then locks organization_plan_policies FOR UPDATE, then the team row FOR UPDATE. Two concurrent sends deadlock on these locks. A 10-recipient broadcast failed completely and paused itself (consecutive_failures). Likely fix: FOR NO KEY UPDATE on the team row. This is why marketing_send_paced couldn't be triggered live; it's covered by a unit test.
  • Raw error code on a paused team. A team paused for review shows the raw code sending_paused on Send now.

Abuse flags reached PostHog without their kind or severity (dropped by the
property allowlist), and refused signups, scans, team pauses, operator
decisions and new-account pacing sent nothing. Add recordAbuseEvent
(abuse/telemetry.ts), which logs and captures: signup_blocked,
content_scanned, team_paused_for_review, abuse_flag_raised,
abuse_flag_resolved, organization_sending_(un)suspended and
marketing_send_paced (once per sequence per hour). Allow the codes and
internal ids they carry, report recordSignup and operator-alert failures
as exceptions, and flush telemetry before the operator CLI exits.

AGENTS.md: always send important events and errors through
@codelitdev/observability.

Closes #108
@clbotdev

Copy link
Copy Markdown
Contributor Author

Verified in PostHog (DEPLOY_ENV=predator-agent): signup_blocked (disposable_email, too_many_signups), content_scanned (blocked/clean, heuristic), abuse_flag_raised (content, high), team_paused_for_review (content_review), organization_sending_suspended/unsuspended from the operator CLI all arrived with their properties.

Two Platform (@codelitdev/observability) problems seen while verifying, not fixed here:

  • Values of allowlisted id properties (team_id, organization_id) arrive as [redacted-token]: the token mask catches any 24+ char id.
  • Captures are one HTTP request each with a 2s abort and no retry; 8 of 32 requests in one run were dropped (about 0.75s per request from this host, more under bursts). One earlier flag/pause pair never arrived.

@clbotdev
clbotdev merged commit 6da3d28 into main Oct 21, 2026
5 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Abuse controls: send proper events and logs to PostHog

2 participants