orbitdb

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 29, 2026 License: MIT Imports: 20 Imported by: 0

README

go-orbitdb

Go Go Reference

A Go implementation of OrbitDB, the serverless, peer-to-peer database built on IPFS and libp2p.

It is a port of @orbitdb/core 4 and interoperates with it: identities, log entries, manifests, access controllers and the sync protocol are byte-for-byte compatible, so Go and JavaScript peers open and replicate the same databases. This is checked on every push against a live @orbitdb/core 4.0.0 node (see Compatibility).

node, _ := ipfs.New(ctx, ipfs.Options{Repo: "./blocks"})
odb, _ := orbitdb.New(ctx, orbitdb.Options{IPFS: node, Directory: "./orbitdb"})
defer odb.Stop()

kv, _ := odb.OpenKeyValue(ctx, "settings")
kv.Put(ctx, "theme", "dark")
fmt.Println(kv.Address()) // /orbitdb/zdpu... — share it with peers

Install

go get github.com/orbitdb/go-orbitdb

Go 1.26 or later.

What's included

@orbitdb/core Go package Notes
createOrbitDB, open orbitdb Open, typed OpenEvents / OpenKeyValue / OpenKeyValueIndexed / OpenDocuments, OpenAs[T]
Events, KeyValue, KeyValueIndexed, Documents databases custom types via UseDatabaseType
Log, Entry, Heads, Clock, conflict resolution oplog append, join, traverse, range iterator, entry/payload encryption hooks
Sync syncutils gossipsub updates + /orbitdb/heads/<address> exchange
IPFS and OrbitDB access controllers accesscontrollers grant / revoke, custom types via UseAccessController
Identities, publickey provider identities custom providers via providers.UseIdentityProvider
KeyStore keystore secp256k1 via go-libp2p; reads JS keystores
Memory, LRU, Level, IPFS block, Composed storage storage
@orbitdb/simple-encryption encryption AES-GCM + PBKDF2, same ciphertext format
Helia ipfs go-libp2p host, gossipsub and a boxo bitswap block service

Usage

Databases
events, _ := odb.OpenEvents(ctx, "log")
hash, _ := events.Add(ctx, map[string]any{"msg": "hello"})
all, _ := events.All(ctx) // oldest first

type Post struct {
	ID    string `json:"_id"` // documents are keyed by "_id" unless configured otherwise
	Title string `json:"title"`
}
docs, _ := odb.OpenDocuments(ctx, "posts")
docs.Put(ctx, Post{ID: "p1", Title: "first"}) // structs are stored through their JSON form
matches, _ := docs.Query(ctx, func(d map[string]any) bool { return d["title"] == "first" })

kvi, _ := odb.OpenKeyValueIndexed(ctx, "settings-indexed") // reads from a local index

Values are anything dag-cbor can hold: nil, booleans, numbers, strings, []byte, CIDs, slices and string-keyed maps, or structs (encoded via encoding/json). Reads return map[string]any, []any, int64, float64, string, []byte, bool or nil. As in JavaScript, a float with an integral value is stored as an integer.

Replication

Open the same address on another peer:

db, _ := odb.OpenKeyValue(ctx, "/orbitdb/zdpu...")
sub := db.Events().Subscribe()
for ev := range sub.Events() {
	if ev.Type == databases.EventUpdate {
		fmt.Println("update:", ev.Entry.Payload)
	}
}

The manifest, access controller, heads, missing ancestors and writer identities are fetched from connected peers over bitswap. Peers find each other however you connect their libp2p hosts (node.Connect, or your own discovery); there is no DHT or delegated routing by default.

To talk to a JavaScript peer, give its Helia node TCP (or another transport both sides share), Noise, Yamux, gossipsub and bitswap.

Access control

By default only the creator may write. Pass an access controller when creating a database:

// Anyone may write.
orbitdb.WithAccessController(accesscontrollers.IPFS(accesscontrollers.IPFSOptions{Write: []string{"*"}}))

// Writers listed in a database of their own, changeable later.
db, _ := odb.OpenEvents(ctx, "team", orbitdb.WithAccessController(accesscontrollers.OrbitDB(accesscontrollers.OrbitDBOptions{})))
db.AccessController().(*accesscontrollers.OrbitDBAccessController).Grant(ctx, "write", otherIdentityID)

Every entry, local or from a peer, is checked against the access controller and its signature before it joins the log. The controllers also require the key that signed an entry to be the key of the identity the entry names.

Encryption
password := []byte("correct horse battery staple")
data, _ := encryption.NewSimple(password)
replication, _ := encryption.NewSimple(password)
db, _ := odb.OpenKeyValue(ctx, "secret", orbitdb.WithEncryption(oplog.Encryption{Data: data, Replication: replication}))

encryption.Simple reads and writes the format of @orbitdb/simple-encryption and uses a fresh random nonce for every message. Any oplog.Encrypter works.

Persistence

ipfs.Options.Repo keeps blocks (log entries, identities, manifests) in LevelDB; orbitdb.Options.Directory holds the keystore and each database's heads and index. With both set, databases survive restarts. Without Repo, blocks live in memory.

Demo

go run ./cmd/orbitdb-demo --dir /tmp/a
# in a second terminal, with the multiaddr and address the first printed:
go run ./cmd/orbitdb-demo --dir /tmp/b --peer /ip4/127.0.0.1/tcp/.../p2p/12D3... --db /orbitdb/zdpu...
> put greeting hello

Compatibility

Two kinds of tests pin the Go implementation to @orbitdb/core 4.0.0:

  • Golden vectors. tools/jsvectors runs scenarios on @orbitdb/core with the keys of its own test fixture keystore and records every identity, entry block, hash, traversal order, iterator range, manifest, access controller and encrypted block. Signatures are deterministic (RFC 6979), so the Go tests replay the same scenarios and require identical bytes: multi-writer joins, references, same-clock ties, every payload type and both encryption hooks included. CI regenerates the vectors to prove they are what @orbitdb/core produces.
  • Live interop. interop starts a JavaScript OrbitDB peer on Helia and replicates with it both ways: JS-created and Go-created databases, concurrent writers converging to identical logs, documents, write permissions, OrbitDB access controller grants and encrypted databases.
go test -race ./...                                             # unit + vector tests
(cd interop/js-peer && npm ci) && go test -tags interop ./interop/  # needs Node.js 22
(cd tools/jsvectors && npm ci && npm run check)                 # vectors are reproducible

Differences from the JavaScript API

  • Every call takes a context.Context; iteration uses iter.Seq2.
  • Missing keys and documents return errors wrapping databases.ErrNotFound instead of undefined.
  • Events are delivered on channels (db.Events().Subscribe()); emitting never blocks and never drops.
  • Iterator Amount of zero means "all" (JavaScript uses -1).
  • Documents keys are strings; a numeric key written by a JavaScript peer is looked up by its decimal form.

License

MIT

Documentation

Overview

Package orbitdb is a Go implementation of OrbitDB, a serverless, peer-to-peer database built on IPFS and libp2p.

It is a port of @orbitdb/core 4 and interoperates with it: identities, log entries, manifests, access controllers and the sync protocol are byte-compatible, so Go and JavaScript peers can open and replicate the same databases.

node, _ := ipfs.New(ctx, ipfs.Options{})
odb, _ := orbitdb.New(ctx, orbitdb.Options{IPFS: node})
defer odb.Stop()

kv, _ := odb.OpenKeyValue(ctx, "settings")
kv.Put(ctx, "theme", "dark")
fmt.Println(kv.Address()) // share this with peers

A peer that opens the same address replicates the database.

Example

Two peers replicate a keyvalue database. The second one only needs the address; everything else comes from the first over the network.

package main

import (
	"context"
	"fmt"
	"os"
	"time"

	orbitdb "github.com/orbitdb/go-orbitdb"
	"github.com/orbitdb/go-orbitdb/accesscontrollers"
	"github.com/orbitdb/go-orbitdb/ipfs"
)

func main() {
	ctx := context.Background()
	newPeer := func() (*ipfs.Node, *orbitdb.OrbitDB, func()) {
		dir, _ := os.MkdirTemp("", "orbitdb-example")
		node, err := ipfs.New(ctx, ipfs.Options{})
		if err != nil {
			panic(err)
		}
		odb, err := orbitdb.New(ctx, orbitdb.Options{IPFS: node, Directory: dir})
		if err != nil {
			panic(err)
		}
		return node, odb, func() {
			_ = odb.Stop()
			_ = node.Close()
			_ = os.RemoveAll(dir)
		}
	}
	nodeA, alice, closeA := newPeer()
	defer closeA()
	nodeB, bob, closeB := newPeer()
	defer closeB()
	if err := nodeB.Connect(ctx, nodeA.AddrInfo()); err != nil {
		panic(err)
	}

	// Alice creates a database anyone may write to.
	settings, err := alice.OpenKeyValue(ctx, "settings",
		orbitdb.WithAccessController(accesscontrollers.IPFS(accesscontrollers.IPFSOptions{Write: []string{"*"}})))
	if err != nil {
		panic(err)
	}
	if _, err := settings.Put(ctx, "theme", "dark"); err != nil {
		panic(err)
	}

	// Bob opens it by address and waits for the value to arrive.
	replica, err := bob.OpenKeyValue(ctx, settings.Address())
	if err != nil {
		panic(err)
	}
	for range 100 {
		if v, err := replica.Get(ctx, "theme"); err == nil {
			fmt.Println("bob sees theme =", v)
			break
		}
		time.Sleep(100 * time.Millisecond)
	}
}
Output:
bob sees theme = dark

Index

Examples

Constants

View Source
const DefaultDatabaseType = databases.EventsType

DefaultDatabaseType is the type Open creates when none is given.

Variables

This section is empty.

Functions

func IsValidAddress

func IsValidAddress(address string) bool

IsValidAddress reports whether address is an OrbitDB address: it starts with /orbitdb (or \orbitdb) and the rest is a base58btc CID. It accepts what @orbitdb/core's isValidAddress accepts.

func OpenAs

func OpenAs[T databases.Store](ctx context.Context, o *OrbitDB, address string, opts ...OpenOption) (T, error)

OpenAs opens a database and returns it as type T, e.g. OpenAs[*databases.KeyValue].

Types

type Address

type Address struct {
	// Hash is the base58btc CID of the database manifest.
	Hash string
}

Address is a database address, "/orbitdb/<manifest hash>".

func ParseAddress

func ParseAddress(address string) (Address, error)

ParseAddress parses a database address.

func (Address) String

func (a Address) String() string

String returns the address in its canonical "/orbitdb/<hash>" form.

type Manifest

type Manifest struct {
	Name string
	Type string
	// AccessController is the address of the access controller, e.g.
	// "/ipfs/zdpu...".
	AccessController string
	// Meta is optional metadata; nil means the manifest has no meta field.
	Meta any
}

Manifest describes a database. Its hash is the database address.

type OpenOption

type OpenOption func(*openConfig)

OpenOption configures Open.

func WithAccessController

func WithAccessController(factory accesscontrollers.Factory) OpenOption

WithAccessController sets the access controller of a new database. Defaults to accesscontrollers.IPFS with the local identity as sole writer. An existing database uses the controller in its manifest.

func WithDatabase

func WithDatabase(factory databases.Factory) OpenOption

WithDatabase opens the database with factory instead of the one registered for its type, e.g. databases.KeyValueIndexedFactory.

func WithEncryption

func WithEncryption(e oplog.Encryption) OpenOption

WithEncryption encrypts entries and/or payloads. Every peer must use the same encryption to read the database.

func WithEntryStorage

func WithEntryStorage(s storage.Storage) OpenOption

WithEntryStorage replaces the default entry storage.

func WithHeadsStorage

func WithHeadsStorage(s storage.Storage) OpenOption

WithHeadsStorage, WithEntryStorage and WithIndexStorage replace the database's default storages.

func WithIndexStorage

func WithIndexStorage(s storage.Storage) OpenOption

WithIndexStorage replaces the default index storage.

func WithMeta

func WithMeta(meta any) OpenOption

WithMeta sets the manifest metadata of a new database.

func WithReferencesCount

func WithReferencesCount(n int) OpenOption

WithReferencesCount sets how many older entries each new entry refers to; see databases.Params.ReferencesCount.

func WithSync

func WithSync(enabled bool) OpenOption

WithSync turns automatic syncing on (the default) or off.

func WithType

func WithType(typ string) OpenOption

WithType sets the database type ("events", "keyvalue", "documents" or a registered custom type). It applies when creating a database; an existing database keeps the type in its manifest unless this is set.

type Options

type Options struct {
	// IPFS is the node databases store blocks on and sync over. Required.
	IPFS *ipfs.Node
	// ID is the id the identity is created for. Defaults to a random id.
	ID string
	// Identity is used as is instead of creating one. It must have been
	// created by Identities (so its key is in the keystore).
	Identity *identitytypes.Identity
	// IdentityProvider creates the identity instead of the default
	// publickey provider.
	IdentityProvider providers.Provider
	// Identities is used instead of creating one over a keystore in
	// Directory/keystore.
	Identities *identities.Identities
	// Directory holds the keystore and the databases' local state.
	// Defaults to "./orbitdb".
	Directory string
}

Options configures New.

type OrbitDB

type OrbitDB struct {
	// contains filtered or unexported fields
}

OrbitDB opens and manages databases.

func New

func New(ctx context.Context, opts Options) (*OrbitDB, error)

New creates an OrbitDB instance.

func (*OrbitDB) Directory

func (o *OrbitDB) Directory() string

Directory returns the directory local state is kept in.

func (*OrbitDB) ID

func (o *OrbitDB) ID() string

ID returns the id the identity was created for.

func (*OrbitDB) IPFS

func (o *OrbitDB) IPFS() *ipfs.Node

IPFS returns the IPFS node.

func (*OrbitDB) Identities

func (o *OrbitDB) Identities() *identities.Identities

Identities returns the identities manager.

func (*OrbitDB) Identity

func (o *OrbitDB) Identity() *identitytypes.Identity

Identity returns the identity databases are written as.

func (*OrbitDB) Keystore

func (o *OrbitDB) Keystore() *keystore.KeyStore

Keystore returns the keystore.

func (*OrbitDB) Open

func (o *OrbitDB) Open(ctx context.Context, address string, opts ...OpenOption) (databases.Store, error)

Open opens the database at address, or creates a database named address if it is not an OrbitDB address. Opening an open database returns the same instance.

func (*OrbitDB) OpenDocuments

func (o *OrbitDB) OpenDocuments(ctx context.Context, address string, opts ...OpenOption) (*databases.Documents, error)

OpenDocuments opens or creates a documents database. To index documents by a field other than "_id", pass WithDatabase(databases.DocumentsFactory(databases.DocumentsOptions{IndexBy: field})).

func (*OrbitDB) OpenEvents

func (o *OrbitDB) OpenEvents(ctx context.Context, address string, opts ...OpenOption) (*databases.Events, error)

OpenEvents opens or creates an events database.

func (*OrbitDB) OpenKeyValue

func (o *OrbitDB) OpenKeyValue(ctx context.Context, address string, opts ...OpenOption) (*databases.KeyValue, error)

OpenKeyValue opens or creates a keyvalue database.

func (*OrbitDB) OpenKeyValueIndexed

func (o *OrbitDB) OpenKeyValueIndexed(ctx context.Context, address string, opts ...OpenOption) (*databases.KeyValueIndexed, error)

OpenKeyValueIndexed opens or creates a keyvalue database with a local index.

func (*OrbitDB) PeerID

func (o *OrbitDB) PeerID() peer.ID

PeerID returns the libp2p peer id of the node.

func (*OrbitDB) Stop

func (o *OrbitDB) Stop() error

Stop closes every open database, the keystore and the manifest store. It does not close the IPFS node.

Directories

Path Synopsis
Package accesscontrollers decides who may write to a database.
Package accesscontrollers decides who may write to a database.
cmd
orbitdb-demo command
Command orbitdb-demo is a small interactive OrbitDB peer.
Command orbitdb-demo is a small interactive OrbitDB peer.
Package databases implements the OrbitDB database types on top of the operation log: Events (an append-only event log), KeyValue, KeyValueIndexed and Documents.
Package databases implements the OrbitDB database types on top of the operation log: Events (an append-only event log), KeyValue, KeyValueIndexed and Documents.
Package encryption provides encryption modules for oplog.Encryption.
Package encryption provides encryption modules for oplog.Encryption.
Package identities creates, stores, signs with and verifies OrbitDB identities.
Package identities creates, stores, signs with and verifies OrbitDB identities.
identitytypes
Package identitytypes defines the Identity record that signs OrbitDB log entries, and its dag-cbor encoding.
Package identitytypes defines the Identity record that signs OrbitDB log entries, and its dag-cbor encoding.
providers
Package providers implements identity providers, which decide what an identity id is and vouch for the identity's signing key.
Package providers implements identity providers, which decide what an identity id is and vouch for the identity's signing key.
internal
block
Package block encodes and decodes OrbitDB's dag-cbor blocks.
Package block encodes and decodes OrbitDB's dag-cbor blocks.
queue
Package queue runs tasks one at a time, in the order they were added, like the p-queue (concurrency 1) instances @orbitdb/core uses to serialise sync messages.
Package queue runs tasks one at a time, in the order they were added, like the p-queue (concurrency 1) instances @orbitdb/core uses to serialise sync messages.
testutil
Package testutil holds fixtures shared by the package tests.
Package testutil holds fixtures shared by the package tests.
Package ipfs assembles the IPFS pieces OrbitDB runs on: a libp2p host, gossipsub for database update announcements, and a block service whose bitswap exchange fetches entries, identities and manifests from peers.
Package ipfs assembles the IPFS pieces OrbitDB runs on: a libp2p host, gossipsub for database update announcements, and a block service whose bitswap exchange fetches entries, identities and manifests from peers.
Package keystore manages the private keys OrbitDB identities sign with.
Package keystore manages the private keys OrbitDB identities sign with.
Package oplog implements OrbitDB's operation log: an append-only, signed Merkle-CRDT of entries.
Package oplog implements OrbitDB's operation log: an append-only, signed Merkle-CRDT of entries.
Package storage provides the key/value storage backends OrbitDB uses for log entries, heads, indexes, keys and identities.
Package storage provides the key/value storage backends OrbitDB uses for log entries, heads, indexes, keys and identities.
Package syncutils implements the OrbitDB sync protocol, which replicates a log between peers.
Package syncutils implements the OrbitDB sync protocol, which replicates a log between peers.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL