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:

Dependency intake prepares repositories before actions run
Each service answers a different question about metadata, bytes, materialization, or persistence
REGISTRY
Which module and source?
Metadata and integrity
FETCH ROUTE
Where do bytes come from?
Mirror or downloader
REPOSITORY
What does Bazel materialize?
External repository
LOCAL REUSE
What persists for later fetches?
Cache or vendor directory
SEPARATE ACTION BOUNDARY
An action key finds an ActionResult in AC
Content digests retrieve output bytes from CAS
Neither service resolves modules or fetches external repositories
Observe identity, route, and persistence independently. A dependency “cache hit” is not enough evidence.
MechanismWhat it serves or controlsWhat it does not establish
Bzlmod registryModule metadata and source-fetch instructionsAvailability of the source objects named by those instructions
Source mirror or --distdirArchives addressed by repository downloadsModule selection or arbitrary repository-rule output
Repository cacheReusable downloaded values, and in current Bazel a separate shareable cache of fetched repository directoriesAction results or build output artifacts
Remote downloaderA centralized proxy for supported repository downloadsGeneral execution of repository rules on a service
Vendor modeA workspace-selected local copy of external repositories and fetched registry filesAutomatic 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 failureSafe expected result
Internal registry unavailableFail closed, or use one named fallback registry and emit evidence identifying it
Mirror object missingFail closed, or use an approved origin while retaining the declared integrity check and recording the route
Archive bytes alteredIntegrity failure; never accept the object from cache, mirror, or origin
Downloader policy blocks a hostPolicy denial remains distinguishable from a transient proxy error
Credential unavailable or out of scopeAuthentication failure without copying credentials into action inputs or logs
Vendor marker staleVisible 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:

  1. Identity: which module version, source URL, and integrity value was requested, and which digest was accepted?
  2. Route: which registry, mirror, downloader, or local vendor path served the request, and was fallback used?
  3. 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.

key takeaway

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

Answers
Bzlmod registry
Source mirror or remote downloader
Repository cache or vendor directory
Action cache and CAS

2.Which dependency-intake records may be accepted under a fail-closed contract?

Choose True or False for each sentence

The object is missing, but a warm repository cache made the build green.
The route is approved, authorization succeeds, and the observed digest matches the requested digest.
The fetch is unauthorized, but the returned bytes have the expected digest.
The bytes have the expected digest, but they arrived through a public-origin fallback prohibited by policy.

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

0 of 4 answered

Footnotes

  1. Bazel registries — index registry roles, source metadata, mirrors, integrity, and registry precedence 1 2 3

  2. Command-Line Reference — Bazel 9.1.0 repository cache, distdir, downloader, fallback, and credential-helper flags 1 2 3 4

  3. Vendor Mode — target-sensitive vendoring, registry files, marker freshness, pinning, and fetch fallback 1 2