upleb.uk

Public git repos — served from a NIP-34 GRASP relay at git.upleb.uk

summaryrefslogtreecommitdiff
path: root/src/git
diff options
context:
space:
mode:
authorDanConwayDev <DanConwayDev@protonmail.com>2026-02-03 21:21:33 +0000
committerDanConwayDev <DanConwayDev@protonmail.com>2026-02-03 21:41:19 +0000
commitd392f0bc14bcd687e918d4653ae016226496b4c4 (patch)
tree2738b66be231c90615b15d2da1266e16704c510d /src/git
parentc163d717147b92b16d89da2fbccef775647b5a07 (diff)
feat: add diagnostic logging for partial state event matches
Improves observability when pushes are rejected due to state events that only partially match the pushed refs. Previously, logs only showed 'No state event found' even when state events existed but didn't match. Changes: - Add diagnose_state_mismatch() to explain why state events don't match - Log specific reasons: missing refs, wrong SHAs, or extra refs - Update rejection message to 'No matching state event found' (more accurate) - Add 4 unit tests for diagnostic function Example diagnostic output: WARN State event abc123 from authorized author doesn't match push: refs/heads/main missing (state declares 9cc3d93b) This addresses the issue where a push with only refs/heads/test was rejected because the state event also declared refs/heads/main, but logs didn't explain why the match failed.
Diffstat (limited to 'src/git')
-rw-r--r--src/git/authorization.rs80
-rw-r--r--src/git/handlers.rs2
2 files changed, 79 insertions, 3 deletions
diff --git a/src/git/authorization.rs b/src/git/authorization.rs
index db2b992..27107db 100644
--- a/src/git/authorization.rs
+++ b/src/git/authorization.rs
@@ -666,12 +666,88 @@ pub async fn get_state_authorization_for_specific_owner_repo(
666 debug!("Purgatory events found but none from authorized authors"); 666 debug!("Purgatory events found but none from authorized authors");
667 } 667 }
668 } else { 668 } else {
669 debug!("No matching state events found in purgatory"); 669 // Check if there are ANY state events in purgatory for this identifier
670 let all_purgatory_states = purgatory.find_state(identifier);
671
672 if !all_purgatory_states.is_empty() {
673 // There are state events but none match the push - diagnose why
674 debug!(
675 "Found {} state event(s) in purgatory for {} but none match the push",
676 all_purgatory_states.len(),
677 identifier
678 );
679
680 // Count authorized state events and collect diagnostic info
681 let mut authorized_count = 0;
682 let mut diagnostic_reasons = Vec::new();
683
684 // Diagnose why each authorized state event doesn't match
685 for entry in all_purgatory_states.iter() {
686 let author_hex = entry.event.pubkey.to_hex();
687 if authorized.contains(&author_hex) {
688 authorized_count += 1;
689 if let Some(reason) = crate::purgatory::diagnose_state_mismatch(
690 &entry.event,
691 &pushed_updates,
692 &local_refs,
693 ) {
694 debug!(
695 "State event {} from authorized author {} doesn't match push: {}",
696 entry.event.id,
697 entry
698 .event
699 .pubkey
700 .to_bech32()
701 .unwrap_or_else(|_| author_hex.clone()),
702 reason
703 );
704 diagnostic_reasons.push(reason);
705 }
706 }
707 }
708
709 // Create concise WARN message summarizing the rejection
710 let summary = if authorized_count > 0 {
711 let reason_summary = if !diagnostic_reasons.is_empty() {
712 // Take the first diagnostic reason as representative
713 format!(" ({})", diagnostic_reasons[0])
714 } else {
715 String::new()
716 };
717 format!(
718 "{} state event{} in purgatory from authorized publisher{} but doesn't match push{}",
719 authorized_count,
720 if authorized_count == 1 { "" } else { "s" },
721 if authorized_count == 1 { "" } else { "s" },
722 reason_summary
723 )
724 } else {
725 format!(
726 "{} state event{} in purgatory but none from authorized publishers",
727 all_purgatory_states.len(),
728 if all_purgatory_states.len() == 1 {
729 ""
730 } else {
731 "s"
732 }
733 )
734 };
735
736 warn!("Push rejected for {}: {}", identifier, summary);
737 return Ok(AuthorizationResult::denied(summary));
738 } else {
739 debug!("No state events found in purgatory for {}", identifier);
740 warn!(
741 "Push rejected for {}: No state events in purgatory",
742 identifier
743 );
744 return Ok(AuthorizationResult::denied("No state events in purgatory"));
745 }
670 } 746 }
671 747
672 // No matching state found in purgatory 748 // No matching state found in purgatory
673 Ok(AuthorizationResult::denied( 749 Ok(AuthorizationResult::denied(
674 "No state event found in purgatory from authorized publishers", 750 "No matching state event found in purgatory from authorized publishers",
675 )) 751 ))
676} 752}
677 753
diff --git a/src/git/handlers.rs b/src/git/handlers.rs
index e3a6ad4..7244abb 100644
--- a/src/git/handlers.rs
+++ b/src/git/handlers.rs
@@ -254,7 +254,7 @@ pub async fn handle_receive_pack(
254 } 254 }
255 255
256 // GRASP Authorization Check 256 // GRASP Authorization Check
257 info!( 257 debug!(
258 "Authorizing push for {} owned by {} via database query", 258 "Authorizing push for {} owned by {} via database query",
259 identifier, owner_pubkey 259 identifier, owner_pubkey
260 ); 260 );