Skip to content

Commit e88a59f

Browse files
avrabeclaude
andcommitted
feat: add comprehensive air-gap and vendoring support
Add support for fully offline/air-gapped builds with: ## OCI Component Vendoring - Add `digest` attribute to `wasm_component_from_oci` and `wkg_pull` for immutable, reproducible pulls via SHA256 digest - Add `vendored_component` attribute for pre-downloaded local components - Three modes: tag-based (default), digest-based (immutable), vendored (air-gap) ## WIT Dependency Vendoring - Add `wit_package` repository rule for downloading WIT packages with checksum verification and offline mode support - Add `vendored_wit_library` macro for local vendored WIT packages - Add `wit_vendor_lock` for lock file generation - Support `BAZEL_WASM_WIT_VENDOR_DIR` environment variable ## Component Checksum Registry - Add `checksums/components/` directory for OCI component checksums - Add `component_registry.bzl` API mirroring toolchain registry pattern - Enables centralized security auditing for component downloads ## Environment Variables - Document `BAZEL_WASM_OFFLINE`, `BAZEL_WASM_VENDOR_DIR`, `BAZEL_WASM_MIRROR` - Document wkg-specific env vars (`WKG_CONFIG_FILE`, `WKG_REGISTRY_*`) - Explain `--repo_env` and `--action_env` patterns for Bazel ## Documentation & Tests - Add comprehensive air-gap-builds.md guide covering all strategies - Add tests/airgap/ test suite for vendored component workflows 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent f1394f7 commit e88a59f

10 files changed

Lines changed: 1317 additions & 35 deletions

File tree

checksums/BUILD.bazel

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,13 @@ bzl_library(
1414
visibility = ["//visibility:public"],
1515
)
1616

17+
# Component checksum registry API
18+
bzl_library(
19+
name = "component_registry",
20+
srcs = ["component_registry.bzl"],
21+
visibility = ["//visibility:public"],
22+
)
23+
1724
# Export all tool checksum files for consumption by toolchains
1825
# Note: tools/*.json files are exported from //checksums/tools package
1926
filegroup(
@@ -22,6 +29,13 @@ filegroup(
2229
visibility = ["//visibility:public"],
2330
)
2431

32+
# Export all component checksum files for OCI components
33+
filegroup(
34+
name = "all_component_checksums",
35+
srcs = ["//checksums/components:all_json"],
36+
visibility = ["//visibility:public"],
37+
)
38+
2539
# Individual tool checksum files
2640
# These reference files from the //checksums/tools package
2741
filegroup(

checksums/component_registry.bzl

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
"""Component Checksum Registry API
2+
3+
This module provides a unified API for accessing OCI component checksums
4+
from JSON files, mirroring the toolchain registry pattern.
5+
6+
The component registry enables:
7+
1. Reproducible builds with digest-based pulls
8+
2. Security auditing via centralized checksums
9+
3. Air-gap builds with pre-verified components
10+
11+
Usage:
12+
load("//checksums:component_registry.bzl", "component_registry")
13+
14+
# Get component digest for reproducible pulls
15+
digest = component_registry.get_digest(ctx, "wasi-http-proxy", "0.2.6")
16+
17+
# Get full component info
18+
info = component_registry.get_info(ctx, "wasi-http-proxy", "0.2.6")
19+
"""
20+
21+
def _load_component_json(repository_ctx, component_name):
22+
"""Load component data from JSON file.
23+
24+
Args:
25+
repository_ctx: Repository context for file operations
26+
component_name: Name of the component (e.g., 'wasi-http-proxy')
27+
28+
Returns:
29+
Dict: Component data from JSON file
30+
31+
Raises:
32+
fail: If JSON file not found
33+
"""
34+
json_file = repository_ctx.path(
35+
Label("@rules_wasm_component//checksums/components:{}.json".format(component_name)),
36+
)
37+
if not json_file.exists:
38+
fail("Component checksums not found: //checksums/components/{}.json\n".format(component_name) +
39+
"Add the component to the registry or use vendored_component attribute.")
40+
41+
content = repository_ctx.read(json_file)
42+
return json.decode(content)
43+
44+
def _get_component_digest(repository_ctx, component_name, version):
45+
"""Get verified digest for a component version.
46+
47+
Args:
48+
repository_ctx: Repository context for file operations
49+
component_name: Name of the component (e.g., 'wasi-http-proxy')
50+
version: Version string (e.g., '0.2.6')
51+
52+
Returns:
53+
String: SHA256 digest in format 'sha256:abc123...', or None if not found
54+
"""
55+
component_data = _load_component_json(repository_ctx, component_name)
56+
57+
versions = component_data.get("versions", {})
58+
version_data = versions.get(version, {})
59+
60+
return version_data.get("digest")
61+
62+
def _get_component_info(repository_ctx, component_name, version):
63+
"""Get complete component information for a version.
64+
65+
Args:
66+
repository_ctx: Repository context for file operations
67+
component_name: Name of the component
68+
version: Version string
69+
70+
Returns:
71+
Dict: Complete version information including digest, wit_world, etc.
72+
"""
73+
component_data = _load_component_json(repository_ctx, component_name)
74+
75+
versions = component_data.get("versions", {})
76+
return versions.get(version)
77+
78+
def _get_latest_version(repository_ctx, component_name):
79+
"""Get latest available version for a component.
80+
81+
Args:
82+
repository_ctx: Repository context for file operations
83+
component_name: Name of the component
84+
85+
Returns:
86+
String: Latest version, or None if component not found
87+
"""
88+
component_data = _load_component_json(repository_ctx, component_name)
89+
return component_data.get("latest_version")
90+
91+
def _get_oci_repository(repository_ctx, component_name):
92+
"""Get OCI repository URL for a component.
93+
94+
Args:
95+
repository_ctx: Repository context for file operations
96+
component_name: Name of the component
97+
98+
Returns:
99+
String: OCI repository URL (e.g., 'ghcr.io/bytecodealliance/wasi-http-proxy')
100+
"""
101+
component_data = _load_component_json(repository_ctx, component_name)
102+
return component_data.get("oci_repository")
103+
104+
def _list_versions(repository_ctx, component_name):
105+
"""List all available versions for a component.
106+
107+
Args:
108+
repository_ctx: Repository context for file operations
109+
component_name: Name of the component
110+
111+
Returns:
112+
List: List of version strings
113+
"""
114+
component_data = _load_component_json(repository_ctx, component_name)
115+
versions = component_data.get("versions", {})
116+
return list(versions.keys())
117+
118+
def _verify_digest(repository_ctx, component_name, version, actual_digest):
119+
"""Verify a component's digest against the registry.
120+
121+
Args:
122+
repository_ctx: Repository context for file operations
123+
component_name: Name of the component
124+
version: Version string
125+
actual_digest: The digest to verify
126+
127+
Returns:
128+
Boolean: True if digest matches, False otherwise
129+
"""
130+
expected = _get_component_digest(repository_ctx, component_name, version)
131+
if not expected:
132+
return False
133+
return expected == actual_digest
134+
135+
# Public API
136+
component_registry = struct(
137+
get_digest = _get_component_digest,
138+
get_info = _get_component_info,
139+
get_latest_version = _get_latest_version,
140+
get_oci_repository = _get_oci_repository,
141+
list_versions = _list_versions,
142+
verify_digest = _verify_digest,
143+
)

checksums/components/BUILD.bazel

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
"""Component checksum registry for OCI components
2+
3+
This directory contains checksum information for WASM components pulled from
4+
OCI registries. Use these checksums with the `digest` attribute in
5+
`wasm_component_from_oci` or `wkg_pull` for reproducible, verifiable builds.
6+
7+
Adding a new component:
8+
1. Create <component-name>.json with version info and digest
9+
2. Add entry to registry.json
10+
3. Use: wkg oci pull <ref> --output /tmp/comp.wasm && shasum -a 256 /tmp/comp.wasm
11+
"""
12+
13+
package(default_visibility = ["//visibility:public"])
14+
15+
# Export all component checksum files
16+
filegroup(
17+
name = "all_json",
18+
srcs = glob(["*.json"]),
19+
)
20+
21+
# Individual component checksum files
22+
filegroup(
23+
name = "registry_index",
24+
srcs = ["registry.json"],
25+
)
26+
27+
filegroup(
28+
name = "wasi_http_proxy_checksums",
29+
srcs = ["wasi-http-proxy.json"],
30+
)

checksums/components/registry.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
{
2+
"description": "Component checksum registry for air-gap and reproducible builds",
3+
"version": "1.0.0",
4+
"last_updated": "2026-01-16T00:00:00Z",
5+
"components": [
6+
{
7+
"name": "wasi-http-proxy",
8+
"package_file": "wasi-http-proxy.json"
9+
}
10+
],
11+
"usage": {
12+
"description": "Use these checksums with wasm_component_from_oci or wkg_pull rules",
13+
"example": {
14+
"rule": "wasm_component_from_oci",
15+
"digest_field": "digest",
16+
"format": "sha256:<hash>"
17+
}
18+
}
19+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
{
2+
"component_name": "wasi-http-proxy",
3+
"description": "WASI HTTP proxy component for handling HTTP requests",
4+
"oci_repository": "ghcr.io/bytecodealliance/wasi-http-proxy",
5+
"homepage": "https://github.com/bytecodealliance/wasmtime",
6+
"latest_version": "0.2.6",
7+
"last_checked": "2026-01-16T00:00:00Z",
8+
"versions": {
9+
"0.2.6": {
10+
"release_date": "2025-10-01",
11+
"digest": "sha256:placeholder_add_real_digest_here",
12+
"size_bytes": 0,
13+
"wit_world": "wasi:http/proxy@0.2.6",
14+
"wasi_version": "0.2.6",
15+
"notes": "Compatible with wasmtime 39.0.0+"
16+
}
17+
},
18+
"security": {
19+
"signing": "Unsigned",
20+
"provenance": "None"
21+
},
22+
"notes": [
23+
"Example component entry - replace with actual digest",
24+
"Run: wkg oci pull <ref> --output /tmp/comp.wasm && shasum -a 256 /tmp/comp.wasm",
25+
"Format digest as sha256:<hash>"
26+
]
27+
}

0 commit comments

Comments
 (0)