Skip to content

Commit 24a1c0d

Browse files
committed
initial commit
0 parents  commit 24a1c0d

15 files changed

+509
-0
lines changed

.gitignore

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
target/
2+
.direnv/
3+
.buildcmd
4+
.build-result
5+
shell.nix
6+
.envrc
7+
tags

COPYING

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Copyright 2024 Damus Nostr, Inc.
2+
3+
Permission is hereby granted, free of charge, to any person obtaining a copy of
4+
this software and associated documentation files (the “Software”), to deal in
5+
the Software without restriction, including without limitation the rights to
6+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
7+
of the Software, and to permit persons to whom the Software is furnished to do
8+
so, subject to the following conditions:
9+
10+
The above copyright notice and this permission notice shall be included in all
11+
copies or substantial portions of the Software.
12+
13+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19+
SOFTWARE.

Cargo.lock

+143
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[package]
2+
name = "noteguard"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[dependencies]
7+
serde = { version = "1.0", features = ["derive"] }
8+
serde_json = "1.0"
9+
toml = "0.5"
10+
tracing = "0.1.40"
11+
12+
13+

Makefile

+2
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
tags:
2+
find src -name '*.rs' | xargs ctags

README.md

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
2+
# noteguard
3+
4+
A high performance note filter plugin system for [strfry]
5+
6+
## Usage
7+
8+
Filters are registered and loaded from the [noteguard.toml](noteguard.toml) config.
9+
10+
You can add any new filter you want by implementing the `NoteFilter` trait and registering it with noteguard via the `register_filter` method.
11+
12+
The `pipeline` config specifies the order in which filters are run. When the first `reject` or `shadowReject` action is hit, then the pipeline stops and returns the rejection error.
13+
14+
```toml
15+
16+
pipeline = ["ratelimit"]
17+
18+
[filters.ratelimit]
19+
notes_per_second = 1
20+
whitelist = ["127.0.0.1"]
21+
```
22+
23+
[strfry]: https://github.com/hoytech/strfry

noteguard.toml

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
2+
pipeline = ["ratelimit"]
3+
4+
[filters.ratelimit]
5+
notes_per_second = 1
6+
whitelist = ["127.0.0.1"]

src/filters/mod.rs

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
mod rate_limit;
2+
mod whitelist;
3+
4+
pub use rate_limit::RateLimit;
5+
pub use whitelist::Whitelist;

src/filters/rate_limit.rs

+54
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
use crate::{Action, InputMessage, NoteFilter, OutputMessage};
2+
use serde::Deserialize;
3+
use std::collections::HashMap;
4+
use std::time::{Duration, Instant};
5+
6+
pub struct RateInfo {
7+
pub last_note: Instant,
8+
}
9+
10+
#[derive(Deserialize, Default)]
11+
pub struct RateLimit {
12+
pub notes_per_second: u64,
13+
pub whitelist: Option<Vec<String>>,
14+
15+
#[serde(skip)]
16+
pub sources: HashMap<String, RateInfo>,
17+
}
18+
19+
impl NoteFilter for RateLimit {
20+
fn filter_note(&mut self, msg: &InputMessage) -> OutputMessage {
21+
if let Some(whitelist) = &self.whitelist {
22+
if whitelist.contains(&msg.source_info) {
23+
return OutputMessage::new(msg.event.id.clone(), Action::Accept, None);
24+
}
25+
}
26+
27+
if self.sources.contains_key(&msg.source_info) {
28+
let now = Instant::now();
29+
let entry = self.sources.get_mut(&msg.source_info).expect("impossiburu");
30+
if now - entry.last_note < Duration::from_secs(self.notes_per_second) {
31+
return OutputMessage::new(
32+
msg.event.id.clone(),
33+
Action::Reject,
34+
Some("rate-limited: you are noting too fast".to_string()),
35+
);
36+
} else {
37+
entry.last_note = Instant::now();
38+
return OutputMessage::new(msg.event.id.clone(), Action::Accept, None);
39+
}
40+
} else {
41+
self.sources.insert(
42+
msg.source_info.to_owned(),
43+
RateInfo {
44+
last_note: Instant::now(),
45+
},
46+
);
47+
return OutputMessage::new(msg.event.id.clone(), Action::Accept, None);
48+
}
49+
}
50+
51+
fn name(&self) -> &'static str {
52+
"ratelimit"
53+
}
54+
}

src/filters/whitelist.rs

+26
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use crate::{Action, InputMessage, NoteFilter, OutputMessage};
2+
use serde::Deserialize;
3+
4+
#[derive(Deserialize)]
5+
pub struct Whitelist {
6+
pub pubkeys: Vec<String>,
7+
pub ips: Vec<String>,
8+
}
9+
10+
impl NoteFilter for Whitelist {
11+
fn filter_note(&mut self, msg: &InputMessage) -> OutputMessage {
12+
if self.pubkeys.contains(&msg.event.pubkey) || self.ips.contains(&msg.source_info) {
13+
OutputMessage::new(msg.event.id.clone(), Action::Accept, None)
14+
} else {
15+
OutputMessage::new(
16+
msg.event.id.clone(),
17+
Action::Reject,
18+
Some("blocked: pubkey not on the whitelist".to_string()),
19+
)
20+
}
21+
}
22+
23+
fn name(&self) -> &'static str {
24+
"whitelist"
25+
}
26+
}

src/lib.rs

+6
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
pub mod filters;
2+
mod messages;
3+
mod note_filter;
4+
5+
pub use messages::{Action, InputMessage, OutputMessage};
6+
pub use note_filter::{Note, NoteFilter};

0 commit comments

Comments
 (0)