Skip to content

Why we weigh reports instead of counting them

A report counter is a weapon. Here is how Kahoo scores reports so one determined person cannot get anyone banned, and genuine reports still work.

Last updated

The obvious way to build a report system is a counter. Three reports and you are gone. It takes an afternoon to write, and it is broken before you ship it.

Why the counter fails

On a platform where anyone can create a guest identity in one click, a counter is not a safety feature — it is a weapon. One person with a browser and ten minutes can remove anybody they dislike. Worse, the people most likely to be targeted are exactly the ones who were behaving well and made someone angry by leaving.

Raising the threshold does not fix it, it just raises the price of the attack. And every point you raise it, the slower you are to act on someone who really is a problem.

What Kahoo does instead

Reports are reduced to a credibility score. The rules, in the order they apply:

  1. A report must reference a real shared session. The server checks that the two identities were genuinely matched in the session cited. A report that cannot be tied to an interaction contributes nothing at all.
  2. One reporter counts once per day against the same person. Reports inside a 24-hour window collapse into their strongest one.
  3. No single reporter can ever contribute more than 1.5 points. No amount of persistence gets one person past that ceiling.
  4. Signed-in accounts weigh more than guests. One point versus half a point. An account has something to lose.
  5. Severe categories weigh more. Nudity, hate, threats and underage concerns get a 1.25 multiplier — still not enough to ban anyone alone.
  6. Reporters with a bad record stop counting. Three dismissed reports and no upheld ones sets that reporter's weight to zero.
  7. Coordinated bursts are capped as a group. Three or more reports from one coarse network bucket within fifteen minutes are scaled down to one point between them.

That last rule is the one that needs care. The signal is a salted hash of a heavily truncated address — a /24 for IPv4, a /48 for IPv6. It cannot be reversed into an IP, is never shown to anyone, and is used for nothing except noticing that five "different" reporters arrived from the same place at the same moment. A university or an office shares a bucket, which is precisely why it caps a group rather than discarding it.

The thresholds are published

One credible point is a warning. Three escalates to a human. Four is a six-month restriction. After a served restriction, two more points make it permanent. These numbers are in the community guidelines because a moderation system nobody can inspect is one nobody can trust.

An automatic decision never jumps straight to permanent. Only a moderator, having looked at the evidence and confirmed a severe violation, can act outside the bands.

What this costs

Honesty about the trade-off: weighing is slower than counting. A genuinely abusive person who meets three guests in a row accumulates 1.5 points, not 3, and gets a warning rather than a restriction. They stay on the platform longer than a naive counter would allow.

We think that is the right side to err on. A false ban is invisible to everyone except the person it happened to, and they have no way to prove a negative. A delayed ban is visible, measurable, and fixable by a moderator.

Where it is written

The whole thing is a pure function over a list of reports — no database, no clock, no network — which means it is exhaustively unit tested, including the attack cases. If you change a weight, a test tells you which scenario you just broke.