Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
263 changes: 263 additions & 0 deletions docs/DataSync/peer-to-peer.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,263 @@
---
id: peer-to-peer
sidebar_position: 3
---

# Peer-to-Peer Sync

> Description - _Couchbase Lite for Ionic — Synchronizing data changes between devices in a peer-to-peer network_

:::note
All code examples are indicative only. They demonstrate the basic concepts and approaches to using a feature. Use them as inspiration and adapt these examples to best practice when developing applications for your platform.
:::

## Introduction

Couchbase Lite for Ionic provides API support for secure, bi-directional synchronization of data changes between mobile applications in a peer-to-peer network. This allows devices to synchronize data directly with each other without requiring a central server.

The **replicator** is designed to manage replication of documents and document changes between a source and a target database. In a peer-to-peer scenario, each device acts as both a client and a server, enabling direct data synchronization between devices.

The **replicator** is the **Active Peer** (client) and the **URL endpoint listener** is the **Passive Peer** (server). The active peer initiates the replication and the passive peer listens for incoming replication requests.

## Prerequisites

Before implementing peer-to-peer sync, ensure you have:

- Couchbase Lite for Ionic installed in your project
- Network connectivity between devices

## Basic Setup

### Creating a Listener

To set up a listener that other peers can connect to:

```typescript
import { URLEndpointListener } from 'cblite-js';

// Create and start a listener
const listener = await URLEndpointListener.create({
collections: [{
databaseName: 'mydb',
scopeName: '_default',
name: '_default'
}],
port: port, // Choose an available port
networkInterface: networkInterface, // '0.0.0.0' for all interfaces
});

await listener.start();
console.log(`Listener started on port ${listener.getPort()}`);
```

### Connecting to the Listener

To connect to the listener from another device:

```typescript
import { Replicator, ReplicatorConfiguration, URLEndpoint, ReplicatorType } from 'cblite-js';

const endpoint = new URLEndpoint('ws://192.168.1.100:4988/mydb');
const config = new ReplicatorConfiguration(endpoint);
config.addCollection(collection);
config.setReplicatorType(ReplicatorType.PUSH_AND_PULL);

const replicator = await Replicator.create(config);

// Monitor replication status
const token = await replicator.addChangeListener((change) => {
console.log('Status:', change.status.getActivityLevel());
if (change.status.getError()) {
console.error('Error:', change.status.getError());
}
});

await replicator.start();
```

## Authentication Options

### Basic Authentication

Basic authentication provides a simple username/password mechanism for securing access to your listener.

#### Server-Side Configuration

```typescript
import { URLEndpointListener } from 'cblite-js';

const listener = await URLEndpointListener.create({
// ... other listener config
authenticatorConfig: {
type: 'basic',
data: {
username: 'admin',
password: 'securepassword123'
}
}
});
```

#### Client-Side Configuration

```typescript
import { Replicator, ReplicatorConfiguration, URLEndpoint, BasicAuthenticator } from 'cblite-js';

const endpoint = new URLEndpoint('ws://192.168.1.100:4988/mydb');
const config = new ReplicatorConfiguration(endpoint);
// ... other config

// Set basic authentication
config.setAuthenticator(new BasicAuthenticator('admin', 'securepassword123'));

const replicator = await Replicator.create(config);
```

### Certificate-Based Authentication
:::note
Certificate-based authentication is not yet implemented in the current version.
:::

## TLS Configuration

TLS (Transport Layer Security) encrypts the connection between peers, ensuring that all data transmitted between devices is secure and cannot be intercepted by third parties. Couchbase Lite provides flexible TLS configuration options to meet various security requirements.

:::caution Development Only
For development and testing purposes, you may choose to disable TLS. **This is not recommended for production environments** as it leaves your data vulnerable to interception. To disable TLS, set `disableTLS: true` in your listener configuration.

```typescript
const listener = await URLEndpointListener.create({
// ... other config
disableTLS: true // Only for development!
});
```
:::

### Self-Signed Certificates

Self-signed certificates provide a quick way to enable TLS encryption without requiring a certificate authority (CA). They're ideal for development and testing, though production environments should consider using certificates from a trusted CA.

#### Server Configuration

When creating a self-signed certificate, you'll need to specify:

- `label`: A unique identifier for the certificate
- `expiration`: Certificate validity period (ISO 8601 format)

:::note
On iOS, the expiration date can be in the past without triggering an error. However, on Android, if the expiration date is in the past, an error will be thrown.
:::
Comment on lines +147 to +149
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It's good to provide platform-specific notes, but consider rephrasing this to be more proactive. Instead of stating that iOS doesn't throw an error, perhaps suggest a best practice of always ensuring the expiration date is valid regardless of the platform.

- `attributes`: Certificate metadata including organization details

Example configuration:

```typescript
import { URLEndpointListener } from 'cblite-js';

const listener = await URLEndpointListener.create({
// ... other config
tlsIdentityConfig: {
mode: 'selfSigned',
label: 'my-server-identity', // Unique identifier for this certificate
expiration: '2030-01-01T00:00:00.000Z', // Certificate expiration date (ISO 8601 format)
attributes: {
certAttrCommonName: 'My Server', // Server's common name (e.g., hostname)
certAttrOrganization: 'My Company', // Your organization name
certAttrOrganizationUnit: 'Mobile',
certAttrEmailAddress: 'admin@mycompany.com'
}
}
});
```

### Accepting Self-Signed Certificates on Client

When connecting to a server using a self-signed certificate, the client must be configured to trust it. This is typically needed in development and testing environments where you're using self-signed certificates instead of certificates from a trusted Certificate Authority (CA).

#### Client Configuration

```typescript
const config = new ReplicatorConfiguration(endpoint);
// ... other configuration options

// Configure the client to accept self-signed certificates
config.setAcceptOnlySelfSignedCerts(true);
```

### Using Pre-Existing Certificates

For production environments, you might want to use certificates signed by a trusted Certificate Authority (CA) or certificates generated by your organization's PKI. Couchbase Lite supports importing these certificates, though with platform-specific limitations.

:::note Platform Limitations
- **iOS**: Full support for importing existing certificates
- **Android**: Not supported in the current version
:::

#### iOS Implementation

On iOS, you can import existing PKCS#12 (.p12) certificates. This is useful when you need to use certificates issued by your organization's PKI or a public CA.

```typescript
import { URLEndpointListener } from 'cblite-js';

// The certificate should be in PKCS#12 format and Base64 encoded
const pkcs12Data = 'BASE64_ENCODED_PKCS12_CERTIFICATE';

const listener = await URLEndpointListener.create({
// ... other listener configuration
tlsIdentityConfig: {
mode: 'imported',
label: 'my-imported-identity', // Unique identifier for this identity
password: 'your-secure-password', // Password used to protect the PKCS#12 file
certBase64: pkcs12Data // Base64 encoded PKCS#12 certificate
}
});
```

Provide the pinned certificate to the client:

```typescript
import { Replicator, ReplicatorConfiguration, URLEndpoint } from 'cblite-js';

const pinnedCert = 'BASE64_ENCODED_PINNED_CERTIFICATE';

const config = new ReplicatorConfiguration(endpoint);
// ... other config

// Set pinned certificate
config.setPinnedServerCertificate(pinnedCert);
```

### TLS Identity Management

#### Deleting a TLS Identity

```typescript
try {
await URLEndpointListener.deleteIdentity({
label: 'my-server-identity'
});
} catch (error) {
console.error('Failed to delete identity:', error);
// On Android this is not supported and will throw an error
}
Comment on lines +242 to +243
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It would be helpful to provide a link to the documentation or a resource that explains why deleteIdentity is not supported on Android.

```

### Platform-Specific Notes

#### iOS
- Full TLS support including self-signed and imported certificates
- Uses secure keychain for certificate storage

#### Android
- Limited to self-signed certificates
- Cannot import existing certificates
- `deleteIdentity` is a no-op


## Security - Best Practices
- Always use TLS in production environments
- Never hardcode credentials in your application
- Use strong passwords for certificates
Comment on lines +260 to +261
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

It's good to recommend not hardcoding credentials. Consider adding a link to a resource that explains how to securely store credentials in a mobile application (e.g., using platform-specific keychains or secure storage).

- Rotate certificates periodically
- Validate server certificates on the client side
4 changes: 4 additions & 0 deletions docs/Queries/live-queries.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ const token = await query.addChangeListener((change) => {
});
```

:::note
Queries that use parameter expressions (e.g. $param) are not compatible with live queries and cannot be observed for changes.
:::

To stop receiving notifications, call `Query.removeChangeListener` with the token that was returned from the registration call. Regardless of the whether the API is synchronous or asynchronous, listeners will stop receiving notifications immediately:

#### Example 2. Stopping a Live Query - Change Listener
Expand Down
5 changes: 5 additions & 0 deletions docs/documents.md
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,11 @@ const token = collection.addDocumentChangeListener('user.john', async (change) =
// Remove the change listener when it is no longer needed
await collection.removeDocumentChangeListener(token);
```

:::note
Document and Collection Change Listeners are currently not supported on iOS
:::
Comment on lines +331 to +332
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

Consider adding a link to the relevant documentation or issue tracker that explains why Document and Collection Change Listeners are not supported on iOS.


## Document Expiration

Document expiration allows users to set the expiration date for a document. When the document expires, it is purged from the database. The purge is not replicated to Sync Gateway or Capella App Services.
Expand Down