ssb-db
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.
Common errors
-
Error: EADDRINUSE: address already in use :::[PORT_NUMBER] or file lock errors (e.g., EBUSY)
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.fixEnsure 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. -
Error: message.type is required (or similar validation error in `app.publish` callback)
cause Publishing a message to `app.publish()` without including the mandatory `type` attribute in the message object.fixAlways include a `type` property in the message object passed to `app.publish()`, for example: `{ type: 'post', text: 'My message' }`. -
Unboxed private messages not appearing or incorrect data in indexes after adding new unboxers
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.fixAfter 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.
Warnings
- breaking 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.
- breaking 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.
- breaking 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.
- deprecated 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.
- gotcha `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.
Install
-
npm install ssb-db -
yarn add ssb-db -
pnpm add ssb-db
Imports
- createApp
import { createApp } from 'secret-stack'const createApp = require('secret-stack') - ssb-db (plugin object)
import ssbdb from 'ssb-db'
.use(require('ssb-db')) - pull
import pull from 'pull-stream'
var pull = require('pull-stream')
Quickstart
/**
* create an ssb-db instance and add a message to it.
*/
var pull = require('pull-stream')
//create a secret-stack instance and add ssb-db, for persistence.
var createApp = require('secret-stack')({})
.use(require('ssb-db'))
// create the db instance.
// Only one instance may be created at a time due to os locks on port and database files.
var app = createApp(require('ssb-config'))
//your public key, the default key of this instance.
console.log("App ID:", app.id)
//or, called remotely
app.whoami(function (err, data) {
if (err) return console.error(err)
console.log("Whoami ID:", data.id) //your id
})
// publish a message to default identity
// - feed.add appends a message to your key's chain.
// - the `type` attribute is required.
app.publish({ type: 'post', text: 'My First Post!' }, function (err, msg) {
if (err) return console.error(err)
// the message as it appears in the database:
console.log("Published message:", msg)
// and its hash:
console.log("Message key:", msg.key)
// collect all the messages into an array, calls back, and then ends
// https://github.com/pull-stream/pull-stream/blob/master/docs/sinks/collect.md
pull(
app.createLogStream(),
pull.collect(function (err, messagesArray) {
if (err) return console.error(err)
console.log("All messages in log:", messagesArray)
})
)
// collect all messages for a particular keypair into an array, calls back, and then ends
// https://github.com/pull-stream/pull-stream/blob/master/docs/sinks/collect.md
pull(
app.createHistoryStream({id: app.id}),
pull.collect(function (err, messagesArray) {
if (err) return console.error(err)
console.log("Messages for app.id:", messagesArray)
app.close() // Close the app instance to release locks
})
)
})