From 1d09e4bdea7e328cf2740818df9df660c5532a99 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Fri, 13 Feb 2026 13:24:46 +0000 Subject: feat: implement announcement purgatory core (breaks archive sync test) Route new announcements to purgatory instead of accepting immediately. Announcements are promoted to the database when git data arrives, ensuring we only serve announcements for repos with actual content. Implemented: - AnnouncementPurgatoryEntry type and DashMap store - Route new announcements to purgatory (replacement announcements skip) - Promote announcements on git data arrival (process_purgatory_announcements) - Authorization checks purgatory announcements (fetch_repository_data_with_purgatory) - State policy uses purgatory announcements for maintainer validation - Cleanup task handles announcement expiry - Updated count()/cleanup() to 3-tuples Known broken: - test_archive_read_only_creates_bare_repo fails: sync module does not treat purgatory announcements as confirmed repos, so per-repo sync (state events, PRs) is never triggered for purgatory announcements - Announcement persistence (save/restore) not implemented - SyncLevel (StateOnly vs Full) not implemented - Soft expiry two-phase not implemented - Expiry extension on state event / git auth not wired up --- src/git/authorization.rs | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) (limited to 'src/git/authorization.rs') diff --git a/src/git/authorization.rs b/src/git/authorization.rs index e174b51..9d53c4f 100644 --- a/src/git/authorization.rs +++ b/src/git/authorization.rs @@ -287,6 +287,39 @@ pub async fn fetch_repository_data( }) } +/// Fetch repository data including announcements from purgatory +/// +/// This combines database announcements with purgatory announcements, +/// which is needed for authorization when the announcement hasn't been +/// promoted yet (no git data has arrived). +pub async fn fetch_repository_data_with_purgatory( + database: &SharedDatabase, + purgatory: &crate::purgatory::Purgatory, + identifier: &str, +) -> Result { + // First, fetch from database + let mut repo_data = fetch_repository_data(database, identifier).await?; + + // Then, add announcements from purgatory + let purgatory_announcements = purgatory.get_announcements_by_identifier(identifier); + let purgatory_count = purgatory_announcements.len(); + + for entry in purgatory_announcements { + if let Ok(announcement) = RepositoryAnnouncement::from_event(entry.event) { + repo_data.announcements.push(announcement); + } + } + + debug!( + "Fetched repository data with purgatory: {} announcements ({} from purgatory), {} states", + repo_data.announcements.len(), + purgatory_count, + repo_data.states.len() + ); + + Ok(repo_data) +} + pub fn pubkey_authorised_for_repo_owners( pubkey: &PublicKey, db_repo_data: &RepositoryData, @@ -539,8 +572,9 @@ pub async fn get_state_authorization_for_specific_owner_repo( use crate::git::list_refs; use crate::purgatory::RefUpdate; - // Fetch announcements only - we don't need database states - let repo_data = fetch_repository_data(database, identifier).await?; + // Fetch announcements from database AND purgatory - needed for authorization + // when the announcement hasn't been promoted yet (no git data has arrived) + let repo_data = fetch_repository_data_with_purgatory(database, purgatory, identifier).await?; if repo_data.announcements.is_empty() { return Ok(AuthorizationResult::denied( -- cgit v1.2.3 From c3dedb7a5b527c3a3deb1e781aba9d562c6eb294 Mon Sep 17 00:00:00 2001 From: DanConwayDev Date: Mon, 23 Feb 2026 12:54:57 +0000 Subject: feat: extend purgatory announcement expiry during git push authorization Per design doc decision #4: when git auth finds a matching state event in purgatory that authorizes a push, extend the announcement's expiry. The repo is actively receiving git data so the announcement should not expire prematurely. Also triggers revival of soft-expired announcements. --- src/git/authorization.rs | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'src/git/authorization.rs') diff --git a/src/git/authorization.rs b/src/git/authorization.rs index 9d53c4f..69a0751 100644 --- a/src/git/authorization.rs +++ b/src/git/authorization.rs @@ -661,6 +661,27 @@ pub async fn get_state_authorization_for_specific_owner_repo( .unwrap_or_else(|_| latest_authorized.pubkey.to_hex()) ); + // Extend purgatory announcement expiry for the owner. + // + // Per design doc decision #4: git auth extending a state event's expiry + // also extends the announcement's expiry. The repo is actively receiving + // git data, so the announcement should not expire prematurely. + // This also revives soft-expired announcements (recreates bare repo). + if let Ok(owner_pk) = PublicKey::parse(owner_pubkey) { + if purgatory.has_purgatory_announcement(&owner_pk, identifier) { + purgatory.extend_announcement_expiry( + &owner_pk, + identifier, + std::time::Duration::from_secs(1800), + ); + debug!( + identifier = %identifier, + owner = %owner_pubkey, + "Extended purgatory announcement expiry due to git push authorization" + ); + } + } + return Ok(AuthorizationResult { authorized: true, reason: "Authorized by state event in purgatory".to_string(), -- cgit v1.2.3