Skip to main content

Active Peer

Description — Configure an active peer to connect to a passive peer and replicate changes over WebSockets.
Important

This feature is an Enterprise Edition feature.

Introduction​

An active peer is the side of a peer-to-peer replication that initiates the connection to a passive peer. In Couchbase Lite for Dart, you implement the active peer with Replicator and point it at a passive peer's UrlEndpointListener.

This page focuses on the replicator-specific setup. For the broader peer-to-peer model, listener behavior, and transport-level constraints, see Data Sync Peer-to-Peer.

Configuration Summary​

Create a ReplicatorConfiguration for the remote listener, add the collection you want to sync, configure authentication and TLS validation as needed, create the replicator, and start it.

Example 1. Replication Configuration and Initialization
final config = ReplicatorConfiguration(
target: UrlEndpoint(
Uri.parse('wss://listener.example.com:55990/travel-sample'),
),
acceptOnlySelfSignedServerCertificate: true,
authenticator: BasicAuthenticator(
username: 'cbl-user-01',
password: 'secret',
),
)
..replicatorType = ReplicatorType.pushAndPull
..continuous = true
..addCollection(collection);

final replicator = await Replicator.create(config);
await replicator.start();

Device Discovery​

Before the active peer can connect, it needs the passive peer's WebSocket URL. That can come from:

  • A well-known hostname or IP address.
  • A discovery phase such as Bonjour or another zero-config protocol.
  • An out-of-band exchange in your application.

However the URL is obtained, pass it to UrlEndpoint.

Configure Replicator​

Configure Target​

Use ReplicatorConfiguration with a UrlEndpoint target. The URL must use ws:// or wss://, and the path must identify the remote database name that the passive peer exposes.

Example 2. Configure the Target Endpoint
final target = UrlEndpoint(
Uri.parse('wss://listener.example.com:55990/travel-sample'),
);

final config = ReplicatorConfiguration(target: target)..addCollection(
collection,
);

Use wss:// when TLS is enabled on the listener, which is the default.

Sync Mode​

Set ReplicatorConfiguration.replicatorType to choose the replication direction:

Set ReplicatorConfiguration.continuous to true for long-lived replication, or leave it false for a one-shot replication.

Example 3. Configure Replication Direction and Mode
final config = ReplicatorConfiguration(target: target)
..replicatorType = ReplicatorType.pushAndPull
..continuous = true
..addCollection(collection);
tip

Unless you have a specific reason not to, prefer a single ReplicatorType.pushAndPull replicator over separate push and pull replicators.

Retry Configuration​

The active peer automatically retries transient failures with exponential backoff. You can tune the behavior with:

Example 4. Configure Retry Behavior
final config = ReplicatorConfiguration(target: target)
..heartbeat = const Duration(seconds: 120)
..maxAttempts = 6
..maxAttemptWaitTime = const Duration(minutes: 2)
..addCollection(collection);

If you leave these values unset, Couchbase Lite uses its default retry logic.

Authenticating the Listener​

By default, the active peer accepts listener certificates that validate against the platform trust store. For peer-to-peer development setups, it is common to use self-signed certificates and set ReplicatorConfiguration.acceptOnlySelfSignedServerCertificate to true.

For stricter validation, configure one of these instead:

Example 5. Pin the Listener Certificate
final config = ReplicatorConfiguration(
target: target,
pinnedServerCertificate: DerData(listenerCertificateBytes),
)..addCollection(collection);

Client Authentication​

The passive peer can require the active peer to authenticate itself. Couchbase Lite for Dart supports two client-side options for peer-to-peer sync:

Use BasicAuthenticator when the listener uses ListenerPasswordAuthenticator:

Example 6. Use Basic Authentication
final config = ReplicatorConfiguration(
target: target,
acceptOnlySelfSignedServerCertificate: true,
authenticator: BasicAuthenticator(
username: 'cbl-user-01',
password: 'secret',
),
)..addCollection(collection);

Use ClientCertificateAuthenticator when the listener authenticates client certificates:

Example 7. Use Client Certificate Authentication
final clientIdentity = await TlsIdentity.createIdentity(
keyUsages: {KeyUsage.clientAuth},
attributes: const CertificateAttributes(commonName: 'Client A'),
expiration: DateTime.utc(2100),
);

final config = ReplicatorConfiguration(
target: target,
acceptOnlySelfSignedServerCertificate: true,
authenticator: ClientCertificateAuthenticator(clientIdentity),
)..addCollection(collection);

Initialize Replicator​

Use Replicator.create to construct a replicator from the configuration. The replicator runs asynchronously after you call Replicator.start.

Example 8. Create and Start a Replicator
final replicator = await Replicator.create(config);
await replicator.start();

Monitor Sync​

Use Replicator.changes or Replicator.addChangeListener to observe status changes. The replicator reports activity through ReplicatorStatus.activity, progress through ReplicatorStatus.progress, and any terminal or transient error through ReplicatorStatus.error.

Example 9. Monitor Replication State
await for (final change in replicator.changes()) {
final status = change.status;

switch (status.activity) {
case ReplicatorActivityLevel.connecting:
case ReplicatorActivityLevel.busy:
print('Replicating: ${status.progress}');
break;
case ReplicatorActivityLevel.idle:
print('Connected and idle');
break;
case ReplicatorActivityLevel.offline:
print('Temporarily offline: ${status.error}');
break;
case ReplicatorActivityLevel.stopped:
print('Stopped: ${status.error}');
break;
}
}

The possible activity states are:

Documents Pending Push​

Use Replicator.pendingDocumentIdsInCollection or Replicator.isDocumentPendingInCollection to inspect whether local changes still need to be pushed.

Example 10. Check Pending Documents
final pendingIds = await replicator.pendingDocumentIdsInCollection(collection);
final isPending = await replicator.isDocumentPendingInCollection(
'hotel-123',
collection,
);

Stop Sync​

Call Replicator.stop to stop the active peer. Stopping is asynchronous, so continue to monitor the status until the activity reaches ReplicatorActivityLevel.stopped.

Example 11. Stop a Replicator
await replicator.stop();

Conflict Resolution​

Conflict resolution is configured per collection with CollectionConfiguration. If you do not provide a custom resolver, Couchbase Lite uses DefaultConflictResolver.

Example 12. Configure a Conflict Resolver
final config = ReplicatorConfiguration(target: target)
..addCollection(
collection,
CollectionConfiguration(
conflictResolver: ConflictResolver.from((conflict) {
return conflict.localDocument ?? conflict.remoteDocument;
}),
),
);

Delta Sync​

Delta sync can reduce bandwidth usage by sending only the changed parts of a document. For peer-to-peer replication, the passive peer must allow delta sync by setting UrlEndpointListenerConfiguration.enableDeltaSync to true.

The active peer does not need extra configuration beyond using a normal replicator against that listener.