{"id":22485315,"url":"https://github.com/ssbc/ssb-db2","last_synced_at":"2025-08-02T18:32:02.135Z","repository":{"id":37401915,"uuid":"308135137","full_name":"ssbc/ssb-db2","owner":"ssbc","description":"A new database for secure-scuttlebutt","archived":false,"fork":false,"pushed_at":"2024-04-11T04:30:34.000Z","size":1082,"stargazers_count":46,"open_issues_count":25,"forks_count":7,"subscribers_count":5,"default_branch":"master","last_synced_at":"2024-05-22T17:22:33.022Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","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":"CHANGELOG.md","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":"2020-10-28T20:37:33.000Z","updated_at":"2024-05-08T18:10:13.000Z","dependencies_parsed_at":"2023-02-16T17:31:43.296Z","dependency_job_id":"f0b9616a-7f55-4bc0-81ad-d92da8f41529","html_url":"https://github.com/ssbc/ssb-db2","commit_stats":{"total_commits":843,"total_committers":8,"mean_commits":105.375,"dds":0.3428232502965599,"last_synced_commit":"83927c0d3de5f4d3623ad0e1c85c9285ae47f33b"},"previous_names":["ssb-ngi-pointer/ssb-db2"],"tags_count":133,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssbc%2Fssb-db2","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssbc%2Fssb-db2/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssbc%2Fssb-db2/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssbc%2Fssb-db2/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ssbc","download_url":"https://codeload.github.com/ssbc/ssb-db2/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":228189189,"owners_count":17882582,"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-12-06T17:12:22.682Z","updated_at":"2024-12-06T17:14:14.350Z","avatar_url":"https://github.com/ssbc.png","language":"JavaScript","funding_links":[],"categories":["JavaScript"],"sub_categories":[],"readme":"\u003c!--\nSPDX-FileCopyrightText: 2021 Anders Rune Jensen\n\nSPDX-License-Identifier: CC0-1.0\n--\u003e\n\n# SSB-DB2\n\nSSB-DB2 is a new database for secure-scuttlebutt, it is meant as a\nreplacement for [ssb-db]. The main reason for creating a new database\nis to be able to rework some of the existing decisions without having\nto be 100% backwards compatible. The main reasons are:\n\n- Performance, the database stores data in [bipf]\n- Replace flume with [jitdb] and specialized indexes\n- Run in the browser via [ssb-browser-core](https://github.com/arj03/ssb-browser-core)\n- Work well with partial replication\n\nOver time, this database received more features than ssb-db, and now supports:\n\n- Deletion and compaction\n- Customizable feed formats and encryption formats\n  - You are not tied to classic SSB messages and the classic mode of encryption,\n    you can use any format you want, or build one yourself, with [ssb-feed-format]\n    and [ssb-encryption-format]\n  - By default supports [ssb-classic]\n- Query language (as composable JS functions)\n\nSSB-DB2 is a secret-stack plugin that registers itself in the db\nnamespace.\n\nBy default SSB-DB2 only loads a base index (indexes/base), this index\nincludes the basic functionality for getting messages from the log and\nfor doing EBT.\n\nBy default the database is stored in `ssb/db2/log.bipf`, leveldb indexes\nare stored in `ssb/db2/indexes/`, and jitdb indexes in `ssb/db2/jit`.\n\n🎥 [Watch a presentation about this new database](https://www.youtube.com/watch?v=efzJheWQey8).\n\n[Read the developer guide](https://dev.scuttlebutt.nz/#/javascript/?id=ssb-db2)\n\n## Installation\n\n- Requires **Node.js 16** or higher\n- Requires `secret-stack@^6.2.0`\n\n```diff\n SecretStack({appKey: require('ssb-caps').shs})\n   .use(require('ssb-master'))\n+  .use(require('ssb-db2'))\n   .use(require('ssb-conn'))\n   .use(require('ssb-blobs'))\n   .call(null, config)\n```\n\n## Usage\n\nTo **create** and publish a new message, you can do:\n\n```js\nconst SecretStack = require('secret-stack')\nconst caps = require('ssb-caps')\n\nconst sbot = SecretStack({ caps })\n  .use(require('ssb-db2'))\n  .call(null, { path: './' })\n\nsbot.db.create({ content: { type: 'post', text: 'hello!' } }, (err, msg) =\u003e {\n  // A new message is now published on the log, with the contents above.\n  console.log(msg)\n  /*\n  {\n    key,\n    value: {\n      previous: null,\n      sequence: 1,\n      author,\n      timestamp: 1633715006539,\n      hash: 'sha256',\n      content: { type: 'post', text: 'hello!' },\n      signature,\n    },\n    timestamp: 1633715006540\n  }\n  */\n})\n```\n\nTo **get** the post messages of a specific author, you can do:\n\n```js\nconst SecretStack = require('secret-stack')\nconst caps = require('ssb-caps')\nconst { where, and, type, author, toCallback } = require('ssb-db2/operators')\n\nconst sbot = SecretStack({ caps })\n  .use(require('ssb-db2'))\n  .call(null, { path: './' })\n\nsbot.db.query(\n  where(\n    and(\n      type('post'),\n      author('@6CAxOI3f+LUOVrbAl0IemqiS7ATpQvr9Mdw9LC4+Uv0=.ed25519')\n    )\n  ),\n  toCallback((err, msgs) =\u003e {\n    console.log(\n      'There are ' + msgs.length + ' messages of type \"post\" from arj'\n    )\n    sbot.close()\n  })\n)\n```\n\nTo get post messages that mention Alice, you can do:\n\n```js\nconst SecretStack = require('secret-stack')\nconst caps = require('ssb-caps')\nconst {where, and, type, mentions, toCallback} = require('ssb-db2/operators')\n\nconst sbot = SecretStack({ caps })\n  .use(require('ssb-db2'))\n  .call(null, { path: './' })\n\nsbot.db.query(\n  where(and(type('post'), mentions(alice.id)))),\n  toCallback((err, msgs) =\u003e {\n    console.log('There are ' + msgs.length + ' messages')\n    sbot.close()\n  })\n)\n```\n\n### Leveldb plugins\n\nThe queries you've seen above use JITDB, but there are some queries\nthat cannot rely on JITDB alone, and we need to depend on\nLeveldb. This section shows some example leveldb indexes, explains\nwhen you need leveldb, and how to make your own leveldb plugin in\nssb-db2.\n\n#### Full-mentions\n\nAn extra index plugin that is commonly needed in SSB communities is\nthe **full-mentions** index. It has one method: getMessagesByMention.\n\nAlthough this accomplishes the same as the previous `mentions()`\nexample, this plugin is meant as an example for application developers\nto write their own plugins if the functionality of JITDB is not\nenough. JITDB is good for indexing specific values, like\n`mentions(alice.id)` which gets its own dedidated JITDB index for\n`alice.id`. But when querying mentions of several feeds or several\nmessages, this creates many indexes, so a specialized index makes more\nsense.\n\nWhat `full-mentions` does is index all possible mentionable items at\nonce, using Leveldb instead of JITDB. You can include it and use it\nlike this:\n\n```js\nconst SecretStack = require('secret-stack')\nconst caps = require('ssb-caps')\nconst {where, and, type, toCallback} = require('ssb-db2/operators')\n\nconst sbot = SecretStack({ caps })\n  .use(require('ssb-db2'))\n  .use(require('ssb-db2/full-mentions')) // include index\n  .call(null, { path: './' })\n\nconst {fullMentions} = sbot.db.operators\n\nsbot.db.query(\n  where(and(type('post'), fullMentions(alice.id)))),\n  toCallback((err, msgs) =\u003e {\n    console.log('There are ' + msgs.length + ' messages')\n    sbot.close()\n  })\n)\n```\n\n#### Your own leveldb index plugin\n\nIt's wise to use JITDB when:\n\n1. You want the query output to be the msg itself, not state derived\n   from msgs\n2. You want the query output ordered by timestamp (either descending\n   or ascending)\n\nThere are some cases where the assumptions above are not met. For\ninstance, with abouts, we often want to aggregate all `type: \"about\"`\nmsgs and return all recent values for each field (`name`, `image`,\n`description`, etc). So assumption number 1 does not apply.\n\nIn that case, you can make a leveldb index for ssb-db2, by creating a\nclass that extends the class at `require('ssb-db2/indexes/plugin')`,\nlike this:\n\n```js\nconst Plugin = require('ssb-db2/indexes/plugin')\n\n// This is a secret-stack plugin\nexports.init = function (sbot, config) {\n  class MyIndex extends Plugin {\n    constructor(log, dir, configDb2) {\n      //    log, dir, name, version, keyEncoding, valueEncoding, configDb2\n      super(log, dir, 'myindex', 1, 'utf8', 'json', configDb2)\n    }\n\n    processRecord(record, seq) {\n      const buf = record.value // this is a BIPF buffer, directly from the log\n      // ...\n      // Use BIPF seeking functions to decode some fields\n      // ...\n      this.batch.push({\n        type: 'put',\n        key: key, // some utf8 string here (see keyEncoding in the constructor)\n        value: value, // some object here (see valueEncoding in the constructor)\n      })\n    }\n\n    myOwnMethodToGetStuff(key, cb) {\n      this.level.get(key, cb)\n    }\n  }\n\n  sbot.db.registerIndex(MyIndex)\n}\n```\n\nThere are three parts you'll always need:\n\n- `constructor`: here you set configurations for the Leveldb index\n  - `log` and `dir` you probably don't need to fiddle with, but you\n    can use `this.log` methods if you know how to use\n    async-append-only-log\n  - `name` is a string that you'll use in `getIndex(name)`, it's also\n    used as a directory name\n  - `version`, upon changing, will cause a full rebuild of this index\n  - `keyEncoding` and `valueEncoding` must be strings from\n    [level-codec]\n  - `configDb2` is the db2 part of the config - `config.db2`\n- `processRecord`: here you handle a msg (in [bipf]) and potentially\n  write something to the index using\n  `this.batch.push(leveldbOperation)`\n- **custom method**: this is an API of your own choosing, that allows\n  you to read data from the index\n\nTo call your custom methods, you'll have to pick them like this:\n\n```js\nsbot.db.getIndex('myindex').myOwnMethodToGetStuff()\n```\n\nOr you can wrap that in a secret-stack plugin (in the example above,\n`exports.init` should return an object with the API functions).\n\nThere are other optional methods you can implement in the `Plugin` subclass:\n\n- `onLoaded(cb)`: a hook called once, at startup, when the index is successfully\n  loaded from disk and is ready to receive queries\n- `onFlush(cb)`: a hook called when the leveldb index is about to be saved to\n  disk\n- `indexesContent()`: method used when reindexing private group messages to\n  determine if the leveldb index needs to be updated for decrypted messages. The\n  default method returns true.\n- `reset()`: a method that you can use to reset in-memory state that you might\n  have in your plugin, when the leveldb index is about to be rebuilt.\n\n### Compatibility plugins\n\nSSB DB2 includes a couple of plugins for backwards compatibility,\nincluding legacy replication, ebt and publish. They can be loaded as:\n\n```js\nconst SecretStack = require('secret-stack')\n\nconst sbot = SecretStack({ caps })\n  .use(require('ssb-db2'))\n  .use(require('ssb-db2/compat')) // include all compatibility plugins\n  .call(null, {})\n```\n\nor specifically:\n\n```js\nconst SecretStack = require('secret-stack')\n\nconst sbot = SecretStack({ caps })\n  .use(require('ssb-db2'))\n  .use(require('ssb-db2/compat/db')) // basic db compatibility\n  .use(require('ssb-db2/compat/log-stream')) // legacy replication\n  .use(require('ssb-db2/compat/history-stream')) // legacy replication\n  .use(require('ssb-db2/compat/ebt')) // ebt db helpers\n  .use(require('ssb-db2/compat/publish')) // publish() function like in ssb-db\n  .use(require('ssb-db2/compat/post')) // post() obv like in ssb-db\n  .call(null, {})\n```\n\n## Secret-stack modules using ssb-db2\n\nThe following is a list of modules that works well with ssb-db2:\n\n- [ssb-threads] for working with post messages as threads\n- [ssb-suggest-lite] for fetching profiles of authors\n- [ssb-friends] for working with the social graph\n- [ssb-search2] for full-text searching\n- [ssb-crut] for working with records that can be modified\n\n## Migrating from ssb-db\n\nThe log used underneath ssb-db2 is different than that one in ssb-db,\nthis means we need to scan over the old log and copy all messages onto\nthe new log, if you wish to use ssb-db2 to make queries.\n\n**⚠️ Warning: please read the following instructions** about using two\nlogs and carefully apply them to avoid forking feeds into an\nirrecoverable state.\n\n### Preventing forking feeds\n\nThe log is the source of truth in SSB, and now with ssb-db2, we\nintroduce a new log alongside the previous one. **One of them, not\nboth** has to be considered the source of truth.\n\nWhile the old log exists, it will be continously migrated to the new\nlog, and ssb-db2 forbids you to use its database-writing APIs such as\n`add()`, `publish()`, `del()` and so forth, to prevent the two logs\nfrom diverging into inconsistent states. The old log will remain the\nsource of truth and the new log will just mirror it.\n\nIf you want to switch the source of truth to be the new log, we must\ndelete the old log, after it has been fully migrated. Only then can\nyou use database-writing APIs such as `publish()`. To delete the old\nlog, one method is to use the [config\n`dangerouslyKillFlumeWhenMigrated`](#configuration). Set it to `true`\nonly when you are **absolutely sure** that no other app will attempt\nto read/write to `~/.ssb/flume/log.offset` or wherever the old log\nlives. It will delete the entire flume folder once migration has\ncompleted writing the messages to the new log. From that point\nonwards, using APIs such as `publish()` will succeed to append\nmessages to the new log.\n\n### Triggering migration\n\nssb-db2 comes with migration methods built-in, you can enable them\n(they are off by default!) in your config file (or object):\n\n```js\nconst path = require('path')\nconst SecretStack = require('secret-stack')\nconst ssbKeys = require('ssb-keys')\nconst keys = ssbKeys.loadOrCreateSync(path.join(__dirname, 'secret'))\n\nconst config = {\n  keys: keys,\n  db2: {\n    automigrate: true,\n  },\n}\n\nconst sbot = SecretStack({ caps })\n  .use(require('ssb-db2'))\n  .use(require('ssb-db2/compat'))\n  .call(null, config)\n```\n\nThe above script will initiate migration as soon as the plugins are\nloaded. If you wish the manually dictate when the migration starts,\ndon't use the `automigrate` config above, instead, call the\n`migrate.start()` method yourself:\n\n```js\nsbot.db.migrate.start()\n```\n\nNote, it is acceptable to load both ssb-db and ssb-db2 plugins, the\nsystem will still function correctly and migrate correctly:\n\n```js\nconst sbot = SecretStack({ caps })\n  .use(require('ssb-db'))\n  .use(require('ssb-db2'))\n  .use(require('ssb-db2/compat'))\n  .call(null, config)\n```\n\n### Migrating without including ssb-db2\n\nBecause ssb-db2 also begins indexing basic metadata once it's included\nas a plugin, this may cost more (precious) CPU time. **If you are not\nyet using db2 APIs** but would like to migrate the log anyway, in\npreparation for later activating db2, then you can include only the\nmigration plugin, like this:\n\n```js\nconst sbot = SecretStack({ appKey: caps.shs })\n  .use(require('ssb-db2/migrate'))\n  .call(null, config)\n```\n\nNote that the `start` behavior is the same: you can either start it\nautomatically using `config.db2.automigrate` or manually like this:\n\n```js\nsbot.db2migrate.start()\n```\n\n## Methods\n\n### create(opts, cb)\n\nMethod for creating and publishing a new message on the log. The `opts` allows\nyou to fully customize how this message will be written, including which feed\nformat is used, which keys are signing/authoring the message, what encryption\nformat is used and so forth.\n\nThe `opts` must be an object and should contain the following keys:\n\n- `content` **required**: the message content, MUST be an object and SHOULD\n  include `type` as a string property.\n- `feedFormat` _optional_ (defaults to `'classic'`): a string that specifies\n  the feed format to use.\n- `keys` _optional_ (defaults to `config.keys`): the keys to use for signing\n  and authoring the message. `keys.id` must be valid for the `feedFormat` you\n  selected.\n- `encryptionFormat` _optional_, if you want to publish an encrypted message,\n  it is recommended you set this field to a string that specifies which\n  encryption format to use. Typically this is the string `'box'`.\n- `recps`: _optional_, an array of feed IDs (strings) that will be used to\n  encrypt the message. **The message is only encrypted if `opts.recps` (or\n  `opts.content.recps`) exists**.\n- `encoding`: _optional_ (defaults to `'js'`): a string that specifies the\n  encoding to use when serializing the message to be written to the database.\n  Supported values are `'js'` and `'bipf'`. Note that all messages in the\n  database end up as `bipf` buffers even if you choose `'js'` encoding, so\n  setting `encoding` to `'bipf'` is only a matter of improving serialize/persist\n  performance **if** your selected feed format supports encoding to bipf\n  directly.\n- Depending on the feed format chosen, you may have to provide additional opts.\n  See the docs for the specific feed format you are using.\n\nThe callback `cb` is called when the message has been published. `cb(err)` if\npublished failed with an error `err`, and `cb(null, kvt)` if it was\nsuccessfully published, where `kvt` is a JavaScript object with the shape\n`{key, value, timestamp}` exactly representing the message written to the\ndatabase.\n\n### get(msgId, cb)\n\nGet a particular message value by message id.\n\n### getMsg(msgId, cb)\n\nGet a particular message including key by message id.\n\n### query(...operators)\n\nFlexible API to get messages from the database based on various criteria\ndetermined by the `operators`. There are usually two parts in this list of\noperators:\n\n- The `where()` part, determining the criteria to match messages against.\n  - Example: `where(and(type('post'), author(ssb.id)))`\n- The `to____()` part, determining how you want the messages delivered, e.g.\n  - `toCallback((err, msgs) =\u003e { })`\n  - `toPromise()`\n  - `toPullStream()` **RECOMMENDED** with a `pull.drain` because it uses less memory\n  - `toAsyncIter()`\n\nSee [jitdb operators] and [operators/index.js] for a complete list of supported\noperators.\n\n### reindexed()\n\nA pull-stream source of newly decrypted reindexed values returned as\nfull messages. Calling `reindexEncrypted` after adding a new box2 key\nwill trigger new values in this stream.\n\nThis api can be combined with `where` to receive messages of a\nparticular type no matter if they are existing, newly added or\ndecrypted values.\n\nExample of getting existing post messages together with newly\ndecrypted ones:\n\n```js\nconst pull = require('pull-stream')\nconst cat = require('pull-cat')\n\npull(\n  cat([\n    sbot2.db.query(where(type('post')), toPullStream()),\n    pull(\n      sbot2.db.reindexed(),\n      pull.filter((msg) =\u003e {\n        return msg.value.content.type === 'post'\n      })\n    ),\n  ]),\n  pull.drain((result) =\u003e {\n    console.log('got a new post', result.value)\n  })\n)\n```\n\n### add(nativeMsg, cb)\n\nValidate and add a message to the database. The callback will the (possible)\nerror in the 1st argument, and in the 2nd argument the stored message in \"KVT\"\nshape, i.e. `{key, value, timestamp}`. The `nativeMsg` is assumed to belong to\na feed format that is currently installed, such as [ssb-classic].\n\n**Alternatively:** `add(nativeMsg, opts, cb)` where `opts.encoding` and\n`opts.feedFormat` can be specified. (Read above, in the `create` method, for\ndetails about these opts)\n\n### addOOO(nativeMsg, cb)\n\nValidate and a message to the database, but validation will not check the\n`previous`. Useful for partial replication.\n\nCallback arguments are `(err, kvt)`, similar to the callback in the `add`\nmethod.\n\n**Alternatively:** `addOOO(nativeMsg, opts, cb)` where `opts.encoding` and\n`opts.feedFormat` can be specified. (Read above, in the `create` method, for\ndetails about these opts)\n\n### addOOOBatch(nativeMsgs, cb)\n\nSimilar to `addOOO`, but you can pass an array of many messages. If the `author`\nis not yet known, the message is validated without checking if the `previous`\nlink is correct, otherwise normal validation is performed. This makes it\npossible to use for partial replication to add all contact messages from a feed.\n\nCallback arguments are `(err, kvts)`, where `kvts` is an array of \"KVT\" matching\neach of the given `nativeMsgs`.\n\n**Alternatively:** `addOOOBatch(nativeMsgs, opts, cb)` where `opts.encoding` and\n`opts.feedFormat` can be specified. (Read above, in the `create` method, for\ndetails about these opts)\n\n### addTransaction(nativeMsgs, oooNativeMsgs, cb)\n\nSimilar to `addOOOBatch`, except you pass in an array of `nativeMsgs` that will\nbe validated in order and an array of `oooNativeMsgs` that will be validated\nsimilar to `addOOOBatch`. Finally all the messages are added to the database in\nsuch a way that either all of them are written to disc or none of them are.\n\nCallback arguments are `(err, kvts)`, similar to the callback in the\n`addOOOBatch` method.\n\n**Alternatively:** `addTransaction(nativeMsgs, oooNativeMsgs, opts, cb)` where\n`opts.encoding` and `opts.feedFormat` can be specified. (Read above, in the\n`create` method, for details about these opts)\n\n### del(msgId, cb)\n\nDelete a specific message given the message ID `msgId` from the database.\n:warning: Please note that this will break replication for anything trying to\nget that message, like `createHistoryStream` for the author or EBT. Because of\nthis, it is not recommended to delete message with this method unless you know\nexactly what you are doing.\n\n### deleteFeed(feedId, cb)\n\nDelete all messages of a specific feedId. Compared to `del` this method is safe\nto use.\n\n### onMsgAdded(cb)\n\nSubscribe to know when a message is added to the database. The `cb` will be\ncalled as soon as a message is successfully persisted to the log, with one\nargument, `event`, which contains:\n\n- `event.feedFormat`: the feed format used to persist the message.\n- `event.nativeMsg`: the message that was just added to the database, in the\n  \"native\" shape determined by its feed format. This could be e.g. a buffer.\n- `event.kvt`: the message that was just added to the database, in the\n  `{key, value, timestamp}` shape, as a JavaScript object.\n\n`onMsgAdded` itself is an [obz], so the latest message added to the database can\nalso be read using `ssb.db.onMsgAdded.value`.\n\n### getStatus\n\nGets the current db status, either as a synchronous function or\nobserveable. This is similar to\n[db.status](https://github.com/ssbc/ssb-db#dbstatus) in ssb-db. The\nvalue contains the following information:\n\n- `log`: current byte offset of the head of the log in bytes (the log\n  is the primary source of truth where raw messages are stored)\n- `jit`: an object with the offset of each [jitdb] index\n- `indexes`: an object with the offset of each level index\n- `progress`: a float representing the index completion from 0 to 1\n\nExample:\n\n```js\n// synchronous\nconst status = ssb.db2.getStatus()\n\n// observeable\nconst listener = (status) =\u003e console.log(status.progress)\nconst unsubscribe = ssb.db2.getStatus(listener)\n\n// later\nunsubscribe() // unsubscribes the listener from status updates\n```\n\n### reindexEncrypted(cb)\n\nThis function is useful in [ssb-box2] where box2 keys can be added\nat runtime and that changes what messages can be decrypted. Calling\nthis function is needed after adding a new key. The function can be\ncalled multiple times safely.\n\nThe decrypted values from this can be consumed using `reindexed`.\n\n### logStats(cb)\n\nUse [async-append-only-log]'s `stats` method to get information on how many\nbytes are used by messages in the log, and how many bytes are zero-filled.\n\n### prepare(operation, cb)\n\nUse [JITDB's prepare](https://github.com/ssb-ngi-pointer/jitdb/#prepareoperation-cb) method to warm up a JIT index.\n\n### onDrain(indexName?, cb)\n\nWaits for the index with name `indexName` to be in sync with the main\nlog and then call `cb` with no arguments. If `indexName` is not\nprovided, the base index will be used.\n\nThe reason we do it this way is that indexes are updated\nasynchronously in order to not block message writing.\n\n### compact(cb)\n\nCompacts the log (filling in the blanks left by deleted messages and optimizing\nspace) and then rebuilds indexes.\n\n### installFeedFormat(feedFormat)\n\nIf `feedFormat` conforms to the [ssb-feed-format] spec, then this method will\ninstall the `feedFormat` in this database instance, meaning that you can create\nmessages for that feed format using the `create` method.\n\n### installEncryptionFormat(encryptionFormat)\n\nIf `encryptionFormat` conforms to the [ssb-encryption-format] spec, then this\nmethod will install the `encryptionFormat` in this database instance, meaning\nthat you can now encrypt and decrypt messages using that encryption format.\n\n### reset(cb)\n\nForce all indexese to be rebuilt. Use this as a last resort if you suspect that\nthe indexes are corrupted.\n\n## Configuration\n\nYou can use ssb-config parameters to configure some aspects of ssb-db2:\n\n```js\nconst config = {\n  keys: keys,\n  db2: {\n    /**\n     * Start the migration plugin automatically as soon as possible.\n     * Default: false\n     */\n    automigrate: true,\n\n    /**\n     * If the migration plugin is used, then when migration has completed, we\n     * will remove the entire `~/.ssb/flume` directory, including the log.\n     *\n     * As the name indicates, this is dangerous, because if there are other apps\n     * that still use `~/.ssb/flume`, they will see an empty log and progress to\n     * write on that empty log using the `~/.ssb/secret` and this will very\n     * likely fork the feed in comparison to new posts on the new log. Only use\n     * this when you know the risks and you know that only the new log will be\n     * written.\n     * Default: false\n     */\n    dangerouslyKillFlumeWhenMigrated: false,\n\n    /**\n     * If the environment that db2 runs in has access to a regular disk to write to. Normally detected automatically but in environments like electron you might have to set this manually.\n     * Default in node: true, default in a browser: false\n     */\n    hasDiskAccess: true\n\n    /**\n     * Only try to decrypt box1 messages created after this date\n     * Default: null\n     */\n    startDecryptBox1: '2022-03-25',\n\n    /**\n     * A throttle interval (measured in milliseconds) to control how often\n     * should messages given to `sbot.add` be flushed in batches.\n     * Default: 250\n     */\n    addBatchThrottle: 250,\n\n    /**\n     * An upper limit on the CPU load that ssb-db2 can use while indexing\n     * and scanning. `85` means \"ssb-db2 will only index when CPU load is at\n     * 85% or lower\".\n     * Default: Infinity\n     */\n    maxCpu: 85,\n\n    /** This applies only if `maxCpu` is defined.\n     * See `maxPause` in the module `too-hot`, for its definition.\n     * Default: 300\n     */\n    maxCpuMaxPause: 180,\n\n    /** This applies only if `maxCpu` is defined.\n     * See `wait` in the module `too-hot`, for its definition.\n     * Default: 90\n     */\n    maxCpuWait: 90,\n\n\n    flushDebounce: 250,\n\n    writeTimeout: 250,\n  },\n}\n```\n\n## Operators\n\nThe following operators are included by default, see\n[operators/index.js] for how they are implemented. Also exposed are\nall [jitdb operators]\n\n- type\n- author\n- channel\n- key\n- votesFor\n- contact\n- mentions\n- about\n- hasRoot\n- hasFork\n- hasBranch\n- authorIsBendyButtV1\n- isRoot\n- isPublic\n- isEncrypted\n- isDecrypted\n\n[ssb-db]: https://github.com/ssbc/ssb-db/\n[async-append-only-log]: https://github.com/ssbc/async-append-only-log/\n[bipf]: https://github.com/ssbc/bipf/\n[jitdb]: https://github.com/ssb-ngi-pointer/jitdb/\n[bendy butt]: https://github.com/ssb-ngi-pointer/ssb-bendy-butt\n[obz]: https://github.com/ssbc/obz/\n[ssb-social-index]: https://github.com/ssbc/ssb-social-index\n[ssb-box2]: https://github.com/ssb-ngi-pointer/ssb-box2\n[level-codec]: https://github.com/Level/codec#builtin-encodings\n[ssb-threads]: https://github.com/ssbc/ssb-threads\n[ssb-suggest-lite]: https://github.com/ssb-ngi-pointer/ssb-suggest-lite\n[ssb-friends]: https://github.com/ssbc/ssb-friends\n[ssb-classic]: https://github.com/ssbc/ssb-classic\n[ssb-feed-format]: https://github.com/ssbc/ssb-feed-format\n[ssb-encryption-format]: https://github.com/ssbc/ssb-encryption-format\n[ssb-search2]: https://github.com/staltz/ssb-search2\n[ssb-crut]: https://gitlab.com/ahau/lib/ssb-crut\n[operators/index.js]: https://github.com/ssb-ngi-pointer/ssb-db2/blob/master/operators/index.js\n[jitdb operators]: https://github.com/ssb-ngi-pointer/jitdb#operators\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fssbc%2Fssb-db2","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fssbc%2Fssb-db2","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fssbc%2Fssb-db2/lists"}