What precompiled does not do yet, or does not protect against. Each item says what happens and what to do about it. Apart from the first item under Matching, none of them can make Mix use a wrong build: the worst case is a dependency that compiles anyway.

Matching

Your build environment is not recorded. Builds are made in a clean environment, and no build records the environment variables, PATH or tools of the machine it would replace. A dependency whose compile-time output depends on them (a Makefile, c_src, an env read in mix.exs or inside a compile-time function) can be served a build made without your values. Variables set in your shell and a global Mix.install(deps, system_env: ...) count the same. Rule 8 refuses only the case it can see: an env read at module level in lib/. Fix: list such a dependency in :except.

Trust

One key signs everything. The portal holds one online key for both the root and the batches. Whoever controls the portal host could ship code to every project and device that uses the library, until a library release ships a new root. Status: proposals to split the roles are in the compatibility repository's docs/PRECOMPILED_SIGNING_PROPOSALS.md. See Who you are trusting today.

A fresh machine can't spot an older batch. With no trust state yet (a fresh install, or a new :home), there is no stored sequence to compare against. An older batch, issued within the last 24 hours, still passes the freshness checks. Impact: small: the batch must still be signed and less than a day old.

A mirror can hold back a root rotation. A root is only adopted when the source serves it, so a mirror that withholds newer roots keeps a fresh :home on the root this library ships. Impact: that machine misses newer builds signed under the new root; nothing wrong installs.

_build is trusted. The provenance file (.mix/precompiled.json in a dependency's build dir) is how an installed build is told apart from a compiled one. Anyone who can write _build can forge it, but they could already change any compiled code there.

Mix.install

These options of Mix.install/2 are not applied to the precompiled step:

OptionWhat happensResult
:lockfilePassed to Mix, not used for the checkMix may re-resolve and recompile what was just installed
:config, :config_pathNot applied before the checkA dependency whose compile-time config they change misses, or is placed and then recompiled by Mix
MIX_INSTALL_RESTORE_PROJECT_DIRIgnored—

In every case the dependency ends up compiled, never wrong.

Nerves

Released nerves serves only pure_elixir builds. Nerves 1.x has no Nerves.build_plan/0, so the system isn't known. Use the fork for system builds; see Nerves.

System builds may have stripped beams. Protocols they define are not consolidated, and Dialyzer has no types for them. A run says how many.

The fork's hook does its own setup under precompiled.* tasks. The library does nothing under help, clean and precompiled.*, but the fork's nerves.init still runs its setup first. Fix: the fork's skip list should name precompiled.* too (a change on the fork's side).

On a device

See also Not supported in the device guide.

Offline bundles last 24 hours for now. A bundle that is good for longer needs a signed offline.json, and the root this library ships has no offline role yet. Until then: build the bundle the same day you install it.

An unsynced clock weakens a bundle's expiry check. A device without NTP judges a bundle's batch by its own clock, which only has to be later than this library's build time. A batch that has really expired can still pass. The batch must still be signed and not older than one the device already saw. This goes away once bundles carry a signed offline.json, which needs no clock.

Open handles are not "in use". Precompiled.uninstall/2 sees code that is running, not resources that are open, like a GPIO handle. Fix: close them before you uninstall.

Upgrading from an older version

Older installs are not rechecked. Builds installed by a version of this library from before provenance files carry none, so they are not rechecked. Fix: run mix deps.clean --build, or start from a clean _build. For Precompiled.Host.install/2, use a forced install.