6.7.1 Serving Dependencies Inside the Organization
A shared dependency service controls how Bazel learns about dependencies and obtains their sources. It is not one cache with several names. A module registry, source mirror, repository cache, remote downloader, and vendor directory sit at different points in dependency intake, so each one removes a different external dependency and creates a different operational boundary.
Start with the intake path
When Bazel resolves a Bzlmod graph, it first needs module metadata. An index registry supplies versioned MODULE.bazel files and source.json records. The latter tells Bazel how to obtain a module's source; the registry does not need to host the source archive itself.1 After resolution, repository rules and module extensions materialize external repositories. Only then can ordinary build analysis and action execution consume targets from those repositories.
That sequence gives you a useful service map:
| Mechanism | What it serves or controls | What it does not establish |
|---|---|---|
| Bzlmod registry | Module metadata and source-fetch instructions | Availability of the source objects named by those instructions |
Source mirror or --distdir | Archives addressed by repository downloads | Module selection or arbitrary repository-rule output |
| Repository cache | Reusable downloaded values, and in current Bazel a separate shareable cache of fetched repository directories | Action results or build output artifacts |
| Remote downloader | A centralized proxy for supported repository downloads | General execution of repository rules on a service |
| Vendor mode | A workspace-selected local copy of external repositories and fetched registry files | Automatic completeness for every configuration, platform, or subcommand |
This path is separate from the action cache and content-addressable store described in 6.2.1 How Remote Cache Keys Work. A repository archive may eventually contribute inputs to many actions, but reusing that archive is not an ActionResult lookup. Treating the two as one cache obscures ownership, retention, credentials, and failure diagnosis.
Give each dependency layer one job
An internal registry is the right control point when the organization needs to govern which Bazel modules and versions are discoverable. Bazel's index registry can be a local directory or a static HTTP service. Multiple --registry values are consulted in order, and setting the flag replaces the implicit Bazel Central Registry unless you add the BCR explicitly.1 3.1.8 Registry Structure & Custom Registries covers the format and declaration details; at the service layer, the important question is which registry is authoritative and whether any later registry is an intentional fallback.
A registry can also direct archive requests through mirrors. Its bazel_registry.json may list mirror prefixes; Bazel constructs mirror URLs and tries them before the original source URL. An archive entry can additionally carry its own backup mirror URLs and an integrity checksum.1 This separates two responsibilities: the registry vouches for metadata and expected identity, while the object service supplies bytes matching that identity.
--distdir is a smaller mechanism. It tells Bazel to search one or more local directories for archives before downloading them from the network.2 It can be useful for a prepared CI image or mounted artifact volume, but it is not a registry and does not proxy arbitrary repository behavior.
The repository cache removes repeated downloads across Bazel workspaces on the same host or on a deliberately shared filesystem. In Bazel 9.1.0, --repository_cache stores downloaded values obtained while fetching external repositories. Bazel also exposes --repo_contents_cache for fetched repository directories shareable across workspaces; by default that directory lives below the repository cache.2 These are persistent optimization layers, not the declaration of which dependency is allowed.
A remote downloader centralizes supported fetches behind a Remote Asset API endpoint. In Bazel 9.1.0 the endpoint flag remains named --experimental_remote_downloader, so an infrastructure design must version-label it rather than present it as an unchanging stable interface. Bazel also exposes a downloader configuration with allow, block, and rewrite directives. Local fallback is a separate, disabled-by-default experimental control.2 That separation matters: a proxy outage and a policy denial should not accidentally take the same path.
Vendor mode shifts selected external repositories into a workspace-controlled directory. Bazel can vendor dependencies reachable from target patterns, one named repository, or the transitive external graph. It also vendors registry files fetched during module resolution. The selection is configuration-sensitive, and some Bazel subcommands have tool dependencies that are not reachable from ordinary build targets, so a successful bazel vendor //... is not universal closure evidence.3 3.1.9 Offline / Air-Gapped Builds explains the developer workflow; 6.7.2 Operating Bazel Builds in Air-Gapped Environments extends it into fresh-state proof for an environment with no public network.
Compose the minimum architecture
Do not begin with a box called "the internal Bazel dependency service." Begin with requirements and add only the layer that satisfies each one.
- To control module discovery, operate an internal index registry.
- To keep upstream archives available and reduce origin dependence, operate a source mirror and make integrity metadata mandatory.
- To amortize repeated fetches on trusted workers, provide repository-cache storage with an explicit sharing and eviction model.
- To mediate or audit supported downloads centrally, evaluate the versioned remote-downloader surface and its policy configuration.
- To make selected builds portable without those live services, maintain a vendor directory and test its actual closure.
A single platform may implement several of these roles behind one hostname, but the contracts must remain separate. Registry metadata has different immutability rules from archive blobs. A shared mutable repository cache has different poisoning and eviction risks from checked-in vendored sources. One credential that can read every upstream and write every mirror object also creates a larger compromise domain than scoped identities.
Keep repository credentials on Bazel's supported fetching boundary. Bazel 9.1.0 credential helpers can be scoped by domain and are used for repository fetching as well as remote caching, remote execution, and BES communication.2 4.9.9 Credential Helper Protocol covers their configuration. Do not pass a mirror token through an action environment or declared input: that moves a dependency-intake secret into cache keys, logs, execution requests, or artifacts where it does not belong.
Make fallback observable
Fallback is part of the trust policy, not merely an availability feature. Suppose the intended path is:
internal registry -> internal source mirror -> verified archive
If the mirror fails and Bazel silently retrieves the same archive from its public origin, integrity verification may still protect the object's identity, but the organization has lost the promised network and audit boundary. If the internal registry fails and configuration falls through to another registry, the selected metadata may come from a different authority. Availability may improve while policy compliance changes.
Write the expected outcome for each failure before enabling fallback:
| Injected failure | Safe expected result |
|---|---|
| Internal registry unavailable | Fail closed, or use one named fallback registry and emit evidence identifying it |
| Mirror object missing | Fail closed, or use an approved origin while retaining the declared integrity check and recording the route |
| Archive bytes altered | Integrity failure; never accept the object from cache, mirror, or origin |
| Downloader policy blocks a host | Policy denial remains distinguishable from a transient proxy error |
| Credential unavailable or out of scope | Authentication failure without copying credentials into action inputs or logs |
| Vendor marker stale | Visible refresh/fetch behavior, or a deliberately pinned source; never assume the stale copy was accepted |
The last case is easy to miss. Bazel uses a vendored repository when its marker is current or the repository is pinned in VENDOR.bazel; otherwise it warns and falls back to fetching the current repository version.3 If vendor mode is meant to enforce a controlled intake path, collect that warning and exercise the stale-marker case in a network-restricted environment.
Test identity, route, and persistence separately
A green build proves too little. Test the service with a small dependency fixture whose module metadata, archive digest, and expected repository output are known. Run it from fresh Bazel state so a warm repository cache cannot hide a missing service object.
Record three dimensions for every fetch:
- Identity: which module version, source URL, and integrity value was requested, and which digest was accepted?
- Route: which registry, mirror, downloader, or local vendor path served the request, and was fallback used?
- Persistence: did the result enter a repository cache, repo-contents cache, vendor directory, or only the current output base, and what invalidates or evicts it?
The internal dependency service contract makes this decision boundary runnable without pretending to emulate a production registry or network. Its verifier consumes normalized evidence records and rejects a missing object, a wrong digest, an unauthorized fetch, and a forbidden public-origin fallback. Each failure has a repaired negative control, so a verifier that merely rejects every record cannot pass. A real deployment still has to adapt its native audit logs to this record and retain the original route and authentication evidence.
Then run negative paths individually. Remove the archive from the mirror without changing registry metadata. Replace it with bytes that fail the declared integrity value. Deny the downloader credential. Block the origin while leaving the mirror available, then reverse the failure. Clear the client repository state between cases. Each result should identify the failed boundary and must not become green because an unrelated cache or fallback masked it.
This test matrix also reveals whether the architecture is actually unified or only visually unified. If one dashboard reports "dependency cache hit" without identifying registry metadata, archive object, fetched repository contents, or vendored source, it cannot tell you which contract worked. Preserve those distinctions in metrics, alerts, retention controls, and incident runbooks.
A Bazel dependency-intake platform is a composition of distinct mechanisms. Registries serve module metadata and fetch instructions; mirrors and downloaders control object retrieval; repository caches reuse fetched values or repository contents; vendor mode preserves selected material locally. None of them is Bazel's action cache or CAS.
Choose each layer for a stated requirement, keep credentials at the repository-fetch boundary, and test identity, route, persistence, and fallback independently. A fresh-state negative-path test—not a warm green build—is the evidence that integrity failures remain failures and that fallback does not silently cross your trust boundary.
Check your understanding · 4 questions
1.Match each dependency or build service to the job it performs:
Drag each answer onto the matching prompt, or click an answer and then click a prompt
2.Which dependency-intake records may be accepted under a fail-closed contract?
Choose True or False for each sentence
3.A mirror returned the wrong digest. An operator repairs the mirror object. What should the service do next?
Select one answer
4.A dashboard reports only "dependency cache hit." Why is that insufficient evidence for the shared dependency service?
Select one answer
Footnotes
-
Bazel registries — index registry roles, source metadata, mirrors, integrity, and registry precedence ↩1 ↩2 ↩3
-
Command-Line Reference — Bazel 9.1.0 repository cache, distdir, downloader, fallback, and credential-helper flags ↩1 ↩2 ↩3 ↩4
-
Vendor Mode — target-sensitive vendoring, registry files, marker freshness, pinning, and fetch fallback ↩1 ↩2