# Advanced Elixir cache configuration

Start with <.localized_link href="/guides/features/cache/elixir">the Elixir cache setup guide</.localized_link>.
The options below are only needed when your project has additional compilation
inputs, dependency exceptions or different checkout paths.

## Select dependencies

`cache: true` caches supported dependencies automatically. A keyword list has the
same automatic selection, with optional overrides:

```elixir
cache: [exclude: [:a_dependency_with_compile_time_side_effects]]
```

To limit caching to specific dependencies instead:

```elixir
cache: [dependencies: [:nimble_options, :nimble_parsec]]
```

`dependencies: :all` explicitly selects all supported dependencies;
`dependencies: []` selects none. `exclude:` takes precedence over either selection.
Omitting `cache`, or setting it to `false` or `nil`, disables caching.

Supported dependencies are fetched through Hex or Git and use the standard Mix
compiler pipeline with pure Elixir sources. Native/custom compilers, custom
tracers, source symlinks, umbrella roots and chains containing local dependencies
compile normally. Tuist's own bootstrap dependencies also compile normally.
Structural eligibility does not prove that arbitrary compilation code is hermetic.

## Declare additional inputs

Source trees, locked dependency identities, upstream sources and BEAMs,
explicit effective application configuration, compiler options, Mix environment
and target, and toolchain identity are included automatically. Dependency-specific
`env:` and `system_env:` enter the key and configuration is evaluated under that
environment. Application defaults enter through source inputs, not VM loading state.

For dependencies that read files outside their source tree or additional
environment variables at compile time, declare those inputs:

```elixir
cache: [
  inputs: ["schemas", "config/compiler-input.json"],
  env: ["YOUR_COMPILER_OPTION"]
]
```

Elixir macros and module bodies can read arbitrary files, environment, ambient
code paths, network responses and time. Tuist does not automatically discover
these reads. Changing undeclared inputs can produce stale outputs. Exclude
dependencies with time/network-dependent compilation or side effects that must
execute on every build; use `dependencies:` for an audited dependency allowlist
when you need tighter control.

Standard compiler/runtime variables (`ERL_COMPILER_OPTIONS`, `ELIXIR_ERL_OPTIONS`,
`ERL_AFLAGS`, `ERL_FLAGS`, `ERL_ZFLAGS`, `SOURCE_DATE_EPOCH`, `ERL_LIBS`, `MIX_PATH`
and `MIX_ARCHIVES`) are captured automatically. Toolchain identity includes the
Elixir build, full OTP version when available, ERTS, compiler bytecode, OS/version,
architecture, word size and byte order. Inputs are captured before each lookup
and checked again before publication, including changes from earlier compilations.

## Reuse across checkout paths

Checkout paths participate in keys by default. If your dependencies don't rely
on producer paths, opt into reuse between different checkout locations:

```elixir
cache: [portable: true]
```

Cached BEAMs retain producer debug/source paths and compile-time `__DIR__`,
`__ENV__.file` and `File.cwd!()` values. Portable reuse does not rewrite them.
Resource links point to the current source checkout. Upstream BEAM bytes also
enter keys and may embed paths, so reuse can require restoring the upstream
closure from the same cache rather than recompiling it independently.

## Storage and access

| Option | Default | Purpose |
| --- | --- | --- |
| `remote` | `true` | Use the shared remote cache after a local miss. |
| `upload` | `true` | Publish new bundles remotely. `false` still allows reads and local snapshots. |
| `path` | Tuist's cache directory, under `mix/dependencies/v1` | Choose a different local store. |
| `endpoint` | Discovered automatically | Override the account's HTTPS REAPI endpoint. |

`upload: false` controls client behavior, not authorization. Project cache
permissions are enforced by the server. HTTP is only allowed on loopback
(`localhost`, `127.0.0.1`, `::1`) for development and sends credentials/artifacts
unencrypted. Local snapshots and remote bundles are limited to 64 MiB; larger
artifacts compile without being cached. The local store has no automatic eviction;
remove its contents when needed. Remote artifacts use Kura's existing retention,
eviction and project-cleanup rules.

> [!IMPORTANT]
> Compiler manifests and BEAMs can embed producer paths and compile-time values,
> including secrets. Project cache readers can access those artifacts. Cache
> writers are trusted code producers: digest checks detect corruption, not
> malicious executable BEAMs. Don't expose cache-write credentials to untrusted
> PR jobs or compile untrusted dependencies with those credentials available.

## Troubleshoot missing hits

- Run `mix tuist.setup` after installing the package and commit the generated
  Mix configuration. Setup preserves other aliases and Tuist options; it refuses
  customized compile actions rather than silently removing them.
- Keep the generated compile alias. Direct `mix tuist.compile` task
  discovery can compile dependencies before caching can inspect them.
- Already-current outputs aren't cache hits and aren't published retrospectively.
  Clean just a supported dependency's build with `mix deps.clean NAME --build`
  after it has populated the cache, then compile again.
- Match `MIX_ENV`, `MIX_TARGET`, compiler settings and declared inputs. Path changes
  also change keys unless portable reuse is enabled.
- Unsupported dependencies appear as **Not cached**, not misses. They are excluded
  from hit rate. A build with no lookups has an unavailable rate, not 0%.
- Missing credentials, unavailable endpoints and corrupt snapshots fall back to
  compilation. Compiler failures still fail the build.
- CAS records can arrive after the build report. Refresh to see newly ingested
  transfers. Local hits and legacy builds have no remote CAS outputs.

## Manual compile integration

If setup cannot edit your project's configuration safely, integrate this alias
manually, preserving any custom compile actions:

```elixir
compile: [
  "deps.compile tuist_ex --include-children",
  "deps.loadpaths --no-compile --no-deps-check",
  "tuist.compile"
]
```

These steps compile and load Tuist before other dependencies, including on cold
parallel builds. The wrapper restores normal dependency checks afterward. The
same alias works with Hex, Git and path installations; no script path is needed.

## How it works

Elixir caching uses the shared REAPI ActionCache/CAS pipeline, authentication and
Kura-signed analytics used by Bazel, not a separate Mix storage service. A modeled
input key references an ActionResult containing the raw-byte SHA-256 digest of a
complete compilation bundle. Tuist verifies its files, application metadata,
declared BEAMs and SCM manifest before staged installation. Corrupt entries are
repaired after successful compilation.

Remote credentials/connections are resolved lazily; idle builds and local hits
don't contact remote endpoints. One session is closed on every exit, including
compiler failure. Client RPCs have a 30-second deadline. Mix remains responsible
for lock checks, local dependency compilation and unsupported work.

Dashboard latency/bytes preserve local versus remote provenance and distinguish
unavailable values from measured zero. Remote timing measures client CAS transfers,
while local timing includes verification and installation; neither includes
compilation. CAS Outputs uses successful Kura-signed transfers, not local copies
or action-input keys. Throughput is bytes divided by measured time; compressed
sizes are unavailable. Estimated compilation time avoided is the producer's
original compile duration, not measured end-to-end build savings.
