Skip to content

feat(abuse): send new events and identity metadata to abuse service (#3288)#3289

Open
jrf0110 wants to merge 6 commits into
mainfrom
gt/toast/6bd36786
Open

feat(abuse): send new events and identity metadata to abuse service (#3288)#3289
jrf0110 wants to merge 6 commits into
mainfrom
gt/toast/6bd36786

Conversation

@jrf0110
Copy link
Copy Markdown
Contributor

@jrf0110 jrf0110 commented May 17, 2026

Summary

  • Adds reportEvents calls for three previously missing production call sites: Stytch autoban (user.blocked), admin cancel-and-refund (user.blocked), and org admin credit grant (billing.credit_purchased).
  • Expands fireAuthEvent in user.ts to populate the new AuthEventPayload fields (auth_providers, org_memberships) via parallel DB queries at auth time, keeping the call fire-and-forget.
  • The reportEvents helper, all CloudEvent types, the expanded AuthEventPayload type, and the majority of the 17 event emit sites were already implemented on this branch prior to this commit. This completes coverage for the remaining gaps identified during codebase audit.

Closes #3288

Verification

  • Existing stytch.test.ts, cancel-and-refund.test.ts, and user.test.ts exercise the modified code paths; ABUSE_SERVICE_URL is unset in CI so all new reportEvents/reportAuthEvent calls no-op safely.
  • The Stytch autoban path now uses .returning() to confirm the row was actually updated before firing the event, matching the conditional guard that was already present.

Visual Changes

N/A

Reviewer Notes

  • fireAuthEvent is now async; all three call sites already used void so the change is non-blocking.
  • The org admin credit grant emits billing.credit_purchased only for positive grant amounts and only when created_by_kilo_user_id is set (orgs created before that column existed have it null).
  • user.email_changed has no call site because the email field is immutable in normal flows (tied to OAuth provider); no change needed.
  • One-off backfill scripts that also set blocked_at were intentionally skipped per the bead guidance (low priority for one-off migration scripts).

jrf0110 added 2 commits May 17, 2026 19:44
…r abuse service

- Add CloudEvent union type and reportEvents() helper hitting POST /api/events
- Expand AuthEventPayload with new optional user/auth/org metadata fields
- Emit user.blocked/unblocked from admin-router updateBlockStatus and bulkBlock helpers
- Emit user.deleted from softDeleteUser
- Emit org.created/org.member_added from createOrganization and addUserToOrganization
- Emit org.member_added from acceptOrganizationInvite
- Emit org.member_removed from removeUserFromOrganization
- Emit org.deleted from organization-admin-router delete mutation
- Emit billing.credit_purchased from processTopUp
- Emit billing.kilo_pass_changed from handleKiloPassSubscriptionEvent
- Emit stripe.payment_method.attached/detached from handlePaymentMethodEvent
- Emit stripe.charge.dispute.created alongside existing KiloClaw logic
- Add new cases for stripe.charge.dispute.funds_withdrawn, stripe.radar.early_fraud_warning.created, stripe.charge.failed, stripe.payment_intent.succeeded
- Update fireAuthEvent to send richer user metadata fields

Closes #3288
- Add user.blocked event to Stytch autoban path (stytch.ts)
- Add user.blocked event to admin cancel-and-refund flow (cancel-and-refund.ts)
- Add billing.credit_purchased event for org admin credit grants (organization-admin-router.ts)
- Expand fireAuthEvent to include auth_providers and org_memberships fields
  by querying DB at auth time; makes fireAuthEvent async (user.ts)

The abuse-service.ts types and reportEvents helper, plus the bulk of the
CloudEvent emit sites, were already implemented. This completes coverage
for the remaining production call sites per issue #3288.
@jrf0110 jrf0110 requested a review from kilocode-bot May 17, 2026 20:06
@jrf0110
Copy link
Copy Markdown
Contributor Author

jrf0110 commented May 17, 2026

@kilocode-bot review please?

@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot Bot commented May 17, 2026

To use Kilo from GitHub you first need to link your GitHub account to Kilo. Link your Kilo account to continue. After linking, mention me again in this issue or pull request.

Comment thread apps/web/src/lib/user.ts Outdated
Comment thread apps/web/src/lib/kilo-pass/cancel-and-refund.ts Outdated
@kilo-code-bot
Copy link
Copy Markdown
Contributor

kilo-code-bot Bot commented May 17, 2026

Code Review Summary

Status: No Issues Found | Recommendation: Merge

Executive Summary

All previously-flagged issues remain resolved. The single incremental change (removing customer: dispute.customer from the stripe.charge.dispute.created event payload) correctly fixes the non-existent property access and makes the dispute event consistent with charge.dispute.funds_withdrawn.

Resolved Issues (from previous rounds)

File Previous Issue Fix
apps/web/src/lib/abuse/bulkBlock.ts actor_email received a user ID instead of an email Changed to actor_email: null
apps/web/src/lib/user.ts Promise.all for DB enrichment could throw and abort auth telemetry Wrapped with .catch(() => null)
apps/web/src/lib/organizations/organizations.ts org.member_added fired even when user was already a member Added membershipInserted boolean guard ✅
apps/web/src/lib/kilo-pass/stripe-handlers-subscription-events.ts occurred_at used subscription.created (creation time, not transition time) Removed occurred_at entirely ✅
apps/web/src/routers/admin-router.ts Event fired unconditionally regardless of whether the DB update matched any row Added .returning() + updated.length > 0 guard ✅
apps/web/src/lib/stripe.ts dispute.customer does not exist on Stripe Dispute object Removed customer field from event data entirely ✅
Files Reviewed (12 files)
  • apps/web/src/lib/stripe.ts — ✅ fixed (incremental)
  • apps/web/src/lib/abuse/bulkBlock.ts — ✅ fixed
  • apps/web/src/lib/ai-gateway/abuse-service.ts — no issues
  • apps/web/src/lib/credits.ts — no issues
  • apps/web/src/lib/kilo-pass/cancel-and-refund.ts — ✅ fixed
  • apps/web/src/lib/kilo-pass/stripe-handlers-subscription-events.ts — ✅ fixed
  • apps/web/src/lib/organizations/organizations.ts — ✅ fixed
  • apps/web/src/lib/stytch.ts — no issues
  • apps/web/src/lib/user.ts — ✅ fixed
  • apps/web/src/routers/admin-router.ts — ✅ fixed
  • apps/web/src/routers/organizations/organization-admin-router.ts — no issues

Reviewed by claude-4.6-sonnet-20260217 · 352,618 tokens

Review guidance: REVIEW.md from base branch main

…ll in cancel-and-refund

- user.ts: reportEvents for user.deleted was firing before the db.transaction,
  meaning phantom events could be sent if precondition checks (active subscription,
  live KiloClaw instance) threw SoftDeletePreconditionError. Move it to after
  the transaction completes.

- cancel-and-refund.ts: actor_email was mistakenly set to adminKiloUserId (a UUID),
  not an email address. Set it to null as a safe stopgap.
Copy link
Copy Markdown
Contributor

@jeanduplessis jeanduplessis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Found six concrete event-contract issues in this PR:

  • bulk block reports admin UUIDs as actor_email
  • async auth enrichment can reject outside existing fail-open handling
  • duplicate invite acceptance emits false org.member_added
  • Kilo Pass changes use subscription creation time as event time
  • admin block/unblock emits events without confirming a real mutation
  • dispute-created Stripe payload stores charge identity under customer

These affect event correctness, retry behavior, and downstream abuse-service attribution/state ordering.

Comment thread apps/web/src/lib/abuse/bulkBlock.ts Outdated
Comment thread apps/web/src/lib/user.ts Outdated
Comment thread apps/web/src/lib/organizations/organizations.ts Outdated
Comment thread apps/web/src/lib/kilo-pass/stripe-handlers-subscription-events.ts Outdated
Comment thread apps/web/src/routers/admin-router.ts Outdated
Comment thread apps/web/src/lib/stripe.ts Outdated
jrf0110 added 2 commits May 18, 2026 06:20
- bulkBlock: set actor_email to null instead of UUID
- user.ts fireAuthEvent: catch DB enrichment errors to preserve fail-open behavior
- organizations: only emit org.member_added when membership was actually inserted
- stripe-handlers-subscription-events: remove incorrect occurred_at backdated to subscription.created
- admin-router updateBlockStatus: gate event emission on .returning() confirming a real mutation
- stripe.ts dispute.created: use dispute.customer instead of dispute.charge for customer field
…te.created event data

Stripe's Dispute type does not have a customer field. The previous fix
incorrectly used dispute.customer (which doesn't exist on the type) instead
of dispute.charge. Since we cannot get the customer ID without an extra API
call, omit the customer field entirely, consistent with how the
charge.dispute.funds_withdrawn event is handled.
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.

Send new events and identity metadata to the abuse service

2 participants