working title


We want to put different group content in different sub-feeds, in order to support more partial replication.

But we need have a clear way to discover how you’ve been invited to a group without replicating the whole group’s data.

We also need to consider how to ensure our group data is replicated enough. For example, if a group is just me and my sister, then are there gonna be enough copies of the group data? If there are too few copies of the data, there’s little or no gossip propagation, and can only get updates from my sister when we are directly connected.


  1. Group membership should be opaque
    • You shouldn’t be able to guess who is in a group using public info, such as:
      • Shard feeds
      • groupId as a “cloaked message ID”
  2. Peers should replicate sympathetically


This work builds on the ssb-meta-feeds-spec (v1).

We define two types of feeds that each peer will have: 1.
the group-invite feed 2.
    group feeds

Diagram showing an example layout of group-related feeds. Note that the shards in your use-case will likely not be those shown, see how they are determined below.

The invitations feed

This feed holds messages which help peers join groups (e.g. group/registration, group/add-member messages). This feed MUST be unique for each peer (a singleton). Each peer A who replicates the root metafeed of another B SHOULD also replicate B’s invitation feed.

The invitations feed MUST be a direct subfeed of a shard feed, where the shard is derived using the string "invitations". The invitations feed MUST be declared on the shard feed with feedpurpose equal to "invitations".


Where: - :shard is the shard feed derived using the string "group-invite" - group-invite is a content feed with - feedpurpose = "group-invite" - feedtype = ? :warning:

See ssb-meta-feeds-spec for detail about the v1 shared and the shard calculation.

All content on this feed SHOULD be encrypted with box2 encryption.

Group feeds

Each group feeds MUST be a direct subfeed of a shard feed, where the shard is derived using the groupKey encoded in BFE. A group feed MUST be declared on the shard feed with feedpurpose equal to the groupKey encoded as an SSB URI string.

Where: - :shard is the shard feed derived using BFE encoded groupKey
    We cannot use the `groupId`, as this is publicly known, which would give attackers a way to test if people are in the group (breaking Principle 1.)
    <br />
    We choose the the `groupKey` because it is a value known only to those already in the group.
  • :groupKey is a content feed where
    • feedpurpose = groupKey where groupKey is the ssb-uri encoded groupKey for the group
    • feedtype = ? :warning:
    • the announcement of the this sub-feed MUST be encrypted with this group’s groupKey
      We need a `feedpurpose` which is unique to the group, which the `groupKey` is.
      We cannot use the `groupId`, because this is derived using the group init message, which does not exist until our feed exists.
      We encrypt this announce message so as not to leak the groupKey AND to protect group membership.
      <br />
      For sympathetic replication we will therefor need a distinct type of announce message (TODO)


1. Creating a group

Staltz starts up his application. We assume he has already created his invitations feed (following the spec above). In his application he creates a new “helsinki” group, which means he: 1. Creates a new symmetric groupKey 2. Creates a content feed under a new shard (using the groupKey following the spec above) 3. Publishes a box2-encrypted group/init message on that new “helsinki” content feed 4. Publishes a box2-encrypted group/add-member message on his “invitations” feed
      This helps new members quickly see he is a member of the group, and also ensures he has a copy of the groupKey persisted in his records (encrypted to him and the group)

Diagram showing Staltz feed state from his perspective

2. Group creator invites someone

Staltz wants to invite his friend Arj to the group he set up, so he publishes a group/add-member message (which contains the groupKey) on his “invitations” feed.

When Arj next starts up his application and replicates Staltz’s feed tree (they are friends), he discovers the new group/add-member for him on Staltz’s “invitations” feed (because peers must replicate their friends’ “invitations” feeds).

Diagram showing feed state of Arj and Staltz from Arj’s perspective. The greyed out feeds show feeds that exist for Staltz but which Arj has yet to want to replicate.

Assuming he accepts this invitation, Arj then does the following: 1. Calculates the shard for the “helsinki” group for staltz, and starts replicating that shard feed and the “helsinki” feed 2. Creates a “helsinki” feed for himself

Diagram showing the updated state for Arj after he joins the group. Note the shards each feed lands in are different for each person (but deterministic if you know the groupKey).

Staltz can see that Arj has accepted the invitation because he is able to decrypt the feed announcement message for Arj’s “helsinki” feed on the shard feed, and read that the feedpurpose is the groupKey. Staltz knows which shard feed to watch for the announcement, because Arj’s shard feed is deterministically determined with information Staltz is aware of.

3. Non-group creator invites someone

Arj now wants to invite Mix to the “helsinki” group. He follows the same pattern as in (2), but now as the inviter.

Mix knows Arj is a part of the group because he was invited by them. Mix also knows staltz is part of the group because all group/add-member messages have

recps: [groupId, groupCreatorId, ...inviteeIds]

As long as we know the creator we can always re-follow the chain of group-additions.

:fire: TODO - we need to write the group spec up with these changes clearly somewhere.

Staltz see Arj has invited Mix because he’s replicating Arj’s “group-invite” feed, so Statlz starts replicating Mix.


  1. same problem as (1) exists when people join a group - they’re going to start all asking for the same pattern of feeds (e.g. “can I have staltz/v1/d4/helsinki and arj/v1/c/helsinki”). It’s like a fingerprint…

Existing work to be ported in: 1. 2022-06-02 meeting - has good steps for how discovery works 2. 2022-07-06 meeting