- Add geteventsforpubkey API method for viewing user events with pagination
- Add deleteeventsforpubkey API method to purge blacklisted user events
- Add clickable user detail view in curation UI showing all events
- Add event content expansion/truncation for long content
- Add kind name display for common Nostr event types
- Implement safety check requiring blacklist before event deletion
Files modified:
- app/handle-nip86-curating.go: Add event fetch/delete handlers
- pkg/database/curating-acl.go: Add GetEventsForPubkey, DeleteEventsForPubkey
- app/web/src/CurationView.svelte: Add user detail view with event listing
- pkg/version/version: Bump to v0.50.0
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix countEventsForPubkey to use SHA256 hash of pubkey (first 8 bytes)
matching the PubHash type used in the Pubkey index
- Fix UI to use event_count field instead of total_events
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Adds "Scan Database" button that calls the scanpubkeys API
- Shows results with total pubkeys, events, and skipped count
- Automatically refreshes the unclassified users list after scan
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add ScanAllPubkeys method to scan SerialPubkey index for all pubkeys
- Count events for each pubkey using the Pubkey index
- Store event counts in CURATING_ACL_EVENT_COUNT_ prefix
- Add NIP-86 "scanpubkeys" API endpoint to trigger the scan
This allows the curation UI to show all existing users in the unclassified
list, even if they had events before curating mode was enabled.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
The frontend expected 'categories' and 'custom_kinds' but the backend
returned 'kind_categories' and 'allowed_kinds'. Add aliases for both
naming conventions to ensure frontend compatibility.
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change OPTIONAL MATCH to EXISTS subquery for tag filtering in Neo4j
- OPTIONAL MATCH returned rows even when tags didn't match (NULL values)
- EXISTS subquery correctly requires matching tags to exist
- Strip "#" prefix from filter tag types before matching
- Filters use "#d", "#p", "#e" but events store tags without prefix
- Add trace-level logging for Neo4j query debugging
- Add comprehensive tests for Neo4j query builder
- Clean up temporary debug logging from handle-req.go
Files modified:
- pkg/neo4j/query-events.go: Fix tag filtering with EXISTS subquery
- pkg/neo4j/query-events_test.go: Add query builder tests
- app/handle-req.go: Remove debug logging
- pkg/version/version: Bump to v0.49.2
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix fetchEvents() discarding IndexedDB cached events instead of merging with relay results
- Add mergeAndDeduplicateEvents() helper to combine and dedupe events by ID
- Add ORLY_BLOSSOM_ENABLED config option to disable Blossom server
- Make fetch-kinds.js fall back to existing eventKinds.js when network unavailable
Files modified:
- app/web/src/nostr.js: Fix event caching, add merge helper
- app/web/scripts/fetch-kinds.js: Add fallback for network failures
- app/config/config.go: Add BlossomEnabled config field
- app/main.go: Check BlossomEnabled before initializing Blossom server
- pkg/version/version: Bump to v0.48.13
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
- Remove createDefaultProfile() function from nostr.js that auto-created
placeholder profiles for new users - profiles should not be auto-generated
- Add auth-required configuration caution section to CLAUDE.md documenting
risks of enabling NIP-42 auth on production relays
Files modified:
- CLAUDE.md: Added auth-required configuration section
- app/web/src/nostr.js: Removed createDefaultProfile and auto-profile logic
- app/web/dist/bundle.js: Rebuilt with changes
- pkg/version/version: v0.48.11
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add progressive throttle feature for follows ACL mode, allowing
non-followed users to write with increasing delay instead of blocking
- Delay increases linearly per event (default 200ms) and decays at 1:1
ratio with elapsed time, capping at configurable max (default 60s)
- Track both IP and pubkey independently to prevent evasion
- Add periodic cleanup to remove fully-decayed throttle entries
- Fix BBolt serial resolver to return proper errors when buckets or
entries are not found
Files modified:
- app/config/config.go: Add ORLY_FOLLOWS_THROTTLE_* env vars
- app/handle-event.go: Apply throttle delay before event processing
- app/listener.go: Add getFollowsThrottleDelay helper method
- pkg/acl/follows.go: Integrate throttle with follows ACL
- pkg/acl/follows_throttle.go: New progressive throttle implementation
- pkg/bbolt/save-event.go: Return errors from serial lookups
- pkg/version/version: Bump to v0.48.10
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix Content-Type header being set on request instead of response
- Add Vary: Accept header to prevent browser/CDN caching NIP-11 for HTML
- Add periodic flushing during export for HTTP streaming (every 100 events)
- Add initial flush after headers to start streaming immediately
- Add X-Content-Type-Options: nosniff to prevent browser buffering
Files modified:
- app/handle-relayinfo.go: Fix header and add Vary: Accept
- app/server.go: Add initial flush and nosniff header for export
- pkg/database/export.go: Add periodic and final flushing for streaming
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Spawn tor binary as subprocess instead of requiring external daemon
- Auto-generate torrc in $ORLY_DATA_DIR/tor/ (userspace, no root)
- Enable Tor by default; gracefully disable if tor binary not found
- Add ORLY_TOR_BINARY and ORLY_TOR_SOCKS config options
- Remove external Tor setup scripts and documentation
Files modified:
- app/config/config.go: New subprocess-based Tor config options
- app/main.go: Updated Tor initialization for new config
- pkg/tor/service.go: Rewritten for subprocess management
- Removed: deploy/orly-tor.service, docs/TOR_SETUP.md, scripts/tor-*.sh
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add pkg/tor package for Tor hidden service integration
- Add Tor config options: ORLY_TOR_ENABLED, ORLY_TOR_PORT, ORLY_TOR_HS_DIR, ORLY_TOR_ONION_ADDRESS
- Extend NIP-11 relay info with addresses field for .onion URLs
- Add fallback relays (Damus, nos.lol, nostr.band, purplepag.es) for profile lookups
- Refactor profile fetching to try local relay first, then fallback relays
- Add Tor setup documentation and deployment scripts
Files modified:
- app/config/config.go: Add Tor configuration options
- app/handle-relayinfo.go: Add ExtendedRelayInfo with addresses field
- app/main.go: Initialize and manage Tor service lifecycle
- app/server.go: Add torService field to Server struct
- app/web/src/constants.js: Add FALLBACK_RELAYS
- app/web/src/nostr.js: Add fallback relay profile fetching
- pkg/tor/: New package for Tor hidden service management
- docs/TOR_SETUP.md: Documentation for Tor configuration
- deploy/orly-tor.service: Systemd service for Tor integration
- scripts/tor-*.sh: Setup scripts for Tor development and production
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add bunker-worker.js Web Worker for NIP-46 signing
- Update rollup to build worker as separate bundle
- Move bunker state to stores.js for persistence across tab switches
- Worker maintains WebSocket connection independently of UI lifecycle
Files modified:
- app/web/src/bunker-worker.js: New Web Worker implementation
- app/web/src/stores.js: Added bunker worker state management
- app/web/src/BunkerView.svelte: Use worker instead of inline service
- app/web/rollup.config.js: Build worker bundle separately
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add guard to prevent duplicate service starts
- Fix stale variable references in error handler
- Show token list even when WebSocket temporarily disconnects
- Add logging for bunker service status changes
Files modified:
- app/web/src/BunkerView.svelte: UI state fixes
- app/web/dist/bundle.js: Rebuilt web UI
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add FileStore implementation for keyset persistence
- Keysets now survive server restarts
- Store keysets in JSON file at $ORLY_DATA_DIR/cashu-keysets.json
- Tokens issued before restart remain valid
Files modified:
- pkg/cashu/keyset/file_store.go: New file-based keyset store
- app/main.go: Use FileStore instead of MemoryStore
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Each client device now gets its own CAT token
- Tokens can be individually named (editable, defaults to cute names like "jolly-jellyfish")
- Tokens can be individually revoked
- Expandable table rows show QR code and full bunker URL per token
- Separate service token for ORLY's own relay connection
- Add Token button to create additional client tokens
Files modified:
- app/web/src/BunkerView.svelte: Token list UI with expandable details
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add automatic Cashu issuer/verifier initialization when ACL mode is not 'none'
- Use memory store for keyset management with proper TTL configuration
- Import cashuiface package for AllowAllChecker implementation
- ACL handles authorization; CAT provides token-based authentication
Files modified:
- app/main.go: Add Cashu system initialization when ACL active
- pkg/version/version: Bump to v0.44.2
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Update handleFavicon to serve /favicon.png instead of non-existent orly-favicon.png
- Remove orly-favicon.png from rollup copy targets
- Update release command to include setcap before restart
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add web app manifest for standalone installation
- Add service worker with offline-first caching for static assets
- Add network-first caching with fallback for API calls
- Generate PWA icons (192x192, 512x512) from favicon
- Add Apple PWA meta tags for iOS support
- Update rollup config to copy PWA files to dist
Files modified:
- app/web/public/manifest.json: New PWA manifest
- app/web/public/sw.js: New service worker
- app/web/public/icon-192.png: New PWA icon
- app/web/public/icon-512.png: New PWA icon
- app/web/public/index.html: Add manifest link, meta tags, SW registration
- app/web/rollup.config.js: Add PWA files to copy targets
- pkg/version/version: Bump to v0.43.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Enforce Cashu access token for kind 24133 events when Cashu is enabled and ACL is active
- Reject NIP-46 events without valid token with "restricted: NIP-46 requires Cashu access token"
- Verify token scope is NIP-46 or RELAY
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add BunkerView with two QR codes: client (bunker://) and signer (nostr+connect://)
- Add click-to-copy functionality on QR codes with visual "Copied!" feedback
- Add CAT requirement warning (only shows when ACL mode is active)
- Remove WireGuard dependencies from bunker page
- Add /api/bunker/info public endpoint for relay URL, ACL mode, CAT status
- Add Cashu token verification for WebSocket connections
- Add kind permission checking for Cashu token scopes
- Add cashuToken field to Listener for connection-level token tracking
Files modified:
- app/handle-bunker.go: New bunker info endpoint (without WireGuard)
- app/handle-event.go: Add Cashu token kind permission check
- app/handle-websocket.go: Extract and verify Cashu token on WS upgrade
- app/listener.go: Add cashuToken field
- app/server.go: Register bunker info endpoint
- app/web/src/BunkerView.svelte: Complete rewrite with QR codes
- app/web/src/api.js: Add getBunkerInfo() function
- pkg/version/version: Bump to v0.41.0
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add embedded WireGuard VPN server using wireguard-go + netstack
- Implement deterministic /31 subnet allocation from seed + sequence
- Use Badger's built-in Sequence for atomic counter allocation
- Add NIP-46 bunker server for remote signing over VPN
- Add revoked key tracking and access audit logging for users
- Add Bunker tab to web UI with WireGuard/bunker QR codes
- Support key regeneration with old keypair archiving
New environment variables:
- ORLY_WG_ENABLED: Enable WireGuard VPN server
- ORLY_WG_PORT: UDP port for WireGuard (default 51820)
- ORLY_WG_ENDPOINT: Public endpoint for WireGuard
- ORLY_WG_NETWORK: Base network for subnet pool (default 10.0.0.0/8)
- ORLY_BUNKER_ENABLED: Enable NIP-46 bunker
- ORLY_BUNKER_PORT: WebSocket port for bunker (default 3335)
Files added:
- pkg/wireguard/: WireGuard server, keygen, subnet pool, errors
- pkg/bunker/: NIP-46 bunker server and session handling
- pkg/database/wireguard.go: Peer storage with audit logging
- app/handle-wireguard.go: API endpoints for config/regenerate/audit
- app/wireguard-helpers.go: Key derivation helpers
- app/web/src/BunkerView.svelte: Bunker UI with QR codes
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix base64 encoding to use URL-safe format (- instead of +, _ instead of /)
- Remove padding characters (=) from base64 output
- Apply fix to LogView, BlossomView, and api.js
Files modified:
- app/web/src/LogView.svelte: URL-safe base64 for NIP-98 auth
- app/web/src/BlossomView.svelte: URL-safe base64 for Blossom auth
- app/web/src/api.js: URL-safe base64 for NIP-98 auth
- pkg/version/version: Bump to v0.39.2
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Include query parameters in signed NIP-98 auth URL
- Auth event URL must match actual request URL including ?offset=&limit=
Files modified:
- app/web/src/LogView.svelte: Fix auth URL to include query params
- pkg/version/version: Bump to v0.39.1
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Reinitialize lol loggers after wrapping Writer with BufferedWriter
- The lol.Main logger was initialized in init() with os.Stderr directly,
bypassing the Writer variable, so we now recreate it with the wrapped Writer
- Log level changes now properly affect both the buffer and syslog output
Files modified:
- app/config/config.go: Reinitialize loggers after BufferedWriter setup
- pkg/logbuffer/writer.go: Remove unused stub function
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Show full npub on screens > 720px, truncated on smaller screens
- Make admin users list extend to full width
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Make Blossom view use full available width
- Add "Upload new files" label with Select Files button on right
- Show image/video thumbnails in file list (48x48px)
- Add emoji icons for audio (🎵) and documents (📄)
- Show full hash on screens > 720px, truncated on smaller
Files modified:
- app/web/src/BlossomView.svelte: UI layout and thumbnail changes
- app/web/dist/*: Rebuilt bundle
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add console.log to trace currentEffectiveRole value in BlossomView
- Add HTML comment showing role and isAdmin values for debugging
Files modified:
- app/web/src/BlossomView.svelte: Add debug logging for role detection
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Change version link color from muted to readable text color
- Add background color hover effect matching tab styling
- Replace Gitea icon with mug-and-leaf icon
- Rename CSS class from gitea-icon to version-icon
Files modified:
- app/web/src/Sidebar.svelte: Updated version link styling and icon
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add version footer to sidebar bottom-left with Gitea icon link
- Fetch relay version from NIP-11 relay info document
- Link opens https://next.orly.dev in new tab
- Responsive design hides version text on medium screens
Files modified:
- app/web/src/api.js: Add fetchRelayInfo() function
- app/web/src/Sidebar.svelte: Add version display with Gitea SVG icon
- app/web/src/App.svelte: Add relayVersion state and fetch on init
- pkg/version/version: Bump to v0.36.18
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Add buffer pool (pkg/database/bufpool) with SmallPool (64B) and MediumPool (1KB)
for reusing bytes.Buffer instances on hot paths
- Fix escape analysis in index types (uint40, letter, word) by using fixed-size
arrays instead of make() calls that escape to heap
- Add handler concurrency limiter (ORLY_MAX_HANDLERS_PER_CONN, default 100) to
prevent unbounded goroutine growth under WebSocket load
- Add pre-allocation hints to Uint40s.Union/Intersection/Difference methods
- Update compact_event.go, save-event.go, serial_cache.go, and
get-indexes-for-event.go to use pooled buffers
Files modified:
- app/config/config.go: Add MaxHandlersPerConnection config
- app/handle-websocket.go: Initialize handler semaphore
- app/listener.go: Add semaphore acquire/release in messageProcessor
- pkg/database/bufpool/pool.go: New buffer pool package
- pkg/database/compact_event.go: Use buffer pool, fix escape analysis
- pkg/database/get-indexes-for-event.go: Reuse single buffer for all indexes
- pkg/database/indexes/types/letter.go: Fixed array in UnmarshalRead
- pkg/database/indexes/types/uint40.go: Fixed arrays, pre-allocation hints
- pkg/database/indexes/types/word.go: Fixed array in UnmarshalRead
- pkg/database/save-event.go: Use buffer pool for key encoding
- pkg/database/serial_cache.go: Use buffer pool for lookups
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>