#!/bin/bash # Setup script for downloading and configuring external relay repositories # for benchmarking set -e SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" EXTERNAL_DIR="${SCRIPT_DIR}/external" echo "Setting up external relay repositories for benchmarking..." # Create external directory mkdir -p "${EXTERNAL_DIR}" # Function to clone or update repository clone_or_update() { local repo_url="$1" local repo_dir="$2" local repo_name="$3" echo "Setting up ${repo_name}..." if [ -d "${repo_dir}" ]; then echo " ${repo_name} already exists, updating..." cd "${repo_dir}" git pull origin main 2>/dev/null || git pull origin master 2>/dev/null || true cd - > /dev/null else echo " Cloning ${repo_name}..." git clone "${repo_url}" "${repo_dir}" fi } # Clone khatru clone_or_update "https://github.com/fiatjaf/khatru.git" "${EXTERNAL_DIR}/khatru" "Khatru" # Clone relayer clone_or_update "https://github.com/fiatjaf/relayer.git" "${EXTERNAL_DIR}/relayer" "Relayer" # Clone strfry clone_or_update "https://github.com/hoytech/strfry.git" "${EXTERNAL_DIR}/strfry" "Strfry" # Clone nostr-rs-relay clone_or_update "https://git.sr.ht/~gheartsfield/nostr-rs-relay" "${EXTERNAL_DIR}/nostr-rs-relay" "Nostr-rs-relay" echo "Creating Dockerfiles for external relays..." # Create Dockerfile for Khatru SQLite cat > "${SCRIPT_DIR}/Dockerfile.khatru-sqlite" << 'EOF' FROM golang:1.25-alpine AS builder RUN apk add --no-cache git ca-certificates sqlite-dev gcc musl-dev WORKDIR /build COPY . . # Build the basic-sqlite example RUN cd examples/basic-sqlite && \ go mod tidy && \ CGO_ENABLED=1 go build -o khatru-sqlite . FROM alpine:latest RUN apk --no-cache add ca-certificates sqlite wget WORKDIR /app COPY --from=builder /build/examples/basic-sqlite/khatru-sqlite /app/ RUN mkdir -p /data EXPOSE 8080 ENV DATABASE_PATH=/data/khatru.db ENV PORT=8080 HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD wget --quiet --tries=1 --spider http://localhost:8080 || exit 1 CMD ["/app/khatru-sqlite"] EOF # Create Dockerfile for Khatru Badger cat > "${SCRIPT_DIR}/Dockerfile.khatru-badger" << 'EOF' FROM golang:1.25-alpine AS builder RUN apk add --no-cache git ca-certificates WORKDIR /build COPY . . # Build the basic-badger example RUN cd examples/basic-badger && \ go mod tidy && \ CGO_ENABLED=0 go build -o khatru-badger . FROM alpine:latest RUN apk --no-cache add ca-certificates wget WORKDIR /app COPY --from=builder /build/examples/basic-badger/khatru-badger /app/ RUN mkdir -p /data EXPOSE 8080 ENV DATABASE_PATH=/data/badger ENV PORT=8080 HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD wget --quiet --tries=1 --spider http://localhost:8080 || exit 1 CMD ["/app/khatru-badger"] EOF # Create Dockerfile for Relayer basic example cat > "${SCRIPT_DIR}/Dockerfile.relayer-basic" << 'EOF' FROM golang:1.25-alpine AS builder RUN apk add --no-cache git ca-certificates sqlite-dev gcc musl-dev WORKDIR /build COPY . . # Build the basic example RUN cd examples/basic && \ go mod tidy && \ CGO_ENABLED=1 go build -o relayer-basic . FROM alpine:latest RUN apk --no-cache add ca-certificates sqlite wget WORKDIR /app COPY --from=builder /build/examples/basic/relayer-basic /app/ RUN mkdir -p /data EXPOSE 8080 ENV DATABASE_PATH=/data/relayer.db ENV PORT=8080 HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD wget --quiet --tries=1 --spider http://localhost:8080 || exit 1 CMD ["/app/relayer-basic"] EOF # Create Dockerfile for Strfry cat > "${SCRIPT_DIR}/Dockerfile.strfry" << 'EOF' FROM ubuntu:22.04 AS builder ENV DEBIAN_FRONTEND=noninteractive # Install build dependencies RUN apt-get update && apt-get install -y \ git \ build-essential \ liblmdb-dev \ libsecp256k1-dev \ pkg-config \ libtool \ autoconf \ automake \ && rm -rf /var/lib/apt/lists/* WORKDIR /build COPY . . # Build strfry RUN make setup-golpe && \ make -j$(nproc) FROM ubuntu:22.04 RUN apt-get update && apt-get install -y \ liblmdb0 \ libsecp256k1-0 \ curl \ && rm -rf /var/lib/apt/lists/* WORKDIR /app COPY --from=builder /build/strfry /app/ RUN mkdir -p /data EXPOSE 8080 ENV STRFRY_DB_PATH=/data/strfry.lmdb ENV STRFRY_RELAY_PORT=8080 HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD curl -f http://localhost:8080 || exit 1 CMD ["/app/strfry", "relay"] EOF # Create Dockerfile for nostr-rs-relay cat > "${SCRIPT_DIR}/Dockerfile.nostr-rs-relay" << 'EOF' FROM rust:1.70-alpine AS builder RUN apk add --no-cache musl-dev sqlite-dev WORKDIR /build COPY . . # Build the relay RUN cargo build --release FROM alpine:latest RUN apk --no-cache add ca-certificates sqlite wget WORKDIR /app COPY --from=builder /build/target/release/nostr-rs-relay /app/ RUN mkdir -p /data EXPOSE 8080 ENV RUST_LOG=info HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \ CMD wget --quiet --tries=1 --spider http://localhost:8080 || exit 1 CMD ["/app/nostr-rs-relay"] EOF echo "Creating configuration files..." # Create configs directory mkdir -p "${SCRIPT_DIR}/configs" # Create strfry configuration cat > "${SCRIPT_DIR}/configs/strfry.conf" << 'EOF' ## ## Default strfry config ## # Directory that contains the strfry LMDB database (restart required) db = "/data/strfry.lmdb" dbParams { # Maximum number of threads/processes that can simultaneously have LMDB transactions open (restart required) maxreaders = 256 # Size of mmap to use when loading LMDB (default is 1TB, which is probably reasonable) (restart required) mapsize = 1099511627776 } relay { # Interface to listen on. Use 0.0.0.0 to listen on all interfaces (restart required) bind = "0.0.0.0" # Port to open for the nostr websocket protocol (restart required) port = 8080 # Set OS-limit on maximum number of open files/sockets (if 0, don't attempt to set) (restart required) nofiles = 1000000 # HTTP header that contains the client's real IP, before reverse proxying (ie x-real-ip) (MUST be all lower-case) realIpHeader = "" info { # NIP-11: Name of this server. Short/descriptive (< 30 characters) name = "strfry benchmark" # NIP-11: Detailed description of this server, free-form description = "A strfry relay for benchmarking" # NIP-11: Administrative pubkey, for contact purposes pubkey = "" # NIP-11: Alternative contact for this server contact = "" } # Maximum accepted incoming websocket frame size (should be larger than max event) (restart required) maxWebsocketPayloadSize = 104857600 # Websocket-level PING message frequency (should be less than any reverse proxy idle timeouts) (restart required) autoPingSeconds = 55 # If TCP keep-alive should be enabled (detect dropped connections to upstream reverse proxy) (restart required) enableTcpKeepalive = false # How much uninterrupted CPU time a REQ query should get during its DB scan queryTimesliceBudgetMicroseconds = 10000 # Maximum records that can be returned per filter maxFilterLimit = 500 # Maximum number of subscriptions (concurrent REQs) a connection can have open at any time maxSubsPerConnection = 20 writePolicy { # If non-empty, path to an executable script that implements the writePolicy plugin logic plugin = "" } compression { # Use permessage-deflate compression if supported by client. Reduces bandwidth, but uses more CPU (restart required) enabled = true # Maintain a sliding window buffer for each connection. Improves compression, but uses more memory (restart required) slidingWindow = true } logging { # Dump all incoming messages dumpInAll = false # Dump all incoming EVENT messages dumpInEvents = false # Dump all incoming REQ/CLOSE messages dumpInReqs = false # Log performance metrics for initial REQ database scans dbScanPerf = false } numThreads { # Ingester threads: route incoming requests, validate events/sigs (restart required) ingester = 3 # reqWorker threads: Handle initial DB scan for events (restart required) reqWorker = 3 # reqMonitor threads: Handle filtering of new events (restart required) reqMonitor = 3 # yesstr threads: experimental yesstr protocol (restart required) yesstr = 1 } } EOF # Create nostr-rs-relay configuration cat > "${SCRIPT_DIR}/configs/config.toml" << 'EOF' [info] relay_url = "ws://localhost:8080" name = "nostr-rs-relay benchmark" description = "A nostr-rs-relay for benchmarking" pubkey = "" contact = "" [database] data_directory = "/data" in_memory = false engine = "sqlite" [network] port = 8080 address = "0.0.0.0" [limits] messages_per_sec = 0 subscriptions_per_min = 0 max_event_bytes = 65535 max_ws_message_bytes = 104857600 max_ws_frame_bytes = 104857600 [authorization] pubkey_whitelist = [] [verified_users] mode = "passive" domain_whitelist = [] domain_blacklist = [] [pay_to_relay] enabled = false [options] reject_future_seconds = 30 EOF echo "Creating data directories..." mkdir -p "${SCRIPT_DIR}/data"/{next-orly,khatru-sqlite,khatru-badger,relayer-basic,strfry,nostr-rs-relay} mkdir -p "${SCRIPT_DIR}/reports" echo "Setup complete!" echo "" echo "External relay repositories have been cloned to: ${EXTERNAL_DIR}" echo "Dockerfiles have been created for all relay implementations" echo "Configuration files have been created in: ${SCRIPT_DIR}/configs" echo "Data directories have been created in: ${SCRIPT_DIR}/data" echo "" echo "To run the benchmark:" echo " cd ${SCRIPT_DIR}" echo " docker-compose up --build" echo "" echo "Reports will be generated in: ${SCRIPT_DIR}/reports"