Finding groups
How an app learns that a DID is a group, shows it to signed-out visitors, and lists the groups a person is in.
From the network: the declaration
A group that wants to be found publishes fyi.opensocial.declaration (record key self) in its public repo:
{
"$type": "fyi.opensocial.declaration",
"meta": "at://did:plc:7x2k…/space/fyi.opensocial.meta/self",
"createdAt": "2026-09-24T17:37:00.000Z"
}
Watch for that collection on a firehose (a relay, Jetstream, or the host’s own com.atproto.sync.subscribeRepos) and you learn about each group as it appears. An indexer can use fyi.opensocial.declaration as its signal collection, so it only follows repos that are groups.
The declaration only exists while the group’s meta space is public. A group with a private meta publishes none.
For signed-out visitors: listGroups
Nobody can read a space without signing in, even a public one, because every space credential starts with a delegation token from the reader’s own PDS. For signed-out pages, the reference host offers an unauthenticated listing:
GET https://host.opensocial.fyi/xrpc/fyi.opensocial.listGroups
{
"groups": [
{
"did": "did:plc:idmhyhx3335jt2vin45xauu5",
"handle": "rainshadow-riders.opensocial.fyi",
"meta": "at://did:plc:idmhyhx3335jt2vin45xauu5/space/fyi.opensocial.meta/self",
"members": "at://did:plc:idmhyhx3335jt2vin45xauu5/space/fyi.opensocial.members/self",
"displayName": "Rainshadow Riders",
"description": "Seattle-based cycling club",
"url": "https://rainshadow-riders.opensocial.fyi",
"joinPolicy": "fyi.opensocial.profile#request",
"avatar": "bafkreifqxrjvscxf6uu23gr34uaugzyobcfdfjjajfialsyvbkorzrgo7q",
"rules": []
}
]
}
- Profile and rule fields appear only for groups whose
metaspace is public. Others return justdid,handle,metaandmembers. avataris a CID. Show it fromhttps://host.opensocial.fyi/img/<did>/avatar(or/banner), with?v=<cid>as a cache-buster.- A deactivated group comes back with
deactivated: trueand no profile.
For a signed-in user: “which groups am I in?”
When a member writes their acceptance into a group’s members space, their PDS records that they have a repo in that space. Ask their PDS:
GET <user's PDS>/xrpc/com.atproto.space.listSpaces?type=fyi.opensocial.members
(Through the user’s OAuth session. It needs space:fyi.opensocial.members?authority=*&action=read_self in the scope, or the PDS refuses with ScopeMissingError.) The response is { "spaces": [{ "uri": "at://<group>/space/fyi.opensocial.members/self" }], "cursor": … }.
From a handle to a group
Resolve the handle to a DID in the usual way. The DID document’s #atproto_pds service is the group’s host, and that’s where its spaces and records live. On the reference host, send group methods to https://host.opensocial.fyi with audience did:web:host.opensocial.fyi#opensocial (see Calling group methods).