· Brittany Ellich · proposal  · 9 min read

Representing groups in ATProto

I wanted to add book clubs to my GoodReads-like app (Collective), but ATProto doesn't have a standard way to handle shared group resources yet. So I'm building opensocial.community—a separate service that manages groups independently from any single app. This means the same book club could potentially work across multiple apps (imagine your book club having both a reading list in Collective AND a discussion forum in another app), and groups can migrate between providers if needed. It's probably over-engineered for my use case, but might help other ATProto developers building community features.

I wanted to add book clubs to my GoodReads-like app (Collective), but ATProto doesn't have a standard way to handle shared group resources yet. So I'm building opensocial.community—a separate service that manages groups independently from any single app. This means the same book club could potentially work across multiple apps (imagine your book club having both a reading list in Collective AND a discussion forum in another app), and groups can migrate between providers if needed. It's probably over-engineered for my use case, but might help other ATProto developers building community features.

Around the holidays I’ve had some spare time and energy for tinkering, so I decided I wanted to dip my toes into the AT Proto space. The community has been lovely and welcoming, and I have learned so much in the past few weeks of engaging in this space.

I aligned on building an app that is a bit like GoodReads, but for all types of media. I selfishly just want to keep track of media recommendations from folks, and know who to reach out to to update them when I finish the thing. I also get a bit of dopamine out of completing things, so love having a list that I can check off as I go. You can see some screenshots of that effort below!

A media page for the book Atomic Habits. There's an edit button, a plus button to add it to a collection, and a share button. There are review stars, tags, and a list of reviews with the ability to add comments and reactions to the reviews.

A collection of ATProto Resources. Shown are two videos from last Atmosphereconf and Dan Abramov's Open Social article. There's a progress bar showing the completion rate of the items in the collection, an edit button to edit the collection, a copy button to copy a collection to a new one, a share button to share a collection, a reorder button to change the order of items in the collection, and an add item button which lets you add new items to the collection. Each media item card has a type (video, article, etc), a link if there's a link to access the item, a share button to allow easy recommendations to friends, an edit button to add reviews/notes to the item, and a delete button to remove it from the collection.

The vast majority of the app came together pretty naturally, however, there was one key feature that that has required more of a deeper dive: Groups. My personal use case is that I run a book club for Overcommitted that reads technical or software specific books (join us if that sounds interesting)!

I wanted a way to represent a shared resource in Collective, a book club group that anyone could join and would have a shared list to keep track of the books in progress and share a way to show where we are at in the book.

It turns out that shared resources like this that would have state shared across multiple individuals is a pretty exciting area of ATProto that is still in development! There are many ways to handle this problem, with a ton of prior art and great ways folks have been thinking about it. I’m linking a bunch of resources I’ve been collecting related to it to the end of this post.

What I’ve aligned on

There are so many mental models to represent and manage groups, and I realized it was time to just build a thing and see how it goes to see if it can help! A lot of the complexity is just needing to be opinionated enough to pick a thing, like how I want administration and moderation to work for the group. I bought opensocial.community and am planning to use that as a place to store a group management app as well as use subdomains to represent group handles (ex. overcommitted.opensocial.community is a community handle representing the group resource).

I want to manage groups separate from my existing app so that it can potentially be reused in other apps if anyone likes this group management structure. I think that a lot of apps have some sort of concept of a shared group record (ex. a server for Discord servers or a subreddit for Reddit), and having a shared solution for this might unblock other apps building these things even faster. But if no one else decides to use it, at least it gives me another tool to unblock my next app even faster!

I am actively working on developing a model for groups as described below, primarily for use by Collective. I want to work with interoperability in mind and also work in the open, so I’m very open to feedback or direction if there’s anything I’m missing or fundamentally misunderstanding.

AppView: An app that is used to “view” the ATmosphere (ex. Bluesky is an AppView, Collective is an AppView).

Group: A shared resource DID that can be owned by multiple people and can have members. Group resources can be added under its repository, like a shared book list that is owned by a group of friends.

An overview of registered AppViews in the opensocial.community app, where a developer will set up and authenticate their app to be able to access API resources from opensocial.community.

Registering AppViews

The app at opensocial.community will authenticate AppViews using service authentication tokens (signed JWTs), which allow AppViews to perform operations like searching for groups, creating new groups, and managing group-specific records. For example, collectivesocial.app could authenticate with the JWT to GET a list of groups available, search for a particular group, or allow creating a new group with opensocial.community.

Each AppView can only create, update, and delete records in its own namespace. For example, collectivesocial.app can manage app.collectivesocial.feed.list records for the overcommitted.opensocial.community group, but cannot touch records created by other AppViews like app.other.feed.post.

Creating groups

AppViews like collectivesocial.app will be authenticated to opensocial.community with an API key to be able to create and search groups. This group creation will add the creating USER as the first admin, and the creating AppView as the first group app.

A diagram of what the opensocial.community PDS would look like, with records and repos for each individual group with metadata that admins can update and also has some shared records from different AppViews

Storing group data

I’m creating a PDS for opensocial.community to store the group data, like the metadata and the data associated with the creating AppView. I’m pretty new to this concept but I know that the data needs to live ✨somewhere ✨, so I’m assuming that requires creating my own PDS. I’m initially hosting the opensocial.community PDS myself to prove out the concept, though I’m exploring sustainable hosting models as usage grows—potentially including allowing groups to migrate to self-hosted PDSs or other providers while maintaining compatibility with opensocial.community’s management interface.

I also want to make sure I can support managing a group through opensocial.community created through a different PDS as well, but I haven’t totally figured that out just yet. Perhaps that just means registering an owned DID and a name of the group from any user?

An overview of the admin access to opensocial.community for accessing and updating groups

Administering groups

When a group is first created, the creating user is added as the first administrator. Admins then can use the opensocial.community app to view all of the groups they administrate, add other admins to those groups, update group metadata (ex. name, description, icon), or approve other AppViews requesting access to that group to make that group available to another AppView (so that different AppViews can reuse the same group with approval from a group admin).

Moderation

This is another thing I’m not 100% on, but presumably administrators will want to be able to moderate data in their group. For that I’m thinking we allow storing a list of labelers approved by the admins of the group to show/hide data based on the way the labeler is set up.

An overview of individual group membership, how members can subscribe to being in multiple groups with records in their own repo

Group membership

Finally that brings me to the last piece of this puzzle (for now), allowing users of different AppViews to join those groups. Those membership records will live in the user’s own repo so they can easily manage that group membership, and it will store a hard reference to the group located in the opensocial.community app. I think groups will be able to get data on things like who is in the group from backlinks—when a user’s repo contains a membership record that references the group DID, the group can query for all records that reference it.

The AppView will have an API available from opensocial to view all available groups to join and any user can join any group, with moderation/labelers administrated by the group administrators when needed.

Users authenticate to both Collective and opensocial.community using OAuth, which allows them to log in using their existing ATProto identity without sharing credentials.

Example: The Overcommitted Book Club

When I create the Overcommitted Book Club in Collective:

  1. Collective authenticates to opensocial.community
  2. opensocial.community creates a new group with DID did:web:overcommitted.opensocial.community
  3. I’m added as the first admin
  4. Collective is registered as an approved AppView for this group
  5. The group gets a shared reading list stored under app.collectivesocial.feed.list in the group’s repo
  6. Other users can join and we all see the same shared list

Roadmap

I’m considering the above list of capabilities as the initial roadmap for a MVP of this, and am hoping to have a working prototype in the next few weeks so that I can release the Collective app I’ve built with group functionality built in. This is essentially just a very over-engineered way for me to manage my book club 😂

In the future I want to consider things like:

  • Registering an existing group resource in another PDS to be managed by the opensocial.community app
    • I want to be able to store a mapping of concepts for an existing group to opensocial.community concepts so that we can maintain a single opinionated API between opensocial and consuming AppViews but allow multiple different group administration methods
  • Allowing easy migration of opensocial.community resources to be administrated by another group management app (in case folks want a different administration/moderation setup, in case I run out of money to host large groups, etc)
  • Perhaps supporting additional moderation/administration schemes, such as private groups or groups that require admin approval to join.

If there’s anything in here that you read and agree/disagree with or there are any concepts you see that I am fundamentally misunderstanding (likely hah), please do comment and let me know! I’m particularly interested in feedback on the authentication model. Based on my research, service-to-service authentication using signed JWTs seems like the right approach for AppView-to-opensocial.community communication, but I’d love to hear from others who’ve tackled similar challenges in the ATProto ecosystem.

Here’s a list of links I’ve accumulated while researching this:

0 Likes on Bluesky

Likes:

  • Oh no, no likes, how sad! How about you add one?
Like this post on Bluesky to see your face show up here

Comments:

  • Oh no, no comments, how sad! How about you add one?
Comment on Bluesky to see your comment show up here
Back to Blog

Related Posts

View All Posts »
Build the thing you wish to see in the world

Build the thing you wish to see in the world

For most of my career, I've been confusing building products with building businesses—and that confusion kept me from pursuing a lot of ideas. Two weeks off helped me realize that not everything needs to be a startup, and some of the best things we build are the ones we build just because we want them to exist.

A Software Engineer's Guide to Agentic Software Development

A Software Engineer's Guide to Agentic Software Development

I've cracked the code on breaking the eternal cycle - features win, tech debt piles up, codebase becomes 'legacy', and an eventual rewrite. Using coding agents at GitHub, I now merge multiple tech debt PRs weekly while still delivering features. Tickets open for months get closed. 'Too hard to change' code actually improves. This is the story of the workflow.