Ability to distinguish individual users behind Flag activities in a privacy preserving way

At present, a common (and good) practice in the Fediverse is that moderation reports sent from one server to another are sent as the “instance actor” to protect the underlying reporting user from malicious or retaliatory server administrators.

This is generally good, however, it does mean that there’s generally no way to reject reports from a specific bad remote actor, instead you can only reject the reports for that entire server.

So if a particular remote actor decides to try to harass via reports (and the remote server moderators aren’t watching), then there’s a method for recourse of the receiving server.

For example, a Flag activity from Mastodon looks something like the following:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "http://social.example/a9e86dbe-3173-4c4d-a9b8-dd031f26aebc",
  "type": "Flag",
  "actor": "http://social.example/actor",
  "content": "This account harrased me",
  "object": [
    "http://social.example/users/sid_ebert1"
    "http://social.example/users/sid_ebert1/statuses/112436359884862090"
  ]
}

I’d like to propose that a privacy preserving way, whilst still enabling rejecting of reports may be to use the attributedTo property, using a URL that contains a salted hash of the users’ username, handle, or ID

So we’d get something like:

{
  "@context": "https://www.w3.org/ns/activitystreams",
  "id": "http://social.example/a9e86dbe-3173-4c4d-a9b8-dd031f26aebc",
  "type": "Flag",
  "actor": "http://social.example/actor",
  "content": "This account harrased me",
  "attributedTo": "http://social.example/report_user/13abbd03326d4e5f7777267365301409df7eaae66888a675a18dcaf668bfd1b3",
  "object": [
    "http://social.example/users/sid_ebert1"
    "http://social.example/users/sid_ebert1/statuses/112436359884862090"
  ]
}

For this example, I used the following hacked into Mastodon’s codebase:

URI.join(root_url, 'report_user/', OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha256'), Rails.application.secret_key_base, object.account.username))

Where Rails.application.secret_key_base is near-guaranteed to be unique to the server, and looks something like:

57b233bc6b8904a94bee43c36d6168b6a71c2d6be1ade992cea08317695eeca31b830e4f398bc689fa168de333a9ee36a80f45b282be63dedae5e6d3c0f12b16

Sure, you wouldn’t be able to look up the “attributedTo” actor (it’s a “fake URL”), but it would be consistent between reports by the same user (as long as the server’s secret key doesn’t change)

Of course, you don’t need to use a hash or anything, it just needs to be a unique identifier for the user that isn’t public / doesn’t reveal information about the user (other that a unique identity), so for instance you could store a UUID in a reporter_id column on your accounts table, and that’d be fine to use since you don’t return that back ever to the public.

you wouldn’t need both “actor” and “attributedTo” since “actor” is already a subproperty of “attributedTo”. i’d just make the actor the report_user directly.