Qualify canonical relay images for staged delivery (#6781)

## Why

Phase 1 of progressive delivery needs an explicit eligibility boundary
before deployment automation can safely consume relay artifacts. Today
the canonical Docker workflow can publish independently of the same-SHA
CI result, the relay does not expose its source/build identity, and the
Helm chart only renders mutable tags.

The existing `ghcr.io/block/buzz-staging-dev` workflow in #6709 remains
a manual pre-merge preview lane. It is deliberately not a canonical
promotion input.

## What changed

- Compile the full source SHA and GitHub Actions build identity into
canonical relay binaries and expose it at `/_status`.
- Gate creation of tagged multi-architecture manifests on the latest
exact-SHA `CI` push run succeeding on `main` or `release`.
- Preserve the signed SLSA provenance and add a signed, release-only
deployment-eligibility predicate with source, build run, qualifying CI
run, and exact compatible Buzz chart version.
- Keep failed or pending builds untagged and without an eligibility
predicate; preview-package artifacts are excluded by package and signer
contract.
- Add optional `image.digest` support to Buzz chart `0.1.8`, retaining
the existing tag fallback.
- Document verification and the intentional boundary between image/chart
compatibility and backwards-compatible database migrations.

## Risk and rollout

Canonical tag publication now waits for same-SHA CI and fails closed
when the newest matching run fails. Per-architecture blobs may remain
untagged after a failed qualification; they are not promotion inputs.
Debug manifests are also CI-gated but do not receive the release
eligibility predicate.

There are no migration or schema-orchestration changes. The companion
BuilderBot infrastructure PR will pin a separately verified canonical
main digest and expose a read-only running-deployment inspection
command. Signed eligibility is available only for artifacts published
after this workflow lands.

## Verification

- Blox: workflow contract fixtures, including stale-green rejection and
predicate structure
- Blox: `actionlint` for `ci.yml` and `docker.yml`
- Blox: 46 Helm unit tests, chart lint, and production/quickstart render
fixtures
- Blox: relay status unit test, workspace Rust formatting, and workspace
all-target clippy
- Live evidence: exact-SHA CI selector chose successful run
`32857345153` for source `9d1e4b257657f382d3111ce748f3da8d063b7671`
- Existing canonical digest provenance verified with `gh attestation
verify`

## References

- #6709 — developer-preview precursor; intentionally separate from
canonical eligibility
- `docs/deployment-identity.md`

*Generated with Codex*

---------

Signed-off-by: Luke Tornquist <tornquist@squareup.com>
This commit is contained in:
Luke Tornquist
2026-08-25 14:44:49 -04:00
committed by GitHub
parent 7ba1197aa6
commit f249710331
17 changed files with 467 additions and 10 deletions

View File

@@ -413,14 +413,22 @@ async fn readiness_handler(State(state): State<Arc<AppState>>) -> impl IntoRespo
}
}
/// Status endpoint — service name, version, uptime.
async fn status_handler(State(state): State<Arc<AppState>>) -> impl IntoResponse {
let uptime_secs = state.started_at.elapsed().as_secs();
Json(json!({
fn status_payload(uptime_secs: u64) -> serde_json::Value {
json!({
"service": "buzz-relay",
"version": env!("CARGO_PKG_VERSION"),
"uptime_seconds": uptime_secs,
}))
"build": {
"source_sha": crate::build_info::source_sha(),
"id": crate::build_info::build_id(),
"url": crate::build_info::build_url(),
},
})
}
/// Status endpoint — service name, version, uptime, and intrinsic build identity.
async fn status_handler(State(state): State<Arc<AppState>>) -> impl IntoResponse {
Json(status_payload(state.started_at.elapsed().as_secs()))
}
/// `/_mesh` — live mesh status: peer table, connection/phi state, per-peer
@@ -506,6 +514,23 @@ mod tests {
assert!(!should_serve_spa("/arbitrary", true));
}
#[test]
fn status_payload_exposes_source_and_build_identity() {
let payload = status_payload(42);
assert_eq!(payload["service"], "buzz-relay");
assert_eq!(payload["version"], env!("CARGO_PKG_VERSION"));
assert_eq!(payload["uptime_seconds"], 42);
for field in ["source_sha", "id", "url"] {
assert!(
payload["build"][field]
.as_str()
.is_some_and(|value| !value.is_empty()),
"build.{field} must be a non-empty string"
);
}
}
#[tokio::test(flavor = "current_thread")]
async fn http_and_datastore_spans_are_exported_in_the_same_trace() {
let exporter = InMemorySpanExporter::default();