commit 1c07a5b4b34147700921cf2a2bb111b38ed0f4a5
parent 7ce1c9252c230aa6b256d1c2833d5b9ee1842672
Author: triesap <tyson@radroots.org>
Date: Sun, 14 Jun 2026 04:41:16 -0700
runtime: centralize group read gate
- Replace separate Tangle and Pocket visibility helpers with one group_read_gate_visible_to_auth path.
- Route offset lookup, store query screening, direct fanout, and offset fanout through the shared read gate helper.
- Activate the Phase 2 target that guards REQ COUNT and live fanout against divergent group read-gate paths.
- Validated with cargo fmt --all -- --check, cargo check --workspace --all-targets, cargo test --workspace, and cargo clippy --workspace --all-targets -- -D warnings.
Diffstat:
2 files changed, 32 insertions(+), 30 deletions(-)
diff --git a/crates/tangle_runtime/src/relay/core.rs b/crates/tangle_runtime/src/relay/core.rs
@@ -11,11 +11,11 @@ use crate::relay::{
use std::{cell::RefCell, collections::BTreeSet};
use tangle_crypto::verify_event_signature;
use tangle_groups::{
- GroupAuthContext, GroupEventClass, GroupProjection, GroupRuntimeConfig, StoreOffset,
- validate_client_group_event_structure,
+ GroupAuthContext, GroupEventClass, GroupEventView, GroupProjection, GroupRuntimeConfig,
+ StoreOffset, validate_client_group_event_structure,
};
use tangle_protocol::{ClientMessage, Event, Filter, RelayMessage, SubscriptionId, UnixTimestamp};
-use tangle_store_pocket::{PocketEvent, PocketScreenResult, PocketStoreConfig, PocketStoreHandle};
+use tangle_store_pocket::{PocketScreenResult, PocketStoreConfig, PocketStoreHandle};
pub struct BaseRelay {
store: PocketStoreHandle,
@@ -160,7 +160,8 @@ impl BaseRelay {
auth: &BaseAuthState,
) -> Result<Option<Event>, BaseRelayError> {
let event = self.event_by_offset(offset)?;
- if self.event_visible_to_auth(
+ if Self::group_read_gate_visible_to_auth(
+ self.groups.as_ref(),
&event,
&GroupAuthContext::new(auth.authenticated_pubkeys().iter().cloned()),
)? {
@@ -415,9 +416,7 @@ impl BaseRelay {
pub fn fanout(&mut self, event: &Event) -> Vec<RelayMessage> {
let groups = self.groups.as_ref();
self.subscriptions.fanout(event, |event, auth| {
- groups
- .map(|groups| groups.event_visible_to_auth(event, auth).unwrap_or(false))
- .unwrap_or(true)
+ Self::group_read_gate_visible_to_auth(groups, event, auth).unwrap_or(false)
})
}
@@ -479,7 +478,11 @@ impl BaseRelay {
}
match pocket_filter.event_matches(pocket_event) {
Ok(false) => PocketScreenResult::Mismatch,
- Ok(true) => match self.pocket_event_visible_to_auth(pocket_event, auth) {
+ Ok(true) => match Self::group_read_gate_visible_to_auth(
+ self.groups.as_ref(),
+ pocket_event,
+ auth,
+ ) {
Ok(true) => PocketScreenResult::Match,
Ok(false) => PocketScreenResult::Redacted,
Err(error) => {
@@ -519,25 +522,12 @@ impl BaseRelay {
.collect()
}
- fn event_visible_to_auth(
- &self,
- event: &Event,
+ fn group_read_gate_visible_to_auth(
+ groups: Option<&GroupService>,
+ event: &(impl GroupEventView + ?Sized),
auth: &GroupAuthContext,
) -> Result<bool, BaseRelayError> {
- self.groups
- .as_ref()
- .map(|groups| groups.event_visible_to_auth(event, auth))
- .unwrap_or(Ok(true))
- .map_err(BaseRelayError::from)
- }
-
- fn pocket_event_visible_to_auth(
- &self,
- event: &PocketEvent,
- auth: &GroupAuthContext,
- ) -> Result<bool, BaseRelayError> {
- self.groups
- .as_ref()
+ groups
.map(|groups| groups.event_visible_to_auth(event, auth))
.unwrap_or(Ok(true))
.map_err(BaseRelayError::from)
@@ -551,9 +541,7 @@ impl BaseRelay {
let event = self.event_by_offset(offset)?;
let groups = self.groups.as_ref();
Ok(subscriptions.fanout(&event, |event, auth| {
- groups
- .map(|groups| groups.event_visible_to_auth(event, auth).unwrap_or(false))
- .unwrap_or(true)
+ Self::group_read_gate_visible_to_auth(groups, event, auth).unwrap_or(false)
}))
}
}
diff --git a/crates/tangle_runtime/tests/phase2_acceptance_targets.rs b/crates/tangle_runtime/tests/phase2_acceptance_targets.rs
@@ -480,9 +480,23 @@ fn closed_groups_use_strict_nip29_semantics_without_compatibility_flag() {
}
#[test]
-#[ignore = "phase2 target: central read gate"]
fn req_count_and_live_fanout_share_one_group_read_gate() {
- pending("REQ COUNT and live fanout must use one central group read gate");
+ let relay_core = include_str!("../src/relay/core.rs");
+
+ assert_eq!(
+ relay_core
+ .matches("fn group_read_gate_visible_to_auth")
+ .count(),
+ 1
+ );
+ assert_eq!(
+ relay_core
+ .matches("Self::group_read_gate_visible_to_auth")
+ .count(),
+ 4
+ );
+ assert!(!relay_core.contains("fn event_visible_to_auth("));
+ assert!(!relay_core.contains("fn pocket_event_visible_to_auth("));
}
#[test]