diff --git a/DOCKER.md b/DOCKER.md index 8e23e4a..8eb91c9 100644 --- a/DOCKER.md +++ b/DOCKER.md @@ -9,7 +9,7 @@ docker-compose up -d # View logs -docker-compose logs -f stella-relay +docker-compose logs -f orly-relay # Stop the relay docker-compose down @@ -136,7 +136,7 @@ go run ./cmd/stresstest -relay ws://localhost:7777 ```bash # Container debugging docker ps | grep relay -docker logs stella-relay +docker logs orly-relay curl -I http://127.0.0.1:7777 # Should return HTTP 426 # WebSocket testing @@ -153,7 +153,7 @@ grep ProxyPass /etc/apache2/plesk.conf.d/vhosts/domain.conf ```bash # View relay logs -docker-compose logs -f stella-relay +docker-compose logs -f orly-relay # View nginx logs (if using proxy) docker-compose logs -f nginx diff --git a/docker-compose.yml b/docker-compose.yml index 6abc2c2..6d66916 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,12 +2,12 @@ # Owner: npub1v30tsz9vw6ylpz63g0a702nj3xa26t3m7p5us8f2y2sd8v6cnsvq465zjx services: - stella-relay: + orly-relay: build: context: . dockerfile: Dockerfile image: silberengel/next-orly:latest - container_name: stella-nostr-relay + container_name: orly-nostr-relay restart: unless-stopped ports: - "127.0.0.1:7777:7777" @@ -22,14 +22,14 @@ services: - ORLY_LOG_LEVEL=info - ORLY_DB_LOG_LEVEL=error - ORLY_OWNERS=npub1v30tsz9vw6ylpz63g0a702nj3xa26t3m7p5us8f2y2sd8v6cnsvq465zjx - - ORLY_ADMINS=npub1v30tsz9vw6ylpz63g0a702nj3xa26t3m7p5us8f2y2sd8v6cnsvq465zjx + - ORLY_ADMINS=npub1v30tsz9vw6ylpz63g0a702nj3xa26t3m7p5us8f2y2sd8v6cnsvq465zjx,npub1m4ny6hjqzepn4rxknuq94c2gpqzr29ufkkw7ttcxyak7v43n6vvsajc2jl,npub1l5sga6xg72phsz5422ykujprejwud075ggrr3z2hwyrfgr7eylqstegx9z # ACL and Spider Configuration - ORLY_ACL_MODE=follows - ORLY_SPIDER_MODE=follows # Bootstrap relay URLs for initial sync - - ORLY_BOOTSTRAP_RELAYS=wss://profiles.nostr1.com,wss://purplepag.es,wss://relay.damus.io,wss://nostr.wine,wss://relay.nostr.band,wss://freelay.sovbit.host + - ORLY_BOOTSTRAP_RELAYS=wss://profiles.nostr1.com,wss://purplepag.es,wss://relay.nostr.band,wss://relay.damus.io # Subscription Settings (optional) - ORLY_SUBSCRIPTION_ENABLED=false @@ -77,7 +77,7 @@ services: - ./nginx/ssl:/etc/nginx/ssl:ro - nginx_logs:/var/log/nginx depends_on: - - stella-relay + - orly-relay profiles: - proxy # Only start with: docker-compose --profile proxy up @@ -93,4 +93,4 @@ volumes: networks: default: - name: stella-relay-network + name: orly-relay-network diff --git a/manage-relay.sh b/manage-relay.sh index 1dc4d9d..9aeec78 100755 --- a/manage-relay.sh +++ b/manage-relay.sh @@ -9,8 +9,8 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_DIR="$SCRIPT_DIR" # Configuration from docker-compose.yml -RELAY_SERVICE="stella-relay" -CONTAINER_NAME="stella-nostr-relay" +RELAY_SERVICE="orly-relay" +CONTAINER_NAME="orly-nostr-relay" RELAY_URL="ws://127.0.0.1:7777" HTTP_URL="http://127.0.0.1:7777" RELAY_DATA_DIR="/home/madmin/.local/share/orly-relay" @@ -21,7 +21,7 @@ cd "$PROJECT_DIR" case "${1:-}" in "start") echo "๐Ÿš€ Starting Stella's Orly Relay..." - docker compose up -d stella-relay + docker compose up -d orly-relay echo "โœ… Relay started!" ;; "stop") @@ -31,16 +31,16 @@ case "${1:-}" in ;; "restart") echo "๐Ÿ”„ Restarting Stella's Orly Relay..." - docker compose restart stella-relay + docker compose restart orly-relay echo "โœ… Relay restarted!" ;; "status") echo "๐Ÿ“Š Stella's Orly Relay Status:" - docker compose ps stella-relay + docker compose ps orly-relay ;; "logs") echo "๐Ÿ“œ Stella's Orly Relay Logs:" - docker compose logs -f stella-relay + docker compose logs -f orly-relay ;; "test") echo "๐Ÿงช Testing relay connection..." @@ -77,7 +77,7 @@ case "${1:-}" in echo "๐Ÿณ Docker Information:" echo " Compose File: $PROJECT_DIR/docker-compose.yml" echo " Container Status:" - docker compose ps stella-relay 2>/dev/null || echo " Not running" + docker compose ps orly-relay 2>/dev/null || echo " Not running" echo "" echo "๐Ÿ’ก Configuration:" echo " All settings are defined in docker-compose.yml" @@ -85,26 +85,26 @@ case "${1:-}" in ;; "docker-logs") echo "๐Ÿณ Docker Container Logs:" - docker compose logs -f stella-relay 2>/dev/null || echo "โŒ Container not found or not running" + docker compose logs -f orly-relay 2>/dev/null || echo "โŒ Container not found or not running" ;; "docker-status") echo "๐Ÿณ Docker Container Status:" - docker compose ps stella-relay + docker compose ps orly-relay ;; "docker-restart") echo "๐Ÿ”„ Restarting Docker Container..." - docker compose restart stella-relay + docker compose restart orly-relay echo "โœ… Container restarted!" ;; "docker-update") echo "๐Ÿ”„ Updating and restarting Docker Container..." - docker compose pull stella-relay - docker compose up -d stella-relay + docker compose pull orly-relay + docker compose up -d orly-relay echo "โœ… Container updated and restarted!" ;; "docker-build") echo "๐Ÿ”จ Building Docker Container..." - docker compose build stella-relay + docker compose build orly-relay echo "โœ… Container built!" ;; "docker-down") diff --git a/pkg/acl/follows.go b/pkg/acl/follows.go index de22486..583b9e3 100644 --- a/pkg/acl/follows.go +++ b/pkg/acl/follows.go @@ -3,6 +3,8 @@ package acl import ( "bytes" "context" + "encoding/hex" + "net/http" "reflect" "strings" "sync" @@ -22,9 +24,9 @@ import ( "next.orly.dev/pkg/encoders/envelopes/reqenvelope" "next.orly.dev/pkg/encoders/event" "next.orly.dev/pkg/encoders/filter" - "next.orly.dev/pkg/encoders/hex" "next.orly.dev/pkg/encoders/kind" "next.orly.dev/pkg/encoders/tag" + "next.orly.dev/pkg/encoders/timestamp" "next.orly.dev/pkg/protocol/publish" "next.orly.dev/pkg/utils" "next.orly.dev/pkg/utils/normalize" @@ -108,7 +110,7 @@ func (f *Follows) Configure(cfg ...any) (err error) { for _, v := range ev.Tags.GetAll([]byte("p")) { // log.I.F("adding follow: %s", v.Value()) var a []byte - if b, e := hex.Dec(string(v.Value())); chk.E(e) { + if b, e := hex.DecodeString(string(v.Value())); chk.E(e) { continue } else { a = b @@ -253,18 +255,45 @@ func (f *Follows) startSubscriptions(ctx context.Context) { return default: } - c, _, err := websocket.Dial(ctx, u, nil) + // Create a timeout context for the connection + connCtx, cancel := context.WithTimeout(ctx, 10*time.Second) + + // Create proper headers for the WebSocket connection + headers := http.Header{} + headers.Set("User-Agent", "ORLY-Relay/0.9.2") + headers.Set("Origin", "https://orly.dev") + + // Use proper WebSocket dial options + dialOptions := &websocket.DialOptions{ + HTTPHeader: headers, + } + + c, _, err := websocket.Dial(connCtx, u, dialOptions) + cancel() if err != nil { log.W.F("follows syncer: dial %s failed: %v", u, err) - if strings.Contains( - err.Error(), "response status code 101 but got 403", - ) { - // 403 means the relay is not accepting connections from - // us. Forbidden is the meaning, usually used to - // indicate either the IP or user is blocked. so stop - // trying this one. - return + + // Handle different types of errors + if strings.Contains(err.Error(), "response status code 101 but got 403") { + // 403 means the relay is not accepting connections from us + // Forbidden is the meaning, usually used to indicate either the IP or user is blocked + // But we should still retry after a longer delay + log.W.F("follows syncer: relay %s returned 403, will retry after longer delay", u) + timer := time.NewTimer(5 * time.Minute) // Wait 5 minutes before retrying 403 errors + select { + case <-ctx.Done(): + return + case <-timer.C: + } + continue + } else if strings.Contains(err.Error(), "timeout") || strings.Contains(err.Error(), "connection refused") { + // Network issues, retry with normal backoff + log.W.F("follows syncer: network issue with %s, retrying in %v", u, backoff) + } else { + // Other errors, retry with normal backoff + log.W.F("follows syncer: connection error with %s, retrying in %v", u, backoff) } + timer := time.NewTimer(backoff) select { case <-ctx.Done(): @@ -277,22 +306,37 @@ func (f *Follows) startSubscriptions(ctx context.Context) { continue } backoff = time.Second - // send REQ + log.I.F("follows syncer: successfully connected to %s", u) + + // send REQ for kind 3 (follow lists), kind 10002 (relay lists), and all events from follows ff := &filter.S{} f1 := &filter.F{ Authors: tag.NewFromBytesSlice(authors...), - Kinds: kind.NewS(kind.New(kind.RelayListMetadata.K)), - Limit: values.ToUintPointer(0), + Kinds: kind.NewS(kind.New(kind.FollowList.K)), + Limit: values.ToUintPointer(100), } - *ff = append(*ff, f1) + f2 := &filter.F{ + Authors: tag.NewFromBytesSlice(authors...), + Kinds: kind.NewS(kind.New(kind.RelayListMetadata.K)), + Limit: values.ToUintPointer(100), + } + // Add filter for all events from follows (last 30 days) + oneMonthAgo := timestamp.FromUnix(time.Now().Add(-30 * 24 * time.Hour).Unix()) + f3 := &filter.F{ + Authors: tag.NewFromBytesSlice(authors...), + Since: oneMonthAgo, + Limit: values.ToUintPointer(1000), + } + *ff = append(*ff, f1, f2, f3) req := reqenvelope.NewFrom([]byte("follows-sync"), ff) if err = c.Write( ctx, websocket.MessageText, req.Marshal(nil), ); chk.E(err) { + log.W.F("follows syncer: failed to send REQ to %s: %v", u, err) _ = c.Close(websocket.StatusInternalError, "write failed") continue } - log.T.F("sent REQ to %s for follows subscription", u) + log.I.F("follows syncer: sent REQ to %s for kind 3, 10002, and all events (last 30 days) from followed users", u) // read loop for { select { @@ -320,6 +364,23 @@ func (f *Follows) startSubscriptions(ctx context.Context) { if ok, err := res.Event.Verify(); chk.T(err) || !ok { continue } + + // Process events based on kind + switch res.Event.Kind { + case kind.FollowList.K: + log.I.F("follows syncer: received kind 3 (follow list) event from %s on relay %s", + hex.EncodeToString(res.Event.Pubkey), u) + // Extract followed pubkeys from 'p' tags in kind 3 events + f.extractFollowedPubkeys(res.Event) + case kind.RelayListMetadata.K: + log.I.F("follows syncer: received kind 10002 (relay list) event from %s on relay %s", + hex.EncodeToString(res.Event.Pubkey), u) + default: + // Log all other events from followed users + log.I.F("follows syncer: received kind %d event from %s on relay %s", + res.Event.Kind, hex.EncodeToString(res.Event.Pubkey), u) + } + if _, _, err = f.D.SaveEvent( ctx, res.Event, ); err != nil { @@ -397,6 +458,20 @@ func (f *Follows) GetFollowedPubkeys() [][]byte { return followedPubkeys } +// extractFollowedPubkeys extracts followed pubkeys from 'p' tags in kind 3 events +func (f *Follows) extractFollowedPubkeys(event *event.E) { + if event.Kind != kind.FollowList.K { + return + } + + // Extract all 'p' tags (followed pubkeys) from the kind 3 event + for _, tag := range event.Tags.GetAll([]byte("p")) { + if len(tag.Value()) == 32 { // Valid pubkey length + f.AddFollow(tag.Value()) + } + } +} + // AddFollow appends a pubkey to the in-memory follows list if not already present // and signals the syncer to refresh subscriptions. func (f *Follows) AddFollow(pub []byte) { @@ -413,6 +488,7 @@ func (f *Follows) AddFollow(pub []byte) { b := make([]byte, len(pub)) copy(b, pub) f.follows = append(f.follows, b) + log.I.F("follows syncer: added new followed pubkey: %s", hex.EncodeToString(pub)) // notify syncer if initialized if f.updated != nil { select { diff --git a/stella-relay.service b/stella-relay.service index b0e92e6..cd1bea6 100644 --- a/stella-relay.service +++ b/stella-relay.service @@ -13,13 +13,13 @@ Group=madmin WorkingDirectory=/home/madmin/Projects/GitCitadel/next.orly.dev # Start the relay using docker compose -ExecStart=/usr/bin/docker compose up -d stella-relay +ExecStart=/usr/bin/docker compose up -d orly-relay # Stop the relay ExecStop=/usr/bin/docker compose down # Reload configuration (restart containers) -ExecReload=/usr/bin/docker compose restart stella-relay +ExecReload=/usr/bin/docker compose restart orly-relay # Security settings NoNewPrivileges=true