Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
e999ba9
Add KLIPY GIF search to message composers
klopez4212 Jul 15, 2026
4a764bd
Use KLIPY-hosted GIF URLs
klopez4212 Aug 11, 2026
a0102bd
Polish GIF picker controls
klopez4212 Aug 11, 2026
66a2de3
Route KLIPY GIF search through relays
klopez4212 Aug 20, 2026
c0c24d8
Merge remote-tracking branch 'origin/main' into kennylopez-klipy-gif-…
klopez4212 Aug 20, 2026
c7416f2
Refine relay GIF capability contract
klopez4212 Aug 20, 2026
1bd867b
Address KLIPY GIF review feedback
Aug 21, 2026
9db36e2
Merge origin/main into kennylopez-klipy-gif-picker
Aug 21, 2026
13ccf1b
fix(gifs): address KLIPY GIF picker review — redirects, reduced motio…
Aug 25, 2026
29090f5
Merge remote-tracking branch 'origin/main' into kennylopez-klipy-gif-…
Aug 25, 2026
2d9c11d
fix(gifs): route GIF displayLabel through ImetaMedia, not BlobDescriptor
Aug 25, 2026
66fedbb
fix(gifs): preserve type-aware hash labels for ordinary uploads
Aug 25, 2026
a5d6f80
Merge remote-tracking branch 'origin/main' into kennylopez-klipy-gif-…
Aug 25, 2026
e7b9f5a
fix(gifs): name the media trigger button, not its child image
Aug 25, 2026
23ac906
test(gifs): stop the selection test toggling the picker shut
Aug 25, 2026
f2135f7
Merge remote-tracking branch 'origin/main' into kennylopez-klipy-gif-…
Aug 25, 2026
4ab25dd
Merge remote-tracking branch 'origin/main' into kennylopez-klipy-gif-…
Aug 25, 2026
3edae81
chore(desktop-messages): keep MessageComposer within file-size cap
Aug 25, 2026
77eefdd
Merge remote-tracking branch 'origin/main' into kennylopez-klipy-gif-…
Aug 25, 2026
7262c8f
test(desktop): stabilize DM agent-startup-failure retry click
Aug 25, 2026
db5e6c7
Merge remote-tracking branch 'origin/main' into kennylopez-klipy-gif-…
Aug 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,15 @@ RELAY_URL=ws://localhost:3000
# (use `just web` for Vite HMR instead).
# BUZZ_WEB_DIR=./web/dist

# Optional relay-owned KLIPY key. When set, NIP-11 advertises GIF search and
# authenticated desktop clients use this relay as the metadata/search proxy.
# Keep the real value in your deployment's secret manager; never commit it.
# BUZZ_KLIPY_API_KEY=

# Shared Redis-backed admission limits. Defaults shown below; each value must
# be a positive integer.
# BUZZ_RATE_LIMIT_HUMAN_MESSAGES_PER_MIN=60
# BUZZ_RATE_LIMIT_GIF_SEARCHES_PER_MIN=30
# BUZZ_RATE_LIMIT_HUMAN_API_CALLS_PER_MIN=300
# BUZZ_RATE_LIMIT_HUMAN_WS_EVENTS_PER_SEC=10
# BUZZ_RATE_LIMIT_AGENT_STANDARD_MESSAGES_PER_MIN=120
Expand Down
22 changes: 22 additions & 0 deletions crates/buzz-auth/src/rate_limit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,8 @@ pub enum LimitType {
Messages,
/// HTTP REST API calls.
ApiCalls,
/// Relay-proxied GIF metadata searches.
GifSearches,
/// All WebSocket events (broader than `Messages`).
WsEvents,
/// Concurrent WebSocket connections from a single IP address.
Expand All @@ -72,6 +74,7 @@ impl LimitType {
match self {
Self::Messages => "msg",
Self::ApiCalls => "api",
Self::GifSearches => "gif",
Self::WsEvents => "ws",
Self::IpConnections => "conn",
}
Expand All @@ -87,6 +90,10 @@ pub struct RateLimitConfig {
/// Maximum messages per minute for human users. Default: 60.
#[serde(default = "default_human_msg")]
pub human_messages_per_min: u64,
/// Maximum relay-proxied GIF searches per minute for each pubkey.
/// Default: 30.
#[serde(default = "default_gif_searches")]
pub gif_searches_per_min: u64,
/// Maximum HTTP API calls per minute for human users. Default: 300.
#[serde(default = "default_human_api")]
pub human_api_calls_per_min: u64,
Expand All @@ -110,6 +117,9 @@ pub struct RateLimitConfig {
fn default_human_msg() -> u64 {
60
}
fn default_gif_searches() -> u64 {
30
}
fn default_human_api() -> u64 {
300
}
Expand All @@ -133,6 +143,7 @@ impl Default for RateLimitConfig {
fn default() -> Self {
Self {
human_messages_per_min: default_human_msg(),
gif_searches_per_min: default_gif_searches(),
human_api_calls_per_min: default_human_api(),
human_ws_events_per_sec: default_human_ws(),
agent_standard_messages_per_min: default_agent_std_msg(),
Expand Down Expand Up @@ -272,6 +283,17 @@ mod tests {
assert!(key.ends_with(":msg"));
}

#[test]
fn gif_searches_have_an_independent_quota_key() {
let ctx = fixture_ctx("relay-a.example");
let keys = Keys::generate();
let gif_key = rate_limit_key(&ctx, &keys.public_key(), &LimitType::GifSearches);
let api_key = rate_limit_key(&ctx, &keys.public_key(), &LimitType::ApiCalls);

assert!(gif_key.ends_with(":gif"));
assert_ne!(gif_key, api_key);
}

#[test]
fn rate_limit_key_isolates_communities_for_same_pubkey() {
// The S1 cross-community isolation fence at the rate-limit key layer:
Expand Down
Loading
Loading