CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-orbitdb--core

Distributed p2p database on IPFS with automatic peer synchronization and conflict-free writes

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

access-controllers.mddocs/

Access Controllers

Access controllers provide a pluggable system for managing database permissions in OrbitDB. They determine who can write to databases, read from them, and perform administrative operations.

Capabilities

Access Controller System

Register and manage access controllers for different permission models.

/**
 * Registers a custom access controller
 * @param accessController Access controller implementation with type property
 * @throws "AccessController does not contain required field 'type'"
 * @throws "AccessController '${type}' already added"
 */
function useAccessController(accessController: AccessController): void;

interface AccessController {
  /** Unique type identifier for this access controller */
  type: string;
  /** Checks if an entry can be appended to the log */
  canAppend(entry: Entry, identityProvider: IdentityProvider): Promise<boolean>;
  /** Optional: grants capabilities to identities */
  grant?(capability: string, identity: string): Promise<void>;
  /** Optional: revokes capabilities from identities */
  revoke?(capability: string, identity: string): Promise<void>;
}

Usage Examples:

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

// Register a custom access controller
const MyAccessController = {
  type: 'my-custom-ac',
  async canAppend(entry, identityProvider) {
    // Custom permission logic
    return entry.identity === 'allowed-user-id';
  }
};

useAccessController(MyAccessController);

// Use the custom access controller
const db = await orbitdb.open('protected-db', {
  AccessController: MyAccessController
});

IPFS Access Controller

The default access controller that uses IPFS for permission management.

interface IPFSAccessController extends AccessController {
  type: 'ipfs';
  
  /**
   * Checks if an identity can append entries to the database
   * @param entry The entry being added
   * @param identityProvider Identity provider for verification
   * @returns Promise resolving to true if allowed
   */
  canAppend(entry: Entry, identityProvider: IdentityProvider): Promise<boolean>;
  
  /**
   * Grants write permission to an identity
   * @param capability The capability to grant (typically 'write')
   * @param identity The identity to grant permission to
   */
  grant(capability: string, identity: string): Promise<void>;
  
  /**
   * Revokes permission from an identity
   * @param capability The capability to revoke
   * @param identity The identity to revoke permission from
   */
  revoke(capability: string, identity: string): Promise<void>;
}

Usage Examples:

import { createOrbitDB, IPFSAccessController } from '@orbitdb/core';

const ipfs = await createHelia();
const orbitdb = await createOrbitDB({ ipfs });

// Create database with IPFS access controller (default)
const db = await orbitdb.open('my-db', {
  AccessController: IPFSAccessController
});

// Grant write access to another identity
await db.access.grant('write', 'another-peer-id');

// Check if current identity can write
const canWrite = await db.access.canAppend(someEntry, identityProvider);
console.log('Can write:', canWrite);

OrbitDB Access Controller

Enhanced access controller with OrbitDB-specific features.

interface OrbitDBAccessController extends AccessController {
  type: 'orbitdb';
  
  /**
   * Checks if an identity can append entries with OrbitDB-specific rules
   * @param entry The entry being added
   * @param identityProvider Identity provider for verification
   * @returns Promise resolving to true if allowed
   */
  canAppend(entry: Entry, identityProvider: IdentityProvider): Promise<boolean>;
}

Usage Examples:

import { createOrbitDB, OrbitDBAccessController } from '@orbitdb/core';

const ipfs = await createHelia();
const orbitdb = await createOrbitDB({ ipfs });

// Create database with OrbitDB access controller
const db = await orbitdb.open('enhanced-db', {
  AccessController: OrbitDBAccessController
});

// OrbitDB access controller provides enhanced permission checking
const entry = await createSomeEntry();
const canAppend = await db.access.canAppend(entry, orbitdb.identities);

Custom Access Controllers

Create custom access controllers for specific permission models.

/**
 * Template for custom access controller implementation
 */
interface CustomAccessController extends AccessController {
  type: string;
  canAppend(entry: Entry, identityProvider: IdentityProvider): Promise<boolean>;
  grant?(capability: string, identity: string): Promise<void>;
  revoke?(capability: string, identity: string): Promise<void>;
  /** Optional: validate access controller configuration */
  validate?(config: any): boolean;
  /** Optional: serialize access controller state */
  serialize?(): any;
  /** Optional: deserialize access controller state */
  deserialize?(data: any): void;
}

Usage Examples:

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

// Role-based access controller
const RoleBasedAccessController = {
  type: 'role-based',
  roles: new Map(), // identity -> role mapping
  permissions: new Map(), // role -> permissions mapping
  
  async canAppend(entry, identityProvider) {
    const identity = entry.identity;
    const role = this.roles.get(identity);
    const permissions = this.permissions.get(role);
    
    return permissions && permissions.includes('write');
  },
  
  async grant(capability, identity) {
    // Grant role to identity
    this.roles.set(identity, capability);
  },
  
  async revoke(capability, identity) {
    // Remove role from identity
    this.roles.delete(identity);
  },
  
  // Custom methods
  setRole(identity, role) {
    this.roles.set(identity, role);
  },
  
  defineRole(role, permissions) {
    this.permissions.set(role, permissions);
  }
};

// Register the custom access controller
useAccessController(RoleBasedAccessController);

// Use in database creation
const db = await orbitdb.open('role-based-db', {
  AccessController: RoleBasedAccessController
});

// Configure roles and permissions
db.access.defineRole('admin', ['read', 'write', 'delete']);
db.access.defineRole('user', ['read', 'write']);
db.access.setRole('user123', 'user');
db.access.setRole('admin456', 'admin');

Multi-Signature Access Controller

Example of a more complex access controller requiring multiple signatures.

const MultiSigAccessController = {
  type: 'multisig',
  requiredSignatures: 2,
  authorizedSigners: new Set(),
  pendingOperations: new Map(),
  
  async canAppend(entry, identityProvider) {
    const identity = entry.identity;
    
    // Check if signer is authorized
    if (!this.authorizedSigners.has(identity)) {
      return false;
    }
    
    const operationId = this.getOperationId(entry);
    const signatures = this.pendingOperations.get(operationId) || new Set();
    signatures.add(identity);
    
    if (signatures.size >= this.requiredSignatures) {
      // Enough signatures, allow operation
      this.pendingOperations.delete(operationId);
      return true;
    } else {
      // Store signature and wait for more
      this.pendingOperations.set(operationId, signatures);
      return false;
    }
  },
  
  getOperationId(entry) {
    // Create deterministic ID for the operation
    return `${entry.payload.op}-${entry.payload.key}-${JSON.stringify(entry.payload.value)}`;
  },
  
  addSigner(identity) {
    this.authorizedSigners.add(identity);
  },
  
  removeSigner(identity) {
    this.authorizedSigners.delete(identity);
  }
};

useAccessController(MultiSigAccessController);

Time-Based Access Controller

Access controller with time-based permissions.

const TimeBasedAccessController = {
  type: 'time-based',
  permissions: new Map(), // identity -> { start, end, capabilities }
  
  async canAppend(entry, identityProvider) {
    const identity = entry.identity;
    const permission = this.permissions.get(identity);
    
    if (!permission) {
      return false;
    }
    
    const now = Date.now();
    const isTimeValid = now >= permission.start && now <= permission.end;
    const hasCapability = permission.capabilities.includes('write');
    
    return isTimeValid && hasCapability;
  },
  
  async grant(capability, identity) {
    const existing = this.permissions.get(identity) || { capabilities: [] };
    if (!existing.capabilities.includes(capability)) {
      existing.capabilities.push(capability);
      this.permissions.set(identity, existing);
    }
  },
  
  // Custom methods
  grantTimeLimited(identity, capabilities, startTime, endTime) {
    this.permissions.set(identity, {
      start: startTime,
      end: endTime,
      capabilities: Array.isArray(capabilities) ? capabilities : [capabilities]
    });
  },
  
  extendPermission(identity, newEndTime) {
    const permission = this.permissions.get(identity);
    if (permission) {
      permission.end = newEndTime;
      this.permissions.set(identity, permission);
    }
  }
};

useAccessController(TimeBasedAccessController);

// Usage
const db = await orbitdb.open('time-limited-db', {
  AccessController: TimeBasedAccessController
});

// Grant write access for 1 hour
const oneHour = 60 * 60 * 1000;
db.access.grantTimeLimited(
  'temp-user-id',
  ['read', 'write'],
  Date.now(),
  Date.now() + oneHour
);

Access Controller Configuration

Configure access controllers when creating databases.

import { createOrbitDB, IPFSAccessController } from '@orbitdb/core';

const ipfs = await createHelia();
const orbitdb = await createOrbitDB({ ipfs });

// Configure access controller with initial permissions
const adminDb = await orbitdb.open('admin-db', {
  AccessController: IPFSAccessController({
    write: ['admin-identity-1', 'admin-identity-2'],
    admin: ['admin-identity-1']
  })
});

// Public read-only database
const publicDb = await orbitdb.open('public-db', {
  AccessController: IPFSAccessController({
    write: [orbitdb.identity.id], // Only creator can write
    read: ['*'] // Anyone can read
  })
});

Error Handling

Handle access control errors appropriately.

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

const ipfs = await createHelia();
const orbitdb = await createOrbitDB({ ipfs });

try {
  const db = await orbitdb.open('protected-db');
  await db.add('Some data');
} catch (error) {
  if (error.message.includes('Access denied')) {
    console.error('Permission denied: Cannot write to this database');
    console.log('Current identity:', orbitdb.identity.id);
  } else if (error.message.includes('AccessController')) {
    console.error('Access controller error:', error.message);
  } else {
    console.error('Unexpected error:', error.message);
  }
}

// Check permissions before attempting operations
const db = await orbitdb.open('some-db');
const testEntry = { /* mock entry */ };
const canWrite = await db.access.canAppend(testEntry, orbitdb.identities);

if (canWrite) {
  await db.add('Data');
} else {
  console.log('No write permission for current identity');
}

Integration with Database Operations

Access controllers are automatically invoked during database operations:

// When adding data, access controller checks permissions
const db = await orbitdb.open('my-db');

// This triggers access controller's canAppend method
await db.add('New data'); // May throw if access denied

// Same for other database operations
await db.put('key', 'value'); // For KeyValue databases
await db.del('key'); // For deletion operations

Access controllers provide fine-grained control over database permissions while maintaining the distributed and decentralized nature of OrbitDB.

Install with Tessl CLI

npx tessl i tessl/npm-orbitdb--core

docs

access-controllers.md

address-management.md

database-types.md

identity-system.md

index.md

oplog-system.md

orbitdb-factory.md

storage-backends.md

tile.json