09. rebar3 and mochi.lock
Two-level lockfile model
The Erlang bridge uses a two-level lockfile model, similar to how MEP-76 relates to Gemfile.lock and MEP-73 relates to Cargo.lock:
-
mochi.lock[[erlang-package]]table: the Mochi-level lock, recording the resolved package version, all three content hashes, the BEAM ingest hash, the shim hash, and the declared capabilities. This is the authoritative reproducibility anchor for the bridge. -
rebar3.lock(synthesised by the bridge duringmochi pkg lock): the rebar3-level lock, recording the same package versions in rebar3's native format. This file is generated frommochi.lockand is used only by therebar3 compilestep during the build. It is committed to the repository alongsidemochi.lock.
The Mochi lock is the primary; the rebar3 lock is derived. If the two drift (e.g., a hand-edit to rebar3.lock), mochi pkg lock --check fails: it recomputes the rebar3 lock from mochi.lock and diffs it.
mochi.lock erlang-package schema
[[erlang-package]]
name = "cowboy"
version = "2.12.0"
source = { kind = "hex", registry = "https://hex.pm" }
outer-sha256 = "a1b2c3d4e5f6..." # SHA-256 of the outer .tar (before extraction)
inner-sha256 = "f6e5d4c3b2a1..." # SHA-256 of contents.tar.gz
inner-sha512 = "9f8e7d6c5b4a..." # SHA-512 of contents.tar.gz (cross-verified with Hex.pm API)
beam-ingest-sha256 = "111aaa..." # SHA-256 of all Dbgi chunk bytes, sorted by module name
shim-sha256 = "222bbb..." # SHA-256 of erlang_shims/cowboy/shim.erl
capabilities-declared = ["net"]
dependencies = ["cowlib@~> 2.13", "ranch@~> 2.1"]
otp-app = "cowboy"
modules = ["cowboy", "cowboy_req", "cowboy_router", "cowboy_handler", "cowboy_websocket"]
rebar3.lock format
rebar3's lock file (rebar3.lock) is a list of Erlang terms:
[
{<<"cowboy">>, {hex, <<"cowboy">>, <<"2.12.0">>,
<<"a1b2c3d4e5f6...">>, %% inner-sha256 in hex
[<<"cowlib">>, <<"ranch">>],
<<"default">>}},
{<<"cowlib">>, {hex, <<"cowlib">>, <<"2.13.0">>,
<<"abcdef...">>,
[],
<<"default">>}},
{<<"ranch">>, {hex, <<"ranch">>, <<"2.1.0">>,
<<"fedcba...">>,
[],
<<"default">>}}
].
The bridge generates this file from [[erlang-package]] entries. The hex hash in rebar3.lock is the inner-sha256 value from mochi.lock, which matches the hash format rebar3 uses (it checksums the inner contents.tar.gz).
rebar.config synthesis
The rebar.config generated by the bridge for the build workspace:
{erl_opts, [debug_info]}.
{deps, [
{cowboy, "2.12.0"},
{hackney, "1.20.0"},
{jose, "1.11.0"}
]}.
{minimum_otp_vsn, "25"}.
{profiles, [
{mochi_bridge, [
{deps, []},
{erl_opts, [debug_info, {i, "include"}]}
]}
]}.
Each {name, "version"} dep uses the exact version from mochi.lock. The minimum_otp_vsn field comes from [erlang].otp-version in mochi.toml. The mochi_bridge profile is used when compiling the shim modules to avoid polluting the user's default profile.
--check mode
mochi pkg lock --check performs the following verifications for each [[erlang-package]] entry:
- Recompute
outer-sha256from the cached.tarfile in~/.cache/mochi/erlang-deps/. If the file is not in the cache, fail withcache_miss. - Extract
contents.tar.gzfrom the cached.tarand recomputeinner-sha256andinner-sha512. - Re-run the BEAM ingest on the cached
.beamfiles and recomputebeam-ingest-sha256. - Re-run the shim synthesiser (without writing files) and recompute
shim-sha256. - Regenerate the
rebar3.lockfrom[[erlang-package]]and diff it against the checked-inrebar3.lock.
Any mismatch in steps 1-5 causes a non-zero exit with a diagnostic message. The check is designed to be run in CI before the build step, ensuring the build is fully reproducible.
Content-addressed cache
The bridge caches downloaded packages in ~/.cache/mochi/erlang-deps/. The cache key is the outer-sha256 hex string. This means:
- Different packages with the same content hash share a cache entry.
- Packages are never re-downloaded if the hash matches.
- The cache is shared across all Mochi projects on the machine.
- A corrupted cache entry (hash mismatch) is detected at
mochi pkg lock --checktime.
The cache layout:
~/.cache/mochi/erlang-deps/
<outer-sha256-hex>/
package.tar (outer tarball, verbatim as downloaded)
contents.tar.gz (inner tarball, extracted)
ebin/ (compiled .beam files, extracted from contents)
src/ (Erlang source files, extracted)
The ebin/ directory is populated lazily: only when the BEAM ingest step reads .beam files for type extraction.
rebar3 version check
At mochi pkg lock time and at build time, the bridge runs rebar3 --version and parses the output (e.g., rebar 3.23.0 on Erlang/OTP 27 Erts 15.0). It checks the rebar3 version against the [erlang].rebar3-version constraint from mochi.toml. If the constraint is not met, the bridge fails with a rebar3_version_mismatch error. This prevents silent build failures from rebar3 API changes between versions.
Cross-references
- 06-hex-publish-flow for how the three hashes are computed.
- 08-port-bridge-protocol for the build workspace structure the lock drives.
- MEP-66 §3 for the normative
[[erlang-package]]schema. - MEP-57 for the broader lockfile model.