Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .bazelignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Standalone example modules (own MODULE.bazel, built independently):
examples/varve_extension
1 change: 1 addition & 0 deletions examples/varve_extension/.bazelversion
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
9.2.0
13 changes: 13 additions & 0 deletions examples/varve_extension/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package(default_visibility = ["//visibility:public"])

exports_files([
"varve.toml",
"rolling.pub",
])

# The layer's tools, resolved and signature-verified by varve, consumable
# as ordinary Bazel targets:
alias(
name = "synth",
actual = "@varve_tools//:synth",
)
21 changes: 21 additions & 0 deletions examples/varve_extension/MODULE.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
"""Live example: Bazel consumes varve — one pin, terminal and build."""

module(name = "varve_extension_example", version = "0.0.0")

bazel_dep(name = "rules_wasm_component", version = "1.0.0")
local_path_override(
module_name = "rules_wasm_component",
path = "../..",
)

varve = use_extension("@rules_wasm_component//varve:varve.bzl", "varve")
varve.configure(
pin = "//:varve.toml",
trust_root = "//:rolling.pub",
tools = [
"rivet",
"synth",
"wsc",
],
)
use_repo(varve, "varve_tools")
285 changes: 285 additions & 0 deletions examples/varve_extension/MODULE.bazel.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions examples/varve_extension/rolling.pub
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
4e771dc62a08be89e3450f8cd807da58ff70af4a4e124ebf2d2b71684cfd9973
5 changes: 5 additions & 0 deletions examples/varve_extension/varve.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
manifest-version = 1

[toolchain]
channel = "rolling"
layer = "2026.08.0"
14 changes: 14 additions & 0 deletions varve/BUILD.bazel
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
"""The varve module extension — Bazel consumes varve (EXPERIMENTAL)."""

package(default_visibility = ["//visibility:public"])

exports_files([
"varve.bzl",
"varve_checksums.json",
])

filegroup(
name = "all_files",
srcs = glob(["**"]),
visibility = ["//visibility:public"],
)
31 changes: 31 additions & 0 deletions varve/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# varve module extension (EXPERIMENTAL)

Bazel consumes [varve](https://github.com/pulseengine/varve) — it does not
reimplement it. One `varve.toml` pin governs the developer terminal (shims)
AND the Bazel build; the layer is signature-verified (DSSE against the
pinned trust root) and anti-rollback-protected by varve itself, inside a
hermetic, repo-local root that Bazel invalidates when the pin or trust
root changes.

```starlark
# MODULE.bazel
varve = use_extension("@rules_wasm_component//varve:varve.bzl", "varve")
varve.configure(
pin = "//:varve.toml",
trust_root = "//:trust-roots/rolling.pub",
tools = ["rivet", "synth", "wsc"],
)
use_repo(varve, "varve_tools")
# then depend on @varve_tools//:synth etc.
```

Trust model: the single trust-on-first-use root is the sha256 pin of the
varve binary in `varve_checksums.json` (transcribed from varve's
cosign-verified release sums). Every tool byte after that is accepted or
refused by varve's signature chain — this extension cannot relax it, and
swapping the `registry` (mirror, air gap) changes availability only.

Relation to the checksum registries in `//checksums`: those remain for
consumers without varve; `varve export-bazel` (varve ≥ 0.9.0) can compile
them from a verified layer so their hashes are signature-anchored rather
than trust-on-first-use.
166 changes: 166 additions & 0 deletions varve/varve.bzl
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
"""Bazel consumes varve — the PulseEngine toolchain layer manager.

EXPERIMENTAL. One pin governs both the developer terminal and the Bazel
build: the module extension reads the project's `varve.toml` and trust
root, bootstraps a sha256-pinned varve binary, and lets VARVE do what varve
does — resolve, signature-verify (DSSE against the pinned root), enforce
anti-rollback counters, and lay the layer down. Bazel contributes what
Bazel does: hermetic repos, invalidation when the pin changes, caching,
toolchain wiring. Neither system reimplements the other.

Trust model: the ONLY trust-on-first-use root is the sha256 pin of the
varve binary itself in `varve/varve_checksums.json` (itself transcribed
from varve's cosign-verified release sums). Every tool byte after that is
accepted or refused by varve's signature chain, not by this file.
"""

_VARVE_CHECKSUMS_LABEL = Label("//varve:varve_checksums.json")

_PLATFORM_TO_TRIPLE = {
"mac os x_aarch64": "aarch64-apple-darwin",
"mac os x_x86_64": "x86_64-apple-darwin",
"linux_aarch64": "aarch64-unknown-linux-gnu",
"linux_amd64": "x86_64-unknown-linux-gnu",
"linux_x86_64": "x86_64-unknown-linux-gnu",
}

def _host_triple(repository_ctx):
os_name = repository_ctx.os.name.lower()
arch = repository_ctx.os.arch.lower()
triple = _PLATFORM_TO_TRIPLE.get("{}_{}".format(os_name, arch))
if not triple:
fail("varve: unsupported host platform {}_{}".format(os_name, arch))
return triple

def _varve_tools_impl(repository_ctx):
"""Install the pinned layer THROUGH varve into a repo-local root."""
triple = _host_triple(repository_ctx)

# 1. Bootstrap varve itself: sha256-pinned download (the one TOFU root).
checksums = json.decode(repository_ctx.read(repository_ctx.attr._varve_checksums))
version = checksums["version"]
entry = checksums["platforms"].get(triple)
if not entry:
fail("varve: no varve binary pinned for host platform {}".format(triple))
archive = "varve-{}-{}.tar.gz".format(version, triple)
repository_ctx.download_and_extract(
url = "https://github.com/pulseengine/varve/releases/download/{}/{}".format(version, archive),
sha256 = entry["sha256"],
output = "varve-dist",
)
varve = repository_ctx.path("varve-dist/varve")

# 2. Watch the pin and trust root: editing either re-runs this rule.
pin = repository_ctx.path(repository_ctx.attr.pin)
trust_root = repository_ctx.path(repository_ctx.attr.trust_root)
repository_ctx.watch(pin)
repository_ctx.watch(trust_root)

# 3. A tiny project dir carrying the pin, and a repo-local varve root:
# fully hermetic — no shared host state, rebuilt when Bazel says so.
repository_ctx.file("project/.keep", "")
repository_ctx.template("project/varve.toml", repository_ctx.attr.pin)
varve_root = repository_ctx.path(".varve-root")

# 4. varve does the trust work. Where the bytes come from is pluggable;
# whether they are accepted is varve's signature chain — this rule
# cannot relax it.
result = repository_ctx.execute(
[varve, "install", "--from", repository_ctx.attr.registry],
environment = {
"VARVE_ROOT": str(varve_root),
"VARVE_TRUST_ROOT": str(trust_root),
},
working_directory = "project",
timeout = 600,
)
if result.return_code != 0:
fail("varve install failed (the layer did not verify or is unavailable):\n{}{}".format(
result.stdout,
result.stderr,
))

# 5. Re-verify offline (the retained envelope) and expose the tools.
result = repository_ctx.execute(
[varve, "verify"],
environment = {
"VARVE_ROOT": str(varve_root),
"VARVE_TRUST_ROOT": str(trust_root),
},
working_directory = "project",
)
if result.return_code != 0:
fail("varve verify failed after install:\n{}{}".format(result.stdout, result.stderr))

build = [
'# Generated by the varve module extension — tools from the signed,',
'# counter-protected layer pinned by {}'.format(repository_ctx.attr.pin),
'package(default_visibility = ["//visibility:public"])',
"",
]
for tool in repository_ctx.attr.tools:
result = repository_ctx.execute(
[varve, "which", tool],
environment = {
"VARVE_ROOT": str(varve_root),
"VARVE_TRUST_ROOT": str(trust_root),
},
working_directory = "project",
)
if result.return_code != 0:
fail("varve: tool '{}' is not in the pinned layer:\n{}".format(tool, result.stderr))
real = result.stdout.splitlines()[0].strip()
repository_ctx.symlink(real, "bin/" + tool)
build.append('filegroup(name = "{tool}", srcs = ["bin/{tool}"])'.format(tool = tool))
repository_ctx.file("BUILD.bazel", "\n".join(build) + "\n")

varve_tools_repository = repository_rule(
implementation = _varve_tools_impl,
attrs = {
"pin": attr.label(
mandatory = True,
allow_single_file = True,
doc = "The project's varve.toml — THE pin, shared with the terminal workflow.",
),
"trust_root": attr.label(
mandatory = True,
allow_single_file = True,
doc = "Hex-encoded ed25519 root public key file (e.g. trust-roots/rolling.pub).",
),
"registry": attr.string(
default = "oci://ghcr.io/pulseengine/varve/layers",
doc = "Layer source. Availability only — acceptance is varve's signature chain.",
),
"tools": attr.string_list(
mandatory = True,
doc = "Tool names to expose as //:<tool> filegroups.",
),
"_varve_checksums": attr.label(default = _VARVE_CHECKSUMS_LABEL),
},
doc = "Installs the pinned varve layer through varve itself and exposes its tools.",
)

_configure = tag_class(attrs = {
"pin": attr.label(mandatory = True),
"trust_root": attr.label(mandatory = True),
"registry": attr.string(default = "oci://ghcr.io/pulseengine/varve/layers"),
"tools": attr.string_list(mandatory = True),
"name": attr.string(default = "varve_tools"),
})

def _varve_extension_impl(module_ctx):
for mod in module_ctx.modules:
for cfg in mod.tags.configure:
varve_tools_repository(
name = cfg.name,
pin = cfg.pin,
trust_root = cfg.trust_root,
registry = cfg.registry,
tools = cfg.tools,
)

varve = module_extension(
implementation = _varve_extension_impl,
tag_classes = {"configure": _configure},
doc = "Bazel-consumes-varve: layer-pinned, signature-verified toolchain acquisition.",
)
18 changes: 18 additions & 0 deletions varve/varve_checksums.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
{
"_comment": "The ONE trust-on-first-use root of the varve extension: sha256 pins of the varve binary itself, transcribed from the cosign-verified SHA256SUMS.txt of the release below. Update by verifying the new release's sums (cosign verify-blob) and transcribing — never by hashing an unverified download.",
"version": "v0.9.0",
"platforms": {
"aarch64-apple-darwin": {
"sha256": "f356c383c4605e36ae0c384587646b8ed5be9c6d13ea6f8cdc6a344170729b88"
},
"x86_64-apple-darwin": {
"sha256": "5f5df17f08b92d4ac6d683b3a13cebabc436b274c69ca5b1e2cfdd3c6393be7f"
},
"aarch64-unknown-linux-gnu": {
"sha256": "22a3c7a49248ffb30f257f901aa3de6a19ca98f8dce37cd6f9bc10be37d72f7e"
},
"x86_64-unknown-linux-gnu": {
"sha256": "c5faea0fcc48efdb317eead03c4684b9e63b5e29f20ff8c89c1902cff089f503"
}
}
}