2016-02-03 14:22:58 +07:00
# OrbitDB
2015-12-26 19:05:51 +02:00
## Introduction
2016-02-26 13:39:51 +01:00
Distributed, peer-to-peer Key-Value Store and Event Log on IPFS.
2015-12-26 19:05:51 +02:00
2016-02-26 13:39:51 +01:00
- Stores all data in IPFS, including the database index
2016-02-26 17:13:08 +01:00
- Aggregation happens on client side and data is eventually consistent
2016-02-26 13:39:51 +01:00
- Uses a LWW-element-set [CRDT ](https://en.wikipedia.org/wiki/Conflict-free_replicated_data_type ) and (sort of) Vector Clocks for partial ordering
- Designed to work offline first and to be easily embeddable to applications
2016-02-03 14:22:58 +07:00
2016-02-26 13:39:51 +01:00
_Currently requires [orbit-server ](https://github.com/haadcode/orbit-server ) for pubsub communication. This will change in the future as soon as IPFS provides pubsub._
2015-12-26 19:05:51 +02:00
2016-02-26 13:39:51 +01:00
_OrbitDB calls its namespaces channels. A channel is similar to "table", "keyspace", "topic", "feed" or "collection" in other db systems._
2015-12-26 19:05:51 +02:00
2016-02-26 13:39:51 +01:00
## Examples
Before running the examples, install dependencies with:
2016-02-22 12:36:38 +02:00
```
npm install
```
Key-Value store example:
```
2016-03-03 15:16:27 +01:00
node examples/keyvalue.js < host > < username > < channel > < key > < value >
node examples/keyvalueReader.js < host > < username > < channel > < key >
2016-02-22 12:36:38 +02:00
```
2016-02-26 13:39:51 +01:00
Event log example (run several in separate shells):
2016-02-22 12:36:38 +02:00
```
2016-03-03 15:16:27 +01:00
node examples/reader.js < host > < username > < channel > < data > < interval in ms >
```
Benchmark writes:
```
node examples/benchmark.js < host > < username > < channel > ;
2016-02-22 12:36:38 +02:00
```
2015-12-26 19:05:51 +02:00
## API
2016-02-26 13:39:51 +01:00
_See usage example below_
2016-02-03 14:22:58 +07:00
2016-02-22 12:36:38 +02:00
connect(host, port, username, password)
2015-12-26 19:05:51 +02:00
channel(name, password)
2016-01-19 21:59:53 +08:00
.add(data: String) // Insert an event to a channel, returns < ipfs-hash > of the event
.iterator([options]) // Returns an iterator of events
// options : {
// gt: < ipfs-hash > , // Return events newer than < ipfs-hash >
// gte: < ipfs-hash > , // Return events newer then < ipfs-hash > (inclusive)
// lt: < ipfs-hash > , // Return events older than < ipfs-hash >
// lte: < ipfs-hash > , // Return events older than < ipfs-hash > (inclusive)
// limit: -1, // Number of events to return, -1 returns all, default 1
// reverse: true // Return items oldest first, default latest first
// }
.put(key, data: String) // Insert (key,value) to a channel
.get(key) // Retrieve value
2015-12-26 19:05:51 +02:00
2016-02-12 09:27:57 +01:00
.del({ key: < key or hash > }) // Remove entry
2016-01-19 11:43:09 +08:00
2016-02-22 12:36:38 +02:00
.delete() // Deletes the channel, all data will be "removed" (unassociated with the channel, actual data is not deleted from ipfs)
2016-01-17 18:10:16 +08:00
2015-12-26 19:05:51 +02:00
## Usage
```javascript
2016-02-22 12:36:38 +02:00
const async = require('asyncawait/async');
const OrbitClient = require('./OrbitClient');
2015-12-26 19:05:51 +02:00
2016-02-26 13:39:51 +01:00
// orbit-server
2016-02-22 12:36:38 +02:00
const host = 'localhost';
2016-02-26 13:39:51 +01:00
const port = 3333;
2015-12-26 19:05:51 +02:00
2015-12-27 17:27:17 +02:00
async(() => {
// Connect
2016-02-22 12:36:38 +02:00
const orbit = OrbitClient.connect(host, port, username, password);
2015-12-27 17:27:17 +02:00
2016-01-19 11:43:09 +08:00
const channelName = 'hello-world';
2016-02-12 09:27:57 +01:00
const db = orbit.channel(channelName);
2015-12-27 17:27:17 +02:00
2016-02-03 14:22:58 +07:00
/* Event Log */
2016-02-12 09:27:57 +01:00
const hash = db.add('hello'); // < ipfs-hash >
// Remove event
db.remove(hash);
2015-12-27 17:27:17 +02:00
// Iterator options
2016-02-12 09:27:57 +01:00
const options = { limit: -1 }; // fetch all messages
2016-01-19 21:59:53 +08:00
// Get events
2016-02-12 09:27:57 +01:00
const iter = db.iterator(options); // Symbol.iterator
const next = iter.next(); // { value: < item > , done: false|true}
2016-01-19 21:59:53 +08:00
2016-01-18 18:41:00 +08:00
// OR:
// var all = iter.collect(); // returns all elements as an array
2015-12-27 17:27:17 +02:00
// OR:
// for(let i of iter)
2016-01-19 21:59:53 +08:00
// console.log(i.hash, i.item);
2015-12-27 17:27:17 +02:00
2016-02-03 14:22:58 +07:00
/* KV Store */
2016-02-12 09:27:57 +01:00
db.put('key1', 'hello world');
db.get('key1'); // returns "hello world"
2016-02-22 12:36:38 +02:00
db.del('key1');
2015-12-27 18:25:23 +02:00
2016-02-03 14:22:58 +07:00
/* Delete channel */
2016-02-12 09:27:57 +01:00
const result = db.delete(); // true | false
2015-12-27 17:27:17 +02:00
})();
2015-12-26 19:05:51 +02:00
```
2016-01-19 21:59:53 +08:00
2016-01-20 16:05:47 +08:00
### Development
2016-03-05 12:03:06 +01:00
#### Source Code
The entry point for the code is [src/OrbitClient.js ](https://github.com/haadcode/orbit-db/blob/master/src/OrbitClient.js ) and the DB implementation is [src/OrbitDB.js ](https://github.com/haadcode/orbit-db/blob/master/src/OrbitDB.js )
2016-01-20 16:05:47 +08:00
#### Run Tests
```
npm test
```
Keep tests running while development:
```
mocha -w
```
2016-03-06 17:00:01 +01:00
#### Lint
*Work in progress! Throws an error "Parsing error: The keyword 'await' is reserved".*
```
npm run lint
```
2016-01-19 21:59:53 +08:00
### TODO
2016-02-26 13:39:51 +01:00
- Fix encryption
2016-03-07 16:48:56 +01:00
- Logging
2016-02-22 12:36:38 +02:00
- Caching
2016-02-26 15:09:33 +01:00
## Notes
### Data structure description
2016-02-26 17:13:08 +01:00
*For future [IPLD ](https://github.com/ipfs/ipld-examples ) references*
2016-02-26 15:09:33 +01:00
List snapshots are posted to pubsub:
```
> QmRzWAiFdLkdkwBDehzxhHdhfwbDKDnzqBnX53va58PuQu
> ...
```
**Get a list snapshot**
`ipfs object get QmRzWAiFdLkdkwBDehzxhHdhfwbDKDnzqBnX53va58PuQu`
2016-02-26 15:17:39 +01:00
```json
2016-02-26 15:09:33 +01:00
{
2016-02-26 15:17:39 +01:00
"Links": [],
"Data": {
"id": "writer",
"seq": 1301,
"ver": 3,
"items": {
"writer.1301.0": "QmNwREbsgGgiQPXxpvGanD55inFjUXjpEqjiPtpa39P7Mn",
"writer.1301.1": "QmQxndNEzWxKT5KRqRsty7JDGcbPVazaYPCqfB5z1mxmon",
"writer.1301.2": "QmUN1X97M2t8MX55H8VoPGXu2fLBpr91iCAzHkXudSMvDE"
2016-02-26 15:09:33 +01:00
}
}
}
```
**Get the item**
`ipfs object get QmNwREbsgGgiQPXxpvGanD55inFjUXjpEqjiPtpa39P7Mn`
2016-02-26 15:17:39 +01:00
```json
2016-02-26 15:09:33 +01:00
{
2016-02-26 15:17:39 +01:00
"Links": [],
"Data": {
"id": "writer",
"seq": 1301,
"ver": 0,
"data": "QmasZEUwc67yftPvdSxRLWenmvF8faLnS7TMphQpn4PCWZ",
"next": {
"writer.1300.9": "QmS17ABxzFEVoHv5WEvATetNEZhN2vkNApRPcFQUaJfij3"
2016-02-26 15:09:33 +01:00
}
}
}
```
**Get the item's data (operation)**
`ipfs object get QmasZEUwc67yftPvdSxRLWenmvF8faLnS7TMphQpn4PCWZ`
2016-02-26 15:17:39 +01:00
```json
2016-02-26 15:09:33 +01:00
{
2016-02-26 15:17:39 +01:00
"Links": [],
"Data": {
"op": "PUT",
"key": "default",
"value": "QmaAPEKDdaucQZRseJmKmWwZhgftBSwj8TD1xEomgcxo1X",
"meta":{
"type": "text",
"size": -1,
"from": "writer",
"ts": 1456494484094
2016-02-26 15:09:33 +01:00
}
}
}
```
**Get the value**
`ipfs object get QmaAPEKDdaucQZRseJmKmWwZhgftBSwj8TD1xEomgcxo1X`
2016-02-26 15:17:39 +01:00
```json
2016-02-26 15:09:33 +01:00
{
2016-02-26 15:17:39 +01:00
"Links": [],
"Data": {
"content": "LambOfGod 347",
"ts": 1456494484089
2016-02-26 15:09:33 +01:00
}
}
```