# `MobDev.DistCookie`
[🔗](https://github.com/genericjam/mob_dev/blob/master/lib/mob_dev/dist_cookie.ex#L1)

The private distribution cookie for a project's development builds.

One random 256-bit cookie per app (keyed by its bundle id), shared by iOS,
Android and the Mac-side node, so concurrent `mob.connect` sessions attach
without restarting the app under different credentials. It lives in an
owner-only directory under `~/.mob/dist_cookies/`, reaches the app only at
deploy/launch time, and is never printed. Deploy also writes it to
`mob_dist_cookie` in the app's beams directory (`write_app_file!/2`), where
the app reads it at boot, so a relaunch outside mob_dev keeps it.
A hand-started node loads it with
`Node.set_cookie(MobDev.DistCookie.for_project!())` from the project, which
keeps it out of process arguments (`--cookie` would show it in `ps`).

Apps built against a mob from before MOB-49 still answer to the public
`:mob_secret`. Without an explicit cookie, `connect/2` tries the private
cookie first and falls back to that legacy one with a warning, so existing
apps stay reachable until they are redeployed.

# `app_file`

```elixir
@spec app_file() :: String.t()
```

Name of the cookie file in an app's beams directory. Android's `Mob.Dist`
and iOS's `mob_beam.m` read `$MOB_BEAMS_DIR/mob_dist_cookie` at boot when
the launch environment carries no cookie.

# `candidates`

```elixir
@spec candidates(atom() | String.t() | nil, (-&gt; atom())) :: [atom(), ...]
```

Cookies to try against a device node, in order.

An explicit cookie (`--cookie`) is the only candidate. Without one, the
project's private cookie comes first and the legacy public cookie second.
The first entry is also the right default cookie for the Mac-side node.

# `connect`

```elixir
@spec connect(node(), [atom(), ...]) :: {:ok, atom()} | :error
```

Connects to `node`, trying each cookie in `cookies` in order.

Returns `{:ok, cookie}` with the cookie that worked. An already connected
node answers with the cookie it was connected under, untouched: OTP reports
an existing connection as success without a handshake, so trying a
candidate on it would prove nothing and overwrite the cookie that did
authenticate. When no cookie works, the node's cookie is reset to the first
candidate: a per-node cookie also authenticates *incoming* connections
claiming that name, so the legacy public cookie must not stay set for a
node that did not need it.

# `for_project!`

```elixir
@spec for_project!() :: atom()
```

Returns the private cookie for the current project, creating it on first use.

# `parse!`

```elixir
@spec parse!(atom() | String.t()) :: atom()
```

Validates an explicit cookie given on the command line.

# `write_app_file!`

```elixir
@spec write_app_file!(String.t(), atom()) :: String.t()
```

Writes `cookie` to `dir/mob_dist_cookie`, owner-only, and returns the path.

For directories on this Mac that an app reads its BEAMs from: the iOS
simulator runtime dir, or the staging dir a physical-iPhone deploy copies to
the device. The cookie is written inside a fresh `0700` directory, made
`0600`, then renamed over any previous file: nobody else can open it at any
point (a descriptor opened on the parent before the `chmod` doesn't help,
since lookups check the directory's current mode), and a booting app never
sees half a file.

---

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