mirror of
https://relay.ngit.dev/npub15qydau2hjma6ngxkl2cyar74wzyjshvl65za5k5rl69264ar2exs5cyejr/ngit-grasp.git
synced 2026-10-05 23:18:24 +00:00
feat(config): add event blacklist to block all events from specific authors
Adds NGIT_EVENT_BLACKLIST option for blocking all events from specific npubs, taking precedence over all other validation to enable comprehensive moderation without affecting curation policy. Key features: - Simple npub-only format: <npub>,<npub>,... - Checked FIRST before any other validation (including repository blacklist) - Blocks ALL event types (announcements, state events, PRs, comments, etc.) - Events never reach relay storage or purgatory - Specific rejection reason for operator debugging Implementation: - Add EventBlacklistConfig struct with check() method - Add NGIT_EVENT_BLACKLIST config option and event_blacklist_config() method - Add config field to PolicyContext for policy access - Add check_event_blacklist() to Nip34WritePolicy - Check event blacklist first in admit_event() method (before any other validation) - 4 new unit tests covering all blacklist behavior Configuration synced across all four sources: - src/config.rs: Core implementation with EventBlacklistConfig - .env.example: Comprehensive documentation with examples - docs/reference/configuration.md: Complete reference documentation - nix/module.nix: NixOS module option with environment mapping README updates: - Add comprehensive "Curation & Moderation" section - Document repository whitelists (GRASP-01 and GRASP-05 modes) - Document repository and event blacklists with precedence order - Add configuration table for all curation/moderation settings - Provide real-world examples for different relay configurations Testing: - 4 new tests for event blacklist functionality - All 336 library tests passing - All 64 integration tests passing - All 38 filter support tests passing Verification: - Repository blacklist confirmed to apply to sync (uses same admit_event flow) - Sync events validated through process_event_static -> write_policy.admit_event Use cases: - Block spam/abusive users completely - Prevent malicious actors from submitting any events - Temporary blocks for investigation - Moderation without affecting whitelist curation policy
This commit is contained in:
+24
-1
@@ -254,4 +254,27 @@
|
||||
# NGIT_REPOSITORY_BLACKLIST=npub1spam...
|
||||
# NGIT_REPOSITORY_BLACKLIST=npub1alice.../bad-repo
|
||||
# NGIT_REPOSITORY_BLACKLIST=malware-repo,spam-repo
|
||||
# NGIT_REPOSITORY_BLACKLIST=
|
||||
# NGIT_REPOSITORY_BLACKLIST=
|
||||
|
||||
# ============================================================================
|
||||
# EVENT BLACKLIST
|
||||
# ============================================================================
|
||||
|
||||
# Blacklist events from specific authors (npubs)
|
||||
# Comma-separated list of npubs whose events are rejected
|
||||
# ALL events from these authors are blocked from both relay storage and purgatory
|
||||
#
|
||||
# Event blacklist takes precedence over ALL other validation:
|
||||
# - Blacklisted events are rejected before any other policy checks
|
||||
# - Applies to announcements, state events, PRs, and all other event types
|
||||
# - Events never reach purgatory (rejected immediately)
|
||||
#
|
||||
# Rejection reason:
|
||||
# - "Event author <npub> is blacklisted"
|
||||
#
|
||||
# CLI: --event-blacklist <list>
|
||||
# Default: (empty - no events are blacklisted by author)
|
||||
# Examples:
|
||||
# NGIT_EVENT_BLACKLIST=npub1spam...
|
||||
# NGIT_EVENT_BLACKLIST=npub1spam...,npub1abuser...
|
||||
# NGIT_EVENT_BLACKLIST=
|
||||
@@ -36,7 +36,7 @@ Unlike the reference implementation ([ngit-relay](https://gitworkshop.dev/npub15
|
||||
- **Pure Rust Implementation**: Single binary, no external dependencies beyond Git itself
|
||||
- **Integrated Authorization**: Push validation happens inline during the Git receive-pack operation
|
||||
- **GRASP-01 Compliant**: Core service requirements for Git hosting with Nostr authorization
|
||||
- **Repository Whitelist/Blacklist**: Optional curation via pubkey/identifier whitelist (GRASP-01 mode) and blacklist (overrides all whitelists)
|
||||
- **Flexible Curation & Moderation**: Repository whitelists (GRASP-01 mode), repository blacklists (moderation), and event blacklists (author blocking)
|
||||
- **GRASP-02 Proactive Sync**: Sophisticated relay-to-relay event and git data synchronization
|
||||
- **NIP-77 Negentropy**: Efficient set reconciliation with automatic fallback to REQ+EOSE
|
||||
- **Live & Historic Sync**: Real-time event streaming plus catch-up for past events
|
||||
@@ -150,6 +150,93 @@ See [GRASP-02 Proactive Sync](docs/explanation/grasp-02-proactive-sync.md) for f
|
||||
|
||||
**See**: [GRASP-05 Archive Mode](docs/explanation/grasp-05-archive.md)
|
||||
|
||||
## Curation & Moderation
|
||||
|
||||
ngit-grasp provides flexible tools for both curation (repository selection) and moderation (blocking spam/abuse):
|
||||
|
||||
### Repository Whitelists (Curation)
|
||||
|
||||
Control which repositories your relay accepts via two independent whitelist modes:
|
||||
|
||||
**Repository Whitelist (GRASP-01 Mode):**
|
||||
- Only accept announcements that **both** list your service AND match the whitelist
|
||||
- Three formats: `<npub>`, `<npub>/<identifier>`, `<identifier>`
|
||||
- Environment: `NGIT_REPOSITORY_WHITELIST=npub1alice...,bitcoin-core`
|
||||
- Use case: Curated relay accepting specific projects/developers
|
||||
|
||||
**Archive Whitelist (GRASP-05 Mode):**
|
||||
- Accept announcements matching the whitelist **even if they don't list your service**
|
||||
- Same three formats as repository whitelist
|
||||
- Environment: `NGIT_ARCHIVE_WHITELIST=npub1satoshi...,linux`
|
||||
- Use case: Backup/mirror relay for critical projects
|
||||
- Default: Read-only mode (`NGIT_ARCHIVE_READ_ONLY=true`)
|
||||
|
||||
Both whitelists support flexible matching:
|
||||
```bash
|
||||
# Accept all repos from specific developer
|
||||
NGIT_REPOSITORY_WHITELIST=npub1alice...
|
||||
|
||||
# Accept specific repository
|
||||
NGIT_REPOSITORY_WHITELIST=npub1alice.../my-project
|
||||
|
||||
# Accept repos with specific identifier (any author)
|
||||
NGIT_REPOSITORY_WHITELIST=bitcoin-core
|
||||
```
|
||||
|
||||
### Blacklists (Moderation)
|
||||
|
||||
Block unwanted content without affecting your curation policy:
|
||||
|
||||
**Repository Blacklist:**
|
||||
- Block specific repositories/developers/identifiers
|
||||
- **Takes precedence over ALL whitelists** (checked first)
|
||||
- Three formats: `<npub>`, `<npub>/<identifier>`, `<identifier>`
|
||||
- Environment: `NGIT_REPOSITORY_BLACKLIST=npub1spam...,malware-repo`
|
||||
- Use case: Block spam/malware repos while maintaining whitelist curation
|
||||
|
||||
**Event Blacklist:**
|
||||
- Block **ALL events** from specific authors (npubs)
|
||||
- **Takes precedence over ALL other validation** (checked first)
|
||||
- Applies to all event types: announcements, state events, PRs, comments, etc.
|
||||
- Events never reach relay storage or purgatory
|
||||
- Environment: `NGIT_EVENT_BLACKLIST=npub1spammer...,npub1abuser...`
|
||||
- Use case: Block abusive users completely
|
||||
|
||||
### Precedence & Interaction
|
||||
|
||||
Validation order (from first to last):
|
||||
|
||||
1. **Event Blacklist** → Reject if author is blacklisted (ALL event types)
|
||||
2. **Repository Blacklist** → Reject if repository/npub/identifier is blacklisted (announcements only)
|
||||
3. **Repository Whitelist** → Accept if announcement lists service AND matches whitelist
|
||||
4. **Archive Whitelist** → Accept if announcement matches whitelist (even without listing service)
|
||||
5. **Default GRASP-01** → Accept if announcement lists service (no whitelist configured)
|
||||
|
||||
Examples:
|
||||
```bash
|
||||
# Curated relay blocking spam
|
||||
NGIT_REPOSITORY_WHITELIST=npub1alice...,npub1bob...
|
||||
NGIT_REPOSITORY_BLACKLIST=npub1alice.../spam-repo
|
||||
NGIT_EVENT_BLACKLIST=npub1spammer...
|
||||
# Result: Accept Alice & Bob's repos EXCEPT Alice's spam-repo, block all events from spammer
|
||||
|
||||
# Archive relay with moderation
|
||||
NGIT_ARCHIVE_WHITELIST=bitcoin-core,linux
|
||||
NGIT_EVENT_BLACKLIST=npub1abuser...
|
||||
# Result: Mirror bitcoin-core and linux projects, block all events from abuser
|
||||
|
||||
# Public relay with spam protection
|
||||
NGIT_EVENT_BLACKLIST=npub1spam1...,npub1spam2...
|
||||
# Result: Accept all GRASP-01 repos, block all events from spammers
|
||||
```
|
||||
|
||||
**Privacy & Transparency:**
|
||||
- Blacklists are **not advertised** in NIP-11 metadata (operational, not curation policy)
|
||||
- Rejected events receive specific error messages for operator debugging
|
||||
- No client-visible indication that blacklists are in use
|
||||
|
||||
**See**: [Configuration Reference](docs/reference/configuration.md) for complete details
|
||||
|
||||
## Roadmap
|
||||
|
||||
### GRASP-02 Enhancements
|
||||
@@ -326,6 +413,17 @@ NGIT_RELAY_OWNER_NSEC=nsec1... ngit-grasp --domain relay.example.com
|
||||
| Disable negentropy | `--sync-disable-negentropy` | `NGIT_SYNC_DISABLE_NEGENTROPY` | `false` |
|
||||
| Batch window | N/A | `NGIT_SYNC_BATCH_WINDOW_MS` | `5000` ms |
|
||||
|
||||
#### Curation & Moderation Settings
|
||||
|
||||
| Option | CLI Flag | Environment Variable | Default |
|
||||
| -------------------- | --------------------------- | ------------------------------ | --------- |
|
||||
| Repository whitelist | `--repository-whitelist` | `NGIT_REPOSITORY_WHITELIST` | (empty) |
|
||||
| Archive whitelist | `--archive-whitelist` | `NGIT_ARCHIVE_WHITELIST` | (empty) |
|
||||
| Archive all | `--archive-all` | `NGIT_ARCHIVE_ALL` | `false` |
|
||||
| Archive read-only | `--archive-read-only` | `NGIT_ARCHIVE_READ_ONLY` | (auto) |
|
||||
| Repository blacklist | `--repository-blacklist` | `NGIT_REPOSITORY_BLACKLIST` | (empty) |
|
||||
| Event blacklist | `--event-blacklist` | `NGIT_EVENT_BLACKLIST` | (empty) |
|
||||
|
||||
**Sync Notes:**
|
||||
|
||||
- **Bootstrap relay**: Optional starting point for relay discovery. System automatically discovers additional relays from repository announcements. URL scheme is optional - if not provided, `wss://` is assumed (e.g., `git.shakespeare.diy` → `wss://git.shakespeare.diy`).
|
||||
|
||||
@@ -833,6 +833,98 @@ Blacklist does **not** affect NIP-11 metadata:
|
||||
|
||||
---
|
||||
|
||||
### Event Blacklist
|
||||
|
||||
#### `NGIT_EVENT_BLACKLIST`
|
||||
|
||||
**Description:** Blacklist events from specific authors (npubs)
|
||||
**Type:** Comma-separated list of npubs
|
||||
**Default:** Empty (no events are blacklisted by author)
|
||||
**Required:** No
|
||||
|
||||
**Format:**
|
||||
- `npub1...` - Block all events from this author
|
||||
|
||||
**Precedence:** Event blacklist takes precedence over **ALL** other validation:
|
||||
- Blacklisted events are rejected **before** any other policy checks
|
||||
- Applies to all event types (announcements, state events, PRs, etc.)
|
||||
- Events never reach purgatory (rejected immediately)
|
||||
- Overrides repository blacklist, whitelists, and all other policies
|
||||
|
||||
**Examples:**
|
||||
|
||||
```bash
|
||||
# Block all events from specific author
|
||||
NGIT_EVENT_BLACKLIST=npub1spam...
|
||||
|
||||
# Block events from multiple authors
|
||||
NGIT_EVENT_BLACKLIST=npub1spam...,npub1abuser...,npub1troll...
|
||||
```
|
||||
|
||||
**Rejection Reason:**
|
||||
|
||||
The event blacklist provides a specific rejection reason:
|
||||
- **Format:** `"Event author <npub> is blacklisted"`
|
||||
|
||||
This reason helps operators understand why an event was rejected without needing to flag it in metadata.
|
||||
|
||||
**Behavior:**
|
||||
|
||||
Event blacklist is checked **first** before all other validation:
|
||||
1. Check event blacklist → Reject if author is blacklisted
|
||||
2. Check repository blacklist (for announcements) → Reject if matched
|
||||
3. Check event-type specific policies → Accept/Reject based on policy
|
||||
4. Process event normally
|
||||
|
||||
**Use Cases:**
|
||||
|
||||
```bash
|
||||
# Block spam/abusive users
|
||||
NGIT_EVENT_BLACKLIST=npub1spammer...,npub1abuser...
|
||||
|
||||
# Block malicious actors
|
||||
NGIT_EVENT_BLACKLIST=npub1malware...,npub1phisher...
|
||||
|
||||
# Temporary block for investigation
|
||||
NGIT_EVENT_BLACKLIST=npub1suspicious...
|
||||
```
|
||||
|
||||
**Comparison with Repository Blacklist:**
|
||||
|
||||
| Configuration | Scope | Checked When | Applies To |
|
||||
|---------------|-------|--------------|------------|
|
||||
| Event Blacklist | Author-based | **First** (before all policies) | **All events** from author |
|
||||
| Repository Blacklist | Repo-based | Second (announcements only) | Specific repositories |
|
||||
|
||||
**Event Blacklist vs Repository Blacklist:**
|
||||
|
||||
```bash
|
||||
# Scenario: npub1alice is event-blacklisted
|
||||
NGIT_EVENT_BLACKLIST=npub1alice...
|
||||
|
||||
# Result:
|
||||
# - ALL events from npub1alice are rejected (announcements, PRs, etc.)
|
||||
# - Events never reach relay or purgatory
|
||||
# - Rejection: "Event author npub1alice... is blacklisted"
|
||||
|
||||
# Scenario: npub1alice/repo is repository-blacklisted
|
||||
NGIT_REPOSITORY_BLACKLIST=npub1alice.../malware
|
||||
|
||||
# Result:
|
||||
# - Only announcements for npub1alice.../malware are rejected
|
||||
# - Other events from npub1alice are still processed normally
|
||||
# - PRs/state events for different repos from npub1alice are accepted
|
||||
```
|
||||
|
||||
**NIP-11 Impact:**
|
||||
|
||||
Event blacklist does **not** affect NIP-11 metadata:
|
||||
- No `curation` field changes (blacklist is operational, not policy)
|
||||
- Blacklist is transparent to clients (rejected with specific reason)
|
||||
- Operators can use blacklist without advertising moderation
|
||||
|
||||
---
|
||||
|
||||
### Logging Configuration
|
||||
|
||||
#### `RUST_LOG`
|
||||
|
||||
@@ -237,6 +237,19 @@ let
|
||||
'';
|
||||
};
|
||||
|
||||
eventBlacklist = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [ ];
|
||||
example = [ "npub1spam..." "npub1abuser..." ];
|
||||
description = ''
|
||||
Event blacklist for blocking all events from specific authors (npubs).
|
||||
Takes precedence over ALL other validation (checked first).
|
||||
ALL events from these authors are rejected from relay storage and purgatory.
|
||||
Applies to announcements, state events, PRs, and all other event types.
|
||||
Does not affect NIP-11 metadata (operational, not curation policy).
|
||||
'';
|
||||
};
|
||||
|
||||
user = mkOption {
|
||||
type = types.str;
|
||||
default = "ngit-grasp-${name}";
|
||||
@@ -281,6 +294,7 @@ let
|
||||
NGIT_ARCHIVE_WHITELIST = concatStringsSep "," cfg.archiveWhitelist;
|
||||
NGIT_REPOSITORY_WHITELIST = concatStringsSep "," cfg.repositoryWhitelist;
|
||||
NGIT_REPOSITORY_BLACKLIST = concatStringsSep "," cfg.repositoryBlacklist;
|
||||
NGIT_EVENT_BLACKLIST = concatStringsSep "," cfg.eventBlacklist;
|
||||
RUST_LOG = cfg.logLevel;
|
||||
} // optionalAttrs (cfg.relayName != null) {
|
||||
NGIT_RELAY_NAME = cfg.relayName;
|
||||
|
||||
+110
@@ -244,6 +244,42 @@ impl Default for BlacklistConfig {
|
||||
}
|
||||
}
|
||||
|
||||
/// Event blacklist configuration for blocking events by author npub
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
pub struct EventBlacklistConfig {
|
||||
/// Blacklisted npubs - events from these authors are rejected
|
||||
///
|
||||
/// If empty, no events are blacklisted by author.
|
||||
/// Applies to ALL event types, preventing events from reaching both the relay and purgatory.
|
||||
pub blacklisted_npubs: Vec<String>,
|
||||
}
|
||||
|
||||
impl EventBlacklistConfig {
|
||||
/// Check if event blacklist is enabled (non-empty blacklist)
|
||||
pub fn enabled(&self) -> bool {
|
||||
!self.blacklisted_npubs.is_empty()
|
||||
}
|
||||
|
||||
/// Check if an event author is blacklisted
|
||||
///
|
||||
/// Returns Some(reason) if blacklisted, None if not blacklisted.
|
||||
pub fn check(&self, npub: &str) -> Option<String> {
|
||||
if self.blacklisted_npubs.contains(&npub.to_string()) {
|
||||
Some(format!("Event author {} is blacklisted", npub))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for EventBlacklistConfig {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
blacklisted_npubs: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Database backend type for the relay
|
||||
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Default, ValueEnum)]
|
||||
#[serde(rename_all = "lowercase")]
|
||||
@@ -428,6 +464,11 @@ pub struct Config {
|
||||
/// Blacklist takes precedence over all whitelists (archive and repository)
|
||||
#[arg(long, env = "NGIT_REPOSITORY_BLACKLIST", default_value = "")]
|
||||
pub repository_blacklist: String,
|
||||
|
||||
/// Event blacklist: comma-separated list of npubs whose events are rejected
|
||||
/// All events from these authors are blocked from both relay storage and purgatory
|
||||
#[arg(long, env = "NGIT_EVENT_BLACKLIST", default_value = "")]
|
||||
pub event_blacklist: String,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -612,6 +653,20 @@ impl Config {
|
||||
BlacklistConfig { blacklist }
|
||||
}
|
||||
|
||||
/// Get parsed event blacklist configuration
|
||||
///
|
||||
/// This method assumes config has been validated - call Config::validate() first!
|
||||
pub fn event_blacklist_config(&self) -> EventBlacklistConfig {
|
||||
let blacklisted_npubs: Vec<String> = self
|
||||
.event_blacklist
|
||||
.split(',')
|
||||
.map(|s| s.trim())
|
||||
.filter(|s| !s.is_empty())
|
||||
.map(|s| s.to_string())
|
||||
.collect();
|
||||
EventBlacklistConfig { blacklisted_npubs }
|
||||
}
|
||||
|
||||
/// Create config for testing
|
||||
#[cfg(test)]
|
||||
pub fn for_testing() -> Self {
|
||||
@@ -647,6 +702,7 @@ impl Config {
|
||||
archive_read_only: None,
|
||||
repository_whitelist: String::new(),
|
||||
repository_blacklist: String::new(),
|
||||
event_blacklist: String::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1248,4 +1304,58 @@ mod tests {
|
||||
let result = config.check(&test_npub, "allowed-repo");
|
||||
assert!(result.is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_event_blacklist_config_parsing() {
|
||||
let keys1 = Keys::generate();
|
||||
let keys2 = Keys::generate();
|
||||
let npub1 = keys1.public_key().to_bech32().unwrap();
|
||||
let npub2 = keys2.public_key().to_bech32().unwrap();
|
||||
let config = Config {
|
||||
event_blacklist: format!("{},{}", npub1, npub2),
|
||||
..Config::for_testing()
|
||||
};
|
||||
let event_blacklist_config = config.event_blacklist_config();
|
||||
assert_eq!(event_blacklist_config.blacklisted_npubs.len(), 2);
|
||||
assert!(event_blacklist_config.enabled());
|
||||
assert!(event_blacklist_config.blacklisted_npubs.contains(&npub1));
|
||||
assert!(event_blacklist_config.blacklisted_npubs.contains(&npub2));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_event_blacklist_config_empty() {
|
||||
let config = Config::for_testing();
|
||||
let event_blacklist_config = config.event_blacklist_config();
|
||||
assert!(event_blacklist_config.blacklisted_npubs.is_empty());
|
||||
assert!(!event_blacklist_config.enabled());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_event_blacklist_check_blacklisted() {
|
||||
let keys = Keys::generate();
|
||||
let test_npub = keys.public_key().to_bech32().unwrap();
|
||||
let config = EventBlacklistConfig {
|
||||
blacklisted_npubs: vec![test_npub.clone()],
|
||||
};
|
||||
|
||||
let result = config.check(&test_npub);
|
||||
assert!(result.is_some());
|
||||
let reason = result.unwrap();
|
||||
assert!(reason.contains("author"));
|
||||
assert!(reason.contains(&test_npub));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_event_blacklist_check_not_blacklisted() {
|
||||
let keys1 = Keys::generate();
|
||||
let keys2 = Keys::generate();
|
||||
let banned_npub = keys1.public_key().to_bech32().unwrap();
|
||||
let allowed_npub = keys2.public_key().to_bech32().unwrap();
|
||||
let config = EventBlacklistConfig {
|
||||
blacklisted_npubs: vec![banned_npub],
|
||||
};
|
||||
|
||||
let result = config.check(&allowed_npub);
|
||||
assert!(result.is_none());
|
||||
}
|
||||
}
|
||||
|
||||
+31
-1
@@ -56,7 +56,13 @@ impl Nip34WritePolicy {
|
||||
purgatory: std::sync::Arc<crate::purgatory::Purgatory>,
|
||||
config: crate::config::Config,
|
||||
) -> Self {
|
||||
let ctx = PolicyContext::new(&config.domain, database, git_data_path, purgatory);
|
||||
let ctx = PolicyContext::new(
|
||||
&config.domain,
|
||||
database,
|
||||
git_data_path,
|
||||
purgatory,
|
||||
config.clone(),
|
||||
);
|
||||
Self {
|
||||
announcement_policy: AnnouncementPolicy::new(ctx.clone(), config.clone()),
|
||||
state_policy: StatePolicy::new(ctx.clone()),
|
||||
@@ -66,6 +72,19 @@ impl Nip34WritePolicy {
|
||||
}
|
||||
}
|
||||
|
||||
/// Check if an event author is blacklisted
|
||||
///
|
||||
/// Returns Some(reason) if blacklisted, None if not blacklisted.
|
||||
fn check_event_blacklist(&self, event: &Event) -> Option<String> {
|
||||
let event_blacklist = self.ctx.config.event_blacklist_config();
|
||||
if !event_blacklist.enabled() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let npub = event.pubkey.to_bech32().ok()?;
|
||||
event_blacklist.check(&npub)
|
||||
}
|
||||
|
||||
/// Get a reference to the purgatory for read-only access
|
||||
pub fn purgatory(&self) -> &std::sync::Arc<crate::purgatory::Purgatory> {
|
||||
&self.ctx.purgatory
|
||||
@@ -474,6 +493,17 @@ impl WritePolicy for Nip34WritePolicy {
|
||||
addr: &'a SocketAddr,
|
||||
) -> BoxedFuture<'a, WritePolicyResult> {
|
||||
Box::pin(async move {
|
||||
// Check event blacklist FIRST - it overrides everything
|
||||
if let Some(reason) = self.check_event_blacklist(event) {
|
||||
tracing::debug!(
|
||||
event_id = %event.id.to_bech32().unwrap_or_else(|_| event.id.to_hex()),
|
||||
author = %event.pubkey.to_hex(),
|
||||
reason = %reason,
|
||||
"Rejected event from blacklisted author"
|
||||
);
|
||||
return WritePolicyResult::reject(reason);
|
||||
}
|
||||
|
||||
// Detect if this is a synced event (from proactive sync) vs user-submitted
|
||||
// Sync uses localhost:0 as a dummy address
|
||||
let is_synced = addr.ip().is_loopback() && addr.port() == 0;
|
||||
|
||||
@@ -32,6 +32,8 @@ pub struct PolicyContext {
|
||||
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 {
|
||||
@@ -40,6 +42,7 @@ impl PolicyContext {
|
||||
database: SharedDatabase,
|
||||
git_data_path: impl Into<std::path::PathBuf>,
|
||||
purgatory: Arc<Purgatory>,
|
||||
config: crate::config::Config,
|
||||
) -> Self {
|
||||
Self {
|
||||
domain: domain.into(),
|
||||
@@ -47,6 +50,7 @@ impl PolicyContext {
|
||||
git_data_path: git_data_path.into(),
|
||||
purgatory,
|
||||
local_relay: Arc::new(std::sync::RwLock::new(None)),
|
||||
config,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user