Passive Peer
This feature is an Enterprise Edition feature.
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.
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.
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.
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.
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.
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:
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,
);
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.
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.
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:
- If
UrlEndpointListenerConfiguration.disableTlsistrue, client certificate authentication is not available. - If TLS is enabled and no
TlsIdentityis configured, the listener creates an anonymous self-signed identity automatically. - Set
UrlEndpointListenerConfiguration.readOnlytotrueif the passive peer should only allow pull replication from clients.
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.
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.
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.
await listener.stop();