Introducing Lemvotes
I have been working on Lemvotes, a tool to check who voted on a Lemmy post. In this blog post, I will describe how it works and the ethics of such a tool.
Why I made Lemvotes
Due to the way the fediverse works, virtually all your activity is public. That includes the votes you have cast. The API for listing the votes on a post is admin-only, however anyone could set up an instance and get the vote counts that way.
Having access to information about who voted on a post would allow people to locate brigading1 efforts and detect bots spamming down/upvotes on posts. Currently, only admins can access this information, which makes it harder for users to report such behaviour to them.
Regarding privacy, non-technical users believe that their votes are private, which is far from the truth. This attitude could potentially lead to harassment of Lemmings (yes, that’s what we Lemmy users call ourselves) for upvoting a particular post. Lemvotes makes it clear that votes are not private, which could help bring a more accurate picture of the way votes work on Lemmy to its users.
How Lemvotes works
The Lemvotes hoster (that would be me if you use lemvotes.org) provides the admin username and password of a Lemmy instance to Lemvotes, which then sends a request to Lemmy to acquire a JWT for authentication.
Once the backend is up and running, the frontend can send a request to it, containing a post URL. The backend then retrieves the original post URL, because /api/v3/resolve_object
2 can’t fetch a post retrieved via another instance, i.e. the post URL has to be from the poster’s instance.
Once it has the original URL, it sends a request to the resolve_object
endpoint to convert the federated post URL into a local ID. Next, we have a loop that iterates over the /api/v3/post/like/list
3 API to get all the votes on a post, which is finally sent back to the frontend in JSON form.
At no point are the login credentials for the admin user required for Lemvotes revealed to the user, and they are not available on any API. Just make sure not to leak them by sharing your docker compose file somewhere :P.
Lemvotes’s backend is written in Python, using the aiohttp library for async http requests. I used FastAPI as the web framework, Uvicorn as the ASGI framework and Docker and Docker Compose for deployment. The frontend is made with SvelteKit (great JS framework, I 100% recommend it for beginners) and Tailwind. The entire source code of my app is available on GitHub.
Why I can’t add the option to check a user’s votes
I am currently unable to add the option to check a user’s votes or the votes in a community, due to the Lemmy devs refusing to implement such an API, which is of course understandable, it’s their project after all. However, such functionality would be very useful for checking if a user is a bot spamming votes, and being able to see votes in a community would again be useful for community moderators for detecting spam. Again, this would not be a bad thing, because your votes are not private. Anyone could set up an instance and check them.
I am considering implementing my own ActivityPub4 server to remove the dependency on a Lemmy server to get votes, and potentially getting rid of the backend entirely and sending all the requests via the client, which could noticeably increase performance. Alas, I have no idea how to do that. Lots of reading the docs (ughhh, a horrible fate) awaits!
A concentrated effort by one online group to manipulate another. (e.g. by mass commenting). ↩︎
A Lemmy API used to resolve a URL of a federated post into one from the local instance. Here is the documentation. ↩︎
An admin-only Lemmy API to get a post’s likes, docs at https://lemmy.readme.io/reference/get_post-like-list ↩︎
The protocol used by Lemmy, Mastodon, Pleroma… servers. https://activitypub.rocks/ is its official website. ↩︎