Cluster client
ClusterClient lets a process outside the cluster talk to
actors inside. The external process isn’t a cluster member —
no gossip, no membership state — but it can tell and ask
cluster-internal actors via known receptionist contacts.
import { ClusterClient } from 'actor-ts';
const client = ClusterClient.create({ contacts: ['actor-ts://my-app@10.0.0.5:2552/system/receptionist'],});
// Send to a well-known cluster actor:client.send('/user/api/orders', { kind: 'place', ... });
// Or via receptionist by service key:client.sendByKey(ServiceKey.of<ApiMsg>('api'), { kind: 'list-orders' });When to use it
Section titled “When to use it”Three legitimate use cases:
- External services that don’t belong in the actor cluster but need to talk to it — a Python ML service that posts to a Kafka actor inside the cluster.
- Mobile / desktop clients via a bridge — a server-side bridge holds a ClusterClient + exposes a REST/WS API externally.
- Cross-cluster federation — two clusters where one needs to talk to specific actors in the other without merging.
For typical setups, everything is part of the cluster — ClusterClient is the escape hatch for cases where it can’t be.
How it works
Section titled “How it works”External process Cluster │ │ │ contact cluster receptionist │ via well-known path │ ├───────────────────────────►│ │ │ │ receptionist responds │ │ with known service refs │ │◄───────────────────────────┤ │ │ │ send messages to refs │ ├───────────────────────────►│The client:
- Connects to one or more contacts (known cluster nodes
running a
ClusterClientReceptionist). - Discovers available services via the receptionist.
- Routes messages to the right actors.
- Handles failover when contacts become unreachable — reconnects to another.
Configuration
Section titled “Configuration”interface ClusterClientSettings { contacts: string[]; // at least one cluster-node path reconnectIntervalMs?: number; acceptableHeartbeatPauseMs?: number;}contacts is the list of receptionist paths. The client
randomly picks one to connect to; on failure, falls back to
others.
For stable contact addresses, the cluster side typically
deploys a ClusterClientReceptionist on a fixed pod (or
sharded set) at a well-known path.
Server side — ClusterClientReceptionist
Section titled “Server side — ClusterClientReceptionist”import { ClusterClientReceptionist } from 'actor-ts';
system.actorOf( ClusterClientReceptionist.props({ cluster, role: 'frontend', // optional — restrict to specific nodes }), 'cluster-client-receptionist',);The receptionist exposes registered services to external clients. Register actors:
const receptionistRef = system.actorSelection('/user/cluster-client-receptionist');
receptionistRef.tell({ kind: 'register-service', key: ServiceKey.of<OrdersMsg>('orders'), ref: ordersActor,});External clients can then sendByKey('orders', msg).
Comparison with cluster-aware ActorRef
Section titled “Comparison with cluster-aware ActorRef”// Inside the cluster — actors talk directly:const ref = await system.actorSelection('actor-ts://my-app@host:2552/user/api').resolveOne();ref.tell(...);
// Outside the cluster — ClusterClient:const client = ClusterClient.create({ contacts: [...] });client.send('/user/api', ...);Differences:
- Inside: requires cluster membership. Refs propagate via gossip; you can hold long-lived refs.
- ClusterClient: no membership; refs are resolved per message via the receptionist.
When NOT to use it
Section titled “When NOT to use it”Where to next
Section titled “Where to next”- Refs across nodes — in-cluster ref semantics for comparison.
- Receptionist — the in-cluster service registry; ClusterClient builds on top.
- HTTP overview — the more common external-facing alternative.