setup-external-relays.sh raw

   1  #!/bin/bash
   2  
   3  # Setup script for downloading and configuring external relay repositories
   4  # for benchmarking
   5  
   6  set -e
   7  
   8  SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
   9  EXTERNAL_DIR="${SCRIPT_DIR}/external"
  10  
  11  echo "Setting up external relay repositories for benchmarking..."
  12  
  13  # Create external directory
  14  mkdir -p "${EXTERNAL_DIR}"
  15  
  16  # Function to clone or update repository
  17  clone_or_update() {
  18      local repo_url="$1"
  19      local repo_dir="$2"
  20      local repo_name="$3"
  21      
  22      echo "Setting up ${repo_name}..."
  23      
  24      if [ -d "${repo_dir}" ]; then
  25          echo "  ${repo_name} already exists, updating..."
  26          cd "${repo_dir}"
  27          git pull origin main 2>/dev/null || git pull origin master 2>/dev/null || true
  28          cd - > /dev/null
  29      else
  30          echo "  Cloning ${repo_name}..."
  31          git clone "${repo_url}" "${repo_dir}"
  32      fi
  33  }
  34  
  35  # Clone khatru
  36  clone_or_update "https://github.com/fiatjaf/khatru.git" "${EXTERNAL_DIR}/khatru" "Khatru"
  37  
  38  # Clone relayer
  39  clone_or_update "https://github.com/fiatjaf/relayer.git" "${EXTERNAL_DIR}/relayer" "Relayer"
  40  
  41  # Clone strfry
  42  clone_or_update "https://github.com/hoytech/strfry.git" "${EXTERNAL_DIR}/strfry" "Strfry"
  43  
  44  # Clone nostr-rs-relay
  45  clone_or_update "https://git.sr.ht/~gheartsfield/nostr-rs-relay" "${EXTERNAL_DIR}/nostr-rs-relay" "Nostr-rs-relay"
  46  
  47  echo "Creating Dockerfiles for external relays..."
  48  
  49  # Create Dockerfile for Khatru SQLite
  50  cat > "${SCRIPT_DIR}/Dockerfile.khatru-sqlite" << 'EOF'
  51  FROM golang:1.25-alpine AS builder
  52  
  53  RUN apk add --no-cache git ca-certificates sqlite-dev gcc musl-dev
  54  
  55  WORKDIR /build
  56  COPY . .
  57  
  58  # Build the basic-sqlite example
  59  RUN cd examples/basic-sqlite && \
  60      go mod tidy && \
  61      CGO_ENABLED=1 go build -o khatru-sqlite .
  62  
  63  FROM alpine:latest
  64  RUN apk --no-cache add ca-certificates sqlite wget
  65  WORKDIR /app
  66  COPY --from=builder /build/examples/basic-sqlite/khatru-sqlite /app/
  67  RUN mkdir -p /data
  68  EXPOSE 8080
  69  ENV DATABASE_PATH=/data/khatru.db
  70  ENV PORT=8080
  71  HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  72    CMD wget --quiet --tries=1 --spider http://localhost:8080 || exit 1
  73  CMD ["/app/khatru-sqlite"]
  74  EOF
  75  
  76  # Create Dockerfile for Khatru Badger
  77  cat > "${SCRIPT_DIR}/Dockerfile.khatru-badger" << 'EOF'
  78  FROM golang:1.25-alpine AS builder
  79  
  80  RUN apk add --no-cache git ca-certificates
  81  
  82  WORKDIR /build
  83  COPY . .
  84  
  85  # Build the basic-badger example
  86  RUN cd examples/basic-badger && \
  87      go mod tidy && \
  88      CGO_ENABLED=0 go build -o khatru-badger .
  89  
  90  FROM alpine:latest
  91  RUN apk --no-cache add ca-certificates wget
  92  WORKDIR /app
  93  COPY --from=builder /build/examples/basic-badger/khatru-badger /app/
  94  RUN mkdir -p /data
  95  EXPOSE 8080
  96  ENV DATABASE_PATH=/data/badger
  97  ENV PORT=8080
  98  HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
  99    CMD wget --quiet --tries=1 --spider http://localhost:8080 || exit 1
 100  CMD ["/app/khatru-badger"]
 101  EOF
 102  
 103  # Create Dockerfile for Relayer basic example
 104  cat > "${SCRIPT_DIR}/Dockerfile.relayer-basic" << 'EOF'
 105  FROM golang:1.25-alpine AS builder
 106  
 107  RUN apk add --no-cache git ca-certificates sqlite-dev gcc musl-dev
 108  
 109  WORKDIR /build
 110  COPY . .
 111  
 112  # Build the basic example
 113  RUN cd examples/basic && \
 114      go mod tidy && \
 115      CGO_ENABLED=1 go build -o relayer-basic .
 116  
 117  FROM alpine:latest
 118  RUN apk --no-cache add ca-certificates sqlite wget
 119  WORKDIR /app
 120  COPY --from=builder /build/examples/basic/relayer-basic /app/
 121  RUN mkdir -p /data
 122  EXPOSE 8080
 123  ENV DATABASE_PATH=/data/relayer.db
 124  ENV PORT=8080
 125  HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
 126    CMD wget --quiet --tries=1 --spider http://localhost:8080 || exit 1
 127  CMD ["/app/relayer-basic"]
 128  EOF
 129  
 130  # Create Dockerfile for Strfry
 131  cat > "${SCRIPT_DIR}/Dockerfile.strfry" << 'EOF'
 132  FROM ubuntu:22.04 AS builder
 133  
 134  ENV DEBIAN_FRONTEND=noninteractive
 135  
 136  # Install build dependencies
 137  RUN apt-get update && apt-get install -y \
 138      git \
 139      build-essential \
 140      liblmdb-dev \
 141      libsecp256k1-dev \
 142      pkg-config \
 143      libtool \
 144      autoconf \
 145      automake \
 146      && rm -rf /var/lib/apt/lists/*
 147  
 148  WORKDIR /build
 149  COPY . .
 150  
 151  # Build strfry
 152  RUN make setup-golpe && \
 153      make -j$(nproc)
 154  
 155  FROM ubuntu:22.04
 156  RUN apt-get update && apt-get install -y \
 157      liblmdb0 \
 158      libsecp256k1-0 \
 159      curl \
 160      && rm -rf /var/lib/apt/lists/*
 161  
 162  WORKDIR /app
 163  COPY --from=builder /build/strfry /app/
 164  RUN mkdir -p /data
 165  
 166  EXPOSE 8080
 167  ENV STRFRY_DB_PATH=/data/strfry.lmdb
 168  ENV STRFRY_RELAY_PORT=8080
 169  
 170  HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
 171    CMD curl -f http://localhost:8080 || exit 1
 172  
 173  CMD ["/app/strfry", "relay"]
 174  EOF
 175  
 176  # Create Dockerfile for nostr-rs-relay
 177  cat > "${SCRIPT_DIR}/Dockerfile.nostr-rs-relay" << 'EOF'
 178  FROM rust:1.70-alpine AS builder
 179  
 180  RUN apk add --no-cache musl-dev sqlite-dev
 181  
 182  WORKDIR /build
 183  COPY . .
 184  
 185  # Build the relay
 186  RUN cargo build --release
 187  
 188  FROM alpine:latest
 189  RUN apk --no-cache add ca-certificates sqlite wget
 190  WORKDIR /app
 191  COPY --from=builder /build/target/release/nostr-rs-relay /app/
 192  RUN mkdir -p /data
 193  
 194  EXPOSE 8080
 195  ENV RUST_LOG=info
 196  
 197  HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
 198    CMD wget --quiet --tries=1 --spider http://localhost:8080 || exit 1
 199  
 200  CMD ["/app/nostr-rs-relay"]
 201  EOF
 202  
 203  echo "Creating configuration files..."
 204  
 205  # Create configs directory
 206  mkdir -p "${SCRIPT_DIR}/configs"
 207  
 208  # Create strfry configuration
 209  cat > "${SCRIPT_DIR}/configs/strfry.conf" << 'EOF'
 210  ##
 211  ## Default strfry config
 212  ##
 213  
 214  # Directory that contains the strfry LMDB database (restart required)
 215  db = "/data/strfry.lmdb"
 216  
 217  dbParams {
 218      # Maximum number of threads/processes that can simultaneously have LMDB transactions open (restart required)
 219      maxreaders = 256
 220  
 221      # Size of mmap to use when loading LMDB (default is 1TB, which is probably reasonable) (restart required)
 222      mapsize = 1099511627776
 223  }
 224  
 225  relay {
 226      # Interface to listen on. Use 0.0.0.0 to listen on all interfaces (restart required)
 227      bind = "0.0.0.0"
 228  
 229      # Port to open for the nostr websocket protocol (restart required)
 230      port = 8080
 231  
 232      # Set OS-limit on maximum number of open files/sockets (if 0, don't attempt to set) (restart required)
 233      nofiles = 1000000
 234  
 235      # HTTP header that contains the client's real IP, before reverse proxying (ie x-real-ip) (MUST be all lower-case)
 236      realIpHeader = ""
 237  
 238      info {
 239          # NIP-11: Name of this server. Short/descriptive (< 30 characters)
 240          name = "strfry benchmark"
 241  
 242          # NIP-11: Detailed description of this server, free-form
 243          description = "A strfry relay for benchmarking"
 244  
 245          # NIP-11: Administrative pubkey, for contact purposes
 246          pubkey = ""
 247  
 248          # NIP-11: Alternative contact for this server
 249          contact = ""
 250      }
 251  
 252      # Maximum accepted incoming websocket frame size (should be larger than max event) (restart required)
 253      maxWebsocketPayloadSize = 104857600
 254  
 255      # Websocket-level PING message frequency (should be less than any reverse proxy idle timeouts) (restart required)
 256      autoPingSeconds = 55
 257  
 258      # If TCP keep-alive should be enabled (detect dropped connections to upstream reverse proxy) (restart required)
 259      enableTcpKeepalive = false
 260  
 261      # How much uninterrupted CPU time a REQ query should get during its DB scan
 262      queryTimesliceBudgetMicroseconds = 10000
 263  
 264      # Maximum records that can be returned per filter
 265      maxFilterLimit = 500
 266  
 267      # Maximum number of subscriptions (concurrent REQs) a connection can have open at any time
 268      maxSubsPerConnection = 20
 269  
 270      writePolicy {
 271          # If non-empty, path to an executable script that implements the writePolicy plugin logic
 272          plugin = ""
 273      }
 274  
 275      compression {
 276          # Use permessage-deflate compression if supported by client. Reduces bandwidth, but uses more CPU (restart required)
 277          enabled = true
 278  
 279          # Maintain a sliding window buffer for each connection. Improves compression, but uses more memory (restart required)
 280          slidingWindow = true
 281      }
 282  
 283      logging {
 284          # Dump all incoming messages
 285          dumpInAll = false
 286  
 287          # Dump all incoming EVENT messages
 288          dumpInEvents = false
 289  
 290          # Dump all incoming REQ/CLOSE messages
 291          dumpInReqs = false
 292  
 293          # Log performance metrics for initial REQ database scans
 294          dbScanPerf = false
 295      }
 296  
 297      numThreads {
 298          # Ingester threads: route incoming requests, validate events/sigs (restart required)
 299          ingester = 3
 300  
 301          # reqWorker threads: Handle initial DB scan for events (restart required)
 302          reqWorker = 3
 303  
 304          # reqMonitor threads: Handle filtering of new events (restart required)
 305          reqMonitor = 3
 306  
 307          # yesstr threads: experimental yesstr protocol (restart required)
 308          yesstr = 1
 309      }
 310  }
 311  EOF
 312  
 313  # Create nostr-rs-relay configuration
 314  cat > "${SCRIPT_DIR}/configs/config.toml" << 'EOF'
 315  [info]
 316  relay_url = "ws://localhost:8080"
 317  name = "nostr-rs-relay benchmark"
 318  description = "A nostr-rs-relay for benchmarking"
 319  pubkey = ""
 320  contact = ""
 321  
 322  [database]
 323  data_directory = "/data"
 324  in_memory = false
 325  engine = "sqlite"
 326  
 327  [network]
 328  port = 8080
 329  address = "0.0.0.0"
 330  
 331  [limits]
 332  messages_per_sec = 0
 333  subscriptions_per_min = 0
 334  max_event_bytes = 65535
 335  max_ws_message_bytes = 104857600
 336  max_ws_frame_bytes = 104857600
 337  
 338  [authorization]
 339  pubkey_whitelist = []
 340  
 341  [verified_users]
 342  mode = "passive"
 343  domain_whitelist = []
 344  domain_blacklist = []
 345  
 346  [pay_to_relay]
 347  enabled = false
 348  
 349  [options]
 350  reject_future_seconds = 30
 351  EOF
 352  
 353  echo "Creating data directories..."
 354  mkdir -p "${SCRIPT_DIR}/data"/{next-orly,khatru-sqlite,khatru-badger,relayer-basic,strfry,nostr-rs-relay}
 355  mkdir -p "${SCRIPT_DIR}/reports"
 356  
 357  echo "Setup complete!"
 358  echo ""
 359  echo "External relay repositories have been cloned to: ${EXTERNAL_DIR}"
 360  echo "Dockerfiles have been created for all relay implementations"
 361  echo "Configuration files have been created in: ${SCRIPT_DIR}/configs"
 362  echo "Data directories have been created in: ${SCRIPT_DIR}/data"
 363  echo ""
 364  echo "To run the benchmark:"
 365  echo "  cd ${SCRIPT_DIR}"
 366  echo "  docker-compose up --build"
 367  echo ""
 368  echo "Reports will be generated in: ${SCRIPT_DIR}/reports"