Adjust ACL behavior for "none" mode and make query cache optional
Some checks failed
Go / build-and-release (push) Has been cancelled
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:
@@ -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
|
||||
|
||||
@@ -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),
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
18
pkg/mode/mode.go
Normal 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"
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -1 +1 @@
|
||||
v0.34.2
|
||||
v0.34.3
|
||||
Reference in New Issue
Block a user