Removing the member list?

I read this comment & this post from @jesseplusplus.com and they got me thinking.

What if we could make our boring auth even more boring-er?

Basically, I don’t think the members list is actually necessary to have on protocol. In the same way we can kick write access up to the application layer, I think we can kick “enumerating readers” up to the application layer. A space host still needs to know who has access to a given space so they can decide whether to mint a credential to a requesting user. But that access doesn’t need to be materialized or broadcast as a list. It could be modeled arbitrarily by the space host.

I’ll discuss the concerns that came up for me, my thoughts on those concerns & then some of the benefits to removing the member list.

Three concerns came up:

  • How do I, as an author in a space (but not the owner), know what the perimeter is for the stuff I write?
  • Does this hurt the “space migration” story?
  • How does an syncer know the boundary to sync from?

After thinking about it, I don’t think either of the first two are concerns.

If a given space host or application cares about either of these they can still materialize the member list or access logic in records in the space! At the end of the day, the space host wasn’t bound to only minting space credentials for accounts on the list. A space host could mint a credential for anyone, regardless of if they were on the space member list or not. This just owns up to that reality, makes the space credential the first-class mechanism, and leaves the member list up to the application.

I believe the space migration story is similar. If migrate-ability is a concern (which I think it generally should be), then you should model access logic in records within the space owner’s repo in the space. This is basically exactly the same as modeling membership in the member list (which was in a repo-like data structure, but not technically a repo). All the same threats are available from a malicious space host: misrepresenting access data, refusing to export, deleting the space without consent, etc.

On the data boundary, I think we can have the space host help facilitate this. The space host should be aware of all repos that have published something in the space (since it helps route write notifications). So to backfill a space, it can just provide that list to any syncer. This is a list of writers not readers. So it’s more efficient to do backfill this way & doesn’t give away reader visibility. This would involve introducing a new method on space hosts to help list all known writers in a space.

Okay now onto benefits:

Applications get to decide how they want to handle reader visibility. This enables apps like Frequency. Removing the memberlist as a required concept is still just as featureful for applicaionts,

We get to ditch the member list data structure! Honestly, I thought this piece felt a little weird. “It kinda functions & syncs like a repo, but isn’t actually a repo”. Now it’s just space credentials & permissioned repos.

Complex access logic doesn’t need to be materialized as a list. Two spaces under the same community DID have basically (but not quite) overlapping member lists? If the member list is part of the protocol: too bad, you have to materialize it twice! Now a space host can store it in whatever way it sees fit. I think the folks at Roomy will be very happy about this one.

Less state needed for minting credentials. Related to the prev benefit^ Imagine a space on your PDS for your “follower-only posts” (this space would be under your DID). If you’re materializing the member list, then an application-aware service (like the Bluesky app) needs to hold an OAuth credential for your account & add/remove members from the list as you gain/lose followers. Without a member list, the managing app doesn’t have to control a list on the PDS. I think we could introduce a com.atproto method that you could configure a space to reach out to to determine if a credential should be minted. So if I configured my “follower-only posts” space as such, then when someone reaches out to my PDS for a space credential for the space, my PDS reaches out (generically) to the configured application to determine if it should mint the credential or not. Just an idea! But this seems like a much less clunky way for PDSes/space hosts & applications to coordinate

9 Likes

This is roughly where I’ve been stuck, but on the other side.

How do we maintain member lists across spaces?

Does it make sense to have an on protocol “groups” entity which contains lists of DIDs?

As a community lead, if I have an ATPROTO TOUCHERS group, how do I maintain (or have open registration) of a list, that I can use when creating a new Space for a data type?

This would be including whether that member list is itself permissioned data so not everyone can read the full list (this is tricky).

I’m pretty sure I need legible groups that works across spaces, and it should be owned by the community DID.

3 Likes

I actually think this makes it easier to maintain member lists across spaces. Because you can maintain that member list in the space host or in a particular collection that’s dedicated to maintaining the member list. If we materialized the member list per-space, then it was a lot of work to keep them all in sync.

I don’t think protocol-level “groups” (I usually refer to that concept as “circles” as in “Google+ circles”) are the answer to this. I talk about it a bit in this post (search “circle). But, the second you have circles that extend across modalities, I think they accumulate a lot of complexity. You’re back in the universal instead of in the particular. I haven’t seen any way to make that legible in OAuth consent screens or prevent unexpected side effects (adding a user to a group in one app adds them to a group in another app that you didn’t realize/forgot where “the same group”).

I think this actually does work! Those groups would just be defined at the application layer. The space host could then use the group to determine access to all the underlying spaces.

Basically, I think the flexibility of removing the member list actually gets you closer to being able to do what you’re interested in doing across spaces in the same community. But without positing a universal “circle” primitive

3 Likes

OK, let’s try this. I have a group[1] called ATPROTO TOUCHERS, description “A group for enthusiasts building on atproto”

The Bluesky Application figures out how it wants to make those things, and makes a group type. ATPROTO TOUCHERS is open registration, anyone can join. The Space associated with the group contains bsky posts. Group (Space?) owners / admins can kick people out and/or moderate out individual posts, members list is public.

Is ATPROTO TOUCHERS the Space name or the Group name?

If I want to re-use ATPROTO TOUCHERS for events, to make a shared calendar, how do I re-use that list of members?

Rather than Bluesky App (where community / group / space support is TBD), we could also use our custom atmosphere community site as an example. One group, one space per lexicon data type, we really do want to re-use the same list across all Spaces, I think?

The non-public member list need could be done off protocol…or using permissioned data spaces itself to implement???

EDITED: basically, if Bluesky-the-app defines a members list, and it isn’t portable across apps, I think that’s going to be a challenge. I have very specifically have concerns about Bluesky-the-app, but any-app-that-gets-big-and-has-groups is the same concern. Hence wanting to move it on protocol for the sake of portability and re-use.


  1. which is the concrete word I’ll use consistently, you can say “member list” or insert “list of DIDs” here as you look, but I do actually think we need a named thing… somewhere ↩︎

1 Like

The short answer, is: I think all of these things get answered on-top of the protocol, hopefully through ecosystem-standardized mechanisms like the stuff @zicklag.dev is working on with the Arbiter (and that I hope to spend more time getting into soon!) I tried to get into that a bit in this post.

Here’s one way I could see this shaking out:

Some vocab:

  • space: a permissioned space in the protocol sense
  • community: a DID with a handle + a collection of a bunch of spaces
  • hub: the bluesky application feature building on permissioned data (plz don’t over-index on that name, just needed something different from space/community)
  • xyz.community: the namespace for the community standard we nail down

Alice creates a new hub on Bluesky. She gives it a handle @atproto-touchers.com. This mints a new DID for the group, and creates 3 spaces with types: xyz.community.mods, xyz.community.info, app.bsky.hub.

The host for the space is aware an ”xyz.community arbiter”. So it is application aware of xyz.community schemas but not necessarily of app.bsky schemas.

The arbiter is hosted by Bluesky so that this is easy for Alice to do form within the Bluesky app. She can migrate it at any time to another xyz.community arbiter by migrating the DID and all associated permissioned repos. Alice can log into the arbiter with her atmosphere account and configure things like the moderator/admin settings as well as the recovery key for the community DID.

xyz.community.admin is a space for privileged things that only mods for the community can see. For instance mod mail and maybe some community configuration. It would also include all the mods for the community as xyz.community.moderator records. Only moderators get read access to these records

xyz.community.info contains a list of all the members of the community. Each member is represented by an xyz.community.member record. It also contains an xyz.community.profile record which declares the community’s name is ATPROTO TOUCHERS. This space is listed as “public”, so any one can request a read credential to sync it.

app.bsky.hub contains all the bluesky posts, likes, comments, reposts, etc for the hub. The space host only grants read access to members based on the xyz.community.member records.

To join a hub, you call the xyz.community.joinRequest method on the space host. The space hosts adds you as a member in the xyz.community.info space which the app.bsky.hub space then inherits.

If you want to re-use “ATPROTO TOUCHERS” for events, then you create another space under the @atproto-touchers.com with the type community.lexicon.events. The xyz.community arbiter is able to inherit the same member list configured in xyz.community.info to this new space.

These events won’t show up in the ATPROTO TOUCHERS hub on Bluesky. But they would show up in any event management app (which could request access to all your community.lexicon.events spaces. ATPROTO TOUCHERS, could deploy a community site at atproto-touchers.com which includes UI for interacting with both Bluesky content from the community & with event content from the community (along with any other space type that you want to hang off it).


So to pull a few things out from that:

  • there’s an in-between between the permissioned data protocol & Bluesky. This is where the “community standards” stuff that I’m trying to direct attention to sits. I laid out one example for how it could work, but what I like is that this layer is very flexible and we can iterate on it as an ecosystem. putting stuff at that layer doesn’t mean it’s “off protocol” or being bogarted by the Bluesky app
  • “Communities” on the protocol can reuse lists of DIDs (or other access control rules) while still grounding them in particular types.
  • Universal communities can emerge in the Atmosphere. While the communities are universal, the spaces that they’re composed of (including the configuration spaces like the members list!) are particular
3 Likes

Great! So what you’re saying is the community should work on community lexicons :squinting_face_with_tongue:

That fits quite nicely into what Ms Boba @essentialrandom.bsky.social just wrote up

2 Likes

Yes! I think this makes a lot of sense. In that thread I suggested that we make a space host that literally lies about the membership list to participants other than the owner.

I think the very fact that we could consider this “compatible” “tweak” to the protocol that is useful and yet somewhat subversive is a good sign that maybe the member list doesn’t belong at this level.

Like you say here:

This is essentially the same boat we were in with the idea of public permissioned spaces: there’s no reason the space host can’t just give the any random passerby a space credential.

Having the protocol be “honest” about the freedom that the space host has here seems like a good call and means that we aren’t soft-excluding more creative use-cases by either giving them more work than necessary or making them technically non-compliant to the spec even if they are compatible because the PDS can’t tell the difference.

Yeah, I think this might help Habitat, too. :smiley:

In Roomy we will probably still need to materialize a member list, so it might not make things a ton simpler at the end of the day, but it does still feel better that we can leave that choice to implementations / scenarios where it is actually needed.

This is very similar to the “federated spaces” idea that we were thinking about with the arbiter and, in the latest iteration, our policies can call out to any other XRPC endpoint in order to make auth decisions like this.

So that’s perfectly along the lines of what we were thinking. And @essentialrandom.bsky.social’s post that Boris link touches lightly on this, too.

Yes! No more syncing. We can just decide, at request time, by any means, whether or not someone is allowed to read the space.


This may actually solve some performance concerns I had about very large groups, too.

You could have millions of readers added to a group and not having to enumerate them can potentially make it a lot easier to maintain performance as the group grows.


Yep! I think that community semantics are notoriously difficult to nail down, and that the Atmosphere should have as much time as it wants to figure that out, while we need private data on protocol ASAP.

I think that Bluesky will likely want to define it’s own group semantics for the purpose of the Bluesky app, and maybe it will be generic enough that some other apps can use it as a starting point too.

But I really think most of this lies in the realm of lexicon development. I think that all we really need on protocol is the bare essentials of private data and synchronization. The rest is all stuff that we need to figure out as the Atmosphere, and that will develop over time, just like standard.site and lexicons.

3 Likes

This simplification would definitely work for frequency!

Without a member list, the managing app doesn’t have to control a list on the PDS. I think we could introduce a com.atproto method that you could configure a space to reach out to to determine if a credential should be minted. So if I configured my “follower-only posts” space as such, then when someone reaches out to my PDS for a space credential for the space, my PDS reaches out (generically) to the configured application to determine if it should mint the credential or not. Just an idea! But this seems like a much less clunky way for PDSes/space hosts & applications to coordinate

This would work really well for a case where you have to approve follow requests and you keep your followers list private. I was struggling with how someone would verify that private follow record existed for the other person if that was personal permissioned data, but just having the PDS reach out to the application to determine creds gets rid of that issue. It obviously adds some complexity to the flow, but just once at the time of credential minting. Seems much better than the complexity and coordination needed if the member list is on protocol!

3 Likes

I’m not 100% up to date on this stuff, so I may be wrong here …… but, it seems like the more stuff we offload to appview and not have on protocol, the more the app owner controls data flow and we revert back to the world where the enshittified service owns my data and not me. Hopefully I’m wrong and ya’ll can correct me

1 Like

I like the cleanness of removing the member list and I think it will work great for the dedicated space hosts. But is it not also expected that anyone can host a space under their own identity on their own PDS? These spaces hosted under the users own identity shouldn’t need the more complex membership management (or if a user thinks they do they should go to one of these dedicated space hosts). Would removing this member list lead to PDS hosts (which are currently very ubiquitous) handling these in different ways?

1 Like

I think that as long as you can query Bluesky’s ATPROTO TOUCHERS group, you can maintain the member list there – or even sync it to another lexicon, just like “follow” can be per app / lexicon – and still use it across many spaces.

This is roughly where some of my concerns were.

3 Likes

I had to think about this a bit because it’s nice to be able to make as-powerful statically hosted apps too, but I agree that taking membership off-protocol is much better:

  • As user: It makes privacy work as expected. Apps can limit what each user can learn to exactly what they see in the UI during normal operation, which matches the precedent established by centralised apps.
  • As dev: It makes the privacy policy easier to write, and also gives the flexibility to make access management as efficient as needed.
  • As protocol designer: Most importantly, it shifts the path of least resistance in making apps more towards best practices (by making user privacy the baseline and raising the minimum viable implementation above “client-only app that leaks user behaviour by calling through to all PDSs directly”).

I assume that it will still be possible to make mostly client-only apps by materialising the list into the space (which may be appropriate for public groups in some cases), but I hope that we’ll rather see turn-key app servers that include privacy-preserving space access management features and are easier to host than in-client permission evaluation is to write from scratch.

2 Likes

I think there are several things that make this different.

The data can still be stored on user PDSes.

So even if the AppView can control who gets a credential to view my data, it can’t take my data on my PDS away from me. ( For the sake of argument, assuming you trust your PDS. )

Communities tend to need some central control.

I think @essentialrandom.bsky.social does a really good job walking through the holistic “why” of that in her post here: The Vault: a (possible) answer to the "Fully-Private Community Data" question.

Basically, we do need communities to be able to elect a central coordinator for private or shared community data. The important part is being able to swap out this service if it goes bad, which is preserved. ( And explained in @essentialrandom.bsky.social’s post, too. )

The members can still bypass the space host.

Hypothetically if the community members know about each-other ( which is sometimes an anti-feature as in the Frequency use-case ), then they can bypass the space host and grant each-other access to the community data hosted on each-others’ PDS.

This can be used to create a new community with the old data, if the community doesn’t have control over the community DID and they just need to “fork” to a new community DID.

The space host is more narrow than an AppView.

The space host doesn’t have all of the responsibilities of an AppView. You can also still have multiple AppViews that index the same community. The space host can end up more like a member / policy-aware “relay”.

So while it does hold a lot of power, it can still be replaced, and it doesn’t hold all the power.


Yeah, I honestly wonder whether or not it’s a good idea to let people host spaces on their PDS or not.

:thinking: I think that the only way it makes sense to have groups hosted on the PDS is if we can come up with very simple, general, and yet still useful group semantics that are reasonable to force every PDS implementation to implement.

Maybe a basic membership list is simple and generic, but I guess the question is whether or not it is useful.

Are apps actually going to use it, or is it something that may as well be stored as records in your repo that an app’s space host can read? In practice will apps generally have to have a more sophisticated space host anyway?

Maybe this is something we don’t have to answer right now?

For example, we can implement the base protocol without having any space host on the PDS and let other people start building apps with private data and groups as soon as that is done. Then if it seems that there is a use-case for a built-in space host that is general enough to expect all PDSes to implement it, we can add that in another step.

3 Likes

I think in a couple of @dholms.xyz diaries he’s listed many cases for permissioned spaces, with groups being at one end. Groups are at the more difficult case, and perhaps arguably even the most important, but definitely not the only use case. But things like bookmarks I may want to stay private, or even use in multiple apps, but surely I don’t need this under a new identity, and preferably I don’t need a separate host from my public content. Or for my favourite use case, around apps returning analytics to creators, I’d want creators to not have to set up additional identities or even specialised space hosts just to get this data. When it gets more complex, e.g, paid for content (but still by me), I’d argue I should still be able to do this on my standard PDS, but I can see that specialised spaces and hosts might be needed.

2 Likes

Not offloaded, which is what we clarified here. We’re saying that member lists are out of spaces, to give people the flexibility on how to manage that member list, including for re-used.

So, converging on e.g. community.lexicon.community as a lexicon for group member lists is one way to do that, without restricting it to just one shape for member lists. That group lexicon does not currently exist!

Spaces / communities are all converging on DID accounts, which are just as portable as anything else.

The tricky thing is going to be PDS forks / special features.

4 Likes

I think it’s likely there are many types of communities that may exist across different apps, quite a few of which will take some similar shapes :grinning_face_with_smiling_eyes: some will be super public and then it may make sense to have a fully public member list, some will be private and just within the app, and perhaps get some sort of management app like the Arbiter to control lists (or make them completely hidden).

But yeah I think there are enough similar shapes that it would be good to work together to outline what is likely to belong in those shapes. I really, really want to see interoperable communities across every app that wants to implement them in the Atmosphere :grinning_face_with_smiling_eyes:

I mention it in this comment but I’ve got a meeting set up for next week to chat about requirements that folks already know of, if anyone is interested in an invite! [Discussion] What would help folks to see standardized? What are you itching to have answers on? - #3 by brittanyellich.com

4 Likes