An in-house Bazel module registry for
Tinyland-authored Bzlmod modules (tummycrypt_*, rules_tectonic,
spear_resumes, dsa_study_packet, ...). It is a static, content-addressed
directory of MODULE.bazel + source.json + metadata.json triples under
modules/<name>/<version>/, served to consumers over raw GitHub URLs — not a
Bazel workspace itself (no MODULE.bazel, WORKSPACE, BUILD, or .bazelrc
at this root; see ".bazelversion here" below).
The estate-wide proven Bazel version is recorded once, in this repo's root
.bazelversion. Every Tinyland repo that runs Bazel
converges its own root .bazelversion to that same value. Bumping it is a
deliberate, cross-repo decision tracked by a Linear issue (the current value,
convention, and full rationale are in
docs/bazel-adoption-v0.md), not a per-repo
drift.
This repo is not a Bazel workspace, so running bazelisk at this root does
not read this file. It is consumed exactly two ways, both by copy:
npm run smoke:resolve/npm run smoke:stage1-consumer(scripts/smoke-active-registry.mjs,scripts/smoke-stage1-consumer-targets.mjs) each copy it verbatim into a generated temp smoke workspace before invokingbazel mod graph/bazel buildthere.- Spoke repos carry their own root
.bazelversion. They converge it to this value by hand when they re-pin this registry, and record the value they converged on next to that pin; each spoke'sscaffold-doctorthen holds them to their recorded value. That check is offline — it catches spoke-side drift, it does not read this file over the network. Seedocs/bazel-adoption-v0.md§5 for the exact limits.
npm run validate cross-checks .bazelversion here against the pin recorded
in package.json's bazelEstate.version, so a drift between the two
is caught even without running the network/token-dependent smoke scripts.
Consumers resolve Bzlmod modules through a fixed two-registry chain, in
order: this registry first (in-house modules resolve before anything else),
then bcr.bazel.build (Bazel Central Registry) as fallback. See
docs/bazel-adoption-v0.md §2.
Spokes pin this registry to an immutable commit SHA in their own .bazelrc
(common --registry=https://raw.githubusercontent.com/tinyland-inc/bazel-registry/<40-char-sha>),
not the mutable main ref, so module metadata cannot change silently
underneath them. A sibling spoke's documented refresh one-liner (quoted verbatim
in docs/bazel-adoption-v0.md §3)
is the canonical way to re-pin after this registry advances:
sed -i '' "s|bazel-registry/[0-9a-f]\{40\}|bazel-registry/$(gh api repos/tinyland-inc/bazel-registry/commits/main --jq .sha)|" .bazelrc
A SHA bump must land alongside a MODULE.bazel.lock refresh in the same
commit — see docs/bazel-adoption-v0.md §3 for the full convention.
npm run validate(scripts/validate-registry.mjs) — pure static checks: everysource.jsonhas SRI integrity, notinyland.devtarball references, itsurlresolves to an allowed GitHub source host (agithub.com/<owner>/<repo>/archive/refs/tags/<tag>.tar.gzrelease archive, or anapi.github.com/repos/<owner>/<repo>/tarball/<ref>authenticated tarball for private repos -- package-registry hosts such asregistry.npmjs.organdnpm.pkg.github.comare explicitly refused),metadata.jsonandMODULE.bazelagree on name/version, and.bazelversionmatches the recorded estate pin. No network.npm run test:validate-source-hosts(scripts/test-validate-registry-source-hosts.mjs): fixture-driven negative control for the source-host allowlist above. Builds throwaway one-module registry fixtures in a temp dir and runsvalidate-registry.mjsagainst each, proving both that allowed URL shapes still pass and thatregistry.npmjs.org,npm.pkg.github.com, and any other unlisted host are rejected.npm run validatealone can't prove the refusal path: this repo's realmodules/tree has no disallowed-host entries to exercise it.scripts/check-immutable-versions.sh— rejects edits to already-shipped module version directories (immutability gate).npm run smoke:resolve/npm run smoke:stage1-consumer— network and GitHub-token dependent; exercise.bazelversionand this registry's module metadata through realbazel mod graph/bazel buildruns in throwaway workspaces.npm run smoke:scheduling-kit-only/npm run smoke:scheduling-bridge-only/npm run smoke:tempo-store-only— isolated one-direct-dependency consumers. Each resolves the selected graph and builds only that module's//:pkg, so an aggregate root dependency cannot raise and mask scheduling-bridge's declared scheduling-kit edge. The bridge proof reads the latest active published bridge version and its declared kit version directly from registry metadata; it does not invent an unpublished successor.
The three isolated package smokes are required GF/self-hosted CI steps in
.github/workflows/validate.yml. Bridge and Tempo consume commit-pinned private
archives through GitHub's authenticated API tarball endpoint and fail closed
when TINYLAND_REGISTRY_GITHUB_TOKEN is absent. The aggregate and Stage 1
legacy-compatibility audits still run and report their failures, but they do not
mask or block current package successors while immutable older private entries
retain browser-archive URLs that GitHub App tokens cannot read.
docs/bazel-adoption-v0.md— the full estate-wide Bazel adoption spec (Step A): single-version convergence, the two-registry chain, the exact-SHA pin convention, endpoint-free.bazelrc.flywheel, and the scaffold-doctor conformance gate. Step B (Bazel 9.x migration) is specified separately indocs/bazel-9x-migration-spec-v0.md(TIN-3897).