{"id":16541,"library":"ssb-db","title":"ssb-db","description":"ssb-db is a core component within the Secure Scuttlebutt (SSB) ecosystem, providing a secure, replicatable, append-only database for cryptographic message feeds. It is built as a plugin for secret-stack applications, enabling peer-to-peer data synchronization and communication without a central authority. The package ensures message unforgeability through digital signing tied to unique public/private key pairs, forming immutable \"feeds.\" Currently at version 20.4.1, ssb-db has a moderate release cadence, with several minor versions released in recent history (v20.x in 2024), indicating active maintenance and feature development. Key differentiators include its foundational role in the decentralized SSB protocol, strict append-only data model (though flexible through delta encoding for \"deletions\"), and built-in support for message encryption and indexing. It's designed for applications requiring resilient, offline-first, and censorship-resistant data storage.","status":"active","version":"20.4.1","language":"javascript","source_language":"en","source_url":"git://github.com/ssbc/ssb-db","tags":["javascript"],"install":[{"cmd":"npm install ssb-db","lang":"bash","label":"npm"},{"cmd":"yarn add ssb-db","lang":"bash","label":"yarn"},{"cmd":"pnpm add ssb-db","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"ssb-db functions as a plugin for secret-stack, which is the extensible framework used in SSB applications.","package":"secret-stack","optional":false},{"reason":"Used for loading configuration for the ssb-db instance, including file paths and network settings.","package":"ssb-config","optional":false},{"reason":"The API exposes pull-stream methods for data manipulation and streaming, making it a common peer dependency for consuming its streams.","package":"pull-stream","optional":false}],"imports":[{"note":"`secret-stack` and `ssb-db` are typically used in a CommonJS Node.js environment. ESM `import` is not directly supported for these packages.","wrong":"import { createApp } from 'secret-stack'","symbol":"createApp","correct":"const createApp = require('secret-stack')"},{"note":"`ssb-db` is instantiated by `secret-stack` when registered via `.use()`. Direct `require()` is for the *plugin object*, not the running instance.","wrong":"import ssbdb from 'ssb-db'","symbol":"ssb-db (plugin object)","correct":".use(require('ssb-db'))"},{"note":"`pull-stream` is a widely used streaming library in the SSB ecosystem. Ensure correct `require` syntax for older Node.js contexts.","wrong":"import pull from 'pull-stream'","symbol":"pull","correct":"var pull = require('pull-stream')"}],"quickstart":{"code":"/**\n * create an ssb-db instance and add a message to it.\n */\nvar pull = require('pull-stream')\n\n//create a secret-stack instance and add ssb-db, for persistence.\nvar createApp = require('secret-stack')({})\n  .use(require('ssb-db'))\n\n\n// create the db instance.\n// Only one instance may be created at a time due to os locks on port and database files.\n\nvar app = createApp(require('ssb-config'))\n\n//your public key, the default key of this instance.\nconsole.log(\"App ID:\", app.id)\n\n//or, called remotely\napp.whoami(function (err, data) {\n  if (err) return console.error(err)\n  console.log(\"Whoami ID:\", data.id) //your id\n})\n\n// publish a message to default identity\n//  - feed.add appends a message to your key's chain.\n//  - the `type` attribute is required.\n\napp.publish({ type: 'post', text: 'My First Post!' }, function (err, msg) {\n  if (err) return console.error(err)\n  // the message as it appears in the database:\n  console.log(\"Published message:\", msg)\n\n  // and its hash:\n  console.log(\"Message key:\", msg.key)\n\n  // collect all the messages into an array, calls back, and then ends\n  // https://github.com/pull-stream/pull-stream/blob/master/docs/sinks/collect.md\n  pull(\n    app.createLogStream(),\n    pull.collect(function (err, messagesArray) {\n      if (err) return console.error(err)\n      console.log(\"All messages in log:\", messagesArray)\n    })\n  )\n\n  // collect all messages for a particular keypair into an array, calls back, and then ends\n  // https://github.com/pull-stream/pull-stream/blob/master/docs/sinks/collect.md\n  pull(\n    app.createHistoryStream({id: app.id}),\n    pull.collect(function (err, messagesArray) {\n      if (err) return console.error(err)\n      console.log(\"Messages for app.id:\", messagesArray)\n      app.close() // Close the app instance to release locks\n    })\n  )\n})","lang":"javascript","description":"This example demonstrates initializing an `ssb-db` instance using `secret-stack`, publishing a new message to the current identity's feed, and then streaming all messages from the log and specifically from the current identity's history. It showcases basic message creation and retrieval patterns."},"warnings":[{"fix":"Update custom `addBoxer` implementations to accept and utilize the `previous` messageId parameter.","message":"The `addBoxer` API changed in v20.3.0 to provide the `previous` messageId, which is required for modules like `ssb-tribes`. Existing implementations of `addBoxer` will need updates to function correctly.","severity":"breaking","affected_versions":">=20.3.0"},{"fix":"Review and update custom `addBoxer` implementations to include an `init` method if necessary and account for initialization delays, ensuring they complete before dependent db operations.","message":"In v20.2.0, the `addBoxer` method was modified to accept an `init` method, and `ssb-db` methods now wait for boxer initialization. This impacts custom boxer implementations and their lifecycle.","severity":"breaking","affected_versions":">=20.2.0"},{"fix":"After adding a new unboxer (e.g., for a new private group format), call the `rebuild` function (exposed since v20.1.0) on your `ssb-db` instance to ensure all flume indexes are consistent.","message":"With v20.0.1, `addBoxer` / `addUnboxer` methods were exposed with an initialization step, and `ssb-private1` was extracted as a standalone module. When including a *new* unboxer, you *must* rebuild your indexes to correctly process and expose unboxed content, which can be a time-consuming operation.","severity":"breaking","affected_versions":">=20.0.1"},{"fix":"Review how unboxed messages are handled if using v20.0.0 and update to a later version to ensure consistent behavior, or modify downstream plugins accordingly to avoid conflicts.","message":"Version 20.0.0 deprecated behavior by exposing unboxed messages on certain methods that other plugins were also exposing. This could lead to conflicts or unexpected data exposure if multiple plugins tried to expose or modify the same message format.","severity":"deprecated","affected_versions":"=20.0.0"},{"fix":"Ensure only one `secret-stack` application using `ssb-db` is running at any given time, or configure different database directories and network ports for multiple instances if necessary.","message":"`ssb-db` instances acquire OS-level locks on port and database files. Therefore, only *one instance* of `ssb-db` can be created and run at a time on a given system/directory without conflicts.","severity":"gotcha","affected_versions":">=1.0.0"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"Ensure all `ssb-db` instances are properly closed (`app.close()` in `secret-stack`) before attempting to start a new one, or configure each instance to use a unique database directory and port.","cause":"Attempting to run multiple `ssb-db` instances simultaneously using the same database directory or network port, or failing to properly close a previous instance, resulting in resource contention.","error":"Error: EADDRINUSE: address already in use :::[PORT_NUMBER] or file lock errors (e.g., EBUSY)"},{"fix":"Always include a `type` property in the message object passed to `app.publish()`, for example: `{ type: 'post', text: 'My message' }`.","cause":"Publishing a message to `app.publish()` without including the mandatory `type` attribute in the message object.","error":"Error: message.type is required (or similar validation error in `app.publish` callback)"},{"fix":"After registering a new unboxer, explicitly call `app.rebuild()` (exposed since v20.1.0) on your `ssb-db` instance to ensure all flume indexes are reprocessed with the new unboxer.","cause":"New unboxers (e.g., for private group formats) are added to an `ssb-db` instance, but the existing flume indexes, which process encrypted messages, are not rebuilt, leading to unboxed content not appearing in views or queries.","error":"Unboxed private messages not appearing or incorrect data in indexes after adding new unboxers"}],"ecosystem":"npm"}