# `Mob.Dist`
[🔗](https://github.com/genericjam/mob/blob/master/lib/mob/dist.ex#L1)

Platform-aware Erlang distribution startup.

On iOS, distribution is started at BEAM launch via flags in mob_beam.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.

## App-chosen distribution config (init arguments)

Flags such as `-proto_dist inet_tls` and `-ssl_dist_optfile <path>` are
Erlang *init* arguments. An app sets them for its next launch with
`Mob.InitArgs.write/1`; both launchers pass them in development **and
release** builds, so a shipped app can carry its own distribution setup.

- **Android** never starts distribution at boot, init arguments or not: the
  hwui race above applies to any dist started from argv. Write the transport
  flags as init arguments, then start the app-owned node with `Node.start/2`
  after the UI is ready. Do **not** use `ensure_started/1` for a shipped
  listener: it is specifically the loopback + ADB development link described
  above and waits for the Mac's forwarded EPMD.
- **iOS**: if the init arguments contain `-name` or `-sname`, the launcher
  leaves out its own development dist flags (`-name`, `-setcookie`, the
  `inet_dist_*` kernel settings), so the app owns distribution and
  `mix mob.connect` won't find the node under mob's name. Without either
  flag, development builds still start mob's own node, now with the app's
  other init arguments (e.g. `-proto_dist`) applied to it, so mob_dev can
  only connect with matching settings. Release builds add no dist flags and
  run no in-process EPMD, so an app naming its node there needs an
  EPMD-less setup (e.g. `-start_epmd false -erl_epmd_port <port>`).

## 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)

# `ensure_started`

```elixir
@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 `:delay` ms then calls
  `Node.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_secret` is 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.

# `stop`

```elixir
@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

---

*Consult [api-reference.md](api-reference.md) for complete listing*
