I think this is The Question™, and I didn’t see a clear answer to it. I have my own opinion, but first two possibilities:
Option 1: Record
I’ve explained why I don’t like this option in the thread itself:
I didn’t elaborate but aside from my NSID point, what I said in another thread remains: you cannot make assumptions about what groups have. Display name? Description? Code of conduct? I can think of groups that require none of these. And if we start listing it, does it mean we need to have one for individual? Where does it end?
Set aside that I don’t think "type": "group" is the best we can do, this isn’t a terrible idea per se. We go to the DID doc all the time to get handles, may as well get some more info from there.
…and speaking of “not a terrible idea per se”…
Proposal 3 (the actual one): Group Service
Ok, bear with me. I already brought this up in my Concierge article, and for how weird it may sound it’s actually the one that makes the most sense and is supported by prior protocol history:
An Atmospheric Group is a DID with #atproto_group entries in its DID document and a public service URL that responds to GET /xrpc/com.atproto.group.getMembers.
Personally, I think the “when you configure an account as a labeler" plainly written there is a great representation of how this mental model is actually already present in the protocol (and we’re expanding, not creating).
Potential objection #1: editing the DID doc is A Lot™ to ask
Well, it should be! We’re talking about changing the whole dynamic of an account. Do we really want someone to be able to just delete a record and make something not be a group anymore?
Plus, so it is for creating a PDS the first time, but we do it anyway: at that point we’re already in the DID doc editing… let’s just put more in it!
And if one didn’t do it at account creation? Well x 2… do you want them to change the way their account is represented in so many places without a scary operation explaining the consequences?
It makes sense that this be an “expensive” operation.
Potential objection #2: the member list Lexicon endpoint is “problematic”
We know a big point of tension is that “membership needs to be able to be private”.
And at the same time, if there’s one thing we know of a group is that it has members. That may be the only thing we know.
And here’s the thing: responding to GET /xrpc/com.atproto.group.getMembers doesn’t mean that the answer needs to make membership not private! Potential answers include:
401, non authenticated
403, unauthorized
Empty list (oooh, wonder if there’s some you may not be able to see )
A list of DID that are associated with this group
A total number of members (or null for “you don’t get to know") => accounts for " you can know how many there is but not who they are”
Etc. etc.
We can work on what this looks like, but it’s not too far fetched as a contract. There may be more, there may be not… but the more I think about this the more this feels correct.
It may make sense to jam on the required endpoints. I wonder if it’s possible there to have more than one, and in that case those become the group management methods. I’m thinking of things like joinGroup and leaveGroup, which also seem like things most groups will need to implement (even if it returns a 404 or something).
I also really like the symmetry with labelers here. And not having to pick a particular domain name to associate a lexicon with because that always feels intimidating
Generally I’m pretty skeptical of the “DID doc service” pattern for stuff. I basically think it makes sense for explicit new “protocol roles”: pds, labeler, space host, etc but probably not for application features. The reason is that DID documents are very sensitive and so there’s a lot of opsec & UX concerns around how to communicate the ability to update DID documents. Generally lexicons are the protocol’s mechanism for making things legible. We could introduce new OAuth resource/scope semantics around DID doc updates, but that’s a whole new chunk of protocol work that I doubt would land in time for atmospheric groups.
It’s worth pointing out that in the context of the Bluesky app, an account “becomes a labeler” by virtue of having a app.bsky.labeler.service not by having the service endpoint in it’s DID document.
I’m not super staunch in this viewpoint. But i think we need a pretty good reason to go with “signature on DID doc” over “signature on record”.
Regardless of it’s in the DID document, I think this design should not be atproto / com.atproto. This is explicitly not a protocol concept. It’s standardized, but it still operates a layer up from the protocol and is just one way of doing groups on the protocol.
I think it’s worth having the discussion of whether a “group” service should be seen as a protocol role, or whether some part of it should be. If some responsibilities are going to be delegated, then maybe we should have a standardized way to make that delegation official, even if not specific to groups?
That’s part of my argument for it, though: it is a sensitive operation! You’ll be changing how your account is seen by most applications in the network, and the relationship people will have with it. To me it feels like a generic “just write a record" permissions don’t convey the “cost" of doing this conversion, and particularly the one of walking it back.
I do think we should! It doesn’t have to be now, but I already felt this pain when trying to create a system for giving people the ability to sign their own attestation/badges, and lamented the lack of one.
I think we should in particular if it’s keeping us from using and expanding the system. It’s a nifty part of the protocol to be able to attach more services to your DID, and it definitely feels underutilized now.
…which causes all types of issues. For example, it contradicts what you said above about services being for “protocol roles": if it is part of the protocol, then why is a bluesky lexicon a fundamental part of it?
In theory, you should be able to write atproto.com without mentioning Bluesky at all, and labelers clearly are making impossible. It also cannot function as a labeler without the actual service, right? Cause nowhere in the record it seems to say where you go to get the labels.
(Want to clarify this is 1000% not meant to be accusatory, it’s fine to me that it’s like that right now. But it is something to think about and figure out an answer for!)
Then we do need to figure out who’s gonna own this other layer, cause I think this is too important to leave to just about any of the folks currently involved here. For example, I cannot speak for e.g. other members of the lexicon community TSC (haven’t talked about this with any), but the responsibility to own a lexicon that defines what a group account is is a pretty big one. Giving control to ““us”” would be turning a community-led organization meant to be “an org, like many can bring up on their own” into a more powerful body than others. It’s fine to start, but it just feels like a responsibility no one group should have.
To clarify, this is specifically related to the fact that a group account is going to be a different type of account. One may call back to Standard Site as an example of this being ok, but consider: a DID may own a publication, but it ISN’T a publication. For how we’ve been treating it, a DID account IS a group.
(It’s fine if we think that’s not how it should be done, but that’s the mental model I’ve been operating with, so it would be good to clarify anyway.)
Pulling this quote out on its own because I think it’s a key part of the mental model. What I said above was:
You can standardize even just this: if a PDS is DID represents a group, then you can get a list of members from it. Nothing else is fixed. You can still have extra lexicons to learn more about the characteristics of members, the name/description/avatars of the group, to define the spaces the group owns and meet in, and everything else. Those can be in the PDS and use Lexicons defined by whoever runs specific apps (similarly to how labels do work right now) and those could be standardized by e.g. lexicon community (or another org). But the part about “tell me who your members are” can be reduced to its essence so it can be build on top of by these apps, and that’s the part I’d argue should be a service.
It’s a different level of sensitive. At the end of the day, it’s application-layer & recoverable. DID operations can result in irrecoverable full-account compromise. For comparison, “deleting all of your Bluesky posts” is a sensitive operation, but is still captured in in “just write a record” scopes.
I generally agree, but I just don’t think we can treat it as a prerequisite for this community/groups work.
I don’t see the issues that it causes. Many things in atproto have a more generic protocol representation and a more particular, userland, application-specific representation.
Labelers & labels are a generic atproto concept. This is represented by the service in the DID document. They’re also used in a particular manner in the context of the Bluesky application and thus have a Bluesky lexicon declaration. Both are necessary in this case. It prevents Bluesky from bogarting what a labeler can be used for by forcing its application-specific labeler semantics down into the protocol layer.
My expectation is that this was something we could collaborate with Lexicon Community on holding. If either Lexicon Community or the working group around this thinks that’s not a fitting organization for it, then we can discuss other options to house it: Bluesky or a new organizational structure. Good topic for conversation!
I’m not sure I like enforcing application semantics like this in an account-wide, cross-protocol manner. I think DIDs can be “modal”, in that they might be a group in one context, a publication in another, and an actor in another.
For instance, a Bluesky labeler can still be a normal Leaflet user. In the context of Bluesky, the labeler record causes it’s profile to render as a labeler. Other applications (like Leaflet for instance), can then determine if they want to treat it like a labeler (because of the Bluesky record) or as a normal user.
I actually don’t think you can standardize on this one at the protocol level. It really does strike me as an application semantic. I expect some groups/communities will want to keep the list private, others will have various tiers & roles, others may have time-limited permissions or permissions that inherit from another signal like a vouch or payment receipt. Maybe in some sense, these can all be discussed as “membership”, but it’s a bit lossy.
To be clear, I actually like the communities/groups standard that we’re working on having a clear notion of membership! I just think that’s a userland thing & isn’t a given at the protocol level.
Yes, but we’re talking about “new accounts” for the most part, right? And at that point you’re creating the DID document (I assume?) which (again, I assume?) means you can write the service directly in it. My point is that it’s not harder than writing the ATproto PDS service for a new account, and for old accounts this is a a significant-enough operation that it should have some “weight”.
Not to open another can of worms, but I think there’s 2 separate things here too: one thing is a “lightweight” creation of a group, where someone is like “oh yes, i’ll make a groupchat for me and my friends” (a user “owns” a groupchat) vs the promotion of it to a Group™, with its own DID (the group is an entity that “““the group itself””” owns). When is that flip warranted?
100% (see below)! I’m mostly making the case over what this should move towards, which we can keep in mind even as we implement a subset of it now.
Yes! 100% (again). My objection was specifically to “an account “becomes a labeler” by virtue of having a app.bsky.labeler.service not by having the service endpoint in it’s DID document”. My point being:
an account should become a labeler because it has a generic atproto “labeler service” (not a app.bsky.labeler.service, but #atproto_labeler)
on top of that the labeler can have application-specific labeler records (like app.bsky.labeler.service)
For groups:
an account should become a group because it has a “group service” (not a community.lexicon.groups.service, but #atproto_group or something different that still encodes a similar semantic)
on top of that a group should have application-specific group records (like maybe community.lexicon.groups.service or community.lexicon.groups.profile/invite/member)
In the short term, we can do the same(ish) as Bluesky does now and only rely on the community.lexicon.groups.service-like record being there, but ideally we’d get services to be a more prominent part of the protocol and switch to considering those the source of authority.
What I’m arguing for is the ability to have completely different definitions for the in-and-outs of groups, and still have a minimum layer of “apps will know there’s a concept of members associated with this DID, and a way to ask who is in it and who isn’t“.
You could also see it as the group deciding its own “authority” for group operations outside of specific AppViews. For example, two different AppViews could return conflicting information over membership, so where do you, downstream application, go to find out who the DID considers “right” without hardcoding the logic within yourself?
This is also not in contrast to what I’m saying: my “labeler” account is still able to have e.g. a “standard.site“ publication and be seen as an author for those purposes. It has the ability to post on Bluesky to communicate with folks. It just has:
A different-looking avatar, signaling this is a different type of account
An extra tab, surfacing information that other accounts don’t need to surface
(from AppViews’ POVs) An extra endpoint to ask questions that other DIDs don’t have the ability to answer
I guess you could have a DID that’s just a publication, but I think that’s not a strong-enough “semantic concept” to be encoded at a protocol layer, but maybe…let’s just leave it at “I have a lot of thoughts about possibilities, but it would risk derailing this convo”.
As I was saying at the start:
The only real difference between an individual account and a group account is just that the latter encodes a notion of “other entities may be members of it”.
To streamline what I meant:
An account is considered a Group account if it has a #atproto_group service endpoint
That service endpoint is where application go to “ask” GET /xrpc/com.atproto.group.getMembers
GET /xrpc/com.atproto.group.getMembers returns “ a list of members, which can be:
A list of DIDs that the asker is allowed to see are members of the group
A 401/403 etc. if it has no permissions to ask the question (the private groups)
An empty list for “you can ask about members, but you don’t have permissions to see details of any”
(optionally) a list of total members for groups that are “you can know how many members we have, but you can’t know who they are”
Then separately from this you can have application-level group semantics, like profile (group’s and members’), roles, permissions, etc. Those can be community.lexicon.groups.profile etc.
The important part to me is that if the definition of group is “it has an endpoint that returns a list of DIDs + a count”, then any other “semantic” added on top is just a different presentation layer.
For example, I can decide that for my application the group’s profiles are com.fujocoded.groups.profile and at the level of the protocol that’s fine! Sure, as the “ATproto builders” community that creates applications on top, we may all eventually converge to community.lexicon.groups.profile, but that’s separate: me choosing to do it differently doesn’t make my DID not a group, it may just result in my groups’ profile looking wonky on other apps.
Doing it this way actually allows us to encode less right now, and still keep it flexible for the future.
Coming out of the meeting, I want to call out @blaine.bsky.social’s objection to this, which I think raises a good point (for both mechanism). The objection was “we want to be able to swap out the group manager because that’s gone rogue, and a signed attestation in a PDS means you can rebuild the list without cooperation from the manager“.
First, to clarify: the idea of “this service answers the GET /xrpc/com.atproto.group.getMembers question” doesn’t mean that these records cannot be published, it would just make having these records one possible way to define membership.
But second, this opens up two further questions:
How do we have a DID attest “yes, indeed, I am part of this group”? Regardless of whether we have a service that lists members or we have what a groups says its members are declared otherwise, that still leaves open the other half of the question: how would a member corroborate this statement?
Do we want to ensure that it’s possible to rebuild the group data if the group DID itself “goes rogue”? For example, if the group DID’s PDS goes down, does that mean the group’s info is lost, or should it be recoverable by going through members’ PDSes and looking at the statements there?
I need to go dive deeper into permissioned spaces, as I think that’s where some answers may be and I need to go re-read the spec with this in mind. I’ll do that, but I wanted to make sure I capture these thoughts first of all
Thank you all for the convo and the meeting today!
This was great and it was really cool to have some synchronous time to chat with everyone and their thoughts around it
I think you both make very valid points here. My preference right now would be to go with a lexicon because it will be the easiest thing to change long-term, with the expectation that we revisit based on adoption by atmospheric communities in the future to see if it should be something a bit more “hardened” and long-standing, living in the DID document. Given that the DID document takes a bit more effort to change, I’d like to better understand exactly what that needs to be before doing it.
That means that we would need to potentially migrate in the future, but hopefully that isn’t the scariest thing in the world to have to do, depending on how many group management schemas end up existing
That brings us to “but which domain/lexicon is a group”, which means either:
Deciding on one domain/lexicon specification OR
Having a central repository mapping the concepts together somewhere
Either seem like valid possibilities! I believe @zicklag.dev is using muni.town in the Arbiter and was planning to continue using that, so perhaps item number 2 would be the easiest thing to do for now, to revisit at a later time what “one single representation of a group” ends up meaning (and if we even need that).
I’m definitely open to using other lexicons. I was using town.muni just because we have that domain. The intention would be to transition it to community.lexicon or some other such standard-focused NSID if at all possible.
My attention has been divided having to focus on shipping Roomy lately, so I’ve been having a hard time keeping up with things.
Generally, though, my perspective right now is that the Arbiter we’re making will serve as the enforcement engine for a community DID. It’s nearly completely generic and can work with anything built on XRPC.
The hope is that as soon as possible we can start prototyping roomy with the Arbiter + the permissioned data proposal, maybe with HappyView’s implementation, and see what that looks like.
As far as the group standard specifics, though, I’m pretty open to trying whatever lexicons surfaces from the community, and I’ll participate as much as I can to try and get the Roomy / Arbiter perspective involved in discussion.
An interesting question is “when will anything other than a PDS get consensus to make it into the DID doc”
If the answer is “nothing, ever” then that’s something we should think about as a community.
I think the concept of “migrating a group off of Bluesky” is something that should get considered. No idea how far along the product team is in thinking about that. This is a “must” and should be considered ahead of time.
…and of course, if it’s “just lexicons” + a regular DID account, then my expectation would be that the bsky team updates the reference PDS so that anything that runs on bsky.app can run on an independently hosted reference PDS.
If it requires additional services beyond a PDS … that argues for having a DID doc service entry, AND having a bsky reference implementation that can run at sub-bsky scale.
All tricky stuff!
Since this thread is all coupled with community / groups … it also makes me think that bsky team should ship some “personal private data” ahead of group permissioned data, in order for us all to practice interoping/migrating THAT data…any comment on that?
I’m not sure that the answer would be “nothing, ever” in this case, but it does seem to me like a DID doc would be harder to change in the future if we add to it now. Personally I’m rooting for us re-evaluating the groups specification 6-12 months from now, realizing we DO have consensus on it, and then putting the important parts in the DID doc and getting alignment on a more permanent structure for them. I really like the symmetry of having those things defined in a DID doc, similar to how labelers are defined
BUT I think the pragmatic thing to do is likely to make the smallest change first, and then re-evaluate as we weigh the risks/drawbacks.
I can’t speak for all of Bluesky (I’m actually not on the app team or even the protocol team, I’m just Highly Opinionated that groups/communities should be interoperable and that is why I continue inserting myself into this discussion), but my preference is definitely for designing to migrate groups off of Bluesky, and off of any app for that matter. Same with migrating group management software; I’d like that to be roughly as easy as switching PDSes.
Also I believe that permissioned spaces is going to ship before groups and communities will. I’m not sure there’s a way to decouple personal private data from group private data, if we are considering that a group is also a DID/account so migrating the two sets of data should functionally be the same (but could be way different depending on app/data structure implementations).
yeah, just pointing out that personal private data is the scoped simplest blast radius of one person – bookmarks in bsky app might be one to work with, and if the app team don’t already have it or something similar on their list, they should