Skip to main content

Passive Peer

Description — Configure a passive peer to accept active peer connections and synchronize changes over WebSockets.
Important

This feature is an Enterprise Edition feature.

caution

On iOS 14 and later, applications that access the local network must declare an NSLocalNetworkUsageDescription in their app bundle metadata so the system can prompt the user for permission.

Introduction​

A passive peer accepts incoming connections from an active peer and exposes one or more local collections for replication. In Couchbase Lite for Dart, passive peer behavior is provided by UrlEndpointListener and UrlEndpointListenerConfiguration.

This page focuses on listener setup. For the overall peer-to-peer model and shared transport concepts, see Data Sync Peer-to-Peer.

Configuration Summary​

Create a listener configuration with the local collections to expose, adjust network, TLS, and authentication settings as needed, then create and start the listener.

Example 1. Listener Configuration and Initialization
final config = UrlEndpointListenerConfiguration(
collections: [collection],
port: 55990,
enableDeltaSync: true,
authenticator: ListenerPasswordAuthenticator((username, password) {
return username == 'cbl-user-01' && password == 'secret';
}),
);

final listener = await UrlEndpointListener.create(config);
await listener.start();

This example leaves UrlEndpointListenerConfiguration.networkInterface unset so the listener accepts connections on all available interfaces. After the listener starts, use UrlEndpointListener.urls to discover the reachable URLs to advertise to active peers.

Device Discovery​

If the listener is reachable at a known host name or IP address, active peers can connect directly. Otherwise, advertise the listener through your own discovery mechanism, such as Bonjour, mDNS, or another application-level discovery channel.

After the listener starts, use UrlEndpointListener.urls to discover the actual URLs clients can use.

Initialize the Listener Configuration​

Start by choosing the local collections that should be available for replication. The collections list must not be empty, and all collections must belong to the same database.

Example 2. Specify the Local Collections
final config = UrlEndpointListenerConfiguration(
collections: [collection],
);

Set Port and Network Interface​

Port Number​

If you do not specify UrlEndpointListenerConfiguration.port, Couchbase Lite assigns an available port when the listener starts. Specify a port when other peers need to know it in advance.

Example 3. Specify a Listener Port
final config = UrlEndpointListenerConfiguration(
collections: [collection],
port: 55990,
);

Network Interface​

Use UrlEndpointListenerConfiguration.networkInterface to bind the listener to a specific IP address or interface name. If you leave it unset, the listener accepts connections on all available interfaces.

Example 4. Specify a Network Interface
final config = UrlEndpointListenerConfiguration(
collections: [collection],
networkInterface: 'en0',
);

Delta Sync​

Set UrlEndpointListenerConfiguration.enableDeltaSync to true to allow delta sync with compatible active peers. Delta sync is disabled by default.

Example 5. Enable Delta Sync
final config = UrlEndpointListenerConfiguration(
collections: [collection],
enableDeltaSync: true,
);

TLS Security​

Enable or Disable TLS​

TLS is enabled by default. If you keep TLS enabled, active peers connect using wss:// URLs. If you set UrlEndpointListenerConfiguration.disableTls to true, the listener uses clear-text ws:// URLs instead.

Disabling TLS is useful for isolated development environments, but it is not recommended for production deployments.

Configure TLS Identity for Listener​

If you do not specify UrlEndpointListenerConfiguration.tlsIdentity, Couchbase Lite generates an anonymous self-signed identity when the listener starts. That gives you encryption, but not a certificate chain that a client can validate as a trusted server identity.

To provide a stable server identity, create a TlsIdentity explicitly:

Example 6. Create and Use a TLS Identity
final tlsIdentity = await TlsIdentity.createIdentity(
keyUsages: {KeyUsage.serverAuth},
attributes: const CertificateAttributes(
commonName: 'travel-sample-listener',
),
expiration: DateTime.utc(2100),
);

final config = UrlEndpointListenerConfiguration(
collections: [collection],
tlsIdentity: tlsIdentity,
);
note

Persisted TLS identities created with a label are not supported on Android or Linux. They are supported on Apple platforms and Windows.

Authenticating the Client​

Client authentication is optional. If you do not configure an authenticator, any active peer that can reach the listener and satisfy the TLS requirements may connect.

Use Basic Authentication​

Use ListenerPasswordAuthenticator when the active peer should present a username and password through BasicAuthenticator.

Example 7. Require Username and Password
final config = UrlEndpointListenerConfiguration(
collections: [collection],
authenticator: ListenerPasswordAuthenticator((username, password) {
return username == 'cbl-user-01' && password == 'secret';
}),
);

Use Client Certificate Authentication​

Use ListenerCertificateAuthenticator when active peers should present a client certificate. You can either verify certificates in a handler or trust certificates issued by a specific root chain.

Example 8. Trust a Specific Client Certificate Chain
final clientIdentity = await TlsIdentity.createIdentity(
keyUsages: {KeyUsage.clientAuth},
attributes: const CertificateAttributes(commonName: 'Client A'),
expiration: DateTime.utc(2100),
);

final config = UrlEndpointListenerConfiguration(
collections: [collection],
authenticator: ListenerCertificateAuthenticator.fromRoots(
clientIdentity.certificates,
),
);

The Impact of TLS Settings​

TLS and authentication settings interact in a few important ways:

Start Listener​

Create the listener with UrlEndpointListener.create, then call UrlEndpointListener.start. The resolved port, URLs, and TLS identity are only available after the listener has started.

Example 9. Create and Start a Listener
final listener = await UrlEndpointListener.create(config);
await listener.start();

print(listener.port);
print(listener.urls);

Monitor Listener​

Use UrlEndpointListener.connectionStatus to inspect how many peers are currently connected and how many are actively replicating.

Example 10. Inspect Listener Status
final status = listener.connectionStatus;

print('Connected peers: ${status.connectionCount}');
print('Active replications: ${status.activeConnectionCount}');

Stop Listener​

Call UrlEndpointListener.stop to stop accepting new connections and close existing peer-to-peer sessions.

Example 11. Stop a Listener
await listener.stop();