Elpe ↔ Bazel Remote Execution API (REAPI)
Elpe exposes its content-addressed store to Bazel/Buck2 via the standard
Remote Execution API. This lets any
REAPI client use Elpe as a --remote_cache (and, later, --remote_executor),
which is how we get a fine-grained “one action = one derivation” build graph for
free — without writing a per-build-system extractor.
Elpe’s own OCaml frontend keeps talking to the elpe.Elpe gRPC service; Bazel
talks to the REAPI services. Two frontends, one backend.
Digest function: BLAKE3
Elpe’s store is BLAKE3-addressed, so the REAPI digest of a blob is simply
blake3(content). GetCapabilities advertises only BLAKE3
(DigestFunction.Value.BLAKE3 = 9). Clients must be told to match:
bazel build --digest_function=BLAKE3. This is the single most important
interop point — a mismatch makes every digest wrong.
Vendored protos
The canonical .proto files live under proto/, fetched at pinned commits for
reproducibility:
remote-apis→.remote-apis-shagoogleapis→.googleapis-sha
build.rs compiles remote_execution.proto + bytestream.proto; protoc pulls
in the transitive imports from the same include root.
Status
Milestone 1 — remote cache: DONE, validated end-to-end against real Bazel
7.6.0 (2026-08-03). A bazel build --remote_cache=grpc://… --digest_function=blake3
uploads on the cold run, and a run with a fresh local output base reports
1 remote cache hit — Bazel fetched the action result + output blob from Elpe
over gRPC in BLAKE3. Reproduce with reapi/test/run.sh.
| Service | Methods | State |
|---|---|---|
| Capabilities | GetCapabilities | ✅ advertises BLAKE3 |
| ContentAddressableStorage | FindMissingBlobs, BatchUpdate/ReadBlobs | ✅ |
| ContentAddressableStorage | GetTree, Split/SpliceBlob | ⛔ unimplemented (not needed for caching) |
| ByteStream | Read, Write, QueryWriteStatus | ✅ |
| ActionCache | Get/UpdateActionResult | ✅ |
CAS blobs live at {store}/reapi/cas/{hex-blake3}; cached action results at
{store}/reapi/ac/{action-hash}_{size}. The CAS verifies every uploaded
blob against its claimed digest — it never trusts the client.
Milestone 2 — remote executor (not started): the Execution service, which
stages an Action’s input-root Merkle tree from CAS into Elpe’s existing
clone3/chroot sandbox (src/container.rs) and captures outputs as an
ActionResult.
Running
Unit tests (no network, real in-process gRPC, proves the BLAKE3 round-trip):
cargo test --lib reapi::
Standalone unprivileged cache server (no root, no PGP, no Ubuntu index):
cargo run --bin elpe-reapi -- --store /path/to/store --addr 127.0.0.1:50051
Full end-to-end against a real Bazel client (installs nothing; needs
bazel/bazelisk on PATH):
reapi/test/run.sh