Wire Forge-native release infrastructure
Some checks failed
Cache: Publish Nix / Publish Nix Cache (push) Waiting to run
Build Rust / Cargo Test (push) Successful in 4m14s
Build Site / Next.js Build (push) Failing after 6s
Infra: OpenTofu / OpenTofu (grafana) (push) Successful in 5s
Lint Governance / BEP Metadata (push) Successful in 0s
Build: Android / Android Rust Core Stub (push) Failing after 23s

This commit is contained in:
Conrad Kramer 2026-06-07 05:51:12 -07:00
parent 97c569fb35
commit 002bd382e9
199 changed files with 14268 additions and 185 deletions

View file

@ -0,0 +1,78 @@
#!/usr/bin/env bash
set -euo pipefail
apply=0
prefer_packagekit=1
usage() {
cat <<'EOF'
Usage: Scripts/package/bootstrap-native-daemon.sh [--apply] [--no-packagekit]
Detects the host package manager and prints, or with --apply runs, the native
install command for the Burrow daemon package. This is the host-side bootstrap
equivalent the Flatpak GUI can point users at when PackageKit is unavailable.
This assumes the Burrow native repository for the detected package manager is
already configured. Repository setup should be explicit because it establishes
package trust roots.
EOF
}
while [[ "$#" -gt 0 ]]; do
case "$1" in
--apply)
apply=1
;;
--no-packagekit)
prefer_packagekit=0
;;
-h|--help)
usage
exit 0
;;
*)
echo "unknown argument: $1" >&2
exit 64
;;
esac
shift
done
run_or_print() {
if [[ "$apply" == "1" ]]; then
"$@"
else
printf '%q ' "$@"
printf '\n'
fi
}
if [[ "$prefer_packagekit" == "1" ]] && command -v pkcon >/dev/null 2>&1; then
run_or_print pkcon install burrow
exit 0
fi
if command -v apt-get >/dev/null 2>&1; then
run_or_print sudo apt-get install burrow
elif command -v dnf >/dev/null 2>&1; then
run_or_print sudo dnf install burrow
elif command -v zypper >/dev/null 2>&1; then
run_or_print sudo zypper install burrow
elif command -v pacman >/dev/null 2>&1; then
run_or_print sudo pacman -S burrow
elif command -v apk >/dev/null 2>&1; then
run_or_print sudo apk add burrow
elif command -v xbps-install >/dev/null 2>&1; then
run_or_print sudo xbps-install -S burrow
elif command -v eopkg >/dev/null 2>&1; then
run_or_print sudo eopkg install burrow
elif command -v emerge >/dev/null 2>&1; then
run_or_print sudo emerge --ask net-vpn/burrow
elif command -v swupd >/dev/null 2>&1; then
run_or_print sudo swupd bundle-add burrow
elif command -v nix >/dev/null 2>&1; then
run_or_print nix profile install git+https://git.burrow.net/hackclub/burrow
else
echo "No supported native package manager found. Install Burrow through DEB, RPM, pacman, AUR, Flatpak GUI plus daemon package, or the NixOS flake." >&2
exit 69
fi

View file

@ -0,0 +1,248 @@
#!/usr/bin/env bash
set -euo pipefail
repo_root="$(
git rev-parse --show-toplevel 2>/dev/null || {
cd "$(dirname "${BASH_SOURCE[0]}")/../.."
pwd
}
)"
input_dir="${BURROW_PACKAGE_INPUT_DIR:-${repo_root}/dist/packages}"
output_dir="${BURROW_PACKAGE_REPOSITORY_DIR:-${repo_root}/publish/repositories}"
channel="${BURROW_PACKAGE_CHANNEL:-stable}"
suite="${BURROW_APT_SUITE:-stable}"
component="${BURROW_APT_COMPONENT:-main}"
apt_arch="${BURROW_APT_ARCH:-${BURROW_PACKAGE_ARCH:-amd64}}"
rpm_arch="${BURROW_RPM_ARCH:-x86_64}"
pacman_arch="${BURROW_PACMAN_ARCH:-x86_64}"
repo_name="${BURROW_ARCH_REPO_NAME:-burrow}"
generic_kms_public_key_pem="${BURROW_PACKAGE_REPO_KMS_PUBLIC_KEY_PEM:-}"
generic_kms_key="${BURROW_PACKAGE_REPO_KMS_KEY:-}"
apt_kms_public_key_pem="${BURROW_APT_REPO_KMS_PUBLIC_KEY_PEM:-$generic_kms_public_key_pem}"
apt_kms_key="${BURROW_APT_REPO_KMS_KEY:-${generic_kms_key:-package-apt-repository}}"
rpm_kms_public_key_pem="${BURROW_RPM_REPO_KMS_PUBLIC_KEY_PEM:-$generic_kms_public_key_pem}"
rpm_kms_key="${BURROW_RPM_REPO_KMS_KEY:-${generic_kms_key:-package-rpm-repository}}"
pacman_kms_public_key_pem="${BURROW_PACMAN_REPO_KMS_PUBLIC_KEY_PEM:-$generic_kms_public_key_pem}"
pacman_kms_key="${BURROW_PACMAN_REPO_KMS_KEY:-${generic_kms_key:-package-pacman-repository}}"
usage() {
cat <<'EOF'
Usage: Scripts/package/build-repositories.sh [apt|rpm|arch|keys|all]
Builds native Linux package repositories from existing package artifacts.
Inputs:
BURROW_PACKAGE_INPUT_DIR directory containing .deb, .rpm, and .pkg.tar.* files
BURROW_PACKAGE_REPOSITORY_DIR output directory, default publish/repositories
BURROW_APT_REPO_KMS_PUBLIC_KEY_PEM Google KMS public key PEM for APT OpenPGP signatures
BURROW_APT_REPO_KMS_KEY Google KMS key name for APT signatures
BURROW_RPM_REPO_KMS_PUBLIC_KEY_PEM Google KMS public key PEM for RPM repository signatures
BURROW_RPM_REPO_KMS_KEY Google KMS key name for RPM repository signatures
BURROW_PACMAN_REPO_KMS_PUBLIC_KEY_PEM Google KMS public key PEM for pacman signatures
BURROW_PACMAN_REPO_KMS_KEY Google KMS key name for pacman signatures
BURROW_PACKAGE_REPO_KMS_PUBLIC_KEY_PEM fallback public key PEM for all formats
BURROW_PACKAGE_REPO_KMS_KEY fallback KMS key name for all formats
BURROW_KMS_KEYRING/BURROW_KMS_LOCATION/BURROW_KMS_VERSION/GOOGLE_CLOUD_PROJECT
The repository metadata is signed with Google KMS-backed OpenPGP detached
signatures when each format's KMS public key and key name are configured.
EOF
}
if [[ "${1:-}" == "-h" || "${1:-}" == "--help" ]]; then
usage
exit 0
fi
mode="${1:-all}"
sha256_file() {
local file="$1"
if command -v shasum >/dev/null 2>&1; then
shasum -a 256 "$file" > "${file}.sha256"
else
sha256sum "$file" > "${file}.sha256"
fi
}
sign_file() {
local file="$1"
local output="${2:-${file}.sig}"
local armor="${3:-0}"
local kms_key="$4"
local kms_public_key_pem="$5"
if [[ -z "$kms_public_key_pem" || -z "$kms_key" ]]; then
echo "warning: KMS OpenPGP signing disabled for ${file}; set the ${file} repository KMS public key and key name" >&2
return 0
fi
args=(
"${repo_root}/Scripts/package/google-kms-openpgp.py"
detach-sign
--public-key-pem "$kms_public_key_pem"
--kms-key "$kms_key"
--input "$file"
--output "$output"
)
if [[ "$armor" == "1" ]]; then
args+=(--armor)
fi
"${args[@]}"
}
write_public_key() {
local name="$1"
local kms_key="$2"
local kms_public_key_pem="$3"
local user_id="$4"
local output="${output_dir}/keys/${name}.asc"
if [[ -z "$kms_public_key_pem" || -z "$kms_key" ]]; then
echo "warning: KMS OpenPGP public key export disabled for ${name}; set the repository KMS public key and key name" >&2
return 0
fi
mkdir -p "$(dirname "$output")"
"${repo_root}/Scripts/package/google-kms-openpgp.py" \
public-key \
--public-key-pem "$kms_public_key_pem" \
--kms-key "$kms_key" \
--user-id "$user_id" \
--output "$output" \
--armor
}
write_release_file() {
local release_path="$1"
local dist_dir="$2"
local now
now="$(LC_ALL=C date -u '+%a, %d %b %Y %H:%M:%S +0000')"
{
printf 'Origin: Burrow\n'
printf 'Label: Burrow\n'
printf 'Suite: %s\n' "$suite"
printf 'Codename: %s\n' "$suite"
printf 'Date: %s\n' "$now"
printf 'Architectures: %s\n' "$apt_arch"
printf 'Components: %s\n' "$component"
printf 'Description: Burrow native package repository\n'
printf 'SHA256:\n'
(
cd "$dist_dir"
find . -type f ! -name Release -print | LC_ALL=C sort | while read -r file; do
clean="${file#./}"
sum="$(sha256sum "$clean" | awk '{print $1}')"
size="$(wc -c < "$clean" | tr -d ' ')"
printf ' %s %s %s\n' "$sum" "$size" "$clean"
done
)
} > "$release_path"
}
build_apt() {
local root binary_dir pool_dir release_dir release_file
root="${output_dir}/apt"
pool_dir="${root}/pool/${component}/b/burrow"
binary_dir="${root}/dists/${suite}/${component}/binary-${apt_arch}"
release_dir="${root}/dists/${suite}"
mkdir -p "$pool_dir" "$binary_dir" "$release_dir"
find "$input_dir" -type f -name '*.deb' -exec cp {} "$pool_dir/" \; 2>/dev/null || true
if ! find "$pool_dir" -type f -name '*.deb' -print -quit | grep -q .; then
echo "warning: no .deb packages found under ${input_dir}; apt repository contains README only" >&2
printf 'Place Burrow .deb artifacts in this repository pool before publishing.\n' > "${root}/README.txt"
return 0
fi
if ! command -v dpkg-scanpackages >/dev/null 2>&1; then
echo "error: dpkg-scanpackages is required to build the apt repository" >&2
exit 127
fi
(
cd "$root"
dpkg-scanpackages --arch "$apt_arch" "pool/${component}" /dev/null > "dists/${suite}/${component}/binary-${apt_arch}/Packages"
gzip -9cn "dists/${suite}/${component}/binary-${apt_arch}/Packages" > "dists/${suite}/${component}/binary-${apt_arch}/Packages.gz"
)
release_file="${release_dir}/Release"
write_release_file "$release_file" "$release_dir"
sign_file "$release_file" "${release_file}.gpg" 1 "$apt_kms_key" "$apt_kms_public_key_pem"
sha256_file "$release_file"
}
build_rpm() {
local root repomd
root="${output_dir}/rpm/${channel}/${rpm_arch}"
mkdir -p "$root"
find "$input_dir" -type f -name '*.rpm' -exec cp {} "$root/" \; 2>/dev/null || true
if ! find "$root" -maxdepth 1 -type f -name '*.rpm' -print -quit | grep -q .; then
echo "warning: no .rpm packages found under ${input_dir}; rpm repository contains README only" >&2
printf 'Place Burrow .rpm artifacts in this directory before publishing.\n' > "${root}/README.txt"
return 0
fi
if ! command -v createrepo_c >/dev/null 2>&1; then
echo "error: createrepo_c is required to build the rpm repository" >&2
exit 127
fi
createrepo_c "$root"
repomd="${root}/repodata/repomd.xml"
sign_file "$repomd" "${repomd}.asc" 1 "$rpm_kms_key" "$rpm_kms_public_key_pem"
sha256_file "$repomd"
}
build_arch() {
local root db
root="${output_dir}/arch/${repo_name}/${pacman_arch}"
mkdir -p "$root"
find "$input_dir" -type f \( -name '*.pkg.tar.zst' -o -name '*.pkg.tar.xz' -o -name '*.pkg.tar.gz' \) -exec cp {} "$root/" \; 2>/dev/null || true
if ! find "$root" -maxdepth 1 -type f -name '*.pkg.tar.*' -print -quit | grep -q .; then
echo "warning: no Arch packages found under ${input_dir}; pacman repository contains README only" >&2
printf 'Place Burrow .pkg.tar.* artifacts in this directory before publishing.\n' > "${root}/README.txt"
return 0
fi
if ! command -v repo-add >/dev/null 2>&1; then
echo "error: repo-add is required to build the Arch repository" >&2
exit 127
fi
(
cd "$root"
repo-add "${repo_name}.db.tar.gz" ./*.pkg.tar.*
)
db="${root}/${repo_name}.db.tar.gz"
sign_file "$db" "${db}.sig" 0 "$pacman_kms_key" "$pacman_kms_public_key_pem"
find "$root" -maxdepth 1 -type f -name '*.pkg.tar.*' ! -name '*.sig' -print | while read -r package; do
sign_file "$package" "${package}.sig" 0 "$pacman_kms_key" "$pacman_kms_public_key_pem"
done
sha256_file "$db"
}
build_keys() {
write_public_key "burrow-package-apt-repository" "$apt_kms_key" "$apt_kms_public_key_pem" "Burrow APT Package Repository <packages@burrow.net>"
write_public_key "burrow-package-rpm-repository" "$rpm_kms_key" "$rpm_kms_public_key_pem" "Burrow RPM Package Repository <packages@burrow.net>"
write_public_key "burrow-package-pacman-repository" "$pacman_kms_key" "$pacman_kms_public_key_pem" "Burrow pacman Package Repository <packages@burrow.net>"
}
mkdir -p "$output_dir"
case "$mode" in
apt)
build_apt
;;
rpm)
build_rpm
;;
arch)
build_arch
;;
keys)
build_keys
;;
all)
build_keys
build_apt
build_rpm
build_arch
;;
*)
echo "unknown repository mode: ${mode}" >&2
exit 64
;;
esac

View file

@ -0,0 +1,313 @@
#!/usr/bin/env python3
"""Create OpenPGP public keys and detached signatures backed by Google KMS.
This intentionally implements only the narrow RSA/SHA-256 subset Burrow needs
for Linux package repository metadata. The private key lives in Google Cloud KMS;
this script builds OpenPGP packets locally and asks KMS to produce the RSA
signature value.
"""
from __future__ import annotations
import argparse
import base64
import binascii
import hashlib
import os
import struct
import subprocess
import sys
import tempfile
import time
from pathlib import Path
RSA_PUBLIC_KEY_ALGO = 1
SHA256_HASH_ALGO = 8
def parse_der_length(data: bytes, offset: int) -> tuple[int, int]:
first = data[offset]
offset += 1
if first < 0x80:
return first, offset
count = first & 0x7F
if count == 0 or count > 4:
raise ValueError("unsupported DER length")
value = int.from_bytes(data[offset : offset + count], "big")
return value, offset + count
def parse_der_tlv(data: bytes, offset: int, expected_tag: int | None = None) -> tuple[int, bytes, int]:
tag = data[offset]
offset += 1
length, offset = parse_der_length(data, offset)
value = data[offset : offset + length]
if len(value) != length:
raise ValueError("truncated DER value")
if expected_tag is not None and tag != expected_tag:
raise ValueError(f"expected DER tag {expected_tag:#x}, got {tag:#x}")
return tag, value, offset + length
def pem_to_der(pem: bytes) -> bytes:
lines = [
line.strip()
for line in pem.splitlines()
if line and not line.startswith(b"-----")
]
return base64.b64decode(b"".join(lines))
def parse_rsa_public_key_from_pem(path: Path) -> tuple[int, int]:
der = pem_to_der(path.read_bytes())
_, spki, end = parse_der_tlv(der, 0, 0x30)
if end != len(der):
raise ValueError("trailing data after SubjectPublicKeyInfo")
_, _alg, offset = parse_der_tlv(spki, 0, 0x30)
_, bit_string, offset = parse_der_tlv(spki, offset, 0x03)
if offset != len(spki):
raise ValueError("trailing data after SPKI bit string")
if not bit_string or bit_string[0] != 0:
raise ValueError("unsupported SPKI bit string")
rsa_der = bit_string[1:]
_, rsa_seq, end = parse_der_tlv(rsa_der, 0, 0x30)
if end != len(rsa_der):
raise ValueError("trailing data after RSA public key")
_, n_bytes, offset = parse_der_tlv(rsa_seq, 0, 0x02)
_, e_bytes, offset = parse_der_tlv(rsa_seq, offset, 0x02)
if offset != len(rsa_seq):
raise ValueError("trailing data after RSA integers")
return int.from_bytes(n_bytes, "big"), int.from_bytes(e_bytes, "big")
def packet_header(tag: int, length: int) -> bytes:
if length < 192:
return bytes([0xC0 | tag, length])
if length < 8384:
length -= 192
return bytes([0xC0 | tag, (length >> 8) + 192, length & 0xFF])
return bytes([0xC0 | tag, 0xFF]) + struct.pack(">I", length)
def old_packet_header(tag: int, length: int) -> bytes:
if length > 0xFFFF:
raise ValueError("old-format two-octet packet length exceeded")
return bytes([(tag << 2) | 1]) + struct.pack(">H", length)
def mpi(value: int) -> bytes:
if value < 0:
raise ValueError("MPI value must be non-negative")
raw = value.to_bytes(max(1, (value.bit_length() + 7) // 8), "big")
return struct.pack(">H", value.bit_length()) + raw.lstrip(b"\x00")
def subpacket(kind: int, body: bytes) -> bytes:
payload = bytes([kind]) + body
length = len(payload)
if length < 192:
return bytes([length]) + payload
if length < 8384:
length -= 192
return bytes([(length >> 8) + 192, length & 0xFF]) + payload
return b"\xff" + struct.pack(">I", length) + payload
def public_key_body(n: int, e: int, created_at: int) -> bytes:
return (
b"\x04"
+ struct.pack(">I", created_at)
+ bytes([RSA_PUBLIC_KEY_ALGO])
+ mpi(n)
+ mpi(e)
)
def fingerprint(public_body: bytes) -> bytes:
return hashlib.sha1(old_packet_header(6, len(public_body)) + public_body).digest()
def armor(kind: str, body: bytes) -> str:
b64 = base64.b64encode(body).decode("ascii")
crc = crc24(body)
checksum = base64.b64encode(crc.to_bytes(3, "big")).decode("ascii")
lines = [f"-----BEGIN PGP {kind}-----", ""]
lines.extend(b64[i : i + 64] for i in range(0, len(b64), 64))
lines.append(f"={checksum}")
lines.append(f"-----END PGP {kind}-----")
return "\n".join(lines) + "\n"
def crc24(data: bytes) -> int:
crc = 0xB704CE
for octet in data:
crc ^= octet << 16
for _ in range(8):
crc <<= 1
if crc & 0x1000000:
crc ^= 0x1864CFB
return crc & 0xFFFFFF
def kms_sign(payload: bytes, args: argparse.Namespace) -> bytes:
with tempfile.TemporaryDirectory(prefix="burrow-kms-openpgp.") as tmp:
input_path = Path(tmp) / "payload"
signature_path = Path(tmp) / "signature.b64"
input_path.write_bytes(payload)
command = [
"gcloud",
"kms",
"asymmetric-sign",
"--location",
args.kms_location,
"--keyring",
args.kms_keyring,
"--key",
args.kms_key,
"--version",
args.kms_version,
"--digest-algorithm",
"sha256",
"--input-file",
str(input_path),
"--signature-file",
str(signature_path),
]
if args.gcloud_project:
command.extend(["--project", args.gcloud_project])
subprocess.run(command, check=True)
return base64.b64decode(signature_path.read_text(encoding="utf-8").strip())
def signature_packet(
signed_payload: bytes,
public_body: bytes,
sig_type: int,
created_at: int,
args: argparse.Namespace,
) -> bytes:
fpr = fingerprint(public_body)
key_id = fpr[-8:]
hashed = b"".join(
[
subpacket(2, struct.pack(">I", created_at)),
subpacket(33, b"\x04" + fpr),
]
)
unhashed = subpacket(16, key_id)
sig_data = (
b"\x04"
+ bytes([sig_type, RSA_PUBLIC_KEY_ALGO, SHA256_HASH_ALGO])
+ struct.pack(">H", len(hashed))
+ hashed
)
trailer = b"\x04\xff" + struct.pack(">I", len(sig_data))
to_hash = signed_payload + sig_data + trailer
digest = hashlib.sha256(to_hash).digest()
signature = kms_sign(to_hash, args)
body = (
sig_data
+ struct.pack(">H", len(unhashed))
+ unhashed
+ digest[:2]
+ mpi(int.from_bytes(signature, "big"))
)
return packet_header(2, len(body)) + body
def public_key_command(args: argparse.Namespace) -> int:
created_at = args.created_at or int(time.time())
n, e = parse_rsa_public_key_from_pem(Path(args.public_key_pem))
public_body = public_key_body(n, e, created_at)
public_packet = packet_header(6, len(public_body)) + public_body
uid = args.user_id.encode("utf-8")
uid_packet = packet_header(13, len(uid)) + uid
uid_signed_payload = (
old_packet_header(6, len(public_body))
+ public_body
+ b"\xb4"
+ struct.pack(">I", len(uid))
+ uid
)
cert_packet = signature_packet(
uid_signed_payload,
public_body,
sig_type=0x13,
created_at=created_at,
args=args,
)
key = public_packet + uid_packet + cert_packet
output = Path(args.output)
if args.armor:
output.write_text(armor("PUBLIC KEY BLOCK", key), encoding="utf-8")
else:
output.write_bytes(key)
return 0
def detach_sign_command(args: argparse.Namespace) -> int:
created_at = args.created_at or int(time.time())
n, e = parse_rsa_public_key_from_pem(Path(args.public_key_pem))
public_body = public_key_body(n, e, args.key_created_at)
payload = Path(args.input).read_bytes()
sig_type = 0x01 if args.text else 0x00
packet = signature_packet(payload, public_body, sig_type, created_at, args)
output = Path(args.output)
if args.armor:
output.write_text(armor("SIGNATURE", packet), encoding="utf-8")
else:
output.write_bytes(packet)
return 0
def add_kms_args(parser: argparse.ArgumentParser) -> None:
parser.add_argument("--kms-location", default=os.environ.get("BURROW_KMS_LOCATION", "global"))
parser.add_argument("--kms-keyring", default=os.environ.get("BURROW_KMS_KEYRING", "burrow-identity"))
parser.add_argument("--kms-key", required=not os.environ.get("BURROW_KMS_KEY"), default=os.environ.get("BURROW_KMS_KEY"))
parser.add_argument("--kms-version", default=os.environ.get("BURROW_KMS_VERSION", "1"))
parser.add_argument("--gcloud-project", default=os.environ.get("GOOGLE_CLOUD_PROJECT"))
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description=__doc__)
subcommands = parser.add_subparsers(dest="command", required=True)
public_key = subcommands.add_parser("public-key")
public_key.add_argument("--public-key-pem", required=True)
public_key.add_argument("--user-id", default="Burrow Package Repository <packages@burrow.net>")
public_key.add_argument("--output", required=True)
public_key.add_argument("--armor", action="store_true")
public_key.add_argument("--created-at", type=int)
add_kms_args(public_key)
public_key.set_defaults(func=public_key_command)
detach = subcommands.add_parser("detach-sign")
detach.add_argument("--public-key-pem", required=True)
detach.add_argument("--key-created-at", type=int, default=1704067200)
detach.add_argument("--input", required=True)
detach.add_argument("--output", required=True)
detach.add_argument("--armor", action="store_true")
detach.add_argument("--text", action="store_true")
detach.add_argument("--created-at", type=int)
add_kms_args(detach)
detach.set_defaults(func=detach_sign_command)
return parser
def main() -> int:
parser = build_parser()
args = parser.parse_args()
try:
return args.func(args)
except subprocess.CalledProcessError as exc:
print(f"gcloud signing failed: {exc}", file=sys.stderr)
return exc.returncode or 1
except Exception as exc:
print(f"error: {exc}", file=sys.stderr)
return 1
if __name__ == "__main__":
raise SystemExit(main())