@@ -427,10 +427,19 @@ fn verify_ref_update(event: &RefUpdateEvent) -> Result<(), String> {
427427pub ( crate ) enum IngestOutcome {
428428 /// The event was authenticated AND every write it implies landed.
429429 Accepted ,
430+ /// The event was admitted WITHOUT authentication, through the rolling-upgrade
431+ /// window, and every write it implies landed.
432+ ///
433+ /// Deliberately a distinct outcome from `Accepted`: a valid signature proves
434+ /// who the sender is, an unsigned event proves nothing, and counting the two
435+ /// under one label would make the fleet's authenticated-admission rate
436+ /// indistinguishable from its reliance on the legacy compatibility path.
437+ UnsignedAdmitted ,
430438 /// The event passed every guard, but a durable write failed. The decision
431439 /// was still "admit it", so this is not a refusal; it exists because
432- /// returning `Accepted` for an event whose row never landed would make the
433- /// outcome an observability lie.
440+ /// returning an admission outcome (`Accepted` or `UnsignedAdmitted`) for an
441+ /// event whose row never landed would make the outcome an observability
442+ /// lie.
434443 WriteFailed ( String ) ,
435444 /// The event was dropped. Nothing is stored, so the reason exists only to
436445 /// be logged and counted.
@@ -468,6 +477,7 @@ impl IngestOutcome {
468477 fn metric_label ( & self ) -> & ' static str {
469478 match self {
470479 IngestOutcome :: Accepted => "accepted" ,
480+ IngestOutcome :: UnsignedAdmitted => "unsigned_admitted" ,
471481 IngestOutcome :: WriteFailed ( _) => "write_failed" ,
472482 IngestOutcome :: Rejected ( _) => "rejected" ,
473483 IngestOutcome :: SourceRateLimited => "source_rate_limited" ,
@@ -747,6 +757,7 @@ pub(crate) async fn ingest_ref_update(
747757 }
748758 match write_error {
749759 Some ( reason) => IngestOutcome :: WriteFailed ( reason) ,
760+ None if unsigned => IngestOutcome :: UnsignedAdmitted ,
750761 None => IngestOutcome :: Accepted ,
751762 }
752763}
@@ -1030,6 +1041,7 @@ pub async fn start(
10301041 crate :: metrics:: record_gossip_ingest( outcome. metric_label( ) ) ;
10311042 match outcome {
10321043 IngestOutcome :: Accepted => { }
1044+ IngestOutcome :: UnsignedAdmitted => { }
10331045 IngestOutcome :: WriteFailed ( reason) => warn!(
10341046 from = %propagation_source,
10351047 reason = %reason,
@@ -1803,6 +1815,9 @@ mod tests {
18031815 match outcome {
18041816 IngestOutcome :: Rejected ( reason) => reason,
18051817 IngestOutcome :: Accepted => panic ! ( "{context}: the event must be rejected" ) ,
1818+ IngestOutcome :: UnsignedAdmitted => {
1819+ panic ! ( "{context}: the event must be rejected, not admitted unsigned" )
1820+ }
18061821 IngestOutcome :: WriteFailed ( reason) => {
18071822 panic ! ( "{context}: the event must be rejected by a guard, not admitted and then failed to write: {reason}" )
18081823 }
@@ -2094,10 +2109,13 @@ mod tests {
20942109 }
20952110
20962111 /// R7, the rolling-upgrade window: with enforcement off, an unsigned event
2097- /// from a KNOWN peer is still accepted . Turning the flag on is the
2112+ /// from a KNOWN peer is still admitted . Turning the flag on is the
20982113 /// operator's step, not a code change, so this path has to keep working
20992114 /// until they take it.
21002115 ///
2116+ /// It is `UnsignedAdmitted`, not `Accepted`: the event wrote rows without
2117+ /// authenticating its sender, and the two must not be observably the same.
2118+ ///
21012119 /// The ingest path also emits a `warn!` pointing at the flag on this
21022120 /// branch. Nothing here asserts that, so the log line is uncovered:
21032121 /// deleting it leaves this test green. Say so rather than implying the
@@ -2113,8 +2131,8 @@ mod tests {
21132131 let outcome =
21142132 ingest_with_fresh_limiter ( & db, false , true , & bytes_of ( & event) , & PeerId :: random ( ) ) . await ;
21152133 assert ! (
2116- matches!( outcome, IngestOutcome :: Accepted ) ,
2117- "an unsigned known-peer event must survive the rolling-upgrade window, got {outcome:?}"
2134+ matches!( outcome, IngestOutcome :: UnsignedAdmitted ) ,
2135+ "an unsigned known-peer event must be admitted (distinct from Accepted) through the rolling-upgrade window, got {outcome:?}"
21182136 ) ;
21192137 assert_eq ! ( count( & pool, "received_ref_updates" ) . await , 1 ) ;
21202138 assert_eq ! ( count( & pool, "sync_queue" ) . await , 1 ) ;
@@ -2393,6 +2411,7 @@ mod tests {
23932411 fn every_ingest_outcome_carries_a_distinct_metric_label ( ) {
23942412 let labels = [
23952413 IngestOutcome :: Accepted . metric_label ( ) ,
2414+ IngestOutcome :: UnsignedAdmitted . metric_label ( ) ,
23962415 IngestOutcome :: WriteFailed ( "db down" . into ( ) ) . metric_label ( ) ,
23972416 IngestOutcome :: Rejected ( "malformed" . into ( ) ) . metric_label ( ) ,
23982417 IngestOutcome :: SourceRateLimited . metric_label ( ) ,
@@ -2403,6 +2422,7 @@ mod tests {
24032422 labels,
24042423 [
24052424 "accepted" ,
2425+ "unsigned_admitted" ,
24062426 "write_failed" ,
24072427 "rejected" ,
24082428 "source_rate_limited" ,
@@ -2629,7 +2649,7 @@ mod tests {
26292649 let outcome =
26302650 ingest_ref_update ( & db, & limiters, false , true , & claim, & attacker_edge) . await ;
26312651 assert ! (
2632- matches!( outcome, IngestOutcome :: Accepted ) ,
2652+ matches!( outcome, IngestOutcome :: UnsignedAdmitted ) ,
26332653 "unsigned event {i} is inside every budget and is admitted in the rolling-upgrade window, got {outcome:?}"
26342654 ) ;
26352655 }
@@ -2671,7 +2691,7 @@ mod tests {
26712691 for i in 0 ..GOSSIP_UNSIGNED_SOURCE_MAX_EVENTS {
26722692 let outcome = ingest_ref_update ( db, limiters, false , true , claim, & source) . await ;
26732693 assert ! (
2674- matches!( outcome, IngestOutcome :: Accepted ) ,
2694+ matches!( outcome, IngestOutcome :: UnsignedAdmitted ) ,
26752695 "unsigned event {i} is inside the unsigned budget and must be admitted, got {outcome:?}"
26762696 ) ;
26772697 }
@@ -2766,7 +2786,7 @@ mod tests {
27662786 let fresh_source = PeerId :: random ( ) ;
27672787 let outcome = ingest_ref_update ( & db, & limiters, false , true , & claim, & fresh_source) . await ;
27682788 assert ! (
2769- matches!( outcome, IngestOutcome :: Accepted ) ,
2789+ matches!( outcome, IngestOutcome :: UnsignedAdmitted ) ,
27702790 "the claimed DID must not carry a spent budget between forwarders, got {outcome:?}"
27712791 ) ;
27722792 assert_eq ! (
@@ -2899,7 +2919,7 @@ mod tests {
28992919 let outcome =
29002920 ingest_ref_update ( & db, & limiters, false , true , & valid_unsigned, & unsigned_edge) . await ;
29012921 assert ! (
2902- matches!( outcome, IngestOutcome :: Accepted ) ,
2922+ matches!( outcome, IngestOutcome :: UnsignedAdmitted ) ,
29032923 "the forwarder's unsigned budget must be untouched by malformed events, got {outcome:?}"
29042924 ) ;
29052925 assert_eq ! (
@@ -3035,7 +3055,7 @@ mod tests {
30353055 let outcome =
30363056 ingest_with_fresh_limiter ( & db, false , true , & claim, & PeerId :: random ( ) ) . await ;
30373057 assert ! (
3038- matches!( outcome, IngestOutcome :: Accepted ) ,
3058+ matches!( outcome, IngestOutcome :: UnsignedAdmitted ) ,
30393059 "B outcome {outcome:?}"
30403060 ) ;
30413061 assert ! ( logs. saw_warn( ) , "B must warn, got: {}" , logs. text( ) ) ;
0 commit comments