or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

access-controllers.mdaddress-management.mddatabase-types.mdidentity-system.mdindex.mdoplog-system.mdorbitdb-factory.mdstorage-backends.md

oplog-system.mddocs/

0

# OpLog System

1

2

The OpLog (Operation Log) is OrbitDB's core data structure - an immutable, cryptographically verifiable, operation-based CRDT (Conflict-free Replicated Data Type) that enables distributed consensus without coordination. All OrbitDB databases are built on top of the OpLog.

3

4

## Capabilities

5

6

### Log Operations

7

8

The Log provides the fundamental operations for building distributed data structures.

9

10

```javascript { .api }

11

/**

12

* Appends an entry to the log

13

* @param entry The entry to append

14

* @returns Promise resolving to the entry hash

15

*/

16

append(entry: Entry): Promise<string>;

17

18

/**

19

* Joins this log with another log, merging their histories

20

* @param log The log to join with

21

* @returns Promise resolving to the merged log

22

*/

23

join(log: Log): Promise<Log>;

24

25

interface Log {

26

/** Current heads of the log (latest entries) */

27

heads: Entry[];

28

/** All entries in the log */

29

entries: Entry[];

30

/** Number of entries in the log */

31

length: number;

32

/** Unique identifier for this log */

33

id: string;

34

/** Access controller for this log */

35

accessController: AccessController;

36

}

37

```

38

39

**Usage Examples:**

40

41

```javascript

42

import { Log } from '@orbitdb/core';

43

44

// Logs are typically accessed through database instances

45

const ipfs = await createHelia();

46

const orbitdb = await createOrbitDB({ ipfs });

47

const db = await orbitdb.open('my-events');

48

49

// Access the underlying log

50

const log = db.log;

51

52

console.log('Log heads:', log.heads);

53

console.log('Total entries:', log.length);

54

55

// Add data (which creates log entries)

56

await db.add('Hello World');

57

console.log('New length:', log.length);

58

```

59

60

### Entry Structure

61

62

Log entries are the atomic units of data in OrbitDB, containing the operation, metadata, and cryptographic proofs.

63

64

```javascript { .api }

65

interface Entry {

66

/** Unique hash identifying this entry */

67

hash: string;

68

/** The operation payload */

69

payload: {

70

/** Operation type (ADD, PUT, DEL, etc.) */

71

op: string;

72

/** Operation key (null for some operations) */

73

key: string | null;

74

/** Operation value */

75

value: any;

76

};

77

/** Identity that created this entry */

78

identity: string;

79

/** Cryptographic signature */

80

sig: string;

81

/** Logical clock for ordering */

82

clock: Clock;

83

/** References to previous entries */

84

refs: string[];

85

/** Additional entry metadata */

86

meta?: object;

87

}

88

```

89

90

**Usage Examples:**

91

92

```javascript

93

// Entries are created automatically when you perform database operations

94

const db = await orbitdb.open('my-events');

95

const hash = await db.add('My event data');

96

97

// Find the entry by hash

98

const entries = db.log.entries;

99

const entry = entries.find(e => e.hash === hash);

100

101

console.log('Entry hash:', entry.hash);

102

console.log('Operation:', entry.payload.op);

103

console.log('Value:', entry.payload.value);

104

console.log('Creator:', entry.identity);

105

console.log('Clock:', entry.clock);

106

```

107

108

### Entry Creation and Verification

109

110

Entries can be created and verified independently of the database context.

111

112

```javascript { .api }

113

/**

114

* Creates a new log entry

115

* @param identity The identity creating the entry

116

* @param operation The operation data

117

* @param clock Optional logical clock

118

* @returns Promise resolving to new Entry

119

*/

120

Entry.create(

121

identity: Identity,

122

operation: { op: string; key?: string; value?: any },

123

clock?: Clock

124

): Promise<Entry>;

125

126

/**

127

* Verifies an entry's signature and integrity

128

* @param entry The entry to verify

129

* @returns Promise resolving to true if valid

130

*/

131

Entry.verify(entry: Entry): Promise<boolean>;

132

```

133

134

**Usage Examples:**

135

136

```javascript

137

import { Entry } from '@orbitdb/core';

138

139

// Create a custom entry (typically done internally)

140

const identity = orbitdb.identity;

141

const operation = { op: 'ADD', value: 'Custom data' };

142

const entry = await Entry.create(identity, operation);

143

144

// Verify entry integrity

145

const isValid = await Entry.verify(entry);

146

console.log('Entry is valid:', isValid);

147

```

148

149

### Logical Clocks

150

151

Logical clocks provide causal ordering for distributed operations without requiring synchronized time.

152

153

```javascript { .api }

154

interface Clock {

155

/** Unique identifier for the clock (usually identity ID) */

156

id: string;

157

/** Logical time value */

158

time: number;

159

}

160

161

/**

162

* Creates a new clock

163

* @param id Clock identifier

164

* @param time Initial time (default: 0)

165

* @returns New Clock instance

166

*/

167

Clock.create(id: string, time?: number): Clock;

168

169

/**

170

* Compares two clocks for ordering

171

* @param a First clock

172

* @param b Second clock

173

* @returns -1, 0, or 1 for less than, equal, or greater than

174

*/

175

Clock.compare(a: Clock, b: Clock): number;

176

177

/**

178

* Advances a clock

179

* @param clock Clock to advance

180

* @param other Optional other clock to sync with

181

* @returns New advanced clock

182

*/

183

Clock.tick(clock: Clock, other?: Clock): Clock;

184

```

185

186

**Usage Examples:**

187

188

```javascript

189

import { Clock } from '@orbitdb/core';

190

191

// Clocks are typically managed automatically

192

const db = await orbitdb.open('my-events');

193

await db.add('First event');

194

await db.add('Second event');

195

196

// Examine the logical clocks in entries

197

const entries = db.log.entries;

198

entries.forEach((entry, index) => {

199

console.log(`Entry ${index}:`, entry.clock);

200

});

201

202

// Create custom clocks (advanced usage)

203

const clock1 = Clock.create('peer1', 0);

204

const clock2 = Clock.create('peer2', 0);

205

const clock3 = Clock.tick(clock1); // Advances time to 1

206

```

207

208

### Default Access Controller

209

210

The OpLog includes a default access controller that allows all operations.

211

212

```javascript { .api }

213

/**

214

* Creates a default access controller that allows all operations

215

* @returns Promise resolving to access controller instance

216

*/

217

function DefaultAccessController(): Promise<AccessController>;

218

219

interface AccessController {

220

/** Checks if an entry can be appended (always returns true for default) */

221

canAppend(entry: Entry): Promise<boolean>;

222

}

223

```

224

225

**Usage Examples:**

226

227

```javascript

228

import { DefaultAccessController } from '@orbitdb/core';

229

230

// The default access controller is used automatically

231

// when no specific access controller is provided

232

const defaultAC = await DefaultAccessController();

233

console.log(await defaultAC.canAppend(someEntry)); // Always true

234

```

235

236

### Conflict Resolution

237

238

The OpLog system automatically handles conflicts when merging concurrent operations from different peers.

239

240

```javascript { .api }

241

interface ConflictResolution {

242

/** Resolves conflicts between concurrent entries */

243

resolve(entries: Entry[]): Entry[];

244

}

245

```

246

247

**Usage Examples:**

248

249

```javascript

250

// Conflict resolution happens automatically during log joins

251

const db1 = await orbitdb1.open('/orbitdb/shared-address');

252

const db2 = await orbitdb2.open('/orbitdb/shared-address');

253

254

// Both peers add concurrent entries

255

await db1.add('From peer 1');

256

await db2.add('From peer 2');

257

258

// When peers sync, conflicts are resolved automatically

259

await db1.sync(db2.log.heads);

260

const allEntries = await db1.all();

261

console.log('Merged data:', allEntries); // Contains both entries

262

```

263

264

### Log Synchronization

265

266

Logs can be synchronized between peers to maintain eventual consistency.

267

268

```javascript { .api }

269

/**

270

* Synchronizes this log with heads from another log

271

* @param heads Array of head entries from another log

272

* @returns Promise resolving when sync is complete

273

*/

274

sync(heads: Entry[]): Promise<void>;

275

276

/**

277

* Gets the difference between this log and another

278

* @param other Another log to compare with

279

* @returns Array of entries that are in other but not in this log

280

*/

281

difference(other: Log): Entry[];

282

```

283

284

**Usage Examples:**

285

286

```javascript

287

// Manual synchronization between database instances

288

const db1 = await orbitdb1.open('shared-db');

289

const db2 = await orbitdb2.open('shared-db');

290

291

// Add data to first database

292

await db1.add('Data from peer 1');

293

294

// Sync second database with first

295

await db2.sync(db1.log.heads);

296

297

// Both databases now have the same data

298

const data1 = await db1.all();

299

const data2 = await db2.all();

300

console.log('Synchronized:', JSON.stringify(data1) === JSON.stringify(data2));

301

```

302

303

### Advanced Log Operations

304

305

Access lower-level log operations for custom database implementations.

306

307

```javascript { .api }

308

interface Log {

309

/** Traverse log entries with optional filtering */

310

traverse(options?: {

311

amount?: number;

312

gt?: string;

313

gte?: string;

314

lt?: string;

315

lte?: string;

316

}): AsyncIterable<Entry>;

317

318

/** Get entries by their hashes */

319

get(hashes: string[]): Entry[];

320

321

/** Check if log contains specific entries */

322

has(hash: string): boolean;

323

324

/** Get log statistics */

325

stats(): {

326

length: number;

327

heads: number;

328

unique: number;

329

};

330

}

331

```

332

333

**Usage Examples:**

334

335

```javascript

336

const db = await orbitdb.open('my-events');

337

338

// Add some data

339

await db.add('Event 1');

340

await db.add('Event 2');

341

await db.add('Event 3');

342

343

const log = db.log;

344

345

// Traverse recent entries

346

for await (const entry of log.traverse({ amount: 2 })) {

347

console.log('Recent entry:', entry.payload.value);

348

}

349

350

// Check log statistics

351

const stats = log.stats();

352

console.log('Log stats:', stats);

353

354

// Check if specific entry exists

355

const hasEntry = log.has(someEntryHash);

356

console.log('Entry exists:', hasEntry);

357

```

358

359

### Custom Operations

360

361

When building custom database types, you can define custom operation types.

362

363

```javascript

364

// Example: Custom counter database operations

365

const CounterOperations = {

366

INCREMENT: 'INCREMENT',

367

DECREMENT: 'DECREMENT',

368

RESET: 'RESET'

369

};

370

371

// Custom database implementation

372

const CounterDB = () => async (params) => {

373

const database = await Database(params);

374

const { addOperation } = database;

375

376

const increment = async () => {

377

return addOperation({

378

op: CounterOperations.INCREMENT,

379

key: null,

380

value: 1

381

});

382

};

383

384

const decrement = async () => {

385

return addOperation({

386

op: CounterOperations.DECREMENT,

387

key: null,

388

value: -1

389

});

390

};

391

392

const reset = async () => {

393

return addOperation({

394

op: CounterOperations.RESET,

395

key: null,

396

value: 0

397

});

398

};

399

400

return {

401

...database,

402

increment,

403

decrement,

404

reset

405

};

406

};

407

```

408

409

## Error Handling

410

411

Handle common OpLog-related errors:

412

413

```javascript

414

try {

415

const db = await orbitdb.open('my-db');

416

await db.add('Some data');

417

} catch (error) {

418

if (error.message.includes('Access denied')) {

419

console.error('No permission to write to this database');

420

} else if (error.message.includes('Invalid entry')) {

421

console.error('Entry validation failed');

422

} else {

423

console.error('Unexpected error:', error.message);

424

}

425

}

426

```

427

428

The OpLog system provides the foundation for all OrbitDB operations, ensuring data integrity, conflict resolution, and eventual consistency across distributed peers.