mirror of
https://github.com/block/buzz.git
synced 2026-10-27 04:30:52 +00:00
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:
16
crates/buzz-relay/src/build_info.rs
Normal file
16
crates/buzz-relay/src/build_info.rs
Normal file
@@ -0,0 +1,16 @@
|
||||
//! Build-time identity compiled into the relay binary.
|
||||
|
||||
/// Full source commit SHA, or `unknown` outside a provenance-aware build.
|
||||
pub(crate) fn source_sha() -> &'static str {
|
||||
option_env!("BUZZ_SOURCE_SHA").unwrap_or("unknown")
|
||||
}
|
||||
|
||||
/// Stable build identifier, or `local` outside CI.
|
||||
pub(crate) fn build_id() -> &'static str {
|
||||
option_env!("BUZZ_BUILD_ID").unwrap_or("local")
|
||||
}
|
||||
|
||||
/// Build details URL, or `unknown` outside CI.
|
||||
pub(crate) fn build_url() -> &'static str {
|
||||
option_env!("BUZZ_BUILD_URL").unwrap_or("unknown")
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
//! NIP-01 WebSocket relay for Buzz private team communication.
|
||||
|
||||
mod admission;
|
||||
mod build_info;
|
||||
|
||||
/// REST API route handlers.
|
||||
pub mod api;
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user