Adjust ACL behavior for "none" mode and make query cache optional
Some checks failed
Go / build-and-release (push) Has been cancelled

This commit allows skipping authentication, permission checks, and certain filters (e.g., deletions, expirations) when the ACL mode is set to "none" (open relay mode). It also introduces a configuration option to disable query caching to reduce memory usage. These changes improve operational flexibility for open relay setups and resource-constrained environments.
This commit is contained in:
2025-12-05 11:25:34 +00:00
parent 6b72f1f2b7
commit c1bd05fb04
19 changed files with 201 additions and 285 deletions

View File

@@ -3,11 +3,19 @@ package acl
import (
"git.mleku.dev/mleku/nostr/encoders/event"
acliface "next.orly.dev/pkg/interfaces/acl"
"next.orly.dev/pkg/mode"
"next.orly.dev/pkg/utils/atomic"
)
var Registry = &S{}
// SetMode sets the active ACL mode and syncs it to the mode package for
// packages that need to check the mode without importing acl (to avoid cycles).
func (s *S) SetMode(m string) {
s.Active.Store(m)
mode.ACLMode.Store(m)
}
type S struct {
ACL []acliface.I
Active atomic.String

View File

@@ -106,6 +106,12 @@ func NewWithConfig(
queryCacheSize := int64(queryCacheSizeMB * 1024 * 1024)
// Create query cache only if not disabled
var qc *querycache.EventCache
if !cfg.QueryCacheDisabled {
qc = querycache.NewEventCache(queryCacheSize, queryCacheMaxAge)
}
d = &D{
ctx: ctx,
cancel: cancel,
@@ -114,7 +120,7 @@ func NewWithConfig(
DB: nil,
seq: nil,
ready: make(chan struct{}),
queryCache: querycache.NewEventCache(queryCacheSize, queryCacheMaxAge),
queryCache: qc,
serialCache: NewSerialCache(serialCachePubkeys, serialCacheEventIds),
}

View File

@@ -18,10 +18,11 @@ type DatabaseConfig struct {
LogLevel string
// Badger-specific settings
BlockCacheMB int // ORLY_DB_BLOCK_CACHE_MB
IndexCacheMB int // ORLY_DB_INDEX_CACHE_MB
QueryCacheSizeMB int // ORLY_QUERY_CACHE_SIZE_MB
QueryCacheMaxAge time.Duration // ORLY_QUERY_CACHE_MAX_AGE
BlockCacheMB int // ORLY_DB_BLOCK_CACHE_MB
IndexCacheMB int // ORLY_DB_INDEX_CACHE_MB
QueryCacheDisabled bool // ORLY_QUERY_CACHE_DISABLED - disable query cache to reduce memory usage
QueryCacheSizeMB int // ORLY_QUERY_CACHE_SIZE_MB
QueryCacheMaxAge time.Duration // ORLY_QUERY_CACHE_MAX_AGE
// Serial cache settings for compact event storage
SerialCachePubkeys int // ORLY_SERIAL_CACHE_PUBKEYS - max pubkeys to cache (default: 100000)

View File

@@ -13,6 +13,7 @@ import (
"lol.mleku.dev/log"
"github.com/minio/sha256-simd"
"next.orly.dev/pkg/database/indexes/types"
"next.orly.dev/pkg/mode"
"git.mleku.dev/mleku/nostr/encoders/event"
"git.mleku.dev/mleku/nostr/encoders/filter"
"git.mleku.dev/mleku/nostr/encoders/hex"
@@ -102,7 +103,8 @@ func (d *D) QueryEventsWithOptions(c context.Context, f *filter.F, includeDelete
}
// check for an expiration tag and delete after returning the result
if CheckExpiration(ev) {
// Skip expiration check when ACL is "none" (open relay mode)
if !mode.IsOpen() && CheckExpiration(ev) {
log.T.F(
"QueryEvents: id=%s filtered out due to expiration", idHex,
)
@@ -112,9 +114,12 @@ func (d *D) QueryEventsWithOptions(c context.Context, f *filter.F, includeDelete
}
// skip events that have been deleted by a proper deletion event
if derr := d.CheckForDeleted(ev, nil); derr != nil {
log.T.F("QueryEvents: id=%s filtered out due to deletion: %v", idHex, derr)
continue
// Skip deletion check when ACL is "none" (open relay mode)
if !mode.IsOpen() {
if derr := d.CheckForDeleted(ev, nil); derr != nil {
log.T.F("QueryEvents: id=%s filtered out due to deletion: %v", idHex, derr)
continue
}
}
// Add the event to the results
@@ -210,13 +215,15 @@ func (d *D) QueryEventsWithOptions(c context.Context, f *filter.F, includeDelete
}
// check for an expiration tag and delete after returning the result
if CheckExpiration(ev) {
// Skip expiration check when ACL is "none" (open relay mode)
if !mode.IsOpen() && CheckExpiration(ev) {
expDeletes = append(expDeletes, ser)
expEvs = append(expEvs, ev)
continue
}
// Process deletion events to build our deletion maps
if ev.Kind == kind.Deletion.K {
// Skip deletion processing when ACL is "none" (open relay mode)
if !mode.IsOpen() && ev.Kind == kind.Deletion.K {
// Check for 'e' tags that directly reference event IDs
eTags := ev.Tags.GetAll([]byte("e"))
for _, eTag := range eTags {
@@ -433,8 +440,10 @@ func (d *D) QueryEventsWithOptions(c context.Context, f *filter.F, includeDelete
}
}
// Check if this specific event has been deleted
// Skip deletion checks when ACL is "none" (open relay mode)
aclActive := !mode.IsOpen()
eventIdHex := hex.Enc(ev.ID)
if deletedEventIds[eventIdHex] {
if aclActive && deletedEventIds[eventIdHex] {
// Skip this event if it has been specifically deleted
continue
}
@@ -446,7 +455,7 @@ func (d *D) QueryEventsWithOptions(c context.Context, f *filter.F, includeDelete
// deletion Only skip this event if it has been deleted by
// kind/pubkey and is not in the filter AND there isn't a newer
// event with the same kind/pubkey
if deletionsByKindPubkey[key] && !isIdInFilter {
if aclActive && deletionsByKindPubkey[key] && !isIdInFilter {
// This replaceable event has been deleted, skip it
continue
} else if wantMultipleVersions {
@@ -475,11 +484,14 @@ func (d *D) QueryEventsWithOptions(c context.Context, f *filter.F, includeDelete
}
// Check if this event has been deleted via an a-tag
if deletionMap, exists := deletionsByKindPubkeyDTag[key]; exists {
// If there is a deletion timestamp and this event is older than the deletion,
// and this event is not specifically requested by ID, skip it
if delTs, ok := deletionMap[dValue]; ok && ev.CreatedAt < delTs && !isIdInFilter {
continue
// Skip deletion check when ACL is "none" (open relay mode)
if aclActive {
if deletionMap, exists := deletionsByKindPubkeyDTag[key]; exists {
// If there is a deletion timestamp and this event is older than the deletion,
// and this event is not specifically requested by ID, skip it
if delTs, ok := deletionMap[dValue]; ok && ev.CreatedAt < delTs && !isIdInFilter {
continue
}
}
}

View File

@@ -14,6 +14,7 @@ import (
"lol.mleku.dev/log"
"next.orly.dev/pkg/database/indexes"
"next.orly.dev/pkg/database/indexes/types"
"next.orly.dev/pkg/mode"
"git.mleku.dev/mleku/nostr/encoders/event"
"git.mleku.dev/mleku/nostr/encoders/filter"
"git.mleku.dev/mleku/nostr/encoders/hex"
@@ -177,13 +178,16 @@ func (d *D) SaveEvent(c context.Context, ev *event.E) (
}
// Check if the event has been deleted before allowing resubmission
if err = d.CheckForDeleted(ev, nil); err != nil {
// log.I.F(
// "SaveEvent: rejecting resubmission of deleted event ID=%s: %v",
// hex.Enc(ev.ID), err,
// )
err = fmt.Errorf("blocked: %s", err.Error())
return
// Skip deletion check when ACL is "none" (open relay mode)
if !mode.IsOpen() {
if err = d.CheckForDeleted(ev, nil); err != nil {
// log.I.F(
// "SaveEvent: rejecting resubmission of deleted event ID=%s: %v",
// hex.Enc(ev.ID), err,
// )
err = fmt.Errorf("blocked: %s", err.Error())
return
}
}
// check for replacement - only validate, don't delete old events
if kind.IsReplaceable(ev.Kind) || kind.IsParameterizedReplaceable(ev.Kind) {

18
pkg/mode/mode.go Normal file
View File

@@ -0,0 +1,18 @@
// Package mode provides a global ACL mode indicator that can be read by
// packages that need to know the current access control mode without creating
// circular dependencies.
package mode
import "next.orly.dev/pkg/utils/atomic"
// ACLMode holds the current ACL mode as a string.
// This is set by the ACL package when configured and can be read by other
// packages (like database) to adjust their behavior.
var ACLMode atomic.String
// IsOpen returns true if the ACL mode is "none" (open relay mode).
// In open mode, security filtering (expiration, deletion, privileged events)
// should be disabled.
func IsOpen() bool {
return ACLMode.Load() == "none"
}

View File

@@ -120,7 +120,7 @@ func Start(cfg *config.C, opts *Options) (relay *Relay, err error) {
}
// Configure ACL
acl.Registry.Active.Store(cfg.ACLMode)
acl.Registry.SetMode(cfg.ACLMode)
if err = acl.Registry.Configure(cfg, relay.db, relay.ctx); chk.E(err) {
return
}

View File

@@ -1 +1 @@
v0.34.2
v0.34.3