Invalidate stale build provenance

This commit is contained in:
Meta Vens 2026-08-17 18:58:54 +02:00
commit 3e367c99cd
4 changed files with 84 additions and 11 deletions

View file

@ -1,30 +1,63 @@
use std::process::Command;
use std::{env, path::Path, process::Command};
fn main() {
println!("cargo:rerun-if-env-changed=GHOSTLIGHT_BUILD_COMMIT");
println!("cargo:rerun-if-changed=../../.git/HEAD");
let manifest_dir =
env::var("CARGO_MANIFEST_DIR").expect("Cargo must supply CARGO_MANIFEST_DIR");
let repo_root = Path::new(&manifest_dir).join("../..");
emit_git_rerun_paths(&repo_root);
// Release tooling injects the clean-tree commit. Git discovery is only a
// convenience for local builds because Cargo cannot reliably observe a
// moving branch ref through `.git/HEAD` alone.
let commit = std::env::var("GHOSTLIGHT_BUILD_COMMIT")
// convenience for local builds. The exact ref and reflog dependencies above
// keep that convenience from silently publishing stale provenance.
let commit = env::var("GHOSTLIGHT_BUILD_COMMIT")
.ok()
.filter(|value| is_commit(value))
.or_else(git_commit)
.or_else(|| git_commit(&repo_root))
.expect("GhostlightDungeon builds require an exact 40-hex source commit");
println!("cargo:rustc-env=GHOSTLIGHT_BUILD_COMMIT={commit}");
}
fn git_commit() -> Option<String> {
fn emit_git_rerun_paths(repo_root: &Path) {
for git_path in ["HEAD", "logs/HEAD", "packed-refs"] {
if let Some(path) = git_output(repo_root, &["rev-parse", "--git-path", git_path]) {
emit_rerun_path(repo_root, &path);
}
}
if let Some(reference) = git_output(repo_root, &["symbolic-ref", "-q", "HEAD"])
&& let Some(path) = git_output(repo_root, &["rev-parse", "--git-path", &reference])
{
emit_rerun_path(repo_root, &path);
}
}
fn emit_rerun_path(repo_root: &Path, git_path: &str) {
let path = Path::new(git_path);
let resolved = if path.is_absolute() {
path.to_path_buf()
} else {
repo_root.join(path)
};
println!("cargo:rerun-if-changed={}", resolved.display());
}
fn git_commit(repo_root: &Path) -> Option<String> {
git_output(repo_root, &["rev-parse", "HEAD"]).filter(|value| is_commit(value))
}
fn git_output(repo_root: &Path, args: &[&str]) -> Option<String> {
let output = Command::new("git")
.args(["rev-parse", "HEAD"])
.current_dir("../..")
.args(args)
.current_dir(repo_root)
.output()
.ok()?;
if !output.status.success() {
return None;
}
let value = String::from_utf8(output.stdout).ok()?.trim().to_owned();
is_commit(&value).then_some(value)
String::from_utf8(output.stdout)
.ok()
.map(|value| value.trim().to_owned())
.filter(|value| !value.is_empty())
}
fn is_commit(value: &str) -> bool {

View file

@ -0,0 +1,22 @@
use std::{path::Path, process::Command};
#[test]
fn embedded_commit_matches_checkout_head() {
let repo_root = Path::new(env!("CARGO_MANIFEST_DIR")).join("../..");
let output = Command::new("git")
.args(["rev-parse", "HEAD"])
.current_dir(repo_root)
.output()
.expect("git must be available for the provenance regression test");
assert!(output.status.success(), "git rev-parse HEAD failed");
let checkout_head = String::from_utf8(output.stdout)
.expect("git commit must be UTF-8")
.trim()
.to_owned();
assert_eq!(
env!("GHOSTLIGHT_BUILD_COMMIT"),
checkout_head,
"the binary embedded stale Git provenance"
);
}

View file

@ -758,6 +758,14 @@ atomic pointer switch, recorded in CultCache, and the rollback runbook lives in
Task Scheduler may be added when tester availability makes that useful; native
Windows service machinery is outside the MVP.
The binary owns its build provenance. Release tooling injects the clean-tree
commit through `GHOSTLIGHT_BUILD_COMMIT`; local builds derive it from Git while
the build script watches `HEAD`, its symbolic branch ref, the HEAD reflog, and
packed refs. The health projection only publishes that compile-time value.
Launchers and manifests may verify it but may not rewrite it. Activation is
rejected unless checkout HEAD, embedded commit, immutable manifest, binary
hash, and the running health projection agree.
Live VoidBot integration uses the independently verified Yggdrasil host and the
restored Starfire loopback crossing at `127.0.0.1:17875/mcp`. The repaired
tunnel task uses the live `F:\Projects` body. Retired local VoidBot/Qdrant

View file

@ -23,6 +23,16 @@ Vault evidence receipts + typed campaign snapshot
Ghostlight owns the generalized projection organ. Epiphany and other consumers
own their canonical Persona state and consequence commits.
Build provenance has one owner: `crates/ghostlight-dungeon/build.rs` binds the
exact source commit into the binary. Its inputs are either the release tool's
explicit clean-tree commit or Git HEAD; it watches the actual symbolic ref,
HEAD reflog, and packed refs so an ordinary commit invalidates Cargo's cached
value. Health derives its commit from that embedded value. Release manifests,
launch scripts, and CultCache deployment receipts are verifiers and records,
not alternate writers. Local runs and immutable releases use this same binding,
and the regression test compares it to the checkout's live `git rev-parse
HEAD`. A mismatch blocks activation rather than being repaired after launch.
Away-time agency follows a separate proposal/commit seam:
```text