{"id":13482328,"url":"https://github.com/ssbc/ssb-db","last_synced_at":"2025-05-14T23:02:25.325Z","repository":{"id":16892966,"uuid":"19653696","full_name":"ssbc/ssb-db","owner":"ssbc","description":"A database of unforgeable append-only feeds, optimized for efficient replication for peer to peer protocols","archived":false,"fork":false,"pushed_at":"2024-07-13T11:53:16.000Z","size":1213,"stargazers_count":1176,"open_issues_count":14,"forks_count":75,"subscribers_count":59,"default_branch":"master","last_synced_at":"2025-05-07T07:41:20.364Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://scuttlebot.io/","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","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":"LICENSE","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,"publiccode":null,"codemeta":null}},"created_at":"2014-05-10T22:42:14.000Z","updated_at":"2025-05-05T11:47:03.000Z","dependencies_parsed_at":"2024-11-09T20:22:24.266Z","dependency_job_id":"db25d3ff-9d02-4b54-ac02-7ad2dbbd2953","html_url":"https://github.com/ssbc/ssb-db","commit_stats":{"total_commits":1109,"total_committers":27,"mean_commits":"41.074074074074076","dds":"0.28133453561767363","last_synced_commit":"18ce13af132456434ad62340784fe2b456c14a8c"},"previous_names":["ssbc/secure-scuttlebutt"],"tags_count":23,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssbc%2Fssb-db","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssbc%2Fssb-db/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssbc%2Fssb-db/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/ssbc%2Fssb-db/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/ssbc","download_url":"https://codeload.github.com/ssbc/ssb-db/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":253603218,"owners_count":21934662,"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-07-31T17:01:00.939Z","updated_at":"2025-05-14T23:02:25.296Z","avatar_url":"https://github.com/ssbc.png","language":"JavaScript","funding_links":[],"categories":["Modules","JavaScript","others"],"sub_categories":[],"readme":"# ssb-db\n\n[secret-stack](https://github.com/ssbc/secret-stack) plugin which provides storing of valid secure-scuttlebutt\nmessages in an append-only log.\n\n## Table of contents\n\n[What does it do?](#what-does-it-do) | \n[Example](#example) | \n[Concepts](#concepts) | \n[API](#api) | \n[Stability](#stability) | \n[License](#License) | \n\n# What does it do?\n\n`ssb-db` provides tools for dealing with unforgeable append-only message feeds. You can create a feed, post \nmessages to that feed, verify a feed created by someone else, stream messages to and from feeds, and more \n(see [API](#api)).\n\n*Unforgeable* means that only the owner of a feed can modify that feed, as enforced by digital signing \n(see [Security properties](#security-properties)).\n\nThis property makes `ssb-db` useful for peer-to-peer applications. `ssb-db` also makes it easy to encrypt \nmessages.\n\n# Example\n\nIn this example, we create a feed, post a signed message to it, then create a stream that reads from the feed. **Note:** `ssb-server` includes the `ssb-db` dependency already, so the example here uses this as a plugin for `secret-stack`.\n\n``` js\n/**\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.\n\napp.id\n\n//or, called remotely\n\napp.whoami(function (err, data) {\n  console.log(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  // the message as it appears in the database:\n  console.log(msg)\n\n  // and its hash:\n  console.log(msg.key)\n})\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\npull(\n  app.createLogStream(),\n  pull.collect(function (err, messagesArray) {\n    console.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\npull(\n  app.createHistoryStream({id: app.id}),\n  pull.collect(function (err, messagesArray) {\n    console.log(messagesArray)\n  })\n)\n```\n\n# Concepts\n\nBuilding upon `ssb-db` requires understanding a few concepts that it uses to ensure the unforgeability of \nmessage feeds.\n\n## Identities\n\nAn identity is simply a public/private key pair.\n\nEven though there is no worldwide store of identities, it's infeasible for anyone to forge your identity. \nIdentities are binary strings, so not particularly human-readable.\n\n## Feeds\n\nA feed is an append-only sequence of messages. Each feed is associated 1:1 with an identity. The feed is \nidentified by its public key. This works because public keys are unique.\n\nSince feeds are append-only, replication is simple:  request all messages in the feed that are newer than \nthe latest message you know about.\n\nNote that append-only really means append-only: you cannot delete an existing message. If you want to enable \nentities to be deleted or modified in your data model, that can be implemented in a layer on top of `ssb-db` \nusing [delta encoding](https://en.wikipedia.org/wiki/Delta_encoding).\n\n## Messages\n\nEach message contains:\n\n- A message object. This is the thing that the end user cares about. If there is no encryption, this is a `{}` \nobject. If there is encryption, this is an encrypted string.\n- A content-hash of the previous message. This prevents somebody with the private key from changing the feed \nhistory after publishing, as a newly-created message wouldn't match the \"prev-hash\" of later messages which \nwere already replicated.\n- The signing public key.\n- A signature. This prevents malicious parties from writing fake messages to a stream.\n- A sequence number. This prevents a malicious party from making a copy of the feed that omits or reorders \nmessages.\n  \nSince each message contains a reference to the previous message, a feed must be replicated in order, starting \nwith the first message. This is the only way that the feed can be verified. A feed can be *viewed* in any order \nafter it's been replicated.\n\n## Object ids\n\nThe text inside a message can refer to three types of ssb-db entities: messages, feeds, and blobs (i.e. \nattachments). Messages and blobs are referred to by their hashes, but a feed is referred to by its signing \npublic key. Thus, a message within a feed can refer to another feed, or to a particular point _within_ a feed.\n\nObject ids begin with a sigil `@` `%` and `\u0026` for a `feedId`, `msgId` and `blobId` respectively.\n\nNote that `ssb-db` does not include facilities for retrieving a blob given the hash.\n\n## Replication\n\nIt is possible to easily replicate data between two instances of `ssb-db`.\nFirst, they exchange maps of their newest data. Then, each one downloads all data newer than its newest data.\n\n[ssb-server](https://github.com/ssbc/ssb-server) is a tool that makes it easy to replicate multiple instances \nof ssb-db using a decentralized network.\n\n## Security properties\n\n`ssb-db` maintains useful security properties even when it is connected to a malicious ssb-db database. This \nmakes it ideal as a store for peer-to-peer applications.\n\nImagine that we want to read from a feed for which we know the identity, but we're connected to a malicious \nssb-db instance. As long as the malicious database does not have the private key:\n\n- The malicious database cannot create a new feed with the same identifier\n- The malicious database cannot write new fake messages to the feed\n- The malicious database cannot reorder the messages in the feed\n- The malicious database cannot send us a new copy of the feed that omits messages from the middle\n- The malicious database *can* refuse to send us the feed, or only send\n  us the first *N* messages in the feed\n- Messages may optionally be encrypted. See `test/end-to-end.js`.\n\n\n## API\n## require('ssb-db')\n```js \nSecretStack.use(require('ssb-db')) =\u003e SecretStackApp\n```\n\nThe design pattern of __ssb-db__ is for it to act as a plugin within the \n[SecretStack](https://github.com/ssbc/secret-stack) plugin framework. The main export provides the plugin, \nwhich extends the SecretStack app with this plugins functionality, and API.\n`ssb-db` adds persistence to a [SecretStack](https://github.com/ssbc/secret-stack) setup.\nWithout other plugins, this instance will not have replication or querying. Loading `ssb-db` directly is \nuseful for testing, but it's recommended to instead start from a plugin bundle like \n[ssb-server](https://github.com/ssbc/ssb-server)\n\n\u003e Because of legacy reasons, all the `ssb-db` methods are mounted on the top level object, so it's `app.get` \ninstead of `app.db.get` as it would be with all the other `ssb-*` plugins.\n\n\u003e In the API docs below, we'll just call it `db`\n\n## db.get: async\n```js\ndb.get(id | seq | opts, cb) // cb(error, message)\n```\n\nGet a message by its hash-id.\n\n* If `id` is a message id, the message is returned.\n* If `seq` is provided, the message at that offset in the underlying flumelog is returned. \n* If `opts` is passed, the message id is taken from either `opts.id` or `opts.key`.\n* If `opts.private = true` the message will be decrypted if possible.\n* If `opts.meta = true` is set, or `seq` is used, the message will be in `{key, value: msg, timestamp}` format. \nOtherwise the raw message (without key and timestamp) are returned. This is for backwards compatibility reasons.\n\nGiven that most other apis (such as createLogStream) by default return `{key, value, timestamp}` it's \nrecommended to use `db.get({id: key, meta: true}, cb)`\n\nNote that the `cb` callback is called with 3 arguments: `cb(err, msg, offset)`, where\nthe 3rd argument is the `offset` position of that message in the log (flumelog-offset).\n\n## db.add: async\n```js\ndb.add(msg, cb) // cb(error, data)\n```\n\nAppend a raw message to the local log. `msg` must be a valid, signed message. \n[ssb-validate](https://github.com/ssbc/ssb-validate) is used internally to validate messages.\n\n## db.publish: async\n```js\ndb.publish(content, cb) // cb(error, data)\n```\nCreate a valid message with `content` with the default identity and append it to the local log. \n[ssb-validate](https://github.com/ssbc/ssb-validate) is used to construct a valid message.\n\nThis is the recommended method for publishing new messages, as it handles the tasks of correctly setting \nthe message's timestamp, sequence number, previous-hash, and signature.\n\n - `content` (object): The content of the message.\n   - `.type` (string): The object's type.\n\n\n## db.del: async \n\n\u003e ⚠ This could break your feed. Please don't run this unless you understand it.\n\nDelete a message by its message key or a whole feed by its key. This only deletes the message from your local \ndatabase, not the network, and could have unintended consequences if you try to delete a single message in \nthe middle of a feed.\n\nThe intended use-case is to delete all messages from a given feed *or* deleting a single message from the tip \nof your feed if you're completely confident that the message hasn't left your device.\n\n```js\n//Delete message\ndb.del(msg.key, (err, key) =\u003e {\n  if (err) throw err\n})\n```\n\n```js\n//Delete all author messages\ndb.del(msg.value.author, (err, key) =\u003e {\n  if (err) throw err\n})\n```\n\n## db.whoami: async\n```js\ndb.whoami(cb) // cb(error, {\"id\": FeedID })\n```\nGet information about the current ssb-server user.\n\n## db.createLogStream: source\n```js\ndb.createLogStream({ live, old, gt, gte, lt, lte, reverse, keys, values, limit, fillCache, keyEncoding, \nvalueEncoding, raw }): PullSource\n```\nCreate a stream of the messages that have been written to this instance in the order they arrived. This is \nmainly intended for building views.\n\n  - `live` *(boolean)* Keep the stream open and emit new messages as they are received. Defaults to `false`.\n - `old` *(boolean)* If `false` the output will not include the old data. *If live and old are both false, \n an error is thrown.* Defaults to `true`.\n - `gt` (greater than), `gte` (greater than or equal) *(timestamp)*  Define the lower bound of the range to \n be streamed. Only records where the key is greater than (or equal to) this option will be included in the \n range. When `reverse=true` the order will be reversed, but the records streamed will be the same.\n - `lt` (less than), `lte` (less than or equal) *(timestamp)* Define the higher bound of the range to be \n streamed. Only key/value pairs where the key is less than (or equal to) this option will be included in \n the range. When `reverse=true` the order will be reversed, but the records streamed will be the same.\n - `reverse` *(boolean)* Set true and the stream output will be reversed. Beware that due to the way LevelDB \n works, a reverse seek will be slower than a forward seek. Defaults to `false`.\n - `keys` *(boolean)* Whether the `data` event should contain keys. If set to `true` and `values` set to \n `false` then `data` events will simply be keys, rather than objects with a `key` property. Defaults to `true`.\n - `values` *(boolean)* Whether the `data` event should contain values. If set to `true` and `keys` set to \n `false` then `data` events will simply be values, rather than objects with a `value` property. Defaults to `true`.\n - `limit` *(number)* Limit the number of results collected by this stream. This number represents a *maximum* \n number of results and may not be reached if you get to the end of the data first. A value of `-1` means there \n is no limit. When `reverse=true` the highest keys will be returned instead of the lowest keys. \n Defaults to `false`.\n - `keyEncoding` / `valueEncoding` *(string)* The encoding applied to each read piece of data.\n - `raw` *(boolean)* Provides access to the raw [flumedb](https://github.com/flumedb/flumedb) log. \n Defaults to `false`.\n\n\nThe objects in this stream will be of the form:\n\n```json\n{\n  \"key\": Hash,\n  \"value\": Message,\n  \"timestamp\": timestamp\n}\n```\n\n* `timestamp` * is the time which the message was received.\nIt is generated by [monotonic-timestamp](https://github.com/dominictarr/monotonic-timestamp). The range \nqueries (gt, gte, lt, lte) filter against this receive timestap.\n\nIf `raw` option is provided, then instead createRawLogStream is called, messages are returned in the form:\n\n```json\n{\n  \"seq\": offset,\n  \"value\": {\n    \"key\": Hash,\n    \"value\": Message,\n    \"timestamp\": timestamp\n  }\n}\n```\nAll options supported by [flumelog-offset](https://github.com/flumedb/flumelog-offset) are supported.\n\n## db.createHistoryStream: source\n```js\ndb.createHistoryStream(id, seq, live) -\u003e PullSource\n//or\ndb.createHistoryStream({ id, seq, live, limit, keys, values, reverse }) -\u003e PullSource\n\n```\n\nCreate a stream of the history of `id`. If `seq \u003e 0`, then only stream messages with sequence numbers greater \nthan `seq`. If `live` is true, the stream will be a \n[live mode](https://github.com/dominictarr/pull-level#example---reading)\n\n`createHistoryStream` and `createUserStream` serve the same purpose.\n\n`createHistoryStream` exists as a separate call because it provides fewer range parameters, which makes it \nsafer for RPC between untrusted peers.\n\n\u003e Note: since `createHistoryStream` is provided over the network to anonymous peers, not all options are \nsupported. `createHistoryStream` does not decrypt private messages.\n\n- `id` *(FeedID)* The id of the feed to fetch.\n- `seq` *(number)* If `seq \u003e 0`, then only stream messages with sequence numbers greater than or equal to `seq`. \nDefaults to `0`.\n- `live` *(boolean)*: Keep the stream open and emit new messages as they are received. Defaults to `false`\n- `keys` *(boolean)*: Whether the `data` event should contain keys. If set to `true` and `values` set to \n`false` then `data` events will simply be keys, rather than objects with a `key` property. Defaults to `true`\n- `values` *(boolean)* Whether the `data` event should contain values. If set to `true` and `keys` set to \n`false` then `data` events will simply be values, rather than objects with a `value` property. Defaults to `true`.\n- `limit` *(number)* Limit the number of results collected by this stream. This number represents a *maximum* \nnumber of results and may not be reached if you get to the end of the data first. A value of `-1` means there \nis no limit. When `reverse=true` the highest keys will be returned instead of the lowest keys. Defaults to `false`.\n- `reverse` *(boolean)* Set true and the stream output will be reversed. Beware that due to the way LevelDB \nworks, a reverse seek will be slower than a forward seek. Defaults to `false`.\n\n## db.messagesByType: source\n```js\ndb.messagesByType({type: string, live,old,reverse: bool?, gt,gte,lt,lte: timestamp, limit: number }) -\u003e PullSource\n```\n\nRetrieve messages with a given type, ordered by receive-time. All messages must have a type, so this is a good way \nto select messages that an application might use. This function returns a source pull-stream.\n\nAs with `createLogStream` messagesByType takes all the options from \n[pull-level#read](https://github.com/dominictarr/pull-level#example---reading) (gt, lt, gte, lte, limit, reverse, \nlive, old)\n\n## db.createFeedStream: source\n```js\ndb.createFeedStream({ live, old, gt, gte, lt, lte, reverse, keys, value,, limit, fillCache, keyEncoding, \nvalueEncoding, raw }))\n```\nLike `createLogStream`, but messages are in order of the claimed time, instead of the received time.\n\nThis may sound like a much better idea, but has surprising effects with live messages (you may receive a old \nmessage in real time) but for old messages, it makes sense.\n\nThe range queries (gt, gte, lt, lte) filter against this claimed timestap.\n\nAs with `createLogStream` createFeedStream takes all the options from \n[pull-level#read](https://github.com/dominictarr/pull-level#example---reading) (gt, lt, gte, lte, limit, reverse, \nlive, old)\n\n\n## db.createUserStream: source\n```js\ndb.createUserStream({id: feed_id, lt, lte ,gt ,gte: sequence, reverse, old, live, raw: boolean, limit: number, private: boolean})\n```\n\n`createUserStream` is like `createHistoryStream`, except all options are\nsupported. Local access is allowed, but not remote anonymous access.\n`createUserStream` can decrypt private messages if you pass the option\n`{ private: true }`.\n\n## db.links: source\n```js\ndb.links({ source, dest: feedId|msgId|blobId, rel, meta, keys, values, live, reverse }) -\u003e PullSource\n```\n\nGet a stream of links from a feed to a blob/msg/feed id. The objects in this stream will be of the form:\n\n```json\n{ \n  \"source\": FeedId,\n  \"rel\": String,\n  \"dest\": Id,\n  \"key\": MsgId,\n  \"value\": Object?\n}\n```\n\n - `source` *(string)* An id or filter, specifying where the link should originate from. To filter, just use \n the sigil of the type you want: `@` for feeds, `%` for messages, and `\u0026` for blobs. Optional.\n - `dest` *(string)* An id or filter, specifying where the link should point to. To filter, just use the sigil \n of the type you want: `@` for feeds, `%` for messages, and `\u0026` for blobs. Optional.\n - `rel` *(string)* Filters the links by the relation string. Optional.\n  - `live` *(boolean)*: Keep the stream open and emit new messages as they are received. Defaults to `false.\n - `values` *(boolean)* Whether the `data` event should contain values. If set to `true` and `keys` set to \n `false` then `data` events will simply be values, rather than objects with a `value` property. \n Defaults to `false`.\n - `keys` *(boolean)* Whether the `data` event should contain keys. If set to `true` and `values` set to \n `false` then `data` events will simply be keys, rather than objects with a `key` property. Defaults to `true`.\n - `reverse` *(boolean)*: Set true and the stream output will be reversed. Beware that due to the way LevelDB \n works, a reverse seek will be slower than a forward seek. Defaults to `false`.\n - `meta` If is unset `source, hash, rel` will be left off. Defaults to `true`.\n\n\u003e Note: if `source`, and `dest` is provided, but not `rel`, ssb will have to scan all the links from source, \nand then filter by dest. Your query will be more efficient if you also provide `rel`.\n\n## db.addMap: sync\n```js\ndb.addMap(fn)\n```\n\nAdd a map function to be applied to all messages on *read*. The `fn` function is should expect `(msg, cb)`, \nand must eventually call `cb(err, msg)` to finish.\n\nThese modifications only change the value being read, but the underlying data is never modified. If multiple \nmap functions are added, they are called serially and the `msg` output by one map function is passed as the \ninput `msg` to the next.\n\nAdditional properties may only be added to `msg.value.meta`, and modifications may only be made *after* the \noriginal value is saved in `msg.value.meta.original`.\n\n```js\ndb.addMap(function (msg, cb) {\n  if (!msg.value.meta) {\n    msg.value.meta = {}\n  }\n\n  if (msg.value.timestamp % 3 === 0)\n    msg.value.meta.fizz = true\n  if (msg.timestamp % 5 === 0)\n    msg.value.meta.buzz = true\n  cb(null, msg)\n})\n\nconst metaBackup = require('ssb-db/util').metaBackup\n\ndb.addMap(function (msg, cb) {\n  // This could instead go in the first map function, but it's added as a second\n  // function for demonstration purposes to show that `msg` is passed serially.\n  if (msg.value.meta.fizz \u0026\u0026 msg.value.meta.buzz) {\n    msg.meta = metaBackup(msg.value, 'content')\n\n    msg.value.content = {\n      type: 'post',\n      text: 'fizzBuzz!'\n    }\n  }\n  cb(null, msg)\n})\n```\n\n## db._flumeUse: view\n```js\ndb._flumeUse(name, flumeview) =\u003e View\n```\n\nAdd a [flumeview](https://github.com/flumedb/flumedb#views) to the current instance.\nThis method was intended to be a temporary solution, but is now used by many plugins, which is why it starts \nwith `_`.\n\nSee [creating a secret-stack plugin](https://github.com/ssbc/secret-stack/blob/master/PLUGINS.md) for more \ndetails.\n\n## db.getAtSequence: async\n```js\ndb.getAtSequence([id, seq], cb) //cb(err, msg)\n```\n\nGet a message for a given feed `id` with given `sequence`. Calls back a message or an error, takes a two \nelement array with a feed `id` as the first element, and `sequence` as second element.\n\nNeeded for [ssb-ebt replication](https://github.com/ssbc/ssb-ebt)\n\n## db.getVectorClock: async\n```js\ndb.getVectorClock(cb) //cb(error, clock)\n```\n\nLoad a map of `id` to latest `sequence` (`{\u003cid\u003e: \u003cseq\u003e,...}`) for every feed in the database.\n\nNeeded for [ssb-ebt replication](https://github.com/ssbc/ssb-ebt)\n\n## db.progress: sync\n```js\ndb.progress()\n```\n\nReturn the current status of various parts of the scuttlebut system that indicate progress. This api is \nhooked by a number of plugins, but `ssb-db` adds an `indexes` section (which represents how fully built the \nindexes are).\n\nThe output might look like:\n```json\n{\n  \"indexes\": {\n    \"start\": 607551054,\n    \"current\": 607551054,\n    \"target\": 607551054\n  }\n}\n```\n\nProgress is represented linearly from `start` to `target`. Once `current` is equal to `target` the progress \nis complete. `start` shows how far it's come. The numbers could be anything, but `start \u003c= current \u003c= target` \nif all three numbers are equal that should be considered 100%\n\n\n## db.status: sync\n```js\ndb.status()\n```\n\nReturns metadata about the status of various ssb plugins. ssb-db adds an `sync` section, that shows where each \nindex is up to. \nThe purpose is to provide an overview of how ssb is working.\n\nOutput might took like this:\n\n```json\n{\n  \"sync\": {\n    \"since\": 607560288,\n    \"plugins\": {\n      \"last\": 607560288,\n      \"keys\": 607560288,\n      \"clock\": 607560288,\n      \"time\": 607560288,\n      \"feed\": 607560288,\n      \"contacts2\": 607560288,\n      \"query\": 607560288,\n      ...\n    },\n    \"sync\": true\n  }\n}\n```\n\n`sync.since` is where the main log is up to, and `since.plugins.\u003cname\u003e` is where each plugin's indexes are up to.\n\n## db.version: sync\n```js\ndb.version()\n```\n\nReturn the version of `ssb-db`. currently, this returns only the ssb-db version and not the ssb-server version, \nor the version of any other plugins. [We should fix this soon](https://github.com/ssbc/ssb-server/issues/648)\n\n## db.queue: async \n```js\ndb.queue(msg, cb) //cb(error, msg)\n```\n\nAdd a message to be validated and written, but don't worry about actually writing it. The callback is called \nwhen the database is ready for more writes to be queued. Usually that means it's called back immediately. \n__This method is not exposed over RPC.__\n\n## db.flush: async \n\n```js\ndb.flush(cb) //cb()\n```\n\nCallback when all queued writes are actually definitely written to the disk.\n\n## db.getFeedState: async\n\n```js\ndb.getFeedState(feedId, (err, state))\n```\n\nCalls back with state, `{ id, sequence }` - the most recent message ID and sequence number according to SSB-Validate: \n\nNOTE\n- **this may contain messages that have been queued and not yet persisted to the database**\n    - this is required for e.g. boxers which depend on knowing previous message state\n- this is the current locally known state of the feed, it is possible if it's a foreign feed\nthat the state has progressed beyond whay you know but you haven't got a copy yet, so use this carefully.\n- \"no known state\" is represented by `{ id: null, sequence: 0 }`\n\n## db.post: Observable\n\n```js\ndb.post(fn({key, value: msg, timestamp})) =\u003e Ovb\n```\n\n[Observable](https://github.com/dominictarr/obv) that calls `fn` whenever a message is appended (with that \nmessage). __This method is not exposed over RPC.__\n\n\n## db.since: Observable\n```js\ndb.since(fn(seq)) =\u003e Obv\n```\n\nAn [observable](https://github.com/dominictarr/obv) of the current log sequence. This is always a positive \ninteger that usually increases, except in the exceptional circumstance that the log is deleted or corrupted.\n\n### db.addBoxer: sync\n\n```js\ndb.addBoxer({ value: boxer, init: initUnboxer })\n```\n\nAdd a `boxer`, which will be added to the list of boxers which will try to\nautomatically box (encrypt) the message `content` if the appropriate\n`content.recps` is provided.\n\nWhere:\n- `boxer (msg.value.content, feedState) =\u003e ciphertext` which is expected to either:\n    - successfully box the content (based on `content.recps`), returning a `ciphertext` String\n    - not know how to box this content (because recps are outside it's understanding), and `undefined` (or `null`)\n    - break (because it should know how to handle `recps`, but can't), and so throw an `Error`\n    - The `feedState` object contains `id` and `sequence` properties that\n      describe the most recent message ID and sequence number for the feed.\n      This is the same data exposed by `db.getFeedState()`.\n- `initUnboxer (done) =\u003e null` (optional)\n    - is a functional which allows you set up your unboxer\n    - you're expected to call `done()` once all your initialisation is complete\n\n## db.addUnboxer: sync\n\n```js\ndb.addUnboxer({ key: unboxKey, value: unboxValue, init: initBoxer })\n```\n\nAdd an unboxer object, any encrypted message is passed to the unboxer object to\ntest if it can be unboxed (decrypted)\n\nWhere:\n- `unboxKey(msg.value.content, msg.value) =\u003e readKey`\n    - Is a function which tries to extract the message key from the encrypted content (`ciphertext`).\n    - Is expected to return `readKey` which is the read capability for the message\n- `unboxValue(msg.value.content, msg.value, readKey) =\u003e plainContent`\n    - Is a function which takes a `readKey` and uses it to try to extract the `plainContent` from the `ciphertext\n- `initBoxer (done) =\u003e null` (optional)\n    - is a functional which allows you set up your boxer\n    - you're expected to call `done()` once all your initialisation is complete\n\n## db.box(content, recps, cb)\n\nattempt to encrypt some `content` to `recps` (an Array of keys/ identifiers).\ncallback has signature `cb(err, ciphertext)`\n\n## db.unbox: sync\n```js\ndb.unbox(data, key)\n```\nAttempt to decrypt data using key. Key is a symmetric key, that is passed to the unboxer objects.\n\n## db.rebuild: async\n```js\ndb.rebuild(cb)\n```\n\nRebuilds the indexes. This takes a while to run and using SSB features before it is completed may lead to confusing experiences as the indexes will be out of sync.\n\n## db.Deprecated apis\n\n## db.getLatest: async\n```js\ndb.getLatest(feed, cb) //cb(err, {key, value: msg})\n```\n\nGet the latest message for the given feed, with `{key, value: msg}` style. Maybe used by some front ends, and \nby ssb-feed.\n\n## db.latestSequene: async\n```js\ndb.latestSequence(feed, cb) //cb(err, sequence)\n```\n\nCall back the sequence number of the latest message for the given feed.\n\n## db.latest: source\n```js\ndb.latest() =\u003e PullSource\n```\n\nReturns a stream of `{author, sequence, ts}` tuples. `ts` is the time claimed by the author, not the received \ntime.\n\n## db.createWriteStream: source\n```js\ndb.createWriteStream() =\u003e PullSink`\n```\nCreate a pull-stream sink that expects a stream of messages and calls `db.add` on each item, appending every \nvalid message to the log.\n\n## db.createFeed: sync\n```js\ndb.createFeed(keys?)\n```\n\n## db.createSequenceStream() =\u003e PullSource\n\nCreate a pull-stream source that provides the latest sequence number from the\ndatabase. Each time a message is appended the sequence number should increase\nand a new event should be sent through the stream.\n\nNote: In the future this stream may be debounced. The number of events passed\nthrough this stream may be less than the number of messages appended.\n\n## db.createFeed(keys?) =\u003e Feed (deprecated)\n__Use [ssb-identities](http://github.com/ssbc/ssb-identities) instead.__\n\nCreate and return a Feed object. A feed is a chain of messages signed by a single key (the identity of the feed).\n\nThis handles the state needed to append valid messages to a feed. If keys are not provided, then a new key pair \nwill be generated.\n\nMay only be called locally, not from a [ssb-client](https://github.com/ssbc/ssb-client) connection.\n\nThe following methods apply to the Feed type.\n\n### Feed#add(message, cb)\n\nAdds a message of a given type to a feed. This is the recommended way to append messages. \n\n`message` is a javascript object. It must be a `{}` object with a `type` property that is a string between 3 \nand 32 chars long.\n\nIf `message` has `recps` property which is an array of feed ids, then the message content will be encrypted \nusing [private-box](https://github.com/auditdrivencrypto/private-box) to those recipients. Any invalid \nrecipients will cause an error, instead of accidentially posting a message publically or without a recipient.\n\n### Feed#id\n\nThe id of the feed (which is the feed's public key)\n\n### Feed#keys\n\nThe key pair for this feed.\n\n## Stability\n\n__Stable__ Expect patches, possible features additions.\n\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fssbc%2Fssb-db","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fssbc%2Fssb-db","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fssbc%2Fssb-db/lists"}