Skip to content

Commit f6b1662

Browse files
Merge pull request #48 from DominikZajac/p2p-docs
feat: Add docs for P2P
2 parents 03e6622 + 9a9ce09 commit f6b1662

File tree

3 files changed

+272
-0
lines changed

3 files changed

+272
-0
lines changed

docs/DataSync/peer-to-peer.md

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,263 @@
1+
---
2+
id: peer-to-peer
3+
sidebar_position: 3
4+
---
5+
6+
# Peer-to-Peer Sync
7+
8+
> Description - _Couchbase Lite for Ionic — Synchronizing data changes between devices in a peer-to-peer network_
9+
10+
:::note
11+
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.
12+
:::
13+
14+
## Introduction
15+
16+
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.
17+
18+
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.
19+
20+
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.
21+
22+
## Prerequisites
23+
24+
Before implementing peer-to-peer sync, ensure you have:
25+
26+
- Couchbase Lite for Ionic installed in your project
27+
- Network connectivity between devices
28+
29+
## Basic Setup
30+
31+
### Creating a Listener
32+
33+
To set up a listener that other peers can connect to:
34+
35+
```typescript
36+
import { URLEndpointListener } from 'cblite-js';
37+
38+
// Create and start a listener
39+
const listener = await URLEndpointListener.create({
40+
collections: [{
41+
databaseName: 'mydb',
42+
scopeName: '_default',
43+
name: '_default'
44+
}],
45+
port: port, // Choose an available port
46+
networkInterface: networkInterface, // '0.0.0.0' for all interfaces
47+
});
48+
49+
await listener.start();
50+
console.log(`Listener started on port ${listener.getPort()}`);
51+
```
52+
53+
### Connecting to the Listener
54+
55+
To connect to the listener from another device:
56+
57+
```typescript
58+
import { Replicator, ReplicatorConfiguration, URLEndpoint, ReplicatorType } from 'cblite-js';
59+
60+
const endpoint = new URLEndpoint('ws://192.168.1.100:4988/mydb');
61+
const config = new ReplicatorConfiguration(endpoint);
62+
config.addCollection(collection);
63+
config.setReplicatorType(ReplicatorType.PUSH_AND_PULL);
64+
65+
const replicator = await Replicator.create(config);
66+
67+
// Monitor replication status
68+
const token = await replicator.addChangeListener((change) => {
69+
console.log('Status:', change.status.getActivityLevel());
70+
if (change.status.getError()) {
71+
console.error('Error:', change.status.getError());
72+
}
73+
});
74+
75+
await replicator.start();
76+
```
77+
78+
## Authentication Options
79+
80+
### Basic Authentication
81+
82+
Basic authentication provides a simple username/password mechanism for securing access to your listener.
83+
84+
#### Server-Side Configuration
85+
86+
```typescript
87+
import { URLEndpointListener } from 'cblite-js';
88+
89+
const listener = await URLEndpointListener.create({
90+
// ... other listener config
91+
authenticatorConfig: {
92+
type: 'basic',
93+
data: {
94+
username: 'admin',
95+
password: 'securepassword123'
96+
}
97+
}
98+
});
99+
```
100+
101+
#### Client-Side Configuration
102+
103+
```typescript
104+
import { Replicator, ReplicatorConfiguration, URLEndpoint, BasicAuthenticator } from 'cblite-js';
105+
106+
const endpoint = new URLEndpoint('ws://192.168.1.100:4988/mydb');
107+
const config = new ReplicatorConfiguration(endpoint);
108+
// ... other config
109+
110+
// Set basic authentication
111+
config.setAuthenticator(new BasicAuthenticator('admin', 'securepassword123'));
112+
113+
const replicator = await Replicator.create(config);
114+
```
115+
116+
### Certificate-Based Authentication
117+
:::note
118+
Certificate-based authentication is not yet implemented in the current version.
119+
:::
120+
121+
## TLS Configuration
122+
123+
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.
124+
125+
:::caution Development Only
126+
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.
127+
128+
```typescript
129+
const listener = await URLEndpointListener.create({
130+
// ... other config
131+
disableTLS: true // Only for development!
132+
});
133+
```
134+
:::
135+
136+
### Self-Signed Certificates
137+
138+
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.
139+
140+
#### Server Configuration
141+
142+
When creating a self-signed certificate, you'll need to specify:
143+
144+
- `label`: A unique identifier for the certificate
145+
- `expiration`: Certificate validity period (ISO 8601 format)
146+
147+
:::note
148+
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.
149+
:::
150+
- `attributes`: Certificate metadata including organization details
151+
152+
Example configuration:
153+
154+
```typescript
155+
import { URLEndpointListener } from 'cblite-js';
156+
157+
const listener = await URLEndpointListener.create({
158+
// ... other config
159+
tlsIdentityConfig: {
160+
mode: 'selfSigned',
161+
label: 'my-server-identity', // Unique identifier for this certificate
162+
expiration: '2030-01-01T00:00:00.000Z', // Certificate expiration date (ISO 8601 format)
163+
attributes: {
164+
certAttrCommonName: 'My Server', // Server's common name (e.g., hostname)
165+
certAttrOrganization: 'My Company', // Your organization name
166+
certAttrOrganizationUnit: 'Mobile',
167+
certAttrEmailAddress: 'admin@mycompany.com'
168+
}
169+
}
170+
});
171+
```
172+
173+
### Accepting Self-Signed Certificates on Client
174+
175+
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).
176+
177+
#### Client Configuration
178+
179+
```typescript
180+
const config = new ReplicatorConfiguration(endpoint);
181+
// ... other configuration options
182+
183+
// Configure the client to accept self-signed certificates
184+
config.setAcceptOnlySelfSignedCerts(true);
185+
```
186+
187+
### Using Pre-Existing Certificates
188+
189+
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.
190+
191+
:::note Platform Limitations
192+
- **iOS**: Full support for importing existing certificates
193+
- **Android**: Not supported in the current version
194+
:::
195+
196+
#### iOS Implementation
197+
198+
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.
199+
200+
```typescript
201+
import { URLEndpointListener } from 'cblite-js';
202+
203+
// The certificate should be in PKCS#12 format and Base64 encoded
204+
const pkcs12Data = 'BASE64_ENCODED_PKCS12_CERTIFICATE';
205+
206+
const listener = await URLEndpointListener.create({
207+
// ... other listener configuration
208+
tlsIdentityConfig: {
209+
mode: 'imported',
210+
label: 'my-imported-identity', // Unique identifier for this identity
211+
password: 'your-secure-password', // Password used to protect the PKCS#12 file
212+
certBase64: pkcs12Data // Base64 encoded PKCS#12 certificate
213+
}
214+
});
215+
```
216+
217+
Provide the pinned certificate to the client:
218+
219+
```typescript
220+
import { Replicator, ReplicatorConfiguration, URLEndpoint } from 'cblite-js';
221+
222+
const pinnedCert = 'BASE64_ENCODED_PINNED_CERTIFICATE';
223+
224+
const config = new ReplicatorConfiguration(endpoint);
225+
// ... other config
226+
227+
// Set pinned certificate
228+
config.setPinnedServerCertificate(pinnedCert);
229+
```
230+
231+
### TLS Identity Management
232+
233+
#### Deleting a TLS Identity
234+
235+
```typescript
236+
try {
237+
await URLEndpointListener.deleteIdentity({
238+
label: 'my-server-identity'
239+
});
240+
} catch (error) {
241+
console.error('Failed to delete identity:', error);
242+
// On Android this is not supported and will throw an error
243+
}
244+
```
245+
246+
### Platform-Specific Notes
247+
248+
#### iOS
249+
- Full TLS support including self-signed and imported certificates
250+
- Uses secure keychain for certificate storage
251+
252+
#### Android
253+
- Limited to self-signed certificates
254+
- Cannot import existing certificates
255+
- `deleteIdentity` is a no-op
256+
257+
258+
## Security - Best Practices
259+
- Always use TLS in production environments
260+
- Never hardcode credentials in your application
261+
- Use strong passwords for certificates
262+
- Rotate certificates periodically
263+
- Validate server certificates on the client side

docs/Queries/live-queries.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,10 @@ const token = await query.addChangeListener((change) => {
3737
});
3838
```
3939

40+
:::note
41+
Queries that use parameter expressions (e.g. $param) are not compatible with live queries and cannot be observed for changes.
42+
:::
43+
4044
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:
4145

4246
#### Example 2. Stopping a Live Query - Change Listener

docs/documents.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -326,6 +326,11 @@ const token = collection.addDocumentChangeListener('user.john', async (change) =
326326
// Remove the change listener when it is no longer needed
327327
await collection.removeDocumentChangeListener(token);
328328
```
329+
330+
:::note
331+
Document and Collection Change Listeners are currently not supported on iOS
332+
:::
333+
329334
## Document Expiration
330335

331336
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.

0 commit comments

Comments
 (0)