Active Peer
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.
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.
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:
ReplicatorType.pushAndPullfor bidirectional sync.ReplicatorType.pushfor push-only replication.ReplicatorType.pullfor pull-only replication.
Set ReplicatorConfiguration.continuous to true for long-lived
replication, or leave it false for a one-shot replication.
final config = ReplicatorConfiguration(target: target)
..replicatorType = ReplicatorType.pushAndPull
..continuous = true
..addCollection(collection);
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:
ReplicatorConfiguration.heartbeatReplicatorConfiguration.maxAttemptsReplicatorConfiguration.maxAttemptWaitTime
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:
ReplicatorConfiguration.pinnedServerCertificateto pin a specific server certificate.ReplicatorConfiguration.trustedRootCertificatesto trust a custom root certificate chain.
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:
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:
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.
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.
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:
ReplicatorActivityLevel.stoppedReplicatorActivityLevel.offlineReplicatorActivityLevel.connectingReplicatorActivityLevel.idleReplicatorActivityLevel.busy
Documents Pending Push​
Use Replicator.pendingDocumentIdsInCollection or
Replicator.isDocumentPendingInCollection to inspect whether local changes
still need to be pushed.
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.
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.
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.