{"id":13629950,"url":"https://github.com/ssbc/ssb-tribes2","last_synced_at":"2025-04-17T13:30:49.531Z","repository":{"id":59506759,"uuid":"536989376","full_name":"ssbc/ssb-tribes2","owner":"ssbc","description":"SSB private groups with ssb-db2","archived":false,"fork":false,"pushed_at":"2024-01-25T21:07:38.000Z","size":552,"stargazers_count":8,"open_issues_count":16,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-10T22:29:51.802Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/ssbc.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSES/CC0-1.0.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2022-09-15T11:12:09.000Z","updated_at":"2024-01-12T18:37:58.000Z","dependencies_parsed_at":"2024-01-14T08:18:11.053Z","dependency_job_id":"d4ea87e1-f383-4018-9594-8316f05279fa","html_url":"https://github.com/ssbc/ssb-tribes2","commit_stats":{"total_commits":503,"total_committers":5,"mean_commits":100.6,"dds":0.1908548707753479,"last_synced_commit":"f38e1ac8a4570337746e862f35dc373999dff292"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssbc%2Fssb-tribes2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssbc%2Fssb-tribes2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssbc%2Fssb-tribes2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssbc%2Fssb-tribes2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ssbc","download_url":"https://codeload.github.com/ssbc/ssb-tribes2/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":249031928,"owners_count":21201371,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-08-01T22:01:25.191Z","updated_at":"2025-04-17T13:30:49.513Z","avatar_url":"https://github.com/ssbc.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"\u003c!--\nSPDX-FileCopyrightText: 2022 Andre 'Staltz' Medeiros \u003ccontact@staltz.com\u003e\n\nSPDX-License-Identifier: CC0-1.0\n--\u003e\n\n# ssb-tribes2\n\nA [secret-stack] plugin that makes it easy to create, manage, and publish\nmessages in SSB \"Private Groups\", following\n[this spec](https://github.com/ssbc/ssb-meta-feeds-group-spec). This module is\nmade to work with [ssb-db2] as the database and with [metafeeds], where your\ncontent in the group is placed on a dedicated feed in your metafeed tree.\nReplication of those group-specific feeds at scale is handled by [ssb-replication-scheduler].\n\nSuccessor of [ssb-tribes].\n\n## Installation\n\n```bash\nnpm install ssb-tribes2\n```\n\n## Usage in ssb-db2\n\n- Requires **Node.js 12** or higher\n- Requires `secret-stack\u003e=6.2.0`\n- Requires `ssb-db2\u003e=6.2.2`\n- Requires `ssb-box2\u003e=4.0.0`\n- Requires `ssb-meta-feeds\u003e=0.38.0`\n- Requires `ssb-bendy-butt\u003e=1.0.0`\n\n```diff\n const ssb = SecretStack({ caps: require('ssb-caps') })\n   .use(require('ssb-master'))\n+  .use(require('ssb-db2'))\n   .use(require('ssb-conn'))\n+  .use(require('ssb-bendy-butt'))\n+  .use(require('ssb-meta-feeds'))\n+  .use(require('ssb-tribes2'))\n   .use(require('ssb-blobs'))\n   .call(null, config)\n```\n\nThen **to create a group** and **publish to it**,\n\n```js\n// This is needed to automatically create an additions feed, needed to be able to send and receive invites\nssb.tribes2.start()\n\n// Create a new group, no further details required, thus the empty object\nssb.tribes2.create({}, (err, group) =\u003e {\n  // Publish a new message to the group, notice the recps\n  ssb.tribes2.publish(\n    {\n      type: 'post',\n      text: 'welcome to the group',\n      recps: [group.id],\n    },\n    cb\n  )\n})\n```\n\nIf you want to **add more members** to the group:\n\n```js\n// You need to know your friends' (bob and carol) *root* metafeed IDs\nssb.tribes2.addMembers(group.id, [bobRootId, carolRootId], {}, (err, msg) =\u003e {\n  // msg is the message that was published on your invitations feed\n})\n```\n\nThen you **list the current members** of the group:\n\n```js\npull(\n  ssb.tribes2.listMembers(group.id),\n  pull.collect((err, members) =\u003e {\n    // `members` is an Array of root metafeed IDs\n  })\n)\n```\n\nFinally, you can **list all the groups you are a member of**:\n\n```js\npull(\n  ssb.tribes2.list(),\n  pull.collect((err, groups) =\u003e {\n    // `groups` is an Array of group objects like { id, secret }\n  })\n)\n```\n\n## API\n\nAll methods with callbacks return a promise instead if a callback isn't provided.\n\n### `ssb.tribes2.create(opts, cb)`\n\nCreates a new private group.\nThis creates an encryption key, sets up a sub-feed for the group, and initializes the\ngroup with a `group/init` message, and `group/add-member` to signal you were added.\nCalls back with important info about the group.\nNOTE: If `create` finds an empty (i.e. seemingly unused) group feed, it will start using that feed instead of creating a new one.\n\n- `opts` _Object_ - currently empty, but will be used in the future to specify details like whether the group has an admin subgroup, etc.\n- `cb` _Function_ - callback function of signature `(err, group)` where `group` is an object containing:\n\n  - `id` _GroupUri_ - an SSB URI that's safe to use publicly to name the group, and is used in `recps` to trigger encrypting messages to that group\n  - `subfeed` _Keys_ - the keys of the subfeed you should publish group data to\n  - `writeKey` _GroupSecret_ - the current key used for publishing new messages to the group. It is one of the `readKeys`.\n  - `readKeys` _[GroupSecret]_ - an array of all keys used to read messages for this group.\n  - `root` _MessagedId_ - the MessageId of the `group/init` message of the group, encoded as an ssb-uri.\n\n  where _GroupSecret_ is an object of the format\n\n  - `key` _Buffer_ - the symmetric key used by the group for encryption\n  - `scheme` _String_ - the scheme for this key\n\n### `ssb.tribes2.get(groupId, cb)`\n\nGets information about a specific group.\n\n- `groupId` _GroupUri_ - the public-safe SSB URI which identifies the group\n- `cb` _Function_ - callback function of signature `(err, group)` where `group` is an object on the same format as the `group` object returned by #create\n\n### `ssb.tribes2.list({ live, excluded }) =\u003e source`\n\nCreates a pull-stream source which emits `group` data of each private group you're a part of. If `live` is true then it also outputs all new groups you join. If `excluded` is true then it only outputs groups that you've been excluded from, instead of just ones you haven't.\n(Same format as `group` object returned by #create)\n\n### `ssb.tribes2.addMembers(groupId, feedIds, opts, cb)`\n\nPublish `group/add-member` messages to a group of peers, which gives them all the details they need to join the group. Newly added members will need to accept the invite using `acceptInvite()` before they start replicating the group.\n\n- `groupId` _GroupUri_ - the public-safe SSB URI which identifies the group (same as in #create)\n- `feedIds` _[FeedId]_ - an Array of 1-15 different ids for peers (accepts ssb-uri or sigil feed ids)\n- `opts` _Object_ - with the options:\n  - `text` _String_ - A piece of text attached to the addition. Visible to the whole group and the newly added people.\n- `cb` _Function_ - a callback of signature `(err, Array\u003cmsg\u003e)`\n\n### `ssb.tribes2.excludeMembers(groupId, feedIds, opts, cb)\n\nExcludes some current members of the group, by creating a new key and group feed and reinviting everyone to that key except for the excluded members.\n\n- `groupId` _GroupUri_ - the public-safe SSB URI which identifies the group (same as in #create)\n- `feedIds` _[FeedId]_ - an Array of 1-15 different ids for peers (accepts ssb-uri or sigil feed ids)\n- `opts` _Object_ - placeholder for future options.\n- `cb` _Function_ - a callback of signature `(err)`\n\n### `ssb.tribes2.publish(content, opts, cb)`\n\nPublishes any kind of message encrypted to the group. The function wraps `ssb.db.create()` but handles adding tangles and using the correct encryption for the `content.recps` that you've provided. Mutates `content`.\n\n- `opts` _Object_ - with the options:\n  - `isValid` _Function_ - a validator (typically `is-my-ssb-valid`/`is-my-json-valid`-based) that you want to check this message against before publishing. Have the function return false if the message is invalid and the message won't be published. By default uses the `content` validator from `private-group-spec`.\n  - `tangles` _[String]_ - by default `publish` always adds the `group` tangle to messages, but using this option you can ask it to add additional tangles. Currently only supports a few tangles that are core to groups.\n  - `feedKeys` _Keys_ - By default the message is published to the currently used group feed (current epoch) but using this option you can provide keys for another feed to publish on. Note that this doesn't affect the encryption used.\n- `cb` _Function_ - a callback of signature `(err, msg)`\n\n### `ssb.tribes2.listMembers(groupId, { live, allAdded }) =\u003e source`\n\nReturns a pull stream source listing the root feed id of every member of the\ngroup with id `groupId`. Note: lists members whether or not they've accepted the\ninvite.\n\nIf `live` is true, then it keeps the stream open and also outputs updates to\nmembership as new members are added / excluded.\n\nIf `allAdded` is true then excludes are ignored and any peer that has ever been a member of the group is listed.\n\nEach update emitted from the source is the updated complete state of the group in the format:\n\n```js\n{\n  added: [feedId, feedId, ...],\n  toExclude: [feedId, ...]\n}\n```\n\n### `ssb.tribes2.listInvites() =\u003e source`\n\nReturns a pull stream source listing invites (another user sent you one with `addMembers`) that you haven't accepted yet. The invites are on the same format as that of #create.\n\n### `ssb.tribes2.acceptInvite(groupId, cb)`\n\nAccepts an invite (addition) for a group, if you've received one, and starts to replicate and decrypt it. Does not publish any message.\n\n### `ssb.tribes2.start(cb)`\n\nMakes sure that you're set up to send and receive group invites, by creating an additions feed for you.\n\n- `cb` _Function_ - a callback of signature `(err)`\n\n## Config\n\nYou can set the secret stack config `config.tribes2.timeoutLow` and `config.tribes2.timeoutHigh` to control how slowly the client should try to fix a conflicting state, where other clients might be trying to fix the same conflict at the same time. The defaults are `5` and `30` respectively, which gives a random timeout between 5s-30s. A higher value reduces the risk of creating new conflicts since other clients don't do the same conflict resolution at the same time, but increase the time that the group is in an unstable state. A lower number corrects things faster but increases the risk of ending up in new conflicts. Should not be `0` or close to it.\n\nYou need to set `config.tribes2.recoverExclude` to true (default false) for the above mentioned conflict recovery to happen at all. The recovery is a bit unreliable but might sometimes be needed to repair broken state.\n\n## Security considerations\n\nWhile we have tried our best to create a secure end-to-end encrypted communication protocol, this module is not fit for use in safety critical situations. Neither the code nor the specification has been vetted by an independent party. Even assuming a solid implementation, and a bug-free spec, we have intentionally left out several security features that are considered state of the art in other apps such as Signal, such as \"forward secrecy\".\n\nBecause of this, we advise that anyone that uses this module in an app, includes prominent UI that warns the user about possible risks.\n\n## Links\n\n- [ssb-tribes2-demo](https://github.com/ssbc/ssb-tribes2-demo) - A demo electron app that shows off the features of `ssb-tribes2`\n- [private-group-spec](https://github.com/ssbc/private-group-spec) - The spec that `ssb-tribes2` is based on\n- [ssb-group-exclusion-spec](https://github.com/ssbc/ssb-group-exclusion-spec) - The spec that defines exclusion from a private group\n\n## License\n\nLGPL-3.0-only\n\n[secret-stack]: https://github.com/ssbc/secret-stack\n[ssb-db2]: https://github.com/ssbc/ssb-db2\n[ssb-tribes]: https://github.com/ssbc/ssb-tribes\n[metafeeds]: https://github.com/ssbc/ssb-meta-feeds\n[ssb-replication-scheduler]: https://github.com/ssbc/ssb-replication-scheduler\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fssbc%2Fssb-tribes2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fssbc%2Fssb-tribes2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fssbc%2Fssb-tribes2/lists"}