feat(egress): thread durable bearer capability through Blossom media auth (C2b)

Bring the owner-derived Blossom bearers into the durable-capability world so
a header minted under identity A cannot be attached after a transition to B.

mint_media_get_auth and the do_upload t=upload mint now sign under a bounded
egress lease (issuance is an ordinary leased operation, spec L4569-4570) and
return/hold an OwnerIdentityCapability<BearerPolicy> registered with its
revocation handle. The four get-auth attach sites (media_download,
personas::card, media_proxy x2) and the upload dispatch validate the bearer
via admit_exercise() immediately before the HTTP send — a stale capability
attaches nothing (get-auth stays fail-open) or uploads zero bytes.

mint_media_get_auth becomes async; the ripple is a mechanical .await through
its four already-async callers. Removes the register_owner_bearer
allow(dead_code) now that C2b consumes it. Substrate coverage is unchanged:
the stale-bearer zero-bytes and registration-completeness controls already
pin the exercise behavior these guards depend on.

2438 lib tests pass, clippy + fmt clean.

Co-authored-by: Will Pfleger <pfleger.will@gmail.com>
Signed-off-by: Will Pfleger <pfleger.will@gmail.com>
This commit is contained in:
Duncan
2026-08-17 23:36:37 -04:00
co-authored by Will Pfleger
parent 05d7eca1ae
commit 02e238b39a
5 changed files with 78 additions and 20 deletions
+45 -9
View File
@@ -6,6 +6,7 @@ use tauri::State;
use tokio_util::sync::CancellationToken;
use crate::app_state::AppState;
use crate::owner_identity_egress::{BearerPolicy, OwnerIdentityCapability};
use crate::relay::{parse_json_response, relay_api_base_url_with_override, relay_error_message};
use super::media_transcode::{
@@ -347,8 +348,15 @@ pub(crate) fn sign_blossom_get_auth_header(
))
}
/// Mint a `t=get` Authorization header value for a relay media fetch, or
/// `None` when signing is unavailable (identity in recovery mode).
/// Mint a `t=get` Authorization header value for a relay media fetch, paired
/// with the durable [`OwnerIdentityCapability`] that governs it, or `None` when
/// signing is unavailable (identity in recovery mode).
///
/// The header is owner-derived bearer authority that a LATER HTTP transmission
/// attaches, so the caller must validate the returned capability
/// ([`OwnerIdentityCapability::admit_exercise`]) immediately before attaching
/// the header — a stale capability (its issuing identity superseded by a
/// transition) attaches nothing.
///
/// Fail-open by design: while the relay's `BUZZ_REQUIRE_MEDIA_GET_AUTH` flag
/// is off, an unauthenticated request still succeeds, so degrading to no
@@ -359,7 +367,10 @@ pub(crate) fn sign_blossom_get_auth_header(
/// Safety contract: callers must only attach the returned header to URLs
/// constructed from (or validated against) the app's own relay base URL —
/// never to third-party origins, where the bearer token would leak.
pub(crate) fn mint_media_get_auth(state: &AppState, base_url: &str) -> Option<String> {
pub(crate) async fn mint_media_get_auth(
state: &AppState,
base_url: &str,
) -> Option<(String, OwnerIdentityCapability<BearerPolicy>)> {
let keys = match state.signing_keys() {
Ok(k) => k,
Err(e) => {
@@ -367,13 +378,27 @@ pub(crate) fn mint_media_get_auth(state: &AppState, base_url: &str) -> Option<St
return None;
}
};
match sign_blossom_get_auth_header(&keys, base_url, MEDIA_GET_AUTH_EXPIRY_SECS) {
Ok(header) => Some(header),
// Issuance runs under a bounded egress lease: signing the bearer is an
// ordinary leased operation (spec L4569-4570). Registering the durable
// capability while the lease is held keeps its generation stamp coherent
// with the state that admitted the sign — no bump can slip between.
let lease = match crate::owner_identity_egress::try_admit_owner_identity_egress().await {
Ok(lease) => lease,
Err(e) => {
eprintln!("buzz-desktop: media get auth egress refused (unsigned request): {e}");
return None;
}
};
let header = match sign_blossom_get_auth_header(&keys, base_url, MEDIA_GET_AUTH_EXPIRY_SECS) {
Ok(header) => header,
Err(e) => {
eprintln!("buzz-desktop: media get auth signing failed (unsigned request): {e}");
None
return None;
}
}
};
let bearer = crate::owner_identity_egress::register_owner_bearer();
drop(lease);
Some((header, bearer))
}
fn sign_blossom_upload_auth(
@@ -440,9 +465,16 @@ async fn do_upload(
300
};
let base_url = relay_api_base_url_with_override(state);
let auth_event = {
// Issuance runs under a bounded egress lease (spec L4569-4570); the durable
// bearer registered under it carries the upload authority forward to the
// HTTP dispatch below, which validates it before each attempt.
let (auth_event, bearer) = {
let lease = crate::owner_identity_egress::try_admit_owner_identity_egress().await?;
let keys = state.signing_keys()?;
sign_blossom_upload_auth(&keys, &sha256, expiry_secs, &base_url)?
let event = sign_blossom_upload_auth(&keys, &sha256, expiry_secs, &base_url)?;
let bearer = crate::owner_identity_egress::register_owner_bearer();
drop(lease);
(event, bearer)
};
let auth_header = format!(
@@ -453,6 +485,10 @@ async fn do_upload(
if let Some((app, progress_id)) = progress.as_ref() {
emit_media_upload_phase(app, Some(progress_id.as_str()), "uploading");
}
// Validate the bearer immediately before transmitting the signed upload
// header: an identity superseded between minting and send uploads zero
// bytes rather than writing to the relay under stale authority.
bearer.admit_exercise()?;
let mut resp = send_upload_attempt(
state,
UploadAttempt {
@@ -292,8 +292,13 @@ async fn fetch_blob_bytes_with_cap(
// `validate_download_url`, satisfying the mint_media_get_auth safety
// contract (the token never leaves the relay origin).
let relay_base = relay_api_base_url_with_override(state);
if let Some(auth) = mint_media_get_auth(state, &relay_base) {
req = req.header("authorization", auth);
if let Some((auth, bearer)) = mint_media_get_auth(state, &relay_base).await {
// Validate the durable bearer immediately before attaching it: a
// capability whose issuing identity was superseded by a transition
// attaches nothing (the fetch proceeds unauthenticated, fail-open).
if bearer.admit_exercise().is_ok() {
req = req.header("authorization", auth);
}
}
let resp = req.send().await.map_err(|e| classify_request_error(&e))?;
@@ -672,9 +672,17 @@ pub async fn mint_agent_card(
// header ONLY for same-origin URLs so the token never leaves the
// relay (same contract as `media_download.rs`).
let relay_base = crate::relay::relay_api_base_url_with_override(&state);
let auth = is_same_origin(url, &relay_base)
.then(|| crate::commands::media::mint_media_get_auth(&state, &relay_base))
.flatten();
// Mint get-auth ONLY for same-origin URLs so the token never leaves
// the relay, then validate the durable bearer before attaching it —
// a superseded identity's header is dropped (fetch stays fail-open).
let auth = if is_same_origin(url, &relay_base) {
crate::commands::media::mint_media_get_auth(&state, &relay_base)
.await
.filter(|(_, bearer)| bearer.admit_exercise().is_ok())
.map(|(header, _)| header)
} else {
None
};
fetch_avatar(url, auth.as_deref()).await?
}
_ => {
+10 -4
View File
@@ -59,8 +59,11 @@ async fn proxy_handler(AxumState(state): AxumState<ProxyState>, req: Request) ->
// `upstream_url` is always `{relay base}{path}`, so the token can't reach
// a third-party origin (mint_media_get_auth safety contract).
if let Some(auth) = mint_media_get_auth(&app_state, &base_url) {
upstream = upstream.header("authorization", auth);
if let Some((auth, bearer)) = mint_media_get_auth(&app_state, &base_url).await {
// A bearer whose issuing identity was superseded attaches nothing.
if bearer.admit_exercise().is_ok() {
upstream = upstream.header("authorization", auth);
}
}
if let Some(range) = req.headers().get("range") {
@@ -187,8 +190,11 @@ pub async fn handle_buzz_media(
// `upstream_url` is always `{relay base}{path}`, so the token can't reach
// a third-party origin (mint_media_get_auth safety contract).
if let Some(auth) = mint_media_get_auth(&state, &base) {
upstream = upstream.header("authorization", auth);
if let Some((auth, bearer)) = mint_media_get_auth(&state, &base).await {
// A bearer whose issuing identity was superseded attaches nothing.
if bearer.admit_exercise().is_ok() {
upstream = upstream.header("authorization", auth);
}
}
if let Some(range) = request.headers().get("range") {
@@ -69,6 +69,11 @@
//! - `managed_agents::persona_events`, `commands::personas::sharing`,
//! `commands::channels` (×3), and the huddle STT task (per-send inline
//! owner lease).
//! - **Durable bearers** — `commands::media::mint_media_get_auth` (Blossom
//! `t=get`) and the `do_upload` `t=upload` mint sign under a bounded lease
//! and register an [`OwnerIdentityCapability<BearerPolicy>`]; the four
//! get-auth attach sites (`media_download`, `personas::card`, `media_proxy`
//! ×2) and the upload dispatch validate it before the HTTP send.
//!
//! Managed-agent keyed (`admit_managed_agent_egress`) — the four sink sites
//! plus the git-workflow branch, variant selected at runtime by
@@ -664,8 +669,6 @@ pub fn register_owner_session(cancel: CancellationToken) -> OwnerIdentityCapabil
/// The returned capability stamps the current generation; every attach site
/// validates it via [`OwnerIdentityCapability::admit_exercise`] before the HTTP
/// dispatch.
// Consumed by the Blossom attach sites in C2b; remove allow when they land.
#[allow(dead_code)]
pub fn register_owner_bearer() -> OwnerIdentityCapability<BearerPolicy> {
let generation = REGISTRY.generation.load(Ordering::Acquire);
let mut durable = lock_durable();