Screenshots from the Rust build; system-roots feature for macOS cross-builds

docs/guide/img is re-shot from the 0.6 binary. TLS roots come from the
operating system by default; build.sh cross-builds macOS on Linux without
that feature (no Apple SDK), trusting the bundled Mozilla roots. Lock
tests wait for the reader to report it is blocked rather than assuming
how long unlocking takes on a busy machine.

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Andy Dixon
2026-10-08 18:27:32 +01:00
co-authored by Claude
parent 06cf7355dc
commit 8aee1f0f04
21 changed files with 62 additions and 15 deletions
+7 -1
View File
@@ -25,7 +25,13 @@ serde_json = "1"
jiff = "0.2" jiff = "0.2"
tar = { version = "0.4.46", default-features = false } tar = { version = "0.4.46", default-features = false }
flate2 = { version = "1.1", default-features = false, features = ["rust_backend"] } flate2 = { version = "1.1", default-features = false, features = ["rust_backend"] }
ureq = { version = "3.4", default-features = false, features = ["rustls", "platform-verifier", "gzip"] } ureq = { version = "3.4", default-features = false, features = ["rustls", "gzip"] }
[features]
default = ["system-roots"]
# Verify TLS certificates against the operating system's trust store. Off,
# the bundled Mozilla roots are used (for cross-built macOS binaries).
system-roots = ["ureq/platform-verifier"]
[dev-dependencies] [dev-dependencies]
aes = "0.9" aes = "0.9"
+3 -2
View File
@@ -134,8 +134,9 @@ for the passphrase.
## Network ## Network
Downloads verify TLS certificates (rustls with the platform verifier; it is Downloads verify TLS certificates (rustls with the operating system's trust
never disabled), follow at most 10 redirects (http and https only; for store; macOS binaries cross-built on Linux use the bundled Mozilla roots
instead, see `docs/PLATFORMS.md`; verification is never disabled), follow at most 10 redirects (http and https only; for
`mirror`, only to the same host), and are bounded by connect, response and `mirror`, only to the same host), and are bounded by connect, response and
whole-request deadlines and a byte budget counted after decompression. See whole-request deadlines and a byte budget counted after decompression. See
`vault(1)` for the defaults and flags. `vault(1)` for the defaults and flags.
+13 -3
View File
@@ -14,7 +14,9 @@
# #
# How each target is built, and what the host needs: # How each target is built, and what the host needs:
# zig cargo-zigbuild and zig on PATH (Linux, Windows GNU ABI) # zig cargo-zigbuild and zig on PATH (Linux, Windows GNU ABI)
# mac a Mac with Xcode (macOS) # mac a Mac with Xcode, or zig (macOS; zig builds trust the
# bundled Mozilla roots, not the
# keychain)
# cross `cross` and Docker (FreeBSD) # cross `cross` and Docker (FreeBSD)
# A requested target the host cannot build is reported, the others are still # A requested target the host cannot build is reported, the others are still
# built, and the script exits non-zero. See docs/PLATFORMS.md for the Go # built, and the script exits non-zero. See docs/PLATFORMS.md for the Go
@@ -98,8 +100,16 @@ build_one() {
{ echo "SKIPPED $want: needs cargo-zigbuild and zig" >&2; return 1; } { echo "SKIPPED $want: needs cargo-zigbuild and zig" >&2; return 1; }
cmd=(cargo zigbuild) ;; cmd=(cargo zigbuild) ;;
mac) mac)
[[ $HOST_OS == Darwin ]] || { echo "SKIPPED $want: needs a Mac with Xcode" >&2; return 1; } if [[ $HOST_OS == Darwin ]]; then
cmd=(cargo build) ;; cmd=(cargo build)
elif command -v cargo-zigbuild >/dev/null && command -v zig >/dev/null; then
# Without Apple's SDK there is no Security framework to link, so
# the cross-built binary trusts the bundled Mozilla roots rather
# than the macOS keychain.
cmd=(cargo zigbuild --no-default-features)
else
echo "SKIPPED $want: needs a Mac, or cargo-zigbuild and zig" >&2; return 1
fi ;;
cross) cross)
command -v cross >/dev/null && command -v docker >/dev/null || command -v cross >/dev/null && command -v docker >/dev/null ||
{ echo "SKIPPED $want: needs cross and docker" >&2; return 1; } { echo "SKIPPED $want: needs cross and docker" >&2; return 1; }
+8 -2
View File
@@ -33,8 +33,8 @@ the port: the Go release's more exotic targets are no longer built).
| windows/amd64 | x86_64-pc-windows-gnu | static CRT | zig | build; tests cross-compile cleanly; runtime in CI only (windows-2025 job, not yet run) | | windows/amd64 | x86_64-pc-windows-gnu | static CRT | zig | build; tests cross-compile cleanly; runtime in CI only (windows-2025 job, not yet run) |
| windows/386 | i686-pc-windows-gnu | static CRT | zig | build only | | windows/386 | i686-pc-windows-gnu | static CRT | zig | build only |
| windows/arm64 | aarch64-pc-windows-gnullvm | static CRT | zig | build only | | windows/arm64 | aarch64-pc-windows-gnullvm | static CRT | zig | build only |
| darwin/amd64 | x86_64-apple-darwin | system | a Mac (Xcode) | CI only (macos-15-intel job, not yet run) | | darwin/amd64 | x86_64-apple-darwin | system | a Mac, or zig (note 2) | build only from Linux; tested in CI on macos-15-intel |
| darwin/arm64 | aarch64-apple-darwin | system | a Mac (Xcode) | CI only (macos-15 job, not yet run) | | darwin/arm64 | aarch64-apple-darwin | system | a Mac, or zig (note 2) | build only from Linux; tested in CI on macos-15 |
| freebsd/amd64 | x86_64-unknown-freebsd | system | cross 0.2.5 (Docker) | build only (no emulator) | | freebsd/amd64 | x86_64-unknown-freebsd | system | cross 0.2.5 (Docker) | build only (no emulator) |
| freebsd/386 | i686-unknown-freebsd | system | cross 0.2.5 (Docker) | build only | | freebsd/386 | i686-unknown-freebsd | system | cross 0.2.5 (Docker) | build only |
@@ -47,6 +47,12 @@ disables core dumps. See `SECURITY.md`.
process spins). `build.sh` and `packaging/package.sh` build that target process spins). `build.sh` and `packaging/package.sh` build that target
with `--cfg getrandom_backend="linux_raw"`, which makes the `getrandom` with `--cfg getrandom_backend="linux_raw"`, which makes the `getrandom`
crate issue the system call itself; with that flag the binary passes. crate issue the system call itself; with that flag the binary passes.
2. Without Apple's SDK there is no Security framework to link, so `build.sh`
on Linux cross-builds macOS with `--no-default-features`: those binaries
verify TLS for `add URL` and `mirror` against the bundled Mozilla roots
instead of the macOS keychain (so a corporate CA added to the keychain is
not trusted). Built on a Mac, or through Homebrew
(`brew install andydixon/tap/vault`), they use the keychain.
## No longer released ## No longer released
Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 169 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

After

Width:  |  Height:  |  Size: 176 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 140 KiB

After

Width:  |  Height:  |  Size: 142 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 134 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 525 KiB

After

Width:  |  Height:  |  Size: 604 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 89 KiB

After

Width:  |  Height:  |  Size: 89 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 172 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 146 KiB

After

Width:  |  Height:  |  Size: 149 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 139 KiB

After

Width:  |  Height:  |  Size: 140 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 191 KiB

After

Width:  |  Height:  |  Size: 196 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 180 KiB

After

Width:  |  Height:  |  Size: 195 KiB

+8 -3
View File
@@ -135,9 +135,14 @@ impl Client {
Some(p) => Some(ureq::Proxy::new(p).map_err(|_| "invalid proxy setting in the environment".to_string())?), Some(p) => Some(ureq::Proxy::new(p).map_err(|_| "invalid proxy setting in the environment".to_string())?),
None => None, None => None,
}; };
let tls = ureq::tls::TlsConfig::builder() // The operating system's trust store; builds without the
.root_certs(ureq::tls::RootCerts::PlatformVerifier) // `system-roots` feature (macOS binaries cross-built without
.build(); // Apple's SDK) use the bundled Mozilla roots instead.
#[cfg(feature = "system-roots")]
let roots = ureq::tls::RootCerts::PlatformVerifier;
#[cfg(not(feature = "system-roots"))]
let roots = ureq::tls::RootCerts::WebPki;
let tls = ureq::tls::TlsConfig::builder().root_certs(roots).build();
let l = &self.limits; let l = &self.limits;
let cfg = ureq::Agent::config_builder() let cfg = ureq::Agent::config_builder()
.tls_config(tls) .tls_config(tls)
+23 -4
View File
@@ -313,7 +313,7 @@ fn hold_lock(e: &Env, bin: &str) -> Child {
c.stdin.as_mut().unwrap().write_all(b"\0").unwrap(); c.stdin.as_mut().unwrap().write_all(b"\0").unwrap();
// Wait until the lock file is held exclusively: probe with our own // Wait until the lock file is held exclusively: probe with our own
// fcntl lock attempt from this process. // fcntl lock attempt from this process.
let deadline = Instant::now() + Duration::from_secs(20); let deadline = Instant::now() + Duration::from_secs(120);
loop { loop {
if locked_exclusively(&e.dir.join("store/.lock")) { if locked_exclusively(&e.dir.join("store/.lock")) {
return c; return c;
@@ -350,13 +350,32 @@ fn lock_contention(holder: &str, waiter: &str) {
.stderr(Stdio::piped()) .stderr(Stdio::piped())
.spawn() .spawn()
.unwrap(); .unwrap();
std::thread::sleep(Duration::from_millis(1500)); // Wait (however long unlocking takes on a busy machine) until the
// reader says it is blocked on the lock, and is still running.
let mut err = w.stderr.take().unwrap();
let (tx, rx) = std::sync::mpsc::channel();
std::thread::spawn(move || {
use std::io::Read;
let mut all = Vec::new();
let mut buf = [0u8; 256];
while let Ok(n) = err.read(&mut buf) {
if n == 0 {
break;
}
all.extend_from_slice(&buf[..n]);
if String::from_utf8_lossy(&all).contains("waiting for another vault process to finish") {
let _ = tx.send(());
}
}
});
rx.recv_timeout(Duration::from_secs(120))
.expect("reader never reported waiting for the lock");
std::thread::sleep(Duration::from_millis(300));
assert!(w.try_wait().unwrap().is_none(), "reader did not wait for the writer"); assert!(w.try_wait().unwrap().is_none(), "reader did not wait for the writer");
h.kill().unwrap(); // death releases the lock h.kill().unwrap(); // death releases the lock
let _ = h.wait(); let _ = h.wait();
let o = w.wait_with_output().unwrap(); let o = w.wait_with_output().unwrap();
assert!(o.status.success()); assert!(o.status.success(), "reader failed after the writer died");
assert!(String::from_utf8_lossy(&o.stderr).contains("waiting for another vault process to finish"));
} }
#[cfg(unix)] #[cfg(unix)]