- Add start/stop/restart buttons for individual services in launcher UI
- Show all available modules with categories (Database, ACL, Sync, Certs)
- Add enable/disable toggles with mutual exclusivity for DB/ACL backends
- Handle service dependencies when stopping (DB stops dependents first)
- Advertise NIP-86 in NIP-11 when ACL mode is managed or curating
- Add module descriptions showing API coverage
Files modified:
- app/handle-relayinfo.go: Add NIP-86 to supported NIPs for managed/curating modes
- cmd/orly-launcher/server.go: Add start-service/stop-service endpoints, ProcessStatus fields
- cmd/orly-launcher/supervisor.go: Add StartService, StopService with dependency handling
- cmd/orly-launcher/web/src/api.js: Add startService, stopService API functions
- cmd/orly-launcher/web/src/components/ProcessCard.svelte: Add toggles, categories, action buttons
- cmd/orly-launcher/web/src/pages/Dashboard.svelte: Add service control handlers
- pkg/version/version: Bump to v0.56.9
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Simplify HandleEvent to thin protocol adapter (~320 -> ~130 lines)
- Create ingestion service for full event pipeline orchestration
- Add specialkinds.go for special kind handler registration
- Eliminate global ACL singleton with injectable Registry interface
- Add ACLRegistry() accessor to Server for dependency injection
- Decompose Rule value object into AccessControl, Constraints,
TagValidationConfig sub-components for cleaner organization
- Add LoggingSubscriber for domain event analytics
- Update all policy tests for embedded struct initialization
- Update DDD_ANALYSIS.md to 10/10 maturity score
Files modified:
- app/handle-event.go: Simplified to delegate to ingestion service
- app/specialkinds.go: NEW - Special kind handler registration
- app/server.go: Add aclRegistry field and ACLRegistry() accessor
- pkg/interfaces/acl/acl.go: Add Registry interface
- pkg/acl/acl.go: Add accessor methods for privatized fields
- pkg/policy/policy.go: Decompose Rule into sub-value objects
- pkg/policy/*_test.go: Update struct literals for embedded types
- pkg/event/ingestion/service.go: Add ACLMode, special kinds support
- pkg/event/processing/processing.go: Add domain event dispatcher
- pkg/domain/events/subscribers/logging.go: NEW - Analytics subscriber
- DDD_ANALYSIS.md: Update to 10/10 maturity score
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change IdPkTs Id/Pub fields from []byte to [32]byte (ntypes.EventID/Pubkey)
- Add NewIdPkTs() constructor for copying byte slices into fixed arrays
- Add IDSlice() and PubSlice() methods for slice access when needed
- Update protobuf converters to handle array-to-slice conversion
- Update all database and neo4j creation sites to use NewIdPkTs()
- Fix test files to use bytes.Equal() for array comparisons
- Benchmark confirms 0 B/op, 0 allocs/op for copy operations
Files modified:
- pkg/interfaces/store/store_interface.go: Core type change to fixed arrays
- pkg/interfaces/store/store_interface_test.go: Tests for new API
- pkg/proto/orlydb/v1/converters.go: Protobuf conversion with ntypes
- pkg/database/get-fullidpubkey-by-serial.go: Use NewIdPkTs constructor
- pkg/database/get-fullidpubkey-by-serials.go: Use NewIdPkTs constructor
- pkg/database/process-delete.go: Slice arrays for DeleteEvent calls
- pkg/neo4j/fetch-event.go: Use NewIdPkTs constructor
- pkg/neo4j/query-events.go: Use NewIdPkTs constructor
- pkg/database/query-for-*_test.go: Use bytes.Equal for comparisons
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Include pre-built libraries in lib/secp256k1/ directory
- Update CI workflow to use local libraries instead of nostr module
- Update release notes to indicate ARM64 libsecp256k1 included
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Building from source was failing. The nostr module already has
a properly built libsecp256k1.so with schnorr/musig2 support.
ARM64 users need to build libsecp256k1 from source separately.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Cross-compile with schnorr/musig2 support for both architectures.
System packages lack these Nostr-required features.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
System packages lack schnorr/musig2 support needed for Nostr.
Copy the custom-built library from git.mleku.dev/mleku/nostr/crypto/p8k/.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The library was removed from the repo - it's loaded at runtime via purego.
Users must install libsecp256k1 separately (apt install libsecp256k1-1).
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ServicesEnabled config to run admin UI without starting services
- Add start/stop services API endpoints and dashboard controls
- Add IsRunning() method to supervisor for service state tracking
- Fix release download URLs to use git.nostrdev.com instead of git.mleku.dev
- Change Makefile to use go install (except main relay uses go build for name)
- Add orly-certs DNS-01 wildcard certificate manager
- Remove libsecp256k1.so from repo (runtime dependency only)
Files modified:
- cmd/orly-launcher/config.go: Add ServicesEnabled option
- cmd/orly-launcher/main.go: Skip services when disabled, update help
- cmd/orly-launcher/server.go: Add start/stop endpoints, fix tags API URL
- cmd/orly-launcher/supervisor.go: Add IsRunning(), allow restart after stop
- cmd/orly-launcher/web/src/api.js: Add startServices/stopServices functions
- cmd/orly-launcher/web/src/pages/Dashboard.svelte: Add start/stop buttons
- cmd/orly-launcher/web/src/pages/Update.svelte: Fix release base URL
- cmd/orly-certs/: New DNS-01 certificate manager
- Makefile: Use go install, keep go build for main relay
- pkg/version/version: Bump to v0.56.2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add jq installation step for JSON processing
- Delete existing release before creating new one (handles re-runs)
- Fetch tag to ensure it exists before release creation
- Use jq properly for JSON payload construction
- Fix release body indentation
Files modified:
- .gitea/workflows/go.yml: Improved release workflow
- pkg/version/version: Bump to v0.56.1
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add embedded Svelte admin web UI for process monitoring and control
- Implement NIP-98 HTTP authentication middleware for secure API access
- Add binary update/rollback system with versioned symlinks
- Add admin API endpoints: status, config, binaries, update, restart, rollback
- Update CI workflow to build all binaries for AMD64 and ARM64 architectures
- Add launcher-web Makefile target for building admin UI separately
Files modified:
- .gitea/workflows/go.yml: Build all binaries and launcher admin UI
- Makefile: Add launcher-web and orly-launcher-no-web targets
- cmd/orly-launcher/auth.go: NIP-98 authentication middleware
- cmd/orly-launcher/config.go: Admin UI configuration (port, owners)
- cmd/orly-launcher/main.go: Start admin server, updated help text
- cmd/orly-launcher/server.go: Admin HTTP server with API endpoints
- cmd/orly-launcher/supervisor.go: GetProcessStatuses, RestartAll methods
- cmd/orly-launcher/updater.go: Binary version management with symlinks
- cmd/orly-launcher/web.go: Embedded admin UI serving
- cmd/orly-launcher/web/: Svelte admin UI (dashboard, config, update pages)
- pkg/version/version: Bump to v0.55.11
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
## Summary
- Add O(1) direct lookup for parameterized replaceable events (kinds 30000-39999)
- Reduces query time from 10+ seconds to <1ms for kind+author+d-tag queries
- Optimizes WouldReplaceEvent() for faster event replacement checks
Fixes#29
## Changes
- `pkg/database/query-addressable.go` - New file with fast path lookup logic
- `pkg/database/query-addressable_test.go` - Tests for the optimization
- `pkg/database/save-event.go` - Writes AddressableEvent index, optimizes WouldReplaceEvent
- `pkg/database/query-events.go` - Integrates fast path at start of QueryEventsWithOptions
- `pkg/database/get-indexes-for-event.go` - Minor refactor (variable rename)
## Test plan
- [x] All existing database tests pass
- [x] New tests added for addressable event queries
- [x] Tests cover: valid/invalid query patterns, not found case, event replacement
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: woikos <woikos@users.noreply.github.com>
Reviewed-on: https://git.nostrdev.com/mleku/next.orly.dev/pulls/30
Problem: Queries with kinds + authors + d-tags take 10+ seconds
Root cause: Using TagKindPubkey index requires timestamp iteration
Solution: Use AddressableEvent index for direct O(1) lookup
Plan covers:
- Phase 1: Enable AddressableEvent index writes on save
- Phase 2: Add fast path query for NIP-33 lookups
- Phase 3: Integrate into QueryEvents flow
- Phase 4: Optimize WouldReplaceEvent
- Phase 5: Handle index updates on replace
- Migration strategy for existing data
Expected improvement: 10+ seconds → <1ms
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Use pre-built dockurr/strfry image instead of building from source
- Fix strfry.conf format to match strfry 1.0.4 syntax
- Fix Dockerfile.orly to build both relay and sync binaries
- Use GOTOOLCHAIN=auto to handle Go version requirements
- Remove healthcheck dependencies to prevent startup blocking
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Use eventenvelope.NewResultWith() to properly format EVENT messages
with subscription ID, matching the expected format: ["EVENT", sub_id, event]
Previously SendEvent was sending just the raw event which caused JSON
parsing errors on the receiving side.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add have_ids, need_ids, complete fields to NegOpenResponse proto
- Fix ID encoding in server: decode hex strings to binary before sending
- Fix event sending order: send NEG-MSG response before EVENT envelopes
- Client now waits for expected events after reconciliation completes
- Store received events from peer in sync client message loop
This enables bidirectional event sync via NIP-77 negentropy. The server
can now push events it has that the client needs, in addition to the
client pushing events to the server.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Implement sendEventsForIDs to actually send events via EVENT envelopes
- Previously this was a TODO stub that only logged "would send"
- Server now fetches and sends identified "have" events to clients
Files modified:
- pkg/version/version: Bump to v0.55.6
- app/handle-negentropy.go: Implement sendEventsForIDs
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add MaxEvents config option to negentropy manager
- Increase default sync limit from 100,000 to 1,000,000 events
- Ensures full database sync for relays with 200k+ events
Files modified:
- pkg/sync/negentropy/manager.go: Add MaxEvents config, use configurable limit
- pkg/sync/negentropy/server/service.go: Increase hardcoded limit to 1M
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Adds ORLY_SYNC_NEGENTROPY_FILTER env var for specifying a JSON-encoded
nostr filter for selective sync. The filter is used both when building
local storage for comparison and in the NEG-OPEN message sent to peers.
Example usage:
ORLY_SYNC_NEGENTROPY_FILTER='{"kinds":[1,6,7,30023]}'
ORLY_SYNC_NEGENTROPY_FILTER='{"authors":["abc123..."],"since":1700000000}'
This allows syncing only specific event types between relays instead of
the full event set.
Files modified:
- cmd/orly-sync-negentropy/config.go: Add FilterJSON config and parsing
- cmd/orly-sync-negentropy/main.go: Pass filter to manager config
- pkg/sync/negentropy/manager.go: Add Filter to Config, use in buildStorage and NEG-OPEN
- pkg/version/version: Bump to v0.55.3
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- ACL: Mark service ready immediately after gRPC server starts
- ACL: Run Configure() in background goroutine (follow list loading)
- Launcher: Actually call gRPC Ready() endpoint instead of just TCP port check
- Launcher: Increase ACL ready timeout from 30s to 120s as fallback
This fixes the issue where relay would timeout waiting for ACL because
the launcher only checked if the TCP port was open, but the ACL service's
Ready() was returning false until Configure() completed (which takes
minutes for large follow lists).
Now ACL marks itself ready immediately so the relay can start while
follow lists continue loading in the background.
Files modified:
- cmd/orly-acl/main.go: Restructure startup to be async
- cmd/orly-launcher/config.go: Increase ACL timeout to 120s
- cmd/orly-launcher/supervisor.go: Add gRPC Ready() check
- pkg/version/version: Bump to v0.55.2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Remove debug 100 iteration limit from GetSerialsByRange
The limit was a debug safety check that should have been removed.
This was capping all range queries to 100 results, causing negentropy
sync to only see 100 events instead of all events.
- Fix ACL service startup to listen immediately before Configure
The ACL server now starts the gRPC listener immediately after database
ready, then runs Configure in the background. This prevents the
launcher from timing out while waiting for ACL to load follow lists.
- Add SetReady method to ACL service for proper ready signaling
Files modified:
- pkg/database/get-serials-by-range.go: Remove debug iteration limit
- cmd/orly-acl/main.go: Start gRPC server before Configure
- cmd/orly-acl/service.go: Add ready field and SetReady method
- pkg/version/version: Bump to v0.55.1
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add four independent gRPC sync service binaries:
- orly-sync-distributed: Serial-based peer-to-peer sync
- orly-sync-cluster: Cluster replication with persistent state
- orly-sync-relaygroup: Relay group config discovery (Kind 39105)
- orly-sync-negentropy: NIP-77 efficient set reconciliation
- Implement NIP-77 negentropy protocol for relay-to-relay sync
- Add push-based event transfer for negentropy sync
- Add client-facing NIP-77 WebSocket support (NEG-OPEN/MSG/CLOSE)
- Add deployment scripts with symlink-based versioning
- Add Makefile targets for quick deployment and rollback
- Refactor existing sync code into modular service packages
- Add proto definitions for all sync services
- Integrate negentropy service into orly-launcher supervisor
- Advertise NIP-77 support in relay info document
Files modified:
- Makefile: Add sync service build targets and deployment commands
- README.md: Document NIP-77 support
- app/config/config.go: Add negentropy configuration options
- app/handle-message.go: Route NEG-* envelopes to negentropy handler
- app/handle-negentropy.go: New file for NIP-77 WebSocket handling
- app/handle-relayinfo.go: Advertise NIP-77 support
- cmd/orly-launcher/: Add negentropy service management
- cmd/orly-sync-*/: New service binaries
- main.go: Add negentropy gRPC client initialization
- pkg/proto/orlysync/: New proto definitions and generated code
- pkg/sync/*/: Refactored sync packages with gRPC services
- scripts/deploy-orly.sh: New symlink-based deployment script
- scripts/build-and-deploy.sh: New build and deploy workflow
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change Follows, Managed, and Curating ACL structs to use database.Database
interface instead of concrete *database.D type
- Add type assertions for ManagedACL and CuratingACL initialization since
they require Badger-specific APIs
- Log warning when running managed/curating ACL with non-Badger backend
- Update managed_minimal_test.go to use new field name
Files modified:
- pkg/acl/follows.go: Use db database.Database field, replace f.D with f.db
- pkg/acl/managed.go: Use db database.Database with type assertion for ManagedACL
- pkg/acl/curating.go: Use db database.Database with type assertion for CuratingACL
- pkg/acl/managed_minimal_test.go: Update test to use db field
- pkg/version/version: Bump to v0.53.1
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add query semaphore limiting concurrent database queries to 3
- Reuse single iterator in FetchEventsBySerials instead of one per serial
- Disable expensive e-tag fallback lookup in deletion processing
- Add empty ID validation in access tracker loop
- Reduce log spam from GetSerialById empty ID errors
These changes reduce memory usage from ~5GB to ~150MB under load by
limiting concurrent Badger iterators which consume significant memory.
Files modified:
- pkg/database/database.go: Add query semaphore with acquire/release
- pkg/database/fetch-events-by-serials.go: Reuse iterator for sev lookups
- pkg/database/query-events.go: Add semaphore, disable e-tag fallback
- pkg/database/get-serial-by-id.go: Don't log empty ID errors
- app/handle-req.go: Validate event ID before GetSerialById
- pkg/version/version: Bump to v0.52.17
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Previous: 10MB read + 10MB write = 20MB per connection
New: 64KB read + 64KB write = 128KB per connection
With 100 connections:
- Before: 2GB just for buffers
- After: 12.5MB for buffers
The read limit (SetReadLimit) still allows 10MB messages, but the
internal buffers don't need to be that large.
Files modified:
- app/handle-websocket.go: Reduce buffer sizes
- pkg/version/version: Bump to v0.52.12
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ORLY_QUERY_RESULT_LIMIT config (default 256)
- Enforce limit on each REQ filter before database query
- Caps client limit if it exceeds server limit
- Applies server limit if client sends no limit
This prevents unbounded queries from loading millions of events
into memory and causing OOM kills.
Files modified:
- app/config/config.go: Add QueryResultLimit config
- app/handle-req.go: Enforce limit on each filter
- pkg/version/version: Bump to v0.52.11
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ORLY_MAX_CONN_PER_IP config option (default 25, hard limit 40)
- Track connections per IP in Server struct
- Reject new connections with HTTP 429 when limit exceeded
- Properly decrement count when connections close
This prevents a single IP from opening unlimited connections and
exhausting server memory/goroutines.
Files modified:
- app/config/config.go: Add MaxConnectionsPerIP config
- app/server.go: Add connPerIP tracking map
- app/main.go: Initialize connPerIP map
- app/handle-websocket.go: Implement per-IP limit check
- pkg/version/version: Bump to v0.52.10
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Disable goroutine that deleted expired events during query processing
- This was causing "assignment to entry in nil map" Badger panics under load
- Expired events will remain until proper background cleanup is implemented
- Added TODO comments explaining the issue and fix needed
Files modified:
- pkg/database/query-events.go: Disable inline expired event deletion
- pkg/version/version: Bump to v0.52.9
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change ORLY_GC_ENABLED default from true to false
- Mark GC as EXPERIMENTAL in config usage string
- Add detailed TODOs documenting Badger race condition issues
- GC triggers "assignment to entry in nil map" panics under concurrent load
Files modified:
- app/config/config.go: Default GC to false, add TODO comments
- pkg/storage/gc.go: Add detailed implementation TODOs
- pkg/version/version: Bump to v0.52.8
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change default event query window from 30 days to 5 years in web UI
- Remove 6-month fallback retry logic (no longer needed with larger window)
- Simplifies fetchAllEvents function in nostr.js
Files modified:
- app/web/src/nostr.js: Extended time window from 30 days to 5 years
- app/web/dist/bundle.js: Rebuilt with changes
- pkg/version/version: Bump to v0.52.7
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add port 7777 to list of known relay ports in config.js
- Prevents false-positive standalone mode detection
Files modified:
- app/web/src/config.js: Add 7777 to relay port list
- pkg/version/version: Bump to v0.52.6
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add post-query filtering to return only latest version per (pubkey, kind, d-tag)
- Delete older versions on save for kinds 30000-39999 in Neo4j backend
- QueryAllVersions bypasses filtering for recovery UI compatibility
- Badger continues to keep old versions (filtered at query time)
Files modified:
- pkg/neo4j/query-events.go: Add replaceable event filtering logic
- pkg/neo4j/save-event.go: Add deleteOlderParameterizedReplaceable helper
- pkg/version/version: Bump to v0.52.5
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>