Bazel rules for building Binary Self-eXtracting archives (BSX) with integrity verification and cryptographic signing.
A BSX file is a single executable shell script containing an embedded gzipped
tarball. When run, it verifies the payload checksum, extracts the contents to a
temporary directory, and executes an embedded script. No external tools beyond a
POSIX shell, tar, and base64 are required to run the output.
Add to your MODULE.bazel:
bazel_dep(name = "rules_bsx", version = "0.1.0")Creates a self-extracting archive.
load("@rules_bsx//:bsx.bzl", "bsx")
bsx(
name = "deploy",
data = {
":config": "config.yaml",
":binary": "bin/server",
},
main = "deploy.sh",
)| Attribute | Type | Description |
|---|---|---|
cmd |
string |
Inline shell command to execute after extraction. Mutually exclusive with main. |
main |
label |
Script file to execute after extraction. Mutually exclusive with cmd. |
data |
label_keyed_string_dict |
Files to include in the archive. Keys are labels, values are destination paths within the archive. |
tarballs |
label_list |
Pre-built tarballs (.tar, .tar.gz, .tgz) to merge into the archive. Later entries override earlier ones. |
Exactly one of cmd or main must be provided. The script runs with the
extraction directory as its working directory and receives any command-line
arguments passed to the BSX file.
Creates a detached cryptographic signature for a BSX file. The signature covers the entire file (preamble + payload), so any modification is detected.
load("@rules_bsx//:bsx.bzl", "bsx_sign")
bsx_sign(
name = "deploy_signed",
bsx = ":deploy",
signing_key_cmd = "op read 'op://Vault/deploy-key/private_key'",
)| Attribute | Type | Description |
|---|---|---|
bsx |
label |
The BSX target to sign. Required. |
signing_key_cmd |
string |
Shell command that outputs the signing private key (PEM) to stdout. Runs at bazel run time, so interactive key retrieval (e.g., 1Password, YubiKey) works. Required. |
output_name |
string |
Filename for the signed output. Defaults to the BSX target's filename. |
Run with bazel run to produce the signed binary and its .sig file:
$ bazel run :deploy_signed
Signed: /path/to/deploy
Signature: /path/to/deploy.sig
Key type: Private-Key: (2048 bit, 2 primes)
Verify with:
openssl pkeyutl -verify -pubin -inkey <pubkey.pem> -rawin -digest sha256 -in deploy -sigfile deploy.sig
Signing happens at bazel run time (not build time) so that interactive key
retrieval works. The key type is auto-detected — RSA and ECDSA use -digest sha256, while algorithms with built-in hashing (ML-DSA, Ed25519, Ed448) omit
it.
OpenSSL requirement: bsx_sign invokes openssl from your host PATH (not
inside the Bazel sandbox). RSA and ECDSA signing work with any OpenSSL or
LibreSSL version. ML-DSA and Ed25519 require OpenSSL 3.5+ (they use pkeyutl -rawin, which is not supported by LibreSSL or older OpenSSL). On macOS, the
system /usr/bin/openssl is LibreSSL — install OpenSSL via Homebrew and ensure
it is on your PATH for ML-DSA/Ed25519 support.
Every BSX file embeds a SHA-256 checksum of the payload. Before extraction, the checksum is verified — if it doesn't match, the file exits with an error. This catches accidental corruption (truncated downloads, bad sectors, etc).
This is not a security measure against tampering. An attacker who can modify
the payload can also modify the embedded checksum. For tamper resistance, use
bsx_sign with a detached signature verified externally against a trusted
public key.
| Variable | Description |
|---|---|
BSX_EXTRACT_DIR |
If set, extract to this directory instead of a temporary one. The directory is not cleaned up on exit. |
Apache License 2.0. See LICENSE.