mirror of
https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git
synced 2026-10-05 15:08:24 +00:00
Repository conversations can continue in the read or write relays advertised by authors of accepted replies, reactions, zaps, and other descendants. Root-author inbox discovery alone therefore leaves valid indirect descendants undiscovered. Carry exact accepted-root provenance through the existing bounded descendant frontier, admit identity events only for those participants, and derive read/write mailbox ownership from accepted kind 10002 events. Probe one byte-bounded historic filter at a time with the existing per-relay fetch_events pagination, pacing, ledger, timeout, policy, and persistence paths. Starts are paced globally, but progress and terminal state remain independent per relay. Require both an established socket and the sync actor committed lifecycle before starting a probe. Prefer lifecycle-active due relays while falling back to the existing oldest-due dial order, so hundreds of unavailable mailbox sources cannot starve already-ready work. Drain completed mailbox probes before accepting more connection results so a busy startup queue cannot delay cursor progress or resource release. Production canaries exposed these startup conditions without requiring cross-relay coordination. The recursive descendant limit bounds which indirect IDs remain query roots; direct root references remain complete. Mailbox filter cursors are intentionally best-effort in-memory state: restart reconstructs ownership from LMDB and safely begins historic coverage again. This does not add permanent participant live subscriptions or a cross-relay completion coordinator. Validated with cargo check, strict all-target Clippy, 767 library tests including lifecycle and ready-selection regressions, and the three proactive Sync+ integration scenarios, including a write-mailbox child that references only a participant reaction.
97 lines
3.2 KiB
Rust
97 lines
3.2 KiB
Rust
/// Policy module for NIP-34 write policies
|
|
///
|
|
/// This module splits the large Nip34WritePolicy into focused sub-policies:
|
|
/// - `AnnouncementPolicy` - Repository announcement validation
|
|
/// - `StatePolicy` - State event validation + ref alignment
|
|
/// - `PrEventPolicy` - PR/PR Update validation
|
|
/// - `RelatedEventPolicy` - Forward/backward reference checking
|
|
/// - Deletion handling lives in `crate::nostr::lifecycle`
|
|
mod announcement;
|
|
mod identity;
|
|
mod pr_event;
|
|
mod related;
|
|
mod result;
|
|
mod state;
|
|
|
|
pub(crate) use result::{
|
|
accepted_purgatory, duplicate, reject_error, reject_invalid, reject_restricted,
|
|
};
|
|
|
|
pub use announcement::{AnnouncementPolicy, AnnouncementResult};
|
|
pub use identity::{
|
|
IdentityAdmission, ProactiveParticipantAuthorIndex, SharedProactiveParticipantAuthorIndex,
|
|
};
|
|
pub use pr_event::PrEventPolicy;
|
|
pub use related::{ReferenceResult, RelatedEventPolicy};
|
|
pub use state::{StatePolicy, StateResult};
|
|
|
|
// Re-export AlignmentResult from git::sync (canonical location)
|
|
pub use crate::git::sync::AlignmentResult;
|
|
|
|
use super::SharedDatabase;
|
|
use crate::purgatory::Purgatory;
|
|
use nostr_sdk::local_relay::LocalRelay;
|
|
use std::sync::Arc;
|
|
|
|
/// Shared context for all sub-policies
|
|
#[derive(Clone)]
|
|
pub struct PolicyContext {
|
|
pub domain: String,
|
|
pub database: SharedDatabase,
|
|
pub git_data_path: std::path::PathBuf,
|
|
pub purgatory: Arc<Purgatory>,
|
|
/// Local relay for notifying WebSocket subscribers (set after relay creation)
|
|
pub local_relay: Arc<std::sync::RwLock<Option<LocalRelay>>>,
|
|
/// Configuration reference for policy settings (includes blacklists)
|
|
pub config: crate::config::Config,
|
|
}
|
|
|
|
impl PolicyContext {
|
|
#[allow(clippy::too_many_arguments)]
|
|
pub fn new(
|
|
domain: impl Into<String>,
|
|
database: SharedDatabase,
|
|
git_data_path: impl Into<std::path::PathBuf>,
|
|
purgatory: Arc<Purgatory>,
|
|
config: crate::config::Config,
|
|
) -> Self {
|
|
Self {
|
|
domain: domain.into(),
|
|
database,
|
|
git_data_path: git_data_path.into(),
|
|
purgatory,
|
|
local_relay: Arc::new(std::sync::RwLock::new(None)),
|
|
config,
|
|
}
|
|
}
|
|
|
|
/// Construct a [`PolicyContext`] with a fresh, isolated [`RepoInitLocks`]
|
|
/// map. Intended for unit tests that exercise policy logic without a
|
|
/// running HTTP server.
|
|
#[cfg(test)]
|
|
pub fn new_for_test(
|
|
domain: impl Into<String>,
|
|
database: SharedDatabase,
|
|
git_data_path: impl Into<std::path::PathBuf>,
|
|
purgatory: Arc<Purgatory>,
|
|
config: crate::config::Config,
|
|
) -> Self {
|
|
Self::new(domain, database, git_data_path, purgatory, config)
|
|
}
|
|
|
|
/// Set the local relay after it's been created.
|
|
///
|
|
/// This is called after the relay is built since the relay depends on the policy
|
|
/// but the policy needs the relay for purgatory notifications.
|
|
pub fn set_local_relay(&self, relay: LocalRelay) {
|
|
let mut guard = self.local_relay.write().unwrap();
|
|
*guard = Some(relay);
|
|
}
|
|
|
|
/// Get a clone of the local relay if it's been set.
|
|
pub fn get_local_relay(&self) -> Option<LocalRelay> {
|
|
let guard = self.local_relay.read().unwrap();
|
|
guard.clone()
|
|
}
|
|
}
|