Platform-aware Erlang distribution startup.
On iOS, distribution is started at BEAM launch via flags in mobbeam.m
(-name mob_demo@127.0.0.1), so nothing extra is needed here. The
iOS node name is auto-suffixed per-simulator (from SIMULATOR_UDID)
and can be overridden via MOB_NODE_SUFFIX (forwarded by
mix mob.deploy --node-suffix via simctl's `SIMCTL_CHILD*`
mechanism, planned).
On Android, starting distribution at BEAM launch races with Android's hwui
thread pool initialization (~125ms window), corrupting an internal mutex and
causing a SIGABRT. The fix is to defer Node.start/2 until after the UI has
fully settled.
Additionally, mix mob.connect runs adb reverse tcp:4369 tcp:4369 to tunnel
Mac EPMD into the device. OTP's Node.start/2 would ordinarily spawn a local
epmd daemon that also tries to bind port 4369 — causing a port conflict and
crash. The fix: set start_epmd: false and wait for the ADB-tunnelled EPMD to
be reachable before calling Node.start/2. If the tunnel is not up within 10s
(standalone launch, no mix mob.connect), distribution is skipped gracefully.
The Android listener binds to loopback only. The Mac reaches it through the
adb forward mob_dev sets up, which connects to the device's own loopback,
so nothing on the phone's WiFi can reach the distribution port.
Cookie
The cookie is private per app: mix mob.deploy and mix mob.connect write
the project's managed cookie (kept by mob_dev under ~/.mob/dist_cookies/)
to $MOB_BEAMS_DIR/mob_dist_cookie in the app's private storage, and the
node uses it. An explicit :cookie other than :mob_secret takes precedence
(then pass the same value to mix mob.connect --cookie). :mob_secret, the
value generated apps used to embed, is public, so it is ignored. With neither
a custom nor a managed cookie the node gets a random one and can't be
attached until mob_dev restarts it.
Usage (in your app's start/0)
Mob.Dist.ensure_started(node: :"mob_demo@127.0.0.1")Options:
:node— node name atom, e.g.:"mob_demo@127.0.0.1"(required on Android):cookie— custom cookie atom (optional; see above):delay— ms to wait before starting dist on Android (default: 3_000)
Summary
Functions
Ensure Erlang distribution is running for the current platform.
Stop Erlang distribution and shut down EPMD.
Functions
@spec ensure_started(keyword()) :: :ok
Ensure Erlang distribution is running for the current platform.
- iOS: no-op (dist already started via BEAM args in mob_beam.m).
- Android: spawns a process that sleeps for
:delayms then callsNode.start/2+Node.set_cookie/1, listening on loopback only. Pins the dist port to:dist_port(default 9100) so mob_dev knows which port to forward.
Options:
:node— base node name atom (required on Android):cookie— custom cookie atom;:mob_secretis ignored (see the moduledoc):delay— ms to wait before starting dist (default: 3_000):dist_port— Erlang dist listen port (default: 9100)
Per-device node names (Android)
Mac's EPMD only allows one registration per name. Two phones running the
same app with the same hardcoded :node collide — the second to start
gets :nodistribution and silently runs without dist. To keep two or
more devices distinguishable, set the MOB_NODE_SUFFIX env var (the
Android shell launcher reads mob_node_suffix from the launch intent
extras and exports it). When present, the resolved node becomes
<base_name>_<suffix>@<host> — e.g. test_nif_android_zy22cr@127.0.0.1.
A launch from the home screen carries no intent extras, so mix mob.deploy
also writes the suffix and port it chose to $MOB_BEAMS_DIR/mob_dist
(suffix=<suffix> and port=<port>, one per line). Each is used when its
env var is unset or empty, so a launcher restart comes back under the name
and port the tooling already forwards. A missing or garbled file, or key,
falls back to the defaults.
@spec stop() :: :ok
Stop Erlang distribution and shut down EPMD.
Disconnects all connected nodes, stops the distribution listener, and terminates the local EPMD daemon if one was started by this node.
Intended for use after an OTA update session or when forming a Mob.Cluster
connection that should not persist. The app continues running normally after
calling stop/0 — only remote connectivity is removed.
Returns :ok whether or not distribution was running.
# OTA update session
Mob.Dist.ensure_started(node: :"my_app@127.0.0.1", cookie: session_cookie)
Node.connect(update_server_node)
# ... receive BEAMs ...
Mob.Dist.stop()
# Mob.Cluster — rotate cookie between sessions
Node.set_cookie(new_session_cookie) # no restart needed