Skip to content

0.7.7

Choose a tag to compare

@github-actions github-actions released this 01 Oct 15:52
· 88 commits to master since this release

Security

  • Every platform uses a private distribution cookie per app; mob_secret
    is only a fallback for old apps (MOB-49).
    The Mac-side node and every
    development app used the public cookie mob_secret, so anyone who could
    reach a dist port could run code on the phone or the Mac. mob_dev now keeps
    a random 256-bit cookie per app in ~/.mob/dist_cookies/ (owner-only) and
    hands it over at deploy/connect time: iOS in the launch environment, Android
    as a file in the app's private storage (sent over adb's stdin, never on a
    command line). An app built against a mob from before MOB-49 still answers
    only to mob_secret; mob.connect, mob.deploy, mob.push, mob.watch,
    mob.attest, mob.smoke, mob.trace_otp and the battery benches fall back
    to it with a warning. mob.deploy restarts such an app instead of
    hot-loading it, so updating mob and deploying (--native for iOS) moves
    it to the private cookie. --cookie now means "only this cookie". To attach
    by hand, load the cookie inside the VM
    (Node.set_cookie(MobDev.DistCookie.for_project!())) rather than passing
    it as --cookie, which would put it in the process arguments. See
    decisions/2026-09-30-private-dist-cookie-for-every-platform.md.

    Upgrade note: update mob_dev together with mob. mob 0.9.8 accepts only
    the private cookie this release hands over, so mob_dev 0.7.6 or older can't
    mix mob.connect (or deploy, push, watch) to an app built on mob 0.9.8.
    mob_dev 0.7.7 still reaches apps built on older mob through the
    mob_secret fallback; the first mix mob.deploy after updating mob
    (--native for iOS) moves the app to its private cookie.

Fixed

  • Tasks that pick devices on their own leave another agent's leased
    device alone (MOB-330).
    A bare mix mob.deploy installed and launched an
    app on an emulator another agent held through agent-device. When
    agent-device is on PATH, every selection the user did not name now skips
    devices another session has claimed (read from agent-device device status --json) and prints each one with the session holding it: single-device
    auto-selection and --all-devices / --all-physical in mix mob.deploy,
    mix mob.uninstall and mix mob.smoke, mix mob.connect without
    --device, mix mob.push / mix mob.watch, and the battery benches'
    device auto-detection. mix mob.connect also leaves the app running on a
    claimed simulator when it kills stale simulator apps, and mix mob.watch
    stops pushing to a device another session claims while it runs. An iPhone
    found only over the LAN is known by its IP while a claim names a UDID, so
    it is skipped whenever another session claims a device that could be an
    iPhone; name it with --device to use it. A claim is yours when its
    session equals AGENT_DEVICE_SESSION; with that unset, every claim is
    someone else's. A claimed device named with --device is still used, after
    a loud warning: naming it is the same consent --device gives for a phone.
    The device auto-selection picks is always printed. Without agent-device,
    selection is unchanged. See MobDev.DeviceLeases.
  • mix mob.plugins says when a plugin's signature doesn't verify (MOB-332).
    Such a plugin was listed as tier 0 … no manifest (regular dep) after a
    separate skipping <name>: invalid_signature line, or not at all when not
    activated. Its row now shows tier ?, what is wrong (not signed, invalid,
    no public key, legacy v1) and the fix: re-sign a local checkout, reinstall,
    ask the author, or acknowledge an unsigned plugin for development.
  • mix mob.plugin.trust writes one trusted plugin per line and shows a path
    dependency's real version (MOB-334).
    config :mob, :trusted_plugins was
    rewritten as one long line; it is now laid out as mix format would, sorted
    by name, so a trust change is a one-line diff. An existing one-line stanza
    is converted in place. The review showed version: (unset) for a path
    dependency; it now shows the version from the plugin's mix.exs (read, not
    evaluated) and the dependency's path.
  • mix mob.deploy's documentation matches what it does (MOB-333). The
    fast deploy hot-loads over Erlang distribution without a restart and only
    restarts when it has to write the BEAMs to the device; --dist-port
    defaults to the port derived from the device serial and app name, not
    9100 + index; the "Under the hood" commands are the ones the task runs.
  • The crypto shim no longer breaks concurrent deploys and test runs
    (MOB-339).
    Deployer.generate_crypto_shim wrote one shared
    $TMPDIR/mob_crypto_shim in place, so two runs on one machine could delete
    or truncate each other's crypto.beam mid-compile ("error writing file").
    It now compiles in a private staging directory and renames it into a
    directory named for the shim's content. Tests that timed out only under
    machine load wait on their work instead of a 5 s clock, or carry an
    explicit timeout for legitimately heavy work.
  • mix mob.smoke explains why a flow recorded on an iOS simulator fails
    its identity check (MOB-343).
    Such flows failed at their first tap with
    REPLAY_DIVERGENCE … nothing in the current tree carries the recorded identity. agent-device (seen on 0.21.1 and 0.21.19) records each step's
    identity (the # agent-device:target-v1 line) from its simulator
    accessibility bridge, but replay checks it against an XCTest snapshot. Inside
    a SwiftUI ScrollView, XCTest labels the content container with its first
    child's text and keeps it, so the recorded ancestry (scrollview) never
    matches the replayed one (other "☀️"). A plain SwiftUI app fails the same
    way, so it is not a mob bug. On an iOS simulator, an IDENTITY_MISMATCH
    failure now carries a hint with the sed command that deletes the flow's
    identity lines; replay then resolves the step by its selector. The README
    ("Smoke flows on devices") says to strip them after recording on a
    simulator.